diff --git a/.gitignore b/.gitignore index 935c7086ef..cd78247219 100644 --- a/.gitignore +++ b/.gitignore @@ -1,21 +1,17 @@ *.pyc -.cache/ -.project -.project/ -.pydevproject -.pydevproject/ -.settings/ -.keystone-venv/ +*.swp +vendor +.ksl-venv .venv -build/ -dist/ -doc/source/sourcecode +.tox keystone.egg-info/ -*.db -.*.swp -*.log -*.pid -pidfile -*.komodoproject +run_tests.log .coverage +covhtml +pep8.txt +nosetests.xml +bla.db +docs/build .DS_Store +docs/source/modules.rst +docs/source/keystone.* diff --git a/.mailmap b/.mailmap deleted file mode 100644 index 9b747732d5..0000000000 --- a/.mailmap +++ /dev/null @@ -1,16 +0,0 @@ - -Edouard Thuleau - - - -Khaled Hussein KnightHacker -Khaled Hussein Khaled Hussein - - - - - -sirish.bitra sirish bitra -sirish.bitra sirishbitra -sirish.bitra bsirish -sirish.bitra root diff --git a/AUTHORS b/AUTHORS deleted file mode 100644 index 9a9aa5d8bc..0000000000 --- a/AUTHORS +++ /dev/null @@ -1,50 +0,0 @@ -Adipudi Praveena -Alex Silva -Anne Gentle -Anthony Young -Brian Lamar -Dan Prince -Dolph Mathews -Ed Leafe -Edouard Thuleau -Eoghan Glynn -gholt -Ionuț Arțăriși -jabdul -James E. Blair -Jason Cannavale -Jay Pipes -Jenkins -Jesse Andrews -Joe Savak -John Dickinson -John Eo -Jorge L. Williams -Joseph W. Breu -Josh Kearney -Julien Danjou -Justin Shepherd -Kevin L. Mitchell -Khaled Hussein -Kiall Mac Innes -Mark Gius -Mark McLoughlin -Monty Taylor -Pádraig Brady -Paul Voccio -Ramana Juvvadi -Robin Norwood -root -Sai Krishna -Sirish Bitra -Sony K. Philip -termie -Thierry Carrez -Todd Willey -Will Kelly -Vishvananda Ishaya -Yaguang Tang -Yogeshwar Srikrishnan -Yuriy Taraday -Ziad Sawalha -Zhongyue Luo diff --git a/HACKING b/HACKING deleted file mode 100644 index 63eb015854..0000000000 --- a/HACKING +++ /dev/null @@ -1,68 +0,0 @@ -Keystone Style Commandments (pilfered from Nova and added to) -============================================================= - -Step 1: Read http://www.python.org/dev/peps/pep-0008/ -Step 2: Read http://www.python.org/dev/peps/pep-0008/ again -Step 3: Read on - -Imports -------- -- thou shalt not import objects, only modules -- thou shalt not import more than one module per line -- thou shalt not make relative imports -- thou shalt organize your imports according to the following template - -:: - # vim: tabstop=4 shiftwidth=4 softtabstop=4 - {{stdlib imports in human alphabetical order}} - \n - {{OpenStack/Keystone imports in human alphabetical order}} - \n - \n - {{begin your code}} - - -General -------- -- thou shalt put two newlines twixt toplevel code (funcs, classes, etc) -- thou shalt put one newline twixt methods in classes and anywhere else -- thou shalt not write "except:", use "except Exception:" at the very least -- thou shalt include your name with TODOs as in "TODO(waldo)" -- thou shalt not name anything the same name as a builtin or reserved word -- thou shouldeth comment profusely -- thou shalt not violate causality in our time cone, or else - - -Human Alphabetical Order Examples ---------------------------------- -:: - import httplib - import logging - import random - import StringIO - import time - import unittest - - import keystone.logic.types.fault as fault - import keystone.db.sqlalchemy.api as db_api - -Docstrings ----------- -Add them to modules, classes, and functions: - """Summary of the function, class or method, less than 80 characters. - - New paragraph after newline that explains in more detail any general - information about the function, class or method. After this, if defining - parameters and return types use the Sphinx format. After that an extra - newline then close the quotations. - - When writing the docstring for a class, an extra line should be placed - after the closing quotations. For more in-depth explanations for these - decisions see http://www.python.org/dev/peps/pep-0257/ - - :param foo: the foo parameter - :param bar: the bar parameter - :returns: description of the return value - - """ - diff --git a/HACKING.rst b/HACKING.rst new file mode 100644 index 0000000000..e9b36c64be --- /dev/null +++ b/HACKING.rst @@ -0,0 +1,192 @@ +Keystone Style Commandments +=========================== + +- Step 1: Read http://www.python.org/dev/peps/pep-0008/ +- Step 2: Read http://www.python.org/dev/peps/pep-0008/ again +- Step 3: Read on + + +General +------- +- Put two newlines between top-level code (funcs, classes, etc) +- Put one newline between methods in classes and anywhere else +- Do not write "except:", use "except Exception:" at the very least +- Include your name with TODOs as in "#TODO(termie)" +- Do not name anything the same name as a built-in or reserved word + +TODO vs FIXME +------------- + +- TODO(name): implies that something should be done (cleanup, refactoring, + etc), but is expected to be functional. +- FIXME(name): implies that the method/function/etc shouldn't be used until + that code is resolved and bug fixed. + +Imports +------- +- Do not import objects, only modules +- Do not import more than one module per line +- Do not make relative imports +- Order your imports by the full module path +- Organize your imports according to the following template + +Example:: + + # vim: tabstop=4 shiftwidth=4 softtabstop=4 + {{stdlib imports in human alphabetical order}} + \n + {{third-party lib imports in human alphabetical order}} + \n + {{nova imports in human alphabetical order}} + \n + \n + {{begin your code}} + + +Human Alphabetical Order Examples +--------------------------------- +Example:: + + import httplib + import logging + import random + import StringIO + import time + import unittest + + import eventlet + import webob.exc + + import nova.api.ec2 + from nova.api import openstack + from nova.auth import users + import nova.flags + from nova.endpoint import cloud + from nova import test + + +Docstrings +---------- +Example:: + + """A one line docstring looks like this and ends in a period.""" + + + """A multiline docstring has a one-line summary, less than 80 characters. + + Then a new paragraph after a newline that explains in more detail any + general information about the function, class or method. Example usages + are also great to have here if it is a complex class for function. + + When writing the docstring for a class, an extra line should be placed + after the closing quotations. For more in-depth explanations for these + decisions see http://www.python.org/dev/peps/pep-0257/ + + A docstring ends with an empty line before the closing quotations. + + Describe parameters and return values, using the Sphinx format; the + appropriate syntax is as follows. + + :param foo: the foo parameter + :param bar: the bar parameter + :type bar: parameter type for 'bar' + :returns: return_type -- description of the return value + :returns: description of the return value + :raises: AttributeError, KeyError + + """ + + +Dictionaries/Lists +------------------ +If a dictionary (dict) or list object is longer than 80 characters, its items +should be split with newlines. Embedded iterables should have their items +indented. Additionally, the last item in the dictionary should have a trailing +comma. This increases readability and simplifies future diffs. + +Example:: + + my_dictionary = { + "image": { + "name": "Just a Snapshot", + "size": 2749573, + "properties": { + "user_id": 12, + "arch": "x86_64", + }, + "things": [ + "thing_one", + "thing_two", + ], + "status": "ACTIVE", + }, + } + + +Calling Methods +--------------- +Calls to methods 80 characters or longer should format each argument with +newlines. This is not a requirement, but a guideline:: + + unnecessarily_long_function_name('string one', + 'string two', + kwarg1=constants.ACTIVE, + kwarg2=['a', 'b', 'c']) + + +Rather than constructing parameters inline, it is better to break things up:: + + list_of_strings = [ + 'what_a_long_string', + 'not as long', + ] + + dict_of_numbers = { + 'one': 1, + 'two': 2, + 'twenty four': 24, + } + + object_one.call_a_method('string three', + 'string four', + kwarg1=list_of_strings, + kwarg2=dict_of_numbers) + + +Internationalization (i18n) Strings +----------------------------------- +In order to support multiple languages, we have a mechanism to support +automatic translations of exception and log strings. + +Example:: + + msg = _("An error occurred") + raise HTTPBadRequest(explanation=msg) + +If you have a variable to place within the string, first internationalize the +template string then do the replacement. + +Example:: + + msg = _("Missing parameter: %s") % ("flavor",) + LOG.error(msg) + +If you have multiple variables to place in the string, use keyword parameters. +This helps our translators reorder parameters when needed. + +Example:: + + msg = _("The server with id %(s_id)s has no key %(m_key)s") + LOG.error(msg % {"s_id": "1234", "m_key": "imageId"}) + + +Creating Unit Tests +------------------- +For every new feature, unit tests should be created that both test and +(implicitly) document the usage of said feature. If submitting a patch for a +bug that had no unit test, a new passing unit test should be added. If a +submitted bug fix does have a unit test, be sure to add a new one that fails +without the patch and passes with the patch. + +For more information on creating unit tests and utilizing the testing +infrastructure in OpenStack Nova, please read nova/testing/README.rst. diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 75b52484ea..0000000000 --- a/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index bf7944a545..0000000000 --- a/MANIFEST.in +++ /dev/null @@ -1,21 +0,0 @@ -include AUTHORS -include HACKING -include LICENSE -include MANIFEST.in -include README.md -include pylintrc -include run_tests.py -include run_tests.sh -include setup.py -graft bin -graft doc -prune doc/source/sourcecode -graft etc -graft examples -graft keystone/content -graft keystone/test/etc -graft tools -recursive-include keystone *.json *.xml *.cfg README -include keystone/backends/ldap/keystone.ldif -include keystone/backends/ldap/keystone.schema -global-exclude *.pyc *.sdx *.log *.db *.swp diff --git a/README.md b/README.md deleted file mode 100644 index 2baf3f61ac..0000000000 --- a/README.md +++ /dev/null @@ -1,275 +0,0 @@ -# Keystone: OpenStack Identity Service - -Keystone is a Python implementation of the [OpenStack](http://www.openstack.org) identity service API. - -# Documentation - -## For users and sysadmins - -Learn how to install, configure, manage, and interact with the OpenStack -Identity Service API at the [OpenStack Documentation](http://docs.openstack.org/) site. - -## For contributors - -Learn how to setup a development environment and then test, run, and contribute to Keystone at the -[Contributor Documentation](http://keystone.openstack.org/) site. - -# Questions/Feedback - -Having trouble? We'd like to help! - -* Try the documentation first — it's got answers to many common questions. -* Search for information in the archives of the [OpenStack mailing list](http://wiki.openstack.org/MailingLists), or post a question. -* Ask a question in the [#openstack IRC channel](http://wiki.openstack.org/UsingIRC). -* If you notice errors, please [open a bug](https://bugs.launchpad.net/keystone) and let us know! Please only use the bug tracker for criticisms and improvements. For tech support, use the resources above. - -# For Contributors - -## What's in the box? - -### Services - -* Keystone - identity store and authentication service -* Auth_Token - WSGI middleware that can be used to handle token auth protocol (WSGI or remote proxy) -* Echo - A sample service that responds by returning call details - -### Also included: - -* Auth_Basic - Stub for WSGI middleware that will be used to handle basic auth -* Auth_OpenID - Stub for WSGI middleware that will be used to handle openid auth protocol (to be implemented) -* RemoteAuth - WSGI middleware that can be used in services (like Swift, Nova, and Glance) when Auth middleware is running remotely - -### Built-In commands: - -* bin/keystone - Provides HTTP API for users and administrators -* bin/keystone-admin - Provides HTTP API for administrators -* bin/keystone-service - Provides HTTP API for users -* bin/keystone-manage - Provides command-line interface for managing all aspects of Keystone - -## Running Keystone - -Starting both Admin and Service API endpoints: - - $ ./bin/keystone - -Starting the auth server only (exposes the Service API): - - $ ./bin/keystone-auth - -Starting the admin server only (exposes the Admin API): - - $ ./bin/keystone-admin - -By default, configuration parameters (such as the IP and port binding for each service) are parsed from `etc/keystone.conf`. - -## Configuring Keystone - -Keystone gets its configuration from command-line parameters or a `.conf` file. While command line parameters take precedence, -Keystone looks in the following location to find a configuration file: - - 1. Command line parameter - 2. /etc/keystone.conf - 3. /etc/keystone/keystone.conf - 4. /etc/keystone.conf - -Additional configuration templates are maintained in `keystone/test/etc/` that may be useful as a reference. - -### Editing and Building the API Developer Guide - -Users of the Keystone API are often developers making ReSTful API calls to Keystone. The guide to provide them -information is therefore called a `Developer Guide`. Developer in this case is not to be confused with contributors -working on the Keystone codebase itself. - -The developer guides are automatically generated from XML and other artifacts that live in the -[OpenStack Manuals project](https://launchpad.net/openstack-manuals). - -To build the Developer Guide from source, you need [Maven](http://maven.apache.org/). To build the docs and publish a new PDF: - - $ cd to folder with the pom.xml file - $ mvn clean generate-sources && cp target/docbkx/pdf/identitydevguide.pdf ../../keystone/content/identitydevguide.pdf - -The output will go into the `target` folder (the source is in `src`). Output generated is PDF and webhelp. - -# Additional Information: - -## Sample data - -A set of sample data can be loaded by running a shell script: - - $ ./bin/sampledata - -The script calls `keystone-manage` to import the sample data. - -After starting keystone or running `keystone-manage` a `keystone.db` sqlite database should be created in the keystone folder, -per the default configuration. - -## Demo - -To run client demo (with all auth middleware running locally on sample service): - - $ ./examples/echo/bin/echod - $ python examples/echo/echo_client.py - -## CURL commands - -
-    # Get an unscoped token
-    $ curl -d '{"auth": {"passwordCredentials": {"username": "joeuser", "password": "secrete"}}}' -H "Content-type: application/json" http://localhost:5000/v2.0/tokens
-
-    # Get a token for a tenant
-    $ curl -d '{"auth": {"passwordCredentials": {"username": "joeuser", "password": "secrete"}, "tenantName": "customer-x"}}' -H "Content-type: application/json" http://localhost:5000/v2.0/tokens
-
-    # Get an admin token
-    $ curl -d '{"auth": {"passwordCredentials": {"username": "admin", "password": "secrete"}}}' -H "Content-type: application/json" http://localhost:35357/v2.0/tokens
-
- -## Load Testing - -
-   # Create post data
-   $ echo '{"auth": {"passwordCredentials": {"username": "joeuser", "password": "secrete", "tenantName": "customer-x"}}}' > post_data
-
-   # Call Apache Bench
-   $ ab -c 30 -n 1000 -T "application/json" -p post_data http://127.0.0.1:35357/v2.0/tokens
-
- -## NOVA Integration - -Initial support for using keystone as nova's identity component has been started. - - # clone projects - bzr clone lp:nova - git clone git://github.com/openstack/keystone.git - - # install keystone on the host which runs nova - run "python setup install" to install keystone. - - # run nova-api based on the paste config in keystone - nova/bin/nova-api --api_paste_config=keystone/examples/paste/nova-api-paste.ini - -Assuming you added the test data using bin/sampledata, you can then use joeuser/secrete - -## Swift Integration - Quick Start - -1. Install Swift, either from trunk or version 1.4.1 (once it's released) or - higher. Do the standard SAIO install with the included TempAuth to be sure - you have a working system to start with. This step is beyond the scope of - this quick start; see http://swift.openstack.org/development_saio.html for - a Swift development set up guide. Once you have a working Swift install, go - ahead and shut it down for now (the default Swift install uses the same - ports Keystone wants): - - $ swift-init all stop - -2. Obtain and install a source copy of Keystone: - - $ git clone https://github.com/openstack/keystone.git ~/keystone - ... - $ cd ~/keystone && sudo python setup.py develop - ... - -3. Start up the Keystone service: - - $ cd ~/keystone/bin && ./keystone - Starting the Legacy Authentication component - Service API listening on 0.0.0.0:5000 - Admin API listening on 0.0.0.0:35357 - -4. In another window, edit the `~/keystone/keystone/test/sampledata.py` file, - find the `swift.publicinternets.com` text and replace it with the URL to - your Swift cluster using the following format (note that we're going to - change Swift to run on port 8888 later): - `http://127.0.0.1:8888/v1/AUTH_%tenant_id%` - -5. Create the sample data entries: - - $ cd ~/keystone/bin && ./sampledata - ... - -6. Reconfigure Swift's proxy server to use Keystone instead of TempAuth. - Here's an example `/etc/swift/proxy-server.conf`: - - [DEFAULT] - bind_port = 8888 - user = - - [pipeline:main] - pipeline = catch_errors cache keystone proxy-server - - [app:proxy-server] - use = egg:swift#proxy - account_autocreate = true - - [filter:keystone] - use = egg:keystone#tokenauth - auth_protocol = http - auth_host = 127.0.0.1 - auth_port = 35357 - admin_token = 999888777666 - delay_auth_decision = 0 - service_protocol = http - service_host = 127.0.0.1 - service_port = 8100 - service_pass = dTpw - - [filter:cache] - use = egg:swift#memcache - set log_name = cache - - [filter:catch_errors] - use = egg:swift#catch_errors - -7. Start Swift back up with the new configuration: - - $ swift-init main start - ... - -8. Use `swift` to check everything works (note: you currently have to create a - container or upload something as your first action to have the account - created; there's a Swift bug to be fixed soon): - - $ swift -A http://127.0.0.1:5000/v1.0 -U joeuser -K secrete post container - $ swift -A http://127.0.0.1:5000/v1.0 -U joeuser -K secrete stat -v - StorageURL: http://127.0.0.1:8888/v1/AUTH_1234 - Auth Token: 74ce1b05-e839-43b7-bd76-85ef178726c3 - Account: AUTH_1234 - Containers: 1 - Objects: 0 - Bytes: 0 - Accept-Ranges: bytes - X-Trans-Id: tx25c1a6969d8f4372b63912f411de3c3b - -**Note: Keystone currently allows any valid token to do anything with any -account.** - -But, it works as a demo! - -## LDAP Setup on a Mac - -Using macports: - - sudo port install openldap - -It appears the package `python-ldap` needs to be recompiled to work. So, -download it from: http://pypi.python.org/pypi/python-ldap/2.4.1 - -After unpacking, edit `setup.cfg` as shown below: - - library_dirs = /opt/local/lib - include_dirs = /opt/local/include /usr/include/sasl - -Then, run: - - python setup.py build - sudo python setup.py install - -# Relevant Standards and Technologies - -[Overlap of Identity Technologies](https://sites.google.com/site/oauthgoog/Overlap) - -Keystone could potentially integrate with: - - 1. [WebID](http://www.w3.org/2005/Incubator/webid/spec/) (See also [FOAF+SSL](http://www.w3.org/wiki/Foaf+ssl)) - 2. [OpenID](http://openid.net/) and/or [OpenIDConnect](http://openidconnect.com/) - 3. [OAUTH2](http://oauth.net/2/) - 4. [SAML](http://saml.xml.org/) diff --git a/README.rst b/README.rst new file mode 100644 index 0000000000..2d24892aeb --- /dev/null +++ b/README.rst @@ -0,0 +1,222 @@ +.. image:: http://term.ie/data/medium_ksl.png + :alt: Keystone + +.. toctree:: + :maxdepth 2 + +Keystone is an OpenStack project that provides Identity, Token, Catalog and +Policy services for use specifically by projects in the OpenStack family. + +Much of the design is precipitated from the expectation that the auth backends +for most deployments will actually be shims in front of existing user systems. + + +----------- +Development +----------- + +Building the Documentation +-------------------------- + +The documentation is all generated with Sphinx from within the docs directory. +To generate the full set of HTML documentation: + + cd docs + make autodoc + make html + make man + +the results are in the docs/build/html and docs/build/man directories +respectively. + +------------ +The Services +------------ + +Keystone is organized as a group of services exposed on one or many endpoints. +Many of these services are used in a combined fashion by the frontend, for +example an authenticate call will validate user/tenant credentials with the +Identity service and, upon success, create and return a token with the Token +service. + + +Identity +-------- + +The Identity service provides auth credential validation and data about Users, +Tenants and Roles, as well as any associated metadata. + +In the basic case all this data is managed by the service, allowing the service +to manage all the CRUD associated with the data. + +In other cases, this data is pulled, by varying degrees, from an authoritative +backend service. An example of this would be when backending on LDAP. See +`LDAP Backend` below for more details. + + +Token +----- + +The Token service validates and manages Tokens used for authenticating requests +once a user/tenant's credentials have already been verified. + + +Catalog +------- + +The Catalog service provides an endpoint registry used for endpoint discovery. + + +Policy +------ + +The Policy service provides a rule-based authorization engine and the +associated rule management interface. + + + +---------- +Data Model +---------- + +Keystone was designed from the ground up to be amenable to multiple styles of +backends and as such many of the methods and data types will happily accept +more data than they know what to do with and pass them on to a backend. + +There are a few main data types: + + * **User**: has account credentials, is associated with one or more tenants + * **Tenant**: unit of ownership in openstack, contains one or more users + * **Role**: a first-class piece of metadata associated with many user-tenant pairs. + * **Token**: identifying credential associated with a user or user and tenant + * **Extras**: bucket of key-value metadata associated with a user-tenant pair. + * **Rule**: describes a set of requirements for performing an action. + +While the general data model allows a many-to-many relationship between Users +and Tenants and a many-to-one relationship between Extras and User-Tenant pairs, +the actual backend implementations take varying levels of advantage of that +functionality. + + +KVS Backend +----------- + +A simple backend interface meant to be further backended on anything that can +support primary key lookups, the most trivial implementation being an in-memory +dict. + +Supports all features of the general data model. + + +PAM Backend +----------- + +Extra simple backend that uses the current system's PAM service to authenticate, +providing a one-to-one relationship between Users and Tenants with the `root` +User also having the 'admin' role. + + +Templated Backend +----------------- + +Largely designed for a common use case around service catalogs in the Keystone +project, a Catalog backend that simply expands pre-configured templates to +provide catalog data. + +Example paste.deploy config (uses $ instead of % to avoid ConfigParser's +interpolation):: + + [DEFAULT] + catalog.RegionOne.identity.publicURL = http://localhost:$(public_port)s/v2.0 + catalog.RegionOne.identity.adminURL = http://localhost:$(public_port)s/v2.0 + catalog.RegionOne.identity.internalURL = http://localhost:$(public_port)s/v2.0 + catalog.RegionOne.identity.name = 'Identity Service' + + +---------------- +Approach to CRUD +---------------- + +While it is expected that any "real" deployment at a large company will manage +their users, tenants and other metadata in their existing user systems, a +variety of CRUD operations are provided for the sake of development and testing. + +CRUD is treated as an extension or additional feature to the core feature set in +that it is not required that a backend support it. + + +---------------------------------- +Approach to Authorization (Policy) +---------------------------------- + +Various components in the system require that different actions are allowed +based on whether the user is authorized to perform that action. + +For the purposes of Keystone there are only a couple levels of +authorization being checked for: + + * Require that the performing user is considered an admin. + * Require that the performing user matches the user being referenced. + +Other systems wishing to use the policy engine will require additional styles +of checks and will possibly write completely custom backends. Backends included +in Keystone are: + + +Trivial True +------------ + +Allows all actions. + + +Simple Match +------------ + +Given a list of matches to check for, simply verify that the credentials +contain the matches. For example:: + + credentials = {'user_id': 'foo', 'is_admin': 1, 'roles': ['nova:netadmin']} + + # An admin only call: + policy_api.can_haz(('is_admin:1',), credentials) + + # An admin or owner call: + policy_api.can_haz(('is_admin:1', 'user_id:foo'), + credentials) + + # A netadmin call: + policy_api.can_haz(('roles:nova:netadmin',), + credentials) + + +Credentials are generally built from the user metadata in the 'extras' part +of the Identity API. So, adding a 'role' to the user just means adding the role +to the user metadata. + + +Capability RBAC +--------------- + +(Not yet implemented.) + +Another approach to authorization can be action-based, with a mapping of roles +to which capabilities are allowed for that role. For example:: + + credentials = {'user_id': 'foo', 'is_admin': 1, 'roles': ['nova:netadmin']} + + # add a policy + policy_api.add_policy('action:nova:add_network', ('roles:nova:netadmin',)) + + policy_api.can_haz(('action:nova:add_network',), credentials) + + +In the backend this would look up the policy for 'action:nova:add_network' and +then do what is effectively a 'Simple Match' style match against the creds. + + +----------- +Still To Do +----------- + + * LDAP backend. + * Diablo migration. diff --git a/TODO b/TODO new file mode 100644 index 0000000000..6eb577b119 --- /dev/null +++ b/TODO @@ -0,0 +1,4 @@ +- test validate token +- policy tests +- ec2 support + diff --git a/bin/keystone b/bin/keystone deleted file mode 100755 index df84036137..0000000000 --- a/bin/keystone +++ /dev/null @@ -1,84 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -""" This is only a convenience script. It starts two endpoints of Keystone; the -first one is a Service API server running on port 5000 (by default), and the -second one is an Admin API server running on port 35357 (by default). - -By default, keystone uses bind_host and bind_port to set its litening ports, -but since this script runs two endpoints, it uses the following options: - - Setting any of the Admin API values for bind host or port using the - admin_* entries in the config file. Specoific to this script only is the - -a/--admin-port option on the command-line (nothing else supports that). - - Setting any of the Service API values for bind host or port using the - service_* entries in the config file. - -""" - -import optparse -import os -import sys - -import keystone.tools.tracer # @UnusedImport # module runs on import -from keystone.common import config -from keystone.config import CONF -import keystone.server - -# If ../../keystone/__init__.py exists, add ../ to Python search path, so that -# it will override what happens to be installed in /usr/(local/)lib/python... -POSSIBLE_TOPDIR = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), - os.pardir, os.pardir)) -if os.path.exists(os.path.join(POSSIBLE_TOPDIR, 'keystone', '__init__.py')): - sys.path.insert(0, POSSIBLE_TOPDIR) - - -def get_options(): - # Initialize a parser for our configuration paramaters - # since we have special handling for the -a|--admin-port argument - parser = optparse.OptionParser() - common_group = config.add_common_options(parser) - config.add_log_options(parser) - - # Handle a special argument to support starting two endpoints - common_group.add_option( - '-a', '--admin-port', dest="admin_port", metavar="PORT", - help="specifies port for Admin API to listen on (default is 35357)") - - # Parse CLI arguments and merge with config - (options, args) = config.parse_options(parser) - return options - - -def main(): - # Get merged config and CLI options and admin-specific settings - options = get_options() - config_file = config.find_config_file(options, sys.argv[1:]) - CONF(config_files=[config_file]) - - # Start services - try: - # Load Service API Server - service = keystone.server.Server(name="Service API", - config_name='keystone-legacy-auth') - service.start(wait=False) - except RuntimeError, e: - sys.exit("ERROR: %s" % e) - - try: - # Get admin-specific settings - port = options.get('admin_port', None) - host = options.get('bind_host', None) - - # Load Admin API server - admin = keystone.server.Server(name='Admin API', config_name='admin') - admin.start(host=host, port=port, wait=True) - except RuntimeError, e: - sys.exit("ERROR: %s" % e) - finally: - service.stop() - - -if __name__ == '__main__': - main() diff --git a/bin/keystone-admin b/bin/keystone-admin deleted file mode 100755 index fa4b6ae1be..0000000000 --- a/bin/keystone-admin +++ /dev/null @@ -1,69 +0,0 @@ -#!/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. -# 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. - -""" -Keystone Identity Server - Admin API -""" - -import optparse -import os -import sys - -# If ../../keystone/__init__.py exists, add ../ to Python search path, so that -# it will override what happens to be installed in /usr/(local/)lib/python... -possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), - os.pardir, - os.pardir)) -if os.path.exists(os.path.join(possible_topdir, 'keystone', '__init__.py')): - sys.path.insert(0, possible_topdir) - -import keystone.tools.tracer # @UnusedImport # module runs on import -from keystone.common import config -from keystone.config import CONF -import keystone.server - - -def get_options(): - # Initialize a parser for our configuration paramaters - # since we have special handling for the -a|--admin-port argument - parser = optparse.OptionParser() - common_group = config.add_common_options(parser) - config.add_log_options(parser) - - # Parse CLI arguments and merge with config - (options, args) = config.parse_options(parser) - return options - - -def main(): - # Get merged config and CLI options and admin-specific settings - options = get_options() - config_file = config.find_config_file(options, sys.argv[1:]) - CONF(config_files=[config_file]) - try: - # Load Admin API server - admin = keystone.server.Server(name='Admin API', config_name='admin') - admin.start(wait=True) - except RuntimeError, e: - sys.exit("ERROR: %s" % e) - - -if __name__ == '__main__': - main() diff --git a/bin/keystone-all b/bin/keystone-all new file mode 100755 index 0000000000..7645a949db --- /dev/null +++ b/bin/keystone-all @@ -0,0 +1,71 @@ +#!/usr/bin/env python +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +import greenlet +import logging +import os +import sys + +# If ../../keystone/__init__.py exists, add ../ to Python search path, so that +# it will override what happens to be installed in /usr/(local/)lib/python... +possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), + os.pardir, + os.pardir)) +if os.path.exists(os.path.join(possible_topdir, + 'keystone-all', + '__init__.py')): + sys.path.insert(0, possible_topdir) + +from paste import deploy + +from keystone import config +from keystone.common import wsgi + + +CONF = config.CONF + + +def create_server(conf, name, port): + app = deploy.loadapp('config:%s' % conf, name=name) + return wsgi.Server(app, port) + + +def serve(*servers): + for server in servers: + logging.debug("starting server %s on port %s", server.application, + server.port) + server.start() + + for server in servers: + try: + server.wait() + except greenlet.GreenletExit: + pass + + +if __name__ == '__main__': + dev_conf = os.path.join(possible_topdir, + 'etc', + 'keystone.conf') + config_files = None + if os.path.exists(dev_conf): + config_files = [dev_conf] + + CONF(config_files=config_files) + + config.setup_logging(CONF) + + # Log the options used when starting if we're in debug mode... + if CONF.debug: + CONF.log_opt_values(logging.getLogger(CONF.prog), logging.DEBUG) + + options = deploy.appconfig('config:%s' % CONF.config_file[0]) + + servers = [] + servers.append(create_server(CONF.config_file[0], + 'admin', + int(options['admin_port']))) + servers.append(create_server(CONF.config_file[0], + 'main', + int(options['public_port']))) + serve(*servers) diff --git a/bin/keystone-auth b/bin/keystone-auth deleted file mode 100755 index 03a2df0c27..0000000000 --- a/bin/keystone-auth +++ /dev/null @@ -1,70 +0,0 @@ -#!/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. -# 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. - -""" -Keystone Identity Server - Service API -""" - -import optparse -import os -import sys - -# If ../../keystone/__init__.py exists, add ../ to Python search path, so that -# it will override what happens to be installed in /usr/(local/)lib/python... -possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), - os.pardir, - os.pardir)) -if os.path.exists(os.path.join(possible_topdir, 'keystone', '__init__.py')): - sys.path.insert(0, possible_topdir) - -import keystone.tools.tracer # @UnusedImport # module runs on import -from keystone.common import config -from keystone.config import CONF -import keystone.server - - -def get_options(): - # Initialize a parser for our configuration paramaters - # since we have special handling for the -a|--admin-port argument - parser = optparse.OptionParser() - common_group = config.add_common_options(parser) - config.add_log_options(parser) - - # Parse CLI arguments and merge with config - (options, args) = config.parse_options(parser) - return options - - -def main(): - # Get merged config and CLI options and admin-specific settings - options = get_options() - config_file = config.find_config_file(options, sys.argv[1:]) - CONF(config_files=[config_file]) - try: - # Load Service API server - server = keystone.server.Server(name='Service API', - config_name='keystone-legacy-auth') - server.start(wait=True) - except RuntimeError, e: - sys.exit("ERROR: %s" % e) - - -if __name__ == '__main__': - main() diff --git a/bin/keystone-control b/bin/keystone-control deleted file mode 100755 index ed92d383a5..0000000000 --- a/bin/keystone-control +++ /dev/null @@ -1,235 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright (c) 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. - -""" -Helper script for starting/stopping/reloading Keystone server programs. -Copied from Glance. Thanks for some of the code, Swifties ;) -""" - -from __future__ import with_statement - -import errno -import gettext -import os -import optparse -import resource -import signal -import sys -import time - -# If ../keystone/__init__.py exists, add ../ to Python search path, so that -# it will override what happens to be installed in /usr/(local/)lib/python... -possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), - os.pardir, - os.pardir)) -if os.path.exists(os.path.join(possible_topdir, 'keystone', '__init__.py')): - sys.path.insert(0, possible_topdir) - -gettext.install('keystone', unicode=1) - -import keystone.version -from keystone.common import config - -ALL_COMMANDS = ['start', 'stop', 'shutdown', 'restart', - 'reload', 'force-reload'] -ALL_SERVERS = ['keystone-auth', 'keystone-admin'] -GRACEFUL_SHUTDOWN_SERVERS = ['keystone-auth', 'keystone-admin'] -MAX_DESCRIPTORS = 32768 -MAX_MEMORY = (1024 * 1024 * 1024) * 2 # 2 GB -USAGE = """%prog [options] [CONFPATH] - -Where is one of: - - all, auth, admin - -And command is one of: - - start, stop, shutdown, restart, reload, force-reload - -And CONFPATH is the optional configuration file to use.""" - - -def pid_files(server, options): - pid_files = [] - if options['pid_file']: - if os.path.exists(os.path.abspath(options['pid_file'])): - pid_files = [os.path.abspath(options['pid_file'])] - else: - if os.path.exists('/var/run/keystone/%s.pid' % server): - pid_files = ['/var/run/keystone/%s.pid' % server] - for pid_file in pid_files: - pid = int(open(pid_file).read().strip()) - yield pid_file, pid - - -def do_start(server, options, args): - server_type = '-'.join(server.split('-')[:-1]) - - for pid_file, pid in pid_files(server, options): - if os.path.exists('/proc/%s' % pid): - print "%s appears to already be running: %s" % (server, pid_file) - return - else: - print "Removing stale pid file %s" % pid_file - os.unlink(pid_file) - - try: - resource.setrlimit(resource.RLIMIT_NOFILE, - (MAX_DESCRIPTORS, MAX_DESCRIPTORS)) - resource.setrlimit(resource.RLIMIT_DATA, - (MAX_MEMORY, MAX_MEMORY)) - except ValueError: - print "Unable to increase file descriptor limit. Running as non-root?" - os.environ['PYTHON_EGG_CACHE'] = '/tmp' - - def write_pid_file(pid_file, pid): - dir, file = os.path.split(pid_file) - if not os.path.exists(dir): - try: - os.makedirs(dir) - except OSError, err: - if err.errno == errno.EACCES: - sys.exit('Unable to create %s. Running as non-root?' - % dir) - fp = open(pid_file, 'w') - fp.write('%d\n' % pid) - fp.close() - - def launch(ini_file, pid_file): - args = [server, ini_file] - print 'Starting %s with %s' % (server, ini_file) - - pid = os.fork() - if pid == 0: - os.setsid() - with open(os.devnull, 'r+b') as nullfile: - for desc in (0, 1, 2): # close stdio - try: - os.dup2(nullfile.fileno(), desc) - except OSError: - pass - try: - os.execlp('%s' % server, server, ini_file) - except OSError, e: - sys.exit('unable to launch %s. Got error: %s' - % (server, "%s" % e)) - sys.exit(0) - else: - write_pid_file(pid_file, pid) - - if not options['pid_file']: - pid_file = '/var/run/keystone/%s.pid' % server - else: - pid_file = os.path.abspath(options['pid_file']) - conf_file = config.find_config_file(options, args) - if not conf_file: - sys.exit("Could not find any configuration file to use!") - launch_args = [(conf_file, pid_file)] - - # start all servers - for conf_file, pid_file in launch_args: - launch(conf_file, pid_file) - - -def do_stop(server, options, args, graceful=False): - if graceful and server in GRACEFUL_SHUTDOWN_SERVERS: - sig = signal.SIGHUP - else: - sig = signal.SIGTERM - - did_anything = False - pfiles = pid_files(server, options) - for pid_file, pid in pfiles: - did_anything = True - try: - print 'Stopping %s pid: %s signal: %s' % (server, pid, sig) - os.kill(pid, sig) - except OSError: - print "Process %d not running" % pid - try: - os.unlink(pid_file) - except OSError: - pass - for pid_file, pid in pfiles: - for _junk in xrange(150): # 15 seconds - if not os.path.exists('/proc/%s' % pid): - break - time.sleep(0.1) - else: - print 'Waited 15 seconds for pid %s (%s) to die; giving up' % \ - (pid, pid_file) - if not did_anything: - print 'No %s running' % server - - -if __name__ == '__main__': - oparser = optparse.OptionParser(usage=USAGE, version='%%prog %s' - % keystone.version.version()) - oparser.add_option('--pid-file', default=None, metavar="PATH", - help="File to use as pid file. Default: " - "/var/run/keystone/$server.pid") - config.add_common_options(oparser) - (options, args) = config.parse_options(oparser) - - if len(args) < 2: - oparser.print_usage() - sys.exit(1) - - server = args.pop(0).lower() - if server == 'all': - servers = ALL_SERVERS - else: - if not server.startswith('keystone-'): - server = 'keystone-%s' % server - if server not in ALL_SERVERS: - server_list = ", ".join([s.replace('keystone-', '') - for s in ALL_SERVERS]) - msg = ("Unknown server '%(server)s' specified. Please specify " - "all, or one of the servers: %(server_list)s" % locals()) - sys.exit(msg) - servers = [server] - - command = args.pop(0).lower() - if command not in ALL_COMMANDS: - command_list = ", ".join(ALL_COMMANDS) - msg = ("Unknown command %(command)s specified. Please specify a " - "command in this list: %(command_list)s" % locals()) - sys.exit(msg) - - if command == 'start': - for server in servers: - do_start(server, options, args) - - if command == 'stop': - for server in servers: - do_stop(server, options, args) - - if command == 'shutdown': - for server in servers: - do_stop(server, options, args, graceful=True) - - if command == 'restart': - for server in servers: - do_stop(server, options, args) - for server in servers: - do_start(server, options, args) - - if command == 'reload' or command == 'force-reload': - for server in servers: - do_stop(server, options, args, graceful=True) - do_start(server, options, args) diff --git a/bin/keystone-import b/bin/keystone-import deleted file mode 100755 index 514f276428..0000000000 --- a/bin/keystone-import +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env python - -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright (C) 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. - -# This file is to read a export file from Nova that will import users, -# tenants and EC2 credentials -# The file should be in the keystone-manage format - -import os -import sys -import shlex - -# If ../../keystone/__init__.py exists, add ../ to Python search path, so that -# it will override what happens to be installed in /usr/(local/)lib/python... -possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), - os.pardir, - os.pardir)) -if os.path.exists(os.path.join(possible_topdir, 'keystone', '__init__.py')): - sys.path.insert(0, possible_topdir) - -import keystone.manage - -with open(sys.argv[1], 'r') as line: - try: - keystone.manage.main(shlex.split(line)) - except Exception as exc: - # Main prints all of the errors we need - sys.exit(1) diff --git a/bin/keystone-manage b/bin/keystone-manage index be0a1036a4..e551c47e2f 100755 --- a/bin/keystone-manage +++ b/bin/keystone-manage @@ -2,35 +2,27 @@ import os import sys + # If ../../keystone/__init__.py exists, add ../ to Python search path, so that # it will override what happens to be installed in /usr/(local/)lib/python... possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), os.pardir, os.pardir)) -if os.path.exists(os.path.join(possible_topdir, 'keystone', '__init__.py')): +if os.path.exists(os.path.join(possible_topdir, + 'keystone', + '__init__.py')): sys.path.insert(0, possible_topdir) -import keystone.manage -import keystone.manage2 -import keystone.tools.tracer # @UnusedImport # module runs on import + +from keystone import cli if __name__ == '__main__': - args = sys.argv[1:] - while True: - if len(args) > 1 and args[0] in keystone.manage.OBJECTS: - # the args look like the old 'subject verb' (e.g. 'user add') - # (this module is pending deprecation) - keystone.manage.main() - break - elif len(args) > 2 and args[0] == '-c': - # Remove -c and try again - del args[0:2] - elif len(args) > 1 and args[0] == '-d': - # Remove -d and try again - del args[0] - else: - # calls that don't start with a 'subject' go to the new impl - # which uses a 'verb_subject' convention (e.g. 'add_user') - keystone.manage2.main() - break + dev_conf = os.path.join(possible_topdir, + 'etc', + 'keystone.conf') + config_files = None + if os.path.exists(dev_conf): + config_files = [dev_conf] + + cli.main(argv=sys.argv, config_files=config_files) diff --git a/bin/sampledata b/bin/sampledata deleted file mode 100755 index 41acfd2342..0000000000 --- a/bin/sampledata +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -# If ../../keystone/__init__.py exists, add ../ to Python search path, so that -# it will override what happens to be installed in /usr/(local/)lib/python... -possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), - os.pardir, - os.pardir)) -if os.path.exists(os.path.join(possible_topdir, 'keystone', '__init__.py')): - sys.path.insert(0, possible_topdir) - -import keystone.test.sampledata - -if __name__ == '__main__': - keystone.test.sampledata.main() diff --git a/doc/Makefile b/doc/Makefile deleted file mode 100644 index b63e30032c..0000000000 --- a/doc/Makefile +++ /dev/null @@ -1,96 +0,0 @@ -# Makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -SPHINXSOURCE = source -PAPER = -BUILDDIR = build - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(SPHINXSOURCE) - -.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest - -.DEFAULT_GOAL = html - -help: - @echo "Please use \`make ' where is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - -clean: - -rm -rf $(BUILDDIR)/* - if [ -f .autogenerated ] ; then \ - cat .autogenerated | xargs rm ; \ - rm .autogenerated ; \ - fi - -html: - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/nova.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/nova.qhc" - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ - "run these through (pdf)latex." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." diff --git a/doc/README.rst b/doc/README.rst deleted file mode 100644 index 4f03350886..0000000000 --- a/doc/README.rst +++ /dev/null @@ -1,38 +0,0 @@ -================================== -Building Contributor Documentation -================================== - -This documentation is written by contributors, for contributors. - -The source is maintained in the `doc/source` folder using -`reStructuredText`_ and built by `Sphinx`_ (a dependency from `tools/pip-requires`). - -.. _reStructuredText: http://docutils.sourceforge.net/rst.html -.. _Sphinx: http://sphinx.pocoo.org/ - -Building automatically -====================== - -From the project root, just type:: - - $ python setup.py build_sphinx - -Building manually -================= - -#. Generate the code.rst file so that Sphinx will pull in our docstrings:: - - $ python doc/generate_autodoc_index.py - -#. Use `sphinx-build` to produce the docs in HTML:: - - $ sphinx-build -b html doc/source/ build/sphinx/html/ - -#. Similarly, build the man pages (optional):: - - $ sphinx-build -b man doc/source/ build/sphinx/man/ - -After building -============== - -Navigate to the `build/sphinx/html` directory to browse generated the HTML docs. diff --git a/doc/design/flow_diagram.pdf b/doc/design/flow_diagram.pdf deleted file mode 100644 index 256086d6a9..0000000000 --- a/doc/design/flow_diagram.pdf +++ /dev/null @@ -1,9495 +0,0 @@ -%PDF-1.4 -%âãÏÓ - -1 0 obj - << - /Title () - /Author () - /Subject () - /Keywords () - /Creator (FreeHEP Graphics2D Driver) - /Producer (org.freehep.graphicsio.pdf.PDFGraphics2D Revision: 10516 ) - /CreationDate (D:20110523220630-05'00') - /ModDate (D:20110523220630-05'00') - /Trapped /False - >> -endobj - -2 0 obj - << - /Type /Catalog - /Pages 3 0 R - /Outlines 4 0 R - /PageMode /UseOutlines - /ViewerPreferences 5 0 R - /OpenAction [6 0 R /Fit] - >> -endobj - -5 0 obj - << - /FitWindow true - /CenterWindow false - >> -endobj - -6 0 obj - << - /Parent 3 0 R - /Type /Page - /Contents 7 0 R - >> -endobj - -7 0 obj - << - /Length 8 0 R - >> -stream -.93277 0.0000 0.0000 -.93277 28.303 575.00 cm -q -0.0000 0.0000 m -842.00 0.0000 l -842.00 595.00 l -0.0000 595.00 l -h -W -n -q -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -1.0000 0.0000 0.0000 1.0000 0.0000 0.0000 cm -Q -q -1.0000 0.0000 0.0000 1.0000 0.0000 0.0000 cm -0.0000 0.0000 m -0.0000 595.00 l -842.00 595.00 l -842.00 0.0000 l -h -W -n -q -.83948 0.0000 0.0000 .83948 0.0000 0.0000 cm -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -0.0000 0.0000 m -1003.0 0.0000 l -1003.0 709.00 l -0.0000 709.00 l -0.0000 0.0000 l -h -f -1.0000 0.0000 0.0000 1.0000 0.0000 0.0000 cm -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -0 J -1 j -1.0000 M -[ 1.0000 2.0000] 0.0000 d -66.500 44.500 m -66.500 54.500 l -S -66.500 44.500 m -66.500 54.500 l -S -[] 0.0000 d -/Alpha1 gs -.93333 .93333 .93333 rg -.93333 .93333 .93333 RG -62.000 54.000 m -70.000 54.000 l -70.000 669.00 l -62.000 669.00 l -62.000 54.000 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -62.500 54.500 m -70.500 54.500 l -70.500 669.50 l -62.500 669.50 l -62.500 54.500 l -h -S -[ 1.0000 2.0000] 0.0000 d -66.500 669.50 m -66.500 669.50 l -S -512.50 44.500 m -512.50 102.50 l -S -512.50 44.500 m -512.50 102.50 l -S -[] 0.0000 d -/Alpha1 gs -.93333 .93333 .93333 rg -.93333 .93333 .93333 RG -508.00 102.00 m -516.00 102.00 l -516.00 120.00 l -508.00 120.00 l -508.00 102.00 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -508.50 102.50 m -516.50 102.50 l -516.50 120.50 l -508.50 120.50 l -508.50 102.50 l -h -S -[ 1.0000 2.0000] 0.0000 d -512.50 120.50 m -512.50 186.50 l -S -[] 0.0000 d -/Alpha1 gs -.93333 .93333 .93333 rg -.93333 .93333 .93333 RG -508.00 186.00 m -516.00 186.00 l -516.00 204.00 l -508.00 204.00 l -508.00 186.00 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -508.50 186.50 m -516.50 186.50 l -516.50 204.50 l -508.50 204.50 l -508.50 186.50 l -h -S -[ 1.0000 2.0000] 0.0000 d -512.50 204.50 m -512.50 270.50 l -S -[] 0.0000 d -/Alpha1 gs -.93333 .93333 .93333 rg -.93333 .93333 .93333 RG -508.00 270.00 m -516.00 270.00 l -516.00 288.00 l -508.00 288.00 l -508.00 270.00 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -508.50 270.50 m -516.50 270.50 l -516.50 288.50 l -508.50 288.50 l -508.50 270.50 l -h -S -[ 1.0000 2.0000] 0.0000 d -512.50 288.50 m -512.50 442.50 l -S -[] 0.0000 d -/Alpha1 gs -.93333 .93333 .93333 rg -.93333 .93333 .93333 RG -508.00 442.00 m -516.00 442.00 l -516.00 460.00 l -508.00 460.00 l -508.00 442.00 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -508.50 442.50 m -516.50 442.50 l -516.50 460.50 l -508.50 460.50 l -508.50 442.50 l -h -S -[ 1.0000 2.0000] 0.0000 d -512.50 460.50 m -512.50 669.50 l -S -691.50 44.500 m -691.50 374.50 l -S -691.50 44.500 m -691.50 374.50 l -S -[] 0.0000 d -/Alpha1 gs -.93333 .93333 .93333 rg -.93333 .93333 .93333 RG -687.00 374.00 m -695.00 374.00 l -695.00 669.00 l -687.00 669.00 l -687.00 374.00 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -687.50 374.50 m -695.50 374.50 l -695.50 669.50 l -687.50 669.50 l -687.50 374.50 l -h -S -[ 1.0000 2.0000] 0.0000 d -691.50 669.50 m -691.50 669.50 l -S -941.50 44.500 m -941.50 586.50 l -S -941.50 44.500 m -941.50 586.50 l -S -[] 0.0000 d -/Alpha1 gs -.93333 .93333 .93333 rg -.93333 .93333 .93333 RG -937.00 586.00 m -945.00 586.00 l -945.00 613.00 l -937.00 613.00 l -937.00 586.00 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -937.50 586.50 m -945.50 586.50 l -945.50 613.50 l -937.50 613.50 l -937.50 586.50 l -h -S -[ 1.0000 2.0000] 0.0000 d -941.50 613.50 m -941.50 669.50 l -S -81.818 350.79 m -81.045 351.03 80.383 351.15 79.832 351.15 c -78.895 351.15 78.130 350.83 77.538 350.21 c -76.946 349.59 76.650 348.78 76.650 347.79 c -76.650 346.82 76.911 346.03 77.433 345.42 c -77.954 344.80 78.621 344.49 79.434 344.49 c -80.203 344.49 80.798 344.76 81.218 345.31 c -81.638 345.86 81.848 346.63 81.848 347.64 c -81.842 348.00 l -77.828 348.00 l -77.996 349.51 78.736 350.27 80.049 350.27 c -80.529 350.27 81.119 350.14 81.818 349.88 c -h -77.881 347.13 m -80.688 347.13 l -80.688 345.95 80.246 345.36 79.363 345.36 c -78.477 345.36 77.982 345.95 77.881 347.13 c -h -83.840 351.00 m -83.840 344.64 l -84.994 344.64 l -84.994 345.83 l -85.604 344.94 86.350 344.50 87.232 344.50 c -87.783 344.50 88.223 344.67 88.551 345.02 c -88.879 345.37 89.043 345.84 89.043 346.43 c -89.043 351.00 l -87.889 351.00 l -87.889 346.80 l -87.889 346.33 87.819 346.00 87.681 345.79 c -87.542 345.59 87.312 345.49 86.992 345.49 c -86.285 345.49 85.619 345.96 84.994 346.88 c -84.994 351.00 l -h -95.371 351.00 m -95.371 349.80 l -94.902 350.70 94.195 351.15 93.250 351.15 c -92.484 351.15 91.882 350.87 91.442 350.31 c -91.003 349.75 90.783 348.99 90.783 348.02 c -90.783 346.96 91.032 346.11 91.530 345.46 c -92.028 344.82 92.684 344.50 93.496 344.50 c -94.250 344.50 94.875 344.79 95.371 345.36 c -95.371 341.75 l -96.531 341.75 l -96.531 351.00 l -h -95.371 346.15 m -94.773 345.63 94.207 345.36 93.672 345.36 c -92.566 345.36 92.014 346.21 92.014 347.90 c -92.014 349.39 92.506 350.13 93.490 350.13 c -94.131 350.13 94.758 349.78 95.371 349.08 c -h -98.840 353.31 m -98.840 344.64 l -99.994 344.64 l -99.994 345.83 l -100.47 344.94 101.18 344.50 102.12 344.50 c -102.89 344.50 103.49 344.78 103.93 345.33 c -104.37 345.89 104.59 346.66 104.59 347.62 c -104.59 348.68 104.34 349.53 103.84 350.18 c -103.34 350.82 102.69 351.15 101.88 351.15 c -101.12 351.15 100.49 350.86 99.994 350.28 c -99.994 353.31 l -h -99.994 349.48 m -100.59 350.01 101.15 350.28 101.69 350.28 c -102.80 350.28 103.36 349.43 103.36 347.74 c -103.36 346.25 102.87 345.50 101.88 345.50 c -101.24 345.50 100.61 345.85 99.994 346.55 c -h -108.88 351.15 m -107.97 351.15 107.25 350.84 106.70 350.24 c -106.16 349.64 105.89 348.83 105.89 347.82 c -105.89 346.79 106.16 345.99 106.71 345.39 c -107.25 344.79 107.99 344.50 108.92 344.50 c -109.86 344.50 110.60 344.79 111.14 345.39 c -111.69 345.99 111.96 346.79 111.96 347.81 c -111.96 348.85 111.69 349.66 111.14 350.26 c -110.59 350.85 109.84 351.15 108.88 351.15 c -h -108.90 350.28 m -110.12 350.28 110.73 349.46 110.73 347.81 c -110.73 346.18 110.13 345.36 108.92 345.36 c -107.72 345.36 107.12 346.18 107.12 347.82 c -107.12 349.46 107.71 350.28 108.90 350.28 c -h -113.76 351.00 m -113.76 344.64 l -114.92 344.64 l -114.92 351.00 l -h -113.76 343.48 m -113.76 342.33 l -114.92 342.33 l -114.92 343.48 l -h -117.23 351.00 m -117.23 344.64 l -118.39 344.64 l -118.39 345.83 l -119.00 344.94 119.74 344.50 120.62 344.50 c -121.18 344.50 121.62 344.67 121.94 345.02 c -122.27 345.37 122.44 345.84 122.44 346.43 c -122.44 351.00 l -121.28 351.00 l -121.28 346.80 l -121.28 346.33 121.21 346.00 121.07 345.79 c -120.93 345.59 120.71 345.49 120.38 345.49 c -119.68 345.49 119.01 345.96 118.39 346.88 c -118.39 351.00 l -h -126.59 351.15 m -126.00 351.15 125.55 350.98 125.22 350.64 c -124.89 350.31 124.73 349.84 124.73 349.24 c -124.73 345.50 l -123.93 345.50 l -123.93 344.64 l -124.73 344.64 l -124.73 343.48 l -125.88 343.37 l -125.88 344.64 l -127.54 344.64 l -127.54 345.50 l -125.88 345.50 l -125.88 349.03 l -125.88 349.86 126.24 350.28 126.96 350.28 c -127.11 350.28 127.30 350.25 127.52 350.20 c -127.52 351.00 l -127.16 351.10 126.85 351.15 126.59 351.15 c -h -129.31 349.05 m -129.31 348.18 l -136.25 348.18 l -136.25 349.05 l -h -129.31 346.88 m -129.31 346.01 l -136.25 346.01 l -136.25 346.88 l -h -140.39 351.15 m -139.87 351.15 139.23 351.02 138.47 350.78 c -138.47 349.72 l -139.23 350.09 139.88 350.28 140.44 350.28 c -140.77 350.28 141.05 350.19 141.27 350.01 c -141.49 349.83 141.60 349.61 141.60 349.34 c -141.60 348.94 141.29 348.62 140.68 348.36 c -140.00 348.07 l -139.01 347.66 138.51 347.06 138.51 346.28 c -138.51 345.73 138.70 345.29 139.10 344.97 c -139.49 344.66 140.03 344.50 140.71 344.50 c -141.07 344.50 141.51 344.54 142.03 344.64 c -142.27 344.69 l -142.27 345.65 l -141.62 345.46 141.11 345.36 140.73 345.36 c -139.99 345.36 139.62 345.63 139.62 346.17 c -139.62 346.52 139.90 346.81 140.46 347.05 c -141.02 347.29 l -141.65 347.55 142.10 347.83 142.36 348.13 c -142.62 348.42 142.75 348.79 142.75 349.23 c -142.75 349.79 142.53 350.25 142.09 350.61 c -141.65 350.97 141.08 351.15 140.39 351.15 c -h -149.49 350.79 m -148.71 351.03 148.05 351.15 147.50 351.15 c -146.56 351.15 145.80 350.83 145.21 350.21 c -144.62 349.59 144.32 348.78 144.32 347.79 c -144.32 346.82 144.58 346.03 145.10 345.42 c -145.62 344.80 146.29 344.49 147.10 344.49 c -147.87 344.49 148.47 344.76 148.89 345.31 c -149.31 345.86 149.52 346.63 149.52 347.64 c -149.51 348.00 l -145.50 348.00 l -145.67 349.51 146.41 350.27 147.72 350.27 c -148.20 350.27 148.79 350.14 149.49 349.88 c -h -145.55 347.13 m -148.36 347.13 l -148.36 345.95 147.92 345.36 147.03 345.36 c -146.15 345.36 145.65 345.95 145.55 347.13 c -h -151.51 351.00 m -151.51 344.64 l -152.66 344.64 l -152.66 345.83 l -153.12 344.94 153.79 344.50 154.66 344.50 c -154.77 344.50 154.90 344.51 155.03 344.53 c -155.03 345.60 l -154.83 345.54 154.65 345.50 154.50 345.50 c -153.77 345.50 153.16 345.94 152.66 346.80 c -152.66 351.00 l -h -157.74 351.00 m -155.37 344.64 l -156.53 344.64 l -158.38 349.59 l -160.33 344.64 l -161.41 344.64 l -158.89 351.00 l -h -162.63 351.00 m -162.63 344.64 l -163.79 344.64 l -163.79 351.00 l -h -162.63 343.48 m -162.63 342.33 l -163.79 342.33 l -163.79 343.48 l -h -168.58 351.15 m -167.72 351.15 167.01 350.83 166.45 350.19 c -165.88 349.55 165.60 348.75 165.60 347.78 c -165.60 346.75 165.88 345.94 166.44 345.36 c -167.00 344.79 167.78 344.50 168.78 344.50 c -169.28 344.50 169.83 344.56 170.45 344.70 c -170.45 345.67 l -169.79 345.48 169.26 345.38 168.85 345.38 c -168.26 345.38 167.79 345.60 167.43 346.05 c -167.08 346.49 166.90 347.08 166.90 347.82 c -166.90 348.53 167.08 349.11 167.45 349.55 c -167.81 349.99 168.29 350.21 168.89 350.21 c -169.42 350.21 169.96 350.08 170.52 349.81 c -170.52 350.81 l -169.77 351.03 169.13 351.15 168.58 351.15 c -h -176.91 350.79 m -176.14 351.03 175.47 351.15 174.92 351.15 c -173.99 351.15 173.22 350.83 172.63 350.21 c -172.04 349.59 171.74 348.78 171.74 347.79 c -171.74 346.82 172.00 346.03 172.52 345.42 c -173.05 344.80 173.71 344.49 174.53 344.49 c -175.29 344.49 175.89 344.76 176.31 345.31 c -176.73 345.86 176.94 346.63 176.94 347.64 c -176.93 348.00 l -172.92 348.00 l -173.09 349.51 173.83 350.27 175.14 350.27 c -175.62 350.27 176.21 350.14 176.91 349.88 c -h -172.97 347.13 m -175.78 347.13 l -175.78 345.95 175.34 345.36 174.46 345.36 c -173.57 345.36 173.07 345.95 172.97 347.13 c -h -182.61 351.22 m -181.26 351.22 180.22 350.82 179.49 350.03 c -178.75 349.24 178.39 348.12 178.39 346.67 c -178.39 345.22 178.76 344.10 179.51 343.31 c -180.26 342.51 181.31 342.11 182.67 342.11 c -183.45 342.11 184.36 342.24 185.40 342.49 c -185.40 343.65 l -184.21 343.24 183.30 343.03 182.65 343.03 c -181.71 343.03 180.98 343.35 180.47 343.99 c -179.95 344.62 179.69 345.52 179.69 346.68 c -179.69 347.79 179.97 348.66 180.52 349.30 c -181.07 349.94 181.82 350.26 182.78 350.26 c -183.60 350.26 184.47 350.00 185.41 349.50 c -185.41 350.55 l -184.56 351.00 183.62 351.22 182.61 351.22 c -h -190.52 350.19 m -189.82 350.83 189.16 351.15 188.52 351.15 c -187.99 351.15 187.55 350.98 187.21 350.65 c -186.86 350.32 186.68 349.90 186.68 349.40 c -186.68 348.71 186.98 348.17 187.56 347.80 c -188.14 347.42 188.98 347.24 190.07 347.24 c -190.35 347.24 l -190.35 346.47 l -190.35 345.73 189.97 345.36 189.21 345.36 c -188.60 345.36 187.94 345.55 187.23 345.93 c -187.23 344.97 l -188.01 344.65 188.74 344.50 189.42 344.50 c -190.13 344.50 190.66 344.66 190.99 344.98 c -191.33 345.30 191.50 345.79 191.50 346.47 c -191.50 349.35 l -191.50 350.01 191.70 350.34 192.11 350.34 c -192.16 350.34 192.23 350.34 192.33 350.32 c -192.41 350.96 l -192.15 351.08 191.86 351.15 191.55 351.15 c -191.01 351.15 190.66 350.83 190.52 350.19 c -h -190.35 349.56 m -190.35 347.92 l -189.96 347.91 l -189.33 347.91 188.81 348.03 188.42 348.27 c -188.03 348.51 187.84 348.82 187.84 349.21 c -187.84 349.49 187.94 349.72 188.13 349.92 c -188.33 350.11 188.56 350.20 188.85 350.20 c -189.33 350.20 189.83 349.99 190.35 349.56 c -h -195.77 351.15 m -195.19 351.15 194.73 350.98 194.40 350.64 c -194.07 350.31 193.91 349.84 193.91 349.24 c -193.91 345.50 l -193.11 345.50 l -193.11 344.64 l -193.91 344.64 l -193.91 343.48 l -195.06 343.37 l -195.06 344.64 l -196.73 344.64 l -196.73 345.50 l -195.06 345.50 l -195.06 349.03 l -195.06 349.86 195.42 350.28 196.14 350.28 c -196.29 350.28 196.48 350.25 196.70 350.20 c -196.70 351.00 l -196.34 351.10 196.03 351.15 195.77 351.15 c -h -201.63 350.19 m -200.94 350.83 200.27 351.15 199.63 351.15 c -199.11 351.15 198.67 350.98 198.32 350.65 c -197.97 350.32 197.80 349.90 197.80 349.40 c -197.80 348.71 198.09 348.17 198.67 347.80 c -199.26 347.42 200.10 347.24 201.19 347.24 c -201.46 347.24 l -201.46 346.47 l -201.46 345.73 201.08 345.36 200.32 345.36 c -199.71 345.36 199.06 345.55 198.35 345.93 c -198.35 344.97 l -199.13 344.65 199.86 344.50 200.54 344.50 c -201.25 344.50 201.77 344.66 202.11 344.98 c -202.45 345.30 202.62 345.79 202.62 346.47 c -202.62 349.35 l -202.62 350.01 202.82 350.34 203.22 350.34 c -203.28 350.34 203.35 350.34 203.45 350.32 c -203.53 350.96 l -203.27 351.08 202.98 351.15 202.66 351.15 c -202.12 351.15 201.78 350.83 201.63 350.19 c -h -201.46 349.56 m -201.46 347.92 l -201.07 347.91 l -200.44 347.91 199.93 348.03 199.54 348.27 c -199.15 348.51 198.95 348.82 198.95 349.21 c -198.95 349.49 199.05 349.72 199.25 349.92 c -199.44 350.11 199.68 350.20 199.96 350.20 c -200.44 350.20 200.94 349.99 201.46 349.56 c -h -204.98 351.00 m -204.98 341.75 l -206.13 341.75 l -206.13 351.00 l -h -210.94 351.15 m -210.03 351.15 209.30 350.84 208.76 350.24 c -208.21 349.64 207.94 348.83 207.94 347.82 c -207.94 346.79 208.21 345.99 208.76 345.39 c -209.30 344.79 210.04 344.50 210.98 344.50 c -211.91 344.50 212.65 344.79 213.19 345.39 c -213.74 345.99 214.01 346.79 214.01 347.81 c -214.01 348.85 213.74 349.66 213.19 350.26 c -212.64 350.85 211.89 351.15 210.94 351.15 c -h -210.95 350.28 m -212.18 350.28 212.79 349.46 212.79 347.81 c -212.79 346.18 212.18 345.36 210.98 345.36 c -209.77 345.36 209.17 346.18 209.17 347.82 c -209.17 349.46 209.77 350.28 210.95 350.28 c -h -215.84 353.12 m -215.97 352.11 l -216.64 352.43 217.30 352.59 217.95 352.59 c -219.25 352.59 219.90 351.90 219.90 350.52 c -219.90 349.52 l -219.47 350.41 218.78 350.85 217.80 350.85 c -217.04 350.85 216.44 350.58 215.99 350.02 c -215.54 349.47 215.31 348.72 215.31 347.78 c -215.31 346.81 215.57 346.02 216.08 345.41 c -216.59 344.80 217.25 344.50 218.07 344.50 c -218.78 344.50 219.39 344.79 219.90 345.36 c -219.90 344.64 l -221.06 344.64 l -221.06 349.27 l -221.06 350.26 221.01 351.00 220.91 351.48 c -220.80 351.96 220.61 352.35 220.32 352.65 c -219.82 353.19 219.04 353.46 217.97 353.46 c -217.23 353.46 216.52 353.34 215.84 353.12 c -h -219.90 348.80 m -219.90 346.15 l -219.39 345.63 218.84 345.36 218.24 345.36 c -217.71 345.36 217.29 345.58 216.99 346.00 c -216.69 346.43 216.54 347.01 216.54 347.75 c -216.54 349.15 217.03 349.85 218.01 349.85 c -218.68 349.85 219.31 349.50 219.90 348.80 c -h -223.30 352.73 m -223.30 341.75 l -225.61 341.75 l -225.61 342.62 l -224.31 342.62 l -224.31 351.87 l -225.61 351.87 l -225.61 352.73 l -h -226.98 344.93 m -226.70 341.75 l -228.14 341.75 l -227.85 344.93 l -h -232.43 351.15 m -231.57 351.15 230.86 350.83 230.29 350.19 c -229.73 349.55 229.45 348.75 229.45 347.78 c -229.45 346.75 229.73 345.94 230.29 345.36 c -230.85 344.79 231.63 344.50 232.63 344.50 c -233.13 344.50 233.68 344.56 234.30 344.70 c -234.30 345.67 l -233.64 345.48 233.11 345.38 232.70 345.38 c -232.11 345.38 231.64 345.60 231.28 346.05 c -230.92 346.49 230.75 347.08 230.75 347.82 c -230.75 348.53 230.93 349.11 231.30 349.55 c -231.66 349.99 232.14 350.21 232.74 350.21 c -233.27 350.21 233.81 350.08 234.37 349.81 c -234.37 350.81 l -233.62 351.03 232.98 351.15 232.43 351.15 c -h -238.59 351.15 m -237.68 351.15 236.95 350.84 236.41 350.24 c -235.86 349.64 235.59 348.83 235.59 347.82 c -235.59 346.79 235.86 345.99 236.41 345.39 c -236.95 344.79 237.69 344.50 238.63 344.50 c -239.56 344.50 240.30 344.79 240.84 345.39 c -241.39 345.99 241.66 346.79 241.66 347.81 c -241.66 348.85 241.39 349.66 240.84 350.26 c -240.29 350.85 239.54 351.15 238.59 351.15 c -h -238.60 350.28 m -239.83 350.28 240.44 349.46 240.44 347.81 c -240.44 346.18 239.83 345.36 238.63 345.36 c -237.42 345.36 236.82 346.18 236.82 347.82 c -236.82 349.46 237.42 350.28 238.60 350.28 c -h -243.47 351.00 m -243.47 344.64 l -244.62 344.64 l -244.62 345.83 l -245.18 344.94 245.91 344.50 246.79 344.50 c -247.64 344.50 248.22 344.94 248.53 345.83 c -249.08 344.94 249.79 344.49 250.66 344.49 c -251.22 344.49 251.66 344.66 251.97 344.99 c -252.28 345.32 252.43 345.78 252.43 346.37 c -252.43 351.00 l -251.27 351.00 l -251.27 346.55 l -251.27 345.83 250.98 345.46 250.41 345.46 c -249.81 345.46 249.19 345.89 248.53 346.73 c -248.53 351.00 l -247.37 351.00 l -247.37 346.55 l -247.37 345.82 247.08 345.46 246.49 345.46 c -245.91 345.46 245.29 345.88 244.62 346.73 c -244.62 351.00 l -h -254.67 353.31 m -254.67 344.64 l -255.82 344.64 l -255.82 345.83 l -256.30 344.94 257.01 344.50 257.95 344.50 c -258.72 344.50 259.32 344.78 259.76 345.33 c -260.20 345.89 260.42 346.66 260.42 347.62 c -260.42 348.68 260.17 349.53 259.67 350.18 c -259.17 350.82 258.52 351.15 257.71 351.15 c -256.95 351.15 256.32 350.86 255.82 350.28 c -255.82 353.31 l -h -255.82 349.48 m -256.42 350.01 256.98 350.28 257.52 350.28 c -258.63 350.28 259.19 349.43 259.19 347.74 c -259.19 346.25 258.70 345.50 257.71 345.50 c -257.07 345.50 256.44 345.85 255.82 346.55 c -h -266.20 351.00 m -266.20 349.80 l -265.59 350.70 264.84 351.15 263.97 351.15 c -263.41 351.15 262.97 350.97 262.64 350.62 c -262.32 350.27 262.15 349.80 262.15 349.21 c -262.15 344.64 l -263.31 344.64 l -263.31 348.83 l -263.31 349.31 263.38 349.65 263.51 349.85 c -263.65 350.05 263.88 350.15 264.21 350.15 c -264.91 350.15 265.58 349.69 266.20 348.76 c -266.20 344.64 l -267.36 344.64 l -267.36 351.00 l -h -271.58 351.15 m -270.99 351.15 270.54 350.98 270.21 350.64 c -269.88 350.31 269.72 349.84 269.72 349.24 c -269.72 345.50 l -268.92 345.50 l -268.92 344.64 l -269.72 344.64 l -269.72 343.48 l -270.87 343.37 l -270.87 344.64 l -272.54 344.64 l -272.54 345.50 l -270.87 345.50 l -270.87 349.03 l -270.87 349.86 271.23 350.28 271.95 350.28 c -272.10 350.28 272.29 350.25 272.51 350.20 c -272.51 351.00 l -272.15 351.10 271.84 351.15 271.58 351.15 c -h -278.82 350.79 m -278.05 351.03 277.39 351.15 276.84 351.15 c -275.90 351.15 275.13 350.83 274.54 350.21 c -273.95 349.59 273.65 348.78 273.65 347.79 c -273.65 346.82 273.92 346.03 274.44 345.42 c -274.96 344.80 275.62 344.49 276.44 344.49 c -277.21 344.49 277.80 344.76 278.22 345.31 c -278.64 345.86 278.85 346.63 278.85 347.64 c -278.85 348.00 l -274.83 348.00 l -275.00 349.51 275.74 350.27 277.05 350.27 c -277.53 350.27 278.12 350.14 278.82 349.88 c -h -274.88 347.13 m -277.69 347.13 l -277.69 345.95 277.25 345.36 276.37 345.36 c -275.48 345.36 274.99 345.95 274.88 347.13 c -h -280.63 344.93 m -280.34 341.75 l -281.79 341.75 l -281.49 344.93 l -h -285.19 352.73 m -285.19 341.75 l -282.87 341.75 l -282.87 342.62 l -284.17 342.62 l -284.17 351.87 l -282.87 351.87 l -282.87 352.73 l -h -f -80.436 98.191 m -79.744 98.828 79.078 99.146 78.438 99.146 c -77.910 99.146 77.473 98.981 77.125 98.651 c -76.777 98.321 76.604 97.904 76.604 97.400 c -76.604 96.705 76.896 96.171 77.479 95.798 c -78.063 95.425 78.900 95.238 79.990 95.238 c -80.266 95.238 l -80.266 94.471 l -80.266 93.732 79.887 93.363 79.129 93.363 c -78.520 93.363 77.861 93.551 77.154 93.926 c -77.154 92.971 l -77.932 92.654 78.660 92.496 79.340 92.496 c -80.051 92.496 80.575 92.656 80.913 92.977 c -81.251 93.297 81.420 93.795 81.420 94.471 c -81.420 97.354 l -81.420 98.014 81.623 98.344 82.029 98.344 c -82.080 98.344 82.154 98.336 82.252 98.320 c -82.334 98.959 l -82.072 99.084 81.783 99.146 81.467 99.146 c -80.928 99.146 80.584 98.828 80.436 98.191 c -h -80.266 97.564 m -80.266 95.918 l -79.879 95.906 l -79.246 95.906 78.734 96.026 78.344 96.267 c -77.953 96.507 77.758 96.822 77.758 97.213 c -77.758 97.490 77.855 97.725 78.051 97.916 c -78.246 98.107 78.484 98.203 78.766 98.203 c -79.246 98.203 79.746 97.990 80.266 97.564 c -h -87.760 99.000 m -87.760 97.805 l -87.146 98.699 86.402 99.146 85.527 99.146 c -84.973 99.146 84.531 98.972 84.203 98.622 c -83.875 98.272 83.711 97.801 83.711 97.207 c -83.711 92.637 l -84.865 92.637 l -84.865 96.832 l -84.865 97.309 84.935 97.647 85.073 97.849 c -85.212 98.050 85.443 98.150 85.768 98.150 c -86.471 98.150 87.135 97.688 87.760 96.762 c -87.760 92.637 l -88.914 92.637 l -88.914 99.000 l -h -93.139 99.146 m -92.553 99.146 92.096 98.979 91.768 98.643 c -91.439 98.307 91.275 97.840 91.275 97.242 c -91.275 93.504 l -90.479 93.504 l -90.479 92.637 l -91.275 92.637 l -91.275 91.482 l -92.430 91.371 l -92.430 92.637 l -94.094 92.637 l -94.094 93.504 l -92.430 93.504 l -92.430 97.031 l -92.430 97.863 92.789 98.279 93.508 98.279 c -93.660 98.279 93.846 98.254 94.064 98.203 c -94.064 99.000 l -93.709 99.098 93.400 99.146 93.139 99.146 c -h -95.717 99.000 m -95.717 89.748 l -96.871 89.748 l -96.871 93.832 l -97.480 92.941 98.227 92.496 99.109 92.496 c -99.660 92.496 100.10 92.671 100.43 93.021 c -100.76 93.370 100.92 93.840 100.92 94.430 c -100.92 99.000 l -99.766 99.000 l -99.766 94.805 l -99.766 94.332 99.696 93.995 99.558 93.794 c -99.419 93.593 99.189 93.492 98.869 93.492 c -98.162 93.492 97.496 93.955 96.871 94.881 c -96.871 99.000 l -h -110.25 99.000 m -103.31 95.531 l -110.25 92.062 l -110.25 93.029 l -105.25 95.531 l -110.25 98.027 l -h -116.68 99.000 m -116.68 97.805 l -116.07 98.699 115.32 99.146 114.45 99.146 c -113.89 99.146 113.45 98.972 113.12 98.622 c -112.80 98.272 112.63 97.801 112.63 97.207 c -112.63 92.637 l -113.79 92.637 l -113.79 96.832 l -113.79 97.309 113.86 97.647 114.00 97.849 c -114.13 98.050 114.37 98.150 114.69 98.150 c -115.39 98.150 116.06 97.688 116.68 96.762 c -116.68 92.637 l -117.84 92.637 l -117.84 99.000 l -h -121.84 99.146 m -121.31 99.146 120.67 99.023 119.92 98.777 c -119.92 97.717 l -120.67 98.092 121.33 98.279 121.88 98.279 c -122.22 98.279 122.49 98.189 122.71 98.010 c -122.93 97.830 123.04 97.605 123.04 97.336 c -123.04 96.941 122.73 96.615 122.12 96.357 c -121.45 96.070 l -120.45 95.656 119.95 95.061 119.95 94.283 c -119.95 93.729 120.15 93.292 120.54 92.974 c -120.93 92.655 121.47 92.496 122.15 92.496 c -122.51 92.496 122.95 92.545 123.47 92.643 c -123.71 92.689 l -123.71 93.650 l -123.07 93.459 122.56 93.363 122.18 93.363 c -121.44 93.363 121.06 93.633 121.06 94.172 c -121.06 94.520 121.35 94.812 121.91 95.051 c -122.46 95.285 l -123.09 95.551 123.54 95.831 123.80 96.126 c -124.06 96.421 124.19 96.789 124.19 97.230 c -124.19 97.789 123.97 98.248 123.53 98.607 c -123.09 98.967 122.53 99.146 121.84 99.146 c -h -130.93 98.795 m -130.16 99.029 129.50 99.146 128.95 99.146 c -128.01 99.146 127.24 98.835 126.65 98.212 c -126.06 97.589 125.76 96.781 125.76 95.789 c -125.76 94.824 126.02 94.033 126.55 93.416 c -127.07 92.799 127.73 92.490 128.55 92.490 c -129.32 92.490 129.91 92.764 130.33 93.311 c -130.75 93.857 130.96 94.635 130.96 95.643 c -130.96 96.000 l -126.94 96.000 l -127.11 97.512 127.85 98.268 129.16 98.268 c -129.64 98.268 130.23 98.139 130.93 97.881 c -h -126.99 95.133 m -129.80 95.133 l -129.80 93.949 129.36 93.357 128.48 93.357 c -127.59 93.357 127.10 93.949 126.99 95.133 c -h -132.95 99.000 m -132.95 92.637 l -134.11 92.637 l -134.11 93.832 l -134.56 92.941 135.23 92.496 136.10 92.496 c -136.22 92.496 136.34 92.506 136.47 92.525 c -136.47 93.604 l -136.27 93.537 136.09 93.504 135.94 93.504 c -135.21 93.504 134.60 93.938 134.11 94.805 c -134.11 99.000 l -h -137.88 100.88 m -137.88 100.45 l -138.26 100.34 138.44 99.898 138.44 99.117 c -138.44 99.000 l -137.88 99.000 l -137.88 97.553 l -139.33 97.553 l -139.33 98.807 l -139.33 100.09 138.85 100.78 137.88 100.88 c -h -147.94 99.146 m -147.08 99.146 146.37 98.828 145.80 98.191 c -145.24 97.555 144.95 96.752 144.95 95.783 c -144.95 94.748 145.23 93.941 145.79 93.363 c -146.35 92.785 147.14 92.496 148.14 92.496 c -148.64 92.496 149.19 92.564 149.80 92.701 c -149.80 93.668 l -149.15 93.477 148.62 93.381 148.21 93.381 c -147.62 93.381 147.15 93.603 146.79 94.046 c -146.43 94.489 146.25 95.080 146.25 95.818 c -146.25 96.533 146.44 97.111 146.80 97.553 c -147.17 97.994 147.65 98.215 148.25 98.215 c -148.77 98.215 149.32 98.080 149.88 97.811 c -149.88 98.807 l -149.13 99.033 148.48 99.146 147.94 99.146 c -h -151.60 99.000 m -151.60 92.637 l -152.76 92.637 l -152.76 93.832 l -153.21 92.941 153.88 92.496 154.75 92.496 c -154.87 92.496 154.99 92.506 155.12 92.525 c -155.12 93.604 l -154.92 93.537 154.74 93.504 154.59 93.504 c -153.86 93.504 153.25 93.938 152.76 94.805 c -152.76 99.000 l -h -161.18 98.795 m -160.40 99.029 159.74 99.146 159.19 99.146 c -158.25 99.146 157.49 98.835 156.90 98.212 c -156.31 97.589 156.01 96.781 156.01 95.789 c -156.01 94.824 156.27 94.033 156.79 93.416 c -157.31 92.799 157.98 92.490 158.79 92.490 c -159.56 92.490 160.16 92.764 160.58 93.311 c -161.00 93.857 161.21 94.635 161.21 95.643 c -161.20 96.000 l -157.19 96.000 l -157.36 97.512 158.10 98.268 159.41 98.268 c -159.89 98.268 160.48 98.139 161.18 97.881 c -h -157.24 95.133 m -160.05 95.133 l -160.05 93.949 159.61 93.357 158.72 93.357 c -157.84 93.357 157.34 93.949 157.24 95.133 c -h -167.28 99.000 m -167.28 97.805 l -166.81 98.699 166.11 99.146 165.16 99.146 c -164.40 99.146 163.79 98.867 163.35 98.309 c -162.92 97.750 162.70 96.986 162.70 96.018 c -162.70 94.959 162.94 94.107 163.44 93.463 c -163.94 92.818 164.60 92.496 165.41 92.496 c -166.16 92.496 166.79 92.785 167.28 93.363 c -167.28 89.748 l -168.44 89.748 l -168.44 99.000 l -h -167.28 94.154 m -166.69 93.627 166.12 93.363 165.58 93.363 c -164.48 93.363 163.93 94.209 163.93 95.900 c -163.93 97.389 164.42 98.133 165.40 98.133 c -166.04 98.133 166.67 97.783 167.28 97.084 c -h -172.44 99.146 m -171.91 99.146 171.27 99.023 170.52 98.777 c -170.52 97.717 l -171.27 98.092 171.93 98.279 172.49 98.279 c -172.82 98.279 173.09 98.189 173.31 98.010 c -173.53 97.830 173.64 97.605 173.64 97.336 c -173.64 96.941 173.33 96.615 172.72 96.357 c -172.05 96.070 l -171.05 95.656 170.55 95.061 170.55 94.283 c -170.55 93.729 170.75 93.292 171.14 92.974 c -171.53 92.655 172.07 92.496 172.76 92.496 c -173.11 92.496 173.55 92.545 174.07 92.643 c -174.31 92.689 l -174.31 93.650 l -173.67 93.459 173.16 93.363 172.78 93.363 c -172.04 93.363 171.67 93.633 171.67 94.172 c -171.67 94.520 171.95 94.812 172.51 95.051 c -173.07 95.285 l -173.70 95.551 174.14 95.831 174.40 96.126 c -174.66 96.421 174.79 96.789 174.79 97.230 c -174.79 97.789 174.57 98.248 174.13 98.607 c -173.69 98.967 173.13 99.146 172.44 99.146 c -h -177.02 99.000 m -183.95 95.531 l -177.02 92.062 l -177.02 93.029 l -182.01 95.531 l -177.02 98.027 l -h -f -[] 0.0000 d -70.500 102.50 m -508.50 102.50 l -S -508.00 102.00 m -502.00 96.000 l -502.00 108.00 l -h -f* -81.238 371.00 m -81.238 369.80 l -80.770 370.70 80.062 371.15 79.117 371.15 c -78.352 371.15 77.749 370.87 77.310 370.31 c -76.870 369.75 76.650 368.99 76.650 368.02 c -76.650 366.96 76.899 366.11 77.397 365.46 c -77.896 364.82 78.551 364.50 79.363 364.50 c -80.117 364.50 80.742 364.79 81.238 365.36 c -81.238 361.75 l -82.398 361.75 l -82.398 371.00 l -h -81.238 366.15 m -80.641 365.63 80.074 365.36 79.539 365.36 c -78.434 365.36 77.881 366.21 77.881 367.90 c -77.881 369.39 78.373 370.13 79.357 370.13 c -79.998 370.13 80.625 369.78 81.238 369.08 c -h -87.197 371.15 m -86.287 371.15 85.561 370.84 85.018 370.24 c -84.475 369.64 84.203 368.83 84.203 367.82 c -84.203 366.79 84.476 365.99 85.021 365.39 c -85.565 364.79 86.305 364.50 87.238 364.50 c -88.172 364.50 88.911 364.79 89.456 365.39 c -90.001 365.99 90.273 366.79 90.273 367.81 c -90.273 368.85 90.000 369.66 89.453 370.26 c -88.906 370.85 88.154 371.15 87.197 371.15 c -h -87.215 370.28 m -88.438 370.28 89.049 369.46 89.049 367.81 c -89.049 366.18 88.445 365.36 87.238 365.36 c -86.035 365.36 85.434 366.18 85.434 367.82 c -85.434 369.46 86.027 370.28 87.215 370.28 c -h -90.924 373.02 m -90.924 372.15 l -96.924 372.15 l -96.924 373.02 l -h -99.766 371.15 m -99.238 371.15 98.598 371.02 97.844 370.78 c -97.844 369.72 l -98.598 370.09 99.254 370.28 99.812 370.28 c -100.14 370.28 100.42 370.19 100.64 370.01 c -100.86 369.83 100.97 369.61 100.97 369.34 c -100.97 368.94 100.66 368.62 100.05 368.36 c -99.373 368.07 l -98.377 367.66 97.879 367.06 97.879 366.28 c -97.879 365.73 98.075 365.29 98.468 364.97 c -98.860 364.66 99.398 364.50 100.08 364.50 c -100.44 364.50 100.88 364.54 101.40 364.64 c -101.64 364.69 l -101.64 365.65 l -101.00 365.46 100.48 365.36 100.11 365.36 c -99.363 365.36 98.992 365.63 98.992 366.17 c -98.992 366.52 99.273 366.81 99.836 367.05 c -100.39 367.29 l -101.02 367.55 101.47 367.83 101.73 368.13 c -101.99 368.42 102.12 368.79 102.12 369.23 c -102.12 369.79 101.90 370.25 101.46 370.61 c -101.02 370.97 100.45 371.15 99.766 371.15 c -h -106.69 371.15 m -105.78 371.15 105.05 370.84 104.51 370.24 c -103.96 369.64 103.69 368.83 103.69 367.82 c -103.69 366.79 103.96 365.99 104.51 365.39 c -105.05 364.79 105.79 364.50 106.73 364.50 c -107.66 364.50 108.40 364.79 108.94 365.39 c -109.49 365.99 109.76 366.79 109.76 367.81 c -109.76 368.85 109.49 369.66 108.94 370.26 c -108.39 370.85 107.64 371.15 106.69 371.15 c -h -106.70 370.28 m -107.93 370.28 108.54 369.46 108.54 367.81 c -108.54 366.18 107.93 365.36 106.73 365.36 c -105.52 365.36 104.92 366.18 104.92 367.82 c -104.92 369.46 105.52 370.28 106.70 370.28 c -h -111.57 371.00 m -111.57 364.64 l -112.72 364.64 l -112.72 365.83 l -113.28 364.94 114.01 364.50 114.89 364.50 c -115.74 364.50 116.32 364.94 116.63 365.83 c -117.18 364.94 117.89 364.49 118.76 364.49 c -119.32 364.49 119.76 364.66 120.07 364.99 c -120.38 365.32 120.53 365.78 120.53 366.37 c -120.53 371.00 l -119.37 371.00 l -119.37 366.55 l -119.37 365.83 119.08 365.46 118.51 365.46 c -117.91 365.46 117.29 365.89 116.63 366.73 c -116.63 371.00 l -115.47 371.00 l -115.47 366.55 l -115.47 365.82 115.18 365.46 114.59 365.46 c -114.01 365.46 113.38 365.88 112.72 366.73 c -112.72 371.00 l -h -127.43 370.79 m -126.66 371.03 126.00 371.15 125.45 371.15 c -124.51 371.15 123.75 370.83 123.15 370.21 c -122.56 369.59 122.27 368.78 122.27 367.79 c -122.27 366.82 122.53 366.03 123.05 365.42 c -123.57 364.80 124.24 364.49 125.05 364.49 c -125.82 364.49 126.41 364.76 126.83 365.31 c -127.25 365.86 127.46 366.63 127.46 367.64 c -127.46 368.00 l -123.44 368.00 l -123.61 369.51 124.35 370.27 125.66 370.27 c -126.14 370.27 126.73 370.14 127.43 369.88 c -h -123.50 367.13 m -126.30 367.13 l -126.30 365.95 125.86 365.36 124.98 365.36 c -124.09 365.36 123.60 365.95 123.50 367.13 c -h -131.37 371.15 m -130.78 371.15 130.32 370.98 129.99 370.64 c -129.67 370.31 129.50 369.84 129.50 369.24 c -129.50 365.50 l -128.71 365.50 l -128.71 364.64 l -129.50 364.64 l -129.50 363.48 l -130.66 363.37 l -130.66 364.64 l -132.32 364.64 l -132.32 365.50 l -130.66 365.50 l -130.66 369.03 l -130.66 369.86 131.02 370.28 131.73 370.28 c -131.89 370.28 132.07 370.25 132.29 370.20 c -132.29 371.00 l -131.94 371.10 131.63 371.15 131.37 371.15 c -h -133.94 371.00 m -133.94 361.75 l -135.10 361.75 l -135.10 365.83 l -135.71 364.94 136.45 364.50 137.34 364.50 c -137.89 364.50 138.33 364.67 138.65 365.02 c -138.98 365.37 139.15 365.84 139.15 366.43 c -139.15 371.00 l -137.99 371.00 l -137.99 366.80 l -137.99 366.33 137.92 366.00 137.78 365.79 c -137.65 365.59 137.42 365.49 137.10 365.49 c -136.39 365.49 135.72 365.96 135.10 366.88 c -135.10 371.00 l -h -141.39 371.00 m -141.39 364.64 l -142.54 364.64 l -142.54 371.00 l -h -141.39 363.48 m -141.39 362.33 l -142.54 362.33 l -142.54 363.48 l -h -144.86 371.00 m -144.86 364.64 l -146.01 364.64 l -146.01 365.83 l -146.62 364.94 147.37 364.50 148.25 364.50 c -148.80 364.50 149.24 364.67 149.57 365.02 c -149.90 365.37 150.06 365.84 150.06 366.43 c -150.06 371.00 l -148.91 371.00 l -148.91 366.80 l -148.91 366.33 148.84 366.00 148.70 365.79 c -148.56 365.59 148.33 365.49 148.01 365.49 c -147.30 365.49 146.64 365.96 146.01 366.88 c -146.01 371.00 l -h -152.33 373.12 m -152.46 372.11 l -153.13 372.43 153.79 372.59 154.44 372.59 c -155.74 372.59 156.39 371.90 156.39 370.52 c -156.39 369.52 l -155.96 370.41 155.27 370.85 154.29 370.85 c -153.53 370.85 152.93 370.58 152.48 370.02 c -152.03 369.47 151.80 368.72 151.80 367.78 c -151.80 366.81 152.06 366.02 152.57 365.41 c -153.08 364.80 153.74 364.50 154.56 364.50 c -155.27 364.50 155.88 364.79 156.39 365.36 c -156.39 364.64 l -157.55 364.64 l -157.55 369.27 l -157.55 370.26 157.50 371.00 157.40 371.48 c -157.29 371.96 157.10 372.35 156.81 372.65 c -156.31 373.19 155.53 373.46 154.46 373.46 c -153.72 373.46 153.01 373.34 152.33 373.12 c -h -156.39 368.80 m -156.39 366.15 l -155.88 365.63 155.33 365.36 154.73 365.36 c -154.20 365.36 153.79 365.58 153.48 366.00 c -153.18 366.43 153.03 367.01 153.03 367.75 c -153.03 369.15 153.52 369.85 154.50 369.85 c -155.17 369.85 155.80 369.50 156.39 368.80 c -h -166.87 371.00 m -159.94 367.53 l -166.87 364.06 l -166.87 365.03 l -161.88 367.53 l -166.87 370.03 l -h -171.24 371.15 m -170.65 371.15 170.20 370.98 169.87 370.64 c -169.54 370.31 169.38 369.84 169.38 369.24 c -169.38 365.50 l -168.58 365.50 l -168.58 364.64 l -169.38 364.64 l -169.38 363.48 l -170.53 363.37 l -170.53 364.64 l -172.19 364.64 l -172.19 365.50 l -170.53 365.50 l -170.53 369.03 l -170.53 369.86 170.89 370.28 171.61 370.28 c -171.76 370.28 171.95 370.25 172.16 370.20 c -172.16 371.00 l -171.81 371.10 171.50 371.15 171.24 371.15 c -h -176.31 371.15 m -175.40 371.15 174.67 370.84 174.13 370.24 c -173.58 369.64 173.31 368.83 173.31 367.82 c -173.31 366.79 173.58 365.99 174.13 365.39 c -174.67 364.79 175.41 364.50 176.35 364.50 c -177.28 364.50 178.02 364.79 178.57 365.39 c -179.11 365.99 179.38 366.79 179.38 367.81 c -179.38 368.85 179.11 369.66 178.56 370.26 c -178.02 370.85 177.26 371.15 176.31 371.15 c -h -176.32 370.28 m -177.55 370.28 178.16 369.46 178.16 367.81 c -178.16 366.18 177.55 365.36 176.35 365.36 c -175.14 365.36 174.54 366.18 174.54 367.82 c -174.54 369.46 175.14 370.28 176.32 370.28 c -h -181.19 371.00 m -181.19 361.75 l -182.34 361.75 l -182.34 367.72 l -185.04 364.64 l -186.28 364.64 l -183.71 367.61 l -186.81 371.00 l -185.34 371.00 l -182.34 367.74 l -182.34 371.00 l -h -192.87 370.79 m -192.09 371.03 191.43 371.15 190.88 371.15 c -189.94 371.15 189.18 370.83 188.58 370.21 c -187.99 369.59 187.70 368.78 187.70 367.79 c -187.70 366.82 187.96 366.03 188.48 365.42 c -189.00 364.80 189.67 364.49 190.48 364.49 c -191.25 364.49 191.84 364.76 192.26 365.31 c -192.68 365.86 192.89 366.63 192.89 367.64 c -192.89 368.00 l -188.88 368.00 l -189.04 369.51 189.78 370.27 191.10 370.27 c -191.58 370.27 192.17 370.14 192.87 369.88 c -h -188.93 367.13 m -191.73 367.13 l -191.73 365.95 191.29 365.36 190.41 365.36 c -189.52 365.36 189.03 365.95 188.93 367.13 c -h -194.89 371.00 m -194.89 364.64 l -196.04 364.64 l -196.04 365.83 l -196.65 364.94 197.40 364.50 198.28 364.50 c -198.83 364.50 199.27 364.67 199.60 365.02 c -199.93 365.37 200.09 365.84 200.09 366.43 c -200.09 371.00 l -198.94 371.00 l -198.94 366.80 l -198.94 366.33 198.87 366.00 198.73 365.79 c -198.59 365.59 198.36 365.49 198.04 365.49 c -197.33 365.49 196.67 365.96 196.04 366.88 c -196.04 371.00 l -h -202.35 372.88 m -202.35 372.45 l -202.73 372.34 202.91 371.90 202.91 371.12 c -202.91 371.00 l -202.35 371.00 l -202.35 369.55 l -203.80 369.55 l -203.80 370.81 l -203.80 372.09 203.32 372.78 202.35 372.88 c -h -209.93 371.00 m -209.93 364.64 l -211.08 364.64 l -211.08 371.00 l -h -209.93 363.48 m -209.93 362.33 l -211.08 362.33 l -211.08 363.48 l -h -213.40 371.00 m -213.40 364.64 l -214.55 364.64 l -214.55 365.83 l -215.16 364.94 215.91 364.50 216.79 364.50 c -217.34 364.50 217.78 364.67 218.11 365.02 c -218.44 365.37 218.60 365.84 218.60 366.43 c -218.60 371.00 l -217.45 371.00 l -217.45 366.80 l -217.45 366.33 217.38 366.00 217.24 365.79 c -217.10 365.59 216.87 365.49 216.55 365.49 c -215.84 365.49 215.18 365.96 214.55 366.88 c -214.55 371.00 l -h -222.53 371.15 m -222.00 371.15 221.36 371.02 220.61 370.78 c -220.61 369.72 l -221.36 370.09 222.02 370.28 222.58 370.28 c -222.91 370.28 223.19 370.19 223.40 370.01 c -223.62 369.83 223.73 369.61 223.73 369.34 c -223.73 368.94 223.43 368.62 222.81 368.36 c -222.14 368.07 l -221.14 367.66 220.64 367.06 220.64 366.28 c -220.64 365.73 220.84 365.29 221.23 364.97 c -221.63 364.66 222.16 364.50 222.85 364.50 c -223.20 364.50 223.64 364.54 224.17 364.64 c -224.41 364.69 l -224.41 365.65 l -223.76 365.46 223.25 365.36 222.87 365.36 c -222.13 365.36 221.76 365.63 221.76 366.17 c -221.76 366.52 222.04 366.81 222.60 367.05 c -223.16 367.29 l -223.79 367.55 224.23 367.83 224.49 368.13 c -224.76 368.42 224.89 368.79 224.89 369.23 c -224.89 369.79 224.67 370.25 224.22 370.61 c -223.78 370.97 223.22 371.15 222.53 371.15 c -h -228.87 371.15 m -228.29 371.15 227.83 370.98 227.50 370.64 c -227.17 370.31 227.01 369.84 227.01 369.24 c -227.01 365.50 l -226.21 365.50 l -226.21 364.64 l -227.01 364.64 l -227.01 363.48 l -228.16 363.37 l -228.16 364.64 l -229.83 364.64 l -229.83 365.50 l -228.16 365.50 l -228.16 369.03 l -228.16 369.86 228.52 370.28 229.24 370.28 c -229.39 370.28 229.58 370.25 229.80 370.20 c -229.80 371.00 l -229.44 371.10 229.13 371.15 228.87 371.15 c -h -234.73 370.19 m -234.04 370.83 233.37 371.15 232.73 371.15 c -232.21 371.15 231.77 370.98 231.42 370.65 c -231.07 370.32 230.90 369.90 230.90 369.40 c -230.90 368.71 231.19 368.17 231.77 367.80 c -232.36 367.42 233.20 367.24 234.29 367.24 c -234.56 367.24 l -234.56 366.47 l -234.56 365.73 234.18 365.36 233.42 365.36 c -232.81 365.36 232.16 365.55 231.45 365.93 c -231.45 364.97 l -232.23 364.65 232.96 364.50 233.63 364.50 c -234.35 364.50 234.87 364.66 235.21 364.98 c -235.55 365.30 235.71 365.79 235.71 366.47 c -235.71 369.35 l -235.71 370.01 235.92 370.34 236.32 370.34 c -236.38 370.34 236.45 370.34 236.55 370.32 c -236.63 370.96 l -236.37 371.08 236.08 371.15 235.76 371.15 c -235.22 371.15 234.88 370.83 234.73 370.19 c -h -234.56 369.56 m -234.56 367.92 l -234.17 367.91 l -233.54 367.91 233.03 368.03 232.64 368.27 c -232.25 368.51 232.05 368.82 232.05 369.21 c -232.05 369.49 232.15 369.72 232.35 369.92 c -232.54 370.11 232.78 370.20 233.06 370.20 c -233.54 370.20 234.04 369.99 234.56 369.56 c -h -238.08 371.00 m -238.08 364.64 l -239.23 364.64 l -239.23 365.83 l -239.84 364.94 240.59 364.50 241.47 364.50 c -242.02 364.50 242.46 364.67 242.79 365.02 c -243.12 365.37 243.28 365.84 243.28 366.43 c -243.28 371.00 l -242.12 371.00 l -242.12 366.80 l -242.12 366.33 242.06 366.00 241.92 365.79 c -241.78 365.59 241.55 365.49 241.23 365.49 c -240.52 365.49 239.86 365.96 239.23 366.88 c -239.23 371.00 l -h -248.01 371.15 m -247.15 371.15 246.44 370.83 245.87 370.19 c -245.30 369.55 245.02 368.75 245.02 367.78 c -245.02 366.75 245.30 365.94 245.86 365.36 c -246.42 364.79 247.20 364.50 248.21 364.50 c -248.70 364.50 249.26 364.56 249.87 364.70 c -249.87 365.67 l -249.22 365.48 248.69 365.38 248.28 365.38 c -247.69 365.38 247.21 365.60 246.86 366.05 c -246.50 366.49 246.32 367.08 246.32 367.82 c -246.32 368.53 246.50 369.11 246.87 369.55 c -247.24 369.99 247.72 370.21 248.31 370.21 c -248.84 370.21 249.38 370.08 249.94 369.81 c -249.94 370.81 l -249.20 371.03 248.55 371.15 248.01 371.15 c -h -256.33 370.79 m -255.56 371.03 254.90 371.15 254.35 371.15 c -253.41 371.15 252.65 370.83 252.05 370.21 c -251.46 369.59 251.17 368.78 251.17 367.79 c -251.17 366.82 251.43 366.03 251.95 365.42 c -252.47 364.80 253.14 364.49 253.95 364.49 c -254.72 364.49 255.31 364.76 255.73 365.31 c -256.15 365.86 256.36 366.63 256.36 367.64 c -256.36 368.00 l -252.34 368.00 l -252.51 369.51 253.25 370.27 254.56 370.27 c -255.04 370.27 255.63 370.14 256.33 369.88 c -h -252.40 367.13 m -255.20 367.13 l -255.20 365.95 254.76 365.36 253.88 365.36 c -252.99 365.36 252.50 365.95 252.40 367.13 c -h -257.20 373.02 m -257.20 372.15 l -263.20 372.15 l -263.20 373.02 l -h -264.36 371.00 m -264.36 364.64 l -265.51 364.64 l -265.51 371.00 l -h -264.36 363.48 m -264.36 362.33 l -265.51 362.33 l -265.51 363.48 l -h -271.91 371.00 m -271.91 369.80 l -271.44 370.70 270.73 371.15 269.79 371.15 c -269.02 371.15 268.42 370.87 267.98 370.31 c -267.54 369.75 267.32 368.99 267.32 368.02 c -267.32 366.96 267.57 366.11 268.07 365.46 c -268.57 364.82 269.22 364.50 270.03 364.50 c -270.79 364.50 271.41 364.79 271.91 365.36 c -271.91 361.75 l -273.07 361.75 l -273.07 371.00 l -h -271.91 366.15 m -271.31 365.63 270.74 365.36 270.21 365.36 c -269.10 365.36 268.55 366.21 268.55 367.90 c -268.55 369.39 269.04 370.13 270.03 370.13 c -270.67 370.13 271.29 369.78 271.91 369.08 c -h -275.52 371.00 m -282.46 367.53 l -275.52 364.06 l -275.52 365.03 l -280.52 367.53 l -275.52 370.03 l -h -f -70.500 374.50 m -687.50 374.50 l -S -687.00 374.00 m -681.00 368.00 l -681.00 380.00 l -h -f* -80.436 266.19 m -79.744 266.83 79.078 267.15 78.438 267.15 c -77.910 267.15 77.473 266.98 77.125 266.65 c -76.777 266.32 76.604 265.90 76.604 265.40 c -76.604 264.71 76.896 264.17 77.479 263.80 c -78.063 263.42 78.900 263.24 79.990 263.24 c -80.266 263.24 l -80.266 262.47 l -80.266 261.73 79.887 261.36 79.129 261.36 c -78.520 261.36 77.861 261.55 77.154 261.93 c -77.154 260.97 l -77.932 260.65 78.660 260.50 79.340 260.50 c -80.051 260.50 80.575 260.66 80.913 260.98 c -81.251 261.30 81.420 261.79 81.420 262.47 c -81.420 265.35 l -81.420 266.01 81.623 266.34 82.029 266.34 c -82.080 266.34 82.154 266.34 82.252 266.32 c -82.334 266.96 l -82.072 267.08 81.783 267.15 81.467 267.15 c -80.928 267.15 80.584 266.83 80.436 266.19 c -h -80.266 265.56 m -80.266 263.92 l -79.879 263.91 l -79.246 263.91 78.734 264.03 78.344 264.27 c -77.953 264.51 77.758 264.82 77.758 265.21 c -77.758 265.49 77.855 265.72 78.051 265.92 c -78.246 266.11 78.484 266.20 78.766 266.20 c -79.246 266.20 79.746 265.99 80.266 265.56 c -h -87.760 267.00 m -87.760 265.80 l -87.146 266.70 86.402 267.15 85.527 267.15 c -84.973 267.15 84.531 266.97 84.203 266.62 c -83.875 266.27 83.711 265.80 83.711 265.21 c -83.711 260.64 l -84.865 260.64 l -84.865 264.83 l -84.865 265.31 84.935 265.65 85.073 265.85 c -85.212 266.05 85.443 266.15 85.768 266.15 c -86.471 266.15 87.135 265.69 87.760 264.76 c -87.760 260.64 l -88.914 260.64 l -88.914 267.00 l -h -93.139 267.15 m -92.553 267.15 92.096 266.98 91.768 266.64 c -91.439 266.31 91.275 265.84 91.275 265.24 c -91.275 261.50 l -90.479 261.50 l -90.479 260.64 l -91.275 260.64 l -91.275 259.48 l -92.430 259.37 l -92.430 260.64 l -94.094 260.64 l -94.094 261.50 l -92.430 261.50 l -92.430 265.03 l -92.430 265.86 92.789 266.28 93.508 266.28 c -93.660 266.28 93.846 266.25 94.064 266.20 c -94.064 267.00 l -93.709 267.10 93.400 267.15 93.139 267.15 c -h -95.717 267.00 m -95.717 257.75 l -96.871 257.75 l -96.871 261.83 l -97.480 260.94 98.227 260.50 99.109 260.50 c -99.660 260.50 100.10 260.67 100.43 261.02 c -100.76 261.37 100.92 261.84 100.92 262.43 c -100.92 267.00 l -99.766 267.00 l -99.766 262.80 l -99.766 262.33 99.696 262.00 99.558 261.79 c -99.419 261.59 99.189 261.49 98.869 261.49 c -98.162 261.49 97.496 261.96 96.871 262.88 c -96.871 267.00 l -h -110.25 267.00 m -103.31 263.53 l -110.25 260.06 l -110.25 261.03 l -105.25 263.53 l -110.25 266.03 l -h -116.68 267.00 m -116.68 265.80 l -116.07 266.70 115.32 267.15 114.45 267.15 c -113.89 267.15 113.45 266.97 113.12 266.62 c -112.80 266.27 112.63 265.80 112.63 265.21 c -112.63 260.64 l -113.79 260.64 l -113.79 264.83 l -113.79 265.31 113.86 265.65 114.00 265.85 c -114.13 266.05 114.37 266.15 114.69 266.15 c -115.39 266.15 116.06 265.69 116.68 264.76 c -116.68 260.64 l -117.84 260.64 l -117.84 267.00 l -h -121.84 267.15 m -121.31 267.15 120.67 267.02 119.92 266.78 c -119.92 265.72 l -120.67 266.09 121.33 266.28 121.88 266.28 c -122.22 266.28 122.49 266.19 122.71 266.01 c -122.93 265.83 123.04 265.61 123.04 265.34 c -123.04 264.94 122.73 264.62 122.12 264.36 c -121.45 264.07 l -120.45 263.66 119.95 263.06 119.95 262.28 c -119.95 261.73 120.15 261.29 120.54 260.97 c -120.93 260.66 121.47 260.50 122.15 260.50 c -122.51 260.50 122.95 260.54 123.47 260.64 c -123.71 260.69 l -123.71 261.65 l -123.07 261.46 122.56 261.36 122.18 261.36 c -121.44 261.36 121.06 261.63 121.06 262.17 c -121.06 262.52 121.35 262.81 121.91 263.05 c -122.46 263.29 l -123.09 263.55 123.54 263.83 123.80 264.13 c -124.06 264.42 124.19 264.79 124.19 265.23 c -124.19 265.79 123.97 266.25 123.53 266.61 c -123.09 266.97 122.53 267.15 121.84 267.15 c -h -130.93 266.79 m -130.16 267.03 129.50 267.15 128.95 267.15 c -128.01 267.15 127.24 266.83 126.65 266.21 c -126.06 265.59 125.76 264.78 125.76 263.79 c -125.76 262.82 126.02 262.03 126.55 261.42 c -127.07 260.80 127.73 260.49 128.55 260.49 c -129.32 260.49 129.91 260.76 130.33 261.31 c -130.75 261.86 130.96 262.63 130.96 263.64 c -130.96 264.00 l -126.94 264.00 l -127.11 265.51 127.85 266.27 129.16 266.27 c -129.64 266.27 130.23 266.14 130.93 265.88 c -h -126.99 263.13 m -129.80 263.13 l -129.80 261.95 129.36 261.36 128.48 261.36 c -127.59 261.36 127.10 261.95 126.99 263.13 c -h -132.95 267.00 m -132.95 260.64 l -134.11 260.64 l -134.11 261.83 l -134.56 260.94 135.23 260.50 136.10 260.50 c -136.22 260.50 136.34 260.51 136.47 260.53 c -136.47 261.60 l -136.27 261.54 136.09 261.50 135.94 261.50 c -135.21 261.50 134.60 261.94 134.11 262.80 c -134.11 267.00 l -h -137.88 268.88 m -137.88 268.45 l -138.26 268.34 138.44 267.90 138.44 267.12 c -138.44 267.00 l -137.88 267.00 l -137.88 265.55 l -139.33 265.55 l -139.33 266.81 l -139.33 268.09 138.85 268.78 137.88 268.88 c -h -147.94 267.15 m -147.08 267.15 146.37 266.83 145.80 266.19 c -145.24 265.55 144.95 264.75 144.95 263.78 c -144.95 262.75 145.23 261.94 145.79 261.36 c -146.35 260.79 147.14 260.50 148.14 260.50 c -148.64 260.50 149.19 260.56 149.80 260.70 c -149.80 261.67 l -149.15 261.48 148.62 261.38 148.21 261.38 c -147.62 261.38 147.15 261.60 146.79 262.05 c -146.43 262.49 146.25 263.08 146.25 263.82 c -146.25 264.53 146.44 265.11 146.80 265.55 c -147.17 265.99 147.65 266.21 148.25 266.21 c -148.77 266.21 149.32 266.08 149.88 265.81 c -149.88 266.81 l -149.13 267.03 148.48 267.15 147.94 267.15 c -h -151.60 267.00 m -151.60 260.64 l -152.76 260.64 l -152.76 261.83 l -153.21 260.94 153.88 260.50 154.75 260.50 c -154.87 260.50 154.99 260.51 155.12 260.53 c -155.12 261.60 l -154.92 261.54 154.74 261.50 154.59 261.50 c -153.86 261.50 153.25 261.94 152.76 262.80 c -152.76 267.00 l -h -161.18 266.79 m -160.40 267.03 159.74 267.15 159.19 267.15 c -158.25 267.15 157.49 266.83 156.90 266.21 c -156.31 265.59 156.01 264.78 156.01 263.79 c -156.01 262.82 156.27 262.03 156.79 261.42 c -157.31 260.80 157.98 260.49 158.79 260.49 c -159.56 260.49 160.16 260.76 160.58 261.31 c -161.00 261.86 161.21 262.63 161.21 263.64 c -161.20 264.00 l -157.19 264.00 l -157.36 265.51 158.10 266.27 159.41 266.27 c -159.89 266.27 160.48 266.14 161.18 265.88 c -h -157.24 263.13 m -160.05 263.13 l -160.05 261.95 159.61 261.36 158.72 261.36 c -157.84 261.36 157.34 261.95 157.24 263.13 c -h -167.28 267.00 m -167.28 265.80 l -166.81 266.70 166.11 267.15 165.16 267.15 c -164.40 267.15 163.79 266.87 163.35 266.31 c -162.92 265.75 162.70 264.99 162.70 264.02 c -162.70 262.96 162.94 262.11 163.44 261.46 c -163.94 260.82 164.60 260.50 165.41 260.50 c -166.16 260.50 166.79 260.79 167.28 261.36 c -167.28 257.75 l -168.44 257.75 l -168.44 267.00 l -h -167.28 262.15 m -166.69 261.63 166.12 261.36 165.58 261.36 c -164.48 261.36 163.93 262.21 163.93 263.90 c -163.93 265.39 164.42 266.13 165.40 266.13 c -166.04 266.13 166.67 265.78 167.28 265.08 c -h -172.44 267.15 m -171.91 267.15 171.27 267.02 170.52 266.78 c -170.52 265.72 l -171.27 266.09 171.93 266.28 172.49 266.28 c -172.82 266.28 173.09 266.19 173.31 266.01 c -173.53 265.83 173.64 265.61 173.64 265.34 c -173.64 264.94 173.33 264.62 172.72 264.36 c -172.05 264.07 l -171.05 263.66 170.55 263.06 170.55 262.28 c -170.55 261.73 170.75 261.29 171.14 260.97 c -171.53 260.66 172.07 260.50 172.76 260.50 c -173.11 260.50 173.55 260.54 174.07 260.64 c -174.31 260.69 l -174.31 261.65 l -173.67 261.46 173.16 261.36 172.78 261.36 c -172.04 261.36 171.67 261.63 171.67 262.17 c -171.67 262.52 171.95 262.81 172.51 263.05 c -173.07 263.29 l -173.70 263.55 174.14 263.83 174.40 264.13 c -174.66 264.42 174.79 264.79 174.79 265.23 c -174.79 265.79 174.57 266.25 174.13 266.61 c -173.69 266.97 173.13 267.15 172.44 267.15 c -h -176.89 268.88 m -176.89 268.45 l -177.26 268.34 177.45 267.90 177.45 267.12 c -177.45 267.00 l -176.89 267.00 l -176.89 265.55 l -178.33 265.55 l -178.33 266.81 l -178.33 268.09 177.85 268.78 176.89 268.88 c -h -186.37 267.15 m -185.79 267.15 185.33 266.98 185.00 266.64 c -184.67 266.31 184.51 265.84 184.51 265.24 c -184.51 261.50 l -183.71 261.50 l -183.71 260.64 l -184.51 260.64 l -184.51 259.48 l -185.66 259.37 l -185.66 260.64 l -187.33 260.64 l -187.33 261.50 l -185.66 261.50 l -185.66 265.03 l -185.66 265.86 186.02 266.28 186.74 266.28 c -186.89 266.28 187.08 266.25 187.30 266.20 c -187.30 267.00 l -186.94 267.10 186.63 267.15 186.37 267.15 c -h -193.62 266.79 m -192.84 267.03 192.18 267.15 191.63 267.15 c -190.69 267.15 189.93 266.83 189.33 266.21 c -188.74 265.59 188.45 264.78 188.45 263.79 c -188.45 262.82 188.71 262.03 189.23 261.42 c -189.75 260.80 190.42 260.49 191.23 260.49 c -192.00 260.49 192.59 260.76 193.01 261.31 c -193.43 261.86 193.64 262.63 193.64 263.64 c -193.64 264.00 l -189.62 264.00 l -189.79 265.51 190.53 266.27 191.85 266.27 c -192.33 266.27 192.92 266.14 193.62 265.88 c -h -189.68 263.13 m -192.48 263.13 l -192.48 261.95 192.04 261.36 191.16 261.36 c -190.27 261.36 189.78 261.95 189.68 263.13 c -h -195.64 267.00 m -195.64 260.64 l -196.79 260.64 l -196.79 261.83 l -197.40 260.94 198.15 260.50 199.03 260.50 c -199.58 260.50 200.02 260.67 200.35 261.02 c -200.68 261.37 200.84 261.84 200.84 262.43 c -200.84 267.00 l -199.69 267.00 l -199.69 262.80 l -199.69 262.33 199.62 262.00 199.48 261.79 c -199.34 261.59 199.11 261.49 198.79 261.49 c -198.08 261.49 197.42 261.96 196.79 262.88 c -196.79 267.00 l -h -206.37 266.19 m -205.67 266.83 205.01 267.15 204.37 267.15 c -203.84 267.15 203.40 266.98 203.05 266.65 c -202.71 266.32 202.53 265.90 202.53 265.40 c -202.53 264.71 202.83 264.17 203.41 263.80 c -203.99 263.42 204.83 263.24 205.92 263.24 c -206.20 263.24 l -206.20 262.47 l -206.20 261.73 205.82 261.36 205.06 261.36 c -204.45 261.36 203.79 261.55 203.08 261.93 c -203.08 260.97 l -203.86 260.65 204.59 260.50 205.27 260.50 c -205.98 260.50 206.50 260.66 206.84 260.98 c -207.18 261.30 207.35 261.79 207.35 262.47 c -207.35 265.35 l -207.35 266.01 207.55 266.34 207.96 266.34 c -208.01 266.34 208.08 266.34 208.18 266.32 c -208.26 266.96 l -208.00 267.08 207.71 267.15 207.40 267.15 c -206.86 267.15 206.51 266.83 206.37 266.19 c -h -206.20 265.56 m -206.20 263.92 l -205.81 263.91 l -205.18 263.91 204.66 264.03 204.27 264.27 c -203.88 264.51 203.69 264.82 203.69 265.21 c -203.69 265.49 203.79 265.72 203.98 265.92 c -204.18 266.11 204.41 266.20 204.70 266.20 c -205.18 266.20 205.68 265.99 206.20 265.56 c -h -209.71 267.00 m -209.71 260.64 l -210.87 260.64 l -210.87 261.83 l -211.47 260.94 212.22 260.50 213.10 260.50 c -213.65 260.50 214.09 260.67 214.42 261.02 c -214.75 261.37 214.91 261.84 214.91 262.43 c -214.91 267.00 l -213.76 267.00 l -213.76 262.80 l -213.76 262.33 213.69 262.00 213.55 261.79 c -213.41 261.59 213.18 261.49 212.86 261.49 c -212.16 261.49 211.49 261.96 210.87 262.88 c -210.87 267.00 l -h -219.07 267.15 m -218.48 267.15 218.03 266.98 217.70 266.64 c -217.37 266.31 217.21 265.84 217.21 265.24 c -217.21 261.50 l -216.41 261.50 l -216.41 260.64 l -217.21 260.64 l -217.21 259.48 l -218.36 259.37 l -218.36 260.64 l -220.02 260.64 l -220.02 261.50 l -218.36 261.50 l -218.36 265.03 l -218.36 265.86 218.72 266.28 219.44 266.28 c -219.59 266.28 219.78 266.25 219.99 266.20 c -219.99 267.00 l -219.64 267.10 219.33 267.15 219.07 267.15 c -h -221.79 267.00 m -228.73 263.53 l -221.79 260.06 l -221.79 261.03 l -226.79 263.53 l -221.79 266.03 l -h -f -70.500 270.50 m -508.50 270.50 l -S -508.00 270.00 m -502.00 264.00 l -502.00 276.00 l -h -f* -77.178 185.12 m -77.312 184.11 l -77.980 184.43 78.639 184.59 79.287 184.59 c -80.588 184.59 81.238 183.90 81.238 182.52 c -81.238 181.52 l -80.812 182.41 80.113 182.85 79.141 182.85 c -78.379 182.85 77.773 182.58 77.324 182.02 c -76.875 181.47 76.650 180.72 76.650 179.78 c -76.650 178.81 76.906 178.02 77.418 177.41 c -77.930 176.80 78.592 176.50 79.404 176.50 c -80.115 176.50 80.727 176.79 81.238 177.36 c -81.238 176.64 l -82.398 176.64 l -82.398 181.27 l -82.398 182.26 82.347 183.00 82.243 183.48 c -82.140 183.96 81.945 184.35 81.660 184.65 c -81.156 185.19 80.373 185.46 79.311 185.46 c -78.568 185.46 77.857 185.34 77.178 185.12 c -h -81.238 180.80 m -81.238 178.15 l -80.730 177.63 80.178 177.36 79.580 177.36 c -79.049 177.36 78.633 177.58 78.332 178.00 c -78.031 178.43 77.881 179.01 77.881 179.75 c -77.881 181.15 78.371 181.85 79.352 181.85 c -80.020 181.85 80.648 181.50 81.238 180.80 c -h -89.301 182.79 m -88.527 183.03 87.865 183.15 87.314 183.15 c -86.377 183.15 85.612 182.83 85.021 182.21 c -84.429 181.59 84.133 180.78 84.133 179.79 c -84.133 178.82 84.394 178.03 84.915 177.42 c -85.437 176.80 86.104 176.49 86.916 176.49 c -87.686 176.49 88.280 176.76 88.700 177.31 c -89.120 177.86 89.330 178.63 89.330 179.64 c -89.324 180.00 l -85.311 180.00 l -85.479 181.51 86.219 182.27 87.531 182.27 c -88.012 182.27 88.602 182.14 89.301 181.88 c -h -85.363 179.13 m -88.170 179.13 l -88.170 177.95 87.729 177.36 86.846 177.36 c -85.959 177.36 85.465 177.95 85.363 179.13 c -h -93.232 183.15 m -92.646 183.15 92.189 182.98 91.861 182.64 c -91.533 182.31 91.369 181.84 91.369 181.24 c -91.369 177.50 l -90.572 177.50 l -90.572 176.64 l -91.369 176.64 l -91.369 175.48 l -92.523 175.37 l -92.523 176.64 l -94.188 176.64 l -94.188 177.50 l -92.523 177.50 l -92.523 181.03 l -92.523 181.86 92.883 182.28 93.602 182.28 c -93.754 182.28 93.939 182.25 94.158 182.20 c -94.158 183.00 l -93.803 183.10 93.494 183.15 93.232 183.15 c -h -94.656 185.02 m -94.656 184.15 l -100.66 184.15 l -100.66 185.02 l -h -103.72 183.15 m -103.13 183.15 102.68 182.98 102.35 182.64 c -102.02 182.31 101.86 181.84 101.86 181.24 c -101.86 177.50 l -101.06 177.50 l -101.06 176.64 l -101.86 176.64 l -101.86 175.48 l -103.01 175.37 l -103.01 176.64 l -104.68 176.64 l -104.68 177.50 l -103.01 177.50 l -103.01 181.03 l -103.01 181.86 103.37 182.28 104.09 182.28 c -104.24 182.28 104.43 182.25 104.65 182.20 c -104.65 183.00 l -104.29 183.10 103.98 183.15 103.72 183.15 c -h -110.96 182.79 m -110.19 183.03 109.53 183.15 108.98 183.15 c -108.04 183.15 107.27 182.83 106.68 182.21 c -106.09 181.59 105.79 180.78 105.79 179.79 c -105.79 178.82 106.06 178.03 106.58 177.42 c -107.10 176.80 107.77 176.49 108.58 176.49 c -109.35 176.49 109.94 176.76 110.36 177.31 c -110.78 177.86 110.99 178.63 110.99 179.64 c -110.99 180.00 l -106.97 180.00 l -107.14 181.51 107.88 182.27 109.19 182.27 c -109.67 182.27 110.26 182.14 110.96 181.88 c -h -107.03 179.13 m -109.83 179.13 l -109.83 177.95 109.39 177.36 108.51 177.36 c -107.62 177.36 107.13 177.95 107.03 179.13 c -h -112.98 183.00 m -112.98 176.64 l -114.14 176.64 l -114.14 177.83 l -114.75 176.94 115.49 176.50 116.38 176.50 c -116.93 176.50 117.37 176.67 117.70 177.02 c -118.02 177.37 118.19 177.84 118.19 178.43 c -118.19 183.00 l -117.03 183.00 l -117.03 178.80 l -117.03 178.33 116.96 178.00 116.83 177.79 c -116.69 177.59 116.46 177.49 116.14 177.49 c -115.43 177.49 114.76 177.96 114.14 178.88 c -114.14 183.00 l -h -123.71 182.19 m -123.02 182.83 122.36 183.15 121.71 183.15 c -121.19 183.15 120.75 182.98 120.40 182.65 c -120.05 182.32 119.88 181.90 119.88 181.40 c -119.88 180.71 120.17 180.17 120.76 179.80 c -121.34 179.42 122.18 179.24 123.27 179.24 c -123.54 179.24 l -123.54 178.47 l -123.54 177.73 123.16 177.36 122.41 177.36 c -121.80 177.36 121.14 177.55 120.43 177.93 c -120.43 176.97 l -121.21 176.65 121.94 176.50 122.62 176.50 c -123.33 176.50 123.85 176.66 124.19 176.98 c -124.53 177.30 124.70 177.79 124.70 178.47 c -124.70 181.35 l -124.70 182.01 124.90 182.34 125.31 182.34 c -125.36 182.34 125.43 182.34 125.53 182.32 c -125.61 182.96 l -125.35 183.08 125.06 183.15 124.74 183.15 c -124.21 183.15 123.86 182.83 123.71 182.19 c -h -123.54 181.56 m -123.54 179.92 l -123.16 179.91 l -122.52 179.91 122.01 180.03 121.62 180.27 c -121.23 180.51 121.04 180.82 121.04 181.21 c -121.04 181.49 121.13 181.72 121.33 181.92 c -121.52 182.11 121.76 182.20 122.04 182.20 c -122.52 182.20 123.02 181.99 123.54 181.56 c -h -127.06 183.00 m -127.06 176.64 l -128.21 176.64 l -128.21 177.83 l -128.82 176.94 129.57 176.50 130.45 176.50 c -131.00 176.50 131.44 176.67 131.77 177.02 c -132.10 177.37 132.26 177.84 132.26 178.43 c -132.26 183.00 l -131.11 183.00 l -131.11 178.80 l -131.11 178.33 131.04 178.00 130.90 177.79 c -130.76 177.59 130.53 177.49 130.21 177.49 c -129.50 177.49 128.84 177.96 128.21 178.88 c -128.21 183.00 l -h -136.42 183.15 m -135.83 183.15 135.37 182.98 135.04 182.64 c -134.72 182.31 134.55 181.84 134.55 181.24 c -134.55 177.50 l -133.76 177.50 l -133.76 176.64 l -134.55 176.64 l -134.55 175.48 l -135.71 175.37 l -135.71 176.64 l -137.37 176.64 l -137.37 177.50 l -135.71 177.50 l -135.71 181.03 l -135.71 181.86 136.07 182.28 136.79 182.28 c -136.94 182.28 137.12 182.25 137.34 182.20 c -137.34 183.00 l -136.99 183.10 136.68 183.15 136.42 183.15 c -h -140.68 183.15 m -140.15 183.15 139.51 183.02 138.76 182.78 c -138.76 181.72 l -139.51 182.09 140.17 182.28 140.73 182.28 c -141.06 182.28 141.34 182.19 141.55 182.01 c -141.77 181.83 141.88 181.61 141.88 181.34 c -141.88 180.94 141.58 180.62 140.96 180.36 c -140.29 180.07 l -139.29 179.66 138.79 179.06 138.79 178.28 c -138.79 177.73 138.99 177.29 139.38 176.97 c -139.78 176.66 140.31 176.50 141.00 176.50 c -141.35 176.50 141.79 176.54 142.32 176.64 c -142.56 176.69 l -142.56 177.65 l -141.91 177.46 141.40 177.36 141.02 177.36 c -140.28 177.36 139.91 177.63 139.91 178.17 c -139.91 178.52 140.19 178.81 140.75 179.05 c -141.31 179.29 l -141.94 179.55 142.38 179.83 142.64 180.13 c -142.91 180.42 143.04 180.79 143.04 181.23 c -143.04 181.79 142.82 182.25 142.38 182.61 c -141.93 182.97 141.37 183.15 140.68 183.15 c -h -152.20 183.00 m -145.26 179.53 l -152.20 176.06 l -152.20 177.03 l -147.20 179.53 l -152.20 182.03 l -h -158.63 183.00 m -158.63 181.80 l -158.02 182.70 157.27 183.15 156.40 183.15 c -155.84 183.15 155.40 182.97 155.07 182.62 c -154.74 182.27 154.58 181.80 154.58 181.21 c -154.58 176.64 l -155.73 176.64 l -155.73 180.83 l -155.73 181.31 155.80 181.65 155.94 181.85 c -156.08 182.05 156.31 182.15 156.64 182.15 c -157.34 182.15 158.00 181.69 158.63 180.76 c -158.63 176.64 l -159.78 176.64 l -159.78 183.00 l -h -162.10 183.00 m -162.10 176.64 l -163.25 176.64 l -163.25 177.83 l -163.86 176.94 164.61 176.50 165.49 176.50 c -166.04 176.50 166.48 176.67 166.81 177.02 c -167.14 177.37 167.30 177.84 167.30 178.43 c -167.30 183.00 l -166.15 183.00 l -166.15 178.80 l -166.15 178.33 166.08 178.00 165.94 177.79 c -165.80 177.59 165.57 177.49 165.25 177.49 c -164.54 177.49 163.88 177.96 163.25 178.88 c -163.25 183.00 l -h -171.23 183.15 m -170.71 183.15 170.06 183.02 169.31 182.78 c -169.31 181.72 l -170.06 182.09 170.72 182.28 171.28 182.28 c -171.61 182.28 171.89 182.19 172.11 182.01 c -172.32 181.83 172.43 181.61 172.43 181.34 c -172.43 180.94 172.13 180.62 171.51 180.36 c -170.84 180.07 l -169.84 179.66 169.35 179.06 169.35 178.28 c -169.35 177.73 169.54 177.29 169.93 176.97 c -170.33 176.66 170.87 176.50 171.55 176.50 c -171.90 176.50 172.34 176.54 172.87 176.64 c -173.11 176.69 l -173.11 177.65 l -172.46 177.46 171.95 177.36 171.57 177.36 c -170.83 177.36 170.46 177.63 170.46 178.17 c -170.46 178.52 170.74 178.81 171.30 179.05 c -171.86 179.29 l -172.49 179.55 172.93 179.83 173.20 180.13 c -173.46 180.42 173.59 180.79 173.59 181.23 c -173.59 181.79 173.37 182.25 172.93 182.61 c -172.48 182.97 171.92 183.15 171.23 183.15 c -h -178.15 183.15 m -177.29 183.15 176.57 182.83 176.01 182.19 c -175.44 181.55 175.16 180.75 175.16 179.78 c -175.16 178.75 175.44 177.94 176.00 177.36 c -176.56 176.79 177.34 176.50 178.35 176.50 c -178.84 176.50 179.40 176.56 180.01 176.70 c -180.01 177.67 l -179.36 177.48 178.83 177.38 178.42 177.38 c -177.83 177.38 177.35 177.60 177.00 178.05 c -176.64 178.49 176.46 179.08 176.46 179.82 c -176.46 180.53 176.64 181.11 177.01 181.55 c -177.38 181.99 177.86 182.21 178.45 182.21 c -178.98 182.21 179.52 182.08 180.08 181.81 c -180.08 182.81 l -179.33 183.03 178.69 183.15 178.15 183.15 c -h -184.30 183.15 m -183.39 183.15 182.66 182.84 182.12 182.24 c -181.58 181.64 181.30 180.83 181.30 179.82 c -181.30 178.79 181.58 177.99 182.12 177.39 c -182.67 176.79 183.41 176.50 184.34 176.50 c -185.27 176.50 186.01 176.79 186.56 177.39 c -187.10 177.99 187.38 178.79 187.38 179.81 c -187.38 180.85 187.10 181.66 186.55 182.26 c -186.01 182.85 185.26 183.15 184.30 183.15 c -h -184.32 182.28 m -185.54 182.28 186.15 181.46 186.15 179.81 c -186.15 178.18 185.55 177.36 184.34 177.36 c -183.14 177.36 182.54 178.18 182.54 179.82 c -182.54 181.46 183.13 182.28 184.32 182.28 c -h -189.18 185.31 m -189.18 176.64 l -190.33 176.64 l -190.33 177.83 l -190.81 176.94 191.52 176.50 192.46 176.50 c -193.23 176.50 193.83 176.78 194.27 177.33 c -194.71 177.89 194.93 178.66 194.93 179.62 c -194.93 180.68 194.68 181.53 194.18 182.18 c -193.68 182.82 193.03 183.15 192.21 183.15 c -191.46 183.15 190.83 182.86 190.33 182.28 c -190.33 185.31 l -h -190.33 181.48 m -190.93 182.01 191.49 182.28 192.03 182.28 c -193.14 182.28 193.70 181.43 193.70 179.74 c -193.70 178.25 193.21 177.50 192.22 177.50 c -191.58 177.50 190.95 177.85 190.33 178.55 c -h -201.40 182.79 m -200.62 183.03 199.96 183.15 199.41 183.15 c -198.47 183.15 197.71 182.83 197.12 182.21 c -196.52 181.59 196.23 180.78 196.23 179.79 c -196.23 178.82 196.49 178.03 197.01 177.42 c -197.53 176.80 198.20 176.49 199.01 176.49 c -199.78 176.49 200.38 176.76 200.80 177.31 c -201.22 177.86 201.43 178.63 201.43 179.64 c -201.42 180.00 l -197.41 180.00 l -197.57 181.51 198.31 182.27 199.63 182.27 c -200.11 182.27 200.70 182.14 201.40 181.88 c -h -197.46 179.13 m -200.27 179.13 l -200.27 177.95 199.82 177.36 198.94 177.36 c -198.05 177.36 197.56 177.95 197.46 179.13 c -h -207.50 183.00 m -207.50 181.80 l -207.03 182.70 206.33 183.15 205.38 183.15 c -204.62 183.15 204.01 182.87 203.57 182.31 c -203.13 181.75 202.91 180.99 202.91 180.02 c -202.91 178.96 203.16 178.11 203.66 177.46 c -204.16 176.82 204.81 176.50 205.63 176.50 c -206.38 176.50 207.01 176.79 207.50 177.36 c -207.50 173.75 l -208.66 173.75 l -208.66 183.00 l -h -207.50 178.15 m -206.90 177.63 206.34 177.36 205.80 177.36 c -204.70 177.36 204.14 178.21 204.14 179.90 c -204.14 181.39 204.64 182.13 205.62 182.13 c -206.26 182.13 206.89 181.78 207.50 181.08 c -h -209.82 185.02 m -209.82 184.15 l -215.82 184.15 l -215.82 185.02 l -h -218.88 183.15 m -218.29 183.15 217.84 182.98 217.51 182.64 c -217.18 182.31 217.02 181.84 217.02 181.24 c -217.02 177.50 l -216.22 177.50 l -216.22 176.64 l -217.02 176.64 l -217.02 175.48 l -218.17 175.37 l -218.17 176.64 l -219.84 176.64 l -219.84 177.50 l -218.17 177.50 l -218.17 181.03 l -218.17 181.86 218.53 182.28 219.25 182.28 c -219.40 182.28 219.59 182.25 219.81 182.20 c -219.81 183.00 l -219.45 183.10 219.14 183.15 218.88 183.15 c -h -223.95 183.15 m -223.04 183.15 222.31 182.84 221.77 182.24 c -221.23 181.64 220.96 180.83 220.96 179.82 c -220.96 178.79 221.23 177.99 221.77 177.39 c -222.32 176.79 223.06 176.50 223.99 176.50 c -224.92 176.50 225.66 176.79 226.21 177.39 c -226.75 177.99 227.03 178.79 227.03 179.81 c -227.03 180.85 226.75 181.66 226.21 182.26 c -225.66 182.85 224.91 183.15 223.95 183.15 c -h -223.97 182.28 m -225.19 182.28 225.80 181.46 225.80 179.81 c -225.80 178.18 225.20 177.36 223.99 177.36 c -222.79 177.36 222.19 178.18 222.19 179.82 c -222.19 181.46 222.78 182.28 223.97 182.28 c -h -228.83 183.00 m -228.83 173.75 l -229.98 173.75 l -229.98 179.72 l -232.68 176.64 l -233.92 176.64 l -231.35 179.61 l -234.46 183.00 l -232.98 183.00 l -229.98 179.74 l -229.98 183.00 l -h -240.51 182.79 m -239.73 183.03 239.07 183.15 238.52 183.15 c -237.58 183.15 236.82 182.83 236.23 182.21 c -235.64 181.59 235.34 180.78 235.34 179.79 c -235.34 178.82 235.60 178.03 236.12 177.42 c -236.64 176.80 237.31 176.49 238.12 176.49 c -238.89 176.49 239.49 176.76 239.91 177.31 c -240.33 177.86 240.54 178.63 240.54 179.64 c -240.53 180.00 l -236.52 180.00 l -236.69 181.51 237.43 182.27 238.74 182.27 c -239.22 182.27 239.81 182.14 240.51 181.88 c -h -236.57 179.13 m -239.38 179.13 l -239.38 177.95 238.94 177.36 238.05 177.36 c -237.17 177.36 236.67 177.95 236.57 179.13 c -h -242.53 183.00 m -242.53 176.64 l -243.68 176.64 l -243.68 177.83 l -244.29 176.94 245.04 176.50 245.92 176.50 c -246.47 176.50 246.91 176.67 247.24 177.02 c -247.57 177.37 247.73 177.84 247.73 178.43 c -247.73 183.00 l -246.58 183.00 l -246.58 178.80 l -246.58 178.33 246.51 178.00 246.37 177.79 c -246.23 177.59 246.00 177.49 245.68 177.49 c -244.97 177.49 244.31 177.96 243.68 178.88 c -243.68 183.00 l -h -250.12 183.00 m -257.06 179.53 l -250.12 176.06 l -250.12 177.03 l -255.12 179.53 l -250.12 182.03 l -h -f -70.500 186.50 m -508.50 186.50 l -S -508.00 186.00 m -502.00 180.00 l -502.00 192.00 l -h -f* -322.13 117.00 m -322.13 115.80 l -321.52 116.70 320.78 117.15 319.90 117.15 c -319.35 117.15 318.90 116.97 318.58 116.62 c -318.25 116.27 318.08 115.80 318.08 115.21 c -318.08 110.64 l -319.24 110.64 l -319.24 114.83 l -319.24 115.31 319.31 115.65 319.45 115.85 c -319.58 116.05 319.82 116.15 320.14 116.15 c -320.84 116.15 321.51 115.69 322.13 114.76 c -322.13 110.64 l -323.29 110.64 l -323.29 117.00 l -h -325.60 117.00 m -325.60 110.64 l -326.76 110.64 l -326.76 111.83 l -327.37 110.94 328.11 110.50 328.99 110.50 c -329.54 110.50 329.98 110.67 330.31 111.02 c -330.64 111.37 330.80 111.84 330.80 112.43 c -330.80 117.00 l -329.65 117.00 l -329.65 112.80 l -329.65 112.33 329.58 112.00 329.44 111.79 c -329.30 111.59 329.07 111.49 328.75 111.49 c -328.05 111.49 327.38 111.96 326.76 112.88 c -326.76 117.00 l -h -334.74 117.15 m -334.21 117.15 333.57 117.02 332.81 116.78 c -332.81 115.72 l -333.57 116.09 334.22 116.28 334.78 116.28 c -335.12 116.28 335.39 116.19 335.61 116.01 c -335.83 115.83 335.94 115.61 335.94 115.34 c -335.94 114.94 335.63 114.62 335.02 114.36 c -334.34 114.07 l -333.35 113.66 332.85 113.06 332.85 112.28 c -332.85 111.73 333.05 111.29 333.44 110.97 c -333.83 110.66 334.37 110.50 335.05 110.50 c -335.41 110.50 335.85 110.54 336.37 110.64 c -336.61 110.69 l -336.61 111.65 l -335.97 111.46 335.46 111.36 335.08 111.36 c -334.33 111.36 333.96 111.63 333.96 112.17 c -333.96 112.52 334.24 112.81 334.81 113.05 c -335.36 113.29 l -335.99 113.55 336.44 113.83 336.70 114.13 c -336.96 114.42 337.09 114.79 337.09 115.23 c -337.09 115.79 336.87 116.25 336.43 116.61 c -335.99 116.97 335.42 117.15 334.74 117.15 c -h -341.65 117.15 m -340.79 117.15 340.08 116.83 339.51 116.19 c -338.95 115.55 338.66 114.75 338.66 113.78 c -338.66 112.75 338.94 111.94 339.50 111.36 c -340.06 110.79 340.85 110.50 341.85 110.50 c -342.35 110.50 342.90 110.56 343.51 110.70 c -343.51 111.67 l -342.86 111.48 342.33 111.38 341.92 111.38 c -341.33 111.38 340.86 111.60 340.50 112.05 c -340.14 112.49 339.96 113.08 339.96 113.82 c -339.96 114.53 340.15 115.11 340.51 115.55 c -340.88 115.99 341.36 116.21 341.96 116.21 c -342.48 116.21 343.03 116.08 343.58 115.81 c -343.58 116.81 l -342.84 117.03 342.19 117.15 341.65 117.15 c -h -347.80 117.15 m -346.89 117.15 346.17 116.84 345.62 116.24 c -345.08 115.64 344.81 114.83 344.81 113.82 c -344.81 112.79 345.08 111.99 345.63 111.39 c -346.17 110.79 346.91 110.50 347.84 110.50 c -348.78 110.50 349.52 110.79 350.06 111.39 c -350.61 111.99 350.88 112.79 350.88 113.81 c -350.88 114.85 350.61 115.66 350.06 116.26 c -349.51 116.85 348.76 117.15 347.80 117.15 c -h -347.82 116.28 m -349.04 116.28 349.65 115.46 349.65 113.81 c -349.65 112.18 349.05 111.36 347.84 111.36 c -346.64 111.36 346.04 112.18 346.04 113.82 c -346.04 115.46 346.63 116.28 347.82 116.28 c -h -352.68 119.31 m -352.68 110.64 l -353.84 110.64 l -353.84 111.83 l -354.31 110.94 355.02 110.50 355.96 110.50 c -356.73 110.50 357.33 110.78 357.77 111.33 c -358.21 111.89 358.43 112.66 358.43 113.62 c -358.43 114.68 358.18 115.53 357.68 116.18 c -357.19 116.82 356.53 117.15 355.72 117.15 c -354.96 117.15 354.34 116.86 353.84 116.28 c -353.84 119.31 l -h -353.84 115.48 m -354.43 116.01 355.00 116.28 355.54 116.28 c -356.65 116.28 357.20 115.43 357.20 113.74 c -357.20 112.25 356.71 111.50 355.72 111.50 c -355.08 111.50 354.45 111.85 353.84 112.55 c -h -364.90 116.79 m -364.13 117.03 363.46 117.15 362.91 117.15 c -361.98 117.15 361.21 116.83 360.62 116.21 c -360.03 115.59 359.73 114.78 359.73 113.79 c -359.73 112.82 359.99 112.03 360.51 111.42 c -361.04 110.80 361.70 110.49 362.52 110.49 c -363.29 110.49 363.88 110.76 364.30 111.31 c -364.72 111.86 364.93 112.63 364.93 113.64 c -364.92 114.00 l -360.91 114.00 l -361.08 115.51 361.82 116.27 363.13 116.27 c -363.61 116.27 364.20 116.14 364.90 115.88 c -h -360.96 113.13 m -363.77 113.13 l -363.77 111.95 363.33 111.36 362.45 111.36 c -361.56 111.36 361.06 111.95 360.96 113.13 c -h -371.01 117.00 m -371.01 115.80 l -370.54 116.70 369.83 117.15 368.88 117.15 c -368.12 117.15 367.52 116.87 367.08 116.31 c -366.64 115.75 366.42 114.99 366.42 114.02 c -366.42 112.96 366.67 112.11 367.17 111.46 c -367.66 110.82 368.32 110.50 369.13 110.50 c -369.88 110.50 370.51 110.79 371.01 111.36 c -371.01 107.75 l -372.17 107.75 l -372.17 117.00 l -h -371.01 112.15 m -370.41 111.63 369.84 111.36 369.31 111.36 c -368.20 111.36 367.65 112.21 367.65 113.90 c -367.65 115.39 368.14 116.13 369.12 116.13 c -369.77 116.13 370.39 115.78 371.01 115.08 c -h -373.32 119.02 m -373.32 118.15 l -379.32 118.15 l -379.32 119.02 l -h -382.38 117.15 m -381.80 117.15 381.34 116.98 381.01 116.64 c -380.69 116.31 380.52 115.84 380.52 115.24 c -380.52 111.50 l -379.72 111.50 l -379.72 110.64 l -380.52 110.64 l -380.52 109.48 l -381.68 109.37 l -381.68 110.64 l -383.34 110.64 l -383.34 111.50 l -381.68 111.50 l -381.68 115.03 l -381.68 115.86 382.04 116.28 382.75 116.28 c -382.91 116.28 383.09 116.25 383.31 116.20 c -383.31 117.00 l -382.96 117.10 382.65 117.15 382.38 117.15 c -h -387.45 117.15 m -386.54 117.15 385.82 116.84 385.27 116.24 c -384.73 115.64 384.46 114.83 384.46 113.82 c -384.46 112.79 384.73 111.99 385.28 111.39 c -385.82 110.79 386.56 110.50 387.49 110.50 c -388.43 110.50 389.17 110.79 389.71 111.39 c -390.26 111.99 390.53 112.79 390.53 113.81 c -390.53 114.85 390.26 115.66 389.71 116.26 c -389.16 116.85 388.41 117.15 387.45 117.15 c -h -387.47 116.28 m -388.69 116.28 389.30 115.46 389.30 113.81 c -389.30 112.18 388.70 111.36 387.49 111.36 c -386.29 111.36 385.69 112.18 385.69 113.82 c -385.69 115.46 386.28 116.28 387.47 116.28 c -h -392.33 117.00 m -392.33 107.75 l -393.49 107.75 l -393.49 113.72 l -396.18 110.64 l -397.43 110.64 l -394.85 113.61 l -397.96 117.00 l -396.48 117.00 l -393.49 113.74 l -393.49 117.00 l -h -404.01 116.79 m -403.24 117.03 402.58 117.15 402.03 117.15 c -401.09 117.15 400.32 116.83 399.73 116.21 c -399.14 115.59 398.84 114.78 398.84 113.79 c -398.84 112.82 399.10 112.03 399.63 111.42 c -400.15 110.80 400.81 110.49 401.63 110.49 c -402.40 110.49 402.99 110.76 403.41 111.31 c -403.83 111.86 404.04 112.63 404.04 113.64 c -404.04 114.00 l -400.02 114.00 l -400.19 115.51 400.93 116.27 402.24 116.27 c -402.72 116.27 403.31 116.14 404.01 115.88 c -h -400.07 113.13 m -402.88 113.13 l -402.88 111.95 402.44 111.36 401.56 111.36 c -400.67 111.36 400.18 111.95 400.07 113.13 c -h -406.03 117.00 m -406.03 110.64 l -407.19 110.64 l -407.19 111.83 l -407.80 110.94 408.54 110.50 409.43 110.50 c -409.98 110.50 410.42 110.67 410.74 111.02 c -411.07 111.37 411.24 111.84 411.24 112.43 c -411.24 117.00 l -410.08 117.00 l -410.08 112.80 l -410.08 112.33 410.01 112.00 409.87 111.79 c -409.74 111.59 409.51 111.49 409.19 111.49 c -408.48 111.49 407.81 111.96 407.19 112.88 c -407.19 117.00 l -h -413.50 118.88 m -413.50 118.45 l -413.87 118.34 414.06 117.90 414.06 117.12 c -414.06 117.00 l -413.50 117.00 l -413.50 115.55 l -414.95 115.55 l -414.95 116.81 l -414.95 118.09 414.46 118.78 413.50 118.88 c -h -422.76 117.15 m -422.23 117.15 421.59 117.02 420.84 116.78 c -420.84 115.72 l -421.59 116.09 422.25 116.28 422.81 116.28 c -423.14 116.28 423.42 116.19 423.63 116.01 c -423.85 115.83 423.96 115.61 423.96 115.34 c -423.96 114.94 423.66 114.62 423.04 114.36 c -422.37 114.07 l -421.37 113.66 420.88 113.06 420.88 112.28 c -420.88 111.73 421.07 111.29 421.46 110.97 c -421.86 110.66 422.39 110.50 423.08 110.50 c -423.43 110.50 423.87 110.54 424.40 110.64 c -424.64 110.69 l -424.64 111.65 l -423.99 111.46 423.48 111.36 423.10 111.36 c -422.36 111.36 421.99 111.63 421.99 112.17 c -421.99 112.52 422.27 112.81 422.83 113.05 c -423.39 113.29 l -424.02 113.55 424.46 113.83 424.72 114.13 c -424.99 114.42 425.12 114.79 425.12 115.23 c -425.12 115.79 424.90 116.25 424.46 116.61 c -424.01 116.97 423.45 117.15 422.76 117.15 c -h -431.86 116.79 m -431.08 117.03 430.42 117.15 429.87 117.15 c -428.93 117.15 428.17 116.83 427.58 116.21 c -426.98 115.59 426.69 114.78 426.69 113.79 c -426.69 112.82 426.95 112.03 427.47 111.42 c -427.99 110.80 428.66 110.49 429.47 110.49 c -430.24 110.49 430.83 110.76 431.25 111.31 c -431.67 111.86 431.88 112.63 431.88 113.64 c -431.88 114.00 l -427.87 114.00 l -428.03 115.51 428.77 116.27 430.09 116.27 c -430.57 116.27 431.16 116.14 431.86 115.88 c -h -427.92 113.13 m -430.72 113.13 l -430.72 111.95 430.28 111.36 429.40 111.36 c -428.51 111.36 428.02 111.95 427.92 113.13 c -h -433.88 117.00 m -433.88 110.64 l -435.03 110.64 l -435.03 111.83 l -435.49 110.94 436.15 110.50 437.02 110.50 c -437.14 110.50 437.26 110.51 437.39 110.53 c -437.39 111.60 l -437.19 111.54 437.02 111.50 436.87 111.50 c -436.13 111.50 435.52 111.94 435.03 112.80 c -435.03 117.00 l -h -440.11 117.00 m -437.74 110.64 l -438.89 110.64 l -440.74 115.59 l -442.70 110.64 l -443.77 110.64 l -441.26 117.00 l -h -445.00 117.00 m -445.00 110.64 l -446.15 110.64 l -446.15 117.00 l -h -445.00 109.48 m -445.00 108.33 l -446.15 108.33 l -446.15 109.48 l -h -450.95 117.15 m -450.09 117.15 449.38 116.83 448.81 116.19 c -448.25 115.55 447.96 114.75 447.96 113.78 c -447.96 112.75 448.24 111.94 448.80 111.36 c -449.36 110.79 450.15 110.50 451.15 110.50 c -451.65 110.50 452.20 110.56 452.81 110.70 c -452.81 111.67 l -452.16 111.48 451.63 111.38 451.22 111.38 c -450.63 111.38 450.16 111.60 449.80 112.05 c -449.44 112.49 449.26 113.08 449.26 113.82 c -449.26 114.53 449.45 115.11 449.81 115.55 c -450.18 115.99 450.66 116.21 451.26 116.21 c -451.78 116.21 452.33 116.08 452.88 115.81 c -452.88 116.81 l -452.14 117.03 451.49 117.15 450.95 117.15 c -h -459.28 116.79 m -458.50 117.03 457.84 117.15 457.29 117.15 c -456.35 117.15 455.59 116.83 455.00 116.21 c -454.41 115.59 454.11 114.78 454.11 113.79 c -454.11 112.82 454.37 112.03 454.89 111.42 c -455.41 110.80 456.08 110.49 456.89 110.49 c -457.66 110.49 458.26 110.76 458.68 111.31 c -459.10 111.86 459.31 112.63 459.31 113.64 c -459.30 114.00 l -455.29 114.00 l -455.46 115.51 456.20 116.27 457.51 116.27 c -457.99 116.27 458.58 116.14 459.28 115.88 c -h -455.34 113.13 m -458.15 113.13 l -458.15 111.95 457.71 111.36 456.82 111.36 c -455.94 111.36 455.44 111.95 455.34 113.13 c -h -464.98 117.22 m -463.63 117.22 462.59 116.82 461.86 116.03 c -461.12 115.24 460.75 114.12 460.75 112.67 c -460.75 111.22 461.13 110.10 461.88 109.31 c -462.62 108.51 463.68 108.11 465.04 108.11 c -465.81 108.11 466.72 108.24 467.77 108.49 c -467.77 109.65 l -466.58 109.24 465.66 109.03 465.02 109.03 c -464.08 109.03 463.35 109.35 462.83 109.99 c -462.32 110.62 462.06 111.52 462.06 112.68 c -462.06 113.79 462.34 114.66 462.89 115.30 c -463.44 115.94 464.19 116.26 465.14 116.26 c -465.96 116.26 466.84 116.00 467.78 115.50 c -467.78 116.55 l -466.92 117.00 465.99 117.22 464.98 117.22 c -h -472.88 116.19 m -472.19 116.83 471.53 117.15 470.88 117.15 c -470.36 117.15 469.92 116.98 469.57 116.65 c -469.22 116.32 469.05 115.90 469.05 115.40 c -469.05 114.71 469.34 114.17 469.93 113.80 c -470.51 113.42 471.35 113.24 472.44 113.24 c -472.71 113.24 l -472.71 112.47 l -472.71 111.73 472.33 111.36 471.58 111.36 c -470.97 111.36 470.31 111.55 469.60 111.93 c -469.60 110.97 l -470.38 110.65 471.11 110.50 471.79 110.50 c -472.50 110.50 473.02 110.66 473.36 110.98 c -473.70 111.30 473.87 111.79 473.87 112.47 c -473.87 115.35 l -473.87 116.01 474.07 116.34 474.48 116.34 c -474.53 116.34 474.60 116.34 474.70 116.32 c -474.78 116.96 l -474.52 117.08 474.23 117.15 473.91 117.15 c -473.38 117.15 473.03 116.83 472.88 116.19 c -h -472.71 115.56 m -472.71 113.92 l -472.33 113.91 l -471.69 113.91 471.18 114.03 470.79 114.27 c -470.40 114.51 470.21 114.82 470.21 115.21 c -470.21 115.49 470.30 115.72 470.50 115.92 c -470.69 116.11 470.93 116.20 471.21 116.20 c -471.69 116.20 472.19 115.99 472.71 115.56 c -h -478.14 117.15 m -477.55 117.15 477.10 116.98 476.77 116.64 c -476.44 116.31 476.28 115.84 476.28 115.24 c -476.28 111.50 l -475.48 111.50 l -475.48 110.64 l -476.28 110.64 l -476.28 109.48 l -477.43 109.37 l -477.43 110.64 l -479.09 110.64 l -479.09 111.50 l -477.43 111.50 l -477.43 115.03 l -477.43 115.86 477.79 116.28 478.51 116.28 c -478.66 116.28 478.85 116.25 479.06 116.20 c -479.06 117.00 l -478.71 117.10 478.40 117.15 478.14 117.15 c -h -484.00 116.19 m -483.31 116.83 482.64 117.15 482.00 117.15 c -481.47 117.15 481.04 116.98 480.69 116.65 c -480.34 116.32 480.17 115.90 480.17 115.40 c -480.17 114.71 480.46 114.17 481.04 113.80 c -481.63 113.42 482.46 113.24 483.55 113.24 c -483.83 113.24 l -483.83 112.47 l -483.83 111.73 483.45 111.36 482.69 111.36 c -482.08 111.36 481.42 111.55 480.72 111.93 c -480.72 110.97 l -481.49 110.65 482.22 110.50 482.90 110.50 c -483.61 110.50 484.14 110.66 484.48 110.98 c -484.81 111.30 484.98 111.79 484.98 112.47 c -484.98 115.35 l -484.98 116.01 485.19 116.34 485.59 116.34 c -485.64 116.34 485.72 116.34 485.81 116.32 c -485.90 116.96 l -485.63 117.08 485.35 117.15 485.03 117.15 c -484.49 117.15 484.15 116.83 484.00 116.19 c -h -483.83 115.56 m -483.83 113.92 l -483.44 113.91 l -482.81 113.91 482.30 114.03 481.91 114.27 c -481.52 114.51 481.32 114.82 481.32 115.21 c -481.32 115.49 481.42 115.72 481.61 115.92 c -481.81 116.11 482.05 116.20 482.33 116.20 c -482.81 116.20 483.31 115.99 483.83 115.56 c -h -487.34 117.00 m -487.34 107.75 l -488.50 107.75 l -488.50 117.00 l -h -493.30 117.15 m -492.39 117.15 491.67 116.84 491.12 116.24 c -490.58 115.64 490.31 114.83 490.31 113.82 c -490.31 112.79 490.58 111.99 491.13 111.39 c -491.67 110.79 492.41 110.50 493.34 110.50 c -494.28 110.50 495.02 110.79 495.56 111.39 c -496.11 111.99 496.38 112.79 496.38 113.81 c -496.38 114.85 496.11 115.66 495.56 116.26 c -495.01 116.85 494.26 117.15 493.30 117.15 c -h -493.32 116.28 m -494.54 116.28 495.15 115.46 495.15 113.81 c -495.15 112.18 494.55 111.36 493.34 111.36 c -492.14 111.36 491.54 112.18 491.54 113.82 c -491.54 115.46 492.13 116.28 493.32 116.28 c -h -498.21 119.12 m -498.34 118.11 l -499.01 118.43 499.67 118.59 500.32 118.59 c -501.62 118.59 502.27 117.90 502.27 116.52 c -502.27 115.52 l -501.84 116.41 501.14 116.85 500.17 116.85 c -499.41 116.85 498.80 116.58 498.35 116.02 c -497.90 115.47 497.68 114.72 497.68 113.78 c -497.68 112.81 497.94 112.02 498.45 111.41 c -498.96 110.80 499.62 110.50 500.43 110.50 c -501.14 110.50 501.76 110.79 502.27 111.36 c -502.27 110.64 l -503.43 110.64 l -503.43 115.27 l -503.43 116.26 503.38 117.00 503.27 117.48 c -503.17 117.96 502.97 118.35 502.69 118.65 c -502.19 119.19 501.40 119.46 500.34 119.46 c -499.60 119.46 498.89 119.34 498.21 119.12 c -h -502.27 114.80 m -502.27 112.15 l -501.76 111.63 501.21 111.36 500.61 111.36 c -500.08 111.36 499.66 111.58 499.36 112.00 c -499.06 112.43 498.91 113.01 498.91 113.75 c -498.91 115.15 499.40 115.85 500.38 115.85 c -501.05 115.85 501.68 115.50 502.27 114.80 c -h -f -[ 5.0000 5.0000] 0.0000 d -508.50 120.50 m -70.500 120.50 l -S -[] 0.0000 d -70.000 120.00 m -76.000 114.00 l -76.000 126.00 l -h -f* -382.06 285.15 m -381.48 285.15 381.02 284.98 380.69 284.64 c -380.37 284.31 380.20 283.84 380.20 283.24 c -380.20 279.50 l -379.40 279.50 l -379.40 278.64 l -380.20 278.64 l -380.20 277.48 l -381.36 277.37 l -381.36 278.64 l -383.02 278.64 l -383.02 279.50 l -381.36 279.50 l -381.36 283.03 l -381.36 283.86 381.71 284.28 382.43 284.28 c -382.59 284.28 382.77 284.25 382.99 284.20 c -382.99 285.00 l -382.63 285.10 382.33 285.15 382.06 285.15 c -h -387.13 285.15 m -386.22 285.15 385.50 284.84 384.95 284.24 c -384.41 283.64 384.14 282.83 384.14 281.82 c -384.14 280.79 384.41 279.99 384.96 279.39 c -385.50 278.79 386.24 278.50 387.17 278.50 c -388.11 278.50 388.85 278.79 389.39 279.39 c -389.94 279.99 390.21 280.79 390.21 281.81 c -390.21 282.85 389.94 283.66 389.39 284.26 c -388.84 284.85 388.09 285.15 387.13 285.15 c -h -387.15 284.28 m -388.37 284.28 388.98 283.46 388.98 281.81 c -388.98 280.18 388.38 279.36 387.17 279.36 c -385.97 279.36 385.37 280.18 385.37 281.82 c -385.37 283.46 385.96 284.28 387.15 284.28 c -h -392.01 285.00 m -392.01 275.75 l -393.17 275.75 l -393.17 281.72 l -395.86 278.64 l -397.11 278.64 l -394.53 281.61 l -397.64 285.00 l -396.16 285.00 l -393.17 281.74 l -393.17 285.00 l -h -403.69 284.79 m -402.92 285.03 402.26 285.15 401.71 285.15 c -400.77 285.15 400.00 284.83 399.41 284.21 c -398.82 283.59 398.52 282.78 398.52 281.79 c -398.52 280.82 398.78 280.03 399.31 279.42 c -399.83 278.80 400.49 278.49 401.31 278.49 c -402.08 278.49 402.67 278.76 403.09 279.31 c -403.51 279.86 403.72 280.63 403.72 281.64 c -403.71 282.00 l -399.70 282.00 l -399.87 283.51 400.61 284.27 401.92 284.27 c -402.40 284.27 402.99 284.14 403.69 283.88 c -h -399.75 281.13 m -402.56 281.13 l -402.56 279.95 402.12 279.36 401.24 279.36 c -400.35 279.36 399.86 279.95 399.75 281.13 c -h -405.71 285.00 m -405.71 278.64 l -406.87 278.64 l -406.87 279.83 l -407.48 278.94 408.22 278.50 409.11 278.50 c -409.66 278.50 410.10 278.67 410.42 279.02 c -410.75 279.37 410.92 279.84 410.92 280.43 c -410.92 285.00 l -409.76 285.00 l -409.76 280.80 l -409.76 280.33 409.69 280.00 409.55 279.79 c -409.42 279.59 409.19 279.49 408.87 279.49 c -408.16 279.49 407.49 279.96 406.87 280.88 c -406.87 285.00 l -h -413.18 286.88 m -413.18 286.45 l -413.55 286.34 413.74 285.90 413.74 285.12 c -413.74 285.00 l -413.18 285.00 l -413.18 283.55 l -414.62 283.55 l -414.62 284.81 l -414.62 286.09 414.14 286.78 413.18 286.88 c -h -422.44 285.15 m -421.91 285.15 421.27 285.02 420.52 284.78 c -420.52 283.72 l -421.27 284.09 421.93 284.28 422.49 284.28 c -422.82 284.28 423.10 284.19 423.31 284.01 c -423.53 283.83 423.64 283.61 423.64 283.34 c -423.64 282.94 423.34 282.62 422.72 282.36 c -422.05 282.07 l -421.05 281.66 420.55 281.06 420.55 280.28 c -420.55 279.73 420.75 279.29 421.14 278.97 c -421.54 278.66 422.07 278.50 422.76 278.50 c -423.11 278.50 423.55 278.54 424.08 278.64 c -424.32 278.69 l -424.32 279.65 l -423.67 279.46 423.16 279.36 422.78 279.36 c -422.04 279.36 421.67 279.63 421.67 280.17 c -421.67 280.52 421.95 280.81 422.51 281.05 c -423.07 281.29 l -423.70 281.55 424.14 281.83 424.40 282.13 c -424.67 282.42 424.80 282.79 424.80 283.23 c -424.80 283.79 424.58 284.25 424.13 284.61 c -423.69 284.97 423.13 285.15 422.44 285.15 c -h -431.54 284.79 m -430.76 285.03 430.10 285.15 429.55 285.15 c -428.61 285.15 427.85 284.83 427.25 284.21 c -426.66 283.59 426.37 282.78 426.37 281.79 c -426.37 280.82 426.63 280.03 427.15 279.42 c -427.67 278.80 428.34 278.49 429.15 278.49 c -429.92 278.49 430.51 278.76 430.93 279.31 c -431.35 279.86 431.56 280.63 431.56 281.64 c -431.56 282.00 l -427.54 282.00 l -427.71 283.51 428.45 284.27 429.77 284.27 c -430.25 284.27 430.84 284.14 431.54 283.88 c -h -427.60 281.13 m -430.40 281.13 l -430.40 279.95 429.96 279.36 429.08 279.36 c -428.19 279.36 427.70 279.95 427.60 281.13 c -h -433.56 285.00 m -433.56 278.64 l -434.71 278.64 l -434.71 279.83 l -435.17 278.94 435.83 278.50 436.70 278.50 c -436.82 278.50 436.94 278.51 437.07 278.53 c -437.07 279.60 l -436.87 279.54 436.70 279.50 436.54 279.50 c -435.81 279.50 435.20 279.94 434.71 280.80 c -434.71 285.00 l -h -439.79 285.00 m -437.42 278.64 l -438.57 278.64 l -440.42 283.59 l -442.38 278.64 l -443.45 278.64 l -440.94 285.00 l -h -444.68 285.00 m -444.68 278.64 l -445.83 278.64 l -445.83 285.00 l -h -444.68 277.48 m -444.68 276.33 l -445.83 276.33 l -445.83 277.48 l -h -450.63 285.15 m -449.77 285.15 449.06 284.83 448.49 284.19 c -447.93 283.55 447.64 282.75 447.64 281.78 c -447.64 280.75 447.92 279.94 448.48 279.36 c -449.04 278.79 449.83 278.50 450.83 278.50 c -451.33 278.50 451.88 278.56 452.49 278.70 c -452.49 279.67 l -451.84 279.48 451.31 279.38 450.90 279.38 c -450.31 279.38 449.84 279.60 449.48 280.05 c -449.12 280.49 448.94 281.08 448.94 281.82 c -448.94 282.53 449.13 283.11 449.49 283.55 c -449.86 283.99 450.34 284.21 450.94 284.21 c -451.46 284.21 452.01 284.08 452.56 283.81 c -452.56 284.81 l -451.82 285.03 451.17 285.15 450.63 285.15 c -h -458.96 284.79 m -458.18 285.03 457.52 285.15 456.97 285.15 c -456.03 285.15 455.27 284.83 454.68 284.21 c -454.08 283.59 453.79 282.78 453.79 281.79 c -453.79 280.82 454.05 280.03 454.57 279.42 c -455.09 278.80 455.76 278.49 456.57 278.49 c -457.34 278.49 457.94 278.76 458.36 279.31 c -458.78 279.86 458.99 280.63 458.99 281.64 c -458.98 282.00 l -454.97 282.00 l -455.13 283.51 455.88 284.27 457.19 284.27 c -457.67 284.27 458.26 284.14 458.96 283.88 c -h -455.02 281.13 m -457.83 281.13 l -457.83 279.95 457.38 279.36 456.50 279.36 c -455.62 279.36 455.12 279.95 455.02 281.13 c -h -464.66 285.22 m -463.31 285.22 462.27 284.82 461.54 284.03 c -460.80 283.24 460.43 282.12 460.43 280.67 c -460.43 279.22 460.81 278.10 461.56 277.31 c -462.30 276.51 463.36 276.11 464.72 276.11 c -465.49 276.11 466.40 276.24 467.45 276.49 c -467.45 277.65 l -466.26 277.24 465.34 277.03 464.70 277.03 c -463.76 277.03 463.03 277.35 462.51 277.99 c -462.00 278.62 461.74 279.52 461.74 280.68 c -461.74 281.79 462.02 282.66 462.57 283.30 c -463.12 283.94 463.87 284.26 464.82 284.26 c -465.64 284.26 466.52 284.00 467.46 283.50 c -467.46 284.55 l -466.60 285.00 465.67 285.22 464.66 285.22 c -h -472.56 284.19 m -471.87 284.83 471.21 285.15 470.56 285.15 c -470.04 285.15 469.60 284.98 469.25 284.65 c -468.90 284.32 468.73 283.90 468.73 283.40 c -468.73 282.71 469.02 282.17 469.61 281.80 c -470.19 281.42 471.03 281.24 472.12 281.24 c -472.39 281.24 l -472.39 280.47 l -472.39 279.73 472.01 279.36 471.26 279.36 c -470.65 279.36 469.99 279.55 469.28 279.93 c -469.28 278.97 l -470.06 278.65 470.79 278.50 471.47 278.50 c -472.18 278.50 472.70 278.66 473.04 278.98 c -473.38 279.30 473.55 279.79 473.55 280.47 c -473.55 283.35 l -473.55 284.01 473.75 284.34 474.16 284.34 c -474.21 284.34 474.28 284.34 474.38 284.32 c -474.46 284.96 l -474.20 285.08 473.91 285.15 473.59 285.15 c -473.05 285.15 472.71 284.83 472.56 284.19 c -h -472.39 283.56 m -472.39 281.92 l -472.01 281.91 l -471.37 281.91 470.86 282.03 470.47 282.27 c -470.08 282.51 469.88 282.82 469.88 283.21 c -469.88 283.49 469.98 283.72 470.18 283.92 c -470.37 284.11 470.61 284.20 470.89 284.20 c -471.37 284.20 471.87 283.99 472.39 283.56 c -h -477.82 285.15 m -477.23 285.15 476.78 284.98 476.45 284.64 c -476.12 284.31 475.96 283.84 475.96 283.24 c -475.96 279.50 l -475.16 279.50 l -475.16 278.64 l -475.96 278.64 l -475.96 277.48 l -477.11 277.37 l -477.11 278.64 l -478.77 278.64 l -478.77 279.50 l -477.11 279.50 l -477.11 283.03 l -477.11 283.86 477.47 284.28 478.19 284.28 c -478.34 284.28 478.53 284.25 478.74 284.20 c -478.74 285.00 l -478.39 285.10 478.08 285.15 477.82 285.15 c -h -483.68 284.19 m -482.99 284.83 482.32 285.15 481.68 285.15 c -481.15 285.15 480.71 284.98 480.37 284.65 c -480.02 284.32 479.85 283.90 479.85 283.40 c -479.85 282.71 480.14 282.17 480.72 281.80 c -481.31 281.42 482.14 281.24 483.23 281.24 c -483.51 281.24 l -483.51 280.47 l -483.51 279.73 483.13 279.36 482.37 279.36 c -481.76 279.36 481.10 279.55 480.40 279.93 c -480.40 278.97 l -481.17 278.65 481.90 278.50 482.58 278.50 c -483.29 278.50 483.82 278.66 484.16 278.98 c -484.49 279.30 484.66 279.79 484.66 280.47 c -484.66 283.35 l -484.66 284.01 484.87 284.34 485.27 284.34 c -485.32 284.34 485.40 284.34 485.49 284.32 c -485.58 284.96 l -485.31 285.08 485.03 285.15 484.71 285.15 c -484.17 285.15 483.83 284.83 483.68 284.19 c -h -483.51 283.56 m -483.51 281.92 l -483.12 281.91 l -482.49 281.91 481.98 282.03 481.59 282.27 c -481.20 282.51 481.00 282.82 481.00 283.21 c -481.00 283.49 481.10 283.72 481.29 283.92 c -481.49 284.11 481.73 284.20 482.01 284.20 c -482.49 284.20 482.99 283.99 483.51 283.56 c -h -487.02 285.00 m -487.02 275.75 l -488.18 275.75 l -488.18 285.00 l -h -492.98 285.15 m -492.07 285.15 491.35 284.84 490.80 284.24 c -490.26 283.64 489.99 282.83 489.99 281.82 c -489.99 280.79 490.26 279.99 490.81 279.39 c -491.35 278.79 492.09 278.50 493.02 278.50 c -493.96 278.50 494.70 278.79 495.24 279.39 c -495.79 279.99 496.06 280.79 496.06 281.81 c -496.06 282.85 495.79 283.66 495.24 284.26 c -494.69 284.85 493.94 285.15 492.98 285.15 c -h -493.00 284.28 m -494.22 284.28 494.83 283.46 494.83 281.81 c -494.83 280.18 494.23 279.36 493.02 279.36 c -491.82 279.36 491.22 280.18 491.22 281.82 c -491.22 283.46 491.81 284.28 493.00 284.28 c -h -497.89 287.12 m -498.02 286.11 l -498.69 286.43 499.35 286.59 500.00 286.59 c -501.30 286.59 501.95 285.90 501.95 284.52 c -501.95 283.52 l -501.52 284.41 500.82 284.85 499.85 284.85 c -499.09 284.85 498.48 284.58 498.03 284.02 c -497.58 283.47 497.36 282.72 497.36 281.78 c -497.36 280.81 497.62 280.02 498.13 279.41 c -498.64 278.80 499.30 278.50 500.11 278.50 c -500.82 278.50 501.44 278.79 501.95 279.36 c -501.95 278.64 l -503.11 278.64 l -503.11 283.27 l -503.11 284.26 503.06 285.00 502.95 285.48 c -502.85 285.96 502.65 286.35 502.37 286.65 c -501.87 287.19 501.08 287.46 500.02 287.46 c -499.28 287.46 498.57 287.34 497.89 287.12 c -h -501.95 282.80 m -501.95 280.15 l -501.44 279.63 500.89 279.36 500.29 279.36 c -499.76 279.36 499.34 279.58 499.04 280.00 c -498.74 280.43 498.59 281.01 498.59 281.75 c -498.59 283.15 499.08 283.85 500.06 283.85 c -500.73 283.85 501.36 283.50 501.95 282.80 c -h -f -[ 5.0000 5.0000] 0.0000 d -508.50 288.50 m -70.500 288.50 l -S -[] 0.0000 d -70.000 288.00 m -76.000 282.00 l -76.000 294.00 l -h -f* -463.06 201.15 m -462.48 201.15 462.02 200.98 461.69 200.64 c -461.37 200.31 461.20 199.84 461.20 199.24 c -461.20 195.50 l -460.40 195.50 l -460.40 194.64 l -461.20 194.64 l -461.20 193.48 l -462.36 193.37 l -462.36 194.64 l -464.02 194.64 l -464.02 195.50 l -462.36 195.50 l -462.36 199.03 l -462.36 199.86 462.71 200.28 463.43 200.28 c -463.59 200.28 463.77 200.25 463.99 200.20 c -463.99 201.00 l -463.63 201.10 463.33 201.15 463.06 201.15 c -h -470.31 200.79 m -469.53 201.03 468.87 201.15 468.32 201.15 c -467.38 201.15 466.62 200.83 466.03 200.21 c -465.43 199.59 465.14 198.78 465.14 197.79 c -465.14 196.82 465.40 196.03 465.92 195.42 c -466.44 194.80 467.11 194.49 467.92 194.49 c -468.69 194.49 469.29 194.76 469.71 195.31 c -470.13 195.86 470.34 196.63 470.34 197.64 c -470.33 198.00 l -466.32 198.00 l -466.48 199.51 467.22 200.27 468.54 200.27 c -469.02 200.27 469.61 200.14 470.31 199.88 c -h -466.37 197.13 m -469.18 197.13 l -469.18 195.95 468.73 195.36 467.85 195.36 c -466.96 195.36 466.47 195.95 466.37 197.13 c -h -472.33 201.00 m -472.33 194.64 l -473.48 194.64 l -473.48 195.83 l -474.09 194.94 474.84 194.50 475.72 194.50 c -476.27 194.50 476.71 194.67 477.04 195.02 c -477.37 195.37 477.53 195.84 477.53 196.43 c -477.53 201.00 l -476.38 201.00 l -476.38 196.80 l -476.38 196.33 476.31 196.00 476.17 195.79 c -476.03 195.59 475.80 195.49 475.48 195.49 c -474.77 195.49 474.11 195.96 473.48 196.88 c -473.48 201.00 l -h -483.06 200.19 m -482.37 200.83 481.70 201.15 481.06 201.15 c -480.53 201.15 480.09 200.98 479.75 200.65 c -479.40 200.32 479.22 199.90 479.22 199.40 c -479.22 198.71 479.52 198.17 480.10 197.80 c -480.68 197.42 481.52 197.24 482.61 197.24 c -482.89 197.24 l -482.89 196.47 l -482.89 195.73 482.51 195.36 481.75 195.36 c -481.14 195.36 480.48 195.55 479.78 195.93 c -479.78 194.97 l -480.55 194.65 481.28 194.50 481.96 194.50 c -482.67 194.50 483.20 194.66 483.53 194.98 c -483.87 195.30 484.04 195.79 484.04 196.47 c -484.04 199.35 l -484.04 200.01 484.24 200.34 484.65 200.34 c -484.70 200.34 484.78 200.34 484.87 200.32 c -484.96 200.96 l -484.69 201.08 484.40 201.15 484.09 201.15 c -483.55 201.15 483.21 200.83 483.06 200.19 c -h -482.89 199.56 m -482.89 197.92 l -482.50 197.91 l -481.87 197.91 481.36 198.03 480.96 198.27 c -480.57 198.51 480.38 198.82 480.38 199.21 c -480.38 199.49 480.48 199.72 480.67 199.92 c -480.87 200.11 481.11 200.20 481.39 200.20 c -481.87 200.20 482.37 199.99 482.89 199.56 c -h -486.40 201.00 m -486.40 194.64 l -487.56 194.64 l -487.56 195.83 l -488.17 194.94 488.91 194.50 489.79 194.50 c -490.35 194.50 490.79 194.67 491.11 195.02 c -491.44 195.37 491.61 195.84 491.61 196.43 c -491.61 201.00 l -490.45 201.00 l -490.45 196.80 l -490.45 196.33 490.38 196.00 490.24 195.79 c -490.10 195.59 489.88 195.49 489.55 195.49 c -488.85 195.49 488.18 195.96 487.56 196.88 c -487.56 201.00 l -h -495.76 201.15 m -495.17 201.15 494.72 200.98 494.39 200.64 c -494.06 200.31 493.90 199.84 493.90 199.24 c -493.90 195.50 l -493.10 195.50 l -493.10 194.64 l -493.90 194.64 l -493.90 193.48 l -495.05 193.37 l -495.05 194.64 l -496.71 194.64 l -496.71 195.50 l -495.05 195.50 l -495.05 199.03 l -495.05 199.86 495.41 200.28 496.13 200.28 c -496.28 200.28 496.47 200.25 496.69 200.20 c -496.69 201.00 l -496.33 201.10 496.02 201.15 495.76 201.15 c -h -500.03 201.15 m -499.50 201.15 498.86 201.02 498.10 200.78 c -498.10 199.72 l -498.86 200.09 499.51 200.28 500.07 200.28 c -500.40 200.28 500.68 200.19 500.90 200.01 c -501.12 199.83 501.23 199.61 501.23 199.34 c -501.23 198.94 500.92 198.62 500.31 198.36 c -499.63 198.07 l -498.64 197.66 498.14 197.06 498.14 196.28 c -498.14 195.73 498.33 195.29 498.73 194.97 c -499.12 194.66 499.66 194.50 500.34 194.50 c -500.70 194.50 501.14 194.54 501.66 194.64 c -501.90 194.69 l -501.90 195.65 l -501.26 195.46 500.74 195.36 500.37 195.36 c -499.62 195.36 499.25 195.63 499.25 196.17 c -499.25 196.52 499.53 196.81 500.10 197.05 c -500.65 197.29 l -501.28 197.55 501.73 197.83 501.99 198.13 c -502.25 198.42 502.38 198.79 502.38 199.23 c -502.38 199.79 502.16 200.25 501.72 200.61 c -501.28 200.97 500.71 201.15 500.03 201.15 c -h -f -[ 5.0000 5.0000] 0.0000 d -508.50 204.50 m -70.500 204.50 l -S -[] 0.0000 d -70.000 204.00 m -76.000 198.00 l -76.000 210.00 l -h -f* -639.84 666.15 m -639.31 666.15 638.67 666.02 637.92 665.78 c -637.92 664.72 l -638.67 665.09 639.33 665.28 639.89 665.28 c -640.22 665.28 640.50 665.19 640.71 665.01 c -640.93 664.83 641.04 664.61 641.04 664.34 c -641.04 663.94 640.74 663.62 640.12 663.36 c -639.45 663.07 l -638.45 662.66 637.96 662.06 637.96 661.28 c -637.96 660.73 638.15 660.29 638.54 659.97 c -638.94 659.66 639.47 659.50 640.16 659.50 c -640.51 659.50 640.95 659.54 641.48 659.64 c -641.72 659.69 l -641.72 660.65 l -641.07 660.46 640.56 660.36 640.18 660.36 c -639.44 660.36 639.07 660.63 639.07 661.17 c -639.07 661.52 639.35 661.81 639.91 662.05 c -640.47 662.29 l -641.10 662.55 641.54 662.83 641.80 663.13 c -642.07 663.42 642.20 663.79 642.20 664.23 c -642.20 664.79 641.98 665.25 641.54 665.61 c -641.09 665.97 640.53 666.15 639.84 666.15 c -h -648.25 666.00 m -648.25 664.80 l -647.64 665.70 646.89 666.15 646.02 666.15 c -645.46 666.15 645.02 665.97 644.69 665.62 c -644.37 665.27 644.20 664.80 644.20 664.21 c -644.20 659.64 l -645.36 659.64 l -645.36 663.83 l -645.36 664.31 645.42 664.65 645.56 664.85 c -645.70 665.05 645.93 665.15 646.26 665.15 c -646.96 665.15 647.62 664.69 648.25 663.76 c -648.25 659.64 l -649.40 659.64 l -649.40 666.00 l -h -654.20 666.15 m -653.34 666.15 652.63 665.83 652.06 665.19 c -651.50 664.55 651.21 663.75 651.21 662.78 c -651.21 661.75 651.50 660.94 652.06 660.36 c -652.62 659.79 653.40 659.50 654.40 659.50 c -654.90 659.50 655.45 659.56 656.07 659.70 c -656.07 660.67 l -655.41 660.48 654.88 660.38 654.47 660.38 c -653.88 660.38 653.41 660.60 653.05 661.05 c -652.69 661.49 652.52 662.08 652.52 662.82 c -652.52 663.53 652.70 664.11 653.07 664.55 c -653.43 664.99 653.91 665.21 654.51 665.21 c -655.04 665.21 655.58 665.08 656.14 664.81 c -656.14 665.81 l -655.39 666.03 654.75 666.15 654.20 666.15 c -h -660.35 666.15 m -659.49 666.15 658.78 665.83 658.21 665.19 c -657.64 664.55 657.36 663.75 657.36 662.78 c -657.36 661.75 657.64 660.94 658.20 660.36 c -658.76 659.79 659.54 659.50 660.55 659.50 c -661.04 659.50 661.60 659.56 662.21 659.70 c -662.21 660.67 l -661.56 660.48 661.03 660.38 660.62 660.38 c -660.03 660.38 659.56 660.60 659.20 661.05 c -658.84 661.49 658.66 662.08 658.66 662.82 c -658.66 663.53 658.85 664.11 659.21 664.55 c -659.58 664.99 660.06 665.21 660.65 665.21 c -661.18 665.21 661.72 665.08 662.28 664.81 c -662.28 665.81 l -661.54 666.03 660.89 666.15 660.35 666.15 c -h -668.68 665.79 m -667.90 666.03 667.24 666.15 666.69 666.15 c -665.75 666.15 664.99 665.83 664.40 665.21 c -663.80 664.59 663.51 663.78 663.51 662.79 c -663.51 661.82 663.77 661.03 664.29 660.42 c -664.81 659.80 665.48 659.49 666.29 659.49 c -667.06 659.49 667.66 659.76 668.08 660.31 c -668.50 660.86 668.71 661.63 668.71 662.64 c -668.70 663.00 l -664.69 663.00 l -664.85 664.51 665.59 665.27 666.91 665.27 c -667.39 665.27 667.98 665.14 668.68 664.88 c -h -664.74 662.13 m -667.54 662.13 l -667.54 660.95 667.10 660.36 666.22 660.36 c -665.33 660.36 664.84 660.95 664.74 662.13 c -h -672.38 666.15 m -671.86 666.15 671.22 666.02 670.46 665.78 c -670.46 664.72 l -671.22 665.09 671.87 665.28 672.43 665.28 c -672.76 665.28 673.04 665.19 673.26 665.01 c -673.48 664.83 673.59 664.61 673.59 664.34 c -673.59 663.94 673.28 663.62 672.67 663.36 c -671.99 663.07 l -671.00 662.66 670.50 662.06 670.50 661.28 c -670.50 660.73 670.69 660.29 671.09 659.97 c -671.48 659.66 672.02 659.50 672.70 659.50 c -673.06 659.50 673.50 659.54 674.02 659.64 c -674.26 659.69 l -674.26 660.65 l -673.62 660.46 673.10 660.36 672.72 660.36 c -671.98 660.36 671.61 660.63 671.61 661.17 c -671.61 661.52 671.89 661.81 672.46 662.05 c -673.01 662.29 l -673.64 662.55 674.09 662.83 674.35 663.13 c -674.61 663.42 674.74 663.79 674.74 664.23 c -674.74 664.79 674.52 665.25 674.08 665.61 c -673.64 665.97 673.07 666.15 672.38 666.15 c -h -678.50 666.15 m -677.97 666.15 677.33 666.02 676.58 665.78 c -676.58 664.72 l -677.33 665.09 677.99 665.28 678.55 665.28 c -678.88 665.28 679.16 665.19 679.38 665.01 c -679.59 664.83 679.70 664.61 679.70 664.34 c -679.70 663.94 679.40 663.62 678.78 663.36 c -678.11 663.07 l -677.11 662.66 676.62 662.06 676.62 661.28 c -676.62 660.73 676.81 660.29 677.20 659.97 c -677.60 659.66 678.13 659.50 678.82 659.50 c -679.17 659.50 679.61 659.54 680.14 659.64 c -680.38 659.69 l -680.38 660.65 l -679.73 660.46 679.22 660.36 678.84 660.36 c -678.10 660.36 677.73 660.63 677.73 661.17 c -677.73 661.52 678.01 661.81 678.57 662.05 c -679.13 662.29 l -679.76 662.55 680.20 662.83 680.46 663.13 c -680.73 663.42 680.86 663.79 680.86 664.23 c -680.86 664.79 680.64 665.25 680.20 665.61 c -679.75 665.97 679.19 666.15 678.50 666.15 c -h -f -[ 5.0000 5.0000] 0.0000 d -687.50 669.50 m -70.500 669.50 l -S -[] 0.0000 d -70.000 669.00 m -76.000 663.00 l -76.000 675.00 l -h -f* -527.13 457.00 m -527.13 455.80 l -526.52 456.70 525.78 457.15 524.90 457.15 c -524.35 457.15 523.90 456.97 523.58 456.62 c -523.25 456.27 523.08 455.80 523.08 455.21 c -523.08 450.64 l -524.24 450.64 l -524.24 454.83 l -524.24 455.31 524.31 455.65 524.45 455.85 c -524.58 456.05 524.82 456.15 525.14 456.15 c -525.84 456.15 526.51 455.69 527.13 454.76 c -527.13 450.64 l -528.29 450.64 l -528.29 457.00 l -h -532.29 457.15 m -531.76 457.15 531.12 457.02 530.37 456.78 c -530.37 455.72 l -531.12 456.09 531.78 456.28 532.34 456.28 c -532.67 456.28 532.94 456.19 533.16 456.01 c -533.38 455.83 533.49 455.61 533.49 455.34 c -533.49 454.94 533.18 454.62 532.57 454.36 c -531.90 454.07 l -530.90 453.66 530.40 453.06 530.40 452.28 c -530.40 451.73 530.60 451.29 530.99 450.97 c -531.38 450.66 531.92 450.50 532.61 450.50 c -532.96 450.50 533.40 450.54 533.92 450.64 c -534.16 450.69 l -534.16 451.65 l -533.52 451.46 533.01 451.36 532.63 451.36 c -531.89 451.36 531.52 451.63 531.52 452.17 c -531.52 452.52 531.80 452.81 532.36 453.05 c -532.92 453.29 l -533.54 453.55 533.99 453.83 534.25 454.13 c -534.51 454.42 534.64 454.79 534.64 455.23 c -534.64 455.79 534.42 456.25 533.98 456.61 c -533.54 456.97 532.98 457.15 532.29 457.15 c -h -541.38 456.79 m -540.61 457.03 539.95 457.15 539.40 457.15 c -538.46 457.15 537.69 456.83 537.10 456.21 c -536.51 455.59 536.21 454.78 536.21 453.79 c -536.21 452.82 536.48 452.03 537.00 451.42 c -537.52 450.80 538.19 450.49 539.00 450.49 c -539.77 450.49 540.36 450.76 540.78 451.31 c -541.20 451.86 541.41 452.63 541.41 453.64 c -541.41 454.00 l -537.39 454.00 l -537.56 455.51 538.30 456.27 539.61 456.27 c -540.09 456.27 540.68 456.14 541.38 455.88 c -h -537.45 453.13 m -540.25 453.13 l -540.25 451.95 539.81 451.36 538.93 451.36 c -538.04 451.36 537.55 451.95 537.45 453.13 c -h -543.40 457.00 m -543.40 450.64 l -544.56 450.64 l -544.56 451.83 l -545.02 450.94 545.68 450.50 546.55 450.50 c -546.67 450.50 546.79 450.51 546.92 450.53 c -546.92 451.60 l -546.72 451.54 546.54 451.50 546.39 451.50 c -545.66 451.50 545.05 451.94 544.56 452.80 c -544.56 457.00 l -h -548.33 458.88 m -548.33 458.45 l -548.71 458.34 548.89 457.90 548.89 457.12 c -548.89 457.00 l -548.33 457.00 l -548.33 455.55 l -549.78 455.55 l -549.78 456.81 l -549.78 458.09 549.30 458.78 548.33 458.88 c -h -555.91 457.00 m -555.91 450.64 l -557.06 450.64 l -557.06 451.83 l -557.52 450.94 558.18 450.50 559.05 450.50 c -559.17 450.50 559.29 450.51 559.42 450.53 c -559.42 451.60 l -559.22 451.54 559.05 451.50 558.90 451.50 c -558.17 451.50 557.55 451.94 557.06 452.80 c -557.06 457.00 l -h -563.31 457.15 m -562.40 457.15 561.67 456.84 561.13 456.24 c -560.59 455.64 560.31 454.83 560.31 453.82 c -560.31 452.79 560.59 451.99 561.13 451.39 c -561.68 450.79 562.42 450.50 563.35 450.50 c -564.28 450.50 565.02 450.79 565.57 451.39 c -566.11 451.99 566.38 452.79 566.38 453.81 c -566.38 454.85 566.11 455.66 565.56 456.26 c -565.02 456.85 564.27 457.15 563.31 457.15 c -h -563.33 456.28 m -564.55 456.28 565.16 455.46 565.16 453.81 c -565.16 452.18 564.56 451.36 563.35 451.36 c -562.15 451.36 561.54 452.18 561.54 453.82 c -561.54 455.46 562.14 456.28 563.33 456.28 c -h -568.19 457.00 m -568.19 447.75 l -569.34 447.75 l -569.34 457.00 l -h -576.32 456.79 m -575.55 457.03 574.89 457.15 574.34 457.15 c -573.40 457.15 572.63 456.83 572.04 456.21 c -571.45 455.59 571.15 454.78 571.15 453.79 c -571.15 452.82 571.42 452.03 571.94 451.42 c -572.46 450.80 573.12 450.49 573.94 450.49 c -574.71 450.49 575.30 450.76 575.72 451.31 c -576.14 451.86 576.35 452.63 576.35 453.64 c -576.35 454.00 l -572.33 454.00 l -572.50 455.51 573.24 456.27 574.55 456.27 c -575.03 456.27 575.62 456.14 576.32 455.88 c -h -572.38 453.13 m -575.19 453.13 l -575.19 451.95 574.75 451.36 573.87 451.36 c -572.98 451.36 572.49 451.95 572.38 453.13 c -h -580.03 457.15 m -579.50 457.15 578.86 457.02 578.11 456.78 c -578.11 455.72 l -578.86 456.09 579.52 456.28 580.08 456.28 c -580.41 456.28 580.69 456.19 580.90 456.01 c -581.12 455.83 581.23 455.61 581.23 455.34 c -581.23 454.94 580.93 454.62 580.31 454.36 c -579.64 454.07 l -578.64 453.66 578.14 453.06 578.14 452.28 c -578.14 451.73 578.34 451.29 578.73 450.97 c -579.13 450.66 579.66 450.50 580.35 450.50 c -580.70 450.50 581.14 450.54 581.67 450.64 c -581.91 450.69 l -581.91 451.65 l -581.26 451.46 580.75 451.36 580.37 451.36 c -579.63 451.36 579.26 451.63 579.26 452.17 c -579.26 452.52 579.54 452.81 580.10 453.05 c -580.66 453.29 l -581.29 453.55 581.73 453.83 581.99 454.13 c -582.26 454.42 582.39 454.79 582.39 455.23 c -582.39 455.79 582.17 456.25 581.72 456.61 c -581.28 456.97 580.72 457.15 580.03 457.15 c -h -f -[ 5.0000 5.0000] 0.0000 d -516.50 460.50 m -687.50 460.50 l -S -[] 0.0000 d -687.00 460.00 m -681.00 454.00 l -681.00 466.00 l -h -f* -534.47 439.00 m -532.11 432.64 l -533.26 432.64 l -535.11 437.59 l -537.06 432.64 l -538.14 432.64 l -535.63 439.00 l -h -542.65 438.19 m -541.96 438.83 541.29 439.15 540.65 439.15 c -540.12 439.15 539.68 438.98 539.34 438.65 c -538.99 438.32 538.81 437.90 538.81 437.40 c -538.81 436.71 539.11 436.17 539.69 435.80 c -540.27 435.42 541.11 435.24 542.20 435.24 c -542.48 435.24 l -542.48 434.47 l -542.48 433.73 542.10 433.36 541.34 433.36 c -540.73 433.36 540.07 433.55 539.37 433.93 c -539.37 432.97 l -540.14 432.65 540.87 432.50 541.55 432.50 c -542.26 432.50 542.79 432.66 543.12 432.98 c -543.46 433.30 543.63 433.79 543.63 434.47 c -543.63 437.35 l -543.63 438.01 543.83 438.34 544.24 438.34 c -544.29 438.34 544.37 438.34 544.46 438.32 c -544.54 438.96 l -544.28 439.08 543.99 439.15 543.68 439.15 c -543.14 439.15 542.79 438.83 542.65 438.19 c -h -542.48 437.56 m -542.48 435.92 l -542.09 435.91 l -541.46 435.91 540.95 436.03 540.55 436.27 c -540.16 436.51 539.97 436.82 539.97 437.21 c -539.97 437.49 540.07 437.72 540.26 437.92 c -540.46 438.11 540.70 438.20 540.98 438.20 c -541.46 438.20 541.96 437.99 542.48 437.56 c -h -545.99 439.00 m -545.99 429.75 l -547.15 429.75 l -547.15 439.00 l -h -549.46 439.00 m -549.46 432.64 l -550.62 432.64 l -550.62 439.00 l -h -549.46 431.48 m -549.46 430.33 l -550.62 430.33 l -550.62 431.48 l -h -557.01 439.00 m -557.01 437.80 l -556.54 438.70 555.84 439.15 554.89 439.15 c -554.13 439.15 553.52 438.87 553.08 438.31 c -552.65 437.75 552.43 436.99 552.43 436.02 c -552.43 434.96 552.67 434.11 553.17 433.46 c -553.67 432.82 554.33 432.50 555.14 432.50 c -555.89 432.50 556.52 432.79 557.01 433.36 c -557.01 429.75 l -558.17 429.75 l -558.17 439.00 l -h -557.01 434.15 m -556.42 433.63 555.85 433.36 555.31 433.36 c -554.21 433.36 553.66 434.21 553.66 435.90 c -553.66 437.39 554.15 438.13 555.13 438.13 c -555.77 438.13 556.40 437.78 557.01 437.08 c -h -563.76 438.19 m -563.07 438.83 562.41 439.15 561.77 439.15 c -561.24 439.15 560.80 438.98 560.45 438.65 c -560.11 438.32 559.93 437.90 559.93 437.40 c -559.93 436.71 560.22 436.17 560.81 435.80 c -561.39 435.42 562.23 435.24 563.32 435.24 c -563.59 435.24 l -563.59 434.47 l -563.59 433.73 563.21 433.36 562.46 433.36 c -561.85 433.36 561.19 433.55 560.48 433.93 c -560.48 432.97 l -561.26 432.65 561.99 432.50 562.67 432.50 c -563.38 432.50 563.90 432.66 564.24 432.98 c -564.58 433.30 564.75 433.79 564.75 434.47 c -564.75 437.35 l -564.75 438.01 564.95 438.34 565.36 438.34 c -565.41 438.34 565.48 438.34 565.58 438.32 c -565.66 438.96 l -565.40 439.08 565.11 439.15 564.79 439.15 c -564.26 439.15 563.91 438.83 563.76 438.19 c -h -563.59 437.56 m -563.59 435.92 l -563.21 435.91 l -562.57 435.91 562.06 436.03 561.67 436.27 c -561.28 436.51 561.09 436.82 561.09 437.21 c -561.09 437.49 561.18 437.72 561.38 437.92 c -561.57 438.11 561.81 438.20 562.09 438.20 c -562.57 438.20 563.07 437.99 563.59 437.56 c -h -569.02 439.15 m -568.43 439.15 567.98 438.98 567.65 438.64 c -567.32 438.31 567.16 437.84 567.16 437.24 c -567.16 433.50 l -566.36 433.50 l -566.36 432.64 l -567.16 432.64 l -567.16 431.48 l -568.31 431.37 l -568.31 432.64 l -569.97 432.64 l -569.97 433.50 l -568.31 433.50 l -568.31 437.03 l -568.31 437.86 568.67 438.28 569.39 438.28 c -569.54 438.28 569.73 438.25 569.95 438.20 c -569.95 439.00 l -569.59 439.10 569.28 439.15 569.02 439.15 c -h -576.26 438.79 m -575.49 439.03 574.83 439.15 574.28 439.15 c -573.34 439.15 572.57 438.83 571.98 438.21 c -571.39 437.59 571.09 436.78 571.09 435.79 c -571.09 434.82 571.35 434.03 571.88 433.42 c -572.40 432.80 573.06 432.49 573.88 432.49 c -574.65 432.49 575.24 432.76 575.66 433.31 c -576.08 433.86 576.29 434.63 576.29 435.64 c -576.29 436.00 l -572.27 436.00 l -572.44 437.51 573.18 438.27 574.49 438.27 c -574.97 438.27 575.56 438.14 576.26 437.88 c -h -572.32 435.13 m -575.13 435.13 l -575.13 433.95 574.69 433.36 573.81 433.36 c -572.92 433.36 572.43 433.95 572.32 435.13 c -h -585.37 439.00 m -578.43 435.53 l -585.37 432.06 l -585.37 433.03 l -580.37 435.53 l -585.37 438.03 l -h -589.73 439.15 m -589.15 439.15 588.69 438.98 588.36 438.64 c -588.03 438.31 587.87 437.84 587.87 437.24 c -587.87 433.50 l -587.07 433.50 l -587.07 432.64 l -587.87 432.64 l -587.87 431.48 l -589.02 431.37 l -589.02 432.64 l -590.69 432.64 l -590.69 433.50 l -589.02 433.50 l -589.02 437.03 l -589.02 437.86 589.38 438.28 590.10 438.28 c -590.25 438.28 590.44 438.25 590.66 438.20 c -590.66 439.00 l -590.30 439.10 589.99 439.15 589.73 439.15 c -h -594.80 439.15 m -593.89 439.15 593.16 438.84 592.62 438.24 c -592.08 437.64 591.81 436.83 591.81 435.82 c -591.81 434.79 592.08 433.99 592.62 433.39 c -593.17 432.79 593.91 432.50 594.84 432.50 c -595.78 432.50 596.51 432.79 597.06 433.39 c -597.60 433.99 597.88 434.79 597.88 435.81 c -597.88 436.85 597.60 437.66 597.06 438.26 c -596.51 438.85 595.76 439.15 594.80 439.15 c -h -594.82 438.28 m -596.04 438.28 596.65 437.46 596.65 435.81 c -596.65 434.18 596.05 433.36 594.84 433.36 c -593.64 433.36 593.04 434.18 593.04 435.82 c -593.04 437.46 593.63 438.28 594.82 438.28 c -h -599.68 439.00 m -599.68 429.75 l -600.84 429.75 l -600.84 435.72 l -603.53 432.64 l -604.77 432.64 l -602.20 435.61 l -605.31 439.00 l -603.83 439.00 l -600.84 435.74 l -600.84 439.00 l -h -611.36 438.79 m -610.59 439.03 609.92 439.15 609.37 439.15 c -608.44 439.15 607.67 438.83 607.08 438.21 c -606.49 437.59 606.19 436.78 606.19 435.79 c -606.19 434.82 606.45 434.03 606.97 433.42 c -607.50 432.80 608.16 432.49 608.97 432.49 c -609.74 432.49 610.34 432.76 610.76 433.31 c -611.18 433.86 611.39 434.63 611.39 435.64 c -611.38 436.00 l -607.37 436.00 l -607.54 437.51 608.28 438.27 609.59 438.27 c -610.07 438.27 610.66 438.14 611.36 437.88 c -h -607.42 435.13 m -610.23 435.13 l -610.23 433.95 609.79 433.36 608.90 433.36 c -608.02 433.36 607.52 433.95 607.42 435.13 c -h -613.38 439.00 m -613.38 432.64 l -614.54 432.64 l -614.54 433.83 l -615.14 432.94 615.89 432.50 616.77 432.50 c -617.32 432.50 617.76 432.67 618.09 433.02 c -618.42 433.37 618.58 433.84 618.58 434.43 c -618.58 439.00 l -617.43 439.00 l -617.43 434.80 l -617.43 434.33 617.36 434.00 617.22 433.79 c -617.08 433.59 616.85 433.49 616.53 433.49 c -615.83 433.49 615.16 433.96 614.54 434.88 c -614.54 439.00 l -h -620.85 440.88 m -620.85 440.45 l -621.22 440.34 621.41 439.90 621.41 439.12 c -621.41 439.00 l -620.85 439.00 l -620.85 437.55 l -622.29 437.55 l -622.29 438.81 l -622.29 440.09 621.81 440.78 620.85 440.88 c -h -628.42 440.73 m -628.42 429.75 l -630.74 429.75 l -630.74 430.62 l -629.44 430.62 l -629.44 439.87 l -630.74 439.87 l -630.74 440.73 l -h -634.23 439.15 m -633.65 439.15 633.19 438.98 632.86 438.64 c -632.54 438.31 632.37 437.84 632.37 437.24 c -632.37 433.50 l -631.57 433.50 l -631.57 432.64 l -632.37 432.64 l -632.37 431.48 l -633.53 431.37 l -633.53 432.64 l -635.19 432.64 l -635.19 433.50 l -633.53 433.50 l -633.53 437.03 l -633.53 437.86 633.88 438.28 634.60 438.28 c -634.76 438.28 634.94 438.25 635.16 438.20 c -635.16 439.00 l -634.80 439.10 634.50 439.15 634.23 439.15 c -h -641.48 438.79 m -640.70 439.03 640.04 439.15 639.49 439.15 c -638.55 439.15 637.79 438.83 637.20 438.21 c -636.60 437.59 636.31 436.78 636.31 435.79 c -636.31 434.82 636.57 434.03 637.09 433.42 c -637.61 432.80 638.28 432.49 639.09 432.49 c -639.86 432.49 640.46 432.76 640.88 433.31 c -641.30 433.86 641.51 434.63 641.51 435.64 c -641.50 436.00 l -637.49 436.00 l -637.65 437.51 638.39 438.27 639.71 438.27 c -640.19 438.27 640.78 438.14 641.48 437.88 c -h -637.54 435.13 m -640.35 435.13 l -640.35 433.95 639.90 433.36 639.02 433.36 c -638.13 433.36 637.64 433.95 637.54 435.13 c -h -643.50 439.00 m -643.50 432.64 l -644.65 432.64 l -644.65 433.83 l -645.26 432.94 646.01 432.50 646.89 432.50 c -647.44 432.50 647.88 432.67 648.21 433.02 c -648.54 433.37 648.70 433.84 648.70 434.43 c -648.70 439.00 l -647.55 439.00 l -647.55 434.80 l -647.55 434.33 647.48 434.00 647.34 433.79 c -647.20 433.59 646.97 433.49 646.65 433.49 c -645.94 433.49 645.28 433.96 644.65 434.88 c -644.65 439.00 l -h -654.23 438.19 m -653.54 438.83 652.87 439.15 652.23 439.15 c -651.70 439.15 651.26 438.98 650.92 438.65 c -650.57 438.32 650.39 437.90 650.39 437.40 c -650.39 436.71 650.69 436.17 651.27 435.80 c -651.85 435.42 652.69 435.24 653.78 435.24 c -654.06 435.24 l -654.06 434.47 l -654.06 433.73 653.68 433.36 652.92 433.36 c -652.31 433.36 651.65 433.55 650.95 433.93 c -650.95 432.97 l -651.72 432.65 652.45 432.50 653.13 432.50 c -653.84 432.50 654.37 432.66 654.70 432.98 c -655.04 433.30 655.21 433.79 655.21 434.47 c -655.21 437.35 l -655.21 438.01 655.41 438.34 655.82 438.34 c -655.87 438.34 655.95 438.34 656.04 438.32 c -656.12 438.96 l -655.86 439.08 655.57 439.15 655.26 439.15 c -654.72 439.15 654.38 438.83 654.23 438.19 c -h -654.06 437.56 m -654.06 435.92 l -653.67 435.91 l -653.04 435.91 652.53 436.03 652.13 436.27 c -651.74 436.51 651.55 436.82 651.55 437.21 c -651.55 437.49 651.65 437.72 651.84 437.92 c -652.04 438.11 652.28 438.20 652.56 438.20 c -653.04 438.20 653.54 437.99 654.06 437.56 c -h -657.57 439.00 m -657.57 432.64 l -658.73 432.64 l -658.73 433.83 l -659.34 432.94 660.08 432.50 660.96 432.50 c -661.52 432.50 661.96 432.67 662.28 433.02 c -662.61 433.37 662.78 433.84 662.78 434.43 c -662.78 439.00 l -661.62 439.00 l -661.62 434.80 l -661.62 434.33 661.55 434.00 661.41 433.79 c -661.27 433.59 661.04 433.49 660.72 433.49 c -660.02 433.49 659.35 433.96 658.73 434.88 c -658.73 439.00 l -h -666.93 439.15 m -666.34 439.15 665.89 438.98 665.56 438.64 c -665.23 438.31 665.07 437.84 665.07 437.24 c -665.07 433.50 l -664.27 433.50 l -664.27 432.64 l -665.07 432.64 l -665.07 431.48 l -666.22 431.37 l -666.22 432.64 l -667.88 432.64 l -667.88 433.50 l -666.22 433.50 l -666.22 437.03 l -666.22 437.86 666.58 438.28 667.30 438.28 c -667.45 438.28 667.64 438.25 667.86 438.20 c -667.86 439.00 l -667.50 439.10 667.19 439.15 666.93 439.15 c -h -671.10 440.73 m -671.10 429.75 l -668.79 429.75 l -668.79 430.62 l -670.09 430.62 l -670.09 439.87 l -668.79 439.87 l -668.79 440.73 l -h -673.56 439.00 m -680.49 435.53 l -673.56 432.06 l -673.56 433.03 l -678.55 435.53 l -673.56 438.03 l -h -f -687.50 442.50 m -516.50 442.50 l -S -516.00 442.00 m -522.00 436.00 l -522.00 448.00 l -h -f* -704.64 583.15 m -703.78 583.15 703.07 582.83 702.50 582.19 c -701.93 581.55 701.65 580.75 701.65 579.78 c -701.65 578.75 701.93 577.94 702.49 577.36 c -703.05 576.79 703.83 576.50 704.84 576.50 c -705.33 576.50 705.89 576.56 706.50 576.70 c -706.50 577.67 l -705.85 577.48 705.32 577.38 704.91 577.38 c -704.32 577.38 703.84 577.60 703.49 578.05 c -703.13 578.49 702.95 579.08 702.95 579.82 c -702.95 580.53 703.13 581.11 703.50 581.55 c -703.87 581.99 704.35 582.21 704.94 582.21 c -705.47 582.21 706.01 582.08 706.57 581.81 c -706.57 582.81 l -705.83 583.03 705.18 583.15 704.64 583.15 c -h -708.30 583.00 m -708.30 573.75 l -709.46 573.75 l -709.46 577.83 l -710.06 576.94 710.81 576.50 711.69 576.50 c -712.24 576.50 712.68 576.67 713.01 577.02 c -713.34 577.37 713.50 577.84 713.50 578.43 c -713.50 583.00 l -712.35 583.00 l -712.35 578.80 l -712.35 578.33 712.28 578.00 712.14 577.79 c -712.00 577.59 711.77 577.49 711.45 577.49 c -710.75 577.49 710.08 577.96 709.46 578.88 c -709.46 583.00 l -h -720.41 582.79 m -719.64 583.03 718.98 583.15 718.43 583.15 c -717.49 583.15 716.72 582.83 716.13 582.21 c -715.54 581.59 715.24 580.78 715.24 579.79 c -715.24 578.82 715.50 578.03 716.03 577.42 c -716.55 576.80 717.21 576.49 718.03 576.49 c -718.80 576.49 719.39 576.76 719.81 577.31 c -720.23 577.86 720.44 578.63 720.44 579.64 c -720.44 580.00 l -716.42 580.00 l -716.59 581.51 717.33 582.27 718.64 582.27 c -719.12 582.27 719.71 582.14 720.41 581.88 c -h -716.47 579.13 m -719.28 579.13 l -719.28 577.95 718.84 577.36 717.96 577.36 c -717.07 577.36 716.58 577.95 716.47 579.13 c -h -724.92 583.15 m -724.06 583.15 723.35 582.83 722.78 582.19 c -722.21 581.55 721.93 580.75 721.93 579.78 c -721.93 578.75 722.21 577.94 722.77 577.36 c -723.33 576.79 724.11 576.50 725.12 576.50 c -725.61 576.50 726.17 576.56 726.78 576.70 c -726.78 577.67 l -726.13 577.48 725.60 577.38 725.19 577.38 c -724.60 577.38 724.12 577.60 723.77 578.05 c -723.41 578.49 723.23 579.08 723.23 579.82 c -723.23 580.53 723.41 581.11 723.78 581.55 c -724.15 581.99 724.63 582.21 725.22 582.21 c -725.75 582.21 726.29 582.08 726.85 581.81 c -726.85 582.81 l -726.11 583.03 725.46 583.15 724.92 583.15 c -h -728.58 583.00 m -728.58 573.75 l -729.73 573.75 l -729.73 579.72 l -732.43 576.64 l -733.67 576.64 l -731.10 579.61 l -734.21 583.00 l -732.73 583.00 l -729.73 579.74 l -729.73 583.00 l -h -734.44 585.02 m -734.44 584.15 l -740.44 584.15 l -740.44 585.02 l -h -744.88 582.19 m -744.18 582.83 743.52 583.15 742.88 583.15 c -742.35 583.15 741.91 582.98 741.56 582.65 c -741.22 582.32 741.04 581.90 741.04 581.40 c -741.04 580.71 741.33 580.17 741.92 579.80 c -742.50 579.42 743.34 579.24 744.43 579.24 c -744.71 579.24 l -744.71 578.47 l -744.71 577.73 744.33 577.36 743.57 577.36 c -742.96 577.36 742.30 577.55 741.59 577.93 c -741.59 576.97 l -742.37 576.65 743.10 576.50 743.78 576.50 c -744.49 576.50 745.01 576.66 745.35 576.98 c -745.69 577.30 745.86 577.79 745.86 578.47 c -745.86 581.35 l -745.86 582.01 746.06 582.34 746.47 582.34 c -746.52 582.34 746.59 582.34 746.69 582.32 c -746.77 582.96 l -746.51 583.08 746.22 583.15 745.91 583.15 c -745.37 583.15 745.02 582.83 744.88 582.19 c -h -744.71 581.56 m -744.71 579.92 l -744.32 579.91 l -743.69 579.91 743.17 580.03 742.78 580.27 c -742.39 580.51 742.20 580.82 742.20 581.21 c -742.20 581.49 742.29 581.72 742.49 581.92 c -742.69 582.11 742.92 582.20 743.21 582.20 c -743.69 582.20 744.19 581.99 744.71 581.56 c -h -750.71 583.15 m -749.85 583.15 749.13 582.83 748.57 582.19 c -748.00 581.55 747.72 580.75 747.72 579.78 c -747.72 578.75 748.00 577.94 748.56 577.36 c -749.12 576.79 749.90 576.50 750.90 576.50 c -751.40 576.50 751.96 576.56 752.57 576.70 c -752.57 577.67 l -751.92 577.48 751.38 577.38 750.97 577.38 c -750.38 577.38 749.91 577.60 749.55 578.05 c -749.20 578.49 749.02 579.08 749.02 579.82 c -749.02 580.53 749.20 581.11 749.57 581.55 c -749.94 581.99 750.42 582.21 751.01 582.21 c -751.54 582.21 752.08 582.08 752.64 581.81 c -752.64 582.81 l -751.89 583.03 751.25 583.15 750.71 583.15 c -h -754.37 583.00 m -754.37 573.75 l -755.52 573.75 l -755.52 583.00 l -h -764.92 583.00 m -757.98 579.53 l -764.92 576.06 l -764.92 577.03 l -759.92 579.53 l -764.92 582.03 l -h -771.35 583.00 m -771.35 581.80 l -770.74 582.70 770.00 583.15 769.12 583.15 c -768.57 583.15 768.12 582.97 767.80 582.62 c -767.47 582.27 767.30 581.80 767.30 581.21 c -767.30 576.64 l -768.46 576.64 l -768.46 580.83 l -768.46 581.31 768.53 581.65 768.67 581.85 c -768.81 582.05 769.04 582.15 769.36 582.15 c -770.06 582.15 770.73 581.69 771.35 580.76 c -771.35 576.64 l -772.51 576.64 l -772.51 583.00 l -h -776.51 583.15 m -775.98 583.15 775.34 583.02 774.59 582.78 c -774.59 581.72 l -775.34 582.09 776.00 582.28 776.56 582.28 c -776.89 582.28 777.16 582.19 777.38 582.01 c -777.60 581.83 777.71 581.61 777.71 581.34 c -777.71 580.94 777.40 580.62 776.79 580.36 c -776.12 580.07 l -775.12 579.66 774.62 579.06 774.62 578.28 c -774.62 577.73 774.82 577.29 775.21 576.97 c -775.60 576.66 776.14 576.50 776.83 576.50 c -777.18 576.50 777.62 576.54 778.14 576.64 c -778.38 576.69 l -778.38 577.65 l -777.74 577.46 777.23 577.36 776.85 577.36 c -776.11 577.36 775.74 577.63 775.74 578.17 c -775.74 578.52 776.02 578.81 776.58 579.05 c -777.14 579.29 l -777.77 579.55 778.21 579.83 778.47 580.13 c -778.73 580.42 778.87 580.79 778.87 581.23 c -778.87 581.79 778.64 582.25 778.20 582.61 c -777.76 582.97 777.20 583.15 776.51 583.15 c -h -785.60 582.79 m -784.83 583.03 784.17 583.15 783.62 583.15 c -782.68 583.15 781.92 582.83 781.32 582.21 c -780.73 581.59 780.44 580.78 780.44 579.79 c -780.44 578.82 780.70 578.03 781.22 577.42 c -781.74 576.80 782.41 576.49 783.22 576.49 c -783.99 576.49 784.58 576.76 785.00 577.31 c -785.42 577.86 785.63 578.63 785.63 579.64 c -785.63 580.00 l -781.61 580.00 l -781.78 581.51 782.52 582.27 783.83 582.27 c -784.31 582.27 784.90 582.14 785.60 581.88 c -h -781.67 579.13 m -784.47 579.13 l -784.47 577.95 784.03 577.36 783.15 577.36 c -782.26 577.36 781.77 577.95 781.67 579.13 c -h -787.62 583.00 m -787.62 576.64 l -788.78 576.64 l -788.78 577.83 l -789.24 576.94 789.90 576.50 790.77 576.50 c -790.89 576.50 791.01 576.51 791.14 576.53 c -791.14 577.60 l -790.94 577.54 790.77 577.50 790.61 577.50 c -789.88 577.50 789.27 577.94 788.78 578.80 c -788.78 583.00 l -h -792.55 584.88 m -792.55 584.45 l -792.93 584.34 793.12 583.90 793.12 583.12 c -793.12 583.00 l -792.55 583.00 l -792.55 581.55 l -794.00 581.55 l -794.00 582.81 l -794.00 584.09 793.52 584.78 792.55 584.88 c -h -802.04 583.15 m -801.45 583.15 801.00 582.98 800.67 582.64 c -800.34 582.31 800.18 581.84 800.18 581.24 c -800.18 577.50 l -799.38 577.50 l -799.38 576.64 l -800.18 576.64 l -800.18 575.48 l -801.33 575.37 l -801.33 576.64 l -802.99 576.64 l -802.99 577.50 l -801.33 577.50 l -801.33 581.03 l -801.33 581.86 801.69 582.28 802.41 582.28 c -802.56 582.28 802.75 582.25 802.96 582.20 c -802.96 583.00 l -802.61 583.10 802.30 583.15 802.04 583.15 c -h -807.90 582.19 m -807.21 582.83 806.54 583.15 805.90 583.15 c -805.37 583.15 804.94 582.98 804.59 582.65 c -804.24 582.32 804.07 581.90 804.07 581.40 c -804.07 580.71 804.36 580.17 804.94 579.80 c -805.53 579.42 806.36 579.24 807.45 579.24 c -807.73 579.24 l -807.73 578.47 l -807.73 577.73 807.35 577.36 806.59 577.36 c -805.98 577.36 805.32 577.55 804.62 577.93 c -804.62 576.97 l -805.39 576.65 806.12 576.50 806.80 576.50 c -807.51 576.50 808.04 576.66 808.38 576.98 c -808.71 577.30 808.88 577.79 808.88 578.47 c -808.88 581.35 l -808.88 582.01 809.09 582.34 809.49 582.34 c -809.54 582.34 809.62 582.34 809.71 582.32 c -809.80 582.96 l -809.54 583.08 809.25 583.15 808.93 583.15 c -808.39 583.15 808.05 582.83 807.90 582.19 c -h -807.73 581.56 m -807.73 579.92 l -807.34 579.91 l -806.71 579.91 806.20 580.03 805.81 580.27 c -805.42 580.51 805.22 580.82 805.22 581.21 c -805.22 581.49 805.32 581.72 805.51 581.92 c -805.71 582.11 805.95 582.20 806.23 582.20 c -806.71 582.20 807.21 581.99 807.73 581.56 c -h -811.24 583.00 m -811.24 576.64 l -812.40 576.64 l -812.40 577.83 l -812.86 576.94 813.52 576.50 814.39 576.50 c -814.51 576.50 814.63 576.51 814.76 576.53 c -814.76 577.60 l -814.56 577.54 814.38 577.50 814.23 577.50 c -813.50 577.50 812.89 577.94 812.40 578.80 c -812.40 583.00 l -h -816.18 585.12 m -816.31 584.11 l -816.98 584.43 817.64 584.59 818.29 584.59 c -819.59 584.59 820.24 583.90 820.24 582.52 c -820.24 581.52 l -819.81 582.41 819.11 582.85 818.14 582.85 c -817.38 582.85 816.77 582.58 816.32 582.02 c -815.88 581.47 815.65 580.72 815.65 579.78 c -815.65 578.81 815.91 578.02 816.42 577.41 c -816.93 576.80 817.59 576.50 818.40 576.50 c -819.12 576.50 819.73 576.79 820.24 577.36 c -820.24 576.64 l -821.40 576.64 l -821.40 581.27 l -821.40 582.26 821.35 583.00 821.24 583.48 c -821.14 583.96 820.95 584.35 820.66 584.65 c -820.16 585.19 819.37 585.46 818.31 585.46 c -817.57 585.46 816.86 585.34 816.18 585.12 c -h -820.24 580.80 m -820.24 578.15 l -819.73 577.63 819.18 577.36 818.58 577.36 c -818.05 577.36 817.63 577.58 817.33 578.00 c -817.03 578.43 816.88 579.01 816.88 579.75 c -816.88 581.15 817.37 581.85 818.35 581.85 c -819.02 581.85 819.65 581.50 820.24 580.80 c -h -828.30 582.79 m -827.53 583.03 826.87 583.15 826.31 583.15 c -825.38 583.15 824.61 582.83 824.02 582.21 c -823.43 581.59 823.13 580.78 823.13 579.79 c -823.13 578.82 823.39 578.03 823.92 577.42 c -824.44 576.80 825.10 576.49 825.92 576.49 c -826.69 576.49 827.28 576.76 827.70 577.31 c -828.12 577.86 828.33 578.63 828.33 579.64 c -828.32 580.00 l -824.31 580.00 l -824.48 581.51 825.22 582.27 826.53 582.27 c -827.01 582.27 827.60 582.14 828.30 581.88 c -h -824.36 579.13 m -827.17 579.13 l -827.17 577.95 826.73 577.36 825.85 577.36 c -824.96 577.36 824.46 577.95 824.36 579.13 c -h -832.23 583.15 m -831.65 583.15 831.19 582.98 830.86 582.64 c -830.53 582.31 830.37 581.84 830.37 581.24 c -830.37 577.50 l -829.57 577.50 l -829.57 576.64 l -830.37 576.64 l -830.37 575.48 l -831.52 575.37 l -831.52 576.64 l -833.19 576.64 l -833.19 577.50 l -831.52 577.50 l -831.52 581.03 l -831.52 581.86 831.88 582.28 832.60 582.28 c -832.75 582.28 832.94 582.25 833.16 582.20 c -833.16 583.00 l -832.80 583.10 832.49 583.15 832.23 583.15 c -h -834.83 584.88 m -834.83 584.45 l -835.20 584.34 835.39 583.90 835.39 583.12 c -835.39 583.00 l -834.83 583.00 l -834.83 581.55 l -836.28 581.55 l -836.28 582.81 l -836.28 584.09 835.79 584.78 834.83 584.88 c -h -845.69 582.19 m -844.99 582.83 844.33 583.15 843.69 583.15 c -843.16 583.15 842.72 582.98 842.38 582.65 c -842.03 582.32 841.85 581.90 841.85 581.40 c -841.85 580.71 842.15 580.17 842.73 579.80 c -843.31 579.42 844.15 579.24 845.24 579.24 c -845.52 579.24 l -845.52 578.47 l -845.52 577.73 845.14 577.36 844.38 577.36 c -843.77 577.36 843.11 577.55 842.40 577.93 c -842.40 576.97 l -843.18 576.65 843.91 576.50 844.59 576.50 c -845.30 576.50 845.83 576.66 846.16 576.98 c -846.50 577.30 846.67 577.79 846.67 578.47 c -846.67 581.35 l -846.67 582.01 846.87 582.34 847.28 582.34 c -847.33 582.34 847.40 582.34 847.50 582.32 c -847.58 582.96 l -847.32 583.08 847.03 583.15 846.72 583.15 c -846.18 583.15 845.83 582.83 845.69 582.19 c -h -845.52 581.56 m -845.52 579.92 l -845.13 579.91 l -844.50 579.91 843.98 580.03 843.59 580.27 c -843.20 580.51 843.01 580.82 843.01 581.21 c -843.01 581.49 843.11 581.72 843.30 581.92 c -843.50 582.11 843.73 582.20 844.02 582.20 c -844.50 582.20 845.00 581.99 845.52 581.56 c -h -851.52 583.15 m -850.66 583.15 849.94 582.83 849.38 582.19 c -848.81 581.55 848.53 580.75 848.53 579.78 c -848.53 578.75 848.81 577.94 849.37 577.36 c -849.93 576.79 850.71 576.50 851.71 576.50 c -852.21 576.50 852.77 576.56 853.38 576.70 c -853.38 577.67 l -852.73 577.48 852.20 577.38 851.79 577.38 c -851.20 577.38 850.72 577.60 850.36 578.05 c -850.01 578.49 849.83 579.08 849.83 579.82 c -849.83 580.53 850.01 581.11 850.38 581.55 c -850.75 581.99 851.23 582.21 851.82 582.21 c -852.35 582.21 852.89 582.08 853.45 581.81 c -853.45 582.81 l -852.70 583.03 852.06 583.15 851.52 583.15 c -h -857.09 583.15 m -856.50 583.15 856.04 582.98 855.72 582.64 c -855.39 582.31 855.22 581.84 855.22 581.24 c -855.22 577.50 l -854.43 577.50 l -854.43 576.64 l -855.22 576.64 l -855.22 575.48 l -856.38 575.37 l -856.38 576.64 l -858.04 576.64 l -858.04 577.50 l -856.38 577.50 l -856.38 581.03 l -856.38 581.86 856.74 582.28 857.46 582.28 c -857.61 582.28 857.79 582.25 858.01 582.20 c -858.01 583.00 l -857.66 583.10 857.35 583.15 857.09 583.15 c -h -859.67 583.00 m -859.67 576.64 l -860.82 576.64 l -860.82 583.00 l -h -859.67 575.48 m -859.67 574.33 l -860.82 574.33 l -860.82 575.48 l -h -865.62 583.15 m -864.71 583.15 863.99 582.84 863.45 582.24 c -862.90 581.64 862.63 580.83 862.63 579.82 c -862.63 578.79 862.90 577.99 863.45 577.39 c -863.99 576.79 864.73 576.50 865.67 576.50 c -866.60 576.50 867.34 576.79 867.88 577.39 c -868.43 577.99 868.70 578.79 868.70 579.81 c -868.70 580.85 868.43 581.66 867.88 582.26 c -867.33 582.85 866.58 583.15 865.62 583.15 c -h -865.64 582.28 m -866.87 582.28 867.48 581.46 867.48 579.81 c -867.48 578.18 866.87 577.36 865.67 577.36 c -864.46 577.36 863.86 578.18 863.86 579.82 c -863.86 581.46 864.46 582.28 865.64 582.28 c -h -870.51 583.00 m -870.51 576.64 l -871.66 576.64 l -871.66 577.83 l -872.27 576.94 873.02 576.50 873.90 576.50 c -874.45 576.50 874.89 576.67 875.22 577.02 c -875.54 577.37 875.71 577.84 875.71 578.43 c -875.71 583.00 l -874.55 583.00 l -874.55 578.80 l -874.55 578.33 874.49 578.00 874.35 577.79 c -874.21 577.59 873.98 577.49 873.66 577.49 c -872.95 577.49 872.29 577.96 871.66 578.88 c -871.66 583.00 l -h -877.97 584.88 m -877.97 584.45 l -878.35 584.34 878.53 583.90 878.53 583.12 c -878.53 583.00 l -877.97 583.00 l -877.97 581.55 l -879.42 581.55 l -879.42 582.81 l -879.42 584.09 878.94 584.78 877.97 584.88 c -h -885.55 583.00 m -885.55 576.64 l -886.70 576.64 l -886.70 577.83 l -887.16 576.94 887.82 576.50 888.69 576.50 c -888.81 576.50 888.93 576.51 889.06 576.53 c -889.06 577.60 l -888.86 577.54 888.69 577.50 888.54 577.50 c -887.80 577.50 887.19 577.94 886.70 578.80 c -886.70 583.00 l -h -892.95 583.15 m -892.04 583.15 891.31 582.84 890.77 582.24 c -890.22 581.64 889.95 580.83 889.95 579.82 c -889.95 578.79 890.23 577.99 890.77 577.39 c -891.32 576.79 892.05 576.50 892.99 576.50 c -893.92 576.50 894.66 576.79 895.21 577.39 c -895.75 577.99 896.02 578.79 896.02 579.81 c -896.02 580.85 895.75 581.66 895.20 582.26 c -894.66 582.85 893.90 583.15 892.95 583.15 c -h -892.96 582.28 m -894.19 582.28 894.80 581.46 894.80 579.81 c -894.80 578.18 894.20 577.36 892.99 577.36 c -891.79 577.36 891.18 578.18 891.18 579.82 c -891.18 581.46 891.78 582.28 892.96 582.28 c -h -897.83 583.00 m -897.83 573.75 l -898.98 573.75 l -898.98 583.00 l -h -905.96 582.79 m -905.19 583.03 904.53 583.15 903.97 583.15 c -903.04 583.15 902.27 582.83 901.68 582.21 c -901.09 581.59 900.79 580.78 900.79 579.79 c -900.79 578.82 901.05 578.03 901.58 577.42 c -902.10 576.80 902.76 576.49 903.58 576.49 c -904.35 576.49 904.94 576.76 905.36 577.31 c -905.78 577.86 905.99 578.63 905.99 579.64 c -905.98 580.00 l -901.97 580.00 l -902.14 581.51 902.88 582.27 904.19 582.27 c -904.67 582.27 905.26 582.14 905.96 581.88 c -h -902.02 579.13 m -904.83 579.13 l -904.83 577.95 904.39 577.36 903.51 577.36 c -902.62 577.36 902.12 577.95 902.02 579.13 c -h -909.67 583.15 m -909.14 583.15 908.50 583.02 907.75 582.78 c -907.75 581.72 l -908.50 582.09 909.16 582.28 909.72 582.28 c -910.05 582.28 910.32 582.19 910.54 582.01 c -910.76 581.83 910.87 581.61 910.87 581.34 c -910.87 580.94 910.56 580.62 909.95 580.36 c -909.28 580.07 l -908.28 579.66 907.78 579.06 907.78 578.28 c -907.78 577.73 907.98 577.29 908.37 576.97 c -908.76 576.66 909.30 576.50 909.99 576.50 c -910.34 576.50 910.78 576.54 911.30 576.64 c -911.54 576.69 l -911.54 577.65 l -910.90 577.46 910.39 577.36 910.01 577.36 c -909.27 577.36 908.90 577.63 908.90 578.17 c -908.90 578.52 909.18 578.81 909.74 579.05 c -910.30 579.29 l -910.93 579.55 911.37 579.83 911.63 580.13 c -911.89 580.42 912.03 580.79 912.03 581.23 c -912.03 581.79 911.80 582.25 911.36 582.61 c -910.92 582.97 910.36 583.15 909.67 583.15 c -h -914.25 583.00 m -921.18 579.53 l -914.25 576.06 l -914.25 577.03 l -919.24 579.53 l -914.25 582.03 l -h -f -695.50 586.50 m -937.50 586.50 l -S -937.00 586.00 m -931.00 580.00 l -931.00 592.00 l -h -f* -702.15 495.00 m -702.15 488.64 l -703.31 488.64 l -703.31 495.00 l -h -702.15 487.48 m -702.15 486.33 l -703.31 486.33 l -703.31 487.48 l -h -705.62 495.00 m -705.62 488.64 l -706.78 488.64 l -706.78 489.83 l -707.39 488.94 708.13 488.50 709.02 488.50 c -709.57 488.50 710.01 488.67 710.33 489.02 c -710.66 489.37 710.83 489.84 710.83 490.43 c -710.83 495.00 l -709.67 495.00 l -709.67 490.80 l -709.67 490.33 709.60 490.00 709.46 489.79 c -709.33 489.59 709.10 489.49 708.78 489.49 c -708.07 489.49 707.40 489.96 706.78 490.88 c -706.78 495.00 l -h -714.76 495.15 m -714.23 495.15 713.59 495.02 712.84 494.78 c -712.84 493.72 l -713.59 494.09 714.25 494.28 714.80 494.28 c -715.14 494.28 715.41 494.19 715.63 494.01 c -715.85 493.83 715.96 493.61 715.96 493.34 c -715.96 492.94 715.65 492.62 715.04 492.36 c -714.37 492.07 l -713.37 491.66 712.87 491.06 712.87 490.28 c -712.87 489.73 713.07 489.29 713.46 488.97 c -713.85 488.66 714.39 488.50 715.07 488.50 c -715.43 488.50 715.87 488.54 716.39 488.64 c -716.63 488.69 l -716.63 489.65 l -715.99 489.46 715.48 489.36 715.10 489.36 c -714.36 489.36 713.98 489.63 713.98 490.17 c -713.98 490.52 714.27 490.81 714.83 491.05 c -715.38 491.29 l -716.01 491.55 716.46 491.83 716.72 492.13 c -716.98 492.42 717.11 492.79 717.11 493.23 c -717.11 493.79 716.89 494.25 716.45 494.61 c -716.01 494.97 715.45 495.15 714.76 495.15 c -h -721.10 495.15 m -720.51 495.15 720.05 494.98 719.73 494.64 c -719.40 494.31 719.23 493.84 719.23 493.24 c -719.23 489.50 l -718.44 489.50 l -718.44 488.64 l -719.23 488.64 l -719.23 487.48 l -720.39 487.37 l -720.39 488.64 l -722.05 488.64 l -722.05 489.50 l -720.39 489.50 l -720.39 493.03 l -720.39 493.86 720.75 494.28 721.47 494.28 c -721.62 494.28 721.80 494.25 722.02 494.20 c -722.02 495.00 l -721.67 495.10 721.36 495.15 721.10 495.15 c -h -726.96 494.19 m -726.27 494.83 725.60 495.15 724.96 495.15 c -724.43 495.15 723.99 494.98 723.65 494.65 c -723.30 494.32 723.12 493.90 723.12 493.40 c -723.12 492.71 723.42 492.17 724.00 491.80 c -724.58 491.42 725.42 491.24 726.51 491.24 c -726.79 491.24 l -726.79 490.47 l -726.79 489.73 726.41 489.36 725.65 489.36 c -725.04 489.36 724.38 489.55 723.68 489.93 c -723.68 488.97 l -724.45 488.65 725.18 488.50 725.86 488.50 c -726.57 488.50 727.10 488.66 727.43 488.98 c -727.77 489.30 727.94 489.79 727.94 490.47 c -727.94 493.35 l -727.94 494.01 728.14 494.34 728.55 494.34 c -728.60 494.34 728.68 494.34 728.77 494.32 c -728.86 494.96 l -728.59 495.08 728.30 495.15 727.99 495.15 c -727.45 495.15 727.11 494.83 726.96 494.19 c -h -726.79 493.56 m -726.79 491.92 l -726.40 491.91 l -725.77 491.91 725.26 492.03 724.87 492.27 c -724.47 492.51 724.28 492.82 724.28 493.21 c -724.28 493.49 724.38 493.72 724.57 493.92 c -724.77 494.11 725.01 494.20 725.29 494.20 c -725.77 494.20 726.27 493.99 726.79 493.56 c -h -730.30 495.00 m -730.30 488.64 l -731.46 488.64 l -731.46 489.83 l -732.07 488.94 732.81 488.50 733.70 488.50 c -734.25 488.50 734.69 488.67 735.01 489.02 c -735.34 489.37 735.51 489.84 735.51 490.43 c -735.51 495.00 l -734.35 495.00 l -734.35 490.80 l -734.35 490.33 734.28 490.00 734.14 489.79 c -734.00 489.59 733.78 489.49 733.46 489.49 c -732.75 489.49 732.08 489.96 731.46 490.88 c -731.46 495.00 l -h -740.23 495.15 m -739.38 495.15 738.66 494.83 738.10 494.19 c -737.53 493.55 737.25 492.75 737.25 491.78 c -737.25 490.75 737.53 489.94 738.09 489.36 c -738.65 488.79 739.43 488.50 740.43 488.50 c -740.93 488.50 741.48 488.56 742.10 488.70 c -742.10 489.67 l -741.45 489.48 740.91 489.38 740.50 489.38 c -739.91 489.38 739.44 489.60 739.08 490.05 c -738.73 490.49 738.55 491.08 738.55 491.82 c -738.55 492.53 738.73 493.11 739.10 493.55 c -739.46 493.99 739.95 494.21 740.54 494.21 c -741.07 494.21 741.61 494.08 742.17 493.81 c -742.17 494.81 l -741.42 495.03 740.78 495.15 740.23 495.15 c -h -748.56 494.79 m -747.79 495.03 747.12 495.15 746.57 495.15 c -745.64 495.15 744.87 494.83 744.28 494.21 c -743.69 493.59 743.39 492.78 743.39 491.79 c -743.39 490.82 743.65 490.03 744.17 489.42 c -744.70 488.80 745.36 488.49 746.18 488.49 c -746.95 488.49 747.54 488.76 747.96 489.31 c -748.38 489.86 748.59 490.63 748.59 491.64 c -748.58 492.00 l -744.57 492.00 l -744.74 493.51 745.48 494.27 746.79 494.27 c -747.27 494.27 747.86 494.14 748.56 493.88 c -h -744.62 491.13 m -747.43 491.13 l -747.43 489.95 746.99 489.36 746.11 489.36 c -745.22 489.36 744.72 489.95 744.62 491.13 c -h -750.73 493.05 m -750.73 492.18 l -757.67 492.18 l -757.67 493.05 l -h -750.73 490.88 m -750.73 490.01 l -757.67 490.01 l -757.67 490.88 l -h -760.12 495.00 m -760.12 488.64 l -761.28 488.64 l -761.28 495.00 l -h -760.12 487.48 m -760.12 486.33 l -761.28 486.33 l -761.28 487.48 l -h -763.59 495.00 m -763.59 488.64 l -764.74 488.64 l -764.74 489.83 l -765.35 488.94 766.10 488.50 766.98 488.50 c -767.53 488.50 767.97 488.67 768.30 489.02 c -768.63 489.37 768.79 489.84 768.79 490.43 c -768.79 495.00 l -767.64 495.00 l -767.64 490.80 l -767.64 490.33 767.57 490.00 767.43 489.79 c -767.29 489.59 767.06 489.49 766.74 489.49 c -766.04 489.49 765.37 489.96 764.74 490.88 c -764.74 495.00 l -h -772.72 495.15 m -772.20 495.15 771.56 495.02 770.80 494.78 c -770.80 493.72 l -771.56 494.09 772.21 494.28 772.77 494.28 c -773.10 494.28 773.38 494.19 773.60 494.01 c -773.82 493.83 773.93 493.61 773.93 493.34 c -773.93 492.94 773.62 492.62 773.01 492.36 c -772.33 492.07 l -771.34 491.66 770.84 491.06 770.84 490.28 c -770.84 489.73 771.03 489.29 771.43 488.97 c -771.82 488.66 772.36 488.50 773.04 488.50 c -773.40 488.50 773.84 488.54 774.36 488.64 c -774.60 488.69 l -774.60 489.65 l -773.96 489.46 773.44 489.36 773.06 489.36 c -772.32 489.36 771.95 489.63 771.95 490.17 c -771.95 490.52 772.23 490.81 772.79 491.05 c -773.35 491.29 l -773.98 491.55 774.43 491.83 774.69 492.13 c -774.95 492.42 775.08 492.79 775.08 493.23 c -775.08 493.79 774.86 494.25 774.42 494.61 c -773.98 494.97 773.41 495.15 772.72 495.15 c -h -779.06 495.15 m -778.48 495.15 778.02 494.98 777.69 494.64 c -777.37 494.31 777.20 493.84 777.20 493.24 c -777.20 489.50 l -776.40 489.50 l -776.40 488.64 l -777.20 488.64 l -777.20 487.48 l -778.36 487.37 l -778.36 488.64 l -780.02 488.64 l -780.02 489.50 l -778.36 489.50 l -778.36 493.03 l -778.36 493.86 778.71 494.28 779.43 494.28 c -779.59 494.28 779.77 494.25 779.99 494.20 c -779.99 495.00 l -779.63 495.10 779.33 495.15 779.06 495.15 c -h -784.92 494.19 m -784.23 494.83 783.57 495.15 782.93 495.15 c -782.40 495.15 781.96 494.98 781.61 494.65 c -781.27 494.32 781.09 493.90 781.09 493.40 c -781.09 492.71 781.38 492.17 781.97 491.80 c -782.55 491.42 783.39 491.24 784.48 491.24 c -784.75 491.24 l -784.75 490.47 l -784.75 489.73 784.38 489.36 783.62 489.36 c -783.01 489.36 782.35 489.55 781.64 489.93 c -781.64 488.97 l -782.42 488.65 783.15 488.50 783.83 488.50 c -784.54 488.50 785.06 488.66 785.40 488.98 c -785.74 489.30 785.91 489.79 785.91 490.47 c -785.91 493.35 l -785.91 494.01 786.11 494.34 786.52 494.34 c -786.57 494.34 786.64 494.34 786.74 494.32 c -786.82 494.96 l -786.56 495.08 786.27 495.15 785.96 495.15 c -785.42 495.15 785.07 494.83 784.92 494.19 c -h -784.75 493.56 m -784.75 491.92 l -784.37 491.91 l -783.73 491.91 783.22 492.03 782.83 492.27 c -782.44 492.51 782.25 492.82 782.25 493.21 c -782.25 493.49 782.34 493.72 782.54 493.92 c -782.73 494.11 782.97 494.20 783.25 494.20 c -783.73 494.20 784.23 493.99 784.75 493.56 c -h -788.27 495.00 m -788.27 488.64 l -789.42 488.64 l -789.42 489.83 l -790.03 488.94 790.78 488.50 791.66 488.50 c -792.21 488.50 792.65 488.67 792.98 489.02 c -793.31 489.37 793.47 489.84 793.47 490.43 c -793.47 495.00 l -792.32 495.00 l -792.32 490.80 l -792.32 490.33 792.25 490.00 792.11 489.79 c -791.97 489.59 791.74 489.49 791.42 489.49 c -790.71 489.49 790.05 489.96 789.42 490.88 c -789.42 495.00 l -h -798.20 495.15 m -797.34 495.15 796.63 494.83 796.06 494.19 c -795.50 493.55 795.21 492.75 795.21 491.78 c -795.21 490.75 795.49 489.94 796.05 489.36 c -796.61 488.79 797.40 488.50 798.40 488.50 c -798.90 488.50 799.45 488.56 800.06 488.70 c -800.06 489.67 l -799.41 489.48 798.88 489.38 798.47 489.38 c -797.88 489.38 797.41 489.60 797.05 490.05 c -796.69 490.49 796.51 491.08 796.51 491.82 c -796.51 492.53 796.70 493.11 797.06 493.55 c -797.43 493.99 797.91 494.21 798.51 494.21 c -799.03 494.21 799.58 494.08 800.13 493.81 c -800.13 494.81 l -799.39 495.03 798.74 495.15 798.20 495.15 c -h -806.53 494.79 m -805.75 495.03 805.09 495.15 804.54 495.15 c -803.60 495.15 802.84 494.83 802.25 494.21 c -801.66 493.59 801.36 492.78 801.36 491.79 c -801.36 490.82 801.62 490.03 802.14 489.42 c -802.66 488.80 803.33 488.49 804.14 488.49 c -804.91 488.49 805.51 488.76 805.93 489.31 c -806.35 489.86 806.56 490.63 806.56 491.64 c -806.55 492.00 l -802.54 492.00 l -802.71 493.51 803.45 494.27 804.76 494.27 c -805.24 494.27 805.83 494.14 806.53 493.88 c -h -802.59 491.13 m -805.40 491.13 l -805.40 489.95 804.96 489.36 804.07 489.36 c -803.19 489.36 802.69 489.95 802.59 491.13 c -h -807.39 497.02 m -807.39 496.15 l -813.39 496.15 l -813.39 497.02 l -h -814.57 497.12 m -814.71 496.11 l -815.38 496.43 816.03 496.59 816.68 496.59 c -817.98 496.59 818.63 495.90 818.63 494.52 c -818.63 493.52 l -818.21 494.41 817.51 494.85 816.54 494.85 c -815.77 494.85 815.17 494.58 814.72 494.02 c -814.27 493.47 814.04 492.72 814.04 491.78 c -814.04 490.81 814.30 490.02 814.81 489.41 c -815.32 488.80 815.99 488.50 816.80 488.50 c -817.51 488.50 818.12 488.79 818.63 489.36 c -818.63 488.64 l -819.79 488.64 l -819.79 493.27 l -819.79 494.26 819.74 495.00 819.64 495.48 c -819.53 495.96 819.34 496.35 819.05 496.65 c -818.55 497.19 817.77 497.46 816.71 497.46 c -815.96 497.46 815.25 497.34 814.57 497.12 c -h -818.63 492.80 m -818.63 490.15 l -818.12 489.63 817.57 489.36 816.97 489.36 c -816.44 489.36 816.03 489.58 815.73 490.00 c -815.43 490.43 815.28 491.01 815.28 491.75 c -815.28 493.15 815.77 493.85 816.75 493.85 c -817.41 493.85 818.04 493.50 818.63 492.80 c -h -826.70 494.79 m -825.92 495.03 825.26 495.15 824.71 495.15 c -823.77 495.15 823.01 494.83 822.42 494.21 c -821.82 493.59 821.53 492.78 821.53 491.79 c -821.53 490.82 821.79 490.03 822.31 489.42 c -822.83 488.80 823.50 488.49 824.31 488.49 c -825.08 488.49 825.67 488.76 826.09 489.31 c -826.51 489.86 826.72 490.63 826.72 491.64 c -826.72 492.00 l -822.71 492.00 l -822.87 493.51 823.61 494.27 824.93 494.27 c -825.41 494.27 826.00 494.14 826.70 493.88 c -h -822.76 491.13 m -825.56 491.13 l -825.56 489.95 825.12 489.36 824.24 489.36 c -823.35 489.36 822.86 489.95 822.76 491.13 c -h -830.63 495.15 m -830.04 495.15 829.58 494.98 829.26 494.64 c -828.93 494.31 828.76 493.84 828.76 493.24 c -828.76 489.50 l -827.97 489.50 l -827.97 488.64 l -828.76 488.64 l -828.76 487.48 l -829.92 487.37 l -829.92 488.64 l -831.58 488.64 l -831.58 489.50 l -829.92 489.50 l -829.92 493.03 l -829.92 493.86 830.28 494.28 831.00 494.28 c -831.15 494.28 831.33 494.25 831.55 494.20 c -831.55 495.00 l -831.20 495.10 830.89 495.15 830.63 495.15 c -h -835.52 495.94 m -835.52 496.73 l -834.68 496.16 834.01 495.38 833.51 494.39 c -833.02 493.41 832.77 492.36 832.77 491.24 c -832.77 490.12 833.02 489.08 833.51 488.09 c -834.01 487.10 834.68 486.32 835.52 485.75 c -835.52 486.54 l -834.95 487.17 834.54 487.84 834.29 488.56 c -834.05 489.28 833.93 490.17 833.93 491.24 c -833.93 492.31 834.05 493.20 834.29 493.92 c -834.54 494.64 834.95 495.31 835.52 495.94 c -h -837.11 495.00 m -837.11 488.64 l -838.26 488.64 l -838.26 495.00 l -h -837.11 487.48 m -837.11 486.33 l -838.26 486.33 l -838.26 487.48 l -h -840.58 495.00 m -840.58 488.64 l -841.73 488.64 l -841.73 489.83 l -842.34 488.94 843.09 488.50 843.97 488.50 c -844.52 488.50 844.96 488.67 845.29 489.02 c -845.62 489.37 845.78 489.84 845.78 490.43 c -845.78 495.00 l -844.62 495.00 l -844.62 490.80 l -844.62 490.33 844.56 490.00 844.42 489.79 c -844.28 489.59 844.05 489.49 843.73 489.49 c -843.02 489.49 842.36 489.96 841.73 490.88 c -841.73 495.00 l -h -849.71 495.15 m -849.18 495.15 848.54 495.02 847.79 494.78 c -847.79 493.72 l -848.54 494.09 849.20 494.28 849.76 494.28 c -850.09 494.28 850.37 494.19 850.58 494.01 c -850.80 493.83 850.91 493.61 850.91 493.34 c -850.91 492.94 850.61 492.62 849.99 492.36 c -849.32 492.07 l -848.32 491.66 847.82 491.06 847.82 490.28 c -847.82 489.73 848.02 489.29 848.41 488.97 c -848.81 488.66 849.34 488.50 850.03 488.50 c -850.38 488.50 850.82 488.54 851.35 488.64 c -851.59 488.69 l -851.59 489.65 l -850.94 489.46 850.43 489.36 850.05 489.36 c -849.31 489.36 848.94 489.63 848.94 490.17 c -848.94 490.52 849.22 490.81 849.78 491.05 c -850.34 491.29 l -850.97 491.55 851.41 491.83 851.67 492.13 c -851.94 492.42 852.07 492.79 852.07 493.23 c -852.07 493.79 851.85 494.25 851.40 494.61 c -850.96 494.97 850.40 495.15 849.71 495.15 c -h -856.05 495.15 m -855.46 495.15 855.01 494.98 854.68 494.64 c -854.35 494.31 854.19 493.84 854.19 493.24 c -854.19 489.50 l -853.39 489.50 l -853.39 488.64 l -854.19 488.64 l -854.19 487.48 l -855.34 487.37 l -855.34 488.64 l -857.01 488.64 l -857.01 489.50 l -855.34 489.50 l -855.34 493.03 l -855.34 493.86 855.70 494.28 856.42 494.28 c -856.57 494.28 856.76 494.25 856.98 494.20 c -856.98 495.00 l -856.62 495.10 856.31 495.15 856.05 495.15 c -h -861.91 494.19 m -861.22 494.83 860.55 495.15 859.91 495.15 c -859.38 495.15 858.95 494.98 858.60 494.65 c -858.25 494.32 858.08 493.90 858.08 493.40 c -858.08 492.71 858.37 492.17 858.95 491.80 c -859.54 491.42 860.38 491.24 861.46 491.24 c -861.74 491.24 l -861.74 490.47 l -861.74 489.73 861.36 489.36 860.60 489.36 c -859.99 489.36 859.34 489.55 858.63 489.93 c -858.63 488.97 l -859.41 488.65 860.13 488.50 860.81 488.50 c -861.53 488.50 862.05 488.66 862.39 488.98 c -862.73 489.30 862.89 489.79 862.89 490.47 c -862.89 493.35 l -862.89 494.01 863.10 494.34 863.50 494.34 c -863.55 494.34 863.63 494.34 863.73 494.32 c -863.81 494.96 l -863.55 495.08 863.26 495.15 862.94 495.15 c -862.40 495.15 862.06 494.83 861.91 494.19 c -h -861.74 493.56 m -861.74 491.92 l -861.35 491.91 l -860.72 491.91 860.21 492.03 859.82 492.27 c -859.43 492.51 859.23 492.82 859.23 493.21 c -859.23 493.49 859.33 493.72 859.53 493.92 c -859.72 494.11 859.96 494.20 860.24 494.20 c -860.72 494.20 861.22 493.99 861.74 493.56 c -h -865.26 495.00 m -865.26 488.64 l -866.41 488.64 l -866.41 489.83 l -867.02 488.94 867.77 488.50 868.65 488.50 c -869.20 488.50 869.64 488.67 869.97 489.02 c -870.29 489.37 870.46 489.84 870.46 490.43 c -870.46 495.00 l -869.30 495.00 l -869.30 490.80 l -869.30 490.33 869.24 490.00 869.10 489.79 c -868.96 489.59 868.73 489.49 868.41 489.49 c -867.70 489.49 867.04 489.96 866.41 490.88 c -866.41 495.00 l -h -875.19 495.15 m -874.33 495.15 873.62 494.83 873.05 494.19 c -872.48 493.55 872.20 492.75 872.20 491.78 c -872.20 490.75 872.48 489.94 873.04 489.36 c -873.60 488.79 874.38 488.50 875.39 488.50 c -875.88 488.50 876.44 488.56 877.05 488.70 c -877.05 489.67 l -876.40 489.48 875.87 489.38 875.46 489.38 c -874.87 489.38 874.39 489.60 874.04 490.05 c -873.68 490.49 873.50 491.08 873.50 491.82 c -873.50 492.53 873.68 493.11 874.05 493.55 c -874.42 493.99 874.90 494.21 875.49 494.21 c -876.02 494.21 876.56 494.08 877.12 493.81 c -877.12 494.81 l -876.38 495.03 875.73 495.15 875.19 495.15 c -h -883.51 494.79 m -882.74 495.03 882.08 495.15 881.53 495.15 c -880.59 495.15 879.83 494.83 879.23 494.21 c -878.64 493.59 878.35 492.78 878.35 491.79 c -878.35 490.82 878.61 490.03 879.13 489.42 c -879.65 488.80 880.32 488.49 881.13 488.49 c -881.90 488.49 882.49 488.76 882.91 489.31 c -883.33 489.86 883.54 490.63 883.54 491.64 c -883.54 492.00 l -879.52 492.00 l -879.69 493.51 880.43 494.27 881.74 494.27 c -882.22 494.27 882.81 494.14 883.51 493.88 c -h -879.58 491.13 m -882.38 491.13 l -882.38 489.95 881.94 489.36 881.06 489.36 c -880.17 489.36 879.68 489.95 879.58 491.13 c -h -884.38 497.02 m -884.38 496.15 l -890.38 496.15 l -890.38 497.02 l -h -891.54 495.00 m -891.54 488.64 l -892.69 488.64 l -892.69 495.00 l -h -891.54 487.48 m -891.54 486.33 l -892.69 486.33 l -892.69 487.48 l -h -899.09 495.00 m -899.09 493.80 l -898.62 494.70 897.91 495.15 896.97 495.15 c -896.20 495.15 895.60 494.87 895.16 494.31 c -894.72 493.75 894.50 492.99 894.50 492.02 c -894.50 490.96 894.75 490.11 895.25 489.46 c -895.75 488.82 896.40 488.50 897.21 488.50 c -897.97 488.50 898.59 488.79 899.09 489.36 c -899.09 485.75 l -900.25 485.75 l -900.25 495.00 l -h -899.09 490.15 m -898.49 489.63 897.92 489.36 897.39 489.36 c -896.28 489.36 895.73 490.21 895.73 491.90 c -895.73 493.39 896.22 494.13 897.21 494.13 c -897.85 494.13 898.47 493.78 899.09 493.08 c -h -901.84 495.94 m -901.84 496.73 l -902.68 496.16 903.35 495.38 903.84 494.39 c -904.34 493.41 904.58 492.36 904.58 491.24 c -904.58 490.12 904.34 489.08 903.84 488.09 c -903.35 487.10 902.68 486.32 901.84 485.75 c -901.84 486.54 l -902.41 487.17 902.82 487.84 903.06 488.56 c -903.31 489.28 903.43 490.17 903.43 491.24 c -903.43 492.31 903.31 493.20 903.06 493.92 c -902.82 494.64 902.41 495.31 901.84 495.94 c -h -f -705.44 542.19 m -704.74 542.83 704.08 543.15 703.44 543.15 c -702.91 543.15 702.47 542.98 702.12 542.65 c -701.78 542.32 701.60 541.90 701.60 541.40 c -701.60 540.71 701.90 540.17 702.48 539.80 c -703.06 539.42 703.90 539.24 704.99 539.24 c -705.27 539.24 l -705.27 538.47 l -705.27 537.73 704.89 537.36 704.13 537.36 c -703.52 537.36 702.86 537.55 702.15 537.93 c -702.15 536.97 l -702.93 536.65 703.66 536.50 704.34 536.50 c -705.05 536.50 705.58 536.66 705.91 536.98 c -706.25 537.30 706.42 537.79 706.42 538.47 c -706.42 541.35 l -706.42 542.01 706.62 542.34 707.03 542.34 c -707.08 542.34 707.15 542.34 707.25 542.32 c -707.33 542.96 l -707.07 543.08 706.78 543.15 706.47 543.15 c -705.93 543.15 705.58 542.83 705.44 542.19 c -h -705.27 541.56 m -705.27 539.92 l -704.88 539.91 l -704.25 539.91 703.73 540.03 703.34 540.27 c -702.95 540.51 702.76 540.82 702.76 541.21 c -702.76 541.49 702.86 541.72 703.05 541.92 c -703.25 542.11 703.48 542.20 703.77 542.20 c -704.25 542.20 704.75 541.99 705.27 541.56 c -h -711.27 543.15 m -710.41 543.15 709.69 542.83 709.13 542.19 c -708.56 541.55 708.28 540.75 708.28 539.78 c -708.28 538.75 708.56 537.94 709.12 537.36 c -709.68 536.79 710.46 536.50 711.46 536.50 c -711.96 536.50 712.52 536.56 713.13 536.70 c -713.13 537.67 l -712.48 537.48 711.95 537.38 711.54 537.38 c -710.95 537.38 710.47 537.60 710.11 538.05 c -709.76 538.49 709.58 539.08 709.58 539.82 c -709.58 540.53 709.76 541.11 710.13 541.55 c -710.50 541.99 710.98 542.21 711.57 542.21 c -712.10 542.21 712.64 542.08 713.20 541.81 c -713.20 542.81 l -712.45 543.03 711.81 543.15 711.27 543.15 c -h -716.84 543.15 m -716.25 543.15 715.79 542.98 715.47 542.64 c -715.14 542.31 714.97 541.84 714.97 541.24 c -714.97 537.50 l -714.18 537.50 l -714.18 536.64 l -714.97 536.64 l -714.97 535.48 l -716.13 535.37 l -716.13 536.64 l -717.79 536.64 l -717.79 537.50 l -716.13 537.50 l -716.13 541.03 l -716.13 541.86 716.49 542.28 717.21 542.28 c -717.36 542.28 717.54 542.25 717.76 542.20 c -717.76 543.00 l -717.41 543.10 717.10 543.15 716.84 543.15 c -h -719.42 543.00 m -719.42 536.64 l -720.57 536.64 l -720.57 543.00 l -h -719.42 535.48 m -719.42 534.33 l -720.57 534.33 l -720.57 535.48 l -h -725.38 543.15 m -724.46 543.15 723.74 542.84 723.20 542.24 c -722.65 541.64 722.38 540.83 722.38 539.82 c -722.38 538.79 722.65 537.99 723.20 537.39 c -723.74 536.79 724.48 536.50 725.42 536.50 c -726.35 536.50 727.09 536.79 727.63 537.39 c -728.18 537.99 728.45 538.79 728.45 539.81 c -728.45 540.85 728.18 541.66 727.63 542.26 c -727.08 542.85 726.33 543.15 725.38 543.15 c -h -725.39 542.28 m -726.62 542.28 727.23 541.46 727.23 539.81 c -727.23 538.18 726.62 537.36 725.42 537.36 c -724.21 537.36 723.61 538.18 723.61 539.82 c -723.61 541.46 724.21 542.28 725.39 542.28 c -h -730.26 543.00 m -730.26 536.64 l -731.41 536.64 l -731.41 537.83 l -732.02 536.94 732.77 536.50 733.65 536.50 c -734.20 536.50 734.64 536.67 734.97 537.02 c -735.29 537.37 735.46 537.84 735.46 538.43 c -735.46 543.00 l -734.30 543.00 l -734.30 538.80 l -734.30 538.33 734.24 538.00 734.10 537.79 c -733.96 537.59 733.73 537.49 733.41 537.49 c -732.70 537.49 732.04 537.96 731.41 538.88 c -731.41 543.00 l -h -737.85 541.05 m -737.85 540.18 l -744.79 540.18 l -744.79 541.05 l -h -737.85 538.88 m -737.85 538.01 l -744.79 538.01 l -744.79 538.88 l -h -747.03 536.93 m -746.74 533.75 l -748.19 533.75 l -747.89 536.93 l -h -754.07 543.00 m -754.07 541.80 l -753.61 542.70 752.90 543.15 751.95 543.15 c -751.19 543.15 750.58 542.87 750.15 542.31 c -749.71 541.75 749.49 540.99 749.49 540.02 c -749.49 538.96 749.74 538.11 750.23 537.46 c -750.73 536.82 751.39 536.50 752.20 536.50 c -752.95 536.50 753.58 536.79 754.07 537.36 c -754.07 533.75 l -755.23 533.75 l -755.23 543.00 l -h -754.07 538.15 m -753.48 537.63 752.91 537.36 752.38 537.36 c -751.27 537.36 750.72 538.21 750.72 539.90 c -750.72 541.39 751.21 542.13 752.19 542.13 c -752.83 542.13 753.46 541.78 754.07 541.08 c -h -760.03 543.15 m -759.12 543.15 758.40 542.84 757.85 542.24 c -757.31 541.64 757.04 540.83 757.04 539.82 c -757.04 538.79 757.31 537.99 757.86 537.39 c -758.40 536.79 759.14 536.50 760.07 536.50 c -761.01 536.50 761.75 536.79 762.29 537.39 c -762.84 537.99 763.11 538.79 763.11 539.81 c -763.11 540.85 762.84 541.66 762.29 542.26 c -761.74 542.85 760.99 543.15 760.03 543.15 c -h -760.05 542.28 m -761.27 542.28 761.88 541.46 761.88 539.81 c -761.88 538.18 761.28 537.36 760.07 537.36 c -758.87 537.36 758.27 538.18 758.27 539.82 c -758.27 541.46 758.86 542.28 760.05 542.28 c -h -763.76 545.02 m -763.76 544.15 l -769.76 544.15 l -769.76 545.02 l -h -772.60 543.15 m -772.07 543.15 771.43 543.02 770.68 542.78 c -770.68 541.72 l -771.43 542.09 772.09 542.28 772.65 542.28 c -772.98 542.28 773.26 542.19 773.47 542.01 c -773.69 541.83 773.80 541.61 773.80 541.34 c -773.80 540.94 773.50 540.62 772.88 540.36 c -772.21 540.07 l -771.21 539.66 770.71 539.06 770.71 538.28 c -770.71 537.73 770.91 537.29 771.30 536.97 c -771.70 536.66 772.23 536.50 772.92 536.50 c -773.27 536.50 773.71 536.54 774.24 536.64 c -774.48 536.69 l -774.48 537.65 l -773.83 537.46 773.32 537.36 772.94 537.36 c -772.20 537.36 771.83 537.63 771.83 538.17 c -771.83 538.52 772.11 538.81 772.67 539.05 c -773.23 539.29 l -773.86 539.55 774.30 539.83 774.56 540.13 c -774.83 540.42 774.96 540.79 774.96 541.23 c -774.96 541.79 774.74 542.25 774.29 542.61 c -773.85 542.97 773.29 543.15 772.60 543.15 c -h -779.52 543.15 m -778.61 543.15 777.88 542.84 777.34 542.24 c -776.80 541.64 776.53 540.83 776.53 539.82 c -776.53 538.79 776.80 537.99 777.34 537.39 c -777.89 536.79 778.63 536.50 779.56 536.50 c -780.50 536.50 781.24 536.79 781.78 537.39 c -782.33 537.99 782.60 538.79 782.60 539.81 c -782.60 540.85 782.32 541.66 781.78 542.26 c -781.23 542.85 780.48 543.15 779.52 543.15 c -h -779.54 542.28 m -780.76 542.28 781.37 541.46 781.37 539.81 c -781.37 538.18 780.77 537.36 779.56 537.36 c -778.36 537.36 777.76 538.18 777.76 539.82 c -777.76 541.46 778.35 542.28 779.54 542.28 c -h -784.40 543.00 m -784.40 536.64 l -785.56 536.64 l -785.56 537.83 l -786.12 536.94 786.84 536.50 787.72 536.50 c -788.58 536.50 789.16 536.94 789.46 537.83 c -790.01 536.94 790.72 536.49 791.60 536.49 c -792.16 536.49 792.60 536.66 792.90 536.99 c -793.21 537.32 793.37 537.78 793.37 538.37 c -793.37 543.00 l -792.21 543.00 l -792.21 538.55 l -792.21 537.83 791.92 537.46 791.35 537.46 c -790.75 537.46 790.12 537.89 789.46 538.73 c -789.46 543.00 l -788.30 543.00 l -788.30 538.55 l -788.30 537.82 788.01 537.46 787.43 537.46 c -786.84 537.46 786.22 537.88 785.56 538.73 c -785.56 543.00 l -h -800.27 542.79 m -799.50 543.03 798.83 543.15 798.28 543.15 c -797.35 543.15 796.58 542.83 795.99 542.21 c -795.40 541.59 795.10 540.78 795.10 539.79 c -795.10 538.82 795.36 538.03 795.88 537.42 c -796.41 536.80 797.07 536.49 797.88 536.49 c -798.65 536.49 799.25 536.76 799.67 537.31 c -800.09 537.86 800.30 538.63 800.30 539.64 c -800.29 540.00 l -796.28 540.00 l -796.45 541.51 797.19 542.27 798.50 542.27 c -798.98 542.27 799.57 542.14 800.27 541.88 c -h -796.33 539.13 m -799.14 539.13 l -799.14 537.95 798.70 537.36 797.81 537.36 c -796.93 537.36 796.43 537.95 796.33 539.13 c -h -804.20 543.15 m -803.62 543.15 803.16 542.98 802.83 542.64 c -802.50 542.31 802.34 541.84 802.34 541.24 c -802.34 537.50 l -801.54 537.50 l -801.54 536.64 l -802.34 536.64 l -802.34 535.48 l -803.49 535.37 l -803.49 536.64 l -805.16 536.64 l -805.16 537.50 l -803.49 537.50 l -803.49 541.03 l -803.49 541.86 803.85 542.28 804.57 542.28 c -804.72 542.28 804.91 542.25 805.13 542.20 c -805.13 543.00 l -804.77 543.10 804.46 543.15 804.20 543.15 c -h -806.78 543.00 m -806.78 533.75 l -807.93 533.75 l -807.93 537.83 l -808.54 536.94 809.29 536.50 810.17 536.50 c -810.72 536.50 811.16 536.67 811.49 537.02 c -811.82 537.37 811.98 537.84 811.98 538.43 c -811.98 543.00 l -810.83 543.00 l -810.83 538.80 l -810.83 538.33 810.76 538.00 810.62 537.79 c -810.48 537.59 810.25 537.49 809.93 537.49 c -809.22 537.49 808.56 537.96 807.93 538.88 c -807.93 543.00 l -h -814.23 543.00 m -814.23 536.64 l -815.38 536.64 l -815.38 543.00 l -h -814.23 535.48 m -814.23 534.33 l -815.38 534.33 l -815.38 535.48 l -h -817.70 543.00 m -817.70 536.64 l -818.85 536.64 l -818.85 537.83 l -819.46 536.94 820.21 536.50 821.09 536.50 c -821.64 536.50 822.08 536.67 822.41 537.02 c -822.73 537.37 822.90 537.84 822.90 538.43 c -822.90 543.00 l -821.74 543.00 l -821.74 538.80 l -821.74 538.33 821.67 538.00 821.54 537.79 c -821.40 537.59 821.17 537.49 820.85 537.49 c -820.14 537.49 819.47 537.96 818.85 538.88 c -818.85 543.00 l -h -825.17 545.12 m -825.30 544.11 l -825.97 544.43 826.63 544.59 827.28 544.59 c -828.58 544.59 829.23 543.90 829.23 542.52 c -829.23 541.52 l -828.80 542.41 828.10 542.85 827.13 542.85 c -826.37 542.85 825.76 542.58 825.31 542.02 c -824.86 541.47 824.64 540.72 824.64 539.78 c -824.64 538.81 824.89 538.02 825.41 537.41 c -825.92 536.80 826.58 536.50 827.39 536.50 c -828.10 536.50 828.71 536.79 829.23 537.36 c -829.23 536.64 l -830.39 536.64 l -830.39 541.27 l -830.39 542.26 830.33 543.00 830.23 543.48 c -830.13 543.96 829.93 544.35 829.65 544.65 c -829.14 545.19 828.36 545.46 827.30 545.46 c -826.56 545.46 825.85 545.34 825.17 545.12 c -h -829.23 540.80 m -829.23 538.15 l -828.72 537.63 828.17 537.36 827.57 537.36 c -827.04 537.36 826.62 537.58 826.32 538.00 c -826.02 538.43 825.87 539.01 825.87 539.75 c -825.87 541.15 826.36 541.85 827.34 541.85 c -828.01 541.85 828.64 541.50 829.23 540.80 c -h -832.41 536.93 m -832.12 533.75 l -833.57 533.75 l -833.28 536.93 l -h -f -706.82 647.79 m -706.04 648.03 705.38 648.15 704.83 648.15 c -703.89 648.15 703.13 647.83 702.54 647.21 c -701.95 646.59 701.65 645.78 701.65 644.79 c -701.65 643.82 701.91 643.03 702.43 642.42 c -702.95 641.80 703.62 641.49 704.43 641.49 c -705.20 641.49 705.80 641.76 706.22 642.31 c -706.64 642.86 706.85 643.63 706.85 644.64 c -706.84 645.00 l -702.83 645.00 l -703.00 646.51 703.74 647.27 705.05 647.27 c -705.53 647.27 706.12 647.14 706.82 646.88 c -h -702.88 644.13 m -705.69 644.13 l -705.69 642.95 705.25 642.36 704.36 642.36 c -703.48 642.36 702.98 642.95 702.88 644.13 c -h -708.20 648.00 m -710.62 644.71 l -708.27 641.64 l -709.64 641.64 l -711.50 644.09 l -713.18 641.64 l -714.31 641.64 l -712.10 644.87 l -714.50 648.00 l -713.13 648.00 l -711.21 645.48 l -709.36 648.00 l -h -720.86 647.79 m -720.09 648.03 719.43 648.15 718.88 648.15 c -717.94 648.15 717.17 647.83 716.58 647.21 c -715.99 646.59 715.70 645.78 715.70 644.79 c -715.70 643.82 715.96 643.03 716.48 642.42 c -717.00 641.80 717.67 641.49 718.48 641.49 c -719.25 641.49 719.84 641.76 720.26 642.31 c -720.68 642.86 720.89 643.63 720.89 644.64 c -720.89 645.00 l -716.87 645.00 l -717.04 646.51 717.78 647.27 719.09 647.27 c -719.57 647.27 720.16 647.14 720.86 646.88 c -h -716.93 644.13 m -719.73 644.13 l -719.73 642.95 719.29 642.36 718.41 642.36 c -717.52 642.36 717.03 642.95 716.93 644.13 c -h -725.37 648.15 m -724.51 648.15 723.80 647.83 723.23 647.19 c -722.66 646.55 722.38 645.75 722.38 644.78 c -722.38 643.75 722.66 642.94 723.22 642.36 c -723.78 641.79 724.56 641.50 725.57 641.50 c -726.06 641.50 726.62 641.56 727.23 641.70 c -727.23 642.67 l -726.58 642.48 726.05 642.38 725.64 642.38 c -725.05 642.38 724.58 642.60 724.22 643.05 c -723.86 643.49 723.68 644.08 723.68 644.82 c -723.68 645.53 723.87 646.11 724.23 646.55 c -724.60 646.99 725.08 647.21 725.67 647.21 c -726.20 647.21 726.74 647.08 727.30 646.81 c -727.30 647.81 l -726.56 648.03 725.91 648.15 725.37 648.15 c -h -733.01 648.00 m -733.01 646.80 l -732.40 647.70 731.65 648.15 730.78 648.15 c -730.22 648.15 729.78 647.97 729.45 647.62 c -729.12 647.27 728.96 646.80 728.96 646.21 c -728.96 641.64 l -730.12 641.64 l -730.12 645.83 l -730.12 646.31 730.18 646.65 730.32 646.85 c -730.46 647.05 730.69 647.15 731.02 647.15 c -731.72 647.15 732.38 646.69 733.01 645.76 c -733.01 641.64 l -734.16 641.64 l -734.16 648.00 l -h -738.39 648.15 m -737.80 648.15 737.35 647.98 737.02 647.64 c -736.69 647.31 736.53 646.84 736.53 646.24 c -736.53 642.50 l -735.73 642.50 l -735.73 641.64 l -736.53 641.64 l -736.53 640.48 l -737.68 640.37 l -737.68 641.64 l -739.34 641.64 l -739.34 642.50 l -737.68 642.50 l -737.68 646.03 l -737.68 646.86 738.04 647.28 738.76 647.28 c -738.91 647.28 739.10 647.25 739.31 647.20 c -739.31 648.00 l -738.96 648.10 738.65 648.15 738.39 648.15 c -h -745.63 647.79 m -744.86 648.03 744.20 648.15 743.64 648.15 c -742.71 648.15 741.94 647.83 741.35 647.21 c -740.76 646.59 740.46 645.78 740.46 644.79 c -740.46 643.82 740.72 643.03 741.25 642.42 c -741.77 641.80 742.43 641.49 743.25 641.49 c -744.02 641.49 744.61 641.76 745.03 642.31 c -745.45 642.86 745.66 643.63 745.66 644.64 c -745.65 645.00 l -741.64 645.00 l -741.81 646.51 742.55 647.27 743.86 647.27 c -744.34 647.27 744.93 647.14 745.63 646.88 c -h -741.69 644.13 m -744.50 644.13 l -744.50 642.95 744.06 642.36 743.18 642.36 c -742.29 642.36 741.79 642.95 741.69 644.13 c -h -753.14 648.15 m -752.61 648.15 751.97 648.02 751.21 647.78 c -751.21 646.72 l -751.97 647.09 752.62 647.28 753.18 647.28 c -753.52 647.28 753.79 647.19 754.01 647.01 c -754.23 646.83 754.34 646.61 754.34 646.34 c -754.34 645.94 754.03 645.62 753.42 645.36 c -752.74 645.07 l -751.75 644.66 751.25 644.06 751.25 643.28 c -751.25 642.73 751.45 642.29 751.84 641.97 c -752.23 641.66 752.77 641.50 753.45 641.50 c -753.81 641.50 754.25 641.54 754.77 641.64 c -755.01 641.69 l -755.01 642.65 l -754.37 642.46 753.86 642.36 753.48 642.36 c -752.73 642.36 752.36 642.63 752.36 643.17 c -752.36 643.52 752.64 643.81 753.21 644.05 c -753.76 644.29 l -754.39 644.55 754.84 644.83 755.10 645.13 c -755.36 645.42 755.49 645.79 755.49 646.23 c -755.49 646.79 755.27 647.25 754.83 647.61 c -754.39 647.97 753.82 648.15 753.14 648.15 c -h -760.06 648.15 m -759.15 648.15 758.42 647.84 757.88 647.24 c -757.33 646.64 757.06 645.83 757.06 644.82 c -757.06 643.79 757.33 642.99 757.88 642.39 c -758.42 641.79 759.16 641.50 760.10 641.50 c -761.03 641.50 761.77 641.79 762.32 642.39 c -762.86 642.99 763.13 643.79 763.13 644.81 c -763.13 645.85 762.86 646.66 762.31 647.26 c -761.77 647.85 761.01 648.15 760.06 648.15 c -h -760.07 647.28 m -761.30 647.28 761.91 646.46 761.91 644.81 c -761.91 643.18 761.30 642.36 760.10 642.36 c -758.89 642.36 758.29 643.18 758.29 644.82 c -758.29 646.46 758.89 647.28 760.07 647.28 c -h -764.94 648.00 m -764.94 641.64 l -766.09 641.64 l -766.09 642.83 l -766.65 641.94 767.38 641.50 768.26 641.50 c -769.11 641.50 769.69 641.94 770.00 642.83 c -770.55 641.94 771.26 641.49 772.13 641.49 c -772.70 641.49 773.13 641.66 773.44 641.99 c -773.75 642.32 773.90 642.78 773.90 643.37 c -773.90 648.00 l -772.74 648.00 l -772.74 643.55 l -772.74 642.83 772.46 642.46 771.88 642.46 c -771.28 642.46 770.66 642.89 770.00 643.73 c -770.00 648.00 l -768.84 648.00 l -768.84 643.55 l -768.84 642.82 768.55 642.46 767.96 642.46 c -767.38 642.46 766.76 642.88 766.09 643.73 c -766.09 648.00 l -h -780.80 647.79 m -780.03 648.03 779.37 648.15 778.82 648.15 c -777.88 648.15 777.12 647.83 776.52 647.21 c -775.93 646.59 775.64 645.78 775.64 644.79 c -775.64 643.82 775.90 643.03 776.42 642.42 c -776.94 641.80 777.61 641.49 778.42 641.49 c -779.19 641.49 779.78 641.76 780.20 642.31 c -780.62 642.86 780.83 643.63 780.83 644.64 c -780.83 645.00 l -776.81 645.00 l -776.98 646.51 777.72 647.27 779.04 647.27 c -779.52 647.27 780.11 647.14 780.80 646.88 c -h -776.87 644.13 m -779.67 644.13 l -779.67 642.95 779.23 642.36 778.35 642.36 c -777.46 642.36 776.97 642.95 776.87 644.13 c -h -784.74 648.15 m -784.15 648.15 783.69 647.98 783.37 647.64 c -783.04 647.31 782.87 646.84 782.87 646.24 c -782.87 642.50 l -782.08 642.50 l -782.08 641.64 l -782.87 641.64 l -782.87 640.48 l -784.03 640.37 l -784.03 641.64 l -785.69 641.64 l -785.69 642.50 l -784.03 642.50 l -784.03 646.03 l -784.03 646.86 784.39 647.28 785.11 647.28 c -785.26 647.28 785.44 647.25 785.66 647.20 c -785.66 648.00 l -785.31 648.10 785.00 648.15 784.74 648.15 c -h -787.31 648.00 m -787.31 638.75 l -788.47 638.75 l -788.47 642.83 l -789.08 641.94 789.82 641.50 790.71 641.50 c -791.26 641.50 791.70 641.67 792.03 642.02 c -792.35 642.37 792.52 642.84 792.52 643.43 c -792.52 648.00 l -791.36 648.00 l -791.36 643.80 l -791.36 643.33 791.29 643.00 791.16 642.79 c -791.02 642.59 790.79 642.49 790.47 642.49 c -789.76 642.49 789.09 642.96 788.47 643.88 c -788.47 648.00 l -h -794.76 648.00 m -794.76 641.64 l -795.92 641.64 l -795.92 648.00 l -h -794.76 640.48 m -794.76 639.33 l -795.92 639.33 l -795.92 640.48 l -h -798.23 648.00 m -798.23 641.64 l -799.38 641.64 l -799.38 642.83 l -799.99 641.94 800.74 641.50 801.62 641.50 c -802.17 641.50 802.61 641.67 802.94 642.02 c -803.27 642.37 803.43 642.84 803.43 643.43 c -803.43 648.00 l -802.28 648.00 l -802.28 643.80 l -802.28 643.33 802.21 643.00 802.07 642.79 c -801.93 642.59 801.70 642.49 801.38 642.49 c -800.68 642.49 800.01 642.96 799.38 643.88 c -799.38 648.00 l -h -805.70 650.12 m -805.84 649.11 l -806.50 649.43 807.16 649.59 807.81 649.59 c -809.11 649.59 809.76 648.90 809.76 647.52 c -809.76 646.52 l -809.34 647.41 808.64 647.85 807.66 647.85 c -806.90 647.85 806.30 647.58 805.85 647.02 c -805.40 646.47 805.17 645.72 805.17 644.78 c -805.17 643.81 805.43 643.02 805.94 642.41 c -806.45 641.80 807.12 641.50 807.93 641.50 c -808.64 641.50 809.25 641.79 809.76 642.36 c -809.76 641.64 l -810.92 641.64 l -810.92 646.27 l -810.92 647.26 810.87 648.00 810.77 648.48 c -810.66 648.96 810.47 649.35 810.18 649.65 c -809.68 650.19 808.90 650.46 807.83 650.46 c -807.09 650.46 806.38 650.34 805.70 650.12 c -h -809.76 645.80 m -809.76 643.15 l -809.25 642.63 808.70 642.36 808.10 642.36 c -807.57 642.36 807.16 642.58 806.86 643.00 c -806.55 643.43 806.40 644.01 806.40 644.75 c -806.40 646.15 806.89 646.85 807.88 646.85 c -808.54 646.85 809.17 646.50 809.76 645.80 c -h -816.96 648.00 m -816.96 638.75 l -818.11 638.75 l -818.11 648.00 l -h -822.92 648.15 m -822.01 648.15 821.28 647.84 820.74 647.24 c -820.19 646.64 819.92 645.83 819.92 644.82 c -819.92 643.79 820.19 642.99 820.74 642.39 c -821.28 641.79 822.02 641.50 822.96 641.50 c -823.89 641.50 824.63 641.79 825.17 642.39 c -825.72 642.99 825.99 643.79 825.99 644.81 c -825.99 645.85 825.72 646.66 825.17 647.26 c -824.62 647.85 823.87 648.15 822.92 648.15 c -h -822.93 647.28 m -824.16 647.28 824.77 646.46 824.77 644.81 c -824.77 643.18 824.16 642.36 822.96 642.36 c -821.75 642.36 821.15 643.18 821.15 644.82 c -821.15 646.46 821.75 647.28 822.93 647.28 c -h -827.82 650.12 m -827.96 649.11 l -828.62 649.43 829.28 649.59 829.93 649.59 c -831.23 649.59 831.88 648.90 831.88 647.52 c -831.88 646.52 l -831.46 647.41 830.76 647.85 829.78 647.85 c -829.02 647.85 828.42 647.58 827.97 647.02 c -827.52 646.47 827.29 645.72 827.29 644.78 c -827.29 643.81 827.55 643.02 828.06 642.41 c -828.57 641.80 829.23 641.50 830.05 641.50 c -830.76 641.50 831.37 641.79 831.88 642.36 c -831.88 641.64 l -833.04 641.64 l -833.04 646.27 l -833.04 647.26 832.99 648.00 832.89 648.48 c -832.78 648.96 832.59 649.35 832.30 649.65 c -831.80 650.19 831.02 650.46 829.95 650.46 c -829.21 650.46 828.50 650.34 827.82 650.12 c -h -831.88 645.80 m -831.88 643.15 l -831.37 642.63 830.82 642.36 830.22 642.36 c -829.69 642.36 829.28 642.58 828.97 643.00 c -828.67 643.43 828.52 644.01 828.52 644.75 c -828.52 646.15 829.01 646.85 829.99 646.85 c -830.66 646.85 831.29 646.50 831.88 645.80 c -h -835.28 648.00 m -835.28 641.64 l -836.43 641.64 l -836.43 648.00 l -h -835.28 640.48 m -835.28 639.33 l -836.43 639.33 l -836.43 640.48 l -h -841.23 648.15 m -840.37 648.15 839.66 647.83 839.09 647.19 c -838.53 646.55 838.24 645.75 838.24 644.78 c -838.24 643.75 838.52 642.94 839.08 642.36 c -839.65 641.79 840.43 641.50 841.43 641.50 c -841.93 641.50 842.48 641.56 843.10 641.70 c -843.10 642.67 l -842.44 642.48 841.91 642.38 841.50 642.38 c -840.91 642.38 840.44 642.60 840.08 643.05 c -839.72 643.49 839.54 644.08 839.54 644.82 c -839.54 645.53 839.73 646.11 840.10 646.55 c -840.46 646.99 840.94 647.21 841.54 647.21 c -842.06 647.21 842.61 647.08 843.17 646.81 c -843.17 647.81 l -842.42 648.03 841.78 648.15 841.23 648.15 c -h -f -889.84 610.15 m -889.31 610.15 888.67 610.02 887.92 609.78 c -887.92 608.72 l -888.67 609.09 889.33 609.28 889.89 609.28 c -890.22 609.28 890.50 609.19 890.71 609.01 c -890.93 608.83 891.04 608.61 891.04 608.34 c -891.04 607.94 890.74 607.62 890.12 607.36 c -889.45 607.07 l -888.45 606.66 887.96 606.06 887.96 605.28 c -887.96 604.73 888.15 604.29 888.54 603.97 c -888.94 603.66 889.47 603.50 890.16 603.50 c -890.51 603.50 890.95 603.54 891.48 603.64 c -891.72 603.69 l -891.72 604.65 l -891.07 604.46 890.56 604.36 890.18 604.36 c -889.44 604.36 889.07 604.63 889.07 605.17 c -889.07 605.52 889.35 605.81 889.91 606.05 c -890.47 606.29 l -891.10 606.55 891.54 606.83 891.80 607.13 c -892.07 607.42 892.20 607.79 892.20 608.23 c -892.20 608.79 891.98 609.25 891.54 609.61 c -891.09 609.97 890.53 610.15 889.84 610.15 c -h -898.25 610.00 m -898.25 608.80 l -897.64 609.70 896.89 610.15 896.02 610.15 c -895.46 610.15 895.02 609.97 894.69 609.62 c -894.37 609.27 894.20 608.80 894.20 608.21 c -894.20 603.64 l -895.36 603.64 l -895.36 607.83 l -895.36 608.31 895.42 608.65 895.56 608.85 c -895.70 609.05 895.93 609.15 896.26 609.15 c -896.96 609.15 897.62 608.69 898.25 607.76 c -898.25 603.64 l -899.40 603.64 l -899.40 610.00 l -h -904.20 610.15 m -903.34 610.15 902.63 609.83 902.06 609.19 c -901.50 608.55 901.21 607.75 901.21 606.78 c -901.21 605.75 901.50 604.94 902.06 604.36 c -902.62 603.79 903.40 603.50 904.40 603.50 c -904.90 603.50 905.45 603.56 906.07 603.70 c -906.07 604.67 l -905.41 604.48 904.88 604.38 904.47 604.38 c -903.88 604.38 903.41 604.60 903.05 605.05 c -902.69 605.49 902.52 606.08 902.52 606.82 c -902.52 607.53 902.70 608.11 903.07 608.55 c -903.43 608.99 903.91 609.21 904.51 609.21 c -905.04 609.21 905.58 609.08 906.14 608.81 c -906.14 609.81 l -905.39 610.03 904.75 610.15 904.20 610.15 c -h -910.35 610.15 m -909.49 610.15 908.78 609.83 908.21 609.19 c -907.64 608.55 907.36 607.75 907.36 606.78 c -907.36 605.75 907.64 604.94 908.20 604.36 c -908.76 603.79 909.54 603.50 910.55 603.50 c -911.04 603.50 911.60 603.56 912.21 603.70 c -912.21 604.67 l -911.56 604.48 911.03 604.38 910.62 604.38 c -910.03 604.38 909.56 604.60 909.20 605.05 c -908.84 605.49 908.66 606.08 908.66 606.82 c -908.66 607.53 908.85 608.11 909.21 608.55 c -909.58 608.99 910.06 609.21 910.65 609.21 c -911.18 609.21 911.72 609.08 912.28 608.81 c -912.28 609.81 l -911.54 610.03 910.89 610.15 910.35 610.15 c -h -918.68 609.79 m -917.90 610.03 917.24 610.15 916.69 610.15 c -915.75 610.15 914.99 609.83 914.40 609.21 c -913.80 608.59 913.51 607.78 913.51 606.79 c -913.51 605.82 913.77 605.03 914.29 604.42 c -914.81 603.80 915.48 603.49 916.29 603.49 c -917.06 603.49 917.66 603.76 918.08 604.31 c -918.50 604.86 918.71 605.63 918.71 606.64 c -918.70 607.00 l -914.69 607.00 l -914.85 608.51 915.59 609.27 916.91 609.27 c -917.39 609.27 917.98 609.14 918.68 608.88 c -h -914.74 606.13 m -917.54 606.13 l -917.54 604.95 917.10 604.36 916.22 604.36 c -915.33 604.36 914.84 604.95 914.74 606.13 c -h -922.38 610.15 m -921.86 610.15 921.22 610.02 920.46 609.78 c -920.46 608.72 l -921.22 609.09 921.87 609.28 922.43 609.28 c -922.76 609.28 923.04 609.19 923.26 609.01 c -923.48 608.83 923.59 608.61 923.59 608.34 c -923.59 607.94 923.28 607.62 922.67 607.36 c -921.99 607.07 l -921.00 606.66 920.50 606.06 920.50 605.28 c -920.50 604.73 920.69 604.29 921.09 603.97 c -921.48 603.66 922.02 603.50 922.70 603.50 c -923.06 603.50 923.50 603.54 924.02 603.64 c -924.26 603.69 l -924.26 604.65 l -923.62 604.46 923.10 604.36 922.72 604.36 c -921.98 604.36 921.61 604.63 921.61 605.17 c -921.61 605.52 921.89 605.81 922.46 606.05 c -923.01 606.29 l -923.64 606.55 924.09 606.83 924.35 607.13 c -924.61 607.42 924.74 607.79 924.74 608.23 c -924.74 608.79 924.52 609.25 924.08 609.61 c -923.64 609.97 923.07 610.15 922.38 610.15 c -h -928.50 610.15 m -927.97 610.15 927.33 610.02 926.58 609.78 c -926.58 608.72 l -927.33 609.09 927.99 609.28 928.55 609.28 c -928.88 609.28 929.16 609.19 929.38 609.01 c -929.59 608.83 929.70 608.61 929.70 608.34 c -929.70 607.94 929.40 607.62 928.78 607.36 c -928.11 607.07 l -927.11 606.66 926.62 606.06 926.62 605.28 c -926.62 604.73 926.81 604.29 927.20 603.97 c -927.60 603.66 928.13 603.50 928.82 603.50 c -929.17 603.50 929.61 603.54 930.14 603.64 c -930.38 603.69 l -930.38 604.65 l -929.73 604.46 929.22 604.36 928.84 604.36 c -928.10 604.36 927.73 604.63 927.73 605.17 c -927.73 605.52 928.01 605.81 928.57 606.05 c -929.13 606.29 l -929.76 606.55 930.20 606.83 930.46 607.13 c -930.73 607.42 930.86 607.79 930.86 608.23 c -930.86 608.79 930.64 609.25 930.20 609.61 c -929.75 609.97 929.19 610.15 928.50 610.15 c -h -f -[ 5.0000 5.0000] 0.0000 d -937.50 613.50 m -695.50 613.50 l -S -[] 0.0000 d -695.00 613.00 m -701.00 607.00 l -701.00 619.00 l -h -f* -704.06 391.15 m -703.48 391.15 703.02 390.98 702.69 390.64 c -702.37 390.31 702.20 389.84 702.20 389.24 c -702.20 385.50 l -701.40 385.50 l -701.40 384.64 l -702.20 384.64 l -702.20 383.48 l -703.36 383.37 l -703.36 384.64 l -705.02 384.64 l -705.02 385.50 l -703.36 385.50 l -703.36 389.03 l -703.36 389.86 703.71 390.28 704.43 390.28 c -704.59 390.28 704.77 390.25 704.99 390.20 c -704.99 391.00 l -704.63 391.10 704.33 391.15 704.06 391.15 c -h -711.31 390.79 m -710.53 391.03 709.87 391.15 709.32 391.15 c -708.38 391.15 707.62 390.83 707.03 390.21 c -706.43 389.59 706.14 388.78 706.14 387.79 c -706.14 386.82 706.40 386.03 706.92 385.42 c -707.44 384.80 708.11 384.49 708.92 384.49 c -709.69 384.49 710.29 384.76 710.71 385.31 c -711.13 385.86 711.34 386.63 711.34 387.64 c -711.33 388.00 l -707.32 388.00 l -707.48 389.51 708.22 390.27 709.54 390.27 c -710.02 390.27 710.61 390.14 711.31 389.88 c -h -707.37 387.13 m -710.18 387.13 l -710.18 385.95 709.73 385.36 708.85 385.36 c -707.96 385.36 707.47 385.95 707.37 387.13 c -h -713.33 391.00 m -713.33 384.64 l -714.48 384.64 l -714.48 385.83 l -715.09 384.94 715.84 384.50 716.72 384.50 c -717.27 384.50 717.71 384.67 718.04 385.02 c -718.37 385.37 718.53 385.84 718.53 386.43 c -718.53 391.00 l -717.38 391.00 l -717.38 386.80 l -717.38 386.33 717.31 386.00 717.17 385.79 c -717.03 385.59 716.80 385.49 716.48 385.49 c -715.77 385.49 715.11 385.96 714.48 386.88 c -714.48 391.00 l -h -724.06 390.19 m -723.37 390.83 722.70 391.15 722.06 391.15 c -721.53 391.15 721.09 390.98 720.75 390.65 c -720.40 390.32 720.22 389.90 720.22 389.40 c -720.22 388.71 720.52 388.17 721.10 387.80 c -721.68 387.42 722.52 387.24 723.61 387.24 c -723.89 387.24 l -723.89 386.47 l -723.89 385.73 723.51 385.36 722.75 385.36 c -722.14 385.36 721.48 385.55 720.78 385.93 c -720.78 384.97 l -721.55 384.65 722.28 384.50 722.96 384.50 c -723.67 384.50 724.20 384.66 724.53 384.98 c -724.87 385.30 725.04 385.79 725.04 386.47 c -725.04 389.35 l -725.04 390.01 725.24 390.34 725.65 390.34 c -725.70 390.34 725.78 390.34 725.87 390.32 c -725.96 390.96 l -725.69 391.08 725.40 391.15 725.09 391.15 c -724.55 391.15 724.21 390.83 724.06 390.19 c -h -723.89 389.56 m -723.89 387.92 l -723.50 387.91 l -722.87 387.91 722.36 388.03 721.96 388.27 c -721.57 388.51 721.38 388.82 721.38 389.21 c -721.38 389.49 721.48 389.72 721.67 389.92 c -721.87 390.11 722.11 390.20 722.39 390.20 c -722.87 390.20 723.37 389.99 723.89 389.56 c -h -727.40 391.00 m -727.40 384.64 l -728.56 384.64 l -728.56 385.83 l -729.17 384.94 729.91 384.50 730.79 384.50 c -731.35 384.50 731.79 384.67 732.11 385.02 c -732.44 385.37 732.61 385.84 732.61 386.43 c -732.61 391.00 l -731.45 391.00 l -731.45 386.80 l -731.45 386.33 731.38 386.00 731.24 385.79 c -731.10 385.59 730.88 385.49 730.55 385.49 c -729.85 385.49 729.18 385.96 728.56 386.88 c -728.56 391.00 l -h -736.76 391.15 m -736.17 391.15 735.72 390.98 735.39 390.64 c -735.06 390.31 734.90 389.84 734.90 389.24 c -734.90 385.50 l -734.10 385.50 l -734.10 384.64 l -734.90 384.64 l -734.90 383.48 l -736.05 383.37 l -736.05 384.64 l -737.71 384.64 l -737.71 385.50 l -736.05 385.50 l -736.05 389.03 l -736.05 389.86 736.41 390.28 737.13 390.28 c -737.28 390.28 737.47 390.25 737.69 390.20 c -737.69 391.00 l -737.33 391.10 737.02 391.15 736.76 391.15 c -h -743.28 389.05 m -743.28 388.18 l -750.22 388.18 l -750.22 389.05 l -h -743.28 386.88 m -743.28 386.01 l -750.22 386.01 l -750.22 386.88 l -h -756.47 393.31 m -756.47 384.64 l -757.62 384.64 l -757.62 385.83 l -758.10 384.94 758.81 384.50 759.75 384.50 c -760.52 384.50 761.12 384.78 761.56 385.33 c -762.00 385.89 762.22 386.66 762.22 387.62 c -762.22 388.68 761.97 389.53 761.47 390.18 c -760.97 390.82 760.32 391.15 759.51 391.15 c -758.75 391.15 758.12 390.86 757.62 390.28 c -757.62 393.31 l -h -757.62 389.48 m -758.22 390.01 758.79 390.28 759.32 390.28 c -760.43 390.28 760.99 389.43 760.99 387.74 c -760.99 386.25 760.50 385.50 759.51 385.50 c -758.87 385.50 758.24 385.85 757.62 386.55 c -h -767.30 390.19 m -766.61 390.83 765.95 391.15 765.31 391.15 c -764.78 391.15 764.34 390.98 763.99 390.65 c -763.65 390.32 763.47 389.90 763.47 389.40 c -763.47 388.71 763.76 388.17 764.35 387.80 c -764.93 387.42 765.77 387.24 766.86 387.24 c -767.13 387.24 l -767.13 386.47 l -767.13 385.73 766.76 385.36 766.00 385.36 c -765.39 385.36 764.73 385.55 764.02 385.93 c -764.02 384.97 l -764.80 384.65 765.53 384.50 766.21 384.50 c -766.92 384.50 767.44 384.66 767.78 384.98 c -768.12 385.30 768.29 385.79 768.29 386.47 c -768.29 389.35 l -768.29 390.01 768.49 390.34 768.90 390.34 c -768.95 390.34 769.02 390.34 769.12 390.32 c -769.20 390.96 l -768.94 391.08 768.65 391.15 768.34 391.15 c -767.80 391.15 767.45 390.83 767.30 390.19 c -h -767.13 389.56 m -767.13 387.92 l -766.75 387.91 l -766.12 387.91 765.60 388.03 765.21 388.27 c -764.82 388.51 764.63 388.82 764.63 389.21 c -764.63 389.49 764.72 389.72 764.92 389.92 c -765.12 390.11 765.35 390.20 765.63 390.20 c -766.12 390.20 766.62 389.99 767.13 389.56 c -h -770.65 391.00 m -770.65 384.64 l -771.80 384.64 l -771.80 385.83 l -772.26 384.94 772.93 384.50 773.80 384.50 c -773.91 384.50 774.04 384.51 774.17 384.53 c -774.17 385.60 l -773.97 385.54 773.79 385.50 773.64 385.50 c -772.91 385.50 772.30 385.94 771.80 386.80 c -771.80 391.00 l -h -777.25 391.15 m -776.72 391.15 776.08 391.02 775.33 390.78 c -775.33 389.72 l -776.08 390.09 776.74 390.28 777.29 390.28 c -777.63 390.28 777.90 390.19 778.12 390.01 c -778.34 389.83 778.45 389.61 778.45 389.34 c -778.45 388.94 778.14 388.62 777.53 388.36 c -776.86 388.07 l -775.86 387.66 775.36 387.06 775.36 386.28 c -775.36 385.73 775.56 385.29 775.95 384.97 c -776.34 384.66 776.88 384.50 777.56 384.50 c -777.92 384.50 778.36 384.54 778.88 384.64 c -779.12 384.69 l -779.12 385.65 l -778.48 385.46 777.97 385.36 777.59 385.36 c -776.85 385.36 776.47 385.63 776.47 386.17 c -776.47 386.52 776.76 386.81 777.32 387.05 c -777.88 387.29 l -778.50 387.55 778.95 387.83 779.21 388.13 c -779.47 388.42 779.60 388.79 779.60 389.23 c -779.60 389.79 779.38 390.25 778.94 390.61 c -778.50 390.97 777.94 391.15 777.25 391.15 c -h -786.34 390.79 m -785.57 391.03 784.91 391.15 784.36 391.15 c -783.42 391.15 782.65 390.83 782.06 390.21 c -781.47 389.59 781.17 388.78 781.17 387.79 c -781.17 386.82 781.43 386.03 781.96 385.42 c -782.48 384.80 783.14 384.49 783.96 384.49 c -784.73 384.49 785.32 384.76 785.74 385.31 c -786.16 385.86 786.37 386.63 786.37 387.64 c -786.37 388.00 l -782.35 388.00 l -782.52 389.51 783.26 390.27 784.57 390.27 c -785.05 390.27 785.64 390.14 786.34 389.88 c -h -782.40 387.13 m -785.21 387.13 l -785.21 385.95 784.77 385.36 783.89 385.36 c -783.00 385.36 782.51 385.95 782.40 387.13 c -h -790.68 391.94 m -790.68 392.73 l -789.83 392.16 789.17 391.38 788.67 390.39 c -788.18 389.41 787.93 388.36 787.93 387.24 c -787.93 386.12 788.18 385.08 788.67 384.09 c -789.17 383.10 789.83 382.32 790.68 381.75 c -790.68 382.54 l -790.10 383.17 789.69 383.84 789.45 384.56 c -789.21 385.28 789.08 386.17 789.08 387.24 c -789.08 388.31 789.21 389.20 789.45 389.92 c -789.69 390.64 790.10 391.31 790.68 391.94 c -h -796.24 391.00 m -796.24 389.80 l -795.63 390.70 794.89 391.15 794.01 391.15 c -793.46 391.15 793.02 390.97 792.69 390.62 c -792.36 390.27 792.20 389.80 792.20 389.21 c -792.20 384.64 l -793.35 384.64 l -793.35 388.83 l -793.35 389.31 793.42 389.65 793.56 389.85 c -793.70 390.05 793.93 390.15 794.25 390.15 c -794.96 390.15 795.62 389.69 796.24 388.76 c -796.24 384.64 l -797.40 384.64 l -797.40 391.00 l -h -799.71 391.00 m -799.71 384.64 l -800.87 384.64 l -800.87 385.83 l -801.32 384.94 801.99 384.50 802.86 384.50 c -802.98 384.50 803.10 384.51 803.23 384.53 c -803.23 385.60 l -803.03 385.54 802.85 385.50 802.70 385.50 c -801.97 385.50 801.36 385.94 800.87 386.80 c -800.87 391.00 l -h -804.62 391.00 m -804.62 381.75 l -805.78 381.75 l -805.78 391.00 l -h -807.37 391.94 m -807.37 392.73 l -808.21 392.16 808.88 391.38 809.38 390.39 c -809.87 389.41 810.12 388.36 810.12 387.24 c -810.12 386.12 809.87 385.08 809.38 384.09 c -808.88 383.10 808.21 382.32 807.37 381.75 c -807.37 382.54 l -807.95 383.17 808.35 383.84 808.60 384.56 c -808.84 385.28 808.96 386.17 808.96 387.24 c -808.96 388.31 808.84 389.20 808.60 389.92 c -808.35 390.64 807.95 391.31 807.37 391.94 c -h -f -704.06 563.15 m -703.48 563.15 703.02 562.98 702.69 562.64 c -702.37 562.31 702.20 561.84 702.20 561.24 c -702.20 557.50 l -701.40 557.50 l -701.40 556.64 l -702.20 556.64 l -702.20 555.48 l -703.36 555.37 l -703.36 556.64 l -705.02 556.64 l -705.02 557.50 l -703.36 557.50 l -703.36 561.03 l -703.36 561.86 703.71 562.28 704.43 562.28 c -704.59 562.28 704.77 562.25 704.99 562.20 c -704.99 563.00 l -704.63 563.10 704.33 563.15 704.06 563.15 c -h -709.92 562.19 m -709.23 562.83 708.57 563.15 707.93 563.15 c -707.40 563.15 706.96 562.98 706.61 562.65 c -706.27 562.32 706.09 561.90 706.09 561.40 c -706.09 560.71 706.38 560.17 706.97 559.80 c -707.55 559.42 708.39 559.24 709.48 559.24 c -709.75 559.24 l -709.75 558.47 l -709.75 557.73 709.38 557.36 708.62 557.36 c -708.01 557.36 707.35 557.55 706.64 557.93 c -706.64 556.97 l -707.42 556.65 708.15 556.50 708.83 556.50 c -709.54 556.50 710.06 556.66 710.40 556.98 c -710.74 557.30 710.91 557.79 710.91 558.47 c -710.91 561.35 l -710.91 562.01 711.11 562.34 711.52 562.34 c -711.57 562.34 711.64 562.34 711.74 562.32 c -711.82 562.96 l -711.56 563.08 711.27 563.15 710.96 563.15 c -710.42 563.15 710.07 562.83 709.92 562.19 c -h -709.75 561.56 m -709.75 559.92 l -709.37 559.91 l -708.73 559.91 708.22 560.03 707.83 560.27 c -707.44 560.51 707.25 560.82 707.25 561.21 c -707.25 561.49 707.34 561.72 707.54 561.92 c -707.73 562.11 707.97 562.20 708.25 562.20 c -708.73 562.20 709.23 561.99 709.75 561.56 c -h -713.27 563.00 m -713.27 556.64 l -714.42 556.64 l -714.42 557.83 l -714.88 556.94 715.54 556.50 716.42 556.50 c -716.53 556.50 716.66 556.51 716.79 556.53 c -716.79 557.60 l -716.59 557.54 716.41 557.50 716.26 557.50 c -715.53 557.50 714.92 557.94 714.42 558.80 c -714.42 563.00 l -h -718.20 565.12 m -718.34 564.11 l -719.01 564.43 719.66 564.59 720.31 564.59 c -721.61 564.59 722.26 563.90 722.26 562.52 c -722.26 561.52 l -721.84 562.41 721.14 562.85 720.17 562.85 c -719.40 562.85 718.80 562.58 718.35 562.02 c -717.90 561.47 717.68 560.72 717.68 559.78 c -717.68 558.81 717.93 558.02 718.44 557.41 c -718.96 556.80 719.62 556.50 720.43 556.50 c -721.14 556.50 721.75 556.79 722.26 557.36 c -722.26 556.64 l -723.42 556.64 l -723.42 561.27 l -723.42 562.26 723.37 563.00 723.27 563.48 c -723.17 563.96 722.97 564.35 722.69 564.65 c -722.18 565.19 721.40 565.46 720.34 565.46 c -719.59 565.46 718.88 565.34 718.20 565.12 c -h -722.26 560.80 m -722.26 558.15 l -721.76 557.63 721.20 557.36 720.61 557.36 c -720.07 557.36 719.66 557.58 719.36 558.00 c -719.06 558.43 718.91 559.01 718.91 559.75 c -718.91 561.15 719.40 561.85 720.38 561.85 c -721.04 561.85 721.67 561.50 722.26 560.80 c -h -730.33 562.79 m -729.55 563.03 728.89 563.15 728.34 563.15 c -727.40 563.15 726.64 562.83 726.05 562.21 c -725.45 561.59 725.16 560.78 725.16 559.79 c -725.16 558.82 725.42 558.03 725.94 557.42 c -726.46 556.80 727.13 556.49 727.94 556.49 c -728.71 556.49 729.31 556.76 729.73 557.31 c -730.15 557.86 730.36 558.63 730.36 559.64 c -730.35 560.00 l -726.34 560.00 l -726.50 561.51 727.24 562.27 728.56 562.27 c -729.04 562.27 729.63 562.14 730.33 561.88 c -h -726.39 559.13 m -729.20 559.13 l -729.20 557.95 728.75 557.36 727.87 557.36 c -726.98 557.36 726.49 557.95 726.39 559.13 c -h -734.26 563.15 m -733.67 563.15 733.21 562.98 732.89 562.64 c -732.56 562.31 732.39 561.84 732.39 561.24 c -732.39 557.50 l -731.60 557.50 l -731.60 556.64 l -732.39 556.64 l -732.39 555.48 l -733.55 555.37 l -733.55 556.64 l -735.21 556.64 l -735.21 557.50 l -733.55 557.50 l -733.55 561.03 l -733.55 561.86 733.91 562.28 734.63 562.28 c -734.78 562.28 734.96 562.25 735.18 562.20 c -735.18 563.00 l -734.83 563.10 734.52 563.15 734.26 563.15 c -h -736.98 561.05 m -736.98 560.18 l -743.92 560.18 l -743.92 561.05 l -h -736.98 558.88 m -736.98 558.01 l -743.92 558.01 l -743.92 558.88 l -h -746.38 563.00 m -746.38 556.64 l -747.53 556.64 l -747.53 563.00 l -h -746.38 555.48 m -746.38 554.33 l -747.53 554.33 l -747.53 555.48 l -h -749.84 563.00 m -749.84 556.64 l -751.00 556.64 l -751.00 557.83 l -751.61 556.94 752.35 556.50 753.24 556.50 c -753.79 556.50 754.23 556.67 754.55 557.02 c -754.88 557.37 755.05 557.84 755.05 558.43 c -755.05 563.00 l -753.89 563.00 l -753.89 558.80 l -753.89 558.33 753.82 558.00 753.68 557.79 c -753.55 557.59 753.32 557.49 753.00 557.49 c -752.29 557.49 751.62 557.96 751.00 558.88 c -751.00 563.00 l -h -758.98 563.15 m -758.45 563.15 757.81 563.02 757.06 562.78 c -757.06 561.72 l -757.81 562.09 758.47 562.28 759.03 562.28 c -759.36 562.28 759.63 562.19 759.85 562.01 c -760.07 561.83 760.18 561.61 760.18 561.34 c -760.18 560.94 759.87 560.62 759.26 560.36 c -758.59 560.07 l -757.59 559.66 757.09 559.06 757.09 558.28 c -757.09 557.73 757.29 557.29 757.68 556.97 c -758.07 556.66 758.61 556.50 759.29 556.50 c -759.65 556.50 760.09 556.54 760.61 556.64 c -760.85 556.69 l -760.85 557.65 l -760.21 557.46 759.70 557.36 759.32 557.36 c -758.58 557.36 758.21 557.63 758.21 558.17 c -758.21 558.52 758.49 558.81 759.05 559.05 c -759.61 559.29 l -760.23 559.55 760.68 559.83 760.94 560.13 c -761.20 560.42 761.33 560.79 761.33 561.23 c -761.33 561.79 761.11 562.25 760.67 562.61 c -760.23 562.97 759.67 563.15 758.98 563.15 c -h -765.32 563.15 m -764.73 563.15 764.28 562.98 763.95 562.64 c -763.62 562.31 763.46 561.84 763.46 561.24 c -763.46 557.50 l -762.66 557.50 l -762.66 556.64 l -763.46 556.64 l -763.46 555.48 l -764.61 555.37 l -764.61 556.64 l -766.27 556.64 l -766.27 557.50 l -764.61 557.50 l -764.61 561.03 l -764.61 561.86 764.97 562.28 765.69 562.28 c -765.84 562.28 766.03 562.25 766.24 562.20 c -766.24 563.00 l -765.89 563.10 765.58 563.15 765.32 563.15 c -h -771.18 562.19 m -770.49 562.83 769.82 563.15 769.18 563.15 c -768.65 563.15 768.21 562.98 767.87 562.65 c -767.52 562.32 767.35 561.90 767.35 561.40 c -767.35 560.71 767.64 560.17 768.22 559.80 c -768.81 559.42 769.64 559.24 770.73 559.24 c -771.01 559.24 l -771.01 558.47 l -771.01 557.73 770.63 557.36 769.87 557.36 c -769.26 557.36 768.60 557.55 767.90 557.93 c -767.90 556.97 l -768.67 556.65 769.40 556.50 770.08 556.50 c -770.79 556.50 771.32 556.66 771.66 556.98 c -771.99 557.30 772.16 557.79 772.16 558.47 c -772.16 561.35 l -772.16 562.01 772.37 562.34 772.77 562.34 c -772.82 562.34 772.90 562.34 772.99 562.32 c -773.08 562.96 l -772.81 563.08 772.53 563.15 772.21 563.15 c -771.67 563.15 771.33 562.83 771.18 562.19 c -h -771.01 561.56 m -771.01 559.92 l -770.62 559.91 l -769.99 559.91 769.48 560.03 769.09 560.27 c -768.70 560.51 768.50 560.82 768.50 561.21 c -768.50 561.49 768.60 561.72 768.79 561.92 c -768.99 562.11 769.23 562.20 769.51 562.20 c -769.99 562.20 770.49 561.99 771.01 561.56 c -h -774.52 563.00 m -774.52 556.64 l -775.68 556.64 l -775.68 557.83 l -776.29 556.94 777.03 556.50 777.92 556.50 c -778.47 556.50 778.91 556.67 779.23 557.02 c -779.56 557.37 779.73 557.84 779.73 558.43 c -779.73 563.00 l -778.57 563.00 l -778.57 558.80 l -778.57 558.33 778.50 558.00 778.36 557.79 c -778.23 557.59 778.00 557.49 777.68 557.49 c -776.97 557.49 776.30 557.96 775.68 558.88 c -775.68 563.00 l -h -784.46 563.15 m -783.60 563.15 782.88 562.83 782.32 562.19 c -781.75 561.55 781.47 560.75 781.47 559.78 c -781.47 558.75 781.75 557.94 782.31 557.36 c -782.87 556.79 783.65 556.50 784.65 556.50 c -785.15 556.50 785.71 556.56 786.32 556.70 c -786.32 557.67 l -785.67 557.48 785.13 557.38 784.72 557.38 c -784.13 557.38 783.66 557.60 783.30 558.05 c -782.95 558.49 782.77 559.08 782.77 559.82 c -782.77 560.53 782.95 561.11 783.32 561.55 c -783.69 561.99 784.17 562.21 784.76 562.21 c -785.29 562.21 785.83 562.08 786.39 561.81 c -786.39 562.81 l -785.64 563.03 785.00 563.15 784.46 563.15 c -h -792.78 562.79 m -792.01 563.03 791.35 563.15 790.79 563.15 c -789.86 563.15 789.09 562.83 788.50 562.21 c -787.91 561.59 787.61 560.78 787.61 559.79 c -787.61 558.82 787.87 558.03 788.40 557.42 c -788.92 556.80 789.58 556.49 790.40 556.49 c -791.17 556.49 791.76 556.76 792.18 557.31 c -792.60 557.86 792.81 558.63 792.81 559.64 c -792.80 560.00 l -788.79 560.00 l -788.96 561.51 789.70 562.27 791.01 562.27 c -791.49 562.27 792.08 562.14 792.78 561.88 c -h -788.84 559.13 m -791.65 559.13 l -791.65 557.95 791.21 557.36 790.33 557.36 c -789.44 557.36 788.95 557.95 788.84 559.13 c -h -f -17.000 7.0000 m -121.00 7.0000 l -121.00 44.000 l -17.000 44.000 l -17.000 7.0000 l -h -f -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -15.000 5.0000 m -117.00 5.0000 l -117.00 40.000 l -15.000 40.000 l -15.000 5.0000 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -15.500 5.5000 m -117.50 5.5000 l -117.50 40.500 l -15.500 40.500 l -15.500 5.5000 l -h -S -29.500 24.500 m -103.50 24.500 l -S -32.639 22.146 m -31.779 22.146 31.066 21.828 30.500 21.191 c -29.934 20.555 29.650 19.752 29.650 18.783 c -29.650 17.748 29.931 16.941 30.491 16.363 c -31.052 15.785 31.834 15.496 32.838 15.496 c -33.334 15.496 33.889 15.564 34.502 15.701 c -34.502 16.668 l -33.850 16.477 33.318 16.381 32.908 16.381 c -32.318 16.381 31.845 16.603 31.487 17.046 c -31.130 17.489 30.951 18.080 30.951 18.818 c -30.951 19.533 31.135 20.111 31.502 20.553 c -31.869 20.994 32.350 21.215 32.943 21.215 c -33.471 21.215 34.014 21.080 34.572 20.811 c -34.572 21.807 l -33.826 22.033 33.182 22.146 32.639 22.146 c -h -36.301 22.000 m -36.301 12.748 l -37.455 12.748 l -37.455 22.000 l -h -39.770 22.000 m -39.770 15.637 l -40.924 15.637 l -40.924 22.000 l -h -39.770 14.482 m -39.770 13.328 l -40.924 13.328 l -40.924 14.482 l -h -47.902 21.795 m -47.129 22.029 46.467 22.146 45.916 22.146 c -44.979 22.146 44.214 21.835 43.622 21.212 c -43.030 20.589 42.734 19.781 42.734 18.789 c -42.734 17.824 42.995 17.033 43.517 16.416 c -44.038 15.799 44.705 15.490 45.518 15.490 c -46.287 15.490 46.882 15.764 47.302 16.311 c -47.722 16.857 47.932 17.635 47.932 18.643 c -47.926 19.000 l -43.912 19.000 l -44.080 20.512 44.820 21.268 46.133 21.268 c -46.613 21.268 47.203 21.139 47.902 20.881 c -h -43.965 18.133 m -46.771 18.133 l -46.771 16.949 46.330 16.357 45.447 16.357 c -44.561 16.357 44.066 16.949 43.965 18.133 c -h -49.924 22.000 m -49.924 15.637 l -51.078 15.637 l -51.078 16.832 l -51.688 15.941 52.434 15.496 53.316 15.496 c -53.867 15.496 54.307 15.671 54.635 16.021 c -54.963 16.370 55.127 16.840 55.127 17.430 c -55.127 22.000 l -53.973 22.000 l -53.973 17.805 l -53.973 17.332 53.903 16.995 53.765 16.794 c -53.626 16.593 53.396 16.492 53.076 16.492 c -52.369 16.492 51.703 16.955 51.078 17.881 c -51.078 22.000 l -h -59.281 22.146 m -58.695 22.146 58.238 21.979 57.910 21.643 c -57.582 21.307 57.418 20.840 57.418 20.242 c -57.418 16.504 l -56.621 16.504 l -56.621 15.637 l -57.418 15.637 l -57.418 14.482 l -58.572 14.371 l -58.572 15.637 l -60.236 15.637 l -60.236 16.504 l -58.572 16.504 l -58.572 20.031 l -58.572 20.863 58.932 21.279 59.650 21.279 c -59.803 21.279 59.988 21.254 60.207 21.203 c -60.207 22.000 l -59.852 22.098 59.543 22.146 59.281 22.146 c -h -62.023 22.000 m -62.023 20.846 l -63.178 20.846 l -63.178 22.000 l -h -62.023 16.797 m -62.023 15.637 l -63.178 15.637 l -63.178 16.797 l -h -65.621 22.000 m -65.621 13.328 l -66.852 13.328 l -66.852 21.080 l -70.754 21.080 l -70.754 22.000 l -h -72.055 22.000 m -72.055 15.637 l -73.209 15.637 l -73.209 22.000 l -h -72.055 14.482 m -72.055 13.328 l -73.209 13.328 l -73.209 14.482 l -h -75.523 22.070 m -75.523 12.748 l -76.678 12.748 l -76.678 16.832 l -77.150 15.941 77.859 15.496 78.805 15.496 c -79.570 15.496 80.173 15.775 80.612 16.334 c -81.052 16.893 81.271 17.656 81.271 18.625 c -81.271 19.680 81.022 20.530 80.524 21.177 c -80.026 21.823 79.371 22.146 78.559 22.146 c -77.805 22.146 77.178 21.857 76.678 21.279 c -76.537 22.070 l -h -76.678 20.482 m -77.271 21.014 77.838 21.279 78.377 21.279 c -79.486 21.279 80.041 20.434 80.041 18.742 c -80.041 17.250 79.549 16.504 78.564 16.504 c -77.920 16.504 77.291 16.854 76.678 17.553 c -h -83.076 22.000 m -83.076 15.637 l -84.230 15.637 l -84.230 16.832 l -84.688 15.941 85.352 15.496 86.223 15.496 c -86.340 15.496 86.463 15.506 86.592 15.525 c -86.592 16.604 l -86.393 16.537 86.217 16.504 86.064 16.504 c -85.334 16.504 84.723 16.938 84.230 17.805 c -84.230 22.000 l -h -91.268 21.191 m -90.576 21.828 89.910 22.146 89.270 22.146 c -88.742 22.146 88.305 21.981 87.957 21.651 c -87.609 21.321 87.436 20.904 87.436 20.400 c -87.436 19.705 87.728 19.171 88.312 18.798 c -88.896 18.425 89.732 18.238 90.822 18.238 c -91.098 18.238 l -91.098 17.471 l -91.098 16.732 90.719 16.363 89.961 16.363 c -89.352 16.363 88.693 16.551 87.986 16.926 c -87.986 15.971 l -88.764 15.654 89.492 15.496 90.172 15.496 c -90.883 15.496 91.407 15.656 91.745 15.977 c -92.083 16.297 92.252 16.795 92.252 17.471 c -92.252 20.354 l -92.252 21.014 92.455 21.344 92.861 21.344 c -92.912 21.344 92.986 21.336 93.084 21.320 c -93.166 21.959 l -92.904 22.084 92.615 22.146 92.299 22.146 c -91.760 22.146 91.416 21.828 91.268 21.191 c -h -91.098 20.564 m -91.098 18.918 l -90.711 18.906 l -90.078 18.906 89.566 19.026 89.176 19.267 c -88.785 19.507 88.590 19.822 88.590 20.213 c -88.590 20.490 88.688 20.725 88.883 20.916 c -89.078 21.107 89.316 21.203 89.598 21.203 c -90.078 21.203 90.578 20.990 91.098 20.564 c -h -94.613 22.000 m -94.613 15.637 l -95.768 15.637 l -95.768 16.832 l -96.225 15.941 96.889 15.496 97.760 15.496 c -97.877 15.496 98.000 15.506 98.129 15.525 c -98.129 16.604 l -97.930 16.537 97.754 16.504 97.602 16.504 c -96.871 16.504 96.260 16.938 95.768 17.805 c -95.768 22.000 l -h -99.893 24.314 m -100.92 22.000 l -98.463 15.637 l -99.711 15.637 l -101.53 20.430 l -103.48 15.637 l -104.57 15.637 l -101.09 24.314 l -h -f -461.00 7.0000 m -570.00 7.0000 l -570.00 44.000 l -461.00 44.000 l -461.00 7.0000 l -h -f -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -459.00 5.0000 m -566.00 5.0000 l -566.00 40.000 l -459.00 40.000 l -459.00 5.0000 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -459.50 5.5000 m -566.50 5.5000 l -566.50 40.500 l -459.50 40.500 l -459.50 5.5000 l -h -S -465.50 24.500 m -560.50 24.500 l -S -466.15 22.000 m -466.15 12.748 l -467.31 12.748 l -467.31 18.725 l -470.00 15.637 l -471.25 15.637 l -468.67 18.607 l -471.78 22.000 l -470.30 22.000 l -467.31 18.736 l -467.31 22.000 l -h -477.83 21.795 m -477.06 22.029 476.40 22.146 475.85 22.146 c -474.91 22.146 474.14 21.835 473.55 21.212 c -472.96 20.589 472.66 19.781 472.66 18.789 c -472.66 17.824 472.92 17.033 473.45 16.416 c -473.97 15.799 474.63 15.490 475.45 15.490 c -476.22 15.490 476.81 15.764 477.23 16.311 c -477.65 16.857 477.86 17.635 477.86 18.643 c -477.86 19.000 l -473.84 19.000 l -474.01 20.512 474.75 21.268 476.06 21.268 c -476.54 21.268 477.13 21.139 477.83 20.881 c -h -473.89 18.133 m -476.70 18.133 l -476.70 16.949 476.26 16.357 475.38 16.357 c -474.49 16.357 474.00 16.949 473.89 18.133 c -h -480.22 24.314 m -481.25 22.000 l -478.79 15.637 l -480.04 15.637 l -481.86 20.430 l -483.81 15.637 l -484.90 15.637 l -481.42 24.314 l -h -487.81 22.146 m -487.28 22.146 486.64 22.023 485.89 21.777 c -485.89 20.717 l -486.64 21.092 487.30 21.279 487.86 21.279 c -488.19 21.279 488.46 21.189 488.68 21.010 c -488.90 20.830 489.01 20.605 489.01 20.336 c -489.01 19.941 488.71 19.615 488.09 19.357 c -487.42 19.070 l -486.42 18.656 485.92 18.061 485.92 17.283 c -485.92 16.729 486.12 16.292 486.51 15.974 c -486.91 15.655 487.44 15.496 488.13 15.496 c -488.48 15.496 488.92 15.545 489.45 15.643 c -489.69 15.689 l -489.69 16.650 l -489.04 16.459 488.53 16.363 488.15 16.363 c -487.41 16.363 487.04 16.633 487.04 17.172 c -487.04 17.520 487.32 17.812 487.88 18.051 c -488.44 18.285 l -489.07 18.551 489.51 18.831 489.77 19.126 c -490.04 19.421 490.17 19.789 490.17 20.230 c -490.17 20.789 489.95 21.248 489.50 21.607 c -489.06 21.967 488.50 22.146 487.81 22.146 c -h -494.15 22.146 m -493.56 22.146 493.11 21.979 492.78 21.643 c -492.45 21.307 492.29 20.840 492.29 20.242 c -492.29 16.504 l -491.49 16.504 l -491.49 15.637 l -492.29 15.637 l -492.29 14.482 l -493.44 14.371 l -493.44 15.637 l -495.11 15.637 l -495.11 16.504 l -493.44 16.504 l -493.44 20.031 l -493.44 20.863 493.80 21.279 494.52 21.279 c -494.67 21.279 494.86 21.254 495.08 21.203 c -495.08 22.000 l -494.72 22.098 494.41 22.146 494.15 22.146 c -h -499.22 22.146 m -498.31 22.146 497.58 21.845 497.04 21.241 c -496.50 20.638 496.22 19.830 496.22 18.818 c -496.22 17.795 496.50 16.985 497.04 16.390 c -497.59 15.794 498.33 15.496 499.26 15.496 c -500.19 15.496 500.93 15.794 501.48 16.390 c -502.02 16.985 502.29 17.791 502.29 18.807 c -502.29 19.846 502.02 20.662 501.47 21.256 c -500.93 21.850 500.18 22.146 499.22 22.146 c -h -499.24 21.279 m -500.46 21.279 501.07 20.455 501.07 18.807 c -501.07 17.178 500.47 16.363 499.26 16.363 c -498.06 16.363 497.46 17.182 497.46 18.818 c -497.46 20.459 498.05 21.279 499.24 21.279 c -h -504.10 22.000 m -504.10 15.637 l -505.25 15.637 l -505.25 16.832 l -505.86 15.941 506.61 15.496 507.49 15.496 c -508.04 15.496 508.48 15.671 508.81 16.021 c -509.14 16.370 509.30 16.840 509.30 17.430 c -509.30 22.000 l -508.15 22.000 l -508.15 17.805 l -508.15 17.332 508.08 16.995 507.94 16.794 c -507.80 16.593 507.57 16.492 507.25 16.492 c -506.54 16.492 505.88 16.955 505.25 17.881 c -505.25 22.000 l -h -516.21 21.795 m -515.44 22.029 514.78 22.146 514.22 22.146 c -513.29 22.146 512.52 21.835 511.93 21.212 c -511.34 20.589 511.04 19.781 511.04 18.789 c -511.04 17.824 511.30 17.033 511.83 16.416 c -512.35 15.799 513.01 15.490 513.83 15.490 c -514.60 15.490 515.19 15.764 515.61 16.311 c -516.03 16.857 516.24 17.635 516.24 18.643 c -516.23 19.000 l -512.22 19.000 l -512.39 20.512 513.13 21.268 514.44 21.268 c -514.92 21.268 515.51 21.139 516.21 20.881 c -h -512.27 18.133 m -515.08 18.133 l -515.08 16.949 514.64 16.357 513.76 16.357 c -512.87 16.357 512.38 16.949 512.27 18.133 c -h -518.40 22.000 m -518.40 20.846 l -519.55 20.846 l -519.55 22.000 l -h -518.40 16.797 m -518.40 15.637 l -519.55 15.637 l -519.55 16.797 l -h -523.69 22.217 m -523.11 22.217 522.37 22.090 521.46 21.836 c -521.46 20.617 l -522.44 21.070 523.24 21.297 523.87 21.297 c -524.35 21.297 524.74 21.170 525.04 20.916 c -525.33 20.662 525.48 20.328 525.48 19.914 c -525.48 19.574 525.38 19.285 525.19 19.047 c -525.00 18.809 524.64 18.543 524.12 18.250 c -523.52 17.904 l -522.79 17.482 522.26 17.085 521.96 16.712 c -521.66 16.339 521.51 15.904 521.51 15.408 c -521.51 14.740 521.75 14.190 522.23 13.759 c -522.72 13.327 523.34 13.111 524.09 13.111 c -524.75 13.111 525.46 13.223 526.20 13.445 c -526.20 14.570 l -525.29 14.211 524.61 14.031 524.16 14.031 c -523.73 14.031 523.38 14.145 523.10 14.371 c -522.82 14.598 522.69 14.883 522.69 15.227 c -522.69 15.516 522.79 15.771 522.99 15.994 c -523.19 16.217 523.56 16.482 524.10 16.791 c -524.72 17.143 l -525.47 17.568 526.00 17.971 526.29 18.350 c -526.59 18.729 526.74 19.184 526.74 19.715 c -526.74 20.469 526.46 21.074 525.91 21.531 c -525.35 21.988 524.61 22.217 523.69 22.217 c -h -533.16 21.795 m -532.38 22.029 531.72 22.146 531.17 22.146 c -530.23 22.146 529.47 21.835 528.88 21.212 c -528.28 20.589 527.99 19.781 527.99 18.789 c -527.99 17.824 528.25 17.033 528.77 16.416 c -529.29 15.799 529.96 15.490 530.77 15.490 c -531.54 15.490 532.14 15.764 532.56 16.311 c -532.98 16.857 533.19 17.635 533.19 18.643 c -533.18 19.000 l -529.17 19.000 l -529.33 20.512 530.07 21.268 531.39 21.268 c -531.87 21.268 532.46 21.139 533.16 20.881 c -h -529.22 18.133 m -532.03 18.133 l -532.03 16.949 531.58 16.357 530.70 16.357 c -529.81 16.357 529.32 16.949 529.22 18.133 c -h -535.18 22.000 m -535.18 15.637 l -536.33 15.637 l -536.33 16.832 l -536.79 15.941 537.45 15.496 538.32 15.496 c -538.44 15.496 538.56 15.506 538.69 15.525 c -538.69 16.604 l -538.49 16.537 538.32 16.504 538.17 16.504 c -537.44 16.504 536.82 16.938 536.33 17.805 c -536.33 22.000 l -h -541.41 22.000 m -539.04 15.637 l -540.19 15.637 l -542.04 20.588 l -544.00 15.637 l -545.07 15.637 l -542.56 22.000 l -h -546.30 22.000 m -546.30 15.637 l -547.45 15.637 l -547.45 22.000 l -h -546.30 14.482 m -546.30 13.328 l -547.45 13.328 l -547.45 14.482 l -h -552.25 22.146 m -551.39 22.146 550.68 21.828 550.11 21.191 c -549.55 20.555 549.26 19.752 549.26 18.783 c -549.26 17.748 549.54 16.941 550.10 16.363 c -550.67 15.785 551.45 15.496 552.45 15.496 c -552.95 15.496 553.50 15.564 554.12 15.701 c -554.12 16.668 l -553.46 16.477 552.93 16.381 552.52 16.381 c -551.93 16.381 551.46 16.603 551.10 17.046 c -550.74 17.489 550.56 18.080 550.56 18.818 c -550.56 19.533 550.75 20.111 551.12 20.553 c -551.48 20.994 551.96 21.215 552.56 21.215 c -553.08 21.215 553.63 21.080 554.19 20.811 c -554.19 21.807 l -553.44 22.033 552.79 22.146 552.25 22.146 c -h -560.58 21.795 m -559.80 22.029 559.14 22.146 558.59 22.146 c -557.65 22.146 556.89 21.835 556.30 21.212 c -555.71 20.589 555.41 19.781 555.41 18.789 c -555.41 17.824 555.67 17.033 556.19 16.416 c -556.71 15.799 557.38 15.490 558.19 15.490 c -558.96 15.490 559.56 15.764 559.98 16.311 c -560.40 16.857 560.61 17.635 560.61 18.643 c -560.60 19.000 l -556.59 19.000 l -556.76 20.512 557.50 21.268 558.81 21.268 c -559.29 21.268 559.88 21.139 560.58 20.881 c -h -556.64 18.133 m -559.45 18.133 l -559.45 16.949 559.01 16.357 558.12 16.357 c -557.24 16.357 556.74 16.949 556.64 18.133 c -h -f -640.00 7.0000 m -748.00 7.0000 l -748.00 44.000 l -640.00 44.000 l -640.00 7.0000 l -h -f -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -638.00 5.0000 m -744.00 5.0000 l -744.00 40.000 l -638.00 40.000 l -638.00 5.0000 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -638.50 5.5000 m -744.50 5.5000 l -744.50 40.500 l -638.50 40.500 l -638.50 5.5000 l -h -S -644.50 24.500 m -738.50 24.500 l -S -647.64 22.146 m -646.78 22.146 646.07 21.828 645.50 21.191 c -644.93 20.555 644.65 19.752 644.65 18.783 c -644.65 17.748 644.93 16.941 645.49 16.363 c -646.05 15.785 646.83 15.496 647.84 15.496 c -648.33 15.496 648.89 15.564 649.50 15.701 c -649.50 16.668 l -648.85 16.477 648.32 16.381 647.91 16.381 c -647.32 16.381 646.84 16.603 646.49 17.046 c -646.13 17.489 645.95 18.080 645.95 18.818 c -645.95 19.533 646.13 20.111 646.50 20.553 c -646.87 20.994 647.35 21.215 647.94 21.215 c -648.47 21.215 649.01 21.080 649.57 20.811 c -649.57 21.807 l -648.83 22.033 648.18 22.146 647.64 22.146 c -h -653.79 22.146 m -652.88 22.146 652.15 21.845 651.61 21.241 c -651.07 20.638 650.80 19.830 650.80 18.818 c -650.80 17.795 651.07 16.985 651.61 16.390 c -652.16 15.794 652.90 15.496 653.83 15.496 c -654.77 15.496 655.50 15.794 656.05 16.390 c -656.59 16.985 656.87 17.791 656.87 18.807 c -656.87 19.846 656.59 20.662 656.05 21.256 c -655.50 21.850 654.75 22.146 653.79 22.146 c -h -653.81 21.279 m -655.03 21.279 655.64 20.455 655.64 18.807 c -655.64 17.178 655.04 16.363 653.83 16.363 c -652.63 16.363 652.03 17.182 652.03 18.818 c -652.03 20.459 652.62 21.279 653.81 21.279 c -h -658.67 22.000 m -658.67 15.637 l -659.83 15.637 l -659.83 16.832 l -660.39 15.941 661.11 15.496 661.99 15.496 c -662.85 15.496 663.43 15.941 663.73 16.832 c -664.28 15.938 664.99 15.490 665.87 15.490 c -666.43 15.490 666.87 15.655 667.17 15.985 c -667.48 16.315 667.64 16.777 667.64 17.371 c -667.64 22.000 l -666.48 22.000 l -666.48 17.553 l -666.48 16.826 666.19 16.463 665.62 16.463 c -665.02 16.463 664.39 16.887 663.73 17.734 c -663.73 22.000 l -662.57 22.000 l -662.57 17.553 l -662.57 16.822 662.28 16.457 661.70 16.457 c -661.11 16.457 660.49 16.883 659.83 17.734 c -659.83 22.000 l -h -669.88 24.314 m -669.88 15.637 l -671.03 15.637 l -671.03 16.832 l -671.50 15.941 672.21 15.496 673.16 15.496 c -673.92 15.496 674.52 15.775 674.96 16.334 c -675.40 16.893 675.62 17.656 675.62 18.625 c -675.62 19.680 675.37 20.530 674.88 21.177 c -674.38 21.823 673.72 22.146 672.91 22.146 c -672.16 22.146 671.53 21.857 671.03 21.279 c -671.03 24.314 l -h -671.03 20.482 m -671.62 21.014 672.19 21.279 672.73 21.279 c -673.84 21.279 674.39 20.434 674.39 18.742 c -674.39 17.250 673.90 16.504 672.92 16.504 c -672.27 16.504 671.64 16.854 671.03 17.553 c -h -681.41 22.000 m -681.41 20.805 l -680.79 21.699 680.05 22.146 679.17 22.146 c -678.62 22.146 678.18 21.972 677.85 21.622 c -677.52 21.272 677.36 20.801 677.36 20.207 c -677.36 15.637 l -678.51 15.637 l -678.51 19.832 l -678.51 20.309 678.58 20.647 678.72 20.849 c -678.86 21.050 679.09 21.150 679.41 21.150 c -680.12 21.150 680.78 20.688 681.41 19.762 c -681.41 15.637 l -682.56 15.637 l -682.56 22.000 l -h -686.79 22.146 m -686.20 22.146 685.74 21.979 685.41 21.643 c -685.09 21.307 684.92 20.840 684.92 20.242 c -684.92 16.504 l -684.12 16.504 l -684.12 15.637 l -684.92 15.637 l -684.92 14.482 l -686.08 14.371 l -686.08 15.637 l -687.74 15.637 l -687.74 16.504 l -686.08 16.504 l -686.08 20.031 l -686.08 20.863 686.44 21.279 687.15 21.279 c -687.31 21.279 687.49 21.254 687.71 21.203 c -687.71 22.000 l -687.36 22.098 687.05 22.146 686.79 22.146 c -h -694.03 21.795 m -693.25 22.029 692.59 22.146 692.04 22.146 c -691.10 22.146 690.34 21.835 689.75 21.212 c -689.16 20.589 688.86 19.781 688.86 18.789 c -688.86 17.824 689.12 17.033 689.64 16.416 c -690.16 15.799 690.83 15.490 691.64 15.490 c -692.41 15.490 693.01 15.764 693.43 16.311 c -693.85 16.857 694.06 17.635 694.06 18.643 c -694.05 19.000 l -690.04 19.000 l -690.21 20.512 690.95 21.268 692.26 21.268 c -692.74 21.268 693.33 21.139 694.03 20.881 c -h -690.09 18.133 m -692.90 18.133 l -692.90 16.949 692.46 16.357 691.57 16.357 c -690.69 16.357 690.19 16.949 690.09 18.133 c -h -696.21 22.000 m -696.21 20.846 l -697.37 20.846 l -697.37 22.000 l -h -696.21 16.797 m -696.21 15.637 l -697.37 15.637 l -697.37 16.797 l -h -701.51 22.217 m -700.93 22.217 700.18 22.090 699.28 21.836 c -699.28 20.617 l -700.25 21.070 701.06 21.297 701.69 21.297 c -702.17 21.297 702.56 21.170 702.85 20.916 c -703.15 20.662 703.30 20.328 703.30 19.914 c -703.30 19.574 703.20 19.285 703.01 19.047 c -702.81 18.809 702.46 18.543 701.94 18.250 c -701.34 17.904 l -700.60 17.482 700.08 17.085 699.78 16.712 c -699.48 16.339 699.32 15.904 699.32 15.408 c -699.32 14.740 699.57 14.190 700.05 13.759 c -700.54 13.327 701.15 13.111 701.90 13.111 c -702.57 13.111 703.28 13.223 704.02 13.445 c -704.02 14.570 l -703.10 14.211 702.42 14.031 701.97 14.031 c -701.55 14.031 701.20 14.145 700.92 14.371 c -700.64 14.598 700.50 14.883 700.50 15.227 c -700.50 15.516 700.60 15.771 700.81 15.994 c -701.01 16.217 701.38 16.482 701.92 16.791 c -702.54 17.143 l -703.29 17.568 703.81 17.971 704.11 18.350 c -704.41 18.729 704.56 19.184 704.56 19.715 c -704.56 20.469 704.28 21.074 703.72 21.531 c -703.17 21.988 702.43 22.217 701.51 22.217 c -h -710.97 21.795 m -710.20 22.029 709.54 22.146 708.99 22.146 c -708.05 22.146 707.28 21.835 706.69 21.212 c -706.10 20.589 705.80 19.781 705.80 18.789 c -705.80 17.824 706.07 17.033 706.59 16.416 c -707.11 15.799 707.78 15.490 708.59 15.490 c -709.36 15.490 709.95 15.764 710.37 16.311 c -710.79 16.857 711.00 17.635 711.00 18.643 c -711.00 19.000 l -706.98 19.000 l -707.15 20.512 707.89 21.268 709.20 21.268 c -709.68 21.268 710.27 21.139 710.97 20.881 c -h -707.04 18.133 m -709.84 18.133 l -709.84 16.949 709.40 16.357 708.52 16.357 c -707.63 16.357 707.14 16.949 707.04 18.133 c -h -712.99 22.000 m -712.99 15.637 l -714.15 15.637 l -714.15 16.832 l -714.61 15.941 715.27 15.496 716.14 15.496 c -716.26 15.496 716.38 15.506 716.51 15.525 c -716.51 16.604 l -716.31 16.537 716.13 16.504 715.98 16.504 c -715.25 16.504 714.64 16.938 714.15 17.805 c -714.15 22.000 l -h -719.22 22.000 m -716.86 15.637 l -718.01 15.637 l -719.86 20.588 l -721.81 15.637 l -722.89 15.637 l -720.38 22.000 l -h -724.12 22.000 m -724.12 15.637 l -725.27 15.637 l -725.27 22.000 l -h -724.12 14.482 m -724.12 13.328 l -725.27 13.328 l -725.27 14.482 l -h -730.07 22.146 m -729.21 22.146 728.50 21.828 727.93 21.191 c -727.36 20.555 727.08 19.752 727.08 18.783 c -727.08 17.748 727.36 16.941 727.92 16.363 c -728.48 15.785 729.26 15.496 730.27 15.496 c -730.76 15.496 731.32 15.564 731.93 15.701 c -731.93 16.668 l -731.28 16.477 730.75 16.381 730.34 16.381 c -729.75 16.381 729.27 16.603 728.92 17.046 c -728.56 17.489 728.38 18.080 728.38 18.818 c -728.38 19.533 728.56 20.111 728.93 20.553 c -729.30 20.994 729.78 21.215 730.37 21.215 c -730.90 21.215 731.44 21.080 732.00 20.811 c -732.00 21.807 l -731.26 22.033 730.61 22.146 730.07 22.146 c -h -738.39 21.795 m -737.62 22.029 736.96 22.146 736.41 22.146 c -735.47 22.146 734.71 21.835 734.11 21.212 c -733.52 20.589 733.23 19.781 733.23 18.789 c -733.23 17.824 733.49 17.033 734.01 16.416 c -734.53 15.799 735.20 15.490 736.01 15.490 c -736.78 15.490 737.37 15.764 737.79 16.311 c -738.21 16.857 738.42 17.635 738.42 18.643 c -738.42 19.000 l -734.40 19.000 l -734.57 20.512 735.31 21.268 736.62 21.268 c -737.11 21.268 737.70 21.139 738.39 20.881 c -h -734.46 18.133 m -737.26 18.133 l -737.26 16.949 736.82 16.357 735.94 16.357 c -735.05 16.357 734.56 16.949 734.46 18.133 c -h -f -892.00 7.0000 m -996.00 7.0000 l -996.00 44.000 l -892.00 44.000 l -892.00 7.0000 l -h -f -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -890.00 5.0000 m -992.00 5.0000 l -992.00 40.000 l -890.00 40.000 l -890.00 5.0000 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -890.50 5.5000 m -992.50 5.5000 l -992.50 40.500 l -890.50 40.500 l -890.50 5.5000 l -h -S -903.50 24.500 m -980.50 24.500 l -S -904.15 24.314 m -904.15 15.637 l -905.31 15.637 l -905.31 16.832 l -905.78 15.941 906.49 15.496 907.44 15.496 c -908.20 15.496 908.80 15.775 909.24 16.334 c -909.68 16.893 909.90 17.656 909.90 18.625 c -909.90 19.680 909.65 20.530 909.16 21.177 c -908.66 21.823 908.00 22.146 907.19 22.146 c -906.44 22.146 905.81 21.857 905.31 21.279 c -905.31 24.314 l -h -905.31 20.482 m -905.90 21.014 906.47 21.279 907.01 21.279 c -908.12 21.279 908.67 20.434 908.67 18.742 c -908.67 17.250 908.18 16.504 907.20 16.504 c -906.55 16.504 905.92 16.854 905.31 17.553 c -h -914.20 22.146 m -913.29 22.146 912.56 21.845 912.02 21.241 c -911.47 20.638 911.20 19.830 911.20 18.818 c -911.20 17.795 911.48 16.985 912.02 16.390 c -912.57 15.794 913.30 15.496 914.24 15.496 c -915.17 15.496 915.91 15.794 916.46 16.390 c -917.00 16.985 917.27 17.791 917.27 18.807 c -917.27 19.846 917.00 20.662 916.45 21.256 c -915.91 21.850 915.15 22.146 914.20 22.146 c -h -914.21 21.279 m -915.44 21.279 916.05 20.455 916.05 18.807 c -916.05 17.178 915.45 16.363 914.24 16.363 c -913.04 16.363 912.43 17.182 912.43 18.818 c -912.43 20.459 913.03 21.279 914.21 21.279 c -h -919.08 22.000 m -919.08 12.748 l -920.23 12.748 l -920.23 22.000 l -h -922.55 22.000 m -922.55 15.637 l -923.70 15.637 l -923.70 22.000 l -h -922.55 14.482 m -922.55 13.328 l -923.70 13.328 l -923.70 14.482 l -h -928.50 22.146 m -927.64 22.146 926.93 21.828 926.36 21.191 c -925.79 20.555 925.51 19.752 925.51 18.783 c -925.51 17.748 925.79 16.941 926.35 16.363 c -926.91 15.785 927.70 15.496 928.70 15.496 c -929.20 15.496 929.75 15.564 930.36 15.701 c -930.36 16.668 l -929.71 16.477 929.18 16.381 928.77 16.381 c -928.18 16.381 927.71 16.603 927.35 17.046 c -926.99 17.489 926.81 18.080 926.81 18.818 c -926.81 19.533 927.00 20.111 927.36 20.553 c -927.73 20.994 928.21 21.215 928.80 21.215 c -929.33 21.215 929.88 21.080 930.43 20.811 c -930.43 21.807 l -929.69 22.033 929.04 22.146 928.50 22.146 c -h -932.53 24.314 m -933.56 22.000 l -931.10 15.637 l -932.35 15.637 l -934.17 20.430 l -936.12 15.637 l -937.21 15.637 l -933.73 24.314 l -h -938.60 22.000 m -938.60 20.846 l -939.75 20.846 l -939.75 22.000 l -h -938.60 16.797 m -938.60 15.637 l -939.75 15.637 l -939.75 16.797 l -h -943.89 22.217 m -943.31 22.217 942.57 22.090 941.66 21.836 c -941.66 20.617 l -942.64 21.070 943.44 21.297 944.07 21.297 c -944.55 21.297 944.94 21.170 945.24 20.916 c -945.53 20.662 945.68 20.328 945.68 19.914 c -945.68 19.574 945.58 19.285 945.39 19.047 c -945.20 18.809 944.84 18.543 944.32 18.250 c -943.72 17.904 l -942.98 17.482 942.46 17.085 942.16 16.712 c -941.86 16.339 941.71 15.904 941.71 15.408 c -941.71 14.740 941.95 14.190 942.43 13.759 c -942.92 13.327 943.54 13.111 944.29 13.111 c -944.95 13.111 945.66 13.223 946.40 13.445 c -946.40 14.570 l -945.49 14.211 944.80 14.031 944.36 14.031 c -943.93 14.031 943.58 14.145 943.30 14.371 c -943.02 14.598 942.88 14.883 942.88 15.227 c -942.88 15.516 942.99 15.771 943.19 15.994 c -943.39 16.217 943.76 16.482 944.30 16.791 c -944.92 17.143 l -945.67 17.568 946.20 17.971 946.49 18.350 c -946.79 18.729 946.94 19.184 946.94 19.715 c -946.94 20.469 946.66 21.074 946.10 21.531 c -945.55 21.988 944.81 22.217 943.89 22.217 c -h -953.36 21.795 m -952.58 22.029 951.92 22.146 951.37 22.146 c -950.43 22.146 949.67 21.835 949.08 21.212 c -948.48 20.589 948.19 19.781 948.19 18.789 c -948.19 17.824 948.45 17.033 948.97 16.416 c -949.49 15.799 950.16 15.490 950.97 15.490 c -951.74 15.490 952.33 15.764 952.75 16.311 c -953.17 16.857 953.38 17.635 953.38 18.643 c -953.38 19.000 l -949.37 19.000 l -949.53 20.512 950.27 21.268 951.59 21.268 c -952.07 21.268 952.66 21.139 953.36 20.881 c -h -949.42 18.133 m -952.22 18.133 l -952.22 16.949 951.78 16.357 950.90 16.357 c -950.01 16.357 949.52 16.949 949.42 18.133 c -h -955.38 22.000 m -955.38 15.637 l -956.53 15.637 l -956.53 16.832 l -956.99 15.941 957.65 15.496 958.52 15.496 c -958.64 15.496 958.76 15.506 958.89 15.525 c -958.89 16.604 l -958.69 16.537 958.52 16.504 958.37 16.504 c -957.63 16.504 957.02 16.938 956.53 17.805 c -956.53 22.000 l -h -961.61 22.000 m -959.24 15.637 l -960.39 15.637 l -962.24 20.588 l -964.20 15.637 l -965.27 15.637 l -962.76 22.000 l -h -966.50 22.000 m -966.50 15.637 l -967.65 15.637 l -967.65 22.000 l -h -966.50 14.482 m -966.50 13.328 l -967.65 13.328 l -967.65 14.482 l -h -972.45 22.146 m -971.59 22.146 970.88 21.828 970.31 21.191 c -969.75 20.555 969.46 19.752 969.46 18.783 c -969.46 17.748 969.74 16.941 970.30 16.363 c -970.86 15.785 971.65 15.496 972.65 15.496 c -973.15 15.496 973.70 15.564 974.31 15.701 c -974.31 16.668 l -973.66 16.477 973.13 16.381 972.72 16.381 c -972.13 16.381 971.66 16.603 971.30 17.046 c -970.94 17.489 970.76 18.080 970.76 18.818 c -970.76 19.533 970.95 20.111 971.31 20.553 c -971.68 20.994 972.16 21.215 972.76 21.215 c -973.28 21.215 973.83 21.080 974.38 20.811 c -974.38 21.807 l -973.64 22.033 972.99 22.146 972.45 22.146 c -h -980.78 21.795 m -980.00 22.029 979.34 22.146 978.79 22.146 c -977.85 22.146 977.09 21.835 976.50 21.212 c -975.91 20.589 975.61 19.781 975.61 18.789 c -975.61 17.824 975.87 17.033 976.39 16.416 c -976.91 15.799 977.58 15.490 978.39 15.490 c -979.16 15.490 979.76 15.764 980.18 16.311 c -980.60 16.857 980.81 17.635 980.81 18.643 c -980.80 19.000 l -976.79 19.000 l -976.96 20.512 977.70 21.268 979.01 21.268 c -979.49 21.268 980.08 21.139 980.78 20.881 c -h -976.84 18.133 m -979.65 18.133 l -979.65 16.949 979.21 16.357 978.32 16.357 c -977.44 16.357 976.94 16.949 976.84 18.133 c -h -f -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -60.000 62.000 m -181.00 62.000 l -181.00 79.000 l -60.000 79.000 l -60.000 62.000 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -2.0000 w -60.500 62.500 m -534.50 62.500 l -534.50 130.50 l -60.500 130.50 l -60.500 62.500 l -h -S -60.500 79.500 m -180.50 79.500 l -S -180.50 79.500 m -182.50 75.500 l -S -182.50 75.500 m -182.50 62.500 l -S -68.561 76.000 m -68.561 74.686 l -68.078 75.668 67.319 76.159 66.282 76.159 c -65.444 76.159 64.786 75.852 64.308 75.238 c -63.829 74.625 63.590 73.780 63.590 72.706 c -63.590 71.538 63.860 70.607 64.400 69.913 c -64.939 69.219 65.664 68.872 66.574 68.872 c -67.302 68.872 67.964 69.159 68.561 69.735 c -68.561 65.977 l -70.446 65.977 l -70.446 76.000 l -h -68.561 70.852 m -68.108 70.344 67.623 70.090 67.107 70.090 c -66.646 70.090 66.278 70.312 66.002 70.757 c -65.727 71.201 65.590 71.798 65.590 72.547 c -65.590 73.973 66.047 74.686 66.961 74.686 c -67.520 74.686 68.053 74.328 68.561 73.613 c -h -78.463 75.765 m -77.570 76.028 76.724 76.159 75.924 76.159 c -74.760 76.159 73.842 75.829 73.169 75.168 c -72.496 74.508 72.160 73.607 72.160 72.464 c -72.160 71.385 72.468 70.517 73.083 69.859 c -73.699 69.201 74.513 68.872 75.524 68.872 c -76.544 68.872 77.289 69.193 77.758 69.836 c -78.228 70.480 78.463 71.497 78.463 72.890 c -74.140 72.890 l -74.267 74.218 74.997 74.883 76.330 74.883 c -76.961 74.883 77.672 74.737 78.463 74.445 c -h -74.115 71.830 m -76.616 71.830 l -76.616 70.640 76.233 70.046 75.467 70.046 c -74.688 70.046 74.237 70.640 74.115 71.830 c -h -80.602 76.000 m -80.602 70.205 l -79.644 70.205 l -79.644 69.030 l -80.602 69.030 l -80.602 68.529 l -80.602 66.722 81.459 65.818 83.173 65.818 c -83.727 65.818 84.299 65.899 84.887 66.060 c -84.887 67.329 l -84.354 67.105 83.875 66.993 83.452 66.993 c -82.805 66.993 82.481 67.481 82.481 68.459 c -82.481 69.030 l -84.246 69.030 l -84.246 70.205 l -82.481 70.205 l -82.481 76.000 l -h -89.209 75.251 m -88.583 75.856 87.912 76.159 87.197 76.159 c -86.588 76.159 86.093 75.972 85.712 75.600 c -85.331 75.228 85.141 74.745 85.141 74.153 c -85.141 73.383 85.448 72.789 86.064 72.372 c -86.680 71.955 87.561 71.747 88.708 71.747 c -89.209 71.747 l -89.209 71.112 l -89.209 70.389 88.797 70.027 87.972 70.027 c -87.240 70.027 86.499 70.234 85.750 70.649 c -85.750 69.354 l -86.601 69.032 87.443 68.872 88.276 68.872 c -90.100 68.872 91.012 69.597 91.012 71.049 c -91.012 74.134 l -91.012 74.680 91.188 74.953 91.539 74.953 c -91.603 74.953 91.685 74.944 91.787 74.927 c -91.831 75.981 l -91.433 76.099 91.082 76.159 90.777 76.159 c -90.007 76.159 89.512 75.856 89.292 75.251 c -h -89.209 74.242 m -89.209 72.826 l -88.765 72.826 l -87.551 72.826 86.943 73.207 86.943 73.969 c -86.943 74.227 87.031 74.444 87.207 74.619 c -87.382 74.795 87.599 74.883 87.857 74.883 c -88.298 74.883 88.748 74.669 89.209 74.242 c -h -97.747 76.000 m -97.747 74.686 l -97.138 75.668 96.346 76.159 95.373 76.159 c -94.751 76.159 94.260 75.962 93.900 75.568 c -93.541 75.175 93.361 74.637 93.361 73.956 c -93.361 69.030 l -95.240 69.030 l -95.240 73.493 l -95.240 74.284 95.504 74.680 96.033 74.680 c -96.626 74.680 97.197 74.259 97.747 73.417 c -97.747 69.030 l -99.626 69.030 l -99.626 76.000 l -h -101.97 76.000 m -101.97 65.977 l -103.85 65.977 l -103.85 76.000 l -h -109.79 75.962 m -109.35 76.093 108.99 76.159 108.73 76.159 c -107.11 76.159 106.29 75.397 106.29 73.874 c -106.29 70.205 l -105.51 70.205 l -105.51 69.030 l -106.29 69.030 l -106.29 67.856 l -108.17 67.640 l -108.17 69.030 l -109.66 69.030 l -109.66 70.205 l -108.17 70.205 l -108.17 73.626 l -108.17 74.481 108.52 74.908 109.22 74.908 c -109.38 74.908 109.57 74.879 109.79 74.819 c -h -110.30 78.272 m -110.30 77.250 l -116.80 77.250 l -116.80 78.272 l -h -121.39 75.251 m -120.76 75.856 120.09 76.159 119.37 76.159 c -118.76 76.159 118.27 75.972 117.89 75.600 c -117.51 75.228 117.32 74.745 117.32 74.153 c -117.32 73.383 117.62 72.789 118.24 72.372 c -118.86 71.955 119.74 71.747 120.88 71.747 c -121.39 71.747 l -121.39 71.112 l -121.39 70.389 120.97 70.027 120.15 70.027 c -119.42 70.027 118.68 70.234 117.93 70.649 c -117.93 69.354 l -118.78 69.032 119.62 68.872 120.45 68.872 c -122.28 68.872 123.19 69.597 123.19 71.049 c -123.19 74.134 l -123.19 74.680 123.36 74.953 123.72 74.953 c -123.78 74.953 123.86 74.944 123.96 74.927 c -124.01 75.981 l -123.61 76.099 123.26 76.159 122.95 76.159 c -122.18 76.159 121.69 75.856 121.47 75.251 c -h -121.39 74.242 m -121.39 72.826 l -120.94 72.826 l -119.73 72.826 119.12 73.207 119.12 73.969 c -119.12 74.227 119.21 74.444 119.38 74.619 c -119.56 74.795 119.78 74.883 120.03 74.883 c -120.47 74.883 120.92 74.669 121.39 74.242 c -h -129.92 76.000 m -129.92 74.686 l -129.31 75.668 128.52 76.159 127.55 76.159 c -126.93 76.159 126.44 75.962 126.08 75.568 c -125.72 75.175 125.54 74.637 125.54 73.956 c -125.54 69.030 l -127.42 69.030 l -127.42 73.493 l -127.42 74.284 127.68 74.680 128.21 74.680 c -128.80 74.680 129.37 74.259 129.92 73.417 c -129.92 69.030 l -131.80 69.030 l -131.80 76.000 l -h -137.74 75.962 m -137.30 76.093 136.94 76.159 136.68 76.159 c -135.05 76.159 134.24 75.397 134.24 73.874 c -134.24 70.205 l -133.46 70.205 l -133.46 69.030 l -134.24 69.030 l -134.24 67.856 l -136.12 67.640 l -136.12 69.030 l -137.61 69.030 l -137.61 70.205 l -136.12 70.205 l -136.12 73.626 l -136.12 74.481 136.47 74.908 137.17 74.908 c -137.33 74.908 137.52 74.879 137.74 74.819 c -h -139.42 76.000 m -139.42 65.977 l -141.30 65.977 l -141.30 70.344 l -141.91 69.362 142.70 68.872 143.67 68.872 c -144.29 68.872 144.79 69.068 145.15 69.462 c -145.50 69.855 145.68 70.393 145.68 71.074 c -145.68 76.000 l -143.81 76.000 l -143.81 71.538 l -143.81 70.746 143.54 70.351 143.02 70.351 c -142.42 70.351 141.85 70.772 141.30 71.614 c -141.30 76.000 l -h -146.78 78.272 m -146.78 77.250 l -153.28 77.250 l -153.28 78.272 l -h -158.05 75.962 m -157.60 76.093 157.25 76.159 156.99 76.159 c -155.36 76.159 154.55 75.397 154.55 73.874 c -154.55 70.205 l -153.77 70.205 l -153.77 69.030 l -154.55 69.030 l -154.55 67.856 l -156.42 67.640 l -156.42 69.030 l -157.92 69.030 l -157.92 70.205 l -156.42 70.205 l -156.42 73.626 l -156.42 74.481 156.77 74.908 157.47 74.908 c -157.63 74.908 157.83 74.879 158.05 74.819 c -h -162.65 76.159 m -161.56 76.159 160.70 75.830 160.06 75.172 c -159.42 74.514 159.10 73.628 159.10 72.515 c -159.10 71.389 159.42 70.501 160.07 69.849 c -160.71 69.197 161.59 68.872 162.70 68.872 c -163.81 68.872 164.69 69.197 165.33 69.849 c -165.98 70.501 166.30 71.385 166.30 72.502 c -166.30 73.645 165.98 74.540 165.33 75.188 c -164.68 75.835 163.79 76.159 162.65 76.159 c -h -162.68 74.984 m -163.76 74.984 164.30 74.157 164.30 72.502 c -164.30 71.745 164.16 71.146 163.87 70.706 c -163.59 70.266 163.20 70.046 162.70 70.046 c -162.20 70.046 161.81 70.266 161.53 70.706 c -161.24 71.146 161.10 71.749 161.10 72.515 c -161.10 73.273 161.24 73.874 161.52 74.318 c -161.81 74.762 162.19 74.984 162.68 74.984 c -h -168.03 76.000 m -168.03 65.977 l -169.91 65.977 l -169.91 72.280 l -170.03 72.280 l -172.51 69.030 l -174.07 69.030 l -171.78 72.014 l -174.79 76.000 l -172.50 76.000 l -170.03 72.515 l -169.91 72.515 l -169.91 76.000 l -h -181.93 75.765 m -181.04 76.028 180.19 76.159 179.39 76.159 c -178.23 76.159 177.31 75.829 176.64 75.168 c -175.96 74.508 175.63 73.607 175.63 72.464 c -175.63 71.385 175.93 70.517 176.55 69.859 c -177.17 69.201 177.98 68.872 178.99 68.872 c -180.01 68.872 180.76 69.193 181.23 69.836 c -181.69 70.480 181.93 71.497 181.93 72.890 c -177.61 72.890 l -177.73 74.218 178.46 74.883 179.80 74.883 c -180.43 74.883 181.14 74.737 181.93 74.445 c -h -177.58 71.830 m -180.08 71.830 l -180.08 70.640 179.70 70.046 178.93 70.046 c -178.15 70.046 177.70 70.640 177.58 71.830 c -h -183.87 76.000 m -183.87 69.030 l -185.75 69.030 l -185.75 70.344 l -186.36 69.362 187.16 68.872 188.12 68.872 c -188.75 68.872 189.24 69.068 189.60 69.462 c -189.96 69.855 190.14 70.393 190.14 71.074 c -190.14 76.000 l -188.26 76.000 l -188.26 71.538 l -188.26 70.746 188.00 70.351 187.47 70.351 c -186.87 70.351 186.30 70.772 185.75 71.614 c -185.75 76.000 l -h -f -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -60.000 146.00 m -138.00 146.00 l -138.00 163.00 l -60.000 163.00 l -60.000 146.00 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -60.500 146.50 m -534.50 146.50 l -534.50 214.50 l -60.500 214.50 l -60.500 146.50 l -h -S -60.500 163.50 m -137.50 163.50 l -S -137.50 163.50 m -139.50 159.50 l -S -139.50 159.50 m -139.50 146.50 l -S -64.225 162.16 m -64.377 160.80 l -65.152 161.16 65.890 161.33 66.593 161.33 c -67.287 161.33 67.788 161.18 68.097 160.89 c -68.406 160.59 68.561 160.11 68.561 159.45 c -68.561 158.50 l -68.104 159.50 67.348 160.00 66.294 160.00 c -65.465 160.00 64.807 159.69 64.320 159.07 c -63.834 158.45 63.590 157.61 63.590 156.55 c -63.590 155.44 63.863 154.55 64.409 153.88 c -64.955 153.21 65.681 152.87 66.586 152.87 c -67.297 152.87 67.955 153.16 68.561 153.73 c -68.757 153.03 l -70.446 153.03 l -70.446 158.35 l -70.446 159.41 70.381 160.18 70.252 160.65 c -70.123 161.12 69.872 161.52 69.500 161.85 c -68.874 162.39 67.991 162.66 66.853 162.66 c -66.045 162.66 65.169 162.49 64.225 162.16 c -h -68.561 157.40 m -68.561 154.83 l -68.108 154.32 67.634 154.06 67.139 154.06 c -66.665 154.06 66.288 154.28 66.009 154.71 c -65.729 155.14 65.590 155.72 65.590 156.45 c -65.590 157.81 66.028 158.50 66.904 158.50 c -67.509 158.50 68.061 158.13 68.561 157.40 c -h -78.425 159.77 m -77.532 160.03 76.686 160.16 75.886 160.16 c -74.722 160.16 73.804 159.83 73.131 159.17 c -72.458 158.51 72.122 157.61 72.122 156.46 c -72.122 155.39 72.429 154.52 73.045 153.86 c -73.661 153.20 74.474 152.87 75.486 152.87 c -76.506 152.87 77.250 153.19 77.720 153.84 c -78.190 154.48 78.425 155.50 78.425 156.89 c -74.102 156.89 l -74.229 158.22 74.959 158.88 76.292 158.88 c -76.923 158.88 77.633 158.74 78.425 158.44 c -h -74.077 155.83 m -76.578 155.83 l -76.578 154.64 76.195 154.05 75.429 154.05 c -74.650 154.05 74.199 154.64 74.077 155.83 c -h -83.960 159.96 m -83.511 160.09 83.158 160.16 82.900 160.16 c -81.271 160.16 80.456 159.40 80.456 157.87 c -80.456 154.20 l -79.675 154.20 l -79.675 153.03 l -80.456 153.03 l -80.456 151.86 l -82.335 151.64 l -82.335 153.03 l -83.827 153.03 l -83.827 154.20 l -82.335 154.20 l -82.335 157.63 l -82.335 158.48 82.684 158.91 83.382 158.91 c -83.543 158.91 83.736 158.88 83.960 158.82 c -h -84.461 162.27 m -84.461 161.25 l -90.961 161.25 l -90.961 162.27 l -h -95.729 159.96 m -95.280 160.09 94.927 160.16 94.668 160.16 c -93.039 160.16 92.225 159.40 92.225 157.87 c -92.225 154.20 l -91.444 154.20 l -91.444 153.03 l -92.225 153.03 l -92.225 151.86 l -94.104 151.64 l -94.104 153.03 l -95.595 153.03 l -95.595 154.20 l -94.104 154.20 l -94.104 157.63 l -94.104 158.48 94.453 158.91 95.151 158.91 c -95.312 158.91 95.504 158.88 95.729 158.82 c -h -103.08 159.77 m -102.19 160.03 101.34 160.16 100.54 160.16 c -99.376 160.16 98.458 159.83 97.785 159.17 c -97.112 158.51 96.776 157.61 96.776 156.46 c -96.776 155.39 97.084 154.52 97.699 153.86 c -98.315 153.20 99.129 152.87 100.14 152.87 c -101.16 152.87 101.90 153.19 102.37 153.84 c -102.84 154.48 103.08 155.50 103.08 156.89 c -98.756 156.89 l -98.883 158.22 99.613 158.88 100.95 158.88 c -101.58 158.88 102.29 158.74 103.08 158.44 c -h -98.731 155.83 m -101.23 155.83 l -101.23 154.64 100.85 154.05 100.08 154.05 c -99.304 154.05 98.854 154.64 98.731 155.83 c -h -105.02 160.00 m -105.02 153.03 l -106.90 153.03 l -106.90 154.34 l -107.51 153.36 108.31 152.87 109.27 152.87 c -109.90 152.87 110.39 153.07 110.75 153.46 c -111.11 153.86 111.29 154.39 111.29 155.07 c -111.29 160.00 l -109.41 160.00 l -109.41 155.54 l -109.41 154.75 109.15 154.35 108.62 154.35 c -108.02 154.35 107.45 154.77 106.90 155.61 c -106.90 160.00 l -h -116.97 159.25 m -116.35 159.86 115.68 160.16 114.96 160.16 c -114.35 160.16 113.86 159.97 113.48 159.60 c -113.10 159.23 112.91 158.75 112.91 158.15 c -112.91 157.38 113.21 156.79 113.83 156.37 c -114.44 155.96 115.33 155.75 116.47 155.75 c -116.97 155.75 l -116.97 155.11 l -116.97 154.39 116.56 154.03 115.74 154.03 c -115.00 154.03 114.26 154.23 113.51 154.65 c -113.51 153.35 l -114.37 153.03 115.21 152.87 116.04 152.87 c -117.86 152.87 118.78 153.60 118.78 155.05 c -118.78 158.13 l -118.78 158.68 118.95 158.95 119.30 158.95 c -119.37 158.95 119.45 158.94 119.55 158.93 c -119.60 159.98 l -119.20 160.10 118.85 160.16 118.54 160.16 c -117.77 160.16 117.28 159.86 117.06 159.25 c -h -116.97 158.24 m -116.97 156.83 l -116.53 156.83 l -115.32 156.83 114.71 157.21 114.71 157.97 c -114.71 158.23 114.80 158.44 114.97 158.62 c -115.15 158.80 115.36 158.88 115.62 158.88 c -116.06 158.88 116.51 158.67 116.97 158.24 c -h -121.20 160.00 m -121.20 153.03 l -123.08 153.03 l -123.08 154.34 l -123.69 153.36 124.49 152.87 125.45 152.87 c -126.08 152.87 126.57 153.07 126.93 153.46 c -127.29 153.86 127.47 154.39 127.47 155.07 c -127.47 160.00 l -125.59 160.00 l -125.59 155.54 l -125.59 154.75 125.33 154.35 124.80 154.35 c -124.20 154.35 123.63 154.77 123.08 155.61 c -123.08 160.00 l -h -133.33 159.96 m -132.88 160.09 132.53 160.16 132.27 160.16 c -130.64 160.16 129.83 159.40 129.83 157.87 c -129.83 154.20 l -129.05 154.20 l -129.05 153.03 l -129.83 153.03 l -129.83 151.86 l -131.71 151.64 l -131.71 153.03 l -133.20 153.03 l -133.20 154.20 l -131.71 154.20 l -131.71 157.63 l -131.71 158.48 132.06 158.91 132.75 158.91 c -132.92 158.91 133.11 158.88 133.33 158.82 c -h -134.82 159.78 m -134.82 158.40 l -135.75 158.79 136.55 158.98 137.21 158.98 c -137.98 158.98 138.37 158.72 138.37 158.20 c -138.37 157.86 138.05 157.56 137.41 157.31 c -136.78 157.05 l -136.09 156.78 135.60 156.47 135.30 156.15 c -135.00 155.83 134.86 155.43 134.86 154.96 c -134.86 154.30 135.11 153.79 135.61 153.42 c -136.11 153.05 136.82 152.87 137.72 152.87 c -138.29 152.87 138.97 152.95 139.75 153.12 c -139.75 154.44 l -139.00 154.18 138.37 154.05 137.88 154.05 c -137.10 154.05 136.71 154.29 136.71 154.77 c -136.71 155.09 137.00 155.36 137.57 155.58 c -138.12 155.79 l -138.93 156.09 139.50 156.41 139.82 156.72 c -140.14 157.04 140.30 157.45 140.30 157.95 c -140.30 158.61 140.03 159.14 139.49 159.55 c -138.94 159.95 138.23 160.16 137.36 160.16 c -136.52 160.16 135.68 160.03 134.82 159.78 c -h -f -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -60.000 230.00 m -177.00 230.00 l -177.00 247.00 l -60.000 247.00 l -60.000 230.00 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -60.500 230.50 m -534.50 230.50 l -534.50 298.50 l -60.500 298.50 l -60.500 230.50 l -h -S -60.500 247.50 m -176.50 247.50 l -S -176.50 247.50 m -178.50 243.50 l -S -178.50 243.50 m -178.50 230.50 l -S -67.767 243.96 m -67.319 244.09 66.965 244.16 66.707 244.16 c -65.078 244.16 64.263 243.40 64.263 241.87 c -64.263 238.20 l -63.482 238.20 l -63.482 237.03 l -64.263 237.03 l -64.263 235.86 l -66.142 235.64 l -66.142 237.03 l -67.634 237.03 l -67.634 238.20 l -66.142 238.20 l -66.142 241.63 l -66.142 242.48 66.491 242.91 67.189 242.91 c -67.350 242.91 67.543 242.88 67.767 242.82 c -h -75.118 243.77 m -74.225 244.03 73.378 244.16 72.579 244.16 c -71.415 244.16 70.497 243.83 69.824 243.17 c -69.151 242.51 68.814 241.61 68.814 240.46 c -68.814 239.39 69.122 238.52 69.738 237.86 c -70.354 237.20 71.167 236.87 72.179 236.87 c -73.199 236.87 73.943 237.19 74.413 237.84 c -74.883 238.48 75.118 239.50 75.118 240.89 c -70.795 240.89 l -70.922 242.22 71.652 242.88 72.985 242.88 c -73.615 242.88 74.326 242.74 75.118 242.44 c -h -70.770 239.83 m -73.271 239.83 l -73.271 238.64 72.888 238.05 72.122 238.05 c -71.343 238.05 70.892 238.64 70.770 239.83 c -h -77.060 244.00 m -77.060 237.03 l -78.939 237.03 l -78.939 238.34 l -79.553 237.36 80.344 236.87 81.313 236.87 c -81.935 236.87 82.426 237.07 82.786 237.46 c -83.145 237.86 83.325 238.39 83.325 239.07 c -83.325 244.00 l -81.446 244.00 l -81.446 239.54 l -81.446 238.75 81.184 238.35 80.659 238.35 c -80.062 238.35 79.489 238.77 78.939 239.61 c -78.939 244.00 l -h -89.013 243.25 m -88.386 243.86 87.716 244.16 87.000 244.16 c -86.391 244.16 85.896 243.97 85.515 243.60 c -85.134 243.23 84.944 242.75 84.944 242.15 c -84.944 241.38 85.252 240.79 85.867 240.37 c -86.483 239.96 87.364 239.75 88.511 239.75 c -89.013 239.75 l -89.013 239.11 l -89.013 238.39 88.600 238.03 87.775 238.03 c -87.043 238.03 86.302 238.23 85.553 238.65 c -85.553 237.35 l -86.404 237.03 87.246 236.87 88.080 236.87 c -89.903 236.87 90.815 237.60 90.815 239.05 c -90.815 242.13 l -90.815 242.68 90.991 242.95 91.342 242.95 c -91.406 242.95 91.488 242.94 91.590 242.93 c -91.634 243.98 l -91.236 244.10 90.885 244.16 90.581 244.16 c -89.810 244.16 89.315 243.86 89.095 243.25 c -h -89.013 242.24 m -89.013 240.83 l -88.568 240.83 l -87.354 240.83 86.747 241.21 86.747 241.97 c -86.747 242.23 86.834 242.44 87.010 242.62 c -87.186 242.80 87.403 242.88 87.661 242.88 c -88.101 242.88 88.551 242.67 89.013 242.24 c -h -93.240 244.00 m -93.240 237.03 l -95.119 237.03 l -95.119 238.34 l -95.733 237.36 96.524 236.87 97.493 236.87 c -98.115 236.87 98.606 237.07 98.966 237.46 c -99.326 237.86 99.505 238.39 99.505 239.07 c -99.505 244.00 l -97.626 244.00 l -97.626 239.54 l -97.626 238.75 97.364 238.35 96.839 238.35 c -96.243 238.35 95.669 238.77 95.119 239.61 c -95.119 244.00 l -h -105.37 243.96 m -104.92 244.09 104.57 244.16 104.31 244.16 c -102.68 244.16 101.87 243.40 101.87 241.87 c -101.87 238.20 l -101.09 238.20 l -101.09 237.03 l -101.87 237.03 l -101.87 235.86 l -103.75 235.64 l -103.75 237.03 l -105.24 237.03 l -105.24 238.20 l -103.75 238.20 l -103.75 241.63 l -103.75 242.48 104.09 242.91 104.79 242.91 c -104.95 242.91 105.15 242.88 105.37 242.82 c -h -105.87 246.27 m -105.87 245.25 l -112.37 245.25 l -112.37 246.27 l -h -116.96 243.25 m -116.34 243.86 115.66 244.16 114.95 244.16 c -114.34 244.16 113.84 243.97 113.46 243.60 c -113.08 243.23 112.89 242.75 112.89 242.15 c -112.89 241.38 113.20 240.79 113.82 240.37 c -114.43 239.96 115.31 239.75 116.46 239.75 c -116.96 239.75 l -116.96 239.11 l -116.96 238.39 116.55 238.03 115.72 238.03 c -114.99 238.03 114.25 238.23 113.50 238.65 c -113.50 237.35 l -114.35 237.03 115.19 236.87 116.03 236.87 c -117.85 236.87 118.76 237.60 118.76 239.05 c -118.76 242.13 l -118.76 242.68 118.94 242.95 119.29 242.95 c -119.35 242.95 119.44 242.94 119.54 242.93 c -119.58 243.98 l -119.19 244.10 118.83 244.16 118.53 244.16 c -117.76 244.16 117.26 243.86 117.04 243.25 c -h -116.96 242.24 m -116.96 240.83 l -116.52 240.83 l -115.30 240.83 114.70 241.21 114.70 241.97 c -114.70 242.23 114.78 242.44 114.96 242.62 c -115.13 242.80 115.35 242.88 115.61 242.88 c -116.05 242.88 116.50 242.67 116.96 242.24 c -h -125.50 244.00 m -125.50 242.69 l -124.89 243.67 124.10 244.16 123.12 244.16 c -122.50 244.16 122.01 243.96 121.65 243.57 c -121.29 243.17 121.11 242.64 121.11 241.96 c -121.11 237.03 l -122.99 237.03 l -122.99 241.49 l -122.99 242.28 123.26 242.68 123.79 242.68 c -124.38 242.68 124.95 242.26 125.50 241.42 c -125.50 237.03 l -127.38 237.03 l -127.38 244.00 l -h -133.32 243.96 m -132.87 244.09 132.52 244.16 132.26 244.16 c -130.63 244.16 129.82 243.40 129.82 241.87 c -129.82 238.20 l -129.03 238.20 l -129.03 237.03 l -129.82 237.03 l -129.82 235.86 l -131.69 235.64 l -131.69 237.03 l -133.19 237.03 l -133.19 238.20 l -131.69 238.20 l -131.69 241.63 l -131.69 242.48 132.04 242.91 132.74 242.91 c -132.90 242.91 133.10 242.88 133.32 242.82 c -h -135.00 244.00 m -135.00 233.98 l -136.87 233.98 l -136.87 238.34 l -137.49 237.36 138.28 236.87 139.25 236.87 c -139.87 236.87 140.36 237.07 140.72 237.46 c -141.08 237.86 141.26 238.39 141.26 239.07 c -141.26 244.00 l -139.38 244.00 l -139.38 239.54 l -139.38 238.75 139.12 238.35 138.59 238.35 c -138.00 238.35 137.42 238.77 136.87 239.61 c -136.87 244.00 l -h -142.36 246.27 m -142.36 245.25 l -148.86 245.25 l -148.86 246.27 l -h -153.63 243.96 m -153.18 244.09 152.82 244.16 152.57 244.16 c -150.94 244.16 150.12 243.40 150.12 241.87 c -150.12 238.20 l -149.34 238.20 l -149.34 237.03 l -150.12 237.03 l -150.12 235.86 l -152.00 235.64 l -152.00 237.03 l -153.49 237.03 l -153.49 238.20 l -152.00 238.20 l -152.00 241.63 l -152.00 242.48 152.35 242.91 153.05 242.91 c -153.21 242.91 153.40 242.88 153.63 242.82 c -h -158.23 244.16 m -157.14 244.16 156.28 243.83 155.63 243.17 c -154.99 242.51 154.67 241.63 154.67 240.52 c -154.67 239.39 155.00 238.50 155.64 237.85 c -156.29 237.20 157.16 236.87 158.27 236.87 c -159.38 236.87 160.26 237.20 160.91 237.85 c -161.55 238.50 161.88 239.39 161.88 240.50 c -161.88 241.65 161.55 242.54 160.91 243.19 c -160.26 243.83 159.37 244.16 158.23 244.16 c -h -158.26 242.98 m -159.34 242.98 159.88 242.16 159.88 240.50 c -159.88 239.74 159.74 239.15 159.45 238.71 c -159.16 238.27 158.77 238.05 158.27 238.05 c -157.78 238.05 157.39 238.27 157.10 238.71 c -156.82 239.15 156.67 239.75 156.67 240.52 c -156.67 241.27 156.81 241.87 157.10 242.32 c -157.38 242.76 157.77 242.98 158.26 242.98 c -h -163.60 244.00 m -163.60 233.98 l -165.48 233.98 l -165.48 240.28 l -165.60 240.28 l -168.09 237.03 l -169.65 237.03 l -167.36 240.01 l -170.36 244.00 l -168.08 244.00 l -165.60 240.52 l -165.48 240.52 l -165.48 244.00 l -h -177.51 243.77 m -176.61 244.03 175.77 244.16 174.97 244.16 c -173.80 244.16 172.88 243.83 172.21 243.17 c -171.54 242.51 171.20 241.61 171.20 240.46 c -171.20 239.39 171.51 238.52 172.13 237.86 c -172.74 237.20 173.56 236.87 174.57 236.87 c -175.59 236.87 176.33 237.19 176.80 237.84 c -177.27 238.48 177.51 239.50 177.51 240.89 c -173.18 240.89 l -173.31 242.22 174.04 242.88 175.37 242.88 c -176.00 242.88 176.71 242.74 177.51 242.44 c -h -173.16 239.83 m -175.66 239.83 l -175.66 238.64 175.28 238.05 174.51 238.05 c -173.73 238.05 173.28 238.64 173.16 239.83 c -h -179.45 244.00 m -179.45 237.03 l -181.33 237.03 l -181.33 238.34 l -181.94 237.36 182.73 236.87 183.70 236.87 c -184.32 236.87 184.81 237.07 185.17 237.46 c -185.53 237.86 185.71 238.39 185.71 239.07 c -185.71 244.00 l -183.83 244.00 l -183.83 239.54 l -183.83 238.75 183.57 238.35 183.05 238.35 c -182.45 238.35 181.88 238.77 181.33 239.61 c -181.33 244.00 l -h -f -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -50.000 314.00 m -214.00 314.00 l -214.00 331.00 l -50.000 331.00 l -50.000 314.00 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -50.500 314.50 m -973.50 314.50 l -973.50 679.50 l -50.500 679.50 l -50.500 314.50 l -h -S -50.500 331.50 m -213.50 331.50 l -S -213.50 331.50 m -215.50 327.50 l -S -215.50 327.50 m -215.50 314.50 l -S -58.561 328.00 m -58.561 326.69 l -58.078 327.67 57.319 328.16 56.282 328.16 c -55.444 328.16 54.786 327.85 54.308 327.24 c -53.829 326.62 53.590 325.78 53.590 324.71 c -53.590 323.54 53.860 322.61 54.400 321.91 c -54.939 321.22 55.664 320.87 56.574 320.87 c -57.302 320.87 57.964 321.16 58.561 321.73 c -58.561 317.98 l -60.446 317.98 l -60.446 328.00 l -h -58.561 322.85 m -58.108 322.34 57.623 322.09 57.107 322.09 c -56.646 322.09 56.278 322.31 56.002 322.76 c -55.727 323.20 55.590 323.80 55.590 324.55 c -55.590 325.97 56.047 326.69 56.961 326.69 c -57.520 326.69 58.053 326.33 58.561 325.61 c -h -65.714 328.16 m -64.627 328.16 63.762 327.83 63.121 327.17 c -62.480 326.51 62.160 325.63 62.160 324.52 c -62.160 323.39 62.482 322.50 63.128 321.85 c -63.773 321.20 64.650 320.87 65.759 320.87 c -66.872 320.87 67.751 321.20 68.396 321.85 c -69.042 322.50 69.364 323.39 69.364 324.50 c -69.364 325.65 69.041 326.54 68.393 327.19 c -67.746 327.83 66.853 328.16 65.714 328.16 c -h -65.746 326.98 m -66.825 326.98 67.365 326.16 67.365 324.50 c -67.365 323.74 67.222 323.15 66.936 322.71 c -66.651 322.27 66.258 322.05 65.759 322.05 c -65.264 322.05 64.873 322.27 64.588 322.71 c -64.302 323.15 64.159 323.75 64.159 324.52 c -64.159 325.27 64.301 325.87 64.584 326.32 c -64.868 326.76 65.255 326.98 65.746 326.98 c -h -69.917 330.27 m -69.917 329.25 l -76.417 329.25 l -76.417 330.27 l -h -77.407 327.78 m -77.407 326.40 l -78.338 326.79 79.133 326.98 79.793 326.98 c -80.564 326.98 80.949 326.72 80.949 326.20 c -80.949 325.86 80.631 325.56 79.997 325.31 c -79.362 325.05 l -78.672 324.78 78.179 324.47 77.883 324.15 c -77.587 323.83 77.438 323.43 77.438 322.96 c -77.438 322.30 77.690 321.79 78.194 321.42 c -78.697 321.05 79.402 320.87 80.308 320.87 c -80.875 320.87 81.550 320.95 82.333 321.12 c -82.333 322.44 l -81.579 322.18 80.955 322.05 80.460 322.05 c -79.681 322.05 79.292 322.29 79.292 322.77 c -79.292 323.09 79.580 323.36 80.155 323.58 c -80.701 323.79 l -81.518 324.09 82.086 324.41 82.406 324.72 c -82.725 325.04 82.885 325.45 82.885 325.95 c -82.885 326.61 82.613 327.14 82.069 327.55 c -81.525 327.95 80.818 328.16 79.946 328.16 c -79.108 328.16 78.262 328.03 77.407 327.78 c -h -87.868 328.16 m -86.780 328.16 85.916 327.83 85.275 327.17 c -84.634 326.51 84.313 325.63 84.313 324.52 c -84.313 323.39 84.636 322.50 85.281 321.85 c -85.926 321.20 86.803 320.87 87.912 320.87 c -89.025 320.87 89.904 321.20 90.550 321.85 c -91.195 322.50 91.518 323.39 91.518 324.50 c -91.518 325.65 91.194 326.54 90.546 327.19 c -89.899 327.83 89.006 328.16 87.868 328.16 c -h -87.899 326.98 m -88.979 326.98 89.518 326.16 89.518 324.50 c -89.518 323.74 89.375 323.15 89.090 322.71 c -88.804 322.27 88.411 322.05 87.912 322.05 c -87.417 322.05 87.027 322.27 86.741 322.71 c -86.455 323.15 86.312 323.75 86.312 324.52 c -86.312 325.27 86.454 325.87 86.738 326.32 c -87.021 326.76 87.409 326.98 87.899 326.98 c -h -93.244 328.00 m -93.244 321.03 l -95.047 321.03 l -95.047 322.34 l -95.597 321.36 96.380 320.87 97.396 320.87 c -97.920 320.87 98.352 321.00 98.690 321.26 c -99.029 321.52 99.236 321.88 99.312 322.34 c -99.964 321.36 100.75 320.87 101.67 320.87 c -102.95 320.87 103.58 321.57 103.58 322.98 c -103.58 328.00 l -101.78 328.00 l -101.78 323.59 l -101.78 322.77 101.51 322.36 100.96 322.36 c -100.39 322.36 99.846 322.76 99.312 323.58 c -99.312 328.00 l -97.510 328.00 l -97.510 323.59 l -97.510 322.77 97.233 322.35 96.678 322.35 c -96.124 322.35 95.580 322.76 95.047 323.58 c -95.047 328.00 l -h -111.53 327.77 m -110.63 328.03 109.79 328.16 108.99 328.16 c -107.82 328.16 106.90 327.83 106.23 327.17 c -105.56 326.51 105.22 325.61 105.22 324.46 c -105.22 323.39 105.53 322.52 106.15 321.86 c -106.76 321.20 107.58 320.87 108.59 320.87 c -109.61 320.87 110.35 321.19 110.82 321.84 c -111.29 322.48 111.53 323.50 111.53 324.89 c -107.20 324.89 l -107.33 326.22 108.06 326.88 109.39 326.88 c -110.02 326.88 110.73 326.74 111.53 326.44 c -h -107.18 323.83 m -109.68 323.83 l -109.68 322.64 109.30 322.05 108.53 322.05 c -107.75 322.05 107.30 322.64 107.18 323.83 c -h -117.06 327.96 m -116.61 328.09 116.26 328.16 116.00 328.16 c -114.37 328.16 113.56 327.40 113.56 325.87 c -113.56 322.20 l -112.78 322.20 l -112.78 321.03 l -113.56 321.03 l -113.56 319.86 l -115.44 319.64 l -115.44 321.03 l -116.93 321.03 l -116.93 322.20 l -115.44 322.20 l -115.44 325.63 l -115.44 326.48 115.78 326.91 116.48 326.91 c -116.64 326.91 116.84 326.88 117.06 326.82 c -h -118.74 328.00 m -118.74 317.98 l -120.62 317.98 l -120.62 322.34 l -121.23 321.36 122.02 320.87 122.99 320.87 c -123.61 320.87 124.10 321.07 124.46 321.46 c -124.82 321.86 125.00 322.39 125.00 323.07 c -125.00 328.00 l -123.12 328.00 l -123.12 323.54 l -123.12 322.75 122.86 322.35 122.34 322.35 c -121.74 322.35 121.17 322.77 120.62 323.61 c -120.62 328.00 l -h -127.27 328.00 m -127.27 321.03 l -129.15 321.03 l -129.15 328.00 l -h -127.27 319.86 m -127.27 318.29 l -129.15 318.29 l -129.15 319.86 l -h -131.50 328.00 m -131.50 321.03 l -133.38 321.03 l -133.38 322.34 l -133.99 321.36 134.79 320.87 135.75 320.87 c -136.38 320.87 136.87 321.07 137.23 321.46 c -137.59 321.86 137.77 322.39 137.77 323.07 c -137.77 328.00 l -135.89 328.00 l -135.89 323.54 l -135.89 322.75 135.63 322.35 135.10 322.35 c -134.50 322.35 133.93 322.77 133.38 323.61 c -133.38 328.00 l -h -140.09 330.16 m -140.24 328.80 l -141.02 329.16 141.76 329.33 142.46 329.33 c -143.15 329.33 143.65 329.18 143.96 328.89 c -144.27 328.59 144.43 328.11 144.43 327.45 c -144.43 326.50 l -143.97 327.50 143.21 328.00 142.16 328.00 c -141.33 328.00 140.67 327.69 140.19 327.07 c -139.70 326.45 139.46 325.61 139.46 324.55 c -139.46 323.44 139.73 322.55 140.27 321.88 c -140.82 321.21 141.55 320.87 142.45 320.87 c -143.16 320.87 143.82 321.16 144.43 321.73 c -144.62 321.03 l -146.31 321.03 l -146.31 326.35 l -146.31 327.41 146.25 328.18 146.12 328.65 c -145.99 329.12 145.74 329.52 145.36 329.85 c -144.74 330.39 143.86 330.66 142.72 330.66 c -141.91 330.66 141.03 330.49 140.09 330.16 c -h -144.43 325.40 m -144.43 322.83 l -143.97 322.32 143.50 322.06 143.00 322.06 c -142.53 322.06 142.15 322.28 141.87 322.71 c -141.59 323.14 141.45 323.72 141.45 324.45 c -141.45 325.81 141.89 326.50 142.77 326.50 c -143.37 326.50 143.93 326.13 144.43 325.40 c -h -147.44 330.27 m -147.44 329.25 l -153.94 329.25 l -153.94 330.27 l -h -155.11 328.00 m -155.11 321.03 l -156.99 321.03 l -156.99 328.00 l -h -155.11 319.86 m -155.11 318.29 l -156.99 318.29 l -156.99 319.86 l -h -159.34 328.00 m -159.34 321.03 l -161.22 321.03 l -161.22 322.34 l -161.83 321.36 162.63 320.87 163.60 320.87 c -164.22 320.87 164.71 321.07 165.07 321.46 c -165.43 321.86 165.61 322.39 165.61 323.07 c -165.61 328.00 l -163.73 328.00 l -163.73 323.54 l -163.73 322.75 163.47 322.35 162.94 322.35 c -162.34 322.35 161.77 322.77 161.22 323.61 c -161.22 328.00 l -h -166.71 330.27 m -166.71 329.25 l -173.21 329.25 l -173.21 330.27 l -h -179.52 327.85 m -178.74 328.06 178.02 328.16 177.36 328.16 c -176.24 328.16 175.36 327.83 174.72 327.18 c -174.07 326.52 173.75 325.63 173.75 324.51 c -173.75 323.37 174.08 322.48 174.75 321.84 c -175.41 321.19 176.33 320.87 177.50 320.87 c -178.07 320.87 178.72 320.96 179.46 321.14 c -179.46 322.50 l -178.69 322.25 178.08 322.13 177.62 322.13 c -177.05 322.13 176.60 322.34 176.26 322.78 c -175.92 323.21 175.74 323.78 175.74 324.50 c -175.74 325.23 175.93 325.81 176.30 326.25 c -176.67 326.69 177.16 326.91 177.78 326.91 c -178.35 326.91 178.92 326.79 179.52 326.55 c -h -184.23 328.16 m -183.14 328.16 182.27 327.83 181.63 327.17 c -180.99 326.51 180.67 325.63 180.67 324.52 c -180.67 323.39 180.99 322.50 181.64 321.85 c -182.28 321.20 183.16 320.87 184.27 320.87 c -185.38 320.87 186.26 321.20 186.91 321.85 c -187.55 322.50 187.88 323.39 187.88 324.50 c -187.88 325.65 187.55 326.54 186.90 327.19 c -186.26 327.83 185.36 328.16 184.23 328.16 c -h -184.26 326.98 m -185.34 326.98 185.88 326.16 185.88 324.50 c -185.88 323.74 185.73 323.15 185.45 322.71 c -185.16 322.27 184.77 322.05 184.27 322.05 c -183.77 322.05 183.38 322.27 183.10 322.71 c -182.81 323.15 182.67 323.75 182.67 324.52 c -182.67 325.27 182.81 325.87 183.10 326.32 c -183.38 326.76 183.77 326.98 184.26 326.98 c -h -189.60 328.00 m -189.60 321.03 l -191.40 321.03 l -191.40 322.34 l -191.95 321.36 192.74 320.87 193.75 320.87 c -194.28 320.87 194.71 321.00 195.05 321.26 c -195.39 321.52 195.59 321.88 195.67 322.34 c -196.32 321.36 197.11 320.87 198.03 320.87 c -199.31 320.87 199.94 321.57 199.94 322.98 c -199.94 328.00 l -198.14 328.00 l -198.14 323.59 l -198.14 322.77 197.86 322.36 197.31 322.36 c -196.75 322.36 196.20 322.76 195.67 323.58 c -195.67 328.00 l -193.87 328.00 l -193.87 323.59 l -193.87 322.77 193.59 322.35 193.04 322.35 c -192.48 322.35 191.94 322.76 191.40 323.58 c -191.40 328.00 l -h -202.21 330.51 m -202.21 321.03 l -204.09 321.03 l -204.09 322.34 l -204.57 321.36 205.33 320.87 206.37 320.87 c -207.20 320.87 207.86 321.18 208.34 321.79 c -208.82 322.41 209.06 323.25 209.06 324.32 c -209.06 325.49 208.79 326.42 208.25 327.12 c -207.71 327.81 206.98 328.16 206.07 328.16 c -205.34 328.16 204.68 327.87 204.09 327.30 c -204.09 330.51 l -h -204.09 326.18 m -204.54 326.69 205.03 326.94 205.55 326.94 c -206.01 326.94 206.38 326.72 206.65 326.27 c -206.93 325.82 207.06 325.23 207.06 324.48 c -207.06 323.06 206.60 322.34 205.69 322.34 c -205.13 322.34 204.60 322.70 204.09 323.42 c -h -215.13 328.00 m -215.13 326.69 l -214.52 327.67 213.73 328.16 212.76 328.16 c -212.14 328.16 211.64 327.96 211.29 327.57 c -210.93 327.17 210.75 326.64 210.75 325.96 c -210.75 321.03 l -212.62 321.03 l -212.62 325.49 l -212.62 326.28 212.89 326.68 213.42 326.68 c -214.01 326.68 214.58 326.26 215.13 325.42 c -215.13 321.03 l -217.01 321.03 l -217.01 328.00 l -h -222.95 327.96 m -222.50 328.09 222.15 328.16 221.89 328.16 c -220.26 328.16 219.45 327.40 219.45 325.87 c -219.45 322.20 l -218.67 322.20 l -218.67 321.03 l -219.45 321.03 l -219.45 319.86 l -221.33 319.64 l -221.33 321.03 l -222.82 321.03 l -222.82 322.20 l -221.33 322.20 l -221.33 325.63 l -221.33 326.48 221.68 326.91 222.37 326.91 c -222.54 326.91 222.73 326.88 222.95 326.82 c -h -230.30 327.77 m -229.41 328.03 228.56 328.16 227.76 328.16 c -226.60 328.16 225.68 327.83 225.01 327.17 c -224.34 326.51 224.00 325.61 224.00 324.46 c -224.00 323.39 224.31 322.52 224.92 321.86 c -225.54 321.20 226.35 320.87 227.36 320.87 c -228.38 320.87 229.13 321.19 229.60 321.84 c -230.07 322.48 230.30 323.50 230.30 324.89 c -225.98 324.89 l -226.11 326.22 226.84 326.88 228.17 326.88 c -228.80 326.88 229.51 326.74 230.30 326.44 c -h -225.95 323.83 m -228.46 323.83 l -228.46 322.64 228.07 322.05 227.31 322.05 c -226.53 322.05 226.08 322.64 225.95 323.83 c -h -f -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -506.00 402.00 m -617.00 402.00 l -617.00 419.00 l -506.00 419.00 l -506.00 402.00 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -506.50 402.50 m -713.50 402.50 l -713.50 470.50 l -506.50 470.50 l -506.50 402.50 l -h -S -506.50 419.50 m -616.50 419.50 l -S -616.50 419.50 m -618.50 415.50 l -S -618.50 415.50 m -618.50 402.50 l -S -513.59 415.25 m -512.96 415.86 512.29 416.16 511.58 416.16 c -510.97 416.16 510.47 415.97 510.09 415.60 c -509.71 415.23 509.52 414.75 509.52 414.15 c -509.52 413.38 509.83 412.79 510.44 412.37 c -511.06 411.96 511.94 411.75 513.09 411.75 c -513.59 411.75 l -513.59 411.11 l -513.59 410.39 513.18 410.03 512.35 410.03 c -511.62 410.03 510.88 410.23 510.13 410.65 c -510.13 409.35 l -510.98 409.03 511.82 408.87 512.66 408.87 c -514.48 408.87 515.39 409.60 515.39 411.05 c -515.39 414.13 l -515.39 414.68 515.57 414.95 515.92 414.95 c -515.98 414.95 516.06 414.94 516.17 414.93 c -516.21 415.98 l -515.81 416.10 515.46 416.16 515.16 416.16 c -514.39 416.16 513.89 415.86 513.67 415.25 c -h -513.59 414.24 m -513.59 412.83 l -513.15 412.83 l -511.93 412.83 511.32 413.21 511.32 413.97 c -511.32 414.23 511.41 414.44 511.59 414.62 c -511.76 414.80 511.98 414.88 512.24 414.88 c -512.68 414.88 513.13 414.67 513.59 414.24 c -h -522.13 416.00 m -522.13 414.69 l -521.52 415.67 520.73 416.16 519.75 416.16 c -519.13 416.16 518.64 415.96 518.28 415.57 c -517.92 415.17 517.74 414.64 517.74 413.96 c -517.74 409.03 l -519.62 409.03 l -519.62 413.49 l -519.62 414.28 519.88 414.68 520.41 414.68 c -521.01 414.68 521.58 414.26 522.13 413.42 c -522.13 409.03 l -524.01 409.03 l -524.01 416.00 l -h -529.95 415.96 m -529.50 416.09 529.15 416.16 528.89 416.16 c -527.26 416.16 526.44 415.40 526.44 413.87 c -526.44 410.20 l -525.66 410.20 l -525.66 409.03 l -526.44 409.03 l -526.44 407.86 l -528.32 407.64 l -528.32 409.03 l -529.81 409.03 l -529.81 410.20 l -528.32 410.20 l -528.32 413.63 l -528.32 414.48 528.67 414.91 529.37 414.91 c -529.53 414.91 529.72 414.88 529.95 414.82 c -h -531.62 416.00 m -531.62 405.98 l -533.50 405.98 l -533.50 410.34 l -534.12 409.36 534.91 408.87 535.88 408.87 c -536.50 408.87 536.99 409.07 537.35 409.46 c -537.71 409.86 537.89 410.39 537.89 411.07 c -537.89 416.00 l -536.01 416.00 l -536.01 411.54 l -536.01 410.75 535.75 410.35 535.22 410.35 c -534.63 410.35 534.05 410.77 533.50 411.61 c -533.50 416.00 l -h -538.99 418.27 m -538.99 417.25 l -545.49 417.25 l -545.49 418.27 l -h -546.66 416.00 m -546.66 409.03 l -548.46 409.03 l -548.46 410.34 l -549.01 409.36 549.80 408.87 550.81 408.87 c -551.34 408.87 551.77 409.00 552.11 409.26 c -552.45 409.52 552.65 409.88 552.73 410.34 c -553.38 409.36 554.17 408.87 555.09 408.87 c -556.36 408.87 557.00 409.57 557.00 410.98 c -557.00 416.00 l -555.20 416.00 l -555.20 411.59 l -555.20 410.77 554.92 410.36 554.37 410.36 c -553.81 410.36 553.26 410.76 552.73 411.58 c -552.73 416.00 l -550.93 416.00 l -550.93 411.59 l -550.93 410.77 550.65 410.35 550.09 410.35 c -549.54 410.35 549.00 410.76 548.46 411.58 c -548.46 416.00 l -h -559.27 416.00 m -559.27 409.03 l -561.15 409.03 l -561.15 416.00 l -h -559.27 407.86 m -559.27 406.29 l -561.15 406.29 l -561.15 407.86 l -h -567.88 416.00 m -567.88 414.69 l -567.40 415.67 566.64 416.16 565.60 416.16 c -564.76 416.16 564.11 415.85 563.63 415.24 c -563.15 414.62 562.91 413.78 562.91 412.71 c -562.91 411.54 563.18 410.61 563.72 409.91 c -564.26 409.22 564.98 408.87 565.89 408.87 c -566.62 408.87 567.28 409.16 567.88 409.73 c -567.88 405.98 l -569.77 405.98 l -569.77 416.00 l -h -567.88 410.85 m -567.43 410.34 566.94 410.09 566.43 410.09 c -565.97 410.09 565.60 410.31 565.32 410.76 c -565.05 411.20 564.91 411.80 564.91 412.55 c -564.91 413.97 565.37 414.69 566.28 414.69 c -566.84 414.69 567.37 414.33 567.88 413.61 c -h -576.49 416.00 m -576.49 414.69 l -576.01 415.67 575.25 416.16 574.22 416.16 c -573.38 416.16 572.72 415.85 572.24 415.24 c -571.76 414.62 571.52 413.78 571.52 412.71 c -571.52 411.54 571.79 410.61 572.33 409.91 c -572.87 409.22 573.60 408.87 574.51 408.87 c -575.24 408.87 575.90 409.16 576.49 409.73 c -576.49 405.98 l -578.38 405.98 l -578.38 416.00 l -h -576.49 410.85 m -576.04 410.34 575.56 410.09 575.04 410.09 c -574.58 410.09 574.21 410.31 573.94 410.76 c -573.66 411.20 573.52 411.80 573.52 412.55 c -573.52 413.97 573.98 414.69 574.90 414.69 c -575.45 414.69 575.99 414.33 576.49 413.61 c -h -580.72 416.00 m -580.72 405.98 l -582.60 405.98 l -582.60 416.00 l -h -590.62 415.77 m -589.73 416.03 588.89 416.16 588.09 416.16 c -586.92 416.16 586.00 415.83 585.33 415.17 c -584.66 414.51 584.32 413.61 584.32 412.46 c -584.32 411.39 584.63 410.52 585.24 409.86 c -585.86 409.20 586.67 408.87 587.69 408.87 c -588.71 408.87 589.45 409.19 589.92 409.84 c -590.39 410.48 590.62 411.50 590.62 412.89 c -586.30 412.89 l -586.43 414.22 587.16 414.88 588.49 414.88 c -589.12 414.88 589.83 414.74 590.62 414.44 c -h -586.28 411.83 m -588.78 411.83 l -588.78 410.64 588.39 410.05 587.63 410.05 c -586.85 410.05 586.40 410.64 586.28 411.83 c -h -593.80 416.00 m -591.86 409.03 l -593.61 409.03 l -594.98 413.91 l -596.44 409.03 l -598.06 409.03 l -599.38 413.94 l -600.86 409.03 l -602.16 409.03 l -600.10 416.00 l -598.29 416.00 l -597.02 411.22 l -595.60 416.00 l -h -607.20 415.25 m -606.57 415.86 605.90 416.16 605.19 416.16 c -604.58 416.16 604.08 415.97 603.70 415.60 c -603.32 415.23 603.13 414.75 603.13 414.15 c -603.13 413.38 603.44 412.79 604.05 412.37 c -604.67 411.96 605.55 411.75 606.70 411.75 c -607.20 411.75 l -607.20 411.11 l -607.20 410.39 606.79 410.03 605.96 410.03 c -605.23 410.03 604.49 410.23 603.74 410.65 c -603.74 409.35 l -604.59 409.03 605.43 408.87 606.27 408.87 c -608.09 408.87 609.00 409.60 609.00 411.05 c -609.00 414.13 l -609.00 414.68 609.18 414.95 609.53 414.95 c -609.59 414.95 609.67 414.94 609.78 414.93 c -609.82 415.98 l -609.42 416.10 609.07 416.16 608.77 416.16 c -608.00 416.16 607.50 415.86 607.28 415.25 c -h -607.20 414.24 m -607.20 412.83 l -606.75 412.83 l -605.54 412.83 604.93 413.21 604.93 413.97 c -604.93 414.23 605.02 414.44 605.20 414.62 c -605.37 414.80 605.59 414.88 605.85 414.88 c -606.29 414.88 606.74 414.67 607.20 414.24 c -h -611.43 416.00 m -611.43 409.03 l -613.30 409.03 l -613.30 410.34 l -613.79 409.36 614.53 408.87 615.53 408.87 c -615.64 408.87 615.76 408.88 615.88 408.91 c -615.88 410.59 l -615.61 410.49 615.36 410.44 615.13 410.44 c -614.38 410.44 613.77 410.82 613.30 411.58 c -613.30 416.00 l -h -623.01 415.77 m -622.12 416.03 621.27 416.16 620.47 416.16 c -619.31 416.16 618.39 415.83 617.72 415.17 c -617.04 414.51 616.71 413.61 616.71 412.46 c -616.71 411.39 617.01 410.52 617.63 409.86 c -618.25 409.20 619.06 408.87 620.07 408.87 c -621.09 408.87 621.84 409.19 622.31 409.84 c -622.78 410.48 623.01 411.50 623.01 412.89 c -618.69 412.89 l -618.81 414.22 619.54 414.88 620.88 414.88 c -621.51 414.88 622.22 414.74 623.01 414.44 c -h -618.66 411.83 m -621.16 411.83 l -621.16 410.64 620.78 410.05 620.01 410.05 c -619.24 410.05 618.78 410.64 618.66 411.83 c -h -f -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -685.00 506.00 m -804.00 506.00 l -804.00 523.00 l -685.00 523.00 l -685.00 506.00 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -685.50 506.50 m -963.50 506.50 l -963.50 623.50 l -685.50 623.50 l -685.50 506.50 l -h -S -685.50 523.50 m -803.50 523.50 l -S -803.50 523.50 m -805.50 519.50 l -S -805.50 519.50 m -805.50 506.50 l -S -689.17 522.51 m -689.17 513.03 l -691.05 513.03 l -691.05 514.34 l -691.54 513.36 692.30 512.87 693.33 512.87 c -694.17 512.87 694.83 513.18 695.31 513.79 c -695.79 514.41 696.03 515.25 696.03 516.32 c -696.03 517.49 695.76 518.42 695.22 519.12 c -694.68 519.81 693.95 520.16 693.04 520.16 c -692.31 520.16 691.65 519.87 691.05 519.30 c -691.05 522.51 l -h -691.05 518.18 m -691.51 518.69 692.00 518.94 692.51 518.94 c -692.97 518.94 693.34 518.72 693.62 518.27 c -693.89 517.82 694.03 517.23 694.03 516.48 c -694.03 515.06 693.57 514.34 692.65 514.34 c -692.10 514.34 691.57 514.70 691.05 515.42 c -h -700.71 520.16 m -699.63 520.16 698.76 519.83 698.12 519.17 c -697.48 518.51 697.16 517.63 697.16 516.52 c -697.16 515.39 697.48 514.50 698.13 513.85 c -698.77 513.20 699.65 512.87 700.76 512.87 c -701.87 512.87 702.75 513.20 703.40 513.85 c -704.04 514.50 704.36 515.39 704.36 516.50 c -704.36 517.65 704.04 518.54 703.39 519.19 c -702.75 519.83 701.85 520.16 700.71 520.16 c -h -700.75 518.98 m -701.83 518.98 702.36 518.16 702.36 516.50 c -702.36 515.74 702.22 515.15 701.94 514.71 c -701.65 514.27 701.26 514.05 700.76 514.05 c -700.26 514.05 699.87 514.27 699.59 514.71 c -699.30 515.15 699.16 515.75 699.16 516.52 c -699.16 517.27 699.30 517.87 699.58 518.32 c -699.87 518.76 700.26 518.98 700.75 518.98 c -h -706.09 520.00 m -706.09 509.98 l -707.97 509.98 l -707.97 520.00 l -h -710.32 520.00 m -710.32 513.03 l -712.20 513.03 l -712.20 520.00 l -h -710.32 511.86 m -710.32 510.29 l -712.20 510.29 l -712.20 511.86 l -h -719.69 519.85 m -718.91 520.06 718.19 520.16 717.52 520.16 c -716.41 520.16 715.53 519.83 714.89 519.18 c -714.24 518.52 713.92 517.63 713.92 516.51 c -713.92 515.37 714.25 514.48 714.91 513.84 c -715.58 513.19 716.50 512.87 717.67 512.87 c -718.24 512.87 718.89 512.96 719.63 513.14 c -719.63 514.50 l -718.86 514.25 718.24 514.13 717.78 514.13 c -717.22 514.13 716.77 514.34 716.42 514.78 c -716.08 515.21 715.91 515.78 715.91 516.50 c -715.91 517.23 716.10 517.81 716.47 518.25 c -716.84 518.69 717.33 518.91 717.95 518.91 c -718.51 518.91 719.09 518.79 719.69 518.55 c -h -721.49 522.51 m -723.07 520.00 l -720.49 513.03 l -722.51 513.03 l -724.23 517.69 l -726.28 513.03 l -727.71 513.03 l -723.41 522.51 l -h -727.87 522.27 m -727.87 521.25 l -734.37 521.25 l -734.37 522.27 l -h -735.54 520.00 m -735.54 513.03 l -737.35 513.03 l -737.35 514.34 l -737.90 513.36 738.68 512.87 739.70 512.87 c -740.22 512.87 740.65 513.00 740.99 513.26 c -741.33 513.52 741.54 513.88 741.61 514.34 c -742.26 513.36 743.05 512.87 743.97 512.87 c -745.25 512.87 745.88 513.57 745.88 514.98 c -745.88 520.00 l -744.08 520.00 l -744.08 515.59 l -744.08 514.77 743.81 514.36 743.26 514.36 c -742.69 514.36 742.15 514.76 741.61 515.58 c -741.61 520.00 l -739.81 520.00 l -739.81 515.59 l -739.81 514.77 739.53 514.35 738.98 514.35 c -738.42 514.35 737.88 514.76 737.35 515.58 c -737.35 520.00 l -h -748.15 520.00 m -748.15 513.03 l -750.03 513.03 l -750.03 520.00 l -h -748.15 511.86 m -748.15 510.29 l -750.03 510.29 l -750.03 511.86 l -h -756.76 520.00 m -756.76 518.69 l -756.28 519.67 755.52 520.16 754.49 520.16 c -753.65 520.16 752.99 519.85 752.51 519.24 c -752.03 518.62 751.79 517.78 751.79 516.71 c -751.79 515.54 752.06 514.61 752.60 513.91 c -753.14 513.22 753.87 512.87 754.78 512.87 c -755.51 512.87 756.17 513.16 756.76 513.73 c -756.76 509.98 l -758.65 509.98 l -758.65 520.00 l -h -756.76 514.85 m -756.31 514.34 755.83 514.09 755.31 514.09 c -754.85 514.09 754.48 514.31 754.21 514.76 c -753.93 515.20 753.79 515.80 753.79 516.55 c -753.79 517.97 754.25 518.69 755.16 518.69 c -755.72 518.69 756.26 518.33 756.76 517.61 c -h -765.38 520.00 m -765.38 518.69 l -764.90 519.67 764.14 520.16 763.10 520.16 c -762.26 520.16 761.60 519.85 761.12 519.24 c -760.65 518.62 760.41 517.78 760.41 516.71 c -760.41 515.54 760.68 514.61 761.22 513.91 c -761.76 513.22 762.48 512.87 763.39 512.87 c -764.12 512.87 764.78 513.16 765.38 513.73 c -765.38 509.98 l -767.26 509.98 l -767.26 520.00 l -h -765.38 514.85 m -764.93 514.34 764.44 514.09 763.92 514.09 c -763.46 514.09 763.09 514.31 762.82 514.76 c -762.54 515.20 762.41 515.80 762.41 516.55 c -762.41 517.97 762.86 518.69 763.78 518.69 c -764.34 518.69 764.87 518.33 765.38 517.61 c -h -769.61 520.00 m -769.61 509.98 l -771.48 509.98 l -771.48 520.00 l -h -779.51 519.77 m -778.61 520.03 777.77 520.16 776.97 520.16 c -775.81 520.16 774.89 519.83 774.21 519.17 c -773.54 518.51 773.20 517.61 773.20 516.46 c -773.20 515.39 773.51 514.52 774.13 513.86 c -774.74 513.20 775.56 512.87 776.57 512.87 c -777.59 512.87 778.33 513.19 778.80 513.84 c -779.27 514.48 779.51 515.50 779.51 516.89 c -775.19 516.89 l -775.31 518.22 776.04 518.88 777.38 518.88 c -778.01 518.88 778.72 518.74 779.51 518.44 c -h -775.16 515.83 m -777.66 515.83 l -777.66 514.64 777.28 514.05 776.51 514.05 c -775.73 514.05 775.28 514.64 775.16 515.83 c -h -782.68 520.00 m -780.75 513.03 l -782.49 513.03 l -783.86 517.91 l -785.32 513.03 l -786.95 513.03 l -788.26 517.94 l -789.74 513.03 l -791.05 513.03 l -788.98 520.00 l -787.18 520.00 l -785.91 515.22 l -784.48 520.00 l -h -796.08 519.25 m -795.46 519.86 794.78 520.16 794.07 520.16 c -793.46 520.16 792.96 519.97 792.58 519.60 c -792.20 519.23 792.01 518.75 792.01 518.15 c -792.01 517.38 792.32 516.79 792.94 516.37 c -793.55 515.96 794.43 515.75 795.58 515.75 c -796.08 515.75 l -796.08 515.11 l -796.08 514.39 795.67 514.03 794.84 514.03 c -794.11 514.03 793.37 514.23 792.62 514.65 c -792.62 513.35 l -793.47 513.03 794.31 512.87 795.15 512.87 c -796.97 512.87 797.88 513.60 797.88 515.05 c -797.88 518.13 l -797.88 518.68 798.06 518.95 798.41 518.95 c -798.47 518.95 798.56 518.94 798.66 518.93 c -798.70 519.98 l -798.31 520.10 797.95 520.16 797.65 520.16 c -796.88 520.16 796.38 519.86 796.16 519.25 c -h -796.08 518.24 m -796.08 516.83 l -795.64 516.83 l -794.42 516.83 793.82 517.21 793.82 517.97 c -793.82 518.23 793.90 518.44 794.08 518.62 c -794.25 518.80 794.47 518.88 794.73 518.88 c -795.17 518.88 795.62 518.67 796.08 518.24 c -h -800.31 520.00 m -800.31 513.03 l -802.19 513.03 l -802.19 514.34 l -802.67 513.36 803.42 512.87 804.41 512.87 c -804.53 512.87 804.64 512.88 804.76 512.91 c -804.76 514.59 l -804.49 514.49 804.24 514.44 804.02 514.44 c -803.27 514.44 802.66 514.82 802.19 515.58 c -802.19 520.00 l -h -811.89 519.77 m -811.00 520.03 810.15 520.16 809.35 520.16 c -808.19 520.16 807.27 519.83 806.60 519.17 c -805.93 518.51 805.59 517.61 805.59 516.46 c -805.59 515.39 805.90 514.52 806.51 513.86 c -807.13 513.20 807.94 512.87 808.95 512.87 c -809.97 512.87 810.72 513.19 811.19 513.84 c -811.66 514.48 811.89 515.50 811.89 516.89 c -807.57 516.89 l -807.70 518.22 808.43 518.88 809.76 518.88 c -810.39 518.88 811.10 518.74 811.89 518.44 c -h -807.55 515.83 m -810.05 515.83 l -810.05 514.64 809.66 514.05 808.90 514.05 c -808.12 514.05 807.67 514.64 807.55 515.83 c -h -f -Q -Q -Q - -endstream -endobj - -8 0 obj - 255399 -endobj - -3 0 obj - << - /Parent null - /Type /Pages - /MediaBox [0.0000 0.0000 842.00 595.00] - /Resources 9 0 R - /Kids [6 0 R] - /Count 1 - >> -endobj - -10 0 obj - [/PDF /Text /ImageC] -endobj - -11 0 obj - << - /Alpha1 - << - /ca 1.0000 - /CA 1.0000 - /BM /Normal - /AIS false - >> - >> -endobj - -9 0 obj - << - /ProcSet 10 0 R - /ExtGState 11 0 R - >> -endobj - -4 0 obj - << - /Type /Outlines - /First 12 0 R - /Last 12 0 R - >> -endobj - -12 0 obj - << - /Parent 4 0 R - /Title (Page 1 \(untitled\)) - /Prev null - /Next null - /Dest [6 0 R /Fit] - >> -endobj - -xref -0 13 -0000000000 65535 f -0000000016 00000 n -0000000343 00000 n -0000256183 00000 n -0000256614 00000 n -0000000525 00000 n -0000000602 00000 n -0000000691 00000 n -0000256157 00000 n -0000256539 00000 n -0000256354 00000 n -0000256395 00000 n -0000256704 00000 n - -trailer -<< - /Size 12 - /Root 2 0 R - /Info 1 0 R ->> - -startxref -256848 - -%%EOF diff --git a/doc/design/flow_diagram.png b/doc/design/flow_diagram.png deleted file mode 100644 index 88faf8d992..0000000000 Binary files a/doc/design/flow_diagram.png and /dev/null differ diff --git a/doc/design/flow_diagram.sdx b/doc/design/flow_diagram.sdx deleted file mode 100644 index 68cccef8c8..0000000000 --- a/doc/design/flow_diagram.sdx +++ /dev/null @@ -1,87 +0,0 @@ - - - -[/c] - -[c:get_tenants] -client:tenants=keystone.get_tenants -[/c] - -[c:tenant_auth_token] -client:token, serviceCatalog=keystone.auth -[/c] - -[c:do_something_in_compute] -client:endpoint=serviceCatalog['compute'] -client:success=compute.do_something - -compute:tenant = parse(url) -[c:auth_middleware] -compute:user, roles=keystone.validate -[/c] -compute:instance=instance_get(instance_id) -[c:policy_middleware] -compute:action='do_something' -compute:target=instance -compute:success=policy.check_acl -[/c] -compute:execute something logic -[/c]]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/design/tenants.pdf b/doc/design/tenants.pdf deleted file mode 100644 index 64d38f1ca4..0000000000 Binary files a/doc/design/tenants.pdf and /dev/null differ diff --git a/doc/design/use_case_1.pdf b/doc/design/use_case_1.pdf deleted file mode 100644 index f0926fd531..0000000000 --- a/doc/design/use_case_1.pdf +++ /dev/null @@ -1,8183 +0,0 @@ -%PDF-1.4 -%âãÏÓ - -1 0 obj - << - /Title () - /Author () - /Subject () - /Keywords () - /Creator (FreeHEP Graphics2D Driver) - /Producer (org.freehep.graphicsio.pdf.PDFGraphics2D Revision: 10516 ) - /CreationDate (D:20111117211837-06'00') - /ModDate (D:20111117211837-06'00') - /Trapped /False - >> -endobj - -2 0 obj - << - /Type /Catalog - /Pages 3 0 R - /Outlines 4 0 R - /PageMode /UseOutlines - /ViewerPreferences 5 0 R - /OpenAction [6 0 R /Fit] - >> -endobj - -5 0 obj - << - /FitWindow true - /CenterWindow false - >> -endobj - -6 0 obj - << - /Parent 3 0 R - /Type /Page - /Contents 7 0 R - >> -endobj - -7 0 obj - << - /Length 8 0 R - >> -stream -.93277 0.0000 0.0000 -.93277 28.303 575.00 cm -q -0.0000 0.0000 m -842.00 0.0000 l -842.00 595.00 l -0.0000 595.00 l -h -W -n -q -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -1.0000 0.0000 0.0000 1.0000 0.0000 0.0000 cm -Q -q -1.0000 0.0000 0.0000 1.0000 0.0000 0.0000 cm -0.0000 0.0000 m -0.0000 595.00 l -842.00 595.00 l -842.00 0.0000 l -h -W -n -q -.73924 0.0000 0.0000 .73924 0.0000 0.0000 cm -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -0.0000 0.0000 m -1139.0 0.0000 l -1139.0 805.00 l -0.0000 805.00 l -0.0000 0.0000 l -h -f -1.0000 0.0000 0.0000 1.0000 0.0000 0.0000 cm -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -0 J -1 j -1.0000 M -[ 1.0000 2.0000] 0.0000 d -66.500 44.500 m -66.500 54.500 l -S -66.500 44.500 m -66.500 54.500 l -S -[] 0.0000 d -/Alpha1 gs -.93333 .93333 .93333 rg -.93333 .93333 .93333 RG -62.000 54.000 m -70.000 54.000 l -70.000 574.00 l -62.000 574.00 l -62.000 54.000 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -62.500 54.500 m -70.500 54.500 l -70.500 574.50 l -62.500 574.50 l -62.500 54.500 l -h -S -[ 1.0000 2.0000] 0.0000 d -66.500 574.50 m -66.500 574.50 l -S -553.50 44.500 m -553.50 102.50 l -S -553.50 44.500 m -553.50 102.50 l -S -[] 0.0000 d -/Alpha1 gs -.93333 .93333 .93333 rg -.93333 .93333 .93333 RG -549.00 102.00 m -557.00 102.00 l -557.00 120.00 l -549.00 120.00 l -549.00 102.00 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -549.50 102.50 m -557.50 102.50 l -557.50 120.50 l -549.50 120.50 l -549.50 102.50 l -h -S -[ 1.0000 2.0000] 0.0000 d -553.50 120.50 m -553.50 186.50 l -S -[] 0.0000 d -/Alpha1 gs -.93333 .93333 .93333 rg -.93333 .93333 .93333 RG -549.00 186.00 m -557.00 186.00 l -557.00 204.00 l -549.00 204.00 l -549.00 186.00 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -549.50 186.50 m -557.50 186.50 l -557.50 204.50 l -549.50 204.50 l -549.50 186.50 l -h -S -[ 1.0000 2.0000] 0.0000 d -553.50 204.50 m -553.50 270.50 l -S -[] 0.0000 d -/Alpha1 gs -.93333 .93333 .93333 rg -.93333 .93333 .93333 RG -549.00 270.00 m -557.00 270.00 l -557.00 288.00 l -549.00 288.00 l -549.00 270.00 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -549.50 270.50 m -557.50 270.50 l -557.50 288.50 l -549.50 288.50 l -549.50 270.50 l -h -S -[ 1.0000 2.0000] 0.0000 d -553.50 288.50 m -553.50 442.50 l -S -[] 0.0000 d -/Alpha1 gs -.93333 .93333 .93333 rg -.93333 .93333 .93333 RG -549.00 442.00 m -557.00 442.00 l -557.00 460.00 l -549.00 460.00 l -549.00 442.00 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -549.50 442.50 m -557.50 442.50 l -557.50 460.50 l -549.50 460.50 l -549.50 442.50 l -h -S -[ 1.0000 2.0000] 0.0000 d -553.50 460.50 m -553.50 574.50 l -S -732.50 44.500 m -732.50 374.50 l -S -732.50 44.500 m -732.50 374.50 l -S -[] 0.0000 d -/Alpha1 gs -.93333 .93333 .93333 rg -.93333 .93333 .93333 RG -728.00 374.00 m -736.00 374.00 l -736.00 536.00 l -728.00 536.00 l -728.00 374.00 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -728.50 374.50 m -736.50 374.50 l -736.50 536.50 l -728.50 536.50 l -728.50 374.50 l -h -S -[ 1.0000 2.0000] 0.0000 d -732.50 536.50 m -732.50 574.50 l -S -80.436 266.19 m -79.744 266.83 79.078 267.15 78.438 267.15 c -77.910 267.15 77.473 266.98 77.125 266.65 c -76.777 266.32 76.604 265.90 76.604 265.40 c -76.604 264.71 76.896 264.17 77.479 263.80 c -78.063 263.42 78.900 263.24 79.990 263.24 c -80.266 263.24 l -80.266 262.47 l -80.266 261.73 79.887 261.36 79.129 261.36 c -78.520 261.36 77.861 261.55 77.154 261.93 c -77.154 260.97 l -77.932 260.65 78.660 260.50 79.340 260.50 c -80.051 260.50 80.575 260.66 80.913 260.98 c -81.251 261.30 81.420 261.79 81.420 262.47 c -81.420 265.35 l -81.420 266.01 81.623 266.34 82.029 266.34 c -82.080 266.34 82.154 266.34 82.252 266.32 c -82.334 266.96 l -82.072 267.08 81.783 267.15 81.467 267.15 c -80.928 267.15 80.584 266.83 80.436 266.19 c -h -80.266 265.56 m -80.266 263.92 l -79.879 263.91 l -79.246 263.91 78.734 264.03 78.344 264.27 c -77.953 264.51 77.758 264.82 77.758 265.21 c -77.758 265.49 77.855 265.72 78.051 265.92 c -78.246 266.11 78.484 266.20 78.766 266.20 c -79.246 266.20 79.746 265.99 80.266 265.56 c -h -87.760 267.00 m -87.760 265.80 l -87.146 266.70 86.402 267.15 85.527 267.15 c -84.973 267.15 84.531 266.97 84.203 266.62 c -83.875 266.27 83.711 265.80 83.711 265.21 c -83.711 260.64 l -84.865 260.64 l -84.865 264.83 l -84.865 265.31 84.935 265.65 85.073 265.85 c -85.212 266.05 85.443 266.15 85.768 266.15 c -86.471 266.15 87.135 265.69 87.760 264.76 c -87.760 260.64 l -88.914 260.64 l -88.914 267.00 l -h -93.139 267.15 m -92.553 267.15 92.096 266.98 91.768 266.64 c -91.439 266.31 91.275 265.84 91.275 265.24 c -91.275 261.50 l -90.479 261.50 l -90.479 260.64 l -91.275 260.64 l -91.275 259.48 l -92.430 259.37 l -92.430 260.64 l -94.094 260.64 l -94.094 261.50 l -92.430 261.50 l -92.430 265.03 l -92.430 265.86 92.789 266.28 93.508 266.28 c -93.660 266.28 93.846 266.25 94.064 266.20 c -94.064 267.00 l -93.709 267.10 93.400 267.15 93.139 267.15 c -h -95.717 267.00 m -95.717 257.75 l -96.871 257.75 l -96.871 261.83 l -97.480 260.94 98.227 260.50 99.109 260.50 c -99.660 260.50 100.10 260.67 100.43 261.02 c -100.76 261.37 100.92 261.84 100.92 262.43 c -100.92 267.00 l -99.766 267.00 l -99.766 262.80 l -99.766 262.33 99.696 262.00 99.558 261.79 c -99.419 261.59 99.189 261.49 98.869 261.49 c -98.162 261.49 97.496 261.96 96.871 262.88 c -96.871 267.00 l -h -110.25 267.00 m -103.31 263.53 l -110.25 260.06 l -110.25 261.03 l -105.25 263.53 l -110.25 266.03 l -h -116.68 267.00 m -116.68 265.80 l -116.07 266.70 115.32 267.15 114.45 267.15 c -113.89 267.15 113.45 266.97 113.12 266.62 c -112.80 266.27 112.63 265.80 112.63 265.21 c -112.63 260.64 l -113.79 260.64 l -113.79 264.83 l -113.79 265.31 113.86 265.65 114.00 265.85 c -114.13 266.05 114.37 266.15 114.69 266.15 c -115.39 266.15 116.06 265.69 116.68 264.76 c -116.68 260.64 l -117.84 260.64 l -117.84 267.00 l -h -120.15 267.00 m -120.15 260.64 l -121.30 260.64 l -121.30 261.83 l -121.91 260.94 122.66 260.50 123.54 260.50 c -124.09 260.50 124.53 260.67 124.86 261.02 c -125.19 261.37 125.35 261.84 125.35 262.43 c -125.35 267.00 l -124.20 267.00 l -124.20 262.80 l -124.20 262.33 124.13 262.00 123.99 261.79 c -123.85 261.59 123.62 261.49 123.30 261.49 c -122.60 261.49 121.93 261.96 121.30 262.88 c -121.30 267.00 l -h -129.29 267.15 m -128.76 267.15 128.12 267.02 127.36 266.78 c -127.36 265.72 l -128.12 266.09 128.77 266.28 129.33 266.28 c -129.66 266.28 129.94 266.19 130.16 266.01 c -130.38 265.83 130.49 265.61 130.49 265.34 c -130.49 264.94 130.18 264.62 129.57 264.36 c -128.89 264.07 l -127.90 263.66 127.40 263.06 127.40 262.28 c -127.40 261.73 127.59 261.29 127.99 260.97 c -128.38 260.66 128.92 260.50 129.60 260.50 c -129.96 260.50 130.40 260.54 130.92 260.64 c -131.16 260.69 l -131.16 261.65 l -130.52 261.46 130.00 261.36 129.62 261.36 c -128.88 261.36 128.51 261.63 128.51 262.17 c -128.51 262.52 128.79 262.81 129.36 263.05 c -129.91 263.29 l -130.54 263.55 130.99 263.83 131.25 264.13 c -131.51 264.42 131.64 264.79 131.64 265.23 c -131.64 265.79 131.42 266.25 130.98 266.61 c -130.54 266.97 129.97 267.15 129.29 267.15 c -h -136.20 267.15 m -135.34 267.15 134.63 266.83 134.06 266.19 c -133.49 265.55 133.21 264.75 133.21 263.78 c -133.21 262.75 133.49 261.94 134.05 261.36 c -134.61 260.79 135.39 260.50 136.40 260.50 c -136.89 260.50 137.45 260.56 138.06 260.70 c -138.06 261.67 l -137.41 261.48 136.88 261.38 136.47 261.38 c -135.88 261.38 135.41 261.60 135.05 262.05 c -134.69 262.49 134.51 263.08 134.51 263.82 c -134.51 264.53 134.70 265.11 135.06 265.55 c -135.43 265.99 135.91 266.21 136.50 266.21 c -137.03 266.21 137.57 266.08 138.13 265.81 c -138.13 266.81 l -137.39 267.03 136.74 267.15 136.20 267.15 c -h -142.35 267.15 m -141.44 267.15 140.71 266.84 140.17 266.24 c -139.63 265.64 139.36 264.83 139.36 263.82 c -139.36 262.79 139.63 261.99 140.17 261.39 c -140.72 260.79 141.46 260.50 142.39 260.50 c -143.33 260.50 144.07 260.79 144.61 261.39 c -145.16 261.99 145.43 262.79 145.43 263.81 c -145.43 264.85 145.15 265.66 144.61 266.26 c -144.06 266.85 143.31 267.15 142.35 267.15 c -h -142.37 266.28 m -143.59 266.28 144.20 265.46 144.20 263.81 c -144.20 262.18 143.60 261.36 142.39 261.36 c -141.19 261.36 140.59 262.18 140.59 263.82 c -140.59 265.46 141.18 266.28 142.37 266.28 c -h -147.23 269.31 m -147.23 260.64 l -148.39 260.64 l -148.39 261.83 l -148.86 260.94 149.57 260.50 150.51 260.50 c -151.28 260.50 151.88 260.78 152.32 261.33 c -152.76 261.89 152.98 262.66 152.98 263.62 c -152.98 264.68 152.73 265.53 152.23 266.18 c -151.74 266.82 151.08 267.15 150.27 267.15 c -149.51 267.15 148.89 266.86 148.39 266.28 c -148.39 269.31 l -h -148.39 265.48 m -148.98 266.01 149.55 266.28 150.09 266.28 c -151.20 266.28 151.75 265.43 151.75 263.74 c -151.75 262.25 151.26 261.50 150.27 261.50 c -149.63 261.50 149.00 261.85 148.39 262.55 c -h -159.45 266.79 m -158.68 267.03 158.01 267.15 157.46 267.15 c -156.53 267.15 155.76 266.83 155.17 266.21 c -154.58 265.59 154.28 264.78 154.28 263.79 c -154.28 262.82 154.54 262.03 155.06 261.42 c -155.58 260.80 156.25 260.49 157.06 260.49 c -157.83 260.49 158.43 260.76 158.85 261.31 c -159.27 261.86 159.48 262.63 159.48 263.64 c -159.47 264.00 l -155.46 264.00 l -155.63 265.51 156.37 266.27 157.68 266.27 c -158.16 266.27 158.75 266.14 159.45 265.88 c -h -155.51 263.13 m -158.32 263.13 l -158.32 261.95 157.88 261.36 156.99 261.36 c -156.11 261.36 155.61 261.95 155.51 263.13 c -h -165.55 267.00 m -165.55 265.80 l -165.09 266.70 164.38 267.15 163.43 267.15 c -162.67 267.15 162.07 266.87 161.63 266.31 c -161.19 265.75 160.97 264.99 160.97 264.02 c -160.97 262.96 161.22 262.11 161.71 261.46 c -162.21 260.82 162.87 260.50 163.68 260.50 c -164.43 260.50 165.06 260.79 165.55 261.36 c -165.55 257.75 l -166.71 257.75 l -166.71 267.00 l -h -165.55 262.15 m -164.96 261.63 164.39 261.36 163.86 261.36 c -162.75 261.36 162.20 262.21 162.20 263.90 c -162.20 265.39 162.69 266.13 163.67 266.13 c -164.31 266.13 164.94 265.78 165.55 265.08 c -h -167.87 269.02 m -167.87 268.15 l -173.87 268.15 l -173.87 269.02 l -h -176.93 267.15 m -176.35 267.15 175.89 266.98 175.56 266.64 c -175.23 266.31 175.07 265.84 175.07 265.24 c -175.07 261.50 l -174.27 261.50 l -174.27 260.64 l -175.07 260.64 l -175.07 259.48 l -176.22 259.37 l -176.22 260.64 l -177.89 260.64 l -177.89 261.50 l -176.22 261.50 l -176.22 265.03 l -176.22 265.86 176.58 266.28 177.30 266.28 c -177.46 266.28 177.64 266.25 177.86 266.20 c -177.86 267.00 l -177.50 267.10 177.20 267.15 176.93 267.15 c -h -182.00 267.15 m -181.09 267.15 180.37 266.84 179.82 266.24 c -179.28 265.64 179.01 264.83 179.01 263.82 c -179.01 262.79 179.28 261.99 179.83 261.39 c -180.37 260.79 181.11 260.50 182.04 260.50 c -182.98 260.50 183.72 260.79 184.26 261.39 c -184.81 261.99 185.08 262.79 185.08 263.81 c -185.08 264.85 184.80 265.66 184.26 266.26 c -183.71 266.85 182.96 267.15 182.00 267.15 c -h -182.02 266.28 m -183.24 266.28 183.85 265.46 183.85 263.81 c -183.85 262.18 183.25 261.36 182.04 261.36 c -180.84 261.36 180.24 262.18 180.24 263.82 c -180.24 265.46 180.83 266.28 182.02 266.28 c -h -186.88 267.00 m -186.88 257.75 l -188.04 257.75 l -188.04 263.72 l -190.73 260.64 l -191.97 260.64 l -189.40 263.61 l -192.51 267.00 l -191.03 267.00 l -188.04 263.74 l -188.04 267.00 l -h -198.56 266.79 m -197.79 267.03 197.12 267.15 196.57 267.15 c -195.64 267.15 194.87 266.83 194.28 266.21 c -193.69 265.59 193.39 264.78 193.39 263.79 c -193.39 262.82 193.65 262.03 194.17 261.42 c -194.70 260.80 195.36 260.49 196.18 260.49 c -196.95 260.49 197.54 260.76 197.96 261.31 c -198.38 261.86 198.59 262.63 198.59 263.64 c -198.58 264.00 l -194.57 264.00 l -194.74 265.51 195.48 266.27 196.79 266.27 c -197.27 266.27 197.86 266.14 198.56 265.88 c -h -194.62 263.13 m -197.43 263.13 l -197.43 261.95 196.99 261.36 196.11 261.36 c -195.22 261.36 194.72 261.95 194.62 263.13 c -h -200.58 267.00 m -200.58 260.64 l -201.74 260.64 l -201.74 261.83 l -202.35 260.94 203.09 260.50 203.97 260.50 c -204.53 260.50 204.96 260.67 205.29 261.02 c -205.62 261.37 205.79 261.84 205.79 262.43 c -205.79 267.00 l -204.63 267.00 l -204.63 262.80 l -204.63 262.33 204.56 262.00 204.42 261.79 c -204.28 261.59 204.05 261.49 203.73 261.49 c -203.03 261.49 202.36 261.96 201.74 262.88 c -201.74 267.00 l -h -208.05 268.88 m -208.05 268.45 l -208.42 268.34 208.61 267.90 208.61 267.12 c -208.61 267.00 l -208.05 267.00 l -208.05 265.55 l -209.49 265.55 l -209.49 266.81 l -209.49 268.09 209.01 268.78 208.05 268.88 c -h -217.53 267.15 m -216.95 267.15 216.49 266.98 216.16 266.64 c -215.83 266.31 215.67 265.84 215.67 265.24 c -215.67 261.50 l -214.87 261.50 l -214.87 260.64 l -215.67 260.64 l -215.67 259.48 l -216.82 259.37 l -216.82 260.64 l -218.49 260.64 l -218.49 261.50 l -216.82 261.50 l -216.82 265.03 l -216.82 265.86 217.18 266.28 217.90 266.28 c -218.05 266.28 218.24 266.25 218.46 266.20 c -218.46 267.00 l -218.10 267.10 217.79 267.15 217.53 267.15 c -h -224.78 266.79 m -224.00 267.03 223.34 267.15 222.79 267.15 c -221.85 267.15 221.09 266.83 220.50 266.21 c -219.90 265.59 219.61 264.78 219.61 263.79 c -219.61 262.82 219.87 262.03 220.39 261.42 c -220.91 260.80 221.58 260.49 222.39 260.49 c -223.16 260.49 223.75 260.76 224.17 261.31 c -224.59 261.86 224.80 262.63 224.80 263.64 c -224.80 264.00 l -220.79 264.00 l -220.95 265.51 221.69 266.27 223.01 266.27 c -223.49 266.27 224.08 266.14 224.78 265.88 c -h -220.84 263.13 m -223.64 263.13 l -223.64 261.95 223.20 261.36 222.32 261.36 c -221.43 261.36 220.94 261.95 220.84 263.13 c -h -226.80 267.00 m -226.80 260.64 l -227.95 260.64 l -227.95 261.83 l -228.56 260.94 229.31 260.50 230.19 260.50 c -230.74 260.50 231.18 260.67 231.51 261.02 c -231.84 261.37 232.00 261.84 232.00 262.43 c -232.00 267.00 l -230.85 267.00 l -230.85 262.80 l -230.85 262.33 230.78 262.00 230.64 261.79 c -230.50 261.59 230.27 261.49 229.95 261.49 c -229.24 261.49 228.58 261.96 227.95 262.88 c -227.95 267.00 l -h -237.53 266.19 m -236.83 266.83 236.17 267.15 235.53 267.15 c -235.00 267.15 234.56 266.98 234.21 266.65 c -233.87 266.32 233.69 265.90 233.69 265.40 c -233.69 264.71 233.99 264.17 234.57 263.80 c -235.15 263.42 235.99 263.24 237.08 263.24 c -237.36 263.24 l -237.36 262.47 l -237.36 261.73 236.98 261.36 236.22 261.36 c -235.61 261.36 234.95 261.55 234.24 261.93 c -234.24 260.97 l -235.02 260.65 235.75 260.50 236.43 260.50 c -237.14 260.50 237.67 260.66 238.00 260.98 c -238.34 261.30 238.51 261.79 238.51 262.47 c -238.51 265.35 l -238.51 266.01 238.71 266.34 239.12 266.34 c -239.17 266.34 239.24 266.34 239.34 266.32 c -239.42 266.96 l -239.16 267.08 238.87 267.15 238.56 267.15 c -238.02 267.15 237.67 266.83 237.53 266.19 c -h -237.36 265.56 m -237.36 263.92 l -236.97 263.91 l -236.34 263.91 235.82 264.03 235.43 264.27 c -235.04 264.51 234.85 264.82 234.85 265.21 c -234.85 265.49 234.95 265.72 235.14 265.92 c -235.34 266.11 235.57 266.20 235.86 266.20 c -236.34 266.20 236.84 265.99 237.36 265.56 c -h -240.87 267.00 m -240.87 260.64 l -242.03 260.64 l -242.03 261.83 l -242.63 260.94 243.38 260.50 244.26 260.50 c -244.81 260.50 245.25 260.67 245.58 261.02 c -245.91 261.37 246.07 261.84 246.07 262.43 c -246.07 267.00 l -244.92 267.00 l -244.92 262.80 l -244.92 262.33 244.85 262.00 244.71 261.79 c -244.57 261.59 244.34 261.49 244.02 261.49 c -243.32 261.49 242.65 261.96 242.03 262.88 c -242.03 267.00 l -h -250.23 267.15 m -249.64 267.15 249.19 266.98 248.86 266.64 c -248.53 266.31 248.37 265.84 248.37 265.24 c -248.37 261.50 l -247.57 261.50 l -247.57 260.64 l -248.37 260.64 l -248.37 259.48 l -249.52 259.37 l -249.52 260.64 l -251.18 260.64 l -251.18 261.50 l -249.52 261.50 l -249.52 265.03 l -249.52 265.86 249.88 266.28 250.60 266.28 c -250.75 266.28 250.94 266.25 251.15 266.20 c -251.15 267.00 l -250.80 267.10 250.49 267.15 250.23 267.15 c -h -252.95 267.00 m -259.89 263.53 l -252.95 260.06 l -252.95 261.03 l -257.95 263.53 l -252.95 266.03 l -h -f -[] 0.0000 d -70.500 270.50 m -549.50 270.50 l -S -549.00 270.00 m -543.00 264.00 l -543.00 276.00 l -h -f* -77.178 185.12 m -77.312 184.11 l -77.980 184.43 78.639 184.59 79.287 184.59 c -80.588 184.59 81.238 183.90 81.238 182.52 c -81.238 181.52 l -80.812 182.41 80.113 182.85 79.141 182.85 c -78.379 182.85 77.773 182.58 77.324 182.02 c -76.875 181.47 76.650 180.72 76.650 179.78 c -76.650 178.81 76.906 178.02 77.418 177.41 c -77.930 176.80 78.592 176.50 79.404 176.50 c -80.115 176.50 80.727 176.79 81.238 177.36 c -81.238 176.64 l -82.398 176.64 l -82.398 181.27 l -82.398 182.26 82.347 183.00 82.243 183.48 c -82.140 183.96 81.945 184.35 81.660 184.65 c -81.156 185.19 80.373 185.46 79.311 185.46 c -78.568 185.46 77.857 185.34 77.178 185.12 c -h -81.238 180.80 m -81.238 178.15 l -80.730 177.63 80.178 177.36 79.580 177.36 c -79.049 177.36 78.633 177.58 78.332 178.00 c -78.031 178.43 77.881 179.01 77.881 179.75 c -77.881 181.15 78.371 181.85 79.352 181.85 c -80.020 181.85 80.648 181.50 81.238 180.80 c -h -89.301 182.79 m -88.527 183.03 87.865 183.15 87.314 183.15 c -86.377 183.15 85.612 182.83 85.021 182.21 c -84.429 181.59 84.133 180.78 84.133 179.79 c -84.133 178.82 84.394 178.03 84.915 177.42 c -85.437 176.80 86.104 176.49 86.916 176.49 c -87.686 176.49 88.280 176.76 88.700 177.31 c -89.120 177.86 89.330 178.63 89.330 179.64 c -89.324 180.00 l -85.311 180.00 l -85.479 181.51 86.219 182.27 87.531 182.27 c -88.012 182.27 88.602 182.14 89.301 181.88 c -h -85.363 179.13 m -88.170 179.13 l -88.170 177.95 87.729 177.36 86.846 177.36 c -85.959 177.36 85.465 177.95 85.363 179.13 c -h -93.232 183.15 m -92.646 183.15 92.189 182.98 91.861 182.64 c -91.533 182.31 91.369 181.84 91.369 181.24 c -91.369 177.50 l -90.572 177.50 l -90.572 176.64 l -91.369 176.64 l -91.369 175.48 l -92.523 175.37 l -92.523 176.64 l -94.188 176.64 l -94.188 177.50 l -92.523 177.50 l -92.523 181.03 l -92.523 181.86 92.883 182.28 93.602 182.28 c -93.754 182.28 93.939 182.25 94.158 182.20 c -94.158 183.00 l -93.803 183.10 93.494 183.15 93.232 183.15 c -h -94.656 185.02 m -94.656 184.15 l -100.66 184.15 l -100.66 185.02 l -h -103.72 183.15 m -103.13 183.15 102.68 182.98 102.35 182.64 c -102.02 182.31 101.86 181.84 101.86 181.24 c -101.86 177.50 l -101.06 177.50 l -101.06 176.64 l -101.86 176.64 l -101.86 175.48 l -103.01 175.37 l -103.01 176.64 l -104.68 176.64 l -104.68 177.50 l -103.01 177.50 l -103.01 181.03 l -103.01 181.86 103.37 182.28 104.09 182.28 c -104.24 182.28 104.43 182.25 104.65 182.20 c -104.65 183.00 l -104.29 183.10 103.98 183.15 103.72 183.15 c -h -110.96 182.79 m -110.19 183.03 109.53 183.15 108.98 183.15 c -108.04 183.15 107.27 182.83 106.68 182.21 c -106.09 181.59 105.79 180.78 105.79 179.79 c -105.79 178.82 106.06 178.03 106.58 177.42 c -107.10 176.80 107.77 176.49 108.58 176.49 c -109.35 176.49 109.94 176.76 110.36 177.31 c -110.78 177.86 110.99 178.63 110.99 179.64 c -110.99 180.00 l -106.97 180.00 l -107.14 181.51 107.88 182.27 109.19 182.27 c -109.67 182.27 110.26 182.14 110.96 181.88 c -h -107.03 179.13 m -109.83 179.13 l -109.83 177.95 109.39 177.36 108.51 177.36 c -107.62 177.36 107.13 177.95 107.03 179.13 c -h -112.98 183.00 m -112.98 176.64 l -114.14 176.64 l -114.14 177.83 l -114.75 176.94 115.49 176.50 116.38 176.50 c -116.93 176.50 117.37 176.67 117.70 177.02 c -118.02 177.37 118.19 177.84 118.19 178.43 c -118.19 183.00 l -117.03 183.00 l -117.03 178.80 l -117.03 178.33 116.96 178.00 116.83 177.79 c -116.69 177.59 116.46 177.49 116.14 177.49 c -115.43 177.49 114.76 177.96 114.14 178.88 c -114.14 183.00 l -h -123.71 182.19 m -123.02 182.83 122.36 183.15 121.71 183.15 c -121.19 183.15 120.75 182.98 120.40 182.65 c -120.05 182.32 119.88 181.90 119.88 181.40 c -119.88 180.71 120.17 180.17 120.76 179.80 c -121.34 179.42 122.18 179.24 123.27 179.24 c -123.54 179.24 l -123.54 178.47 l -123.54 177.73 123.16 177.36 122.41 177.36 c -121.80 177.36 121.14 177.55 120.43 177.93 c -120.43 176.97 l -121.21 176.65 121.94 176.50 122.62 176.50 c -123.33 176.50 123.85 176.66 124.19 176.98 c -124.53 177.30 124.70 177.79 124.70 178.47 c -124.70 181.35 l -124.70 182.01 124.90 182.34 125.31 182.34 c -125.36 182.34 125.43 182.34 125.53 182.32 c -125.61 182.96 l -125.35 183.08 125.06 183.15 124.74 183.15 c -124.21 183.15 123.86 182.83 123.71 182.19 c -h -123.54 181.56 m -123.54 179.92 l -123.16 179.91 l -122.52 179.91 122.01 180.03 121.62 180.27 c -121.23 180.51 121.04 180.82 121.04 181.21 c -121.04 181.49 121.13 181.72 121.33 181.92 c -121.52 182.11 121.76 182.20 122.04 182.20 c -122.52 182.20 123.02 181.99 123.54 181.56 c -h -127.06 183.00 m -127.06 176.64 l -128.21 176.64 l -128.21 177.83 l -128.82 176.94 129.57 176.50 130.45 176.50 c -131.00 176.50 131.44 176.67 131.77 177.02 c -132.10 177.37 132.26 177.84 132.26 178.43 c -132.26 183.00 l -131.11 183.00 l -131.11 178.80 l -131.11 178.33 131.04 178.00 130.90 177.79 c -130.76 177.59 130.53 177.49 130.21 177.49 c -129.50 177.49 128.84 177.96 128.21 178.88 c -128.21 183.00 l -h -136.42 183.15 m -135.83 183.15 135.37 182.98 135.04 182.64 c -134.72 182.31 134.55 181.84 134.55 181.24 c -134.55 177.50 l -133.76 177.50 l -133.76 176.64 l -134.55 176.64 l -134.55 175.48 l -135.71 175.37 l -135.71 176.64 l -137.37 176.64 l -137.37 177.50 l -135.71 177.50 l -135.71 181.03 l -135.71 181.86 136.07 182.28 136.79 182.28 c -136.94 182.28 137.12 182.25 137.34 182.20 c -137.34 183.00 l -136.99 183.10 136.68 183.15 136.42 183.15 c -h -140.68 183.15 m -140.15 183.15 139.51 183.02 138.76 182.78 c -138.76 181.72 l -139.51 182.09 140.17 182.28 140.73 182.28 c -141.06 182.28 141.34 182.19 141.55 182.01 c -141.77 181.83 141.88 181.61 141.88 181.34 c -141.88 180.94 141.58 180.62 140.96 180.36 c -140.29 180.07 l -139.29 179.66 138.79 179.06 138.79 178.28 c -138.79 177.73 138.99 177.29 139.38 176.97 c -139.78 176.66 140.31 176.50 141.00 176.50 c -141.35 176.50 141.79 176.54 142.32 176.64 c -142.56 176.69 l -142.56 177.65 l -141.91 177.46 141.40 177.36 141.02 177.36 c -140.28 177.36 139.91 177.63 139.91 178.17 c -139.91 178.52 140.19 178.81 140.75 179.05 c -141.31 179.29 l -141.94 179.55 142.38 179.83 142.64 180.13 c -142.91 180.42 143.04 180.79 143.04 181.23 c -143.04 181.79 142.82 182.25 142.38 182.61 c -141.93 182.97 141.37 183.15 140.68 183.15 c -h -152.20 183.00 m -145.26 179.53 l -152.20 176.06 l -152.20 177.03 l -147.20 179.53 l -152.20 182.03 l -h -158.63 183.00 m -158.63 181.80 l -158.02 182.70 157.27 183.15 156.40 183.15 c -155.84 183.15 155.40 182.97 155.07 182.62 c -154.74 182.27 154.58 181.80 154.58 181.21 c -154.58 176.64 l -155.73 176.64 l -155.73 180.83 l -155.73 181.31 155.80 181.65 155.94 181.85 c -156.08 182.05 156.31 182.15 156.64 182.15 c -157.34 182.15 158.00 181.69 158.63 180.76 c -158.63 176.64 l -159.78 176.64 l -159.78 183.00 l -h -162.10 183.00 m -162.10 176.64 l -163.25 176.64 l -163.25 177.83 l -163.86 176.94 164.61 176.50 165.49 176.50 c -166.04 176.50 166.48 176.67 166.81 177.02 c -167.14 177.37 167.30 177.84 167.30 178.43 c -167.30 183.00 l -166.15 183.00 l -166.15 178.80 l -166.15 178.33 166.08 178.00 165.94 177.79 c -165.80 177.59 165.57 177.49 165.25 177.49 c -164.54 177.49 163.88 177.96 163.25 178.88 c -163.25 183.00 l -h -171.23 183.15 m -170.71 183.15 170.06 183.02 169.31 182.78 c -169.31 181.72 l -170.06 182.09 170.72 182.28 171.28 182.28 c -171.61 182.28 171.89 182.19 172.11 182.01 c -172.32 181.83 172.43 181.61 172.43 181.34 c -172.43 180.94 172.13 180.62 171.51 180.36 c -170.84 180.07 l -169.84 179.66 169.35 179.06 169.35 178.28 c -169.35 177.73 169.54 177.29 169.93 176.97 c -170.33 176.66 170.87 176.50 171.55 176.50 c -171.90 176.50 172.34 176.54 172.87 176.64 c -173.11 176.69 l -173.11 177.65 l -172.46 177.46 171.95 177.36 171.57 177.36 c -170.83 177.36 170.46 177.63 170.46 178.17 c -170.46 178.52 170.74 178.81 171.30 179.05 c -171.86 179.29 l -172.49 179.55 172.93 179.83 173.20 180.13 c -173.46 180.42 173.59 180.79 173.59 181.23 c -173.59 181.79 173.37 182.25 172.93 182.61 c -172.48 182.97 171.92 183.15 171.23 183.15 c -h -178.15 183.15 m -177.29 183.15 176.57 182.83 176.01 182.19 c -175.44 181.55 175.16 180.75 175.16 179.78 c -175.16 178.75 175.44 177.94 176.00 177.36 c -176.56 176.79 177.34 176.50 178.35 176.50 c -178.84 176.50 179.40 176.56 180.01 176.70 c -180.01 177.67 l -179.36 177.48 178.83 177.38 178.42 177.38 c -177.83 177.38 177.35 177.60 177.00 178.05 c -176.64 178.49 176.46 179.08 176.46 179.82 c -176.46 180.53 176.64 181.11 177.01 181.55 c -177.38 181.99 177.86 182.21 178.45 182.21 c -178.98 182.21 179.52 182.08 180.08 181.81 c -180.08 182.81 l -179.33 183.03 178.69 183.15 178.15 183.15 c -h -184.30 183.15 m -183.39 183.15 182.66 182.84 182.12 182.24 c -181.58 181.64 181.30 180.83 181.30 179.82 c -181.30 178.79 181.58 177.99 182.12 177.39 c -182.67 176.79 183.41 176.50 184.34 176.50 c -185.27 176.50 186.01 176.79 186.56 177.39 c -187.10 177.99 187.38 178.79 187.38 179.81 c -187.38 180.85 187.10 181.66 186.55 182.26 c -186.01 182.85 185.26 183.15 184.30 183.15 c -h -184.32 182.28 m -185.54 182.28 186.15 181.46 186.15 179.81 c -186.15 178.18 185.55 177.36 184.34 177.36 c -183.14 177.36 182.54 178.18 182.54 179.82 c -182.54 181.46 183.13 182.28 184.32 182.28 c -h -189.18 185.31 m -189.18 176.64 l -190.33 176.64 l -190.33 177.83 l -190.81 176.94 191.52 176.50 192.46 176.50 c -193.23 176.50 193.83 176.78 194.27 177.33 c -194.71 177.89 194.93 178.66 194.93 179.62 c -194.93 180.68 194.68 181.53 194.18 182.18 c -193.68 182.82 193.03 183.15 192.21 183.15 c -191.46 183.15 190.83 182.86 190.33 182.28 c -190.33 185.31 l -h -190.33 181.48 m -190.93 182.01 191.49 182.28 192.03 182.28 c -193.14 182.28 193.70 181.43 193.70 179.74 c -193.70 178.25 193.21 177.50 192.22 177.50 c -191.58 177.50 190.95 177.85 190.33 178.55 c -h -201.40 182.79 m -200.62 183.03 199.96 183.15 199.41 183.15 c -198.47 183.15 197.71 182.83 197.12 182.21 c -196.52 181.59 196.23 180.78 196.23 179.79 c -196.23 178.82 196.49 178.03 197.01 177.42 c -197.53 176.80 198.20 176.49 199.01 176.49 c -199.78 176.49 200.38 176.76 200.80 177.31 c -201.22 177.86 201.43 178.63 201.43 179.64 c -201.42 180.00 l -197.41 180.00 l -197.57 181.51 198.31 182.27 199.63 182.27 c -200.11 182.27 200.70 182.14 201.40 181.88 c -h -197.46 179.13 m -200.27 179.13 l -200.27 177.95 199.82 177.36 198.94 177.36 c -198.05 177.36 197.56 177.95 197.46 179.13 c -h -207.50 183.00 m -207.50 181.80 l -207.03 182.70 206.33 183.15 205.38 183.15 c -204.62 183.15 204.01 182.87 203.57 182.31 c -203.13 181.75 202.91 180.99 202.91 180.02 c -202.91 178.96 203.16 178.11 203.66 177.46 c -204.16 176.82 204.81 176.50 205.63 176.50 c -206.38 176.50 207.01 176.79 207.50 177.36 c -207.50 173.75 l -208.66 173.75 l -208.66 183.00 l -h -207.50 178.15 m -206.90 177.63 206.34 177.36 205.80 177.36 c -204.70 177.36 204.14 178.21 204.14 179.90 c -204.14 181.39 204.64 182.13 205.62 182.13 c -206.26 182.13 206.89 181.78 207.50 181.08 c -h -209.82 185.02 m -209.82 184.15 l -215.82 184.15 l -215.82 185.02 l -h -218.88 183.15 m -218.29 183.15 217.84 182.98 217.51 182.64 c -217.18 182.31 217.02 181.84 217.02 181.24 c -217.02 177.50 l -216.22 177.50 l -216.22 176.64 l -217.02 176.64 l -217.02 175.48 l -218.17 175.37 l -218.17 176.64 l -219.84 176.64 l -219.84 177.50 l -218.17 177.50 l -218.17 181.03 l -218.17 181.86 218.53 182.28 219.25 182.28 c -219.40 182.28 219.59 182.25 219.81 182.20 c -219.81 183.00 l -219.45 183.10 219.14 183.15 218.88 183.15 c -h -223.95 183.15 m -223.04 183.15 222.31 182.84 221.77 182.24 c -221.23 181.64 220.96 180.83 220.96 179.82 c -220.96 178.79 221.23 177.99 221.77 177.39 c -222.32 176.79 223.06 176.50 223.99 176.50 c -224.92 176.50 225.66 176.79 226.21 177.39 c -226.75 177.99 227.03 178.79 227.03 179.81 c -227.03 180.85 226.75 181.66 226.21 182.26 c -225.66 182.85 224.91 183.15 223.95 183.15 c -h -223.97 182.28 m -225.19 182.28 225.80 181.46 225.80 179.81 c -225.80 178.18 225.20 177.36 223.99 177.36 c -222.79 177.36 222.19 178.18 222.19 179.82 c -222.19 181.46 222.78 182.28 223.97 182.28 c -h -228.83 183.00 m -228.83 173.75 l -229.98 173.75 l -229.98 179.72 l -232.68 176.64 l -233.92 176.64 l -231.35 179.61 l -234.46 183.00 l -232.98 183.00 l -229.98 179.74 l -229.98 183.00 l -h -240.51 182.79 m -239.73 183.03 239.07 183.15 238.52 183.15 c -237.58 183.15 236.82 182.83 236.23 182.21 c -235.64 181.59 235.34 180.78 235.34 179.79 c -235.34 178.82 235.60 178.03 236.12 177.42 c -236.64 176.80 237.31 176.49 238.12 176.49 c -238.89 176.49 239.49 176.76 239.91 177.31 c -240.33 177.86 240.54 178.63 240.54 179.64 c -240.53 180.00 l -236.52 180.00 l -236.69 181.51 237.43 182.27 238.74 182.27 c -239.22 182.27 239.81 182.14 240.51 181.88 c -h -236.57 179.13 m -239.38 179.13 l -239.38 177.95 238.94 177.36 238.05 177.36 c -237.17 177.36 236.67 177.95 236.57 179.13 c -h -242.53 183.00 m -242.53 176.64 l -243.68 176.64 l -243.68 177.83 l -244.29 176.94 245.04 176.50 245.92 176.50 c -246.47 176.50 246.91 176.67 247.24 177.02 c -247.57 177.37 247.73 177.84 247.73 178.43 c -247.73 183.00 l -246.58 183.00 l -246.58 178.80 l -246.58 178.33 246.51 178.00 246.37 177.79 c -246.23 177.59 246.00 177.49 245.68 177.49 c -244.97 177.49 244.31 177.96 243.68 178.88 c -243.68 183.00 l -h -250.12 183.00 m -257.06 179.53 l -250.12 176.06 l -250.12 177.03 l -255.12 179.53 l -250.12 182.03 l -h -f -70.500 186.50 m -549.50 186.50 l -S -549.00 186.00 m -543.00 180.00 l -543.00 192.00 l -h -f* -80.436 98.191 m -79.744 98.828 79.078 99.146 78.438 99.146 c -77.910 99.146 77.473 98.981 77.125 98.651 c -76.777 98.321 76.604 97.904 76.604 97.400 c -76.604 96.705 76.896 96.171 77.479 95.798 c -78.063 95.425 78.900 95.238 79.990 95.238 c -80.266 95.238 l -80.266 94.471 l -80.266 93.732 79.887 93.363 79.129 93.363 c -78.520 93.363 77.861 93.551 77.154 93.926 c -77.154 92.971 l -77.932 92.654 78.660 92.496 79.340 92.496 c -80.051 92.496 80.575 92.656 80.913 92.977 c -81.251 93.297 81.420 93.795 81.420 94.471 c -81.420 97.354 l -81.420 98.014 81.623 98.344 82.029 98.344 c -82.080 98.344 82.154 98.336 82.252 98.320 c -82.334 98.959 l -82.072 99.084 81.783 99.146 81.467 99.146 c -80.928 99.146 80.584 98.828 80.436 98.191 c -h -80.266 97.564 m -80.266 95.918 l -79.879 95.906 l -79.246 95.906 78.734 96.026 78.344 96.267 c -77.953 96.507 77.758 96.822 77.758 97.213 c -77.758 97.490 77.855 97.725 78.051 97.916 c -78.246 98.107 78.484 98.203 78.766 98.203 c -79.246 98.203 79.746 97.990 80.266 97.564 c -h -87.760 99.000 m -87.760 97.805 l -87.146 98.699 86.402 99.146 85.527 99.146 c -84.973 99.146 84.531 98.972 84.203 98.622 c -83.875 98.272 83.711 97.801 83.711 97.207 c -83.711 92.637 l -84.865 92.637 l -84.865 96.832 l -84.865 97.309 84.935 97.647 85.073 97.849 c -85.212 98.050 85.443 98.150 85.768 98.150 c -86.471 98.150 87.135 97.688 87.760 96.762 c -87.760 92.637 l -88.914 92.637 l -88.914 99.000 l -h -93.139 99.146 m -92.553 99.146 92.096 98.979 91.768 98.643 c -91.439 98.307 91.275 97.840 91.275 97.242 c -91.275 93.504 l -90.479 93.504 l -90.479 92.637 l -91.275 92.637 l -91.275 91.482 l -92.430 91.371 l -92.430 92.637 l -94.094 92.637 l -94.094 93.504 l -92.430 93.504 l -92.430 97.031 l -92.430 97.863 92.789 98.279 93.508 98.279 c -93.660 98.279 93.846 98.254 94.064 98.203 c -94.064 99.000 l -93.709 99.098 93.400 99.146 93.139 99.146 c -h -95.717 99.000 m -95.717 89.748 l -96.871 89.748 l -96.871 93.832 l -97.480 92.941 98.227 92.496 99.109 92.496 c -99.660 92.496 100.10 92.671 100.43 93.021 c -100.76 93.370 100.92 93.840 100.92 94.430 c -100.92 99.000 l -99.766 99.000 l -99.766 94.805 l -99.766 94.332 99.696 93.995 99.558 93.794 c -99.419 93.593 99.189 93.492 98.869 93.492 c -98.162 93.492 97.496 93.955 96.871 94.881 c -96.871 99.000 l -h -110.25 99.000 m -103.31 95.531 l -110.25 92.062 l -110.25 93.029 l -105.25 95.531 l -110.25 98.027 l -h -116.68 99.000 m -116.68 97.805 l -116.07 98.699 115.32 99.146 114.45 99.146 c -113.89 99.146 113.45 98.972 113.12 98.622 c -112.80 98.272 112.63 97.801 112.63 97.207 c -112.63 92.637 l -113.79 92.637 l -113.79 96.832 l -113.79 97.309 113.86 97.647 114.00 97.849 c -114.13 98.050 114.37 98.150 114.69 98.150 c -115.39 98.150 116.06 97.688 116.68 96.762 c -116.68 92.637 l -117.84 92.637 l -117.84 99.000 l -h -121.84 99.146 m -121.31 99.146 120.67 99.023 119.92 98.777 c -119.92 97.717 l -120.67 98.092 121.33 98.279 121.88 98.279 c -122.22 98.279 122.49 98.189 122.71 98.010 c -122.93 97.830 123.04 97.605 123.04 97.336 c -123.04 96.941 122.73 96.615 122.12 96.357 c -121.45 96.070 l -120.45 95.656 119.95 95.061 119.95 94.283 c -119.95 93.729 120.15 93.292 120.54 92.974 c -120.93 92.655 121.47 92.496 122.15 92.496 c -122.51 92.496 122.95 92.545 123.47 92.643 c -123.71 92.689 l -123.71 93.650 l -123.07 93.459 122.56 93.363 122.18 93.363 c -121.44 93.363 121.06 93.633 121.06 94.172 c -121.06 94.520 121.35 94.812 121.91 95.051 c -122.46 95.285 l -123.09 95.551 123.54 95.831 123.80 96.126 c -124.06 96.421 124.19 96.789 124.19 97.230 c -124.19 97.789 123.97 98.248 123.53 98.607 c -123.09 98.967 122.53 99.146 121.84 99.146 c -h -130.93 98.795 m -130.16 99.029 129.50 99.146 128.95 99.146 c -128.01 99.146 127.24 98.835 126.65 98.212 c -126.06 97.589 125.76 96.781 125.76 95.789 c -125.76 94.824 126.02 94.033 126.55 93.416 c -127.07 92.799 127.73 92.490 128.55 92.490 c -129.32 92.490 129.91 92.764 130.33 93.311 c -130.75 93.857 130.96 94.635 130.96 95.643 c -130.96 96.000 l -126.94 96.000 l -127.11 97.512 127.85 98.268 129.16 98.268 c -129.64 98.268 130.23 98.139 130.93 97.881 c -h -126.99 95.133 m -129.80 95.133 l -129.80 93.949 129.36 93.357 128.48 93.357 c -127.59 93.357 127.10 93.949 126.99 95.133 c -h -132.95 99.000 m -132.95 92.637 l -134.11 92.637 l -134.11 93.832 l -134.56 92.941 135.23 92.496 136.10 92.496 c -136.22 92.496 136.34 92.506 136.47 92.525 c -136.47 93.604 l -136.27 93.537 136.09 93.504 135.94 93.504 c -135.21 93.504 134.60 93.938 134.11 94.805 c -134.11 99.000 l -h -137.88 100.88 m -137.88 100.45 l -138.26 100.34 138.44 99.898 138.44 99.117 c -138.44 99.000 l -137.88 99.000 l -137.88 97.553 l -139.33 97.553 l -139.33 98.807 l -139.33 100.09 138.85 100.78 137.88 100.88 c -h -145.46 101.31 m -145.46 92.637 l -146.61 92.637 l -146.61 93.832 l -147.08 92.941 147.79 92.496 148.74 92.496 c -149.50 92.496 150.11 92.775 150.55 93.334 c -150.99 93.893 151.21 94.656 151.21 95.625 c -151.21 96.680 150.96 97.530 150.46 98.177 c -149.96 98.823 149.30 99.146 148.49 99.146 c -147.74 99.146 147.11 98.857 146.61 98.279 c -146.61 101.31 l -h -146.61 97.482 m -147.21 98.014 147.77 98.279 148.31 98.279 c -149.42 98.279 149.97 97.434 149.97 95.742 c -149.97 94.250 149.48 93.504 148.50 93.504 c -147.85 93.504 147.22 93.854 146.61 94.553 c -h -156.29 98.191 m -155.60 98.828 154.93 99.146 154.29 99.146 c -153.77 99.146 153.33 98.981 152.98 98.651 c -152.63 98.321 152.46 97.904 152.46 97.400 c -152.46 96.705 152.75 96.171 153.33 95.798 c -153.92 95.425 154.76 95.238 155.85 95.238 c -156.12 95.238 l -156.12 94.471 l -156.12 93.732 155.74 93.363 154.98 93.363 c -154.38 93.363 153.72 93.551 153.01 93.926 c -153.01 92.971 l -153.79 92.654 154.52 92.496 155.20 92.496 c -155.91 92.496 156.43 92.656 156.77 92.977 c -157.11 93.297 157.28 93.795 157.28 94.471 c -157.28 97.354 l -157.28 98.014 157.48 98.344 157.88 98.344 c -157.94 98.344 158.01 98.336 158.11 98.320 c -158.19 98.959 l -157.93 99.084 157.64 99.146 157.32 99.146 c -156.78 99.146 156.44 98.828 156.29 98.191 c -h -156.12 97.564 m -156.12 95.918 l -155.73 95.906 l -155.10 95.906 154.59 96.026 154.20 96.267 c -153.81 96.507 153.61 96.822 153.61 97.213 c -153.61 97.490 153.71 97.725 153.91 97.916 c -154.10 98.107 154.34 98.203 154.62 98.203 c -155.10 98.203 155.60 97.990 156.12 97.564 c -h -161.32 99.146 m -160.80 99.146 160.16 99.023 159.40 98.777 c -159.40 97.717 l -160.16 98.092 160.81 98.279 161.37 98.279 c -161.70 98.279 161.98 98.189 162.20 98.010 c -162.42 97.830 162.53 97.605 162.53 97.336 c -162.53 96.941 162.22 96.615 161.61 96.357 c -160.93 96.070 l -159.94 95.656 159.44 95.061 159.44 94.283 c -159.44 93.729 159.63 93.292 160.03 92.974 c -160.42 92.655 160.96 92.496 161.64 92.496 c -162.00 92.496 162.44 92.545 162.96 92.643 c -163.20 92.689 l -163.20 93.650 l -162.55 93.459 162.04 93.363 161.66 93.363 c -160.92 93.363 160.55 93.633 160.55 94.172 c -160.55 94.520 160.83 94.812 161.39 95.051 c -161.95 95.285 l -162.58 95.551 163.03 95.831 163.29 96.126 c -163.55 96.421 163.68 96.789 163.68 97.230 c -163.68 97.789 163.46 98.248 163.02 98.607 c -162.58 98.967 162.01 99.146 161.32 99.146 c -h -167.44 99.146 m -166.91 99.146 166.27 99.023 165.52 98.777 c -165.52 97.717 l -166.27 98.092 166.93 98.279 167.49 98.279 c -167.82 98.279 168.10 98.189 168.31 98.010 c -168.53 97.830 168.64 97.605 168.64 97.336 c -168.64 96.941 168.34 96.615 167.72 96.357 c -167.05 96.070 l -166.05 95.656 165.55 95.061 165.55 94.283 c -165.55 93.729 165.75 93.292 166.14 92.974 c -166.54 92.655 167.07 92.496 167.76 92.496 c -168.11 92.496 168.55 92.545 169.08 92.643 c -169.32 92.689 l -169.32 93.650 l -168.67 93.459 168.16 93.363 167.78 93.363 c -167.04 93.363 166.67 93.633 166.67 94.172 c -166.67 94.520 166.95 94.812 167.51 95.051 c -168.07 95.285 l -168.70 95.551 169.14 95.831 169.40 96.126 c -169.67 96.421 169.80 96.789 169.80 97.230 c -169.80 97.789 169.58 98.248 169.13 98.607 c -168.69 98.967 168.13 99.146 167.44 99.146 c -h -172.61 99.000 m -170.79 92.637 l -171.92 92.637 l -173.31 97.564 l -174.82 92.637 l -175.97 92.637 l -177.29 97.564 l -178.89 92.637 l -179.88 92.637 l -177.81 99.000 l -176.65 99.000 l -175.30 94.072 l -173.78 99.000 l -h -183.61 99.146 m -182.70 99.146 181.97 98.845 181.43 98.241 c -180.88 97.638 180.61 96.830 180.61 95.818 c -180.61 94.795 180.89 93.985 181.43 93.390 c -181.98 92.794 182.71 92.496 183.65 92.496 c -184.58 92.496 185.32 92.794 185.87 93.390 c -186.41 93.985 186.68 94.791 186.68 95.807 c -186.68 96.846 186.41 97.662 185.86 98.256 c -185.32 98.850 184.56 99.146 183.61 99.146 c -h -183.62 98.279 m -184.85 98.279 185.46 97.455 185.46 95.807 c -185.46 94.178 184.86 93.363 183.65 93.363 c -182.45 93.363 181.84 94.182 181.84 95.818 c -181.84 97.459 182.44 98.279 183.62 98.279 c -h -188.49 99.000 m -188.49 92.637 l -189.64 92.637 l -189.64 93.832 l -190.10 92.941 190.76 92.496 191.63 92.496 c -191.75 92.496 191.88 92.506 192.00 92.525 c -192.00 93.604 l -191.80 93.537 191.63 93.504 191.48 93.504 c -190.75 93.504 190.13 93.938 189.64 94.805 c -189.64 99.000 l -h -197.48 99.000 m -197.48 97.805 l -197.01 98.699 196.31 99.146 195.36 99.146 c -194.60 99.146 193.99 98.867 193.55 98.309 c -193.11 97.750 192.89 96.986 192.89 96.018 c -192.89 94.959 193.14 94.107 193.64 93.463 c -194.14 92.818 194.79 92.496 195.61 92.496 c -196.36 92.496 196.99 92.785 197.48 93.363 c -197.48 89.748 l -198.64 89.748 l -198.64 99.000 l -h -197.48 94.154 m -196.88 93.627 196.32 93.363 195.78 93.363 c -194.68 93.363 194.12 94.209 194.12 95.900 c -194.12 97.389 194.62 98.133 195.60 98.133 c -196.24 98.133 196.87 97.783 197.48 97.084 c -h -201.10 99.000 m -208.04 95.531 l -201.10 92.062 l -201.10 93.029 l -206.10 95.531 l -201.10 98.027 l -h -f -70.500 102.50 m -549.50 102.50 l -S -549.00 102.00 m -543.00 96.000 l -543.00 108.00 l -h -f* -76.996 571.00 m -76.996 569.99 l -77.332 569.20 78.012 568.35 79.035 567.42 c -79.697 566.83 l -80.549 566.06 80.975 565.29 80.975 564.54 c -80.975 564.05 80.829 563.67 80.538 563.39 c -80.247 563.12 79.848 562.98 79.340 562.98 c -78.738 562.98 78.029 563.21 77.213 563.68 c -77.213 562.66 l -77.982 562.29 78.746 562.11 79.504 562.11 c -80.316 562.11 80.969 562.33 81.461 562.77 c -81.953 563.21 82.199 563.79 82.199 564.51 c -82.199 565.03 82.075 565.49 81.827 565.89 c -81.579 566.29 81.117 566.78 80.441 567.36 c -79.996 567.74 l -79.070 568.52 78.535 569.27 78.391 569.99 c -82.158 569.99 l -82.158 571.00 l -h -87.350 571.22 m -86.455 571.22 85.731 570.80 85.179 569.95 c -84.626 569.11 84.350 568.01 84.350 566.66 c -84.350 565.29 84.628 564.19 85.185 563.36 c -85.741 562.53 86.475 562.11 87.385 562.11 c -88.295 562.11 89.028 562.53 89.585 563.36 c -90.142 564.19 90.420 565.29 90.420 566.64 c -90.420 568.03 90.142 569.14 89.585 569.97 c -89.028 570.80 88.283 571.22 87.350 571.22 c -h -87.361 570.35 m -88.584 570.35 89.195 569.11 89.195 566.62 c -89.195 564.19 88.592 562.98 87.385 562.98 c -86.182 562.98 85.580 564.21 85.580 566.66 c -85.580 569.12 86.174 570.35 87.361 570.35 c -h -94.938 571.22 m -94.043 571.22 93.319 570.80 92.767 569.95 c -92.214 569.11 91.938 568.01 91.938 566.66 c -91.938 565.29 92.216 564.19 92.772 563.36 c -93.329 562.53 94.062 562.11 94.973 562.11 c -95.883 562.11 96.616 562.53 97.173 563.36 c -97.729 564.19 98.008 565.29 98.008 566.64 c -98.008 568.03 97.729 569.14 97.173 569.97 c -96.616 570.80 95.871 571.22 94.938 571.22 c -h -94.949 570.35 m -96.172 570.35 96.783 569.11 96.783 566.62 c -96.783 564.19 96.180 562.98 94.973 562.98 c -93.770 562.98 93.168 564.21 93.168 566.66 c -93.168 569.12 93.762 570.35 94.949 570.35 c -h -107.17 571.22 m -105.97 571.22 105.00 570.80 104.27 569.97 c -103.54 569.13 103.17 568.03 103.17 566.66 c -103.17 565.28 103.54 564.18 104.27 563.35 c -105.01 562.52 105.99 562.11 107.22 562.11 c -108.45 562.11 109.43 562.52 110.17 563.35 c -110.91 564.17 111.28 565.27 111.28 566.65 c -111.28 568.05 110.91 569.16 110.17 569.98 c -109.43 570.81 108.43 571.22 107.17 571.22 c -h -107.19 570.30 m -108.08 570.30 108.76 569.98 109.25 569.34 c -109.73 568.70 109.97 567.80 109.97 566.63 c -109.97 565.51 109.73 564.62 109.24 563.99 c -108.76 563.35 108.08 563.03 107.22 563.03 c -106.36 563.03 105.69 563.35 105.20 563.99 c -104.72 564.63 104.48 565.52 104.48 566.65 c -104.48 567.79 104.72 568.68 105.20 569.32 c -105.68 569.97 106.34 570.30 107.19 570.30 c -h -113.00 571.00 m -113.00 562.33 l -114.16 562.33 l -114.16 566.59 l -117.67 562.33 l -118.90 562.33 l -115.50 566.46 l -119.51 571.00 l -117.95 571.00 l -114.16 566.61 l -114.16 571.00 l -h -f -81.818 322.79 m -81.045 323.03 80.383 323.15 79.832 323.15 c -78.895 323.15 78.130 322.83 77.538 322.21 c -76.946 321.59 76.650 320.78 76.650 319.79 c -76.650 318.82 76.911 318.03 77.433 317.42 c -77.954 316.80 78.621 316.49 79.434 316.49 c -80.203 316.49 80.798 316.76 81.218 317.31 c -81.638 317.86 81.848 318.63 81.848 319.64 c -81.842 320.00 l -77.828 320.00 l -77.996 321.51 78.736 322.27 80.049 322.27 c -80.529 322.27 81.119 322.14 81.818 321.88 c -h -77.881 319.13 m -80.688 319.13 l -80.688 317.95 80.246 317.36 79.363 317.36 c -78.477 317.36 77.982 317.95 77.881 319.13 c -h -83.840 323.00 m -83.840 316.64 l -84.994 316.64 l -84.994 317.83 l -85.604 316.94 86.350 316.50 87.232 316.50 c -87.783 316.50 88.223 316.67 88.551 317.02 c -88.879 317.37 89.043 317.84 89.043 318.43 c -89.043 323.00 l -87.889 323.00 l -87.889 318.80 l -87.889 318.33 87.819 318.00 87.681 317.79 c -87.542 317.59 87.312 317.49 86.992 317.49 c -86.285 317.49 85.619 317.96 84.994 318.88 c -84.994 323.00 l -h -95.371 323.00 m -95.371 321.80 l -94.902 322.70 94.195 323.15 93.250 323.15 c -92.484 323.15 91.882 322.87 91.442 322.31 c -91.003 321.75 90.783 320.99 90.783 320.02 c -90.783 318.96 91.032 318.11 91.530 317.46 c -92.028 316.82 92.684 316.50 93.496 316.50 c -94.250 316.50 94.875 316.79 95.371 317.36 c -95.371 313.75 l -96.531 313.75 l -96.531 323.00 l -h -95.371 318.15 m -94.773 317.63 94.207 317.36 93.672 317.36 c -92.566 317.36 92.014 318.21 92.014 319.90 c -92.014 321.39 92.506 322.13 93.490 322.13 c -94.131 322.13 94.758 321.78 95.371 321.08 c -h -98.840 325.31 m -98.840 316.64 l -99.994 316.64 l -99.994 317.83 l -100.47 316.94 101.18 316.50 102.12 316.50 c -102.89 316.50 103.49 316.78 103.93 317.33 c -104.37 317.89 104.59 318.66 104.59 319.62 c -104.59 320.68 104.34 321.53 103.84 322.18 c -103.34 322.82 102.69 323.15 101.88 323.15 c -101.12 323.15 100.49 322.86 99.994 322.28 c -99.994 325.31 l -h -99.994 321.48 m -100.59 322.01 101.15 322.28 101.69 322.28 c -102.80 322.28 103.36 321.43 103.36 319.74 c -103.36 318.25 102.87 317.50 101.88 317.50 c -101.24 317.50 100.61 317.85 99.994 318.55 c -h -108.88 323.15 m -107.97 323.15 107.25 322.84 106.70 322.24 c -106.16 321.64 105.89 320.83 105.89 319.82 c -105.89 318.79 106.16 317.99 106.71 317.39 c -107.25 316.79 107.99 316.50 108.92 316.50 c -109.86 316.50 110.60 316.79 111.14 317.39 c -111.69 317.99 111.96 318.79 111.96 319.81 c -111.96 320.85 111.69 321.66 111.14 322.26 c -110.59 322.85 109.84 323.15 108.88 323.15 c -h -108.90 322.28 m -110.12 322.28 110.73 321.46 110.73 319.81 c -110.73 318.18 110.13 317.36 108.92 317.36 c -107.72 317.36 107.12 318.18 107.12 319.82 c -107.12 321.46 107.71 322.28 108.90 322.28 c -h -113.76 323.00 m -113.76 316.64 l -114.92 316.64 l -114.92 323.00 l -h -113.76 315.48 m -113.76 314.33 l -114.92 314.33 l -114.92 315.48 l -h -117.23 323.00 m -117.23 316.64 l -118.39 316.64 l -118.39 317.83 l -119.00 316.94 119.74 316.50 120.62 316.50 c -121.18 316.50 121.62 316.67 121.94 317.02 c -122.27 317.37 122.44 317.84 122.44 318.43 c -122.44 323.00 l -121.28 323.00 l -121.28 318.80 l -121.28 318.33 121.21 318.00 121.07 317.79 c -120.93 317.59 120.71 317.49 120.38 317.49 c -119.68 317.49 119.01 317.96 118.39 318.88 c -118.39 323.00 l -h -126.59 323.15 m -126.00 323.15 125.55 322.98 125.22 322.64 c -124.89 322.31 124.73 321.84 124.73 321.24 c -124.73 317.50 l -123.93 317.50 l -123.93 316.64 l -124.73 316.64 l -124.73 315.48 l -125.88 315.37 l -125.88 316.64 l -127.54 316.64 l -127.54 317.50 l -125.88 317.50 l -125.88 321.03 l -125.88 321.86 126.24 322.28 126.96 322.28 c -127.11 322.28 127.30 322.25 127.52 322.20 c -127.52 323.00 l -127.16 323.10 126.85 323.15 126.59 323.15 c -h -133.11 321.05 m -133.11 320.18 l -140.05 320.18 l -140.05 321.05 l -h -133.11 318.88 m -133.11 318.01 l -140.05 318.01 l -140.05 318.88 l -h -147.99 323.15 m -147.46 323.15 146.82 323.02 146.07 322.78 c -146.07 321.72 l -146.82 322.09 147.48 322.28 148.04 322.28 c -148.37 322.28 148.64 322.19 148.86 322.01 c -149.08 321.83 149.19 321.61 149.19 321.34 c -149.19 320.94 148.88 320.62 148.27 320.36 c -147.60 320.07 l -146.60 319.66 146.10 319.06 146.10 318.28 c -146.10 317.73 146.30 317.29 146.69 316.97 c -147.08 316.66 147.62 316.50 148.30 316.50 c -148.66 316.50 149.10 316.54 149.62 316.64 c -149.86 316.69 l -149.86 317.65 l -149.22 317.46 148.71 317.36 148.33 317.36 c -147.59 317.36 147.21 317.63 147.21 318.17 c -147.21 318.52 147.50 318.81 148.06 319.05 c -148.62 319.29 l -149.24 319.55 149.69 319.83 149.95 320.13 c -150.21 320.42 150.34 320.79 150.34 321.23 c -150.34 321.79 150.12 322.25 149.68 322.61 c -149.24 322.97 148.68 323.15 147.99 323.15 c -h -157.08 322.79 m -156.31 323.03 155.65 323.15 155.10 323.15 c -154.16 323.15 153.39 322.83 152.80 322.21 c -152.21 321.59 151.91 320.78 151.91 319.79 c -151.91 318.82 152.17 318.03 152.70 317.42 c -153.22 316.80 153.88 316.49 154.70 316.49 c -155.47 316.49 156.06 316.76 156.48 317.31 c -156.90 317.86 157.11 318.63 157.11 319.64 c -157.11 320.00 l -153.09 320.00 l -153.26 321.51 154.00 322.27 155.31 322.27 c -155.79 322.27 156.38 322.14 157.08 321.88 c -h -153.14 319.13 m -155.95 319.13 l -155.95 317.95 155.51 317.36 154.63 317.36 c -153.74 317.36 153.25 317.95 153.14 319.13 c -h -159.10 323.00 m -159.10 316.64 l -160.26 316.64 l -160.26 317.83 l -160.71 316.94 161.38 316.50 162.25 316.50 c -162.37 316.50 162.49 316.51 162.62 316.53 c -162.62 317.60 l -162.42 317.54 162.24 317.50 162.09 317.50 c -161.36 317.50 160.75 317.94 160.26 318.80 c -160.26 323.00 l -h -165.33 323.00 m -162.96 316.64 l -164.12 316.64 l -165.97 321.59 l -167.92 316.64 l -169.00 316.64 l -166.49 323.00 l -h -170.22 323.00 m -170.22 316.64 l -171.38 316.64 l -171.38 323.00 l -h -170.22 315.48 m -170.22 314.33 l -171.38 314.33 l -171.38 315.48 l -h -176.18 323.15 m -175.32 323.15 174.61 322.83 174.04 322.19 c -173.47 321.55 173.19 320.75 173.19 319.78 c -173.19 318.75 173.47 317.94 174.03 317.36 c -174.59 316.79 175.37 316.50 176.38 316.50 c -176.87 316.50 177.43 316.56 178.04 316.70 c -178.04 317.67 l -177.39 317.48 176.86 317.38 176.45 317.38 c -175.86 317.38 175.38 317.60 175.03 318.05 c -174.67 318.49 174.49 319.08 174.49 319.82 c -174.49 320.53 174.67 321.11 175.04 321.55 c -175.41 321.99 175.89 322.21 176.48 322.21 c -177.01 322.21 177.55 322.08 178.11 321.81 c -178.11 322.81 l -177.37 323.03 176.72 323.15 176.18 323.15 c -h -184.50 322.79 m -183.73 323.03 183.07 323.15 182.52 323.15 c -181.58 323.15 180.82 322.83 180.22 322.21 c -179.63 321.59 179.34 320.78 179.34 319.79 c -179.34 318.82 179.60 318.03 180.12 317.42 c -180.64 316.80 181.31 316.49 182.12 316.49 c -182.89 316.49 183.48 316.76 183.90 317.31 c -184.32 317.86 184.53 318.63 184.53 319.64 c -184.53 320.00 l -180.51 320.00 l -180.68 321.51 181.42 322.27 182.73 322.27 c -183.21 322.27 183.80 322.14 184.50 321.88 c -h -180.57 319.13 m -183.37 319.13 l -183.37 317.95 182.93 317.36 182.05 317.36 c -181.16 317.36 180.67 317.95 180.57 319.13 c -h -190.21 323.22 m -188.86 323.22 187.82 322.82 187.08 322.03 c -186.35 321.24 185.98 320.12 185.98 318.67 c -185.98 317.22 186.35 316.10 187.10 315.31 c -187.85 314.51 188.90 314.11 190.26 314.11 c -191.04 314.11 191.95 314.24 192.99 314.49 c -192.99 315.65 l -191.81 315.24 190.89 315.03 190.25 315.03 c -189.30 315.03 188.58 315.35 188.06 315.99 c -187.54 316.62 187.29 317.52 187.29 318.68 c -187.29 319.79 187.56 320.66 188.11 321.30 c -188.66 321.94 189.42 322.26 190.37 322.26 c -191.19 322.26 192.07 322.00 193.01 321.50 c -193.01 322.55 l -192.15 323.00 191.22 323.22 190.21 323.22 c -h -198.11 322.19 m -197.42 322.83 196.75 323.15 196.11 323.15 c -195.58 323.15 195.15 322.98 194.80 322.65 c -194.45 322.32 194.28 321.90 194.28 321.40 c -194.28 320.71 194.57 320.17 195.15 319.80 c -195.74 319.42 196.57 319.24 197.66 319.24 c -197.94 319.24 l -197.94 318.47 l -197.94 317.73 197.56 317.36 196.80 317.36 c -196.19 317.36 195.54 317.55 194.83 317.93 c -194.83 316.97 l -195.61 316.65 196.33 316.50 197.01 316.50 c -197.72 316.50 198.25 316.66 198.59 316.98 c -198.92 317.30 199.09 317.79 199.09 318.47 c -199.09 321.35 l -199.09 322.01 199.30 322.34 199.70 322.34 c -199.75 322.34 199.83 322.34 199.93 322.32 c -200.01 322.96 l -199.75 323.08 199.46 323.15 199.14 323.15 c -198.60 323.15 198.26 322.83 198.11 322.19 c -h -197.94 321.56 m -197.94 319.92 l -197.55 319.91 l -196.92 319.91 196.41 320.03 196.02 320.27 c -195.63 320.51 195.43 320.82 195.43 321.21 c -195.43 321.49 195.53 321.72 195.72 321.92 c -195.92 322.11 196.16 322.20 196.44 322.20 c -196.92 322.20 197.42 321.99 197.94 321.56 c -h -203.37 323.15 m -202.78 323.15 202.32 322.98 201.99 322.64 c -201.67 322.31 201.50 321.84 201.50 321.24 c -201.50 317.50 l -200.71 317.50 l -200.71 316.64 l -201.50 316.64 l -201.50 315.48 l -202.66 315.37 l -202.66 316.64 l -204.32 316.64 l -204.32 317.50 l -202.66 317.50 l -202.66 321.03 l -202.66 321.86 203.02 322.28 203.73 322.28 c -203.89 322.28 204.07 322.25 204.29 322.20 c -204.29 323.00 l -203.94 323.10 203.63 323.15 203.37 323.15 c -h -209.22 322.19 m -208.53 322.83 207.87 323.15 207.23 323.15 c -206.70 323.15 206.26 322.98 205.91 322.65 c -205.57 322.32 205.39 321.90 205.39 321.40 c -205.39 320.71 205.68 320.17 206.27 319.80 c -206.85 319.42 207.69 319.24 208.78 319.24 c -209.05 319.24 l -209.05 318.47 l -209.05 317.73 208.68 317.36 207.92 317.36 c -207.31 317.36 206.65 317.55 205.94 317.93 c -205.94 316.97 l -206.72 316.65 207.45 316.50 208.13 316.50 c -208.84 316.50 209.36 316.66 209.70 316.98 c -210.04 317.30 210.21 317.79 210.21 318.47 c -210.21 321.35 l -210.21 322.01 210.41 322.34 210.82 322.34 c -210.87 322.34 210.94 322.34 211.04 322.32 c -211.12 322.96 l -210.86 323.08 210.57 323.15 210.26 323.15 c -209.72 323.15 209.37 322.83 209.22 322.19 c -h -209.05 321.56 m -209.05 319.92 l -208.67 319.91 l -208.04 319.91 207.52 320.03 207.13 320.27 c -206.74 320.51 206.55 320.82 206.55 321.21 c -206.55 321.49 206.64 321.72 206.84 321.92 c -207.04 322.11 207.27 322.20 207.55 322.20 c -208.04 322.20 208.54 321.99 209.05 321.56 c -h -212.57 323.00 m -212.57 313.75 l -213.72 313.75 l -213.72 323.00 l -h -218.53 323.15 m -217.62 323.15 216.89 322.84 216.35 322.24 c -215.81 321.64 215.54 320.83 215.54 319.82 c -215.54 318.79 215.81 317.99 216.35 317.39 c -216.90 316.79 217.64 316.50 218.57 316.50 c -219.50 316.50 220.24 316.79 220.79 317.39 c -221.33 317.99 221.61 318.79 221.61 319.81 c -221.61 320.85 221.33 321.66 220.79 322.26 c -220.24 322.85 219.49 323.15 218.53 323.15 c -h -218.55 322.28 m -219.77 322.28 220.38 321.46 220.38 319.81 c -220.38 318.18 219.78 317.36 218.57 317.36 c -217.37 317.36 216.77 318.18 216.77 319.82 c -216.77 321.46 217.36 322.28 218.55 322.28 c -h -223.43 325.12 m -223.57 324.11 l -224.24 324.43 224.89 324.59 225.54 324.59 c -226.84 324.59 227.49 323.90 227.49 322.52 c -227.49 321.52 l -227.07 322.41 226.37 322.85 225.40 322.85 c -224.63 322.85 224.03 322.58 223.58 322.02 c -223.13 321.47 222.91 320.72 222.91 319.78 c -222.91 318.81 223.16 318.02 223.67 317.41 c -224.19 316.80 224.85 316.50 225.66 316.50 c -226.37 316.50 226.98 316.79 227.49 317.36 c -227.49 316.64 l -228.65 316.64 l -228.65 321.27 l -228.65 322.26 228.60 323.00 228.50 323.48 c -228.40 323.96 228.20 324.35 227.92 324.65 c -227.41 325.19 226.63 325.46 225.57 325.46 c -224.82 325.46 224.11 325.34 223.43 325.12 c -h -227.49 320.80 m -227.49 318.15 l -226.99 317.63 226.43 317.36 225.84 317.36 c -225.30 317.36 224.89 317.58 224.59 318.00 c -224.29 318.43 224.14 319.01 224.14 319.75 c -224.14 321.15 224.63 321.85 225.61 321.85 c -226.28 321.85 226.90 321.50 227.49 320.80 c -h -230.89 324.73 m -230.89 313.75 l -233.21 313.75 l -233.21 314.62 l -231.91 314.62 l -231.91 323.87 l -233.21 323.87 l -233.21 324.73 l -h -234.58 316.93 m -234.29 313.75 l -235.74 313.75 l -235.45 316.93 l -h -240.03 323.15 m -239.17 323.15 238.46 322.83 237.89 322.19 c -237.32 321.55 237.04 320.75 237.04 319.78 c -237.04 318.75 237.32 317.94 237.88 317.36 c -238.44 316.79 239.22 316.50 240.23 316.50 c -240.72 316.50 241.28 316.56 241.89 316.70 c -241.89 317.67 l -241.24 317.48 240.71 317.38 240.30 317.38 c -239.71 317.38 239.23 317.60 238.88 318.05 c -238.52 318.49 238.34 319.08 238.34 319.82 c -238.34 320.53 238.52 321.11 238.89 321.55 c -239.26 321.99 239.74 322.21 240.33 322.21 c -240.86 322.21 241.40 322.08 241.96 321.81 c -241.96 322.81 l -241.21 323.03 240.57 323.15 240.03 323.15 c -h -246.18 323.15 m -245.27 323.15 244.54 322.84 244.00 322.24 c -243.46 321.64 243.19 320.83 243.19 319.82 c -243.19 318.79 243.46 317.99 244.00 317.39 c -244.55 316.79 245.29 316.50 246.22 316.50 c -247.15 316.50 247.89 316.79 248.44 317.39 c -248.98 317.99 249.26 318.79 249.26 319.81 c -249.26 320.85 248.98 321.66 248.44 322.26 c -247.89 322.85 247.14 323.15 246.18 323.15 c -h -246.20 322.28 m -247.42 322.28 248.03 321.46 248.03 319.81 c -248.03 318.18 247.43 317.36 246.22 317.36 c -245.02 317.36 244.42 318.18 244.42 319.82 c -244.42 321.46 245.01 322.28 246.20 322.28 c -h -251.06 323.00 m -251.06 316.64 l -252.21 316.64 l -252.21 317.83 l -252.78 316.94 253.50 316.50 254.38 316.50 c -255.23 316.50 255.81 316.94 256.12 317.83 c -256.67 316.94 257.38 316.49 258.26 316.49 c -258.82 316.49 259.25 316.66 259.56 316.99 c -259.87 317.32 260.03 317.78 260.03 318.37 c -260.03 323.00 l -258.87 323.00 l -258.87 318.55 l -258.87 317.83 258.58 317.46 258.00 317.46 c -257.41 317.46 256.78 317.89 256.12 318.73 c -256.12 323.00 l -254.96 323.00 l -254.96 318.55 l -254.96 317.82 254.67 317.46 254.08 317.46 c -253.50 317.46 252.88 317.88 252.21 318.73 c -252.21 323.00 l -h -262.26 325.31 m -262.26 316.64 l -263.42 316.64 l -263.42 317.83 l -263.89 316.94 264.60 316.50 265.54 316.50 c -266.31 316.50 266.91 316.78 267.35 317.33 c -267.79 317.89 268.01 318.66 268.01 319.62 c -268.01 320.68 267.76 321.53 267.26 322.18 c -266.77 322.82 266.11 323.15 265.30 323.15 c -264.54 323.15 263.92 322.86 263.42 322.28 c -263.42 325.31 l -h -263.42 321.48 m -264.01 322.01 264.58 322.28 265.12 322.28 c -266.23 322.28 266.78 321.43 266.78 319.74 c -266.78 318.25 266.29 317.50 265.30 317.50 c -264.66 317.50 264.03 317.85 263.42 318.55 c -h -273.79 323.00 m -273.79 321.80 l -273.18 322.70 272.44 323.15 271.56 323.15 c -271.01 323.15 270.57 322.97 270.24 322.62 c -269.91 322.27 269.75 321.80 269.75 321.21 c -269.75 316.64 l -270.90 316.64 l -270.90 320.83 l -270.90 321.31 270.97 321.65 271.11 321.85 c -271.25 322.05 271.48 322.15 271.80 322.15 c -272.51 322.15 273.17 321.69 273.79 320.76 c -273.79 316.64 l -274.95 316.64 l -274.95 323.00 l -h -279.17 323.15 m -278.59 323.15 278.13 322.98 277.80 322.64 c -277.47 322.31 277.31 321.84 277.31 321.24 c -277.31 317.50 l -276.51 317.50 l -276.51 316.64 l -277.31 316.64 l -277.31 315.48 l -278.46 315.37 l -278.46 316.64 l -280.13 316.64 l -280.13 317.50 l -278.46 317.50 l -278.46 321.03 l -278.46 321.86 278.82 322.28 279.54 322.28 c -279.70 322.28 279.88 322.25 280.10 322.20 c -280.10 323.00 l -279.74 323.10 279.44 323.15 279.17 323.15 c -h -286.42 322.79 m -285.64 323.03 284.98 323.15 284.43 323.15 c -283.49 323.15 282.73 322.83 282.14 322.21 c -281.54 321.59 281.25 320.78 281.25 319.79 c -281.25 318.82 281.51 318.03 282.03 317.42 c -282.55 316.80 283.22 316.49 284.03 316.49 c -284.80 316.49 285.40 316.76 285.82 317.31 c -286.24 317.86 286.45 318.63 286.45 319.64 c -286.44 320.00 l -282.43 320.00 l -282.59 321.51 283.33 322.27 284.65 322.27 c -285.13 322.27 285.72 322.14 286.42 321.88 c -h -282.48 319.13 m -285.29 319.13 l -285.29 317.95 284.84 317.36 283.96 317.36 c -283.07 317.36 282.58 317.95 282.48 319.13 c -h -288.22 316.93 m -287.93 313.75 l -289.38 313.75 l -289.09 316.93 l -h -292.78 324.73 m -292.78 313.75 l -290.46 313.75 l -290.46 314.62 l -291.77 314.62 l -291.77 323.87 l -290.46 323.87 l -290.46 324.73 l -h -f -79.639 371.15 m -78.779 371.15 78.066 370.83 77.500 370.19 c -76.934 369.55 76.650 368.75 76.650 367.78 c -76.650 366.75 76.931 365.94 77.491 365.36 c -78.052 364.79 78.834 364.50 79.838 364.50 c -80.334 364.50 80.889 364.56 81.502 364.70 c -81.502 365.67 l -80.850 365.48 80.318 365.38 79.908 365.38 c -79.318 365.38 78.845 365.60 78.487 366.05 c -78.130 366.49 77.951 367.08 77.951 367.82 c -77.951 368.53 78.135 369.11 78.502 369.55 c -78.869 369.99 79.350 370.21 79.943 370.21 c -80.471 370.21 81.014 370.08 81.572 369.81 c -81.572 370.81 l -80.826 371.03 80.182 371.15 79.639 371.15 c -h -83.301 371.00 m -83.301 364.64 l -84.455 364.64 l -84.455 365.83 l -84.912 364.94 85.576 364.50 86.447 364.50 c -86.564 364.50 86.688 364.51 86.816 364.53 c -86.816 365.60 l -86.617 365.54 86.441 365.50 86.289 365.50 c -85.559 365.50 84.947 365.94 84.455 366.80 c -84.455 371.00 l -h -92.875 370.79 m -92.102 371.03 91.439 371.15 90.889 371.15 c -89.951 371.15 89.187 370.83 88.595 370.21 c -88.003 369.59 87.707 368.78 87.707 367.79 c -87.707 366.82 87.968 366.03 88.489 365.42 c -89.011 364.80 89.678 364.49 90.490 364.49 c -91.260 364.49 91.854 364.76 92.274 365.31 c -92.694 365.86 92.904 366.63 92.904 367.64 c -92.898 368.00 l -88.885 368.00 l -89.053 369.51 89.793 370.27 91.105 370.27 c -91.586 370.27 92.176 370.14 92.875 369.88 c -h -88.938 367.13 m -91.744 367.13 l -91.744 365.95 91.303 365.36 90.420 365.36 c -89.533 365.36 89.039 365.95 88.938 367.13 c -h -98.178 370.19 m -97.486 370.83 96.820 371.15 96.180 371.15 c -95.652 371.15 95.215 370.98 94.867 370.65 c -94.520 370.32 94.346 369.90 94.346 369.40 c -94.346 368.71 94.638 368.17 95.222 367.80 c -95.806 367.42 96.643 367.24 97.732 367.24 c -98.008 367.24 l -98.008 366.47 l -98.008 365.73 97.629 365.36 96.871 365.36 c -96.262 365.36 95.604 365.55 94.896 365.93 c -94.896 364.97 l -95.674 364.65 96.402 364.50 97.082 364.50 c -97.793 364.50 98.317 364.66 98.655 364.98 c -98.993 365.30 99.162 365.79 99.162 366.47 c -99.162 369.35 l -99.162 370.01 99.365 370.34 99.771 370.34 c -99.822 370.34 99.896 370.34 99.994 370.32 c -100.08 370.96 l -99.814 371.08 99.525 371.15 99.209 371.15 c -98.670 371.15 98.326 370.83 98.178 370.19 c -h -98.008 369.56 m -98.008 367.92 l -97.621 367.91 l -96.988 367.91 96.477 368.03 96.086 368.27 c -95.695 368.51 95.500 368.82 95.500 369.21 c -95.500 369.49 95.598 369.72 95.793 369.92 c -95.988 370.11 96.227 370.20 96.508 370.20 c -96.988 370.20 97.488 369.99 98.008 369.56 c -h -103.43 371.15 m -102.85 371.15 102.39 370.98 102.06 370.64 c -101.73 370.31 101.57 369.84 101.57 369.24 c -101.57 365.50 l -100.77 365.50 l -100.77 364.64 l -101.57 364.64 l -101.57 363.48 l -102.72 363.37 l -102.72 364.64 l -104.39 364.64 l -104.39 365.50 l -102.72 365.50 l -102.72 369.03 l -102.72 369.86 103.08 370.28 103.80 370.28 c -103.96 370.28 104.14 370.25 104.36 370.20 c -104.36 371.00 l -104.00 371.10 103.70 371.15 103.43 371.15 c -h -110.68 370.79 m -109.90 371.03 109.24 371.15 108.69 371.15 c -107.75 371.15 106.99 370.83 106.40 370.21 c -105.80 369.59 105.51 368.78 105.51 367.79 c -105.51 366.82 105.77 366.03 106.29 365.42 c -106.81 364.80 107.48 364.49 108.29 364.49 c -109.06 364.49 109.66 364.76 110.08 365.31 c -110.50 365.86 110.71 366.63 110.71 367.64 c -110.70 368.00 l -106.69 368.00 l -106.85 369.51 107.59 370.27 108.91 370.27 c -109.39 370.27 109.98 370.14 110.68 369.88 c -h -106.74 367.13 m -109.54 367.13 l -109.54 365.95 109.10 365.36 108.22 365.36 c -107.33 365.36 106.84 365.95 106.74 367.13 c -h -112.66 371.00 m -112.66 362.33 l -113.89 362.33 l -113.89 371.00 l -h -116.15 371.00 m -116.15 364.64 l -117.31 364.64 l -117.31 365.83 l -117.92 364.94 118.66 364.50 119.55 364.50 c -120.10 364.50 120.54 364.67 120.87 365.02 c -121.19 365.37 121.36 365.84 121.36 366.43 c -121.36 371.00 l -120.20 371.00 l -120.20 366.80 l -120.20 366.33 120.13 366.00 120.00 365.79 c -119.86 365.59 119.63 365.49 119.31 365.49 c -118.60 365.49 117.93 365.96 117.31 366.88 c -117.31 371.00 l -h -125.29 371.15 m -124.76 371.15 124.12 371.02 123.37 370.78 c -123.37 369.72 l -124.12 370.09 124.78 370.28 125.34 370.28 c -125.67 370.28 125.94 370.19 126.16 370.01 c -126.38 369.83 126.49 369.61 126.49 369.34 c -126.49 368.94 126.18 368.62 125.57 368.36 c -124.90 368.07 l -123.90 367.66 123.40 367.06 123.40 366.28 c -123.40 365.73 123.60 365.29 123.99 364.97 c -124.38 364.66 124.92 364.50 125.61 364.50 c -125.96 364.50 126.40 364.54 126.92 364.64 c -127.16 364.69 l -127.16 365.65 l -126.52 365.46 126.01 365.36 125.63 365.36 c -124.89 365.36 124.52 365.63 124.52 366.17 c -124.52 366.52 124.80 366.81 125.36 367.05 c -125.92 367.29 l -126.54 367.55 126.99 367.83 127.25 368.13 c -127.51 368.42 127.64 368.79 127.64 369.23 c -127.64 369.79 127.42 370.25 126.98 370.61 c -126.54 370.97 125.98 371.15 125.29 371.15 c -h -131.63 371.15 m -131.04 371.15 130.59 370.98 130.26 370.64 c -129.93 370.31 129.77 369.84 129.77 369.24 c -129.77 365.50 l -128.97 365.50 l -128.97 364.64 l -129.77 364.64 l -129.77 363.48 l -130.92 363.37 l -130.92 364.64 l -132.58 364.64 l -132.58 365.50 l -130.92 365.50 l -130.92 369.03 l -130.92 369.86 131.28 370.28 132.00 370.28 c -132.15 370.28 132.34 370.25 132.55 370.20 c -132.55 371.00 l -132.20 371.10 131.89 371.15 131.63 371.15 c -h -137.49 370.19 m -136.80 370.83 136.13 371.15 135.49 371.15 c -134.96 371.15 134.53 370.98 134.18 370.65 c -133.83 370.32 133.66 369.90 133.66 369.40 c -133.66 368.71 133.95 368.17 134.53 367.80 c -135.12 367.42 135.95 367.24 137.04 367.24 c -137.32 367.24 l -137.32 366.47 l -137.32 365.73 136.94 365.36 136.18 365.36 c -135.57 365.36 134.91 365.55 134.21 365.93 c -134.21 364.97 l -134.98 364.65 135.71 364.50 136.39 364.50 c -137.10 364.50 137.63 364.66 137.97 364.98 c -138.30 365.30 138.47 365.79 138.47 366.47 c -138.47 369.35 l -138.47 370.01 138.68 370.34 139.08 370.34 c -139.13 370.34 139.21 370.34 139.30 370.32 c -139.39 370.96 l -139.12 371.08 138.84 371.15 138.52 371.15 c -137.98 371.15 137.64 370.83 137.49 370.19 c -h -137.32 369.56 m -137.32 367.92 l -136.93 367.91 l -136.30 367.91 135.79 368.03 135.40 368.27 c -135.01 368.51 134.81 368.82 134.81 369.21 c -134.81 369.49 134.91 369.72 135.10 369.92 c -135.30 370.11 135.54 370.20 135.82 370.20 c -136.30 370.20 136.80 369.99 137.32 369.56 c -h -140.83 371.00 m -140.83 364.64 l -141.99 364.64 l -141.99 365.83 l -142.60 364.94 143.34 364.50 144.23 364.50 c -144.78 364.50 145.22 364.67 145.54 365.02 c -145.87 365.37 146.04 365.84 146.04 366.43 c -146.04 371.00 l -144.88 371.00 l -144.88 366.80 l -144.88 366.33 144.81 366.00 144.67 365.79 c -144.54 365.59 144.31 365.49 143.99 365.49 c -143.28 365.49 142.61 365.96 141.99 366.88 c -141.99 371.00 l -h -150.77 371.15 m -149.91 371.15 149.19 370.83 148.63 370.19 c -148.06 369.55 147.78 368.75 147.78 367.78 c -147.78 366.75 148.06 365.94 148.62 365.36 c -149.18 364.79 149.96 364.50 150.96 364.50 c -151.46 364.50 152.02 364.56 152.63 364.70 c -152.63 365.67 l -151.98 365.48 151.45 365.38 151.04 365.38 c -150.45 365.38 149.97 365.60 149.61 366.05 c -149.26 366.49 149.08 367.08 149.08 367.82 c -149.08 368.53 149.26 369.11 149.63 369.55 c -150.00 369.99 150.48 370.21 151.07 370.21 c -151.60 370.21 152.14 370.08 152.70 369.81 c -152.70 370.81 l -151.95 371.03 151.31 371.15 150.77 371.15 c -h -159.09 370.79 m -158.32 371.03 157.66 371.15 157.11 371.15 c -156.17 371.15 155.40 370.83 154.81 370.21 c -154.22 369.59 153.92 368.78 153.92 367.79 c -153.92 366.82 154.18 366.03 154.71 365.42 c -155.23 364.80 155.89 364.49 156.71 364.49 c -157.48 364.49 158.07 364.76 158.49 365.31 c -158.91 365.86 159.12 366.63 159.12 367.64 c -159.12 368.00 l -155.10 368.00 l -155.27 369.51 156.01 370.27 157.32 370.27 c -157.80 370.27 158.39 370.14 159.09 369.88 c -h -155.15 367.13 m -157.96 367.13 l -157.96 365.95 157.52 365.36 156.64 365.36 c -155.75 365.36 155.26 365.95 155.15 367.13 c -h -168.20 371.00 m -161.26 367.53 l -168.20 364.06 l -168.20 365.03 l -163.20 367.53 l -168.20 370.03 l -h -172.56 371.15 m -171.98 371.15 171.52 370.98 171.19 370.64 c -170.86 370.31 170.70 369.84 170.70 369.24 c -170.70 365.50 l -169.90 365.50 l -169.90 364.64 l -170.70 364.64 l -170.70 363.48 l -171.85 363.37 l -171.85 364.64 l -173.52 364.64 l -173.52 365.50 l -171.85 365.50 l -171.85 369.03 l -171.85 369.86 172.21 370.28 172.93 370.28 c -173.08 370.28 173.27 370.25 173.49 370.20 c -173.49 371.00 l -173.13 371.10 172.82 371.15 172.56 371.15 c -h -177.63 371.15 m -176.72 371.15 175.99 370.84 175.45 370.24 c -174.91 369.64 174.64 368.83 174.64 367.82 c -174.64 366.79 174.91 365.99 175.45 365.39 c -176.00 364.79 176.74 364.50 177.67 364.50 c -178.61 364.50 179.34 364.79 179.89 365.39 c -180.43 365.99 180.71 366.79 180.71 367.81 c -180.71 368.85 180.43 369.66 179.89 370.26 c -179.34 370.85 178.59 371.15 177.63 371.15 c -h -177.65 370.28 m -178.87 370.28 179.48 369.46 179.48 367.81 c -179.48 366.18 178.88 365.36 177.67 365.36 c -176.47 365.36 175.87 366.18 175.87 367.82 c -175.87 369.46 176.46 370.28 177.65 370.28 c -h -182.51 371.00 m -182.51 361.75 l -183.67 361.75 l -183.67 367.72 l -186.36 364.64 l -187.60 364.64 l -185.03 367.61 l -188.14 371.00 l -186.66 371.00 l -183.67 367.74 l -183.67 371.00 l -h -194.19 370.79 m -193.42 371.03 192.75 371.15 192.20 371.15 c -191.27 371.15 190.50 370.83 189.91 370.21 c -189.32 369.59 189.02 368.78 189.02 367.79 c -189.02 366.82 189.28 366.03 189.80 365.42 c -190.33 364.80 190.99 364.49 191.80 364.49 c -192.57 364.49 193.17 364.76 193.59 365.31 c -194.01 365.86 194.22 366.63 194.22 367.64 c -194.21 368.00 l -190.20 368.00 l -190.37 369.51 191.11 370.27 192.42 370.27 c -192.90 370.27 193.49 370.14 194.19 369.88 c -h -190.25 367.13 m -193.06 367.13 l -193.06 365.95 192.62 365.36 191.73 365.36 c -190.85 365.36 190.35 365.95 190.25 367.13 c -h -196.21 371.00 m -196.21 364.64 l -197.37 364.64 l -197.37 365.83 l -197.97 364.94 198.72 364.50 199.60 364.50 c -200.15 364.50 200.59 364.67 200.92 365.02 c -201.25 365.37 201.41 365.84 201.41 366.43 c -201.41 371.00 l -200.26 371.00 l -200.26 366.80 l -200.26 366.33 200.19 366.00 200.05 365.79 c -199.91 365.59 199.68 365.49 199.36 365.49 c -198.66 365.49 197.99 365.96 197.37 366.88 c -197.37 371.00 l -h -203.68 372.88 m -203.68 372.45 l -204.05 372.34 204.24 371.90 204.24 371.12 c -204.24 371.00 l -203.68 371.00 l -203.68 369.55 l -205.12 369.55 l -205.12 370.81 l -205.12 372.09 204.64 372.78 203.68 372.88 c -h -213.16 371.15 m -212.58 371.15 212.12 370.98 211.79 370.64 c -211.46 370.31 211.30 369.84 211.30 369.24 c -211.30 365.50 l -210.50 365.50 l -210.50 364.64 l -211.30 364.64 l -211.30 363.48 l -212.45 363.37 l -212.45 364.64 l -214.12 364.64 l -214.12 365.50 l -212.45 365.50 l -212.45 369.03 l -212.45 369.86 212.81 370.28 213.53 370.28 c -213.68 370.28 213.87 370.25 214.09 370.20 c -214.09 371.00 l -213.73 371.10 213.42 371.15 213.16 371.15 c -h -220.40 370.79 m -219.63 371.03 218.97 371.15 218.42 371.15 c -217.48 371.15 216.72 370.83 216.12 370.21 c -215.53 369.59 215.24 368.78 215.24 367.79 c -215.24 366.82 215.50 366.03 216.02 365.42 c -216.54 364.80 217.21 364.49 218.02 364.49 c -218.79 364.49 219.38 364.76 219.80 365.31 c -220.22 365.86 220.43 366.63 220.43 367.64 c -220.43 368.00 l -216.41 368.00 l -216.58 369.51 217.32 370.27 218.63 370.27 c -219.12 370.27 219.71 370.14 220.40 369.88 c -h -216.47 367.13 m -219.27 367.13 l -219.27 365.95 218.83 365.36 217.95 365.36 c -217.06 365.36 216.57 365.95 216.47 367.13 c -h -222.43 371.00 m -222.43 364.64 l -223.58 364.64 l -223.58 365.83 l -224.19 364.94 224.94 364.50 225.82 364.50 c -226.37 364.50 226.81 364.67 227.14 365.02 c -227.46 365.37 227.63 365.84 227.63 366.43 c -227.63 371.00 l -226.47 371.00 l -226.47 366.80 l -226.47 366.33 226.41 366.00 226.27 365.79 c -226.13 365.59 225.90 365.49 225.58 365.49 c -224.87 365.49 224.21 365.96 223.58 366.88 c -223.58 371.00 l -h -233.15 370.19 m -232.46 370.83 231.80 371.15 231.16 371.15 c -230.63 371.15 230.19 370.98 229.84 370.65 c -229.50 370.32 229.32 369.90 229.32 369.40 c -229.32 368.71 229.61 368.17 230.20 367.80 c -230.78 367.42 231.62 367.24 232.71 367.24 c -232.98 367.24 l -232.98 366.47 l -232.98 365.73 232.61 365.36 231.85 365.36 c -231.24 365.36 230.58 365.55 229.87 365.93 c -229.87 364.97 l -230.65 364.65 231.38 364.50 232.06 364.50 c -232.77 364.50 233.29 364.66 233.63 364.98 c -233.97 365.30 234.14 365.79 234.14 366.47 c -234.14 369.35 l -234.14 370.01 234.34 370.34 234.75 370.34 c -234.80 370.34 234.87 370.34 234.97 370.32 c -235.05 370.96 l -234.79 371.08 234.50 371.15 234.19 371.15 c -233.65 371.15 233.30 370.83 233.15 370.19 c -h -232.98 369.56 m -232.98 367.92 l -232.60 367.91 l -231.96 367.91 231.45 368.03 231.06 368.27 c -230.67 368.51 230.48 368.82 230.48 369.21 c -230.48 369.49 230.57 369.72 230.77 369.92 c -230.96 370.11 231.20 370.20 231.48 370.20 c -231.96 370.20 232.46 369.99 232.98 369.56 c -h -236.50 371.00 m -236.50 364.64 l -237.65 364.64 l -237.65 365.83 l -238.26 364.94 239.01 364.50 239.89 364.50 c -240.44 364.50 240.88 364.67 241.21 365.02 c -241.54 365.37 241.70 365.84 241.70 366.43 c -241.70 371.00 l -240.55 371.00 l -240.55 366.80 l -240.55 366.33 240.48 366.00 240.34 365.79 c -240.20 365.59 239.97 365.49 239.65 365.49 c -238.95 365.49 238.28 365.96 237.65 366.88 c -237.65 371.00 l -h -245.86 371.15 m -245.27 371.15 244.81 370.98 244.49 370.64 c -244.16 370.31 243.99 369.84 243.99 369.24 c -243.99 365.50 l -243.20 365.50 l -243.20 364.64 l -243.99 364.64 l -243.99 363.48 l -245.15 363.37 l -245.15 364.64 l -246.81 364.64 l -246.81 365.50 l -245.15 365.50 l -245.15 369.03 l -245.15 369.86 245.51 370.28 246.23 370.28 c -246.38 370.28 246.56 370.25 246.78 370.20 c -246.78 371.00 l -246.43 371.10 246.12 371.15 245.86 371.15 c -h -247.28 373.02 m -247.28 372.15 l -253.28 372.15 l -253.28 373.02 l -h -254.44 371.00 m -254.44 364.64 l -255.59 364.64 l -255.59 371.00 l -h -254.44 363.48 m -254.44 362.33 l -255.59 362.33 l -255.59 363.48 l -h -261.99 371.00 m -261.99 369.80 l -261.52 370.70 260.81 371.15 259.87 371.15 c -259.10 371.15 258.50 370.87 258.06 370.31 c -257.62 369.75 257.40 368.99 257.40 368.02 c -257.40 366.96 257.65 366.11 258.15 365.46 c -258.65 364.82 259.30 364.50 260.11 364.50 c -260.87 364.50 261.49 364.79 261.99 365.36 c -261.99 361.75 l -263.15 361.75 l -263.15 371.00 l -h -261.99 366.15 m -261.39 365.63 260.82 365.36 260.29 365.36 c -259.18 365.36 258.63 366.21 258.63 367.90 c -258.63 369.39 259.12 370.13 260.11 370.13 c -260.75 370.13 261.38 369.78 261.99 369.08 c -h -265.60 371.00 m -272.54 367.53 l -265.60 364.06 l -265.60 365.03 l -270.60 367.53 l -265.60 370.03 l -h -f -70.500 374.50 m -728.50 374.50 l -S -728.00 374.00 m -722.00 368.00 l -722.00 380.00 l -h -f* -504.06 201.15 m -503.48 201.15 503.02 200.98 502.69 200.64 c -502.37 200.31 502.20 199.84 502.20 199.24 c -502.20 195.50 l -501.40 195.50 l -501.40 194.64 l -502.20 194.64 l -502.20 193.48 l -503.36 193.37 l -503.36 194.64 l -505.02 194.64 l -505.02 195.50 l -503.36 195.50 l -503.36 199.03 l -503.36 199.86 503.71 200.28 504.43 200.28 c -504.59 200.28 504.77 200.25 504.99 200.20 c -504.99 201.00 l -504.63 201.10 504.33 201.15 504.06 201.15 c -h -511.31 200.79 m -510.53 201.03 509.87 201.15 509.32 201.15 c -508.38 201.15 507.62 200.83 507.03 200.21 c -506.43 199.59 506.14 198.78 506.14 197.79 c -506.14 196.82 506.40 196.03 506.92 195.42 c -507.44 194.80 508.11 194.49 508.92 194.49 c -509.69 194.49 510.29 194.76 510.71 195.31 c -511.13 195.86 511.34 196.63 511.34 197.64 c -511.33 198.00 l -507.32 198.00 l -507.48 199.51 508.22 200.27 509.54 200.27 c -510.02 200.27 510.61 200.14 511.31 199.88 c -h -507.37 197.13 m -510.18 197.13 l -510.18 195.95 509.73 195.36 508.85 195.36 c -507.96 195.36 507.47 195.95 507.37 197.13 c -h -513.33 201.00 m -513.33 194.64 l -514.48 194.64 l -514.48 195.83 l -515.09 194.94 515.84 194.50 516.72 194.50 c -517.27 194.50 517.71 194.67 518.04 195.02 c -518.37 195.37 518.53 195.84 518.53 196.43 c -518.53 201.00 l -517.38 201.00 l -517.38 196.80 l -517.38 196.33 517.31 196.00 517.17 195.79 c -517.03 195.59 516.80 195.49 516.48 195.49 c -515.77 195.49 515.11 195.96 514.48 196.88 c -514.48 201.00 l -h -524.06 200.19 m -523.37 200.83 522.70 201.15 522.06 201.15 c -521.53 201.15 521.09 200.98 520.75 200.65 c -520.40 200.32 520.22 199.90 520.22 199.40 c -520.22 198.71 520.52 198.17 521.10 197.80 c -521.68 197.42 522.52 197.24 523.61 197.24 c -523.89 197.24 l -523.89 196.47 l -523.89 195.73 523.51 195.36 522.75 195.36 c -522.14 195.36 521.48 195.55 520.78 195.93 c -520.78 194.97 l -521.55 194.65 522.28 194.50 522.96 194.50 c -523.67 194.50 524.20 194.66 524.53 194.98 c -524.87 195.30 525.04 195.79 525.04 196.47 c -525.04 199.35 l -525.04 200.01 525.24 200.34 525.65 200.34 c -525.70 200.34 525.78 200.34 525.87 200.32 c -525.96 200.96 l -525.69 201.08 525.40 201.15 525.09 201.15 c -524.55 201.15 524.21 200.83 524.06 200.19 c -h -523.89 199.56 m -523.89 197.92 l -523.50 197.91 l -522.87 197.91 522.36 198.03 521.96 198.27 c -521.57 198.51 521.38 198.82 521.38 199.21 c -521.38 199.49 521.48 199.72 521.67 199.92 c -521.87 200.11 522.11 200.20 522.39 200.20 c -522.87 200.20 523.37 199.99 523.89 199.56 c -h -527.40 201.00 m -527.40 194.64 l -528.56 194.64 l -528.56 195.83 l -529.17 194.94 529.91 194.50 530.79 194.50 c -531.35 194.50 531.79 194.67 532.11 195.02 c -532.44 195.37 532.61 195.84 532.61 196.43 c -532.61 201.00 l -531.45 201.00 l -531.45 196.80 l -531.45 196.33 531.38 196.00 531.24 195.79 c -531.10 195.59 530.88 195.49 530.55 195.49 c -529.85 195.49 529.18 195.96 528.56 196.88 c -528.56 201.00 l -h -536.76 201.15 m -536.17 201.15 535.72 200.98 535.39 200.64 c -535.06 200.31 534.90 199.84 534.90 199.24 c -534.90 195.50 l -534.10 195.50 l -534.10 194.64 l -534.90 194.64 l -534.90 193.48 l -536.05 193.37 l -536.05 194.64 l -537.71 194.64 l -537.71 195.50 l -536.05 195.50 l -536.05 199.03 l -536.05 199.86 536.41 200.28 537.13 200.28 c -537.28 200.28 537.47 200.25 537.69 200.20 c -537.69 201.00 l -537.33 201.10 537.02 201.15 536.76 201.15 c -h -541.03 201.15 m -540.50 201.15 539.86 201.02 539.10 200.78 c -539.10 199.72 l -539.86 200.09 540.51 200.28 541.07 200.28 c -541.40 200.28 541.68 200.19 541.90 200.01 c -542.12 199.83 542.23 199.61 542.23 199.34 c -542.23 198.94 541.92 198.62 541.31 198.36 c -540.63 198.07 l -539.64 197.66 539.14 197.06 539.14 196.28 c -539.14 195.73 539.33 195.29 539.73 194.97 c -540.12 194.66 540.66 194.50 541.34 194.50 c -541.70 194.50 542.14 194.54 542.66 194.64 c -542.90 194.69 l -542.90 195.65 l -542.26 195.46 541.74 195.36 541.37 195.36 c -540.62 195.36 540.25 195.63 540.25 196.17 c -540.25 196.52 540.53 196.81 541.10 197.05 c -541.65 197.29 l -542.28 197.55 542.73 197.83 542.99 198.13 c -543.25 198.42 543.38 198.79 543.38 199.23 c -543.38 199.79 543.16 200.25 542.72 200.61 c -542.28 200.97 541.71 201.15 541.03 201.15 c -h -f -[ 5.0000 5.0000] 0.0000 d -549.50 204.50 m -70.500 204.50 l -S -[] 0.0000 d -70.000 204.00 m -76.000 198.00 l -76.000 210.00 l -h -f* -328.13 117.00 m -328.13 115.80 l -327.52 116.70 326.78 117.15 325.90 117.15 c -325.35 117.15 324.90 116.97 324.58 116.62 c -324.25 116.27 324.08 115.80 324.08 115.21 c -324.08 110.64 l -325.24 110.64 l -325.24 114.83 l -325.24 115.31 325.31 115.65 325.45 115.85 c -325.58 116.05 325.82 116.15 326.14 116.15 c -326.84 116.15 327.51 115.69 328.13 114.76 c -328.13 110.64 l -329.29 110.64 l -329.29 117.00 l -h -331.60 117.00 m -331.60 110.64 l -332.76 110.64 l -332.76 111.83 l -333.37 110.94 334.11 110.50 334.99 110.50 c -335.54 110.50 335.98 110.67 336.31 111.02 c -336.64 111.37 336.80 111.84 336.80 112.43 c -336.80 117.00 l -335.65 117.00 l -335.65 112.80 l -335.65 112.33 335.58 112.00 335.44 111.79 c -335.30 111.59 335.07 111.49 334.75 111.49 c -334.05 111.49 333.38 111.96 332.76 112.88 c -332.76 117.00 l -h -340.74 117.15 m -340.21 117.15 339.57 117.02 338.81 116.78 c -338.81 115.72 l -339.57 116.09 340.22 116.28 340.78 116.28 c -341.12 116.28 341.39 116.19 341.61 116.01 c -341.83 115.83 341.94 115.61 341.94 115.34 c -341.94 114.94 341.63 114.62 341.02 114.36 c -340.34 114.07 l -339.35 113.66 338.85 113.06 338.85 112.28 c -338.85 111.73 339.05 111.29 339.44 110.97 c -339.83 110.66 340.37 110.50 341.05 110.50 c -341.41 110.50 341.85 110.54 342.37 110.64 c -342.61 110.69 l -342.61 111.65 l -341.97 111.46 341.46 111.36 341.08 111.36 c -340.33 111.36 339.96 111.63 339.96 112.17 c -339.96 112.52 340.24 112.81 340.81 113.05 c -341.36 113.29 l -341.99 113.55 342.44 113.83 342.70 114.13 c -342.96 114.42 343.09 114.79 343.09 115.23 c -343.09 115.79 342.87 116.25 342.43 116.61 c -341.99 116.97 341.42 117.15 340.74 117.15 c -h -347.65 117.15 m -346.79 117.15 346.08 116.83 345.51 116.19 c -344.95 115.55 344.66 114.75 344.66 113.78 c -344.66 112.75 344.94 111.94 345.50 111.36 c -346.06 110.79 346.85 110.50 347.85 110.50 c -348.35 110.50 348.90 110.56 349.51 110.70 c -349.51 111.67 l -348.86 111.48 348.33 111.38 347.92 111.38 c -347.33 111.38 346.86 111.60 346.50 112.05 c -346.14 112.49 345.96 113.08 345.96 113.82 c -345.96 114.53 346.15 115.11 346.51 115.55 c -346.88 115.99 347.36 116.21 347.96 116.21 c -348.48 116.21 349.03 116.08 349.58 115.81 c -349.58 116.81 l -348.84 117.03 348.19 117.15 347.65 117.15 c -h -353.80 117.15 m -352.89 117.15 352.17 116.84 351.62 116.24 c -351.08 115.64 350.81 114.83 350.81 113.82 c -350.81 112.79 351.08 111.99 351.63 111.39 c -352.17 110.79 352.91 110.50 353.84 110.50 c -354.78 110.50 355.52 110.79 356.06 111.39 c -356.61 111.99 356.88 112.79 356.88 113.81 c -356.88 114.85 356.61 115.66 356.06 116.26 c -355.51 116.85 354.76 117.15 353.80 117.15 c -h -353.82 116.28 m -355.04 116.28 355.65 115.46 355.65 113.81 c -355.65 112.18 355.05 111.36 353.84 111.36 c -352.64 111.36 352.04 112.18 352.04 113.82 c -352.04 115.46 352.63 116.28 353.82 116.28 c -h -358.68 119.31 m -358.68 110.64 l -359.84 110.64 l -359.84 111.83 l -360.31 110.94 361.02 110.50 361.96 110.50 c -362.73 110.50 363.33 110.78 363.77 111.33 c -364.21 111.89 364.43 112.66 364.43 113.62 c -364.43 114.68 364.18 115.53 363.68 116.18 c -363.19 116.82 362.53 117.15 361.72 117.15 c -360.96 117.15 360.34 116.86 359.84 116.28 c -359.84 119.31 l -h -359.84 115.48 m -360.43 116.01 361.00 116.28 361.54 116.28 c -362.65 116.28 363.20 115.43 363.20 113.74 c -363.20 112.25 362.71 111.50 361.72 111.50 c -361.08 111.50 360.45 111.85 359.84 112.55 c -h -370.90 116.79 m -370.13 117.03 369.46 117.15 368.91 117.15 c -367.98 117.15 367.21 116.83 366.62 116.21 c -366.03 115.59 365.73 114.78 365.73 113.79 c -365.73 112.82 365.99 112.03 366.51 111.42 c -367.04 110.80 367.70 110.49 368.52 110.49 c -369.29 110.49 369.88 110.76 370.30 111.31 c -370.72 111.86 370.93 112.63 370.93 113.64 c -370.92 114.00 l -366.91 114.00 l -367.08 115.51 367.82 116.27 369.13 116.27 c -369.61 116.27 370.20 116.14 370.90 115.88 c -h -366.96 113.13 m -369.77 113.13 l -369.77 111.95 369.33 111.36 368.45 111.36 c -367.56 111.36 367.06 111.95 366.96 113.13 c -h -377.01 117.00 m -377.01 115.80 l -376.54 116.70 375.83 117.15 374.88 117.15 c -374.12 117.15 373.52 116.87 373.08 116.31 c -372.64 115.75 372.42 114.99 372.42 114.02 c -372.42 112.96 372.67 112.11 373.17 111.46 c -373.66 110.82 374.32 110.50 375.13 110.50 c -375.88 110.50 376.51 110.79 377.01 111.36 c -377.01 107.75 l -378.17 107.75 l -378.17 117.00 l -h -377.01 112.15 m -376.41 111.63 375.84 111.36 375.31 111.36 c -374.20 111.36 373.65 112.21 373.65 113.90 c -373.65 115.39 374.14 116.13 375.12 116.13 c -375.77 116.13 376.39 115.78 377.01 115.08 c -h -379.32 119.02 m -379.32 118.15 l -385.32 118.15 l -385.32 119.02 l -h -388.38 117.15 m -387.80 117.15 387.34 116.98 387.01 116.64 c -386.69 116.31 386.52 115.84 386.52 115.24 c -386.52 111.50 l -385.72 111.50 l -385.72 110.64 l -386.52 110.64 l -386.52 109.48 l -387.68 109.37 l -387.68 110.64 l -389.34 110.64 l -389.34 111.50 l -387.68 111.50 l -387.68 115.03 l -387.68 115.86 388.04 116.28 388.75 116.28 c -388.91 116.28 389.09 116.25 389.31 116.20 c -389.31 117.00 l -388.96 117.10 388.65 117.15 388.38 117.15 c -h -393.45 117.15 m -392.54 117.15 391.82 116.84 391.27 116.24 c -390.73 115.64 390.46 114.83 390.46 113.82 c -390.46 112.79 390.73 111.99 391.28 111.39 c -391.82 110.79 392.56 110.50 393.49 110.50 c -394.43 110.50 395.17 110.79 395.71 111.39 c -396.26 111.99 396.53 112.79 396.53 113.81 c -396.53 114.85 396.26 115.66 395.71 116.26 c -395.16 116.85 394.41 117.15 393.45 117.15 c -h -393.47 116.28 m -394.69 116.28 395.30 115.46 395.30 113.81 c -395.30 112.18 394.70 111.36 393.49 111.36 c -392.29 111.36 391.69 112.18 391.69 113.82 c -391.69 115.46 392.28 116.28 393.47 116.28 c -h -398.33 117.00 m -398.33 107.75 l -399.49 107.75 l -399.49 113.72 l -402.18 110.64 l -403.43 110.64 l -400.85 113.61 l -403.96 117.00 l -402.48 117.00 l -399.49 113.74 l -399.49 117.00 l -h -410.01 116.79 m -409.24 117.03 408.58 117.15 408.03 117.15 c -407.09 117.15 406.32 116.83 405.73 116.21 c -405.14 115.59 404.84 114.78 404.84 113.79 c -404.84 112.82 405.10 112.03 405.63 111.42 c -406.15 110.80 406.81 110.49 407.63 110.49 c -408.40 110.49 408.99 110.76 409.41 111.31 c -409.83 111.86 410.04 112.63 410.04 113.64 c -410.04 114.00 l -406.02 114.00 l -406.19 115.51 406.93 116.27 408.24 116.27 c -408.72 116.27 409.31 116.14 410.01 115.88 c -h -406.07 113.13 m -408.88 113.13 l -408.88 111.95 408.44 111.36 407.56 111.36 c -406.67 111.36 406.18 111.95 406.07 113.13 c -h -412.03 117.00 m -412.03 110.64 l -413.19 110.64 l -413.19 111.83 l -413.80 110.94 414.54 110.50 415.43 110.50 c -415.98 110.50 416.42 110.67 416.74 111.02 c -417.07 111.37 417.24 111.84 417.24 112.43 c -417.24 117.00 l -416.08 117.00 l -416.08 112.80 l -416.08 112.33 416.01 112.00 415.87 111.79 c -415.74 111.59 415.51 111.49 415.19 111.49 c -414.48 111.49 413.81 111.96 413.19 112.88 c -413.19 117.00 l -h -419.50 118.88 m -419.50 118.45 l -419.87 118.34 420.06 117.90 420.06 117.12 c -420.06 117.00 l -419.50 117.00 l -419.50 115.55 l -420.95 115.55 l -420.95 116.81 l -420.95 118.09 420.46 118.78 419.50 118.88 c -h -427.10 119.12 m -427.23 118.11 l -427.90 118.43 428.56 118.59 429.21 118.59 c -430.51 118.59 431.16 117.90 431.16 116.52 c -431.16 115.52 l -430.73 116.41 430.03 116.85 429.06 116.85 c -428.30 116.85 427.69 116.58 427.24 116.02 c -426.79 115.47 426.57 114.72 426.57 113.78 c -426.57 112.81 426.83 112.02 427.34 111.41 c -427.85 110.80 428.51 110.50 429.32 110.50 c -430.04 110.50 430.65 110.79 431.16 111.36 c -431.16 110.64 l -432.32 110.64 l -432.32 115.27 l -432.32 116.26 432.27 117.00 432.16 117.48 c -432.06 117.96 431.87 118.35 431.58 118.65 c -431.08 119.19 430.29 119.46 429.23 119.46 c -428.49 119.46 427.78 119.34 427.10 119.12 c -h -431.16 114.80 m -431.16 112.15 l -430.65 111.63 430.10 111.36 429.50 111.36 c -428.97 111.36 428.55 111.58 428.25 112.00 c -427.95 112.43 427.80 113.01 427.80 113.75 c -427.80 115.15 428.29 115.85 429.27 115.85 c -429.94 115.85 430.57 115.50 431.16 114.80 c -h -434.56 117.00 m -434.56 107.75 l -435.71 107.75 l -435.71 117.00 l -h -440.52 117.15 m -439.61 117.15 438.88 116.84 438.34 116.24 c -437.79 115.64 437.52 114.83 437.52 113.82 c -437.52 112.79 437.79 111.99 438.34 111.39 c -438.88 110.79 439.62 110.50 440.56 110.50 c -441.49 110.50 442.23 110.79 442.77 111.39 c -443.32 111.99 443.59 112.79 443.59 113.81 c -443.59 114.85 443.32 115.66 442.77 116.26 c -442.22 116.85 441.47 117.15 440.52 117.15 c -h -440.53 116.28 m -441.76 116.28 442.37 115.46 442.37 113.81 c -442.37 112.18 441.76 111.36 440.56 111.36 c -439.35 111.36 438.75 112.18 438.75 113.82 c -438.75 115.46 439.35 116.28 440.53 116.28 c -h -445.40 117.07 m -445.40 107.75 l -446.55 107.75 l -446.55 111.83 l -447.02 110.94 447.73 110.50 448.68 110.50 c -449.44 110.50 450.05 110.78 450.49 111.33 c -450.92 111.89 451.14 112.66 451.14 113.62 c -451.14 114.68 450.90 115.53 450.40 116.18 c -449.90 116.82 449.24 117.15 448.43 117.15 c -447.68 117.15 447.05 116.86 446.55 116.28 c -446.41 117.07 l -h -446.55 115.48 m -447.14 116.01 447.71 116.28 448.25 116.28 c -449.36 116.28 449.91 115.43 449.91 113.74 c -449.91 112.25 449.42 111.50 448.44 111.50 c -447.79 111.50 447.16 111.85 446.55 112.55 c -h -456.23 116.19 m -455.54 116.83 454.87 117.15 454.23 117.15 c -453.71 117.15 453.27 116.98 452.92 116.65 c -452.57 116.32 452.40 115.90 452.40 115.40 c -452.40 114.71 452.69 114.17 453.27 113.80 c -453.86 113.42 454.70 113.24 455.79 113.24 c -456.06 113.24 l -456.06 112.47 l -456.06 111.73 455.68 111.36 454.92 111.36 c -454.31 111.36 453.66 111.55 452.95 111.93 c -452.95 110.97 l -453.73 110.65 454.46 110.50 455.13 110.50 c -455.85 110.50 456.37 110.66 456.71 110.98 c -457.05 111.30 457.21 111.79 457.21 112.47 c -457.21 115.35 l -457.21 116.01 457.42 116.34 457.82 116.34 c -457.88 116.34 457.95 116.34 458.05 116.32 c -458.13 116.96 l -457.87 117.08 457.58 117.15 457.26 117.15 c -456.72 117.15 456.38 116.83 456.23 116.19 c -h -456.06 115.56 m -456.06 113.92 l -455.67 113.91 l -455.04 113.91 454.53 114.03 454.14 114.27 c -453.75 114.51 453.55 114.82 453.55 115.21 c -453.55 115.49 453.65 115.72 453.85 115.92 c -454.04 116.11 454.28 116.20 454.56 116.20 c -455.04 116.20 455.54 115.99 456.06 115.56 c -h -459.58 117.00 m -459.58 107.75 l -460.73 107.75 l -460.73 117.00 l -h -464.71 117.22 m -464.13 117.22 463.38 117.09 462.48 116.84 c -462.48 115.62 l -463.45 116.07 464.26 116.30 464.88 116.30 c -465.37 116.30 465.76 116.17 466.05 115.92 c -466.35 115.66 466.50 115.33 466.50 114.91 c -466.50 114.57 466.40 114.29 466.21 114.05 c -466.01 113.81 465.66 113.54 465.14 113.25 c -464.54 112.90 l -463.80 112.48 463.28 112.08 462.98 111.71 c -462.67 111.34 462.52 110.90 462.52 110.41 c -462.52 109.74 462.77 109.19 463.25 108.76 c -463.73 108.33 464.35 108.11 465.10 108.11 c -465.77 108.11 466.47 108.22 467.22 108.45 c -467.22 109.57 l -466.30 109.21 465.62 109.03 465.17 109.03 c -464.75 109.03 464.39 109.14 464.12 109.37 c -463.84 109.60 463.70 109.88 463.70 110.23 c -463.70 110.52 463.80 110.77 464.01 110.99 c -464.21 111.22 464.58 111.48 465.12 111.79 c -465.74 112.14 l -466.49 112.57 467.01 112.97 467.31 113.35 c -467.61 113.73 467.76 114.18 467.76 114.71 c -467.76 115.47 467.48 116.07 466.92 116.53 c -466.36 116.99 465.63 117.22 464.71 117.22 c -h -474.17 116.79 m -473.40 117.03 472.74 117.15 472.19 117.15 c -471.25 117.15 470.48 116.83 469.89 116.21 c -469.30 115.59 469.00 114.78 469.00 113.79 c -469.00 112.82 469.26 112.03 469.79 111.42 c -470.31 110.80 470.97 110.49 471.79 110.49 c -472.56 110.49 473.15 110.76 473.57 111.31 c -473.99 111.86 474.20 112.63 474.20 113.64 c -474.20 114.00 l -470.18 114.00 l -470.35 115.51 471.09 116.27 472.40 116.27 c -472.88 116.27 473.47 116.14 474.17 115.88 c -h -470.23 113.13 m -473.04 113.13 l -473.04 111.95 472.60 111.36 471.72 111.36 c -470.83 111.36 470.34 111.95 470.23 113.13 c -h -476.19 117.00 m -476.19 110.64 l -477.35 110.64 l -477.35 111.83 l -477.80 110.94 478.47 110.50 479.34 110.50 c -479.46 110.50 479.58 110.51 479.71 110.53 c -479.71 111.60 l -479.51 111.54 479.33 111.50 479.18 111.50 c -478.45 111.50 477.84 111.94 477.35 112.80 c -477.35 117.00 l -h -482.42 117.00 m -480.05 110.64 l -481.21 110.64 l -483.06 115.59 l -485.01 110.64 l -486.09 110.64 l -483.58 117.00 l -h -487.31 117.00 m -487.31 110.64 l -488.47 110.64 l -488.47 117.00 l -h -487.31 109.48 m -487.31 108.33 l -488.47 108.33 l -488.47 109.48 l -h -493.27 117.15 m -492.41 117.15 491.70 116.83 491.13 116.19 c -490.56 115.55 490.28 114.75 490.28 113.78 c -490.28 112.75 490.56 111.94 491.12 111.36 c -491.68 110.79 492.46 110.50 493.47 110.50 c -493.96 110.50 494.52 110.56 495.13 110.70 c -495.13 111.67 l -494.48 111.48 493.95 111.38 493.54 111.38 c -492.95 111.38 492.47 111.60 492.12 112.05 c -491.76 112.49 491.58 113.08 491.58 113.82 c -491.58 114.53 491.76 115.11 492.13 115.55 c -492.50 115.99 492.98 116.21 493.57 116.21 c -494.10 116.21 494.64 116.08 495.20 115.81 c -495.20 116.81 l -494.46 117.03 493.81 117.15 493.27 117.15 c -h -501.59 116.79 m -500.82 117.03 500.16 117.15 499.61 117.15 c -498.67 117.15 497.91 116.83 497.31 116.21 c -496.72 115.59 496.43 114.78 496.43 113.79 c -496.43 112.82 496.69 112.03 497.21 111.42 c -497.73 110.80 498.40 110.49 499.21 110.49 c -499.98 110.49 500.57 110.76 500.99 111.31 c -501.41 111.86 501.62 112.63 501.62 113.64 c -501.62 114.00 l -497.60 114.00 l -497.77 115.51 498.51 116.27 499.82 116.27 c -500.30 116.27 500.89 116.14 501.59 115.88 c -h -497.66 113.13 m -500.46 113.13 l -500.46 111.95 500.02 111.36 499.14 111.36 c -498.25 111.36 497.76 111.95 497.66 113.13 c -h -507.29 117.22 m -505.95 117.22 504.91 116.82 504.17 116.03 c -503.44 115.24 503.07 114.12 503.07 112.67 c -503.07 111.22 503.44 110.10 504.19 109.31 c -504.94 108.51 505.99 108.11 507.35 108.11 c -508.13 108.11 509.04 108.24 510.08 108.49 c -510.08 109.65 l -508.90 109.24 507.98 109.03 507.34 109.03 c -506.39 109.03 505.67 109.35 505.15 109.99 c -504.63 110.62 504.38 111.52 504.38 112.68 c -504.38 113.79 504.65 114.66 505.20 115.30 c -505.75 115.94 506.51 116.26 507.46 116.26 c -508.28 116.26 509.16 116.00 510.10 115.50 c -510.10 116.55 l -509.24 117.00 508.31 117.22 507.29 117.22 c -h -515.20 116.19 m -514.51 116.83 513.84 117.15 513.20 117.15 c -512.67 117.15 512.24 116.98 511.89 116.65 c -511.54 116.32 511.37 115.90 511.37 115.40 c -511.37 114.71 511.66 114.17 512.24 113.80 c -512.83 113.42 513.66 113.24 514.75 113.24 c -515.03 113.24 l -515.03 112.47 l -515.03 111.73 514.65 111.36 513.89 111.36 c -513.28 111.36 512.62 111.55 511.92 111.93 c -511.92 110.97 l -512.70 110.65 513.42 110.50 514.10 110.50 c -514.81 110.50 515.34 110.66 515.68 110.98 c -516.01 111.30 516.18 111.79 516.18 112.47 c -516.18 115.35 l -516.18 116.01 516.39 116.34 516.79 116.34 c -516.84 116.34 516.92 116.34 517.02 116.32 c -517.10 116.96 l -516.84 117.08 516.55 117.15 516.23 117.15 c -515.69 117.15 515.35 116.83 515.20 116.19 c -h -515.03 115.56 m -515.03 113.92 l -514.64 113.91 l -514.01 113.91 513.50 114.03 513.11 114.27 c -512.72 114.51 512.52 114.82 512.52 115.21 c -512.52 115.49 512.62 115.72 512.81 115.92 c -513.01 116.11 513.25 116.20 513.53 116.20 c -514.01 116.20 514.51 115.99 515.03 115.56 c -h -520.46 117.15 m -519.87 117.15 519.41 116.98 519.08 116.64 c -518.76 116.31 518.59 115.84 518.59 115.24 c -518.59 111.50 l -517.79 111.50 l -517.79 110.64 l -518.59 110.64 l -518.59 109.48 l -519.75 109.37 l -519.75 110.64 l -521.41 110.64 l -521.41 111.50 l -519.75 111.50 l -519.75 115.03 l -519.75 115.86 520.11 116.28 520.82 116.28 c -520.98 116.28 521.16 116.25 521.38 116.20 c -521.38 117.00 l -521.03 117.10 520.72 117.15 520.46 117.15 c -h -526.31 116.19 m -525.62 116.83 524.96 117.15 524.32 117.15 c -523.79 117.15 523.35 116.98 523.00 116.65 c -522.66 116.32 522.48 115.90 522.48 115.40 c -522.48 114.71 522.77 114.17 523.36 113.80 c -523.94 113.42 524.78 113.24 525.87 113.24 c -526.14 113.24 l -526.14 112.47 l -526.14 111.73 525.77 111.36 525.01 111.36 c -524.40 111.36 523.74 111.55 523.03 111.93 c -523.03 110.97 l -523.81 110.65 524.54 110.50 525.22 110.50 c -525.93 110.50 526.45 110.66 526.79 110.98 c -527.13 111.30 527.30 111.79 527.30 112.47 c -527.30 115.35 l -527.30 116.01 527.50 116.34 527.91 116.34 c -527.96 116.34 528.03 116.34 528.13 116.32 c -528.21 116.96 l -527.95 117.08 527.66 117.15 527.35 117.15 c -526.81 117.15 526.46 116.83 526.31 116.19 c -h -526.14 115.56 m -526.14 113.92 l -525.76 113.91 l -525.12 113.91 524.61 114.03 524.22 114.27 c -523.83 114.51 523.64 114.82 523.64 115.21 c -523.64 115.49 523.73 115.72 523.93 115.92 c -524.12 116.11 524.36 116.20 524.64 116.20 c -525.12 116.20 525.62 115.99 526.14 115.56 c -h -529.66 117.00 m -529.66 107.75 l -530.81 107.75 l -530.81 117.00 l -h -535.62 117.15 m -534.71 117.15 533.98 116.84 533.44 116.24 c -532.90 115.64 532.62 114.83 532.62 113.82 c -532.62 112.79 532.90 111.99 533.44 111.39 c -533.99 110.79 534.73 110.50 535.66 110.50 c -536.59 110.50 537.33 110.79 537.88 111.39 c -538.42 111.99 538.70 112.79 538.70 113.81 c -538.70 114.85 538.42 115.66 537.88 116.26 c -537.33 116.85 536.58 117.15 535.62 117.15 c -h -535.64 116.28 m -536.86 116.28 537.47 115.46 537.47 113.81 c -537.47 112.18 536.87 111.36 535.66 111.36 c -534.46 111.36 533.86 112.18 533.86 113.82 c -533.86 115.46 534.45 116.28 535.64 116.28 c -h -540.52 119.12 m -540.66 118.11 l -541.33 118.43 541.98 118.59 542.63 118.59 c -543.93 118.59 544.58 117.90 544.58 116.52 c -544.58 115.52 l -544.16 116.41 543.46 116.85 542.49 116.85 c -541.72 116.85 541.12 116.58 540.67 116.02 c -540.22 115.47 540.00 114.72 540.00 113.78 c -540.00 112.81 540.25 112.02 540.76 111.41 c -541.28 110.80 541.94 110.50 542.75 110.50 c -543.46 110.50 544.07 110.79 544.58 111.36 c -544.58 110.64 l -545.74 110.64 l -545.74 115.27 l -545.74 116.26 545.69 117.00 545.59 117.48 c -545.49 117.96 545.29 118.35 545.01 118.65 c -544.50 119.19 543.72 119.46 542.66 119.46 c -541.91 119.46 541.20 119.34 540.52 119.12 c -h -544.58 114.80 m -544.58 112.15 l -544.08 111.63 543.52 111.36 542.93 111.36 c -542.39 111.36 541.98 111.58 541.68 112.00 c -541.38 112.43 541.23 113.01 541.23 113.75 c -541.23 115.15 541.72 115.85 542.70 115.85 c -543.37 115.85 543.99 115.50 544.58 114.80 c -h -f -[ 5.0000 5.0000] 0.0000 d -549.50 120.50 m -70.500 120.50 l -S -[] 0.0000 d -70.000 120.00 m -76.000 114.00 l -76.000 126.00 l -h -f* -423.06 285.15 m -422.48 285.15 422.02 284.98 421.69 284.64 c -421.37 284.31 421.20 283.84 421.20 283.24 c -421.20 279.50 l -420.40 279.50 l -420.40 278.64 l -421.20 278.64 l -421.20 277.48 l -422.36 277.37 l -422.36 278.64 l -424.02 278.64 l -424.02 279.50 l -422.36 279.50 l -422.36 283.03 l -422.36 283.86 422.71 284.28 423.43 284.28 c -423.59 284.28 423.77 284.25 423.99 284.20 c -423.99 285.00 l -423.63 285.10 423.33 285.15 423.06 285.15 c -h -428.13 285.15 m -427.22 285.15 426.50 284.84 425.95 284.24 c -425.41 283.64 425.14 282.83 425.14 281.82 c -425.14 280.79 425.41 279.99 425.96 279.39 c -426.50 278.79 427.24 278.50 428.17 278.50 c -429.11 278.50 429.85 278.79 430.39 279.39 c -430.94 279.99 431.21 280.79 431.21 281.81 c -431.21 282.85 430.94 283.66 430.39 284.26 c -429.84 284.85 429.09 285.15 428.13 285.15 c -h -428.15 284.28 m -429.37 284.28 429.98 283.46 429.98 281.81 c -429.98 280.18 429.38 279.36 428.17 279.36 c -426.97 279.36 426.37 280.18 426.37 281.82 c -426.37 283.46 426.96 284.28 428.15 284.28 c -h -433.01 285.00 m -433.01 275.75 l -434.17 275.75 l -434.17 281.72 l -436.86 278.64 l -438.11 278.64 l -435.53 281.61 l -438.64 285.00 l -437.16 285.00 l -434.17 281.74 l -434.17 285.00 l -h -444.69 284.79 m -443.92 285.03 443.26 285.15 442.71 285.15 c -441.77 285.15 441.00 284.83 440.41 284.21 c -439.82 283.59 439.52 282.78 439.52 281.79 c -439.52 280.82 439.78 280.03 440.31 279.42 c -440.83 278.80 441.49 278.49 442.31 278.49 c -443.08 278.49 443.67 278.76 444.09 279.31 c -444.51 279.86 444.72 280.63 444.72 281.64 c -444.71 282.00 l -440.70 282.00 l -440.87 283.51 441.61 284.27 442.92 284.27 c -443.40 284.27 443.99 284.14 444.69 283.88 c -h -440.75 281.13 m -443.56 281.13 l -443.56 279.95 443.12 279.36 442.24 279.36 c -441.35 279.36 440.86 279.95 440.75 281.13 c -h -446.71 285.00 m -446.71 278.64 l -447.87 278.64 l -447.87 279.83 l -448.48 278.94 449.22 278.50 450.11 278.50 c -450.66 278.50 451.10 278.67 451.42 279.02 c -451.75 279.37 451.92 279.84 451.92 280.43 c -451.92 285.00 l -450.76 285.00 l -450.76 280.80 l -450.76 280.33 450.69 280.00 450.55 279.79 c -450.42 279.59 450.19 279.49 449.87 279.49 c -449.16 279.49 448.49 279.96 447.87 280.88 c -447.87 285.00 l -h -454.18 286.88 m -454.18 286.45 l -454.55 286.34 454.74 285.90 454.74 285.12 c -454.74 285.00 l -454.18 285.00 l -454.18 283.55 l -455.62 283.55 l -455.62 284.81 l -455.62 286.09 455.14 286.78 454.18 286.88 c -h -463.44 285.15 m -462.91 285.15 462.27 285.02 461.52 284.78 c -461.52 283.72 l -462.27 284.09 462.93 284.28 463.49 284.28 c -463.82 284.28 464.10 284.19 464.31 284.01 c -464.53 283.83 464.64 283.61 464.64 283.34 c -464.64 282.94 464.34 282.62 463.72 282.36 c -463.05 282.07 l -462.05 281.66 461.55 281.06 461.55 280.28 c -461.55 279.73 461.75 279.29 462.14 278.97 c -462.54 278.66 463.07 278.50 463.76 278.50 c -464.11 278.50 464.55 278.54 465.08 278.64 c -465.32 278.69 l -465.32 279.65 l -464.67 279.46 464.16 279.36 463.78 279.36 c -463.04 279.36 462.67 279.63 462.67 280.17 c -462.67 280.52 462.95 280.81 463.51 281.05 c -464.07 281.29 l -464.70 281.55 465.14 281.83 465.40 282.13 c -465.67 282.42 465.80 282.79 465.80 283.23 c -465.80 283.79 465.58 284.25 465.13 284.61 c -464.69 284.97 464.13 285.15 463.44 285.15 c -h -472.54 284.79 m -471.76 285.03 471.10 285.15 470.55 285.15 c -469.61 285.15 468.85 284.83 468.25 284.21 c -467.66 283.59 467.37 282.78 467.37 281.79 c -467.37 280.82 467.63 280.03 468.15 279.42 c -468.67 278.80 469.34 278.49 470.15 278.49 c -470.92 278.49 471.51 278.76 471.93 279.31 c -472.35 279.86 472.56 280.63 472.56 281.64 c -472.56 282.00 l -468.54 282.00 l -468.71 283.51 469.45 284.27 470.77 284.27 c -471.25 284.27 471.84 284.14 472.54 283.88 c -h -468.60 281.13 m -471.40 281.13 l -471.40 279.95 470.96 279.36 470.08 279.36 c -469.19 279.36 468.70 279.95 468.60 281.13 c -h -474.56 285.00 m -474.56 278.64 l -475.71 278.64 l -475.71 279.83 l -476.17 278.94 476.83 278.50 477.70 278.50 c -477.82 278.50 477.94 278.51 478.07 278.53 c -478.07 279.60 l -477.87 279.54 477.70 279.50 477.54 279.50 c -476.81 279.50 476.20 279.94 475.71 280.80 c -475.71 285.00 l -h -480.79 285.00 m -478.42 278.64 l -479.57 278.64 l -481.42 283.59 l -483.38 278.64 l -484.45 278.64 l -481.94 285.00 l -h -485.68 285.00 m -485.68 278.64 l -486.83 278.64 l -486.83 285.00 l -h -485.68 277.48 m -485.68 276.33 l -486.83 276.33 l -486.83 277.48 l -h -491.63 285.15 m -490.77 285.15 490.06 284.83 489.49 284.19 c -488.93 283.55 488.64 282.75 488.64 281.78 c -488.64 280.75 488.92 279.94 489.48 279.36 c -490.04 278.79 490.83 278.50 491.83 278.50 c -492.33 278.50 492.88 278.56 493.49 278.70 c -493.49 279.67 l -492.84 279.48 492.31 279.38 491.90 279.38 c -491.31 279.38 490.84 279.60 490.48 280.05 c -490.12 280.49 489.94 281.08 489.94 281.82 c -489.94 282.53 490.13 283.11 490.49 283.55 c -490.86 283.99 491.34 284.21 491.94 284.21 c -492.46 284.21 493.01 284.08 493.56 283.81 c -493.56 284.81 l -492.82 285.03 492.17 285.15 491.63 285.15 c -h -499.96 284.79 m -499.18 285.03 498.52 285.15 497.97 285.15 c -497.03 285.15 496.27 284.83 495.68 284.21 c -495.08 283.59 494.79 282.78 494.79 281.79 c -494.79 280.82 495.05 280.03 495.57 279.42 c -496.09 278.80 496.76 278.49 497.57 278.49 c -498.34 278.49 498.94 278.76 499.36 279.31 c -499.78 279.86 499.99 280.63 499.99 281.64 c -499.98 282.00 l -495.97 282.00 l -496.13 283.51 496.88 284.27 498.19 284.27 c -498.67 284.27 499.26 284.14 499.96 283.88 c -h -496.02 281.13 m -498.83 281.13 l -498.83 279.95 498.38 279.36 497.50 279.36 c -496.62 279.36 496.12 279.95 496.02 281.13 c -h -505.66 285.22 m -504.31 285.22 503.27 284.82 502.54 284.03 c -501.80 283.24 501.43 282.12 501.43 280.67 c -501.43 279.22 501.81 278.10 502.56 277.31 c -503.30 276.51 504.36 276.11 505.72 276.11 c -506.49 276.11 507.40 276.24 508.45 276.49 c -508.45 277.65 l -507.26 277.24 506.34 277.03 505.70 277.03 c -504.76 277.03 504.03 277.35 503.51 277.99 c -503.00 278.62 502.74 279.52 502.74 280.68 c -502.74 281.79 503.02 282.66 503.57 283.30 c -504.12 283.94 504.87 284.26 505.82 284.26 c -506.64 284.26 507.52 284.00 508.46 283.50 c -508.46 284.55 l -507.60 285.00 506.67 285.22 505.66 285.22 c -h -513.56 284.19 m -512.87 284.83 512.21 285.15 511.56 285.15 c -511.04 285.15 510.60 284.98 510.25 284.65 c -509.90 284.32 509.73 283.90 509.73 283.40 c -509.73 282.71 510.02 282.17 510.61 281.80 c -511.19 281.42 512.03 281.24 513.12 281.24 c -513.39 281.24 l -513.39 280.47 l -513.39 279.73 513.01 279.36 512.26 279.36 c -511.65 279.36 510.99 279.55 510.28 279.93 c -510.28 278.97 l -511.06 278.65 511.79 278.50 512.47 278.50 c -513.18 278.50 513.70 278.66 514.04 278.98 c -514.38 279.30 514.55 279.79 514.55 280.47 c -514.55 283.35 l -514.55 284.01 514.75 284.34 515.16 284.34 c -515.21 284.34 515.28 284.34 515.38 284.32 c -515.46 284.96 l -515.20 285.08 514.91 285.15 514.59 285.15 c -514.05 285.15 513.71 284.83 513.56 284.19 c -h -513.39 283.56 m -513.39 281.92 l -513.01 281.91 l -512.37 281.91 511.86 282.03 511.47 282.27 c -511.08 282.51 510.88 282.82 510.88 283.21 c -510.88 283.49 510.98 283.72 511.18 283.92 c -511.37 284.11 511.61 284.20 511.89 284.20 c -512.37 284.20 512.87 283.99 513.39 283.56 c -h -518.82 285.15 m -518.23 285.15 517.78 284.98 517.45 284.64 c -517.12 284.31 516.96 283.84 516.96 283.24 c -516.96 279.50 l -516.16 279.50 l -516.16 278.64 l -516.96 278.64 l -516.96 277.48 l -518.11 277.37 l -518.11 278.64 l -519.77 278.64 l -519.77 279.50 l -518.11 279.50 l -518.11 283.03 l -518.11 283.86 518.47 284.28 519.19 284.28 c -519.34 284.28 519.53 284.25 519.74 284.20 c -519.74 285.00 l -519.39 285.10 519.08 285.15 518.82 285.15 c -h -524.68 284.19 m -523.99 284.83 523.32 285.15 522.68 285.15 c -522.15 285.15 521.71 284.98 521.37 284.65 c -521.02 284.32 520.85 283.90 520.85 283.40 c -520.85 282.71 521.14 282.17 521.72 281.80 c -522.31 281.42 523.14 281.24 524.23 281.24 c -524.51 281.24 l -524.51 280.47 l -524.51 279.73 524.13 279.36 523.37 279.36 c -522.76 279.36 522.10 279.55 521.40 279.93 c -521.40 278.97 l -522.17 278.65 522.90 278.50 523.58 278.50 c -524.29 278.50 524.82 278.66 525.16 278.98 c -525.49 279.30 525.66 279.79 525.66 280.47 c -525.66 283.35 l -525.66 284.01 525.87 284.34 526.27 284.34 c -526.32 284.34 526.40 284.34 526.49 284.32 c -526.58 284.96 l -526.31 285.08 526.03 285.15 525.71 285.15 c -525.17 285.15 524.83 284.83 524.68 284.19 c -h -524.51 283.56 m -524.51 281.92 l -524.12 281.91 l -523.49 281.91 522.98 282.03 522.59 282.27 c -522.20 282.51 522.00 282.82 522.00 283.21 c -522.00 283.49 522.10 283.72 522.29 283.92 c -522.49 284.11 522.73 284.20 523.01 284.20 c -523.49 284.20 523.99 283.99 524.51 283.56 c -h -528.02 285.00 m -528.02 275.75 l -529.18 275.75 l -529.18 285.00 l -h -533.98 285.15 m -533.07 285.15 532.35 284.84 531.80 284.24 c -531.26 283.64 530.99 282.83 530.99 281.82 c -530.99 280.79 531.26 279.99 531.81 279.39 c -532.35 278.79 533.09 278.50 534.02 278.50 c -534.96 278.50 535.70 278.79 536.24 279.39 c -536.79 279.99 537.06 280.79 537.06 281.81 c -537.06 282.85 536.79 283.66 536.24 284.26 c -535.69 284.85 534.94 285.15 533.98 285.15 c -h -534.00 284.28 m -535.22 284.28 535.83 283.46 535.83 281.81 c -535.83 280.18 535.23 279.36 534.02 279.36 c -532.82 279.36 532.22 280.18 532.22 281.82 c -532.22 283.46 532.81 284.28 534.00 284.28 c -h -538.89 287.12 m -539.02 286.11 l -539.69 286.43 540.35 286.59 541.00 286.59 c -542.30 286.59 542.95 285.90 542.95 284.52 c -542.95 283.52 l -542.52 284.41 541.82 284.85 540.85 284.85 c -540.09 284.85 539.48 284.58 539.03 284.02 c -538.58 283.47 538.36 282.72 538.36 281.78 c -538.36 280.81 538.62 280.02 539.13 279.41 c -539.64 278.80 540.30 278.50 541.11 278.50 c -541.82 278.50 542.44 278.79 542.95 279.36 c -542.95 278.64 l -544.11 278.64 l -544.11 283.27 l -544.11 284.26 544.06 285.00 543.95 285.48 c -543.85 285.96 543.65 286.35 543.37 286.65 c -542.87 287.19 542.08 287.46 541.02 287.46 c -540.28 287.46 539.57 287.34 538.89 287.12 c -h -542.95 282.80 m -542.95 280.15 l -542.44 279.63 541.89 279.36 541.29 279.36 c -540.76 279.36 540.34 279.58 540.04 280.00 c -539.74 280.43 539.59 281.01 539.59 281.75 c -539.59 283.15 540.08 283.85 541.06 283.85 c -541.73 283.85 542.36 283.50 542.95 282.80 c -h -f -[ 5.0000 5.0000] 0.0000 d -549.50 288.50 m -70.500 288.50 l -S -[] 0.0000 d -70.000 288.00 m -76.000 282.00 l -76.000 294.00 l -h -f* -568.13 457.00 m -568.13 455.80 l -567.52 456.70 566.78 457.15 565.90 457.15 c -565.35 457.15 564.90 456.97 564.58 456.62 c -564.25 456.27 564.08 455.80 564.08 455.21 c -564.08 450.64 l -565.24 450.64 l -565.24 454.83 l -565.24 455.31 565.31 455.65 565.45 455.85 c -565.58 456.05 565.82 456.15 566.14 456.15 c -566.84 456.15 567.51 455.69 568.13 454.76 c -568.13 450.64 l -569.29 450.64 l -569.29 457.00 l -h -573.29 457.15 m -572.76 457.15 572.12 457.02 571.37 456.78 c -571.37 455.72 l -572.12 456.09 572.78 456.28 573.34 456.28 c -573.67 456.28 573.94 456.19 574.16 456.01 c -574.38 455.83 574.49 455.61 574.49 455.34 c -574.49 454.94 574.18 454.62 573.57 454.36 c -572.90 454.07 l -571.90 453.66 571.40 453.06 571.40 452.28 c -571.40 451.73 571.60 451.29 571.99 450.97 c -572.38 450.66 572.92 450.50 573.61 450.50 c -573.96 450.50 574.40 450.54 574.92 450.64 c -575.16 450.69 l -575.16 451.65 l -574.52 451.46 574.01 451.36 573.63 451.36 c -572.89 451.36 572.52 451.63 572.52 452.17 c -572.52 452.52 572.80 452.81 573.36 453.05 c -573.92 453.29 l -574.54 453.55 574.99 453.83 575.25 454.13 c -575.51 454.42 575.64 454.79 575.64 455.23 c -575.64 455.79 575.42 456.25 574.98 456.61 c -574.54 456.97 573.98 457.15 573.29 457.15 c -h -582.38 456.79 m -581.61 457.03 580.95 457.15 580.40 457.15 c -579.46 457.15 578.69 456.83 578.10 456.21 c -577.51 455.59 577.21 454.78 577.21 453.79 c -577.21 452.82 577.48 452.03 578.00 451.42 c -578.52 450.80 579.19 450.49 580.00 450.49 c -580.77 450.49 581.36 450.76 581.78 451.31 c -582.20 451.86 582.41 452.63 582.41 453.64 c -582.41 454.00 l -578.39 454.00 l -578.56 455.51 579.30 456.27 580.61 456.27 c -581.09 456.27 581.68 456.14 582.38 455.88 c -h -578.45 453.13 m -581.25 453.13 l -581.25 451.95 580.81 451.36 579.93 451.36 c -579.04 451.36 578.55 451.95 578.45 453.13 c -h -584.40 457.00 m -584.40 450.64 l -585.56 450.64 l -585.56 451.83 l -586.02 450.94 586.68 450.50 587.55 450.50 c -587.67 450.50 587.79 450.51 587.92 450.53 c -587.92 451.60 l -587.72 451.54 587.54 451.50 587.39 451.50 c -586.66 451.50 586.05 451.94 585.56 452.80 c -585.56 457.00 l -h -589.33 458.88 m -589.33 458.45 l -589.71 458.34 589.89 457.90 589.89 457.12 c -589.89 457.00 l -589.33 457.00 l -589.33 455.55 l -590.78 455.55 l -590.78 456.81 l -590.78 458.09 590.30 458.78 589.33 458.88 c -h -596.91 457.00 m -596.91 450.64 l -598.06 450.64 l -598.06 451.83 l -598.52 450.94 599.18 450.50 600.05 450.50 c -600.17 450.50 600.29 450.51 600.42 450.53 c -600.42 451.60 l -600.22 451.54 600.05 451.50 599.90 451.50 c -599.17 451.50 598.55 451.94 598.06 452.80 c -598.06 457.00 l -h -604.31 457.15 m -603.40 457.15 602.67 456.84 602.13 456.24 c -601.59 455.64 601.31 454.83 601.31 453.82 c -601.31 452.79 601.59 451.99 602.13 451.39 c -602.68 450.79 603.42 450.50 604.35 450.50 c -605.28 450.50 606.02 450.79 606.57 451.39 c -607.11 451.99 607.38 452.79 607.38 453.81 c -607.38 454.85 607.11 455.66 606.56 456.26 c -606.02 456.85 605.27 457.15 604.31 457.15 c -h -604.33 456.28 m -605.55 456.28 606.16 455.46 606.16 453.81 c -606.16 452.18 605.56 451.36 604.35 451.36 c -603.15 451.36 602.54 452.18 602.54 453.82 c -602.54 455.46 603.14 456.28 604.33 456.28 c -h -609.19 457.00 m -609.19 447.75 l -610.34 447.75 l -610.34 457.00 l -h -617.32 456.79 m -616.55 457.03 615.89 457.15 615.34 457.15 c -614.40 457.15 613.63 456.83 613.04 456.21 c -612.45 455.59 612.15 454.78 612.15 453.79 c -612.15 452.82 612.42 452.03 612.94 451.42 c -613.46 450.80 614.12 450.49 614.94 450.49 c -615.71 450.49 616.30 450.76 616.72 451.31 c -617.14 451.86 617.35 452.63 617.35 453.64 c -617.35 454.00 l -613.33 454.00 l -613.50 455.51 614.24 456.27 615.55 456.27 c -616.03 456.27 616.62 456.14 617.32 455.88 c -h -613.38 453.13 m -616.19 453.13 l -616.19 451.95 615.75 451.36 614.87 451.36 c -613.98 451.36 613.49 451.95 613.38 453.13 c -h -621.03 457.15 m -620.50 457.15 619.86 457.02 619.11 456.78 c -619.11 455.72 l -619.86 456.09 620.52 456.28 621.08 456.28 c -621.41 456.28 621.69 456.19 621.90 456.01 c -622.12 455.83 622.23 455.61 622.23 455.34 c -622.23 454.94 621.93 454.62 621.31 454.36 c -620.64 454.07 l -619.64 453.66 619.14 453.06 619.14 452.28 c -619.14 451.73 619.34 451.29 619.73 450.97 c -620.13 450.66 620.66 450.50 621.35 450.50 c -621.70 450.50 622.14 450.54 622.67 450.64 c -622.91 450.69 l -622.91 451.65 l -622.26 451.46 621.75 451.36 621.37 451.36 c -620.63 451.36 620.26 451.63 620.26 452.17 c -620.26 452.52 620.54 452.81 621.10 453.05 c -621.66 453.29 l -622.29 453.55 622.73 453.83 622.99 454.13 c -623.26 454.42 623.39 454.79 623.39 455.23 c -623.39 455.79 623.17 456.25 622.72 456.61 c -622.28 456.97 621.72 457.15 621.03 457.15 c -h -f -[ 5.0000 5.0000] 0.0000 d -557.50 460.50 m -728.50 460.50 l -S -[] 0.0000 d -728.00 460.00 m -722.00 454.00 l -722.00 466.00 l -h -f* -680.84 533.15 m -680.31 533.15 679.67 533.02 678.92 532.78 c -678.92 531.72 l -679.67 532.09 680.33 532.28 680.89 532.28 c -681.22 532.28 681.50 532.19 681.71 532.01 c -681.93 531.83 682.04 531.61 682.04 531.34 c -682.04 530.94 681.74 530.62 681.12 530.36 c -680.45 530.07 l -679.45 529.66 678.96 529.06 678.96 528.28 c -678.96 527.73 679.15 527.29 679.54 526.97 c -679.94 526.66 680.47 526.50 681.16 526.50 c -681.51 526.50 681.95 526.54 682.48 526.64 c -682.72 526.69 l -682.72 527.65 l -682.07 527.46 681.56 527.36 681.18 527.36 c -680.44 527.36 680.07 527.63 680.07 528.17 c -680.07 528.52 680.35 528.81 680.91 529.05 c -681.47 529.29 l -682.10 529.55 682.54 529.83 682.80 530.13 c -683.07 530.42 683.20 530.79 683.20 531.23 c -683.20 531.79 682.98 532.25 682.54 532.61 c -682.09 532.97 681.53 533.15 680.84 533.15 c -h -689.25 533.00 m -689.25 531.80 l -688.64 532.70 687.89 533.15 687.02 533.15 c -686.46 533.15 686.02 532.97 685.69 532.62 c -685.37 532.27 685.20 531.80 685.20 531.21 c -685.20 526.64 l -686.36 526.64 l -686.36 530.83 l -686.36 531.31 686.42 531.65 686.56 531.85 c -686.70 532.05 686.93 532.15 687.26 532.15 c -687.96 532.15 688.62 531.69 689.25 530.76 c -689.25 526.64 l -690.40 526.64 l -690.40 533.00 l -h -695.20 533.15 m -694.34 533.15 693.63 532.83 693.06 532.19 c -692.50 531.55 692.21 530.75 692.21 529.78 c -692.21 528.75 692.50 527.94 693.06 527.36 c -693.62 526.79 694.40 526.50 695.40 526.50 c -695.90 526.50 696.45 526.56 697.07 526.70 c -697.07 527.67 l -696.41 527.48 695.88 527.38 695.47 527.38 c -694.88 527.38 694.41 527.60 694.05 528.05 c -693.69 528.49 693.52 529.08 693.52 529.82 c -693.52 530.53 693.70 531.11 694.07 531.55 c -694.43 531.99 694.91 532.21 695.51 532.21 c -696.04 532.21 696.58 532.08 697.14 531.81 c -697.14 532.81 l -696.39 533.03 695.75 533.15 695.20 533.15 c -h -701.35 533.15 m -700.49 533.15 699.78 532.83 699.21 532.19 c -698.64 531.55 698.36 530.75 698.36 529.78 c -698.36 528.75 698.64 527.94 699.20 527.36 c -699.76 526.79 700.54 526.50 701.55 526.50 c -702.04 526.50 702.60 526.56 703.21 526.70 c -703.21 527.67 l -702.56 527.48 702.03 527.38 701.62 527.38 c -701.03 527.38 700.56 527.60 700.20 528.05 c -699.84 528.49 699.66 529.08 699.66 529.82 c -699.66 530.53 699.85 531.11 700.21 531.55 c -700.58 531.99 701.06 532.21 701.65 532.21 c -702.18 532.21 702.72 532.08 703.28 531.81 c -703.28 532.81 l -702.54 533.03 701.89 533.15 701.35 533.15 c -h -709.68 532.79 m -708.90 533.03 708.24 533.15 707.69 533.15 c -706.75 533.15 705.99 532.83 705.40 532.21 c -704.80 531.59 704.51 530.78 704.51 529.79 c -704.51 528.82 704.77 528.03 705.29 527.42 c -705.81 526.80 706.48 526.49 707.29 526.49 c -708.06 526.49 708.66 526.76 709.08 527.31 c -709.50 527.86 709.71 528.63 709.71 529.64 c -709.70 530.00 l -705.69 530.00 l -705.85 531.51 706.59 532.27 707.91 532.27 c -708.39 532.27 708.98 532.14 709.68 531.88 c -h -705.74 529.13 m -708.54 529.13 l -708.54 527.95 708.10 527.36 707.22 527.36 c -706.33 527.36 705.84 527.95 705.74 529.13 c -h -713.38 533.15 m -712.86 533.15 712.22 533.02 711.46 532.78 c -711.46 531.72 l -712.22 532.09 712.87 532.28 713.43 532.28 c -713.76 532.28 714.04 532.19 714.26 532.01 c -714.48 531.83 714.59 531.61 714.59 531.34 c -714.59 530.94 714.28 530.62 713.67 530.36 c -712.99 530.07 l -712.00 529.66 711.50 529.06 711.50 528.28 c -711.50 527.73 711.69 527.29 712.09 526.97 c -712.48 526.66 713.02 526.50 713.70 526.50 c -714.06 526.50 714.50 526.54 715.02 526.64 c -715.26 526.69 l -715.26 527.65 l -714.62 527.46 714.10 527.36 713.72 527.36 c -712.98 527.36 712.61 527.63 712.61 528.17 c -712.61 528.52 712.89 528.81 713.46 529.05 c -714.01 529.29 l -714.64 529.55 715.09 529.83 715.35 530.13 c -715.61 530.42 715.74 530.79 715.74 531.23 c -715.74 531.79 715.52 532.25 715.08 532.61 c -714.64 532.97 714.07 533.15 713.38 533.15 c -h -719.50 533.15 m -718.97 533.15 718.33 533.02 717.58 532.78 c -717.58 531.72 l -718.33 532.09 718.99 532.28 719.55 532.28 c -719.88 532.28 720.16 532.19 720.38 532.01 c -720.59 531.83 720.70 531.61 720.70 531.34 c -720.70 530.94 720.40 530.62 719.78 530.36 c -719.11 530.07 l -718.11 529.66 717.62 529.06 717.62 528.28 c -717.62 527.73 717.81 527.29 718.20 526.97 c -718.60 526.66 719.13 526.50 719.82 526.50 c -720.17 526.50 720.61 526.54 721.14 526.64 c -721.38 526.69 l -721.38 527.65 l -720.73 527.46 720.22 527.36 719.84 527.36 c -719.10 527.36 718.73 527.63 718.73 528.17 c -718.73 528.52 719.01 528.81 719.57 529.05 c -720.13 529.29 l -720.76 529.55 721.20 529.83 721.46 530.13 c -721.73 530.42 721.86 530.79 721.86 531.23 c -721.86 531.79 721.64 532.25 721.20 532.61 c -720.75 532.97 720.19 533.15 719.50 533.15 c -h -f -[ 5.0000 5.0000] 0.0000 d -728.50 536.50 m -70.500 536.50 l -S -[] 0.0000 d -70.000 536.00 m -76.000 530.00 l -76.000 542.00 l -h -f* -575.47 439.00 m -573.11 432.64 l -574.26 432.64 l -576.11 437.59 l -578.06 432.64 l -579.14 432.64 l -576.63 439.00 l -h -583.65 438.19 m -582.96 438.83 582.29 439.15 581.65 439.15 c -581.12 439.15 580.68 438.98 580.34 438.65 c -579.99 438.32 579.81 437.90 579.81 437.40 c -579.81 436.71 580.11 436.17 580.69 435.80 c -581.27 435.42 582.11 435.24 583.20 435.24 c -583.48 435.24 l -583.48 434.47 l -583.48 433.73 583.10 433.36 582.34 433.36 c -581.73 433.36 581.07 433.55 580.37 433.93 c -580.37 432.97 l -581.14 432.65 581.87 432.50 582.55 432.50 c -583.26 432.50 583.79 432.66 584.12 432.98 c -584.46 433.30 584.63 433.79 584.63 434.47 c -584.63 437.35 l -584.63 438.01 584.83 438.34 585.24 438.34 c -585.29 438.34 585.37 438.34 585.46 438.32 c -585.54 438.96 l -585.28 439.08 584.99 439.15 584.68 439.15 c -584.14 439.15 583.79 438.83 583.65 438.19 c -h -583.48 437.56 m -583.48 435.92 l -583.09 435.91 l -582.46 435.91 581.95 436.03 581.55 436.27 c -581.16 436.51 580.97 436.82 580.97 437.21 c -580.97 437.49 581.07 437.72 581.26 437.92 c -581.46 438.11 581.70 438.20 581.98 438.20 c -582.46 438.20 582.96 437.99 583.48 437.56 c -h -586.99 439.00 m -586.99 429.75 l -588.15 429.75 l -588.15 439.00 l -h -590.46 439.00 m -590.46 432.64 l -591.62 432.64 l -591.62 439.00 l -h -590.46 431.48 m -590.46 430.33 l -591.62 430.33 l -591.62 431.48 l -h -598.01 439.00 m -598.01 437.80 l -597.54 438.70 596.84 439.15 595.89 439.15 c -595.13 439.15 594.52 438.87 594.08 438.31 c -593.65 437.75 593.43 436.99 593.43 436.02 c -593.43 434.96 593.67 434.11 594.17 433.46 c -594.67 432.82 595.33 432.50 596.14 432.50 c -596.89 432.50 597.52 432.79 598.01 433.36 c -598.01 429.75 l -599.17 429.75 l -599.17 439.00 l -h -598.01 434.15 m -597.42 433.63 596.85 433.36 596.31 433.36 c -595.21 433.36 594.66 434.21 594.66 435.90 c -594.66 437.39 595.15 438.13 596.13 438.13 c -596.77 438.13 597.40 437.78 598.01 437.08 c -h -604.76 438.19 m -604.07 438.83 603.41 439.15 602.77 439.15 c -602.24 439.15 601.80 438.98 601.45 438.65 c -601.11 438.32 600.93 437.90 600.93 437.40 c -600.93 436.71 601.22 436.17 601.81 435.80 c -602.39 435.42 603.23 435.24 604.32 435.24 c -604.59 435.24 l -604.59 434.47 l -604.59 433.73 604.21 433.36 603.46 433.36 c -602.85 433.36 602.19 433.55 601.48 433.93 c -601.48 432.97 l -602.26 432.65 602.99 432.50 603.67 432.50 c -604.38 432.50 604.90 432.66 605.24 432.98 c -605.58 433.30 605.75 433.79 605.75 434.47 c -605.75 437.35 l -605.75 438.01 605.95 438.34 606.36 438.34 c -606.41 438.34 606.48 438.34 606.58 438.32 c -606.66 438.96 l -606.40 439.08 606.11 439.15 605.79 439.15 c -605.26 439.15 604.91 438.83 604.76 438.19 c -h -604.59 437.56 m -604.59 435.92 l -604.21 435.91 l -603.57 435.91 603.06 436.03 602.67 436.27 c -602.28 436.51 602.09 436.82 602.09 437.21 c -602.09 437.49 602.18 437.72 602.38 437.92 c -602.57 438.11 602.81 438.20 603.09 438.20 c -603.57 438.20 604.07 437.99 604.59 437.56 c -h -610.02 439.15 m -609.43 439.15 608.98 438.98 608.65 438.64 c -608.32 438.31 608.16 437.84 608.16 437.24 c -608.16 433.50 l -607.36 433.50 l -607.36 432.64 l -608.16 432.64 l -608.16 431.48 l -609.31 431.37 l -609.31 432.64 l -610.97 432.64 l -610.97 433.50 l -609.31 433.50 l -609.31 437.03 l -609.31 437.86 609.67 438.28 610.39 438.28 c -610.54 438.28 610.73 438.25 610.95 438.20 c -610.95 439.00 l -610.59 439.10 610.28 439.15 610.02 439.15 c -h -617.26 438.79 m -616.49 439.03 615.83 439.15 615.28 439.15 c -614.34 439.15 613.57 438.83 612.98 438.21 c -612.39 437.59 612.09 436.78 612.09 435.79 c -612.09 434.82 612.35 434.03 612.88 433.42 c -613.40 432.80 614.06 432.49 614.88 432.49 c -615.65 432.49 616.24 432.76 616.66 433.31 c -617.08 433.86 617.29 434.63 617.29 435.64 c -617.29 436.00 l -613.27 436.00 l -613.44 437.51 614.18 438.27 615.49 438.27 c -615.97 438.27 616.56 438.14 617.26 437.88 c -h -613.32 435.13 m -616.13 435.13 l -616.13 433.95 615.69 433.36 614.81 433.36 c -613.92 433.36 613.43 433.95 613.32 435.13 c -h -626.37 439.00 m -619.43 435.53 l -626.37 432.06 l -626.37 433.03 l -621.37 435.53 l -626.37 438.03 l -h -630.73 439.15 m -630.15 439.15 629.69 438.98 629.36 438.64 c -629.03 438.31 628.87 437.84 628.87 437.24 c -628.87 433.50 l -628.07 433.50 l -628.07 432.64 l -628.87 432.64 l -628.87 431.48 l -630.02 431.37 l -630.02 432.64 l -631.69 432.64 l -631.69 433.50 l -630.02 433.50 l -630.02 437.03 l -630.02 437.86 630.38 438.28 631.10 438.28 c -631.25 438.28 631.44 438.25 631.66 438.20 c -631.66 439.00 l -631.30 439.10 630.99 439.15 630.73 439.15 c -h -635.80 439.15 m -634.89 439.15 634.16 438.84 633.62 438.24 c -633.08 437.64 632.81 436.83 632.81 435.82 c -632.81 434.79 633.08 433.99 633.62 433.39 c -634.17 432.79 634.91 432.50 635.84 432.50 c -636.78 432.50 637.51 432.79 638.06 433.39 c -638.60 433.99 638.88 434.79 638.88 435.81 c -638.88 436.85 638.60 437.66 638.06 438.26 c -637.51 438.85 636.76 439.15 635.80 439.15 c -h -635.82 438.28 m -637.04 438.28 637.65 437.46 637.65 435.81 c -637.65 434.18 637.05 433.36 635.84 433.36 c -634.64 433.36 634.04 434.18 634.04 435.82 c -634.04 437.46 634.63 438.28 635.82 438.28 c -h -640.68 439.00 m -640.68 429.75 l -641.84 429.75 l -641.84 435.72 l -644.53 432.64 l -645.77 432.64 l -643.20 435.61 l -646.31 439.00 l -644.83 439.00 l -641.84 435.74 l -641.84 439.00 l -h -652.36 438.79 m -651.59 439.03 650.92 439.15 650.37 439.15 c -649.44 439.15 648.67 438.83 648.08 438.21 c -647.49 437.59 647.19 436.78 647.19 435.79 c -647.19 434.82 647.45 434.03 647.97 433.42 c -648.50 432.80 649.16 432.49 649.97 432.49 c -650.74 432.49 651.34 432.76 651.76 433.31 c -652.18 433.86 652.39 434.63 652.39 435.64 c -652.38 436.00 l -648.37 436.00 l -648.54 437.51 649.28 438.27 650.59 438.27 c -651.07 438.27 651.66 438.14 652.36 437.88 c -h -648.42 435.13 m -651.23 435.13 l -651.23 433.95 650.79 433.36 649.90 433.36 c -649.02 433.36 648.52 433.95 648.42 435.13 c -h -654.38 439.00 m -654.38 432.64 l -655.54 432.64 l -655.54 433.83 l -656.14 432.94 656.89 432.50 657.77 432.50 c -658.32 432.50 658.76 432.67 659.09 433.02 c -659.42 433.37 659.58 433.84 659.58 434.43 c -659.58 439.00 l -658.43 439.00 l -658.43 434.80 l -658.43 434.33 658.36 434.00 658.22 433.79 c -658.08 433.59 657.85 433.49 657.53 433.49 c -656.83 433.49 656.16 433.96 655.54 434.88 c -655.54 439.00 l -h -661.85 440.88 m -661.85 440.45 l -662.22 440.34 662.41 439.90 662.41 439.12 c -662.41 439.00 l -661.85 439.00 l -661.85 437.55 l -663.29 437.55 l -663.29 438.81 l -663.29 440.09 662.81 440.78 661.85 440.88 c -h -669.42 440.73 m -669.42 429.75 l -671.74 429.75 l -671.74 430.62 l -670.44 430.62 l -670.44 439.87 l -671.74 439.87 l -671.74 440.73 l -h -675.23 439.15 m -674.65 439.15 674.19 438.98 673.86 438.64 c -673.54 438.31 673.37 437.84 673.37 437.24 c -673.37 433.50 l -672.57 433.50 l -672.57 432.64 l -673.37 432.64 l -673.37 431.48 l -674.53 431.37 l -674.53 432.64 l -676.19 432.64 l -676.19 433.50 l -674.53 433.50 l -674.53 437.03 l -674.53 437.86 674.88 438.28 675.60 438.28 c -675.76 438.28 675.94 438.25 676.16 438.20 c -676.16 439.00 l -675.80 439.10 675.50 439.15 675.23 439.15 c -h -682.48 438.79 m -681.70 439.03 681.04 439.15 680.49 439.15 c -679.55 439.15 678.79 438.83 678.20 438.21 c -677.60 437.59 677.31 436.78 677.31 435.79 c -677.31 434.82 677.57 434.03 678.09 433.42 c -678.61 432.80 679.28 432.49 680.09 432.49 c -680.86 432.49 681.46 432.76 681.88 433.31 c -682.30 433.86 682.51 434.63 682.51 435.64 c -682.50 436.00 l -678.49 436.00 l -678.65 437.51 679.39 438.27 680.71 438.27 c -681.19 438.27 681.78 438.14 682.48 437.88 c -h -678.54 435.13 m -681.35 435.13 l -681.35 433.95 680.90 433.36 680.02 433.36 c -679.13 433.36 678.64 433.95 678.54 435.13 c -h -684.50 439.00 m -684.50 432.64 l -685.65 432.64 l -685.65 433.83 l -686.26 432.94 687.01 432.50 687.89 432.50 c -688.44 432.50 688.88 432.67 689.21 433.02 c -689.54 433.37 689.70 433.84 689.70 434.43 c -689.70 439.00 l -688.55 439.00 l -688.55 434.80 l -688.55 434.33 688.48 434.00 688.34 433.79 c -688.20 433.59 687.97 433.49 687.65 433.49 c -686.94 433.49 686.28 433.96 685.65 434.88 c -685.65 439.00 l -h -695.23 438.19 m -694.54 438.83 693.87 439.15 693.23 439.15 c -692.70 439.15 692.26 438.98 691.92 438.65 c -691.57 438.32 691.39 437.90 691.39 437.40 c -691.39 436.71 691.69 436.17 692.27 435.80 c -692.85 435.42 693.69 435.24 694.78 435.24 c -695.06 435.24 l -695.06 434.47 l -695.06 433.73 694.68 433.36 693.92 433.36 c -693.31 433.36 692.65 433.55 691.95 433.93 c -691.95 432.97 l -692.72 432.65 693.45 432.50 694.13 432.50 c -694.84 432.50 695.37 432.66 695.70 432.98 c -696.04 433.30 696.21 433.79 696.21 434.47 c -696.21 437.35 l -696.21 438.01 696.41 438.34 696.82 438.34 c -696.87 438.34 696.95 438.34 697.04 438.32 c -697.12 438.96 l -696.86 439.08 696.57 439.15 696.26 439.15 c -695.72 439.15 695.38 438.83 695.23 438.19 c -h -695.06 437.56 m -695.06 435.92 l -694.67 435.91 l -694.04 435.91 693.53 436.03 693.13 436.27 c -692.74 436.51 692.55 436.82 692.55 437.21 c -692.55 437.49 692.65 437.72 692.84 437.92 c -693.04 438.11 693.28 438.20 693.56 438.20 c -694.04 438.20 694.54 437.99 695.06 437.56 c -h -698.57 439.00 m -698.57 432.64 l -699.73 432.64 l -699.73 433.83 l -700.34 432.94 701.08 432.50 701.96 432.50 c -702.52 432.50 702.96 432.67 703.28 433.02 c -703.61 433.37 703.78 433.84 703.78 434.43 c -703.78 439.00 l -702.62 439.00 l -702.62 434.80 l -702.62 434.33 702.55 434.00 702.41 433.79 c -702.27 433.59 702.04 433.49 701.72 433.49 c -701.02 433.49 700.35 433.96 699.73 434.88 c -699.73 439.00 l -h -707.93 439.15 m -707.34 439.15 706.89 438.98 706.56 438.64 c -706.23 438.31 706.07 437.84 706.07 437.24 c -706.07 433.50 l -705.27 433.50 l -705.27 432.64 l -706.07 432.64 l -706.07 431.48 l -707.22 431.37 l -707.22 432.64 l -708.88 432.64 l -708.88 433.50 l -707.22 433.50 l -707.22 437.03 l -707.22 437.86 707.58 438.28 708.30 438.28 c -708.45 438.28 708.64 438.25 708.86 438.20 c -708.86 439.00 l -708.50 439.10 708.19 439.15 707.93 439.15 c -h -712.10 440.73 m -712.10 429.75 l -709.79 429.75 l -709.79 430.62 l -711.09 430.62 l -711.09 439.87 l -709.79 439.87 l -709.79 440.73 l -h -714.56 439.00 m -721.49 435.53 l -714.56 432.06 l -714.56 433.03 l -719.55 435.53 l -714.56 438.03 l -h -f -728.50 442.50 m -557.50 442.50 l -S -557.00 442.00 m -563.00 436.00 l -563.00 448.00 l -h -f* -747.82 514.79 m -747.04 515.03 746.38 515.15 745.83 515.15 c -744.89 515.15 744.13 514.83 743.54 514.21 c -742.95 513.59 742.65 512.78 742.65 511.79 c -742.65 510.82 742.91 510.03 743.43 509.42 c -743.95 508.80 744.62 508.49 745.43 508.49 c -746.20 508.49 746.80 508.76 747.22 509.31 c -747.64 509.86 747.85 510.63 747.85 511.64 c -747.84 512.00 l -743.83 512.00 l -744.00 513.51 744.74 514.27 746.05 514.27 c -746.53 514.27 747.12 514.14 747.82 513.88 c -h -743.88 511.13 m -746.69 511.13 l -746.69 509.95 746.25 509.36 745.36 509.36 c -744.48 509.36 743.98 509.95 743.88 511.13 c -h -749.20 515.00 m -751.62 511.71 l -749.27 508.64 l -750.64 508.64 l -752.50 511.09 l -754.18 508.64 l -755.31 508.64 l -753.10 511.87 l -755.50 515.00 l -754.13 515.00 l -752.21 512.48 l -750.36 515.00 l -h -761.86 514.79 m -761.09 515.03 760.43 515.15 759.88 515.15 c -758.94 515.15 758.17 514.83 757.58 514.21 c -756.99 513.59 756.70 512.78 756.70 511.79 c -756.70 510.82 756.96 510.03 757.48 509.42 c -758.00 508.80 758.67 508.49 759.48 508.49 c -760.25 508.49 760.84 508.76 761.26 509.31 c -761.68 509.86 761.89 510.63 761.89 511.64 c -761.89 512.00 l -757.87 512.00 l -758.04 513.51 758.78 514.27 760.09 514.27 c -760.57 514.27 761.16 514.14 761.86 513.88 c -h -757.93 511.13 m -760.73 511.13 l -760.73 509.95 760.29 509.36 759.41 509.36 c -758.52 509.36 758.03 509.95 757.93 511.13 c -h -766.37 515.15 m -765.51 515.15 764.80 514.83 764.23 514.19 c -763.66 513.55 763.38 512.75 763.38 511.78 c -763.38 510.75 763.66 509.94 764.22 509.36 c -764.78 508.79 765.56 508.50 766.57 508.50 c -767.06 508.50 767.62 508.56 768.23 508.70 c -768.23 509.67 l -767.58 509.48 767.05 509.38 766.64 509.38 c -766.05 509.38 765.58 509.60 765.22 510.05 c -764.86 510.49 764.68 511.08 764.68 511.82 c -764.68 512.53 764.87 513.11 765.23 513.55 c -765.60 513.99 766.08 514.21 766.67 514.21 c -767.20 514.21 767.74 514.08 768.30 513.81 c -768.30 514.81 l -767.56 515.03 766.91 515.15 766.37 515.15 c -h -774.01 515.00 m -774.01 513.80 l -773.40 514.70 772.65 515.15 771.78 515.15 c -771.22 515.15 770.78 514.97 770.45 514.62 c -770.12 514.27 769.96 513.80 769.96 513.21 c -769.96 508.64 l -771.12 508.64 l -771.12 512.83 l -771.12 513.31 771.18 513.65 771.32 513.85 c -771.46 514.05 771.69 514.15 772.02 514.15 c -772.72 514.15 773.38 513.69 774.01 512.76 c -774.01 508.64 l -775.16 508.64 l -775.16 515.00 l -h -779.39 515.15 m -778.80 515.15 778.35 514.98 778.02 514.64 c -777.69 514.31 777.53 513.84 777.53 513.24 c -777.53 509.50 l -776.73 509.50 l -776.73 508.64 l -777.53 508.64 l -777.53 507.48 l -778.68 507.37 l -778.68 508.64 l -780.34 508.64 l -780.34 509.50 l -778.68 509.50 l -778.68 513.03 l -778.68 513.86 779.04 514.28 779.76 514.28 c -779.91 514.28 780.10 514.25 780.31 514.20 c -780.31 515.00 l -779.96 515.10 779.65 515.15 779.39 515.15 c -h -786.63 514.79 m -785.86 515.03 785.20 515.15 784.64 515.15 c -783.71 515.15 782.94 514.83 782.35 514.21 c -781.76 513.59 781.46 512.78 781.46 511.79 c -781.46 510.82 781.72 510.03 782.25 509.42 c -782.77 508.80 783.43 508.49 784.25 508.49 c -785.02 508.49 785.61 508.76 786.03 509.31 c -786.45 509.86 786.66 510.63 786.66 511.64 c -786.65 512.00 l -782.64 512.00 l -782.81 513.51 783.55 514.27 784.86 514.27 c -785.34 514.27 785.93 514.14 786.63 513.88 c -h -782.69 511.13 m -785.50 511.13 l -785.50 509.95 785.06 509.36 784.18 509.36 c -783.29 509.36 782.79 509.95 782.69 511.13 c -h -794.93 515.15 m -794.07 515.15 793.36 514.83 792.79 514.19 c -792.23 513.55 791.95 512.75 791.95 511.78 c -791.95 510.75 792.23 509.94 792.79 509.36 c -793.35 508.79 794.13 508.50 795.13 508.50 c -795.63 508.50 796.18 508.56 796.80 508.70 c -796.80 509.67 l -796.14 509.48 795.61 509.38 795.20 509.38 c -794.61 509.38 794.14 509.60 793.78 510.05 c -793.42 510.49 793.25 511.08 793.25 511.82 c -793.25 512.53 793.43 513.11 793.80 513.55 c -794.16 513.99 794.64 514.21 795.24 514.21 c -795.77 514.21 796.31 514.08 796.87 513.81 c -796.87 514.81 l -796.12 515.03 795.48 515.15 794.93 515.15 c -h -798.60 515.00 m -798.60 508.64 l -799.75 508.64 l -799.75 509.83 l -800.21 508.94 800.87 508.50 801.74 508.50 c -801.86 508.50 801.98 508.51 802.11 508.53 c -802.11 509.60 l -801.91 509.54 801.74 509.50 801.58 509.50 c -800.85 509.50 800.24 509.94 799.75 510.80 c -799.75 515.00 l -h -808.17 514.79 m -807.40 515.03 806.73 515.15 806.18 515.15 c -805.25 515.15 804.48 514.83 803.89 514.21 c -803.30 513.59 803.00 512.78 803.00 511.79 c -803.00 510.82 803.26 510.03 803.78 509.42 c -804.31 508.80 804.97 508.49 805.79 508.49 c -806.55 508.49 807.15 508.76 807.57 509.31 c -807.99 509.86 808.20 510.63 808.20 511.64 c -808.19 512.00 l -804.18 512.00 l -804.35 513.51 805.09 514.27 806.40 514.27 c -806.88 514.27 807.47 514.14 808.17 513.88 c -h -804.23 511.13 m -807.04 511.13 l -807.04 509.95 806.60 509.36 805.71 509.36 c -804.83 509.36 804.33 509.95 804.23 511.13 c -h -813.47 514.19 m -812.78 514.83 812.12 515.15 811.47 515.15 c -810.95 515.15 810.51 514.98 810.16 514.65 c -809.81 514.32 809.64 513.90 809.64 513.40 c -809.64 512.71 809.93 512.17 810.52 511.80 c -811.10 511.42 811.94 511.24 813.03 511.24 c -813.30 511.24 l -813.30 510.47 l -813.30 509.73 812.92 509.36 812.17 509.36 c -811.56 509.36 810.90 509.55 810.19 509.93 c -810.19 508.97 l -810.97 508.65 811.70 508.50 812.38 508.50 c -813.09 508.50 813.61 508.66 813.95 508.98 c -814.29 509.30 814.46 509.79 814.46 510.47 c -814.46 513.35 l -814.46 514.01 814.66 514.34 815.07 514.34 c -815.12 514.34 815.19 514.34 815.29 514.32 c -815.37 514.96 l -815.11 515.08 814.82 515.15 814.50 515.15 c -813.96 515.15 813.62 514.83 813.47 514.19 c -h -813.30 513.56 m -813.30 511.92 l -812.92 511.91 l -812.28 511.91 811.77 512.03 811.38 512.27 c -810.99 512.51 810.79 512.82 810.79 513.21 c -810.79 513.49 810.89 513.72 811.09 513.92 c -811.28 514.11 811.52 514.20 811.80 514.20 c -812.28 514.20 812.78 513.99 813.30 513.56 c -h -818.73 515.15 m -818.14 515.15 817.69 514.98 817.36 514.64 c -817.03 514.31 816.87 513.84 816.87 513.24 c -816.87 509.50 l -816.07 509.50 l -816.07 508.64 l -816.87 508.64 l -816.87 507.48 l -818.02 507.37 l -818.02 508.64 l -819.68 508.64 l -819.68 509.50 l -818.02 509.50 l -818.02 513.03 l -818.02 513.86 818.38 514.28 819.10 514.28 c -819.25 514.28 819.44 514.25 819.65 514.20 c -819.65 515.00 l -819.30 515.10 818.99 515.15 818.73 515.15 c -h -825.97 514.79 m -825.20 515.03 824.54 515.15 823.98 515.15 c -823.05 515.15 822.28 514.83 821.69 514.21 c -821.10 513.59 820.80 512.78 820.80 511.79 c -820.80 510.82 821.06 510.03 821.58 509.42 c -822.11 508.80 822.77 508.49 823.59 508.49 c -824.36 508.49 824.95 508.76 825.37 509.31 c -825.79 509.86 826.00 510.63 826.00 511.64 c -825.99 512.00 l -821.98 512.00 l -822.15 513.51 822.89 514.27 824.20 514.27 c -824.68 514.27 825.27 514.14 825.97 513.88 c -h -822.03 511.13 m -824.84 511.13 l -824.84 509.95 824.40 509.36 823.52 509.36 c -822.63 509.36 822.13 509.95 822.03 511.13 c -h -826.84 517.02 m -826.84 516.15 l -832.84 516.15 l -832.84 517.02 l -h -833.99 515.00 m -833.99 508.64 l -835.15 508.64 l -835.15 515.00 l -h -833.99 507.48 m -833.99 506.33 l -835.15 506.33 l -835.15 507.48 l -h -837.46 515.00 m -837.46 508.64 l -838.62 508.64 l -838.62 509.83 l -839.22 508.94 839.97 508.50 840.85 508.50 c -841.40 508.50 841.84 508.67 842.17 509.02 c -842.50 509.37 842.66 509.84 842.66 510.43 c -842.66 515.00 l -841.51 515.00 l -841.51 510.80 l -841.51 510.33 841.44 510.00 841.30 509.79 c -841.16 509.59 840.93 509.49 840.61 509.49 c -839.91 509.49 839.24 509.96 838.62 510.88 c -838.62 515.00 l -h -846.60 515.15 m -846.07 515.15 845.43 515.02 844.67 514.78 c -844.67 513.72 l -845.43 514.09 846.08 514.28 846.64 514.28 c -846.97 514.28 847.25 514.19 847.47 514.01 c -847.69 513.83 847.80 513.61 847.80 513.34 c -847.80 512.94 847.49 512.62 846.88 512.36 c -846.20 512.07 l -845.21 511.66 844.71 511.06 844.71 510.28 c -844.71 509.73 844.91 509.29 845.30 508.97 c -845.69 508.66 846.23 508.50 846.91 508.50 c -847.27 508.50 847.71 508.54 848.23 508.64 c -848.47 508.69 l -848.47 509.65 l -847.83 509.46 847.31 509.36 846.94 509.36 c -846.19 509.36 845.82 509.63 845.82 510.17 c -845.82 510.52 846.10 510.81 846.67 511.05 c -847.22 511.29 l -847.85 511.55 848.30 511.83 848.56 512.13 c -848.82 512.42 848.95 512.79 848.95 513.23 c -848.95 513.79 848.73 514.25 848.29 514.61 c -847.85 514.97 847.28 515.15 846.60 515.15 c -h -852.94 515.15 m -852.35 515.15 851.89 514.98 851.56 514.64 c -851.24 514.31 851.07 513.84 851.07 513.24 c -851.07 509.50 l -850.28 509.50 l -850.28 508.64 l -851.07 508.64 l -851.07 507.48 l -852.23 507.37 l -852.23 508.64 l -853.89 508.64 l -853.89 509.50 l -852.23 509.50 l -852.23 513.03 l -852.23 513.86 852.59 514.28 853.30 514.28 c -853.46 514.28 853.64 514.25 853.86 514.20 c -853.86 515.00 l -853.51 515.10 853.20 515.15 852.94 515.15 c -h -858.79 514.19 m -858.10 514.83 857.44 515.15 856.80 515.15 c -856.27 515.15 855.83 514.98 855.48 514.65 c -855.14 514.32 854.96 513.90 854.96 513.40 c -854.96 512.71 855.25 512.17 855.84 511.80 c -856.42 511.42 857.26 511.24 858.35 511.24 c -858.62 511.24 l -858.62 510.47 l -858.62 509.73 858.25 509.36 857.49 509.36 c -856.88 509.36 856.22 509.55 855.51 509.93 c -855.51 508.97 l -856.29 508.65 857.02 508.50 857.70 508.50 c -858.41 508.50 858.93 508.66 859.27 508.98 c -859.61 509.30 859.78 509.79 859.78 510.47 c -859.78 513.35 l -859.78 514.01 859.98 514.34 860.39 514.34 c -860.44 514.34 860.51 514.34 860.61 514.32 c -860.69 514.96 l -860.43 515.08 860.14 515.15 859.83 515.15 c -859.29 515.15 858.94 514.83 858.79 514.19 c -h -858.62 513.56 m -858.62 511.92 l -858.24 511.91 l -857.61 511.91 857.09 512.03 856.70 512.27 c -856.31 512.51 856.12 512.82 856.12 513.21 c -856.12 513.49 856.21 513.72 856.41 513.92 c -856.61 514.11 856.84 514.20 857.12 514.20 c -857.61 514.20 858.11 513.99 858.62 513.56 c -h -862.14 515.00 m -862.14 508.64 l -863.29 508.64 l -863.29 509.83 l -863.90 508.94 864.65 508.50 865.53 508.50 c -866.08 508.50 866.52 508.67 866.85 509.02 c -867.18 509.37 867.34 509.84 867.34 510.43 c -867.34 515.00 l -866.19 515.00 l -866.19 510.80 l -866.19 510.33 866.12 510.00 865.98 509.79 c -865.84 509.59 865.61 509.49 865.29 509.49 c -864.59 509.49 863.92 509.96 863.29 510.88 c -863.29 515.00 l -h -872.07 515.15 m -871.21 515.15 870.50 514.83 869.93 514.19 c -869.37 513.55 869.08 512.75 869.08 511.78 c -869.08 510.75 869.36 509.94 869.92 509.36 c -870.49 508.79 871.27 508.50 872.27 508.50 c -872.77 508.50 873.32 508.56 873.94 508.70 c -873.94 509.67 l -873.28 509.48 872.75 509.38 872.34 509.38 c -871.75 509.38 871.28 509.60 870.92 510.05 c -870.56 510.49 870.38 511.08 870.38 511.82 c -870.38 512.53 870.57 513.11 870.94 513.55 c -871.30 513.99 871.78 514.21 872.38 514.21 c -872.90 514.21 873.45 514.08 874.01 513.81 c -874.01 514.81 l -873.26 515.03 872.62 515.15 872.07 515.15 c -h -880.40 514.79 m -879.62 515.03 878.96 515.15 878.41 515.15 c -877.47 515.15 876.71 514.83 876.12 514.21 c -875.53 513.59 875.23 512.78 875.23 511.79 c -875.23 510.82 875.49 510.03 876.01 509.42 c -876.53 508.80 877.20 508.49 878.01 508.49 c -878.78 508.49 879.38 508.76 879.80 509.31 c -880.22 509.86 880.43 510.63 880.43 511.64 c -880.42 512.00 l -876.41 512.00 l -876.58 513.51 877.32 514.27 878.63 514.27 c -879.11 514.27 879.70 514.14 880.40 513.88 c -h -876.46 511.13 m -879.27 511.13 l -879.27 509.95 878.83 509.36 877.94 509.36 c -877.06 509.36 876.56 509.95 876.46 511.13 c -h -f -745.06 391.15 m -744.48 391.15 744.02 390.98 743.69 390.64 c -743.37 390.31 743.20 389.84 743.20 389.24 c -743.20 385.50 l -742.40 385.50 l -742.40 384.64 l -743.20 384.64 l -743.20 383.48 l -744.36 383.37 l -744.36 384.64 l -746.02 384.64 l -746.02 385.50 l -744.36 385.50 l -744.36 389.03 l -744.36 389.86 744.71 390.28 745.43 390.28 c -745.59 390.28 745.77 390.25 745.99 390.20 c -745.99 391.00 l -745.63 391.10 745.33 391.15 745.06 391.15 c -h -752.31 390.79 m -751.53 391.03 750.87 391.15 750.32 391.15 c -749.38 391.15 748.62 390.83 748.03 390.21 c -747.43 389.59 747.14 388.78 747.14 387.79 c -747.14 386.82 747.40 386.03 747.92 385.42 c -748.44 384.80 749.11 384.49 749.92 384.49 c -750.69 384.49 751.29 384.76 751.71 385.31 c -752.13 385.86 752.34 386.63 752.34 387.64 c -752.33 388.00 l -748.32 388.00 l -748.48 389.51 749.22 390.27 750.54 390.27 c -751.02 390.27 751.61 390.14 752.31 389.88 c -h -748.37 387.13 m -751.18 387.13 l -751.18 385.95 750.73 385.36 749.85 385.36 c -748.96 385.36 748.47 385.95 748.37 387.13 c -h -754.33 391.00 m -754.33 384.64 l -755.48 384.64 l -755.48 385.83 l -756.09 384.94 756.84 384.50 757.72 384.50 c -758.27 384.50 758.71 384.67 759.04 385.02 c -759.37 385.37 759.53 385.84 759.53 386.43 c -759.53 391.00 l -758.38 391.00 l -758.38 386.80 l -758.38 386.33 758.31 386.00 758.17 385.79 c -758.03 385.59 757.80 385.49 757.48 385.49 c -756.77 385.49 756.11 385.96 755.48 386.88 c -755.48 391.00 l -h -765.06 390.19 m -764.37 390.83 763.70 391.15 763.06 391.15 c -762.53 391.15 762.09 390.98 761.75 390.65 c -761.40 390.32 761.22 389.90 761.22 389.40 c -761.22 388.71 761.52 388.17 762.10 387.80 c -762.68 387.42 763.52 387.24 764.61 387.24 c -764.89 387.24 l -764.89 386.47 l -764.89 385.73 764.51 385.36 763.75 385.36 c -763.14 385.36 762.48 385.55 761.78 385.93 c -761.78 384.97 l -762.55 384.65 763.28 384.50 763.96 384.50 c -764.67 384.50 765.20 384.66 765.53 384.98 c -765.87 385.30 766.04 385.79 766.04 386.47 c -766.04 389.35 l -766.04 390.01 766.24 390.34 766.65 390.34 c -766.70 390.34 766.78 390.34 766.87 390.32 c -766.96 390.96 l -766.69 391.08 766.40 391.15 766.09 391.15 c -765.55 391.15 765.21 390.83 765.06 390.19 c -h -764.89 389.56 m -764.89 387.92 l -764.50 387.91 l -763.87 387.91 763.36 388.03 762.96 388.27 c -762.57 388.51 762.38 388.82 762.38 389.21 c -762.38 389.49 762.48 389.72 762.67 389.92 c -762.87 390.11 763.11 390.20 763.39 390.20 c -763.87 390.20 764.37 389.99 764.89 389.56 c -h -768.40 391.00 m -768.40 384.64 l -769.56 384.64 l -769.56 385.83 l -770.17 384.94 770.91 384.50 771.79 384.50 c -772.35 384.50 772.79 384.67 773.11 385.02 c -773.44 385.37 773.61 385.84 773.61 386.43 c -773.61 391.00 l -772.45 391.00 l -772.45 386.80 l -772.45 386.33 772.38 386.00 772.24 385.79 c -772.10 385.59 771.88 385.49 771.55 385.49 c -770.85 385.49 770.18 385.96 769.56 386.88 c -769.56 391.00 l -h -777.76 391.15 m -777.17 391.15 776.72 390.98 776.39 390.64 c -776.06 390.31 775.90 389.84 775.90 389.24 c -775.90 385.50 l -775.10 385.50 l -775.10 384.64 l -775.90 384.64 l -775.90 383.48 l -777.05 383.37 l -777.05 384.64 l -778.71 384.64 l -778.71 385.50 l -777.05 385.50 l -777.05 389.03 l -777.05 389.86 777.41 390.28 778.13 390.28 c -778.28 390.28 778.47 390.25 778.69 390.20 c -778.69 391.00 l -778.33 391.10 778.02 391.15 777.76 391.15 c -h -784.28 389.05 m -784.28 388.18 l -791.22 388.18 l -791.22 389.05 l -h -784.28 386.88 m -784.28 386.01 l -791.22 386.01 l -791.22 386.88 l -h -797.47 393.31 m -797.47 384.64 l -798.62 384.64 l -798.62 385.83 l -799.10 384.94 799.81 384.50 800.75 384.50 c -801.52 384.50 802.12 384.78 802.56 385.33 c -803.00 385.89 803.22 386.66 803.22 387.62 c -803.22 388.68 802.97 389.53 802.47 390.18 c -801.97 390.82 801.32 391.15 800.51 391.15 c -799.75 391.15 799.12 390.86 798.62 390.28 c -798.62 393.31 l -h -798.62 389.48 m -799.22 390.01 799.79 390.28 800.32 390.28 c -801.43 390.28 801.99 389.43 801.99 387.74 c -801.99 386.25 801.50 385.50 800.51 385.50 c -799.87 385.50 799.24 385.85 798.62 386.55 c -h -808.30 390.19 m -807.61 390.83 806.95 391.15 806.31 391.15 c -805.78 391.15 805.34 390.98 804.99 390.65 c -804.65 390.32 804.47 389.90 804.47 389.40 c -804.47 388.71 804.76 388.17 805.35 387.80 c -805.93 387.42 806.77 387.24 807.86 387.24 c -808.13 387.24 l -808.13 386.47 l -808.13 385.73 807.76 385.36 807.00 385.36 c -806.39 385.36 805.73 385.55 805.02 385.93 c -805.02 384.97 l -805.80 384.65 806.53 384.50 807.21 384.50 c -807.92 384.50 808.44 384.66 808.78 384.98 c -809.12 385.30 809.29 385.79 809.29 386.47 c -809.29 389.35 l -809.29 390.01 809.49 390.34 809.90 390.34 c -809.95 390.34 810.02 390.34 810.12 390.32 c -810.20 390.96 l -809.94 391.08 809.65 391.15 809.34 391.15 c -808.80 391.15 808.45 390.83 808.30 390.19 c -h -808.13 389.56 m -808.13 387.92 l -807.75 387.91 l -807.12 387.91 806.60 388.03 806.21 388.27 c -805.82 388.51 805.63 388.82 805.63 389.21 c -805.63 389.49 805.72 389.72 805.92 389.92 c -806.12 390.11 806.35 390.20 806.63 390.20 c -807.12 390.20 807.62 389.99 808.13 389.56 c -h -811.65 391.00 m -811.65 384.64 l -812.80 384.64 l -812.80 385.83 l -813.26 384.94 813.93 384.50 814.80 384.50 c -814.91 384.50 815.04 384.51 815.17 384.53 c -815.17 385.60 l -814.97 385.54 814.79 385.50 814.64 385.50 c -813.91 385.50 813.30 385.94 812.80 386.80 c -812.80 391.00 l -h -818.25 391.15 m -817.72 391.15 817.08 391.02 816.33 390.78 c -816.33 389.72 l -817.08 390.09 817.74 390.28 818.29 390.28 c -818.63 390.28 818.90 390.19 819.12 390.01 c -819.34 389.83 819.45 389.61 819.45 389.34 c -819.45 388.94 819.14 388.62 818.53 388.36 c -817.86 388.07 l -816.86 387.66 816.36 387.06 816.36 386.28 c -816.36 385.73 816.56 385.29 816.95 384.97 c -817.34 384.66 817.88 384.50 818.56 384.50 c -818.92 384.50 819.36 384.54 819.88 384.64 c -820.12 384.69 l -820.12 385.65 l -819.48 385.46 818.97 385.36 818.59 385.36 c -817.85 385.36 817.47 385.63 817.47 386.17 c -817.47 386.52 817.76 386.81 818.32 387.05 c -818.88 387.29 l -819.50 387.55 819.95 387.83 820.21 388.13 c -820.47 388.42 820.60 388.79 820.60 389.23 c -820.60 389.79 820.38 390.25 819.94 390.61 c -819.50 390.97 818.94 391.15 818.25 391.15 c -h -827.34 390.79 m -826.57 391.03 825.91 391.15 825.36 391.15 c -824.42 391.15 823.65 390.83 823.06 390.21 c -822.47 389.59 822.17 388.78 822.17 387.79 c -822.17 386.82 822.43 386.03 822.96 385.42 c -823.48 384.80 824.14 384.49 824.96 384.49 c -825.73 384.49 826.32 384.76 826.74 385.31 c -827.16 385.86 827.37 386.63 827.37 387.64 c -827.37 388.00 l -823.35 388.00 l -823.52 389.51 824.26 390.27 825.57 390.27 c -826.05 390.27 826.64 390.14 827.34 389.88 c -h -823.40 387.13 m -826.21 387.13 l -826.21 385.95 825.77 385.36 824.89 385.36 c -824.00 385.36 823.51 385.95 823.40 387.13 c -h -831.68 391.94 m -831.68 392.73 l -830.83 392.16 830.17 391.38 829.67 390.39 c -829.18 389.41 828.93 388.36 828.93 387.24 c -828.93 386.12 829.18 385.08 829.67 384.09 c -830.17 383.10 830.83 382.32 831.68 381.75 c -831.68 382.54 l -831.10 383.17 830.69 383.84 830.45 384.56 c -830.21 385.28 830.08 386.17 830.08 387.24 c -830.08 388.31 830.21 389.20 830.45 389.92 c -830.69 390.64 831.10 391.31 831.68 391.94 c -h -837.24 391.00 m -837.24 389.80 l -836.63 390.70 835.89 391.15 835.01 391.15 c -834.46 391.15 834.02 390.97 833.69 390.62 c -833.36 390.27 833.20 389.80 833.20 389.21 c -833.20 384.64 l -834.35 384.64 l -834.35 388.83 l -834.35 389.31 834.42 389.65 834.56 389.85 c -834.70 390.05 834.93 390.15 835.25 390.15 c -835.96 390.15 836.62 389.69 837.24 388.76 c -837.24 384.64 l -838.40 384.64 l -838.40 391.00 l -h -840.71 391.00 m -840.71 384.64 l -841.87 384.64 l -841.87 385.83 l -842.32 384.94 842.99 384.50 843.86 384.50 c -843.98 384.50 844.10 384.51 844.23 384.53 c -844.23 385.60 l -844.03 385.54 843.85 385.50 843.70 385.50 c -842.97 385.50 842.36 385.94 841.87 386.80 c -841.87 391.00 l -h -845.62 391.00 m -845.62 381.75 l -846.78 381.75 l -846.78 391.00 l -h -848.37 391.94 m -848.37 392.73 l -849.21 392.16 849.88 391.38 850.38 390.39 c -850.87 389.41 851.12 388.36 851.12 387.24 c -851.12 386.12 850.87 385.08 850.38 384.09 c -849.88 383.10 849.21 382.32 848.37 381.75 c -848.37 382.54 l -848.95 383.17 849.35 383.84 849.60 384.56 c -849.84 385.28 849.96 386.17 849.96 387.24 c -849.96 388.31 849.84 389.20 849.60 389.92 c -849.35 390.64 848.95 391.31 848.37 391.94 c -h -f -746.44 494.19 m -745.74 494.83 745.08 495.15 744.44 495.15 c -743.91 495.15 743.47 494.98 743.12 494.65 c -742.78 494.32 742.60 493.90 742.60 493.40 c -742.60 492.71 742.90 492.17 743.48 491.80 c -744.06 491.42 744.90 491.24 745.99 491.24 c -746.27 491.24 l -746.27 490.47 l -746.27 489.73 745.89 489.36 745.13 489.36 c -744.52 489.36 743.86 489.55 743.15 489.93 c -743.15 488.97 l -743.93 488.65 744.66 488.50 745.34 488.50 c -746.05 488.50 746.58 488.66 746.91 488.98 c -747.25 489.30 747.42 489.79 747.42 490.47 c -747.42 493.35 l -747.42 494.01 747.62 494.34 748.03 494.34 c -748.08 494.34 748.15 494.34 748.25 494.32 c -748.33 494.96 l -748.07 495.08 747.78 495.15 747.47 495.15 c -746.93 495.15 746.58 494.83 746.44 494.19 c -h -746.27 493.56 m -746.27 491.92 l -745.88 491.91 l -745.25 491.91 744.73 492.03 744.34 492.27 c -743.95 492.51 743.76 492.82 743.76 493.21 c -743.76 493.49 743.86 493.72 744.05 493.92 c -744.25 494.11 744.48 494.20 744.77 494.20 c -745.25 494.20 745.75 493.99 746.27 493.56 c -h -753.76 495.00 m -753.76 493.80 l -753.15 494.70 752.40 495.15 751.53 495.15 c -750.97 495.15 750.53 494.97 750.20 494.62 c -749.88 494.27 749.71 493.80 749.71 493.21 c -749.71 488.64 l -750.87 488.64 l -750.87 492.83 l -750.87 493.31 750.93 493.65 751.07 493.85 c -751.21 494.05 751.44 494.15 751.77 494.15 c -752.47 494.15 753.13 493.69 753.76 492.76 c -753.76 488.64 l -754.91 488.64 l -754.91 495.00 l -h -759.14 495.15 m -758.55 495.15 758.10 494.98 757.77 494.64 c -757.44 494.31 757.28 493.84 757.28 493.24 c -757.28 489.50 l -756.48 489.50 l -756.48 488.64 l -757.28 488.64 l -757.28 487.48 l -758.43 487.37 l -758.43 488.64 l -760.09 488.64 l -760.09 489.50 l -758.43 489.50 l -758.43 493.03 l -758.43 493.86 758.79 494.28 759.51 494.28 c -759.66 494.28 759.85 494.25 760.06 494.20 c -760.06 495.00 l -759.71 495.10 759.40 495.15 759.14 495.15 c -h -761.72 495.00 m -761.72 485.75 l -762.87 485.75 l -762.87 489.83 l -763.48 488.94 764.23 488.50 765.11 488.50 c -765.66 488.50 766.10 488.67 766.43 489.02 c -766.76 489.37 766.92 489.84 766.92 490.43 c -766.92 495.00 l -765.77 495.00 l -765.77 490.80 l -765.77 490.33 765.70 490.00 765.56 489.79 c -765.42 489.59 765.19 489.49 764.87 489.49 c -764.16 489.49 763.50 489.96 762.87 490.88 c -762.87 495.00 l -h -771.65 495.15 m -770.74 495.15 770.02 494.84 769.47 494.24 c -768.93 493.64 768.66 492.83 768.66 491.82 c -768.66 490.79 768.93 489.99 769.48 489.39 c -770.02 488.79 770.76 488.50 771.70 488.50 c -772.63 488.50 773.37 488.79 773.91 489.39 c -774.46 489.99 774.73 490.79 774.73 491.81 c -774.73 492.85 774.46 493.66 773.91 494.26 c -773.36 494.85 772.61 495.15 771.65 495.15 c -h -771.67 494.28 m -772.89 494.28 773.51 493.46 773.51 491.81 c -773.51 490.18 772.90 489.36 771.70 489.36 c -770.49 489.36 769.89 490.18 769.89 491.82 c -769.89 493.46 770.48 494.28 771.67 494.28 c -h -776.54 495.00 m -776.54 488.64 l -777.69 488.64 l -777.69 489.83 l -778.15 488.94 778.81 488.50 779.68 488.50 c -779.80 488.50 779.92 488.51 780.05 488.53 c -780.05 489.60 l -779.85 489.54 779.68 489.50 779.52 489.50 c -778.79 489.50 778.18 489.94 777.69 490.80 c -777.69 495.00 l -h -781.45 495.00 m -781.45 488.64 l -782.60 488.64 l -782.60 495.00 l -h -781.45 487.48 m -781.45 486.33 l -782.60 486.33 l -782.60 487.48 l -h -784.48 495.00 m -784.48 494.13 l -788.42 489.50 l -784.66 489.50 l -784.66 488.64 l -789.84 488.64 l -789.84 489.50 l -785.90 494.13 l -789.92 494.13 l -789.92 495.00 l -h -796.46 494.79 m -795.68 495.03 795.02 495.15 794.47 495.15 c -793.53 495.15 792.77 494.83 792.18 494.21 c -791.58 493.59 791.29 492.78 791.29 491.79 c -791.29 490.82 791.55 490.03 792.07 489.42 c -792.59 488.80 793.26 488.49 794.07 488.49 c -794.84 488.49 795.44 488.76 795.86 489.31 c -796.28 489.86 796.49 490.63 796.49 491.64 c -796.48 492.00 l -792.47 492.00 l -792.63 493.51 793.38 494.27 794.69 494.27 c -795.17 494.27 795.76 494.14 796.46 493.88 c -h -792.52 491.13 m -795.33 491.13 l -795.33 489.95 794.88 489.36 794.00 489.36 c -793.12 489.36 792.62 489.95 792.52 491.13 c -h -798.62 493.05 m -798.62 492.18 l -805.56 492.18 l -805.56 493.05 l -h -798.62 490.88 m -798.62 490.01 l -805.56 490.01 l -805.56 490.88 l -h -810.50 495.15 m -809.64 495.15 808.93 494.83 808.36 494.19 c -807.80 493.55 807.51 492.75 807.51 491.78 c -807.51 490.75 807.79 489.94 808.35 489.36 c -808.92 488.79 809.70 488.50 810.70 488.50 c -811.20 488.50 811.75 488.56 812.37 488.70 c -812.37 489.67 l -811.71 489.48 811.18 489.38 810.77 489.38 c -810.18 489.38 809.71 489.60 809.35 490.05 c -808.99 490.49 808.81 491.08 808.81 491.82 c -808.81 492.53 809.00 493.11 809.37 493.55 c -809.73 493.99 810.21 494.21 810.81 494.21 c -811.33 494.21 811.88 494.08 812.44 493.81 c -812.44 494.81 l -811.69 495.03 811.04 495.15 810.50 495.15 c -h -817.45 494.19 m -816.75 494.83 816.09 495.15 815.45 495.15 c -814.92 495.15 814.48 494.98 814.13 494.65 c -813.79 494.32 813.61 493.90 813.61 493.40 c -813.61 492.71 813.91 492.17 814.49 491.80 c -815.07 491.42 815.91 491.24 817.00 491.24 c -817.28 491.24 l -817.28 490.47 l -817.28 489.73 816.90 489.36 816.14 489.36 c -815.53 489.36 814.87 489.55 814.16 489.93 c -814.16 488.97 l -814.94 488.65 815.67 488.50 816.35 488.50 c -817.06 488.50 817.58 488.66 817.92 488.98 c -818.26 489.30 818.43 489.79 818.43 490.47 c -818.43 493.35 l -818.43 494.01 818.63 494.34 819.04 494.34 c -819.09 494.34 819.16 494.34 819.26 494.32 c -819.34 494.96 l -819.08 495.08 818.79 495.15 818.48 495.15 c -817.94 495.15 817.59 494.83 817.45 494.19 c -h -817.28 493.56 m -817.28 491.92 l -816.89 491.91 l -816.26 491.91 815.74 492.03 815.35 492.27 c -814.96 492.51 814.77 492.82 814.77 493.21 c -814.77 493.49 814.87 493.72 815.06 493.92 c -815.26 494.11 815.49 494.20 815.78 494.20 c -816.26 494.20 816.76 493.99 817.28 493.56 c -h -820.79 495.00 m -820.79 488.64 l -821.95 488.64 l -821.95 489.83 l -822.55 488.94 823.30 488.50 824.18 488.50 c -824.73 488.50 825.17 488.67 825.50 489.02 c -825.83 489.37 825.99 489.84 825.99 490.43 c -825.99 495.00 l -824.84 495.00 l -824.84 490.80 l -824.84 490.33 824.77 490.00 824.63 489.79 c -824.49 489.59 824.26 489.49 823.94 489.49 c -823.24 489.49 822.57 489.96 821.95 490.88 c -821.95 495.00 l -h -827.08 497.02 m -827.08 496.15 l -833.08 496.15 l -833.08 497.02 l -h -834.24 495.00 m -834.24 485.75 l -835.39 485.75 l -835.39 489.83 l -836.00 488.94 836.75 488.50 837.63 488.50 c -838.18 488.50 838.62 488.67 838.95 489.02 c -839.28 489.37 839.44 489.84 839.44 490.43 c -839.44 495.00 l -838.29 495.00 l -838.29 490.80 l -838.29 490.33 838.22 490.00 838.08 489.79 c -837.94 489.59 837.71 489.49 837.39 489.49 c -836.68 489.49 836.02 489.96 835.39 490.88 c -835.39 495.00 l -h -844.97 494.19 m -844.28 494.83 843.61 495.15 842.97 495.15 c -842.44 495.15 842.00 494.98 841.66 494.65 c -841.31 494.32 841.13 493.90 841.13 493.40 c -841.13 492.71 841.43 492.17 842.01 491.80 c -842.59 491.42 843.43 491.24 844.52 491.24 c -844.80 491.24 l -844.80 490.47 l -844.80 489.73 844.42 489.36 843.66 489.36 c -843.05 489.36 842.39 489.55 841.69 489.93 c -841.69 488.97 l -842.46 488.65 843.19 488.50 843.87 488.50 c -844.58 488.50 845.11 488.66 845.44 488.98 c -845.78 489.30 845.95 489.79 845.95 490.47 c -845.95 493.35 l -845.95 494.01 846.15 494.34 846.56 494.34 c -846.61 494.34 846.69 494.34 846.78 494.32 c -846.87 494.96 l -846.60 495.08 846.31 495.15 846.00 495.15 c -845.46 495.15 845.12 494.83 844.97 494.19 c -h -844.80 493.56 m -844.80 491.92 l -844.41 491.91 l -843.78 491.91 843.27 492.03 842.88 492.27 c -842.48 492.51 842.29 492.82 842.29 493.21 c -842.29 493.49 842.39 493.72 842.58 493.92 c -842.78 494.11 843.02 494.20 843.30 494.20 c -843.78 494.20 844.28 493.99 844.80 493.56 c -h -847.88 495.00 m -847.88 494.13 l -851.82 489.50 l -848.06 489.50 l -848.06 488.64 l -853.24 488.64 l -853.24 489.50 l -849.30 494.13 l -853.32 494.13 l -853.32 495.00 l -h -857.51 495.94 m -857.51 496.73 l -856.66 496.16 855.99 495.38 855.50 494.39 c -855.00 493.41 854.76 492.36 854.76 491.24 c -854.76 490.12 855.00 489.08 855.50 488.09 c -855.99 487.10 856.66 486.32 857.51 485.75 c -857.51 486.54 l -856.93 487.17 856.52 487.84 856.28 488.56 c -856.03 489.28 855.91 490.17 855.91 491.24 c -855.91 492.31 856.03 493.20 856.28 493.92 c -856.52 494.64 856.93 495.31 857.51 495.94 c -h -861.58 495.15 m -860.72 495.15 860.01 494.83 859.44 494.19 c -858.87 493.55 858.59 492.75 858.59 491.78 c -858.59 490.75 858.87 489.94 859.43 489.36 c -859.99 488.79 860.77 488.50 861.78 488.50 c -862.27 488.50 862.83 488.56 863.44 488.70 c -863.44 489.67 l -862.79 489.48 862.26 489.38 861.85 489.38 c -861.26 489.38 860.78 489.60 860.43 490.05 c -860.07 490.49 859.89 491.08 859.89 491.82 c -859.89 492.53 860.07 493.11 860.44 493.55 c -860.81 493.99 861.29 494.21 861.88 494.21 c -862.41 494.21 862.95 494.08 863.51 493.81 c -863.51 494.81 l -862.77 495.03 862.12 495.15 861.58 495.15 c -h -867.73 495.15 m -866.82 495.15 866.09 494.84 865.55 494.24 c -865.01 493.64 864.74 492.83 864.74 491.82 c -864.74 490.79 865.01 489.99 865.55 489.39 c -866.10 488.79 866.84 488.50 867.77 488.50 c -868.71 488.50 869.44 488.79 869.99 489.39 c -870.53 489.99 870.81 490.79 870.81 491.81 c -870.81 492.85 870.53 493.66 869.99 494.26 c -869.44 494.85 868.69 495.15 867.73 495.15 c -h -867.75 494.28 m -868.97 494.28 869.58 493.46 869.58 491.81 c -869.58 490.18 868.98 489.36 867.77 489.36 c -866.57 489.36 865.97 490.18 865.97 491.82 c -865.97 493.46 866.56 494.28 867.75 494.28 c -h -872.61 495.00 m -872.61 488.64 l -873.77 488.64 l -873.77 489.83 l -874.38 488.94 875.12 488.50 876.00 488.50 c -876.55 488.50 876.99 488.67 877.32 489.02 c -877.65 489.37 877.81 489.84 877.81 490.43 c -877.81 495.00 l -876.66 495.00 l -876.66 490.80 l -876.66 490.33 876.59 490.00 876.45 489.79 c -876.31 489.59 876.08 489.49 875.76 489.49 c -875.06 489.49 874.39 489.96 873.77 490.88 c -873.77 495.00 l -h -881.97 495.15 m -881.38 495.15 880.93 494.98 880.60 494.64 c -880.27 494.31 880.11 493.84 880.11 493.24 c -880.11 489.50 l -879.31 489.50 l -879.31 488.64 l -880.11 488.64 l -880.11 487.48 l -881.26 487.37 l -881.26 488.64 l -882.92 488.64 l -882.92 489.50 l -881.26 489.50 l -881.26 493.03 l -881.26 493.86 881.62 494.28 882.34 494.28 c -882.49 494.28 882.68 494.25 882.89 494.20 c -882.89 495.00 l -882.54 495.10 882.23 495.15 881.97 495.15 c -h -889.21 494.79 m -888.44 495.03 887.78 495.15 887.22 495.15 c -886.29 495.15 885.52 494.83 884.93 494.21 c -884.34 493.59 884.04 492.78 884.04 491.79 c -884.04 490.82 884.30 490.03 884.83 489.42 c -885.35 488.80 886.01 488.49 886.83 488.49 c -887.60 488.49 888.19 488.76 888.61 489.31 c -889.03 489.86 889.24 490.63 889.24 491.64 c -889.23 492.00 l -885.22 492.00 l -885.39 493.51 886.13 494.27 887.44 494.27 c -887.92 494.27 888.51 494.14 889.21 493.88 c -h -885.27 491.13 m -888.08 491.13 l -888.08 489.95 887.64 489.36 886.76 489.36 c -885.87 489.36 885.38 489.95 885.27 491.13 c -h -890.59 495.00 m -893.01 491.71 l -890.66 488.64 l -892.04 488.64 l -893.89 491.09 l -895.57 488.64 l -896.70 488.64 l -894.50 491.87 l -896.89 495.00 l -895.52 495.00 l -893.61 492.48 l -891.75 495.00 l -h -900.50 495.15 m -899.92 495.15 899.46 494.98 899.13 494.64 c -898.80 494.31 898.64 493.84 898.64 493.24 c -898.64 489.50 l -897.84 489.50 l -897.84 488.64 l -898.64 488.64 l -898.64 487.48 l -899.79 487.37 l -899.79 488.64 l -901.46 488.64 l -901.46 489.50 l -899.79 489.50 l -899.79 493.03 l -899.79 493.86 900.15 494.28 900.87 494.28 c -901.02 494.28 901.21 494.25 901.43 494.20 c -901.43 495.00 l -901.07 495.10 900.76 495.15 900.50 495.15 c -h -903.10 496.88 m -903.10 496.45 l -903.47 496.34 903.66 495.90 903.66 495.12 c -903.66 495.00 l -903.10 495.00 l -903.10 493.55 l -904.54 493.55 l -904.54 494.81 l -904.54 496.09 904.06 496.78 903.10 496.88 c -h -914.65 495.00 m -914.65 493.80 l -914.04 494.70 913.29 495.15 912.42 495.15 c -911.87 495.15 911.42 494.97 911.10 494.62 c -910.77 494.27 910.60 493.80 910.60 493.21 c -910.60 488.64 l -911.76 488.64 l -911.76 492.83 l -911.76 493.31 911.83 493.65 911.97 493.85 c -912.10 494.05 912.34 494.15 912.66 494.15 c -913.36 494.15 914.03 493.69 914.65 492.76 c -914.65 488.64 l -915.81 488.64 l -915.81 495.00 l -h -919.81 495.15 m -919.28 495.15 918.64 495.02 917.89 494.78 c -917.89 493.72 l -918.64 494.09 919.30 494.28 919.86 494.28 c -920.19 494.28 920.46 494.19 920.68 494.01 c -920.90 493.83 921.01 493.61 921.01 493.34 c -921.01 492.94 920.70 492.62 920.09 492.36 c -919.42 492.07 l -918.42 491.66 917.92 491.06 917.92 490.28 c -917.92 489.73 918.12 489.29 918.51 488.97 c -918.90 488.66 919.44 488.50 920.12 488.50 c -920.48 488.50 920.92 488.54 921.44 488.64 c -921.68 488.69 l -921.68 489.65 l -921.04 489.46 920.53 489.36 920.15 489.36 c -919.41 489.36 919.04 489.63 919.04 490.17 c -919.04 490.52 919.32 490.81 919.88 491.05 c -920.44 491.29 l -921.06 491.55 921.51 491.83 921.77 492.13 c -922.03 492.42 922.16 492.79 922.16 493.23 c -922.16 493.79 921.94 494.25 921.50 494.61 c -921.06 494.97 920.50 495.15 919.81 495.15 c -h -928.90 494.79 m -928.13 495.03 927.47 495.15 926.92 495.15 c -925.98 495.15 925.21 494.83 924.62 494.21 c -924.03 493.59 923.73 492.78 923.73 491.79 c -923.73 490.82 924.00 490.03 924.52 489.42 c -925.04 488.80 925.71 488.49 926.52 488.49 c -927.29 488.49 927.88 488.76 928.30 489.31 c -928.72 489.86 928.93 490.63 928.93 491.64 c -928.93 492.00 l -924.91 492.00 l -925.08 493.51 925.82 494.27 927.13 494.27 c -927.61 494.27 928.20 494.14 928.90 493.88 c -h -924.96 491.13 m -927.77 491.13 l -927.77 489.95 927.33 489.36 926.45 489.36 c -925.56 489.36 925.07 489.95 924.96 491.13 c -h -930.92 495.00 m -930.92 488.64 l -932.08 488.64 l -932.08 489.83 l -932.54 488.94 933.20 488.50 934.07 488.50 c -934.19 488.50 934.31 488.51 934.44 488.53 c -934.44 489.60 l -934.24 489.54 934.06 489.50 933.91 489.50 c -933.18 489.50 932.57 489.94 932.08 490.80 c -932.08 495.00 l -h -935.85 496.88 m -935.85 496.45 l -936.23 496.34 936.41 495.90 936.41 495.12 c -936.41 495.00 l -935.85 495.00 l -935.85 493.55 l -937.30 493.55 l -937.30 494.81 l -937.30 496.09 936.82 496.78 935.85 496.88 c -h -943.21 488.93 m -942.92 485.75 l -944.37 485.75 l -944.08 488.93 l -h -948.66 495.15 m -947.80 495.15 947.09 494.83 946.52 494.19 c -945.96 493.55 945.67 492.75 945.67 491.78 c -945.67 490.75 945.95 489.94 946.51 489.36 c -947.07 488.79 947.86 488.50 948.86 488.50 c -949.36 488.50 949.91 488.56 950.52 488.70 c -950.52 489.67 l -949.87 489.48 949.34 489.38 948.93 489.38 c -948.34 489.38 947.87 489.60 947.51 490.05 c -947.15 490.49 946.97 491.08 946.97 491.82 c -946.97 492.53 947.16 493.11 947.52 493.55 c -947.89 493.99 948.37 494.21 948.96 494.21 c -949.49 494.21 950.04 494.08 950.59 493.81 c -950.59 494.81 l -949.85 495.03 949.20 495.15 948.66 495.15 c -h -952.32 495.00 m -952.32 488.64 l -953.48 488.64 l -953.48 489.83 l -953.93 488.94 954.60 488.50 955.47 488.50 c -955.59 488.50 955.71 488.51 955.84 488.53 c -955.84 489.60 l -955.64 489.54 955.46 489.50 955.31 489.50 c -954.58 489.50 953.97 489.94 953.48 490.80 c -953.48 495.00 l -h -961.90 494.79 m -961.12 495.03 960.46 495.15 959.91 495.15 c -958.97 495.15 958.21 494.83 957.62 494.21 c -957.02 493.59 956.73 492.78 956.73 491.79 c -956.73 490.82 956.99 490.03 957.51 489.42 c -958.03 488.80 958.70 488.49 959.51 488.49 c -960.28 488.49 960.88 488.76 961.30 489.31 c -961.72 489.86 961.93 490.63 961.93 491.64 c -961.92 492.00 l -957.91 492.00 l -958.07 493.51 958.81 494.27 960.13 494.27 c -960.61 494.27 961.20 494.14 961.90 493.88 c -h -957.96 491.13 m -960.77 491.13 l -960.77 489.95 960.32 489.36 959.44 489.36 c -958.55 489.36 958.06 489.95 957.96 491.13 c -h -967.20 494.19 m -966.51 494.83 965.84 495.15 965.20 495.15 c -964.67 495.15 964.24 494.98 963.89 494.65 c -963.54 494.32 963.37 493.90 963.37 493.40 c -963.37 492.71 963.66 492.17 964.24 491.80 c -964.83 491.42 965.66 491.24 966.75 491.24 c -967.03 491.24 l -967.03 490.47 l -967.03 489.73 966.65 489.36 965.89 489.36 c -965.28 489.36 964.62 489.55 963.92 489.93 c -963.92 488.97 l -964.70 488.65 965.42 488.50 966.10 488.50 c -966.81 488.50 967.34 488.66 967.68 488.98 c -968.01 489.30 968.18 489.79 968.18 490.47 c -968.18 493.35 l -968.18 494.01 968.39 494.34 968.79 494.34 c -968.84 494.34 968.92 494.34 969.02 494.32 c -969.10 494.96 l -968.84 495.08 968.55 495.15 968.23 495.15 c -967.69 495.15 967.35 494.83 967.20 494.19 c -h -967.03 493.56 m -967.03 491.92 l -966.64 491.91 l -966.01 491.91 965.50 492.03 965.11 492.27 c -964.72 492.51 964.52 492.82 964.52 493.21 c -964.52 493.49 964.62 493.72 964.81 493.92 c -965.01 494.11 965.25 494.20 965.53 494.20 c -966.01 494.20 966.51 493.99 967.03 493.56 c -h -972.46 495.15 m -971.87 495.15 971.41 494.98 971.08 494.64 c -970.76 494.31 970.59 493.84 970.59 493.24 c -970.59 489.50 l -969.79 489.50 l -969.79 488.64 l -970.59 488.64 l -970.59 487.48 l -971.75 487.37 l -971.75 488.64 l -973.41 488.64 l -973.41 489.50 l -971.75 489.50 l -971.75 493.03 l -971.75 493.86 972.11 494.28 972.82 494.28 c -972.98 494.28 973.16 494.25 973.38 494.20 c -973.38 495.00 l -973.03 495.10 972.72 495.15 972.46 495.15 c -h -979.70 494.79 m -978.92 495.03 978.26 495.15 977.71 495.15 c -976.77 495.15 976.01 494.83 975.42 494.21 c -974.83 493.59 974.53 492.78 974.53 491.79 c -974.53 490.82 974.79 490.03 975.31 489.42 c -975.83 488.80 976.50 488.49 977.31 488.49 c -978.08 488.49 978.68 488.76 979.10 489.31 c -979.52 489.86 979.73 490.63 979.73 491.64 c -979.72 492.00 l -975.71 492.00 l -975.88 493.51 976.62 494.27 977.93 494.27 c -978.41 494.27 979.00 494.14 979.70 493.88 c -h -975.76 491.13 m -978.57 491.13 l -978.57 489.95 978.12 489.36 977.24 489.36 c -976.36 489.36 975.86 489.95 975.76 491.13 c -h -980.56 497.02 m -980.56 496.15 l -986.56 496.15 l -986.56 497.02 l -h -987.72 495.00 m -987.72 488.64 l -988.87 488.64 l -988.87 495.00 l -h -987.72 487.48 m -987.72 486.33 l -988.87 486.33 l -988.87 487.48 l -h -991.19 495.00 m -991.19 488.64 l -992.34 488.64 l -992.34 489.83 l -992.95 488.94 993.70 488.50 994.58 488.50 c -995.13 488.50 995.57 488.67 995.90 489.02 c -996.23 489.37 996.39 489.84 996.39 490.43 c -996.39 495.00 l -995.24 495.00 l -995.24 490.80 l -995.24 490.33 995.17 490.00 995.03 489.79 c -994.89 489.59 994.66 489.49 994.34 489.49 c -993.63 489.49 992.97 489.96 992.34 490.88 c -992.34 495.00 l -h -1000.3 495.15 m -999.79 495.15 999.15 495.02 998.40 494.78 c -998.40 493.72 l -999.15 494.09 999.81 494.28 1000.4 494.28 c -1000.7 494.28 1001.0 494.19 1001.2 494.01 c -1001.4 493.83 1001.5 493.61 1001.5 493.34 c -1001.5 492.94 1001.2 492.62 1000.6 492.36 c -999.93 492.07 l -998.93 491.66 998.44 491.06 998.44 490.28 c -998.44 489.73 998.63 489.29 999.02 488.97 c -999.42 488.66 999.96 488.50 1000.6 488.50 c -1001.0 488.50 1001.4 488.54 1002.0 488.64 c -1002.2 488.69 l -1002.2 489.65 l -1001.6 489.46 1001.0 489.36 1000.7 489.36 c -999.92 489.36 999.55 489.63 999.55 490.17 c -999.55 490.52 999.83 490.81 1000.4 491.05 c -1000.9 491.29 l -1001.6 491.55 1002.0 491.83 1002.3 492.13 c -1002.5 492.42 1002.7 492.79 1002.7 493.23 c -1002.7 493.79 1002.5 494.25 1002.0 494.61 c -1001.6 494.97 1001.0 495.15 1000.3 495.15 c -h -1006.7 495.15 m -1006.1 495.15 1005.6 494.98 1005.3 494.64 c -1005.0 494.31 1004.8 493.84 1004.8 493.24 c -1004.8 489.50 l -1004.0 489.50 l -1004.0 488.64 l -1004.8 488.64 l -1004.8 487.48 l -1006.0 487.37 l -1006.0 488.64 l -1007.6 488.64 l -1007.6 489.50 l -1006.0 489.50 l -1006.0 493.03 l -1006.0 493.86 1006.3 494.28 1007.0 494.28 c -1007.2 494.28 1007.4 494.25 1007.6 494.20 c -1007.6 495.00 l -1007.2 495.10 1006.9 495.15 1006.7 495.15 c -h -1012.5 494.19 m -1011.8 494.83 1011.2 495.15 1010.5 495.15 c -1010.0 495.15 1009.6 494.98 1009.2 494.65 c -1008.9 494.32 1008.7 493.90 1008.7 493.40 c -1008.7 492.71 1009.0 492.17 1009.6 491.80 c -1010.1 491.42 1011.0 491.24 1012.1 491.24 c -1012.4 491.24 l -1012.4 490.47 l -1012.4 489.73 1012.0 489.36 1011.2 489.36 c -1010.6 489.36 1009.9 489.55 1009.2 489.93 c -1009.2 488.97 l -1010.0 488.65 1010.7 488.50 1011.4 488.50 c -1012.1 488.50 1012.7 488.66 1013.0 488.98 c -1013.3 489.30 1013.5 489.79 1013.5 490.47 c -1013.5 493.35 l -1013.5 494.01 1013.7 494.34 1014.1 494.34 c -1014.2 494.34 1014.2 494.34 1014.3 494.32 c -1014.4 494.96 l -1014.2 495.08 1013.9 495.15 1013.6 495.15 c -1013.0 495.15 1012.7 494.83 1012.5 494.19 c -h -1012.4 493.56 m -1012.4 491.92 l -1012.0 491.91 l -1011.3 491.91 1010.8 492.03 1010.4 492.27 c -1010.0 492.51 1009.8 492.82 1009.8 493.21 c -1009.8 493.49 1009.9 493.72 1010.1 493.92 c -1010.3 494.11 1010.6 494.20 1010.9 494.20 c -1011.3 494.20 1011.8 493.99 1012.4 493.56 c -h -1015.9 495.00 m -1015.9 488.64 l -1017.0 488.64 l -1017.0 489.83 l -1017.6 488.94 1018.4 488.50 1019.3 488.50 c -1019.8 488.50 1020.2 488.67 1020.6 489.02 c -1020.9 489.37 1021.1 489.84 1021.1 490.43 c -1021.1 495.00 l -1019.9 495.00 l -1019.9 490.80 l -1019.9 490.33 1019.8 490.00 1019.7 489.79 c -1019.6 489.59 1019.3 489.49 1019.0 489.49 c -1018.3 489.49 1017.6 489.96 1017.0 490.88 c -1017.0 495.00 l -h -1025.8 495.15 m -1024.9 495.15 1024.2 494.83 1023.7 494.19 c -1023.1 493.55 1022.8 492.75 1022.8 491.78 c -1022.8 490.75 1023.1 489.94 1023.7 489.36 c -1024.2 488.79 1025.0 488.50 1026.0 488.50 c -1026.5 488.50 1027.0 488.56 1027.7 488.70 c -1027.7 489.67 l -1027.0 489.48 1026.5 489.38 1026.1 489.38 c -1025.5 489.38 1025.0 489.60 1024.6 490.05 c -1024.3 490.49 1024.1 491.08 1024.1 491.82 c -1024.1 492.53 1024.3 493.11 1024.7 493.55 c -1025.0 493.99 1025.5 494.21 1026.1 494.21 c -1026.6 494.21 1027.2 494.08 1027.7 493.81 c -1027.7 494.81 l -1027.0 495.03 1026.3 495.15 1025.8 495.15 c -h -1034.1 494.79 m -1033.4 495.03 1032.7 495.15 1032.1 495.15 c -1031.2 495.15 1030.4 494.83 1029.8 494.21 c -1029.3 493.59 1029.0 492.78 1029.0 491.79 c -1029.0 490.82 1029.2 490.03 1029.7 489.42 c -1030.3 488.80 1030.9 488.49 1031.7 488.49 c -1032.5 488.49 1033.1 488.76 1033.5 489.31 c -1033.9 489.86 1034.2 490.63 1034.2 491.64 c -1034.1 492.00 l -1030.1 492.00 l -1030.3 493.51 1031.0 494.27 1032.4 494.27 c -1032.8 494.27 1033.4 494.14 1034.1 493.88 c -h -1030.2 491.13 m -1033.0 491.13 l -1033.0 489.95 1032.6 489.36 1031.7 489.36 c -1030.8 489.36 1030.3 489.95 1030.2 491.13 c -h -1035.9 488.93 m -1035.6 485.75 l -1037.1 485.75 l -1036.8 488.93 l -h -1038.9 496.88 m -1038.9 496.45 l -1039.3 496.34 1039.5 495.90 1039.5 495.12 c -1039.5 495.00 l -1038.9 495.00 l -1038.9 493.55 l -1040.4 493.55 l -1040.4 494.81 l -1040.4 496.09 1039.9 496.78 1038.9 496.88 c -h -1048.4 495.15 m -1047.8 495.15 1047.4 494.98 1047.0 494.64 c -1046.7 494.31 1046.5 493.84 1046.5 493.24 c -1046.5 489.50 l -1045.7 489.50 l -1045.7 488.64 l -1046.5 488.64 l -1046.5 487.48 l -1047.7 487.37 l -1047.7 488.64 l -1049.4 488.64 l -1049.4 489.50 l -1047.7 489.50 l -1047.7 493.03 l -1047.7 493.86 1048.0 494.28 1048.8 494.28 c -1048.9 494.28 1049.1 494.25 1049.3 494.20 c -1049.3 495.00 l -1049.0 495.10 1048.7 495.15 1048.4 495.15 c -h -1055.6 494.79 m -1054.9 495.03 1054.2 495.15 1053.7 495.15 c -1052.7 495.15 1052.0 494.83 1051.4 494.21 c -1050.8 493.59 1050.5 492.78 1050.5 491.79 c -1050.5 490.82 1050.7 490.03 1051.3 489.42 c -1051.8 488.80 1052.4 488.49 1053.3 488.49 c -1054.0 488.49 1054.6 488.76 1055.0 489.31 c -1055.5 489.86 1055.7 490.63 1055.7 491.64 c -1055.7 492.00 l -1051.7 492.00 l -1051.8 493.51 1052.6 494.27 1053.9 494.27 c -1054.4 494.27 1054.9 494.14 1055.6 493.88 c -h -1051.7 491.13 m -1054.5 491.13 l -1054.5 489.95 1054.1 489.36 1053.2 489.36 c -1052.3 489.36 1051.8 489.95 1051.7 491.13 c -h -1057.7 495.00 m -1057.7 488.64 l -1058.8 488.64 l -1058.8 489.83 l -1059.4 488.94 1060.2 488.50 1061.1 488.50 c -1061.6 488.50 1062.0 488.67 1062.4 489.02 c -1062.7 489.37 1062.9 489.84 1062.9 490.43 c -1062.9 495.00 l -1061.7 495.00 l -1061.7 490.80 l -1061.7 490.33 1061.6 490.00 1061.5 489.79 c -1061.4 489.59 1061.1 489.49 1060.8 489.49 c -1060.1 489.49 1059.4 489.96 1058.8 490.88 c -1058.8 495.00 l -h -1068.4 494.19 m -1067.7 494.83 1067.0 495.15 1066.4 495.15 c -1065.9 495.15 1065.4 494.98 1065.1 494.65 c -1064.7 494.32 1064.6 493.90 1064.6 493.40 c -1064.6 492.71 1064.9 492.17 1065.4 491.80 c -1066.0 491.42 1066.9 491.24 1067.9 491.24 c -1068.2 491.24 l -1068.2 490.47 l -1068.2 489.73 1067.8 489.36 1067.1 489.36 c -1066.5 489.36 1065.8 489.55 1065.1 489.93 c -1065.1 488.97 l -1065.9 488.65 1066.6 488.50 1067.3 488.50 c -1068.0 488.50 1068.5 488.66 1068.9 488.98 c -1069.2 489.30 1069.4 489.79 1069.4 490.47 c -1069.4 493.35 l -1069.4 494.01 1069.6 494.34 1070.0 494.34 c -1070.0 494.34 1070.1 494.34 1070.2 494.32 c -1070.3 494.96 l -1070.0 495.08 1069.7 495.15 1069.4 495.15 c -1068.9 495.15 1068.5 494.83 1068.4 494.19 c -h -1068.2 493.56 m -1068.2 491.92 l -1067.8 491.91 l -1067.2 491.91 1066.7 492.03 1066.3 492.27 c -1065.9 492.51 1065.7 492.82 1065.7 493.21 c -1065.7 493.49 1065.8 493.72 1066.0 493.92 c -1066.2 494.11 1066.4 494.20 1066.7 494.20 c -1067.2 494.20 1067.7 493.99 1068.2 493.56 c -h -1071.7 495.00 m -1071.7 488.64 l -1072.9 488.64 l -1072.9 489.83 l -1073.5 488.94 1074.2 488.50 1075.1 488.50 c -1075.7 488.50 1076.1 488.67 1076.4 489.02 c -1076.8 489.37 1076.9 489.84 1076.9 490.43 c -1076.9 495.00 l -1075.8 495.00 l -1075.8 490.80 l -1075.8 490.33 1075.7 490.00 1075.6 489.79 c -1075.4 489.59 1075.2 489.49 1074.9 489.49 c -1074.2 489.49 1073.5 489.96 1072.9 490.88 c -1072.9 495.00 l -h -1081.1 495.15 m -1080.5 495.15 1080.1 494.98 1079.7 494.64 c -1079.4 494.31 1079.2 493.84 1079.2 493.24 c -1079.2 489.50 l -1078.4 489.50 l -1078.4 488.64 l -1079.2 488.64 l -1079.2 487.48 l -1080.4 487.37 l -1080.4 488.64 l -1082.0 488.64 l -1082.0 489.50 l -1080.4 489.50 l -1080.4 493.03 l -1080.4 493.86 1080.7 494.28 1081.5 494.28 c -1081.6 494.28 1081.8 494.25 1082.0 494.20 c -1082.0 495.00 l -1081.7 495.10 1081.4 495.15 1081.1 495.15 c -h -1082.5 497.02 m -1082.5 496.15 l -1088.5 496.15 l -1088.5 497.02 l -h -1089.7 495.00 m -1089.7 488.64 l -1090.8 488.64 l -1090.8 495.00 l -h -1089.7 487.48 m -1089.7 486.33 l -1090.8 486.33 l -1090.8 487.48 l -h -1097.2 495.00 m -1097.2 493.80 l -1096.8 494.70 1096.0 495.15 1095.1 495.15 c -1094.3 495.15 1093.7 494.87 1093.3 494.31 c -1092.9 493.75 1092.6 492.99 1092.6 492.02 c -1092.6 490.96 1092.9 490.11 1093.4 489.46 c -1093.9 488.82 1094.5 488.50 1095.3 488.50 c -1096.1 488.50 1096.7 488.79 1097.2 489.36 c -1097.2 485.75 l -1098.4 485.75 l -1098.4 495.00 l -h -1097.2 490.15 m -1096.6 489.63 1096.1 489.36 1095.5 489.36 c -1094.4 489.36 1093.9 490.21 1093.9 491.90 c -1093.9 493.39 1094.4 494.13 1095.3 494.13 c -1096.0 494.13 1096.6 493.78 1097.2 493.08 c -h -1100.0 495.94 m -1100.0 496.73 l -1100.8 496.16 1101.5 495.38 1102.0 494.39 c -1102.5 493.41 1102.7 492.36 1102.7 491.24 c -1102.7 490.12 1102.5 489.08 1102.0 488.09 c -1101.5 487.10 1100.8 486.32 1100.0 485.75 c -1100.0 486.54 l -1100.5 487.17 1101.0 487.84 1101.2 488.56 c -1101.4 489.28 1101.6 490.17 1101.6 491.24 c -1101.6 492.31 1101.4 493.20 1101.2 493.92 c -1101.0 494.64 1100.5 495.31 1100.0 495.94 c -h -f -17.000 7.0000 m -121.00 7.0000 l -121.00 44.000 l -17.000 44.000 l -17.000 7.0000 l -h -f -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -15.000 5.0000 m -117.00 5.0000 l -117.00 40.000 l -15.000 40.000 l -15.000 5.0000 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -15.500 5.5000 m -117.50 5.5000 l -117.50 40.500 l -15.500 40.500 l -15.500 5.5000 l -h -S -36.500 24.500 m -96.500 24.500 l -S -39.639 22.146 m -38.779 22.146 38.066 21.828 37.500 21.191 c -36.934 20.555 36.650 19.752 36.650 18.783 c -36.650 17.748 36.931 16.941 37.491 16.363 c -38.052 15.785 38.834 15.496 39.838 15.496 c -40.334 15.496 40.889 15.564 41.502 15.701 c -41.502 16.668 l -40.850 16.477 40.318 16.381 39.908 16.381 c -39.318 16.381 38.845 16.603 38.487 17.046 c -38.130 17.489 37.951 18.080 37.951 18.818 c -37.951 19.533 38.135 20.111 38.502 20.553 c -38.869 20.994 39.350 21.215 39.943 21.215 c -40.471 21.215 41.014 21.080 41.572 20.811 c -41.572 21.807 l -40.826 22.033 40.182 22.146 39.639 22.146 c -h -43.301 22.000 m -43.301 12.748 l -44.455 12.748 l -44.455 22.000 l -h -46.770 22.000 m -46.770 15.637 l -47.924 15.637 l -47.924 22.000 l -h -46.770 14.482 m -46.770 13.328 l -47.924 13.328 l -47.924 14.482 l -h -54.902 21.795 m -54.129 22.029 53.467 22.146 52.916 22.146 c -51.979 22.146 51.214 21.835 50.622 21.212 c -50.030 20.589 49.734 19.781 49.734 18.789 c -49.734 17.824 49.995 17.033 50.517 16.416 c -51.038 15.799 51.705 15.490 52.518 15.490 c -53.287 15.490 53.882 15.764 54.302 16.311 c -54.722 16.857 54.932 17.635 54.932 18.643 c -54.926 19.000 l -50.912 19.000 l -51.080 20.512 51.820 21.268 53.133 21.268 c -53.613 21.268 54.203 21.139 54.902 20.881 c -h -50.965 18.133 m -53.771 18.133 l -53.771 16.949 53.330 16.357 52.447 16.357 c -51.561 16.357 51.066 16.949 50.965 18.133 c -h -56.924 22.000 m -56.924 15.637 l -58.078 15.637 l -58.078 16.832 l -58.688 15.941 59.434 15.496 60.316 15.496 c -60.867 15.496 61.307 15.671 61.635 16.021 c -61.963 16.370 62.127 16.840 62.127 17.430 c -62.127 22.000 l -60.973 22.000 l -60.973 17.805 l -60.973 17.332 60.903 16.995 60.765 16.794 c -60.626 16.593 60.396 16.492 60.076 16.492 c -59.369 16.492 58.703 16.955 58.078 17.881 c -58.078 22.000 l -h -66.281 22.146 m -65.695 22.146 65.238 21.979 64.910 21.643 c -64.582 21.307 64.418 20.840 64.418 20.242 c -64.418 16.504 l -63.621 16.504 l -63.621 15.637 l -64.418 15.637 l -64.418 14.482 l -65.572 14.371 l -65.572 15.637 l -67.236 15.637 l -67.236 16.504 l -65.572 16.504 l -65.572 20.031 l -65.572 20.863 65.932 21.279 66.650 21.279 c -66.803 21.279 66.988 21.254 67.207 21.203 c -67.207 22.000 l -66.852 22.098 66.543 22.146 66.281 22.146 c -h -69.023 22.000 m -69.023 20.846 l -70.178 20.846 l -70.178 22.000 l -h -69.023 16.797 m -69.023 15.637 l -70.178 15.637 l -70.178 16.797 l -h -72.551 13.328 m -73.781 13.328 l -73.781 18.801 l -73.781 19.672 73.942 20.306 74.265 20.702 c -74.587 21.099 75.100 21.297 75.803 21.297 c -76.490 21.297 76.978 21.110 77.265 20.737 c -77.552 20.364 77.695 19.732 77.695 18.842 c -77.695 13.328 l -78.773 13.328 l -78.773 18.824 l -78.773 20.008 78.530 20.869 78.044 21.408 c -77.558 21.947 76.783 22.217 75.721 22.217 c -74.639 22.217 73.840 21.938 73.324 21.379 c -72.809 20.820 72.551 19.957 72.551 18.789 c -h -82.658 22.146 m -82.131 22.146 81.490 22.023 80.736 21.777 c -80.736 20.717 l -81.490 21.092 82.146 21.279 82.705 21.279 c -83.037 21.279 83.312 21.189 83.531 21.010 c -83.750 20.830 83.859 20.605 83.859 20.336 c -83.859 19.941 83.553 19.615 82.939 19.357 c -82.266 19.070 l -81.270 18.656 80.771 18.061 80.771 17.283 c -80.771 16.729 80.968 16.292 81.360 15.974 c -81.753 15.655 82.291 15.496 82.975 15.496 c -83.330 15.496 83.770 15.545 84.293 15.643 c -84.533 15.689 l -84.533 16.650 l -83.889 16.459 83.377 16.363 82.998 16.363 c -82.256 16.363 81.885 16.633 81.885 17.172 c -81.885 17.520 82.166 17.812 82.729 18.051 c -83.285 18.285 l -83.914 18.551 84.359 18.831 84.621 19.126 c -84.883 19.421 85.014 19.789 85.014 20.230 c -85.014 20.789 84.793 21.248 84.352 21.607 c -83.910 21.967 83.346 22.146 82.658 22.146 c -h -91.752 21.795 m -90.979 22.029 90.316 22.146 89.766 22.146 c -88.828 22.146 88.063 21.835 87.472 21.212 c -86.880 20.589 86.584 19.781 86.584 18.789 c -86.584 17.824 86.845 17.033 87.366 16.416 c -87.888 15.799 88.555 15.490 89.367 15.490 c -90.137 15.490 90.731 15.764 91.151 16.311 c -91.571 16.857 91.781 17.635 91.781 18.643 c -91.775 19.000 l -87.762 19.000 l -87.930 20.512 88.670 21.268 89.982 21.268 c -90.463 21.268 91.053 21.139 91.752 20.881 c -h -87.814 18.133 m -90.621 18.133 l -90.621 16.949 90.180 16.357 89.297 16.357 c -88.410 16.357 87.916 16.949 87.814 18.133 c -h -93.773 22.000 m -93.773 15.637 l -94.928 15.637 l -94.928 16.832 l -95.385 15.941 96.049 15.496 96.920 15.496 c -97.037 15.496 97.160 15.506 97.289 15.525 c -97.289 16.604 l -97.090 16.537 96.914 16.504 96.762 16.504 c -96.031 16.504 95.420 16.938 94.928 17.805 c -94.928 22.000 l -h -f -502.00 7.0000 m -611.00 7.0000 l -611.00 44.000 l -502.00 44.000 l -502.00 7.0000 l -h -f -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -500.00 5.0000 m -607.00 5.0000 l -607.00 40.000 l -500.00 40.000 l -500.00 5.0000 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -500.50 5.5000 m -607.50 5.5000 l -607.50 40.500 l -500.50 40.500 l -500.50 5.5000 l -h -S -506.50 24.500 m -601.50 24.500 l -S -507.15 22.000 m -507.15 12.748 l -508.31 12.748 l -508.31 18.725 l -511.00 15.637 l -512.25 15.637 l -509.67 18.607 l -512.78 22.000 l -511.30 22.000 l -508.31 18.736 l -508.31 22.000 l -h -518.83 21.795 m -518.06 22.029 517.40 22.146 516.85 22.146 c -515.91 22.146 515.14 21.835 514.55 21.212 c -513.96 20.589 513.66 19.781 513.66 18.789 c -513.66 17.824 513.92 17.033 514.45 16.416 c -514.97 15.799 515.63 15.490 516.45 15.490 c -517.22 15.490 517.81 15.764 518.23 16.311 c -518.65 16.857 518.86 17.635 518.86 18.643 c -518.86 19.000 l -514.84 19.000 l -515.01 20.512 515.75 21.268 517.06 21.268 c -517.54 21.268 518.13 21.139 518.83 20.881 c -h -514.89 18.133 m -517.70 18.133 l -517.70 16.949 517.26 16.357 516.38 16.357 c -515.49 16.357 515.00 16.949 514.89 18.133 c -h -521.22 24.314 m -522.25 22.000 l -519.79 15.637 l -521.04 15.637 l -522.86 20.430 l -524.81 15.637 l -525.90 15.637 l -522.42 24.314 l -h -528.81 22.146 m -528.28 22.146 527.64 22.023 526.89 21.777 c -526.89 20.717 l -527.64 21.092 528.30 21.279 528.86 21.279 c -529.19 21.279 529.46 21.189 529.68 21.010 c -529.90 20.830 530.01 20.605 530.01 20.336 c -530.01 19.941 529.71 19.615 529.09 19.357 c -528.42 19.070 l -527.42 18.656 526.92 18.061 526.92 17.283 c -526.92 16.729 527.12 16.292 527.51 15.974 c -527.91 15.655 528.44 15.496 529.13 15.496 c -529.48 15.496 529.92 15.545 530.45 15.643 c -530.69 15.689 l -530.69 16.650 l -530.04 16.459 529.53 16.363 529.15 16.363 c -528.41 16.363 528.04 16.633 528.04 17.172 c -528.04 17.520 528.32 17.812 528.88 18.051 c -529.44 18.285 l -530.07 18.551 530.51 18.831 530.77 19.126 c -531.04 19.421 531.17 19.789 531.17 20.230 c -531.17 20.789 530.95 21.248 530.50 21.607 c -530.06 21.967 529.50 22.146 528.81 22.146 c -h -535.15 22.146 m -534.56 22.146 534.11 21.979 533.78 21.643 c -533.45 21.307 533.29 20.840 533.29 20.242 c -533.29 16.504 l -532.49 16.504 l -532.49 15.637 l -533.29 15.637 l -533.29 14.482 l -534.44 14.371 l -534.44 15.637 l -536.11 15.637 l -536.11 16.504 l -534.44 16.504 l -534.44 20.031 l -534.44 20.863 534.80 21.279 535.52 21.279 c -535.67 21.279 535.86 21.254 536.08 21.203 c -536.08 22.000 l -535.72 22.098 535.41 22.146 535.15 22.146 c -h -540.22 22.146 m -539.31 22.146 538.58 21.845 538.04 21.241 c -537.50 20.638 537.22 19.830 537.22 18.818 c -537.22 17.795 537.50 16.985 538.04 16.390 c -538.59 15.794 539.33 15.496 540.26 15.496 c -541.19 15.496 541.93 15.794 542.48 16.390 c -543.02 16.985 543.29 17.791 543.29 18.807 c -543.29 19.846 543.02 20.662 542.47 21.256 c -541.93 21.850 541.18 22.146 540.22 22.146 c -h -540.24 21.279 m -541.46 21.279 542.07 20.455 542.07 18.807 c -542.07 17.178 541.47 16.363 540.26 16.363 c -539.06 16.363 538.46 17.182 538.46 18.818 c -538.46 20.459 539.05 21.279 540.24 21.279 c -h -545.10 22.000 m -545.10 15.637 l -546.25 15.637 l -546.25 16.832 l -546.86 15.941 547.61 15.496 548.49 15.496 c -549.04 15.496 549.48 15.671 549.81 16.021 c -550.14 16.370 550.30 16.840 550.30 17.430 c -550.30 22.000 l -549.15 22.000 l -549.15 17.805 l -549.15 17.332 549.08 16.995 548.94 16.794 c -548.80 16.593 548.57 16.492 548.25 16.492 c -547.54 16.492 546.88 16.955 546.25 17.881 c -546.25 22.000 l -h -557.21 21.795 m -556.44 22.029 555.78 22.146 555.22 22.146 c -554.29 22.146 553.52 21.835 552.93 21.212 c -552.34 20.589 552.04 19.781 552.04 18.789 c -552.04 17.824 552.30 17.033 552.83 16.416 c -553.35 15.799 554.01 15.490 554.83 15.490 c -555.60 15.490 556.19 15.764 556.61 16.311 c -557.03 16.857 557.24 17.635 557.24 18.643 c -557.23 19.000 l -553.22 19.000 l -553.39 20.512 554.13 21.268 555.44 21.268 c -555.92 21.268 556.51 21.139 557.21 20.881 c -h -553.27 18.133 m -556.08 18.133 l -556.08 16.949 555.64 16.357 554.76 16.357 c -553.87 16.357 553.38 16.949 553.27 18.133 c -h -559.40 22.000 m -559.40 20.846 l -560.55 20.846 l -560.55 22.000 l -h -559.40 16.797 m -559.40 15.637 l -560.55 15.637 l -560.55 16.797 l -h -564.69 22.217 m -564.11 22.217 563.37 22.090 562.46 21.836 c -562.46 20.617 l -563.44 21.070 564.24 21.297 564.87 21.297 c -565.35 21.297 565.74 21.170 566.04 20.916 c -566.33 20.662 566.48 20.328 566.48 19.914 c -566.48 19.574 566.38 19.285 566.19 19.047 c -566.00 18.809 565.64 18.543 565.12 18.250 c -564.52 17.904 l -563.79 17.482 563.26 17.085 562.96 16.712 c -562.66 16.339 562.51 15.904 562.51 15.408 c -562.51 14.740 562.75 14.190 563.23 13.759 c -563.72 13.327 564.34 13.111 565.09 13.111 c -565.75 13.111 566.46 13.223 567.20 13.445 c -567.20 14.570 l -566.29 14.211 565.61 14.031 565.16 14.031 c -564.73 14.031 564.38 14.145 564.10 14.371 c -563.82 14.598 563.69 14.883 563.69 15.227 c -563.69 15.516 563.79 15.771 563.99 15.994 c -564.19 16.217 564.56 16.482 565.10 16.791 c -565.72 17.143 l -566.47 17.568 567.00 17.971 567.29 18.350 c -567.59 18.729 567.74 19.184 567.74 19.715 c -567.74 20.469 567.46 21.074 566.91 21.531 c -566.35 21.988 565.61 22.217 564.69 22.217 c -h -574.16 21.795 m -573.38 22.029 572.72 22.146 572.17 22.146 c -571.23 22.146 570.47 21.835 569.88 21.212 c -569.28 20.589 568.99 19.781 568.99 18.789 c -568.99 17.824 569.25 17.033 569.77 16.416 c -570.29 15.799 570.96 15.490 571.77 15.490 c -572.54 15.490 573.14 15.764 573.56 16.311 c -573.98 16.857 574.19 17.635 574.19 18.643 c -574.18 19.000 l -570.17 19.000 l -570.33 20.512 571.07 21.268 572.39 21.268 c -572.87 21.268 573.46 21.139 574.16 20.881 c -h -570.22 18.133 m -573.03 18.133 l -573.03 16.949 572.58 16.357 571.70 16.357 c -570.81 16.357 570.32 16.949 570.22 18.133 c -h -576.18 22.000 m -576.18 15.637 l -577.33 15.637 l -577.33 16.832 l -577.79 15.941 578.45 15.496 579.32 15.496 c -579.44 15.496 579.56 15.506 579.69 15.525 c -579.69 16.604 l -579.49 16.537 579.32 16.504 579.17 16.504 c -578.44 16.504 577.82 16.938 577.33 17.805 c -577.33 22.000 l -h -582.41 22.000 m -580.04 15.637 l -581.19 15.637 l -583.04 20.588 l -585.00 15.637 l -586.07 15.637 l -583.56 22.000 l -h -587.30 22.000 m -587.30 15.637 l -588.45 15.637 l -588.45 22.000 l -h -587.30 14.482 m -587.30 13.328 l -588.45 13.328 l -588.45 14.482 l -h -593.25 22.146 m -592.39 22.146 591.68 21.828 591.11 21.191 c -590.55 20.555 590.26 19.752 590.26 18.783 c -590.26 17.748 590.54 16.941 591.10 16.363 c -591.67 15.785 592.45 15.496 593.45 15.496 c -593.95 15.496 594.50 15.564 595.12 15.701 c -595.12 16.668 l -594.46 16.477 593.93 16.381 593.52 16.381 c -592.93 16.381 592.46 16.603 592.10 17.046 c -591.74 17.489 591.56 18.080 591.56 18.818 c -591.56 19.533 591.75 20.111 592.12 20.553 c -592.48 20.994 592.96 21.215 593.56 21.215 c -594.08 21.215 594.63 21.080 595.19 20.811 c -595.19 21.807 l -594.44 22.033 593.79 22.146 593.25 22.146 c -h -601.58 21.795 m -600.80 22.029 600.14 22.146 599.59 22.146 c -598.65 22.146 597.89 21.835 597.30 21.212 c -596.71 20.589 596.41 19.781 596.41 18.789 c -596.41 17.824 596.67 17.033 597.19 16.416 c -597.71 15.799 598.38 15.490 599.19 15.490 c -599.96 15.490 600.56 15.764 600.98 16.311 c -601.40 16.857 601.61 17.635 601.61 18.643 c -601.60 19.000 l -597.59 19.000 l -597.76 20.512 598.50 21.268 599.81 21.268 c -600.29 21.268 600.88 21.139 601.58 20.881 c -h -597.64 18.133 m -600.45 18.133 l -600.45 16.949 600.01 16.357 599.12 16.357 c -598.24 16.357 597.74 16.949 597.64 18.133 c -h -f -683.00 7.0000 m -787.00 7.0000 l -787.00 44.000 l -683.00 44.000 l -683.00 7.0000 l -h -f -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -681.00 5.0000 m -783.00 5.0000 l -783.00 40.000 l -681.00 40.000 l -681.00 5.0000 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -681.50 5.5000 m -783.50 5.5000 l -783.50 40.500 l -681.50 40.500 l -681.50 5.5000 l -h -S -697.50 24.500 m -768.50 24.500 l -S -698.15 22.000 m -698.15 15.637 l -699.31 15.637 l -699.31 16.832 l -699.92 15.941 700.66 15.496 701.55 15.496 c -702.10 15.496 702.54 15.671 702.87 16.021 c -703.19 16.370 703.36 16.840 703.36 17.430 c -703.36 22.000 l -702.20 22.000 l -702.20 17.805 l -702.20 17.332 702.13 16.995 702.00 16.794 c -701.86 16.593 701.63 16.492 701.31 16.492 c -700.60 16.492 699.93 16.955 699.31 17.881 c -699.31 22.000 l -h -708.09 22.146 m -707.18 22.146 706.46 21.845 705.91 21.241 c -705.37 20.638 705.10 19.830 705.10 18.818 c -705.10 17.795 705.37 16.985 705.92 16.390 c -706.46 15.794 707.20 15.496 708.13 15.496 c -709.07 15.496 709.81 15.794 710.35 16.390 c -710.90 16.985 711.17 17.791 711.17 18.807 c -711.17 19.846 710.89 20.662 710.35 21.256 c -709.80 21.850 709.05 22.146 708.09 22.146 c -h -708.11 21.279 m -709.33 21.279 709.94 20.455 709.94 18.807 c -709.94 17.178 709.34 16.363 708.13 16.363 c -706.93 16.363 706.33 17.182 706.33 18.818 c -706.33 20.459 706.92 21.279 708.11 21.279 c -h -714.29 22.000 m -711.92 15.637 l -713.08 15.637 l -714.93 20.588 l -716.88 15.637 l -717.96 15.637 l -715.45 22.000 l -h -722.46 21.191 m -721.77 21.828 721.11 22.146 720.47 22.146 c -719.94 22.146 719.50 21.981 719.15 21.651 c -718.81 21.321 718.63 20.904 718.63 20.400 c -718.63 19.705 718.92 19.171 719.51 18.798 c -720.09 18.425 720.93 18.238 722.02 18.238 c -722.29 18.238 l -722.29 17.471 l -722.29 16.732 721.92 16.363 721.16 16.363 c -720.55 16.363 719.89 16.551 719.18 16.926 c -719.18 15.971 l -719.96 15.654 720.69 15.496 721.37 15.496 c -722.08 15.496 722.60 15.656 722.94 15.977 c -723.28 16.297 723.45 16.795 723.45 17.471 c -723.45 20.354 l -723.45 21.014 723.65 21.344 724.06 21.344 c -724.11 21.344 724.18 21.336 724.28 21.320 c -724.36 21.959 l -724.10 22.084 723.81 22.146 723.50 22.146 c -722.96 22.146 722.61 21.828 722.46 21.191 c -h -722.29 20.564 m -722.29 18.918 l -721.91 18.906 l -721.28 18.906 720.76 19.026 720.37 19.267 c -719.98 19.507 719.79 19.822 719.79 20.213 c -719.79 20.490 719.88 20.725 720.08 20.916 c -720.28 21.107 720.51 21.203 720.79 21.203 c -721.28 21.203 721.78 20.990 722.29 20.564 c -h -725.97 22.000 m -725.97 20.846 l -727.13 20.846 l -727.13 22.000 l -h -725.97 16.797 m -725.97 15.637 l -727.13 15.637 l -727.13 16.797 l -h -731.27 22.217 m -730.69 22.217 729.95 22.090 729.04 21.836 c -729.04 20.617 l -730.02 21.070 730.82 21.297 731.45 21.297 c -731.93 21.297 732.32 21.170 732.62 20.916 c -732.91 20.662 733.06 20.328 733.06 19.914 c -733.06 19.574 732.96 19.285 732.77 19.047 c -732.58 18.809 732.22 18.543 731.70 18.250 c -731.10 17.904 l -730.36 17.482 729.84 17.085 729.54 16.712 c -729.24 16.339 729.09 15.904 729.09 15.408 c -729.09 14.740 729.33 14.190 729.81 13.759 c -730.30 13.327 730.91 13.111 731.66 13.111 c -732.33 13.111 733.04 13.223 733.78 13.445 c -733.78 14.570 l -732.87 14.211 732.18 14.031 731.73 14.031 c -731.31 14.031 730.96 14.145 730.68 14.371 c -730.40 14.598 730.26 14.883 730.26 15.227 c -730.26 15.516 730.37 15.771 730.57 15.994 c -730.77 16.217 731.14 16.482 731.68 16.791 c -732.30 17.143 l -733.05 17.568 733.58 17.971 733.87 18.350 c -734.17 18.729 734.32 19.184 734.32 19.715 c -734.32 20.469 734.04 21.074 733.48 21.531 c -732.93 21.988 732.19 22.217 731.27 22.217 c -h -740.73 21.795 m -739.96 22.029 739.30 22.146 738.75 22.146 c -737.81 22.146 737.05 21.835 736.45 21.212 c -735.86 20.589 735.57 19.781 735.57 18.789 c -735.57 17.824 735.83 17.033 736.35 16.416 c -736.87 15.799 737.54 15.490 738.35 15.490 c -739.12 15.490 739.71 15.764 740.13 16.311 c -740.55 16.857 740.76 17.635 740.76 18.643 c -740.76 19.000 l -736.74 19.000 l -736.91 20.512 737.65 21.268 738.96 21.268 c -739.45 21.268 740.04 21.139 740.73 20.881 c -h -736.80 18.133 m -739.60 18.133 l -739.60 16.949 739.16 16.357 738.28 16.357 c -737.39 16.357 736.90 16.949 736.80 18.133 c -h -742.76 22.000 m -742.76 15.637 l -743.91 15.637 l -743.91 16.832 l -744.37 15.941 745.03 15.496 745.90 15.496 c -746.02 15.496 746.14 15.506 746.27 15.525 c -746.27 16.604 l -746.07 16.537 745.90 16.504 745.74 16.504 c -745.01 16.504 744.40 16.938 743.91 17.805 c -743.91 22.000 l -h -748.98 22.000 m -746.62 15.637 l -747.77 15.637 l -749.62 20.588 l -751.57 15.637 l -752.65 15.637 l -750.14 22.000 l -h -753.88 22.000 m -753.88 15.637 l -755.03 15.637 l -755.03 22.000 l -h -753.88 14.482 m -753.88 13.328 l -755.03 13.328 l -755.03 14.482 l -h -759.83 22.146 m -758.97 22.146 758.26 21.828 757.69 21.191 c -757.12 20.555 756.84 19.752 756.84 18.783 c -756.84 17.748 757.12 16.941 757.68 16.363 c -758.24 15.785 759.03 15.496 760.03 15.496 c -760.53 15.496 761.08 15.564 761.69 15.701 c -761.69 16.668 l -761.04 16.477 760.51 16.381 760.10 16.381 c -759.51 16.381 759.04 16.603 758.68 17.046 c -758.32 17.489 758.14 18.080 758.14 18.818 c -758.14 19.533 758.33 20.111 758.69 20.553 c -759.06 20.994 759.54 21.215 760.13 21.215 c -760.66 21.215 761.21 21.080 761.76 20.811 c -761.76 21.807 l -761.02 22.033 760.37 22.146 759.83 22.146 c -h -768.16 21.795 m -767.38 22.029 766.72 22.146 766.17 22.146 c -765.23 22.146 764.47 21.835 763.88 21.212 c -763.28 20.589 762.99 19.781 762.99 18.789 c -762.99 17.824 763.25 17.033 763.77 16.416 c -764.29 15.799 764.96 15.490 765.77 15.490 c -766.54 15.490 767.14 15.764 767.56 16.311 c -767.98 16.857 768.19 17.635 768.19 18.643 c -768.18 19.000 l -764.17 19.000 l -764.33 20.512 765.07 21.268 766.39 21.268 c -766.87 21.268 767.46 21.139 768.16 20.881 c -h -764.22 18.133 m -767.03 18.133 l -767.03 16.949 766.58 16.357 765.70 16.357 c -764.81 16.357 764.32 16.949 764.22 18.133 c -h -f -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -60.000 62.000 m -181.00 62.000 l -181.00 79.000 l -60.000 79.000 l -60.000 62.000 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -2.0000 w -60.500 62.500 m -575.50 62.500 l -575.50 130.50 l -60.500 130.50 l -60.500 62.500 l -h -S -60.500 79.500 m -180.50 79.500 l -S -180.50 79.500 m -182.50 75.500 l -S -182.50 75.500 m -182.50 62.500 l -S -68.561 76.000 m -68.561 74.686 l -68.078 75.668 67.319 76.159 66.282 76.159 c -65.444 76.159 64.786 75.852 64.308 75.238 c -63.829 74.625 63.590 73.780 63.590 72.706 c -63.590 71.538 63.860 70.607 64.400 69.913 c -64.939 69.219 65.664 68.872 66.574 68.872 c -67.302 68.872 67.964 69.159 68.561 69.735 c -68.561 65.977 l -70.446 65.977 l -70.446 76.000 l -h -68.561 70.852 m -68.108 70.344 67.623 70.090 67.107 70.090 c -66.646 70.090 66.278 70.312 66.002 70.757 c -65.727 71.201 65.590 71.798 65.590 72.547 c -65.590 73.973 66.047 74.686 66.961 74.686 c -67.520 74.686 68.053 74.328 68.561 73.613 c -h -78.463 75.765 m -77.570 76.028 76.724 76.159 75.924 76.159 c -74.760 76.159 73.842 75.829 73.169 75.168 c -72.496 74.508 72.160 73.607 72.160 72.464 c -72.160 71.385 72.468 70.517 73.083 69.859 c -73.699 69.201 74.513 68.872 75.524 68.872 c -76.544 68.872 77.289 69.193 77.758 69.836 c -78.228 70.480 78.463 71.497 78.463 72.890 c -74.140 72.890 l -74.267 74.218 74.997 74.883 76.330 74.883 c -76.961 74.883 77.672 74.737 78.463 74.445 c -h -74.115 71.830 m -76.616 71.830 l -76.616 70.640 76.233 70.046 75.467 70.046 c -74.688 70.046 74.237 70.640 74.115 71.830 c -h -80.602 76.000 m -80.602 70.205 l -79.644 70.205 l -79.644 69.030 l -80.602 69.030 l -80.602 68.529 l -80.602 66.722 81.459 65.818 83.173 65.818 c -83.727 65.818 84.299 65.899 84.887 66.060 c -84.887 67.329 l -84.354 67.105 83.875 66.993 83.452 66.993 c -82.805 66.993 82.481 67.481 82.481 68.459 c -82.481 69.030 l -84.246 69.030 l -84.246 70.205 l -82.481 70.205 l -82.481 76.000 l -h -89.209 75.251 m -88.583 75.856 87.912 76.159 87.197 76.159 c -86.588 76.159 86.093 75.972 85.712 75.600 c -85.331 75.228 85.141 74.745 85.141 74.153 c -85.141 73.383 85.448 72.789 86.064 72.372 c -86.680 71.955 87.561 71.747 88.708 71.747 c -89.209 71.747 l -89.209 71.112 l -89.209 70.389 88.797 70.027 87.972 70.027 c -87.240 70.027 86.499 70.234 85.750 70.649 c -85.750 69.354 l -86.601 69.032 87.443 68.872 88.276 68.872 c -90.100 68.872 91.012 69.597 91.012 71.049 c -91.012 74.134 l -91.012 74.680 91.188 74.953 91.539 74.953 c -91.603 74.953 91.685 74.944 91.787 74.927 c -91.831 75.981 l -91.433 76.099 91.082 76.159 90.777 76.159 c -90.007 76.159 89.512 75.856 89.292 75.251 c -h -89.209 74.242 m -89.209 72.826 l -88.765 72.826 l -87.551 72.826 86.943 73.207 86.943 73.969 c -86.943 74.227 87.031 74.444 87.207 74.619 c -87.382 74.795 87.599 74.883 87.857 74.883 c -88.298 74.883 88.748 74.669 89.209 74.242 c -h -97.747 76.000 m -97.747 74.686 l -97.138 75.668 96.346 76.159 95.373 76.159 c -94.751 76.159 94.260 75.962 93.900 75.568 c -93.541 75.175 93.361 74.637 93.361 73.956 c -93.361 69.030 l -95.240 69.030 l -95.240 73.493 l -95.240 74.284 95.504 74.680 96.033 74.680 c -96.626 74.680 97.197 74.259 97.747 73.417 c -97.747 69.030 l -99.626 69.030 l -99.626 76.000 l -h -101.97 76.000 m -101.97 65.977 l -103.85 65.977 l -103.85 76.000 l -h -109.79 75.962 m -109.35 76.093 108.99 76.159 108.73 76.159 c -107.11 76.159 106.29 75.397 106.29 73.874 c -106.29 70.205 l -105.51 70.205 l -105.51 69.030 l -106.29 69.030 l -106.29 67.856 l -108.17 67.640 l -108.17 69.030 l -109.66 69.030 l -109.66 70.205 l -108.17 70.205 l -108.17 73.626 l -108.17 74.481 108.52 74.908 109.22 74.908 c -109.38 74.908 109.57 74.879 109.79 74.819 c -h -110.30 78.272 m -110.30 77.250 l -116.80 77.250 l -116.80 78.272 l -h -121.39 75.251 m -120.76 75.856 120.09 76.159 119.37 76.159 c -118.76 76.159 118.27 75.972 117.89 75.600 c -117.51 75.228 117.32 74.745 117.32 74.153 c -117.32 73.383 117.62 72.789 118.24 72.372 c -118.86 71.955 119.74 71.747 120.88 71.747 c -121.39 71.747 l -121.39 71.112 l -121.39 70.389 120.97 70.027 120.15 70.027 c -119.42 70.027 118.68 70.234 117.93 70.649 c -117.93 69.354 l -118.78 69.032 119.62 68.872 120.45 68.872 c -122.28 68.872 123.19 69.597 123.19 71.049 c -123.19 74.134 l -123.19 74.680 123.36 74.953 123.72 74.953 c -123.78 74.953 123.86 74.944 123.96 74.927 c -124.01 75.981 l -123.61 76.099 123.26 76.159 122.95 76.159 c -122.18 76.159 121.69 75.856 121.47 75.251 c -h -121.39 74.242 m -121.39 72.826 l -120.94 72.826 l -119.73 72.826 119.12 73.207 119.12 73.969 c -119.12 74.227 119.21 74.444 119.38 74.619 c -119.56 74.795 119.78 74.883 120.03 74.883 c -120.47 74.883 120.92 74.669 121.39 74.242 c -h -129.92 76.000 m -129.92 74.686 l -129.31 75.668 128.52 76.159 127.55 76.159 c -126.93 76.159 126.44 75.962 126.08 75.568 c -125.72 75.175 125.54 74.637 125.54 73.956 c -125.54 69.030 l -127.42 69.030 l -127.42 73.493 l -127.42 74.284 127.68 74.680 128.21 74.680 c -128.80 74.680 129.37 74.259 129.92 73.417 c -129.92 69.030 l -131.80 69.030 l -131.80 76.000 l -h -137.74 75.962 m -137.30 76.093 136.94 76.159 136.68 76.159 c -135.05 76.159 134.24 75.397 134.24 73.874 c -134.24 70.205 l -133.46 70.205 l -133.46 69.030 l -134.24 69.030 l -134.24 67.856 l -136.12 67.640 l -136.12 69.030 l -137.61 69.030 l -137.61 70.205 l -136.12 70.205 l -136.12 73.626 l -136.12 74.481 136.47 74.908 137.17 74.908 c -137.33 74.908 137.52 74.879 137.74 74.819 c -h -139.42 76.000 m -139.42 65.977 l -141.30 65.977 l -141.30 70.344 l -141.91 69.362 142.70 68.872 143.67 68.872 c -144.29 68.872 144.79 69.068 145.15 69.462 c -145.50 69.855 145.68 70.393 145.68 71.074 c -145.68 76.000 l -143.81 76.000 l -143.81 71.538 l -143.81 70.746 143.54 70.351 143.02 70.351 c -142.42 70.351 141.85 70.772 141.30 71.614 c -141.30 76.000 l -h -146.78 78.272 m -146.78 77.250 l -153.28 77.250 l -153.28 78.272 l -h -158.05 75.962 m -157.60 76.093 157.25 76.159 156.99 76.159 c -155.36 76.159 154.55 75.397 154.55 73.874 c -154.55 70.205 l -153.77 70.205 l -153.77 69.030 l -154.55 69.030 l -154.55 67.856 l -156.42 67.640 l -156.42 69.030 l -157.92 69.030 l -157.92 70.205 l -156.42 70.205 l -156.42 73.626 l -156.42 74.481 156.77 74.908 157.47 74.908 c -157.63 74.908 157.83 74.879 158.05 74.819 c -h -162.65 76.159 m -161.56 76.159 160.70 75.830 160.06 75.172 c -159.42 74.514 159.10 73.628 159.10 72.515 c -159.10 71.389 159.42 70.501 160.07 69.849 c -160.71 69.197 161.59 68.872 162.70 68.872 c -163.81 68.872 164.69 69.197 165.33 69.849 c -165.98 70.501 166.30 71.385 166.30 72.502 c -166.30 73.645 165.98 74.540 165.33 75.188 c -164.68 75.835 163.79 76.159 162.65 76.159 c -h -162.68 74.984 m -163.76 74.984 164.30 74.157 164.30 72.502 c -164.30 71.745 164.16 71.146 163.87 70.706 c -163.59 70.266 163.20 70.046 162.70 70.046 c -162.20 70.046 161.81 70.266 161.53 70.706 c -161.24 71.146 161.10 71.749 161.10 72.515 c -161.10 73.273 161.24 73.874 161.52 74.318 c -161.81 74.762 162.19 74.984 162.68 74.984 c -h -168.03 76.000 m -168.03 65.977 l -169.91 65.977 l -169.91 72.280 l -170.03 72.280 l -172.51 69.030 l -174.07 69.030 l -171.78 72.014 l -174.79 76.000 l -172.50 76.000 l -170.03 72.515 l -169.91 72.515 l -169.91 76.000 l -h -181.93 75.765 m -181.04 76.028 180.19 76.159 179.39 76.159 c -178.23 76.159 177.31 75.829 176.64 75.168 c -175.96 74.508 175.63 73.607 175.63 72.464 c -175.63 71.385 175.93 70.517 176.55 69.859 c -177.17 69.201 177.98 68.872 178.99 68.872 c -180.01 68.872 180.76 69.193 181.23 69.836 c -181.69 70.480 181.93 71.497 181.93 72.890 c -177.61 72.890 l -177.73 74.218 178.46 74.883 179.80 74.883 c -180.43 74.883 181.14 74.737 181.93 74.445 c -h -177.58 71.830 m -180.08 71.830 l -180.08 70.640 179.70 70.046 178.93 70.046 c -178.15 70.046 177.70 70.640 177.58 71.830 c -h -183.87 76.000 m -183.87 69.030 l -185.75 69.030 l -185.75 70.344 l -186.36 69.362 187.16 68.872 188.12 68.872 c -188.75 68.872 189.24 69.068 189.60 69.462 c -189.96 69.855 190.14 70.393 190.14 71.074 c -190.14 76.000 l -188.26 76.000 l -188.26 71.538 l -188.26 70.746 188.00 70.351 187.47 70.351 c -186.87 70.351 186.30 70.772 185.75 71.614 c -185.75 76.000 l -h -f -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -60.000 146.00 m -138.00 146.00 l -138.00 163.00 l -60.000 163.00 l -60.000 146.00 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -60.500 146.50 m -575.50 146.50 l -575.50 214.50 l -60.500 214.50 l -60.500 146.50 l -h -S -60.500 163.50 m -137.50 163.50 l -S -137.50 163.50 m -139.50 159.50 l -S -139.50 159.50 m -139.50 146.50 l -S -64.225 162.16 m -64.377 160.80 l -65.152 161.16 65.890 161.33 66.593 161.33 c -67.287 161.33 67.788 161.18 68.097 160.89 c -68.406 160.59 68.561 160.11 68.561 159.45 c -68.561 158.50 l -68.104 159.50 67.348 160.00 66.294 160.00 c -65.465 160.00 64.807 159.69 64.320 159.07 c -63.834 158.45 63.590 157.61 63.590 156.55 c -63.590 155.44 63.863 154.55 64.409 153.88 c -64.955 153.21 65.681 152.87 66.586 152.87 c -67.297 152.87 67.955 153.16 68.561 153.73 c -68.757 153.03 l -70.446 153.03 l -70.446 158.35 l -70.446 159.41 70.381 160.18 70.252 160.65 c -70.123 161.12 69.872 161.52 69.500 161.85 c -68.874 162.39 67.991 162.66 66.853 162.66 c -66.045 162.66 65.169 162.49 64.225 162.16 c -h -68.561 157.40 m -68.561 154.83 l -68.108 154.32 67.634 154.06 67.139 154.06 c -66.665 154.06 66.288 154.28 66.009 154.71 c -65.729 155.14 65.590 155.72 65.590 156.45 c -65.590 157.81 66.028 158.50 66.904 158.50 c -67.509 158.50 68.061 158.13 68.561 157.40 c -h -78.425 159.77 m -77.532 160.03 76.686 160.16 75.886 160.16 c -74.722 160.16 73.804 159.83 73.131 159.17 c -72.458 158.51 72.122 157.61 72.122 156.46 c -72.122 155.39 72.429 154.52 73.045 153.86 c -73.661 153.20 74.474 152.87 75.486 152.87 c -76.506 152.87 77.250 153.19 77.720 153.84 c -78.190 154.48 78.425 155.50 78.425 156.89 c -74.102 156.89 l -74.229 158.22 74.959 158.88 76.292 158.88 c -76.923 158.88 77.633 158.74 78.425 158.44 c -h -74.077 155.83 m -76.578 155.83 l -76.578 154.64 76.195 154.05 75.429 154.05 c -74.650 154.05 74.199 154.64 74.077 155.83 c -h -83.960 159.96 m -83.511 160.09 83.158 160.16 82.900 160.16 c -81.271 160.16 80.456 159.40 80.456 157.87 c -80.456 154.20 l -79.675 154.20 l -79.675 153.03 l -80.456 153.03 l -80.456 151.86 l -82.335 151.64 l -82.335 153.03 l -83.827 153.03 l -83.827 154.20 l -82.335 154.20 l -82.335 157.63 l -82.335 158.48 82.684 158.91 83.382 158.91 c -83.543 158.91 83.736 158.88 83.960 158.82 c -h -84.461 162.27 m -84.461 161.25 l -90.961 161.25 l -90.961 162.27 l -h -95.729 159.96 m -95.280 160.09 94.927 160.16 94.668 160.16 c -93.039 160.16 92.225 159.40 92.225 157.87 c -92.225 154.20 l -91.444 154.20 l -91.444 153.03 l -92.225 153.03 l -92.225 151.86 l -94.104 151.64 l -94.104 153.03 l -95.595 153.03 l -95.595 154.20 l -94.104 154.20 l -94.104 157.63 l -94.104 158.48 94.453 158.91 95.151 158.91 c -95.312 158.91 95.504 158.88 95.729 158.82 c -h -103.08 159.77 m -102.19 160.03 101.34 160.16 100.54 160.16 c -99.376 160.16 98.458 159.83 97.785 159.17 c -97.112 158.51 96.776 157.61 96.776 156.46 c -96.776 155.39 97.084 154.52 97.699 153.86 c -98.315 153.20 99.129 152.87 100.14 152.87 c -101.16 152.87 101.90 153.19 102.37 153.84 c -102.84 154.48 103.08 155.50 103.08 156.89 c -98.756 156.89 l -98.883 158.22 99.613 158.88 100.95 158.88 c -101.58 158.88 102.29 158.74 103.08 158.44 c -h -98.731 155.83 m -101.23 155.83 l -101.23 154.64 100.85 154.05 100.08 154.05 c -99.304 154.05 98.854 154.64 98.731 155.83 c -h -105.02 160.00 m -105.02 153.03 l -106.90 153.03 l -106.90 154.34 l -107.51 153.36 108.31 152.87 109.27 152.87 c -109.90 152.87 110.39 153.07 110.75 153.46 c -111.11 153.86 111.29 154.39 111.29 155.07 c -111.29 160.00 l -109.41 160.00 l -109.41 155.54 l -109.41 154.75 109.15 154.35 108.62 154.35 c -108.02 154.35 107.45 154.77 106.90 155.61 c -106.90 160.00 l -h -116.97 159.25 m -116.35 159.86 115.68 160.16 114.96 160.16 c -114.35 160.16 113.86 159.97 113.48 159.60 c -113.10 159.23 112.91 158.75 112.91 158.15 c -112.91 157.38 113.21 156.79 113.83 156.37 c -114.44 155.96 115.33 155.75 116.47 155.75 c -116.97 155.75 l -116.97 155.11 l -116.97 154.39 116.56 154.03 115.74 154.03 c -115.00 154.03 114.26 154.23 113.51 154.65 c -113.51 153.35 l -114.37 153.03 115.21 152.87 116.04 152.87 c -117.86 152.87 118.78 153.60 118.78 155.05 c -118.78 158.13 l -118.78 158.68 118.95 158.95 119.30 158.95 c -119.37 158.95 119.45 158.94 119.55 158.93 c -119.60 159.98 l -119.20 160.10 118.85 160.16 118.54 160.16 c -117.77 160.16 117.28 159.86 117.06 159.25 c -h -116.97 158.24 m -116.97 156.83 l -116.53 156.83 l -115.32 156.83 114.71 157.21 114.71 157.97 c -114.71 158.23 114.80 158.44 114.97 158.62 c -115.15 158.80 115.36 158.88 115.62 158.88 c -116.06 158.88 116.51 158.67 116.97 158.24 c -h -121.20 160.00 m -121.20 153.03 l -123.08 153.03 l -123.08 154.34 l -123.69 153.36 124.49 152.87 125.45 152.87 c -126.08 152.87 126.57 153.07 126.93 153.46 c -127.29 153.86 127.47 154.39 127.47 155.07 c -127.47 160.00 l -125.59 160.00 l -125.59 155.54 l -125.59 154.75 125.33 154.35 124.80 154.35 c -124.20 154.35 123.63 154.77 123.08 155.61 c -123.08 160.00 l -h -133.33 159.96 m -132.88 160.09 132.53 160.16 132.27 160.16 c -130.64 160.16 129.83 159.40 129.83 157.87 c -129.83 154.20 l -129.05 154.20 l -129.05 153.03 l -129.83 153.03 l -129.83 151.86 l -131.71 151.64 l -131.71 153.03 l -133.20 153.03 l -133.20 154.20 l -131.71 154.20 l -131.71 157.63 l -131.71 158.48 132.06 158.91 132.75 158.91 c -132.92 158.91 133.11 158.88 133.33 158.82 c -h -134.82 159.78 m -134.82 158.40 l -135.75 158.79 136.55 158.98 137.21 158.98 c -137.98 158.98 138.37 158.72 138.37 158.20 c -138.37 157.86 138.05 157.56 137.41 157.31 c -136.78 157.05 l -136.09 156.78 135.60 156.47 135.30 156.15 c -135.00 155.83 134.86 155.43 134.86 154.96 c -134.86 154.30 135.11 153.79 135.61 153.42 c -136.11 153.05 136.82 152.87 137.72 152.87 c -138.29 152.87 138.97 152.95 139.75 153.12 c -139.75 154.44 l -139.00 154.18 138.37 154.05 137.88 154.05 c -137.10 154.05 136.71 154.29 136.71 154.77 c -136.71 155.09 137.00 155.36 137.57 155.58 c -138.12 155.79 l -138.93 156.09 139.50 156.41 139.82 156.72 c -140.14 157.04 140.30 157.45 140.30 157.95 c -140.30 158.61 140.03 159.14 139.49 159.55 c -138.94 159.95 138.23 160.16 137.36 160.16 c -136.52 160.16 135.68 160.03 134.82 159.78 c -h -f -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -60.000 230.00 m -177.00 230.00 l -177.00 247.00 l -60.000 247.00 l -60.000 230.00 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -60.500 230.50 m -575.50 230.50 l -575.50 298.50 l -60.500 298.50 l -60.500 230.50 l -h -S -60.500 247.50 m -176.50 247.50 l -S -176.50 247.50 m -178.50 243.50 l -S -178.50 243.50 m -178.50 230.50 l -S -67.767 243.96 m -67.319 244.09 66.965 244.16 66.707 244.16 c -65.078 244.16 64.263 243.40 64.263 241.87 c -64.263 238.20 l -63.482 238.20 l -63.482 237.03 l -64.263 237.03 l -64.263 235.86 l -66.142 235.64 l -66.142 237.03 l -67.634 237.03 l -67.634 238.20 l -66.142 238.20 l -66.142 241.63 l -66.142 242.48 66.491 242.91 67.189 242.91 c -67.350 242.91 67.543 242.88 67.767 242.82 c -h -75.118 243.77 m -74.225 244.03 73.378 244.16 72.579 244.16 c -71.415 244.16 70.497 243.83 69.824 243.17 c -69.151 242.51 68.814 241.61 68.814 240.46 c -68.814 239.39 69.122 238.52 69.738 237.86 c -70.354 237.20 71.167 236.87 72.179 236.87 c -73.199 236.87 73.943 237.19 74.413 237.84 c -74.883 238.48 75.118 239.50 75.118 240.89 c -70.795 240.89 l -70.922 242.22 71.652 242.88 72.985 242.88 c -73.615 242.88 74.326 242.74 75.118 242.44 c -h -70.770 239.83 m -73.271 239.83 l -73.271 238.64 72.888 238.05 72.122 238.05 c -71.343 238.05 70.892 238.64 70.770 239.83 c -h -77.060 244.00 m -77.060 237.03 l -78.939 237.03 l -78.939 238.34 l -79.553 237.36 80.344 236.87 81.313 236.87 c -81.935 236.87 82.426 237.07 82.786 237.46 c -83.145 237.86 83.325 238.39 83.325 239.07 c -83.325 244.00 l -81.446 244.00 l -81.446 239.54 l -81.446 238.75 81.184 238.35 80.659 238.35 c -80.062 238.35 79.489 238.77 78.939 239.61 c -78.939 244.00 l -h -89.013 243.25 m -88.386 243.86 87.716 244.16 87.000 244.16 c -86.391 244.16 85.896 243.97 85.515 243.60 c -85.134 243.23 84.944 242.75 84.944 242.15 c -84.944 241.38 85.252 240.79 85.867 240.37 c -86.483 239.96 87.364 239.75 88.511 239.75 c -89.013 239.75 l -89.013 239.11 l -89.013 238.39 88.600 238.03 87.775 238.03 c -87.043 238.03 86.302 238.23 85.553 238.65 c -85.553 237.35 l -86.404 237.03 87.246 236.87 88.080 236.87 c -89.903 236.87 90.815 237.60 90.815 239.05 c -90.815 242.13 l -90.815 242.68 90.991 242.95 91.342 242.95 c -91.406 242.95 91.488 242.94 91.590 242.93 c -91.634 243.98 l -91.236 244.10 90.885 244.16 90.581 244.16 c -89.810 244.16 89.315 243.86 89.095 243.25 c -h -89.013 242.24 m -89.013 240.83 l -88.568 240.83 l -87.354 240.83 86.747 241.21 86.747 241.97 c -86.747 242.23 86.834 242.44 87.010 242.62 c -87.186 242.80 87.403 242.88 87.661 242.88 c -88.101 242.88 88.551 242.67 89.013 242.24 c -h -93.240 244.00 m -93.240 237.03 l -95.119 237.03 l -95.119 238.34 l -95.733 237.36 96.524 236.87 97.493 236.87 c -98.115 236.87 98.606 237.07 98.966 237.46 c -99.326 237.86 99.505 238.39 99.505 239.07 c -99.505 244.00 l -97.626 244.00 l -97.626 239.54 l -97.626 238.75 97.364 238.35 96.839 238.35 c -96.243 238.35 95.669 238.77 95.119 239.61 c -95.119 244.00 l -h -105.37 243.96 m -104.92 244.09 104.57 244.16 104.31 244.16 c -102.68 244.16 101.87 243.40 101.87 241.87 c -101.87 238.20 l -101.09 238.20 l -101.09 237.03 l -101.87 237.03 l -101.87 235.86 l -103.75 235.64 l -103.75 237.03 l -105.24 237.03 l -105.24 238.20 l -103.75 238.20 l -103.75 241.63 l -103.75 242.48 104.09 242.91 104.79 242.91 c -104.95 242.91 105.15 242.88 105.37 242.82 c -h -105.87 246.27 m -105.87 245.25 l -112.37 245.25 l -112.37 246.27 l -h -116.96 243.25 m -116.34 243.86 115.66 244.16 114.95 244.16 c -114.34 244.16 113.84 243.97 113.46 243.60 c -113.08 243.23 112.89 242.75 112.89 242.15 c -112.89 241.38 113.20 240.79 113.82 240.37 c -114.43 239.96 115.31 239.75 116.46 239.75 c -116.96 239.75 l -116.96 239.11 l -116.96 238.39 116.55 238.03 115.72 238.03 c -114.99 238.03 114.25 238.23 113.50 238.65 c -113.50 237.35 l -114.35 237.03 115.19 236.87 116.03 236.87 c -117.85 236.87 118.76 237.60 118.76 239.05 c -118.76 242.13 l -118.76 242.68 118.94 242.95 119.29 242.95 c -119.35 242.95 119.44 242.94 119.54 242.93 c -119.58 243.98 l -119.19 244.10 118.83 244.16 118.53 244.16 c -117.76 244.16 117.26 243.86 117.04 243.25 c -h -116.96 242.24 m -116.96 240.83 l -116.52 240.83 l -115.30 240.83 114.70 241.21 114.70 241.97 c -114.70 242.23 114.78 242.44 114.96 242.62 c -115.13 242.80 115.35 242.88 115.61 242.88 c -116.05 242.88 116.50 242.67 116.96 242.24 c -h -125.50 244.00 m -125.50 242.69 l -124.89 243.67 124.10 244.16 123.12 244.16 c -122.50 244.16 122.01 243.96 121.65 243.57 c -121.29 243.17 121.11 242.64 121.11 241.96 c -121.11 237.03 l -122.99 237.03 l -122.99 241.49 l -122.99 242.28 123.26 242.68 123.79 242.68 c -124.38 242.68 124.95 242.26 125.50 241.42 c -125.50 237.03 l -127.38 237.03 l -127.38 244.00 l -h -133.32 243.96 m -132.87 244.09 132.52 244.16 132.26 244.16 c -130.63 244.16 129.82 243.40 129.82 241.87 c -129.82 238.20 l -129.03 238.20 l -129.03 237.03 l -129.82 237.03 l -129.82 235.86 l -131.69 235.64 l -131.69 237.03 l -133.19 237.03 l -133.19 238.20 l -131.69 238.20 l -131.69 241.63 l -131.69 242.48 132.04 242.91 132.74 242.91 c -132.90 242.91 133.10 242.88 133.32 242.82 c -h -135.00 244.00 m -135.00 233.98 l -136.87 233.98 l -136.87 238.34 l -137.49 237.36 138.28 236.87 139.25 236.87 c -139.87 236.87 140.36 237.07 140.72 237.46 c -141.08 237.86 141.26 238.39 141.26 239.07 c -141.26 244.00 l -139.38 244.00 l -139.38 239.54 l -139.38 238.75 139.12 238.35 138.59 238.35 c -138.00 238.35 137.42 238.77 136.87 239.61 c -136.87 244.00 l -h -142.36 246.27 m -142.36 245.25 l -148.86 245.25 l -148.86 246.27 l -h -153.63 243.96 m -153.18 244.09 152.82 244.16 152.57 244.16 c -150.94 244.16 150.12 243.40 150.12 241.87 c -150.12 238.20 l -149.34 238.20 l -149.34 237.03 l -150.12 237.03 l -150.12 235.86 l -152.00 235.64 l -152.00 237.03 l -153.49 237.03 l -153.49 238.20 l -152.00 238.20 l -152.00 241.63 l -152.00 242.48 152.35 242.91 153.05 242.91 c -153.21 242.91 153.40 242.88 153.63 242.82 c -h -158.23 244.16 m -157.14 244.16 156.28 243.83 155.63 243.17 c -154.99 242.51 154.67 241.63 154.67 240.52 c -154.67 239.39 155.00 238.50 155.64 237.85 c -156.29 237.20 157.16 236.87 158.27 236.87 c -159.38 236.87 160.26 237.20 160.91 237.85 c -161.55 238.50 161.88 239.39 161.88 240.50 c -161.88 241.65 161.55 242.54 160.91 243.19 c -160.26 243.83 159.37 244.16 158.23 244.16 c -h -158.26 242.98 m -159.34 242.98 159.88 242.16 159.88 240.50 c -159.88 239.74 159.74 239.15 159.45 238.71 c -159.16 238.27 158.77 238.05 158.27 238.05 c -157.78 238.05 157.39 238.27 157.10 238.71 c -156.82 239.15 156.67 239.75 156.67 240.52 c -156.67 241.27 156.81 241.87 157.10 242.32 c -157.38 242.76 157.77 242.98 158.26 242.98 c -h -163.60 244.00 m -163.60 233.98 l -165.48 233.98 l -165.48 240.28 l -165.60 240.28 l -168.09 237.03 l -169.65 237.03 l -167.36 240.01 l -170.36 244.00 l -168.08 244.00 l -165.60 240.52 l -165.48 240.52 l -165.48 244.00 l -h -177.51 243.77 m -176.61 244.03 175.77 244.16 174.97 244.16 c -173.80 244.16 172.88 243.83 172.21 243.17 c -171.54 242.51 171.20 241.61 171.20 240.46 c -171.20 239.39 171.51 238.52 172.13 237.86 c -172.74 237.20 173.56 236.87 174.57 236.87 c -175.59 236.87 176.33 237.19 176.80 237.84 c -177.27 238.48 177.51 239.50 177.51 240.89 c -173.18 240.89 l -173.31 242.22 174.04 242.88 175.37 242.88 c -176.00 242.88 176.71 242.74 177.51 242.44 c -h -173.16 239.83 m -175.66 239.83 l -175.66 238.64 175.28 238.05 174.51 238.05 c -173.73 238.05 173.28 238.64 173.16 239.83 c -h -179.45 244.00 m -179.45 237.03 l -181.33 237.03 l -181.33 238.34 l -181.94 237.36 182.73 236.87 183.70 236.87 c -184.32 236.87 184.81 237.07 185.17 237.46 c -185.53 237.86 185.71 238.39 185.71 239.07 c -185.71 244.00 l -183.83 244.00 l -183.83 239.54 l -183.83 238.75 183.57 238.35 183.05 238.35 c -182.45 238.35 181.88 238.77 181.33 239.61 c -181.33 244.00 l -h -f -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -50.000 334.00 m -151.00 334.00 l -151.00 351.00 l -50.000 351.00 l -50.000 334.00 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -50.500 334.50 m -1128.5 334.50 l -1128.5 546.50 l -50.500 546.50 l -50.500 334.50 l -h -S -50.500 351.50 m -150.50 351.50 l -S -150.50 351.50 m -152.50 347.50 l -S -152.50 347.50 m -152.50 334.50 l -S -59.316 347.85 m -58.537 348.06 57.816 348.16 57.151 348.16 c -56.038 348.16 55.159 347.83 54.514 347.18 c -53.869 346.52 53.546 345.63 53.546 344.51 c -53.546 343.37 53.878 342.48 54.542 341.84 c -55.207 341.19 56.125 340.87 57.297 340.87 c -57.864 340.87 58.518 340.96 59.259 341.14 c -59.259 342.50 l -58.489 342.25 57.873 342.13 57.412 342.13 c -56.849 342.13 56.396 342.34 56.053 342.78 c -55.710 343.21 55.539 343.78 55.539 344.50 c -55.539 345.23 55.724 345.81 56.094 346.25 c -56.465 346.69 56.959 346.91 57.577 346.91 c -58.139 346.91 58.719 346.79 59.316 346.55 c -h -61.093 348.00 m -61.093 341.03 l -62.972 341.03 l -62.972 342.34 l -63.459 341.36 64.199 340.87 65.194 340.87 c -65.312 340.87 65.429 340.88 65.543 340.91 c -65.543 342.59 l -65.276 342.49 65.029 342.44 64.800 342.44 c -64.051 342.44 63.442 342.82 62.972 343.58 c -62.972 348.00 l -h -72.678 347.77 m -71.785 348.03 70.938 348.16 70.139 348.16 c -68.975 348.16 68.057 347.83 67.384 347.17 c -66.711 346.51 66.375 345.61 66.375 344.46 c -66.375 343.39 66.682 342.52 67.298 341.86 c -67.914 341.20 68.727 340.87 69.739 340.87 c -70.759 340.87 71.503 341.19 71.973 341.84 c -72.443 342.48 72.678 343.50 72.678 344.89 c -68.355 344.89 l -68.482 346.22 69.212 346.88 70.545 346.88 c -71.175 346.88 71.886 346.74 72.678 346.44 c -h -68.330 343.83 m -70.831 343.83 l -70.831 342.64 70.448 342.05 69.682 342.05 c -68.903 342.05 68.452 342.64 68.330 343.83 c -h -78.035 347.25 m -77.409 347.86 76.738 348.16 76.023 348.16 c -75.414 348.16 74.918 347.97 74.538 347.60 c -74.157 347.23 73.966 346.75 73.966 346.15 c -73.966 345.38 74.274 344.79 74.890 344.37 c -75.506 343.96 76.387 343.75 77.534 343.75 c -78.035 343.75 l -78.035 343.11 l -78.035 342.39 77.623 342.03 76.797 342.03 c -76.065 342.03 75.325 342.23 74.576 342.65 c -74.576 341.35 l -75.426 341.03 76.268 340.87 77.102 340.87 c -78.926 340.87 79.838 341.60 79.838 343.05 c -79.838 346.13 l -79.838 346.68 80.014 346.95 80.365 346.95 c -80.428 346.95 80.511 346.94 80.612 346.93 c -80.657 347.98 l -80.259 348.10 79.908 348.16 79.603 348.16 c -78.833 348.16 78.338 347.86 78.118 347.25 c -h -78.035 346.24 m -78.035 344.83 l -77.591 344.83 l -76.376 344.83 75.769 345.21 75.769 345.97 c -75.769 346.23 75.857 346.44 76.032 346.62 c -76.208 346.80 76.425 346.88 76.683 346.88 c -77.123 346.88 77.574 346.67 78.035 346.24 c -h -85.855 347.96 m -85.407 348.09 85.054 348.16 84.795 348.16 c -83.166 348.16 82.352 347.40 82.352 345.87 c -82.352 342.20 l -81.571 342.20 l -81.571 341.03 l -82.352 341.03 l -82.352 339.86 l -84.230 339.64 l -84.230 341.03 l -85.722 341.03 l -85.722 342.20 l -84.230 342.20 l -84.230 345.63 l -84.230 346.48 84.580 346.91 85.278 346.91 c -85.439 346.91 85.631 346.88 85.855 346.82 c -h -93.206 347.77 m -92.313 348.03 91.467 348.16 90.667 348.16 c -89.503 348.16 88.585 347.83 87.912 347.17 c -87.239 346.51 86.903 345.61 86.903 344.46 c -86.903 343.39 87.211 342.52 87.826 341.86 c -88.442 341.20 89.256 340.87 90.267 340.87 c -91.287 340.87 92.032 341.19 92.501 341.84 c -92.971 342.48 93.206 343.50 93.206 344.89 c -88.883 344.89 l -89.010 346.22 89.740 346.88 91.073 346.88 c -91.704 346.88 92.415 346.74 93.206 346.44 c -h -88.858 343.83 m -91.359 343.83 l -91.359 342.64 90.976 342.05 90.210 342.05 c -89.431 342.05 88.981 342.64 88.858 343.83 c -h -93.974 350.27 m -93.974 349.25 l -100.47 349.25 l -100.47 350.27 l -h -101.65 348.00 m -101.65 341.03 l -103.53 341.03 l -103.53 348.00 l -h -101.65 339.86 m -101.65 338.29 l -103.53 338.29 l -103.53 339.86 l -h -105.88 348.00 m -105.88 341.03 l -107.75 341.03 l -107.75 342.34 l -108.37 341.36 109.16 340.87 110.13 340.87 c -110.75 340.87 111.24 341.07 111.60 341.46 c -111.96 341.86 112.14 342.39 112.14 343.07 c -112.14 348.00 l -110.26 348.00 l -110.26 343.54 l -110.26 342.75 110.00 342.35 109.48 342.35 c -108.88 342.35 108.31 342.77 107.75 343.61 c -107.75 348.00 l -h -114.23 347.78 m -114.23 346.40 l -115.16 346.79 115.96 346.98 116.62 346.98 c -117.39 346.98 117.77 346.72 117.77 346.20 c -117.77 345.86 117.45 345.56 116.82 345.31 c -116.18 345.05 l -115.49 344.78 115.00 344.47 114.71 344.15 c -114.41 343.83 114.26 343.43 114.26 342.96 c -114.26 342.30 114.51 341.79 115.02 341.42 c -115.52 341.05 116.22 340.87 117.13 340.87 c -117.70 340.87 118.37 340.95 119.16 341.12 c -119.16 342.44 l -118.40 342.18 117.78 342.05 117.28 342.05 c -116.50 342.05 116.11 342.29 116.11 342.77 c -116.11 343.09 116.40 343.36 116.98 343.58 c -117.52 343.79 l -118.34 344.09 118.91 344.41 119.23 344.72 c -119.55 345.04 119.71 345.45 119.71 345.95 c -119.71 346.61 119.44 347.14 118.89 347.55 c -118.35 347.95 117.64 348.16 116.77 348.16 c -115.93 348.16 115.08 348.03 114.23 347.78 c -h -125.36 347.96 m -124.91 348.09 124.56 348.16 124.30 348.16 c -122.67 348.16 121.85 347.40 121.85 345.87 c -121.85 342.20 l -121.07 342.20 l -121.07 341.03 l -121.85 341.03 l -121.85 339.86 l -123.73 339.64 l -123.73 341.03 l -125.22 341.03 l -125.22 342.20 l -123.73 342.20 l -123.73 345.63 l -123.73 346.48 124.08 346.91 124.78 346.91 c -124.94 346.91 125.13 346.88 125.36 346.82 c -h -130.45 347.25 m -129.82 347.86 129.15 348.16 128.44 348.16 c -127.83 348.16 127.33 347.97 126.95 347.60 c -126.57 347.23 126.38 346.75 126.38 346.15 c -126.38 345.38 126.69 344.79 127.30 344.37 c -127.92 343.96 128.80 343.75 129.95 343.75 c -130.45 343.75 l -130.45 343.11 l -130.45 342.39 130.04 342.03 129.21 342.03 c -128.48 342.03 127.74 342.23 126.99 342.65 c -126.99 341.35 l -127.84 341.03 128.68 340.87 129.51 340.87 c -131.34 340.87 132.25 341.60 132.25 343.05 c -132.25 346.13 l -132.25 346.68 132.43 346.95 132.78 346.95 c -132.84 346.95 132.92 346.94 133.02 346.93 c -133.07 347.98 l -132.67 348.10 132.32 348.16 132.02 348.16 c -131.25 348.16 130.75 347.86 130.53 347.25 c -h -130.45 346.24 m -130.45 344.83 l -130.00 344.83 l -128.79 344.83 128.18 345.21 128.18 345.97 c -128.18 346.23 128.27 346.44 128.45 346.62 c -128.62 346.80 128.84 346.88 129.10 346.88 c -129.54 346.88 129.99 346.67 130.45 346.24 c -h -134.68 348.00 m -134.68 341.03 l -136.55 341.03 l -136.55 342.34 l -137.17 341.36 137.96 340.87 138.93 340.87 c -139.55 340.87 140.04 341.07 140.40 341.46 c -140.76 341.86 140.94 342.39 140.94 343.07 c -140.94 348.00 l -139.06 348.00 l -139.06 343.54 l -139.06 342.75 138.80 342.35 138.27 342.35 c -137.68 342.35 137.10 342.77 136.55 343.61 c -136.55 348.00 l -h -148.35 347.85 m -147.58 348.06 146.85 348.16 146.19 348.16 c -145.08 348.16 144.20 347.83 143.55 347.18 c -142.91 346.52 142.58 345.63 142.58 344.51 c -142.58 343.37 142.92 342.48 143.58 341.84 c -144.25 341.19 145.16 340.87 146.34 340.87 c -146.90 340.87 147.56 340.96 148.30 341.14 c -148.30 342.50 l -147.53 342.25 146.91 342.13 146.45 342.13 c -145.89 342.13 145.43 342.34 145.09 342.78 c -144.75 343.21 144.58 343.78 144.58 344.50 c -144.58 345.23 144.76 345.81 145.13 346.25 c -145.50 346.69 146.00 346.91 146.62 346.91 c -147.18 346.91 147.76 346.79 148.35 346.55 c -h -155.81 347.77 m -154.91 348.03 154.07 348.16 153.27 348.16 c -152.10 348.16 151.19 347.83 150.51 347.17 c -149.84 346.51 149.50 345.61 149.50 344.46 c -149.50 343.39 149.81 342.52 150.43 341.86 c -151.04 341.20 151.86 340.87 152.87 340.87 c -153.89 340.87 154.63 341.19 155.10 341.84 c -155.57 342.48 155.81 343.50 155.81 344.89 c -151.48 344.89 l -151.61 346.22 152.34 346.88 153.67 346.88 c -154.30 346.88 155.02 346.74 155.81 346.44 c -h -151.46 343.83 m -153.96 343.83 l -153.96 342.64 153.58 342.05 152.81 342.05 c -152.03 342.05 151.58 342.64 151.46 343.83 c -h -f -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -547.00 402.00 m -658.00 402.00 l -658.00 419.00 l -547.00 419.00 l -547.00 402.00 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -547.50 402.50 m -754.50 402.50 l -754.50 470.50 l -547.50 470.50 l -547.50 402.50 l -h -S -547.50 419.50 m -657.50 419.50 l -S -657.50 419.50 m -659.50 415.50 l -S -659.50 415.50 m -659.50 402.50 l -S -554.59 415.25 m -553.96 415.86 553.29 416.16 552.58 416.16 c -551.97 416.16 551.47 415.97 551.09 415.60 c -550.71 415.23 550.52 414.75 550.52 414.15 c -550.52 413.38 550.83 412.79 551.44 412.37 c -552.06 411.96 552.94 411.75 554.09 411.75 c -554.59 411.75 l -554.59 411.11 l -554.59 410.39 554.18 410.03 553.35 410.03 c -552.62 410.03 551.88 410.23 551.13 410.65 c -551.13 409.35 l -551.98 409.03 552.82 408.87 553.66 408.87 c -555.48 408.87 556.39 409.60 556.39 411.05 c -556.39 414.13 l -556.39 414.68 556.57 414.95 556.92 414.95 c -556.98 414.95 557.06 414.94 557.17 414.93 c -557.21 415.98 l -556.81 416.10 556.46 416.16 556.16 416.16 c -555.39 416.16 554.89 415.86 554.67 415.25 c -h -554.59 414.24 m -554.59 412.83 l -554.15 412.83 l -552.93 412.83 552.32 413.21 552.32 413.97 c -552.32 414.23 552.41 414.44 552.59 414.62 c -552.76 414.80 552.98 414.88 553.24 414.88 c -553.68 414.88 554.13 414.67 554.59 414.24 c -h -563.13 416.00 m -563.13 414.69 l -562.52 415.67 561.73 416.16 560.75 416.16 c -560.13 416.16 559.64 415.96 559.28 415.57 c -558.92 415.17 558.74 414.64 558.74 413.96 c -558.74 409.03 l -560.62 409.03 l -560.62 413.49 l -560.62 414.28 560.88 414.68 561.41 414.68 c -562.01 414.68 562.58 414.26 563.13 413.42 c -563.13 409.03 l -565.01 409.03 l -565.01 416.00 l -h -570.95 415.96 m -570.50 416.09 570.15 416.16 569.89 416.16 c -568.26 416.16 567.44 415.40 567.44 413.87 c -567.44 410.20 l -566.66 410.20 l -566.66 409.03 l -567.44 409.03 l -567.44 407.86 l -569.32 407.64 l -569.32 409.03 l -570.81 409.03 l -570.81 410.20 l -569.32 410.20 l -569.32 413.63 l -569.32 414.48 569.67 414.91 570.37 414.91 c -570.53 414.91 570.72 414.88 570.95 414.82 c -h -572.62 416.00 m -572.62 405.98 l -574.50 405.98 l -574.50 410.34 l -575.12 409.36 575.91 408.87 576.88 408.87 c -577.50 408.87 577.99 409.07 578.35 409.46 c -578.71 409.86 578.89 410.39 578.89 411.07 c -578.89 416.00 l -577.01 416.00 l -577.01 411.54 l -577.01 410.75 576.75 410.35 576.22 410.35 c -575.63 410.35 575.05 410.77 574.50 411.61 c -574.50 416.00 l -h -579.99 418.27 m -579.99 417.25 l -586.49 417.25 l -586.49 418.27 l -h -587.66 416.00 m -587.66 409.03 l -589.46 409.03 l -589.46 410.34 l -590.01 409.36 590.80 408.87 591.81 408.87 c -592.34 408.87 592.77 409.00 593.11 409.26 c -593.45 409.52 593.65 409.88 593.73 410.34 c -594.38 409.36 595.17 408.87 596.09 408.87 c -597.36 408.87 598.00 409.57 598.00 410.98 c -598.00 416.00 l -596.20 416.00 l -596.20 411.59 l -596.20 410.77 595.92 410.36 595.37 410.36 c -594.81 410.36 594.26 410.76 593.73 411.58 c -593.73 416.00 l -591.93 416.00 l -591.93 411.59 l -591.93 410.77 591.65 410.35 591.09 410.35 c -590.54 410.35 590.00 410.76 589.46 411.58 c -589.46 416.00 l -h -600.27 416.00 m -600.27 409.03 l -602.15 409.03 l -602.15 416.00 l -h -600.27 407.86 m -600.27 406.29 l -602.15 406.29 l -602.15 407.86 l -h -608.88 416.00 m -608.88 414.69 l -608.40 415.67 607.64 416.16 606.60 416.16 c -605.76 416.16 605.11 415.85 604.63 415.24 c -604.15 414.62 603.91 413.78 603.91 412.71 c -603.91 411.54 604.18 410.61 604.72 409.91 c -605.26 409.22 605.98 408.87 606.89 408.87 c -607.62 408.87 608.28 409.16 608.88 409.73 c -608.88 405.98 l -610.77 405.98 l -610.77 416.00 l -h -608.88 410.85 m -608.43 410.34 607.94 410.09 607.43 410.09 c -606.97 410.09 606.60 410.31 606.32 410.76 c -606.05 411.20 605.91 411.80 605.91 412.55 c -605.91 413.97 606.37 414.69 607.28 414.69 c -607.84 414.69 608.37 414.33 608.88 413.61 c -h -617.49 416.00 m -617.49 414.69 l -617.01 415.67 616.25 416.16 615.22 416.16 c -614.38 416.16 613.72 415.85 613.24 415.24 c -612.76 414.62 612.52 413.78 612.52 412.71 c -612.52 411.54 612.79 410.61 613.33 409.91 c -613.87 409.22 614.60 408.87 615.51 408.87 c -616.24 408.87 616.90 409.16 617.49 409.73 c -617.49 405.98 l -619.38 405.98 l -619.38 416.00 l -h -617.49 410.85 m -617.04 410.34 616.56 410.09 616.04 410.09 c -615.58 410.09 615.21 410.31 614.94 410.76 c -614.66 411.20 614.52 411.80 614.52 412.55 c -614.52 413.97 614.98 414.69 615.90 414.69 c -616.45 414.69 616.99 414.33 617.49 413.61 c -h -621.72 416.00 m -621.72 405.98 l -623.60 405.98 l -623.60 416.00 l -h -631.62 415.77 m -630.73 416.03 629.89 416.16 629.09 416.16 c -627.92 416.16 627.00 415.83 626.33 415.17 c -625.66 414.51 625.32 413.61 625.32 412.46 c -625.32 411.39 625.63 410.52 626.24 409.86 c -626.86 409.20 627.67 408.87 628.69 408.87 c -629.71 408.87 630.45 409.19 630.92 409.84 c -631.39 410.48 631.62 411.50 631.62 412.89 c -627.30 412.89 l -627.43 414.22 628.16 414.88 629.49 414.88 c -630.12 414.88 630.83 414.74 631.62 414.44 c -h -627.28 411.83 m -629.78 411.83 l -629.78 410.64 629.39 410.05 628.63 410.05 c -627.85 410.05 627.40 410.64 627.28 411.83 c -h -634.80 416.00 m -632.86 409.03 l -634.61 409.03 l -635.98 413.91 l -637.44 409.03 l -639.06 409.03 l -640.38 413.94 l -641.86 409.03 l -643.16 409.03 l -641.10 416.00 l -639.29 416.00 l -638.02 411.22 l -636.60 416.00 l -h -648.20 415.25 m -647.57 415.86 646.90 416.16 646.19 416.16 c -645.58 416.16 645.08 415.97 644.70 415.60 c -644.32 415.23 644.13 414.75 644.13 414.15 c -644.13 413.38 644.44 412.79 645.05 412.37 c -645.67 411.96 646.55 411.75 647.70 411.75 c -648.20 411.75 l -648.20 411.11 l -648.20 410.39 647.79 410.03 646.96 410.03 c -646.23 410.03 645.49 410.23 644.74 410.65 c -644.74 409.35 l -645.59 409.03 646.43 408.87 647.27 408.87 c -649.09 408.87 650.00 409.60 650.00 411.05 c -650.00 414.13 l -650.00 414.68 650.18 414.95 650.53 414.95 c -650.59 414.95 650.67 414.94 650.78 414.93 c -650.82 415.98 l -650.42 416.10 650.07 416.16 649.77 416.16 c -649.00 416.16 648.50 415.86 648.28 415.25 c -h -648.20 414.24 m -648.20 412.83 l -647.75 412.83 l -646.54 412.83 645.93 413.21 645.93 413.97 c -645.93 414.23 646.02 414.44 646.20 414.62 c -646.37 414.80 646.59 414.88 646.85 414.88 c -647.29 414.88 647.74 414.67 648.20 414.24 c -h -652.43 416.00 m -652.43 409.03 l -654.30 409.03 l -654.30 410.34 l -654.79 409.36 655.53 408.87 656.53 408.87 c -656.64 408.87 656.76 408.88 656.88 408.91 c -656.88 410.59 l -656.61 410.49 656.36 410.44 656.13 410.44 c -655.38 410.44 654.77 410.82 654.30 411.58 c -654.30 416.00 l -h -664.01 415.77 m -663.12 416.03 662.27 416.16 661.47 416.16 c -660.31 416.16 659.39 415.83 658.72 415.17 c -658.04 414.51 657.71 413.61 657.71 412.46 c -657.71 411.39 658.01 410.52 658.63 409.86 c -659.25 409.20 660.06 408.87 661.07 408.87 c -662.09 408.87 662.84 409.19 663.31 409.84 c -663.78 410.48 664.01 411.50 664.01 412.89 c -659.69 412.89 l -659.81 414.22 660.54 414.88 661.88 414.88 c -662.51 414.88 663.22 414.74 664.01 414.44 c -h -659.66 411.83 m -662.16 411.83 l -662.16 410.64 661.78 410.05 661.01 410.05 c -660.24 410.05 659.78 410.64 659.66 411.83 c -h -f -Q -Q -Q - -endstream -endobj - -8 0 obj - 219823 -endobj - -3 0 obj - << - /Parent null - /Type /Pages - /MediaBox [0.0000 0.0000 842.00 595.00] - /Resources 9 0 R - /Kids [6 0 R] - /Count 1 - >> -endobj - -10 0 obj - [/PDF /Text /ImageC] -endobj - -11 0 obj - << - /Alpha1 - << - /ca 1.0000 - /CA 1.0000 - /BM /Normal - /AIS false - >> - >> -endobj - -9 0 obj - << - /ProcSet 10 0 R - /ExtGState 11 0 R - >> -endobj - -4 0 obj - << - /Type /Outlines - /First 12 0 R - /Last 12 0 R - >> -endobj - -12 0 obj - << - /Parent 4 0 R - /Title (Page 1 \(untitled\)) - /Prev null - /Next null - /Dest [6 0 R /Fit] - >> -endobj - -xref -0 13 -0000000000 65535 f -0000000016 00000 n -0000000343 00000 n -0000220607 00000 n -0000221038 00000 n -0000000525 00000 n -0000000602 00000 n -0000000691 00000 n -0000220581 00000 n -0000220963 00000 n -0000220778 00000 n -0000220819 00000 n -0000221128 00000 n - -trailer -<< - /Size 12 - /Root 2 0 R - /Info 1 0 R ->> - -startxref -221272 - -%%EOF diff --git a/doc/design/use_case_1.png b/doc/design/use_case_1.png deleted file mode 100644 index 1090528fa6..0000000000 Binary files a/doc/design/use_case_1.png and /dev/null differ diff --git a/doc/design/use_case_1.sdx b/doc/design/use_case_1.sdx deleted file mode 100644 index df678491a7..0000000000 --- a/doc/design/use_case_1.sdx +++ /dev/null @@ -1,83 +0,0 @@ - - - -[/c] - -[c:get_tenants] -client:tenants=keystone.get_tenants -[/c] - -[c:tenant_auth_token] -client:token, serviceCatalog=keystone.auth -[/c] - -client:endpoint = serviceCatalog['compute'] - -[c:create_instance] -client:success=nova.createInstance - -nova:tenant = parse(url) -[c:auth_middleware] -nova:user, roles=keystone.validate -[/c] -nova:authorize=can_haz(context, user, 'create_instance', tenant_id) -nova:execute create_instance -[/c] -client:200 OK]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/design/use_case_2.pdf b/doc/design/use_case_2.pdf deleted file mode 100644 index be79b52f43..0000000000 --- a/doc/design/use_case_2.pdf +++ /dev/null @@ -1,6006 +0,0 @@ -%PDF-1.4 -%âãÏÓ - -1 0 obj - << - /Title () - /Author () - /Subject () - /Keywords () - /Creator (FreeHEP Graphics2D Driver) - /Producer (org.freehep.graphicsio.pdf.PDFGraphics2D Revision: 10516 ) - /CreationDate (D:20111117212727-06'00') - /ModDate (D:20111117212727-06'00') - /Trapped /False - >> -endobj - -2 0 obj - << - /Type /Catalog - /Pages 3 0 R - /Outlines 4 0 R - /PageMode /UseOutlines - /ViewerPreferences 5 0 R - /OpenAction [6 0 R /Fit] - >> -endobj - -5 0 obj - << - /FitWindow true - /CenterWindow false - >> -endobj - -6 0 obj - << - /Parent 3 0 R - /Type /Page - /Contents 7 0 R - >> -endobj - -7 0 obj - << - /Length 8 0 R - >> -stream -.93277 0.0000 0.0000 -.93277 28.303 575.00 cm -q -0.0000 0.0000 m -842.00 0.0000 l -842.00 595.00 l -0.0000 595.00 l -h -W -n -q -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -1.0000 0.0000 0.0000 1.0000 0.0000 0.0000 cm -Q -q -1.0000 0.0000 0.0000 1.0000 0.0000 0.0000 cm -0.0000 0.0000 m -0.0000 595.00 l -842.00 595.00 l -842.00 0.0000 l -h -W -n -q -.80806 0.0000 0.0000 .80806 0.0000 0.0000 cm -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -0.0000 0.0000 m -1042.0 0.0000 l -1042.0 737.00 l -0.0000 737.00 l -0.0000 0.0000 l -h -f -1.0000 0.0000 0.0000 1.0000 0.0000 0.0000 cm -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -0 J -1 j -1.0000 M -[ 1.0000 2.0000] 0.0000 d -66.500 44.500 m -66.500 54.500 l -S -66.500 44.500 m -66.500 54.500 l -S -[] 0.0000 d -/Alpha1 gs -.93333 .93333 .93333 rg -.93333 .93333 .93333 RG -62.000 54.000 m -70.000 54.000 l -70.000 406.00 l -62.000 406.00 l -62.000 54.000 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -62.500 54.500 m -70.500 54.500 l -70.500 406.50 l -62.500 406.50 l -62.500 54.500 l -h -S -[ 1.0000 2.0000] 0.0000 d -66.500 406.50 m -66.500 406.50 l -S -448.50 44.500 m -448.50 102.50 l -S -448.50 44.500 m -448.50 102.50 l -S -[] 0.0000 d -/Alpha1 gs -.93333 .93333 .93333 rg -.93333 .93333 .93333 RG -444.00 102.00 m -452.00 102.00 l -452.00 120.00 l -444.00 120.00 l -444.00 102.00 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -444.50 102.50 m -452.50 102.50 l -452.50 120.50 l -444.50 120.50 l -444.50 102.50 l -h -S -[ 1.0000 2.0000] 0.0000 d -448.50 120.50 m -448.50 274.50 l -S -[] 0.0000 d -/Alpha1 gs -.93333 .93333 .93333 rg -.93333 .93333 .93333 RG -444.00 274.00 m -452.00 274.00 l -452.00 292.00 l -444.00 292.00 l -444.00 274.00 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -444.50 274.50 m -452.50 274.50 l -452.50 292.50 l -444.50 292.50 l -444.50 274.50 l -h -S -[ 1.0000 2.0000] 0.0000 d -448.50 292.50 m -448.50 406.50 l -S -627.50 44.500 m -627.50 206.50 l -S -627.50 44.500 m -627.50 206.50 l -S -[] 0.0000 d -/Alpha1 gs -.93333 .93333 .93333 rg -.93333 .93333 .93333 RG -623.00 206.00 m -631.00 206.00 l -631.00 368.00 l -623.00 368.00 l -623.00 206.00 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -623.50 206.50 m -631.50 206.50 l -631.50 368.50 l -623.50 368.50 l -623.50 206.50 l -h -S -[ 1.0000 2.0000] 0.0000 d -627.50 368.50 m -627.50 406.50 l -S -81.818 154.79 m -81.045 155.03 80.383 155.15 79.832 155.15 c -78.895 155.15 78.130 154.83 77.538 154.21 c -76.946 153.59 76.650 152.78 76.650 151.79 c -76.650 150.82 76.911 150.03 77.433 149.42 c -77.954 148.80 78.621 148.49 79.434 148.49 c -80.203 148.49 80.798 148.76 81.218 149.31 c -81.638 149.86 81.848 150.63 81.848 151.64 c -81.842 152.00 l -77.828 152.00 l -77.996 153.51 78.736 154.27 80.049 154.27 c -80.529 154.27 81.119 154.14 81.818 153.88 c -h -77.881 151.13 m -80.688 151.13 l -80.688 149.95 80.246 149.36 79.363 149.36 c -78.477 149.36 77.982 149.95 77.881 151.13 c -h -83.840 155.00 m -83.840 148.64 l -84.994 148.64 l -84.994 149.83 l -85.604 148.94 86.350 148.50 87.232 148.50 c -87.783 148.50 88.223 148.67 88.551 149.02 c -88.879 149.37 89.043 149.84 89.043 150.43 c -89.043 155.00 l -87.889 155.00 l -87.889 150.80 l -87.889 150.33 87.819 150.00 87.681 149.79 c -87.542 149.59 87.312 149.49 86.992 149.49 c -86.285 149.49 85.619 149.96 84.994 150.88 c -84.994 155.00 l -h -95.371 155.00 m -95.371 153.80 l -94.902 154.70 94.195 155.15 93.250 155.15 c -92.484 155.15 91.882 154.87 91.442 154.31 c -91.003 153.75 90.783 152.99 90.783 152.02 c -90.783 150.96 91.032 150.11 91.530 149.46 c -92.028 148.82 92.684 148.50 93.496 148.50 c -94.250 148.50 94.875 148.79 95.371 149.36 c -95.371 145.75 l -96.531 145.75 l -96.531 155.00 l -h -95.371 150.15 m -94.773 149.63 94.207 149.36 93.672 149.36 c -92.566 149.36 92.014 150.21 92.014 151.90 c -92.014 153.39 92.506 154.13 93.490 154.13 c -94.131 154.13 94.758 153.78 95.371 153.08 c -h -98.840 157.31 m -98.840 148.64 l -99.994 148.64 l -99.994 149.83 l -100.47 148.94 101.18 148.50 102.12 148.50 c -102.89 148.50 103.49 148.78 103.93 149.33 c -104.37 149.89 104.59 150.66 104.59 151.62 c -104.59 152.68 104.34 153.53 103.84 154.18 c -103.34 154.82 102.69 155.15 101.88 155.15 c -101.12 155.15 100.49 154.86 99.994 154.28 c -99.994 157.31 l -h -99.994 153.48 m -100.59 154.01 101.15 154.28 101.69 154.28 c -102.80 154.28 103.36 153.43 103.36 151.74 c -103.36 150.25 102.87 149.50 101.88 149.50 c -101.24 149.50 100.61 149.85 99.994 150.55 c -h -108.88 155.15 m -107.97 155.15 107.25 154.84 106.70 154.24 c -106.16 153.64 105.89 152.83 105.89 151.82 c -105.89 150.79 106.16 149.99 106.71 149.39 c -107.25 148.79 107.99 148.50 108.92 148.50 c -109.86 148.50 110.60 148.79 111.14 149.39 c -111.69 149.99 111.96 150.79 111.96 151.81 c -111.96 152.85 111.69 153.66 111.14 154.26 c -110.59 154.85 109.84 155.15 108.88 155.15 c -h -108.90 154.28 m -110.12 154.28 110.73 153.46 110.73 151.81 c -110.73 150.18 110.13 149.36 108.92 149.36 c -107.72 149.36 107.12 150.18 107.12 151.82 c -107.12 153.46 107.71 154.28 108.90 154.28 c -h -113.76 155.00 m -113.76 148.64 l -114.92 148.64 l -114.92 155.00 l -h -113.76 147.48 m -113.76 146.33 l -114.92 146.33 l -114.92 147.48 l -h -117.23 155.00 m -117.23 148.64 l -118.39 148.64 l -118.39 149.83 l -119.00 148.94 119.74 148.50 120.62 148.50 c -121.18 148.50 121.62 148.67 121.94 149.02 c -122.27 149.37 122.44 149.84 122.44 150.43 c -122.44 155.00 l -121.28 155.00 l -121.28 150.80 l -121.28 150.33 121.21 150.00 121.07 149.79 c -120.93 149.59 120.71 149.49 120.38 149.49 c -119.68 149.49 119.01 149.96 118.39 150.88 c -118.39 155.00 l -h -126.59 155.15 m -126.00 155.15 125.55 154.98 125.22 154.64 c -124.89 154.31 124.73 153.84 124.73 153.24 c -124.73 149.50 l -123.93 149.50 l -123.93 148.64 l -124.73 148.64 l -124.73 147.48 l -125.88 147.37 l -125.88 148.64 l -127.54 148.64 l -127.54 149.50 l -125.88 149.50 l -125.88 153.03 l -125.88 153.86 126.24 154.28 126.96 154.28 c -127.11 154.28 127.30 154.25 127.52 154.20 c -127.52 155.00 l -127.16 155.10 126.85 155.15 126.59 155.15 c -h -129.31 153.05 m -129.31 152.18 l -136.25 152.18 l -136.25 153.05 l -h -129.31 150.88 m -129.31 150.01 l -136.25 150.01 l -136.25 150.88 l -h -140.39 155.15 m -139.87 155.15 139.23 155.02 138.47 154.78 c -138.47 153.72 l -139.23 154.09 139.88 154.28 140.44 154.28 c -140.77 154.28 141.05 154.19 141.27 154.01 c -141.49 153.83 141.60 153.61 141.60 153.34 c -141.60 152.94 141.29 152.62 140.68 152.36 c -140.00 152.07 l -139.01 151.66 138.51 151.06 138.51 150.28 c -138.51 149.73 138.70 149.29 139.10 148.97 c -139.49 148.66 140.03 148.50 140.71 148.50 c -141.07 148.50 141.51 148.54 142.03 148.64 c -142.27 148.69 l -142.27 149.65 l -141.62 149.46 141.11 149.36 140.73 149.36 c -139.99 149.36 139.62 149.63 139.62 150.17 c -139.62 150.52 139.90 150.81 140.46 151.05 c -141.02 151.29 l -141.65 151.55 142.10 151.83 142.36 152.13 c -142.62 152.42 142.75 152.79 142.75 153.23 c -142.75 153.79 142.53 154.25 142.09 154.61 c -141.65 154.97 141.08 155.15 140.39 155.15 c -h -149.49 154.79 m -148.71 155.03 148.05 155.15 147.50 155.15 c -146.56 155.15 145.80 154.83 145.21 154.21 c -144.62 153.59 144.32 152.78 144.32 151.79 c -144.32 150.82 144.58 150.03 145.10 149.42 c -145.62 148.80 146.29 148.49 147.10 148.49 c -147.87 148.49 148.47 148.76 148.89 149.31 c -149.31 149.86 149.52 150.63 149.52 151.64 c -149.51 152.00 l -145.50 152.00 l -145.67 153.51 146.41 154.27 147.72 154.27 c -148.20 154.27 148.79 154.14 149.49 153.88 c -h -145.55 151.13 m -148.36 151.13 l -148.36 149.95 147.92 149.36 147.03 149.36 c -146.15 149.36 145.65 149.95 145.55 151.13 c -h -151.51 155.00 m -151.51 148.64 l -152.66 148.64 l -152.66 149.83 l -153.12 148.94 153.79 148.50 154.66 148.50 c -154.77 148.50 154.90 148.51 155.03 148.53 c -155.03 149.60 l -154.83 149.54 154.65 149.50 154.50 149.50 c -153.77 149.50 153.16 149.94 152.66 150.80 c -152.66 155.00 l -h -157.74 155.00 m -155.37 148.64 l -156.53 148.64 l -158.38 153.59 l -160.33 148.64 l -161.41 148.64 l -158.89 155.00 l -h -162.63 155.00 m -162.63 148.64 l -163.79 148.64 l -163.79 155.00 l -h -162.63 147.48 m -162.63 146.33 l -163.79 146.33 l -163.79 147.48 l -h -168.58 155.15 m -167.72 155.15 167.01 154.83 166.45 154.19 c -165.88 153.55 165.60 152.75 165.60 151.78 c -165.60 150.75 165.88 149.94 166.44 149.36 c -167.00 148.79 167.78 148.50 168.78 148.50 c -169.28 148.50 169.83 148.56 170.45 148.70 c -170.45 149.67 l -169.79 149.48 169.26 149.38 168.85 149.38 c -168.26 149.38 167.79 149.60 167.43 150.05 c -167.08 150.49 166.90 151.08 166.90 151.82 c -166.90 152.53 167.08 153.11 167.45 153.55 c -167.81 153.99 168.29 154.21 168.89 154.21 c -169.42 154.21 169.96 154.08 170.52 153.81 c -170.52 154.81 l -169.77 155.03 169.13 155.15 168.58 155.15 c -h -176.91 154.79 m -176.14 155.03 175.47 155.15 174.92 155.15 c -173.99 155.15 173.22 154.83 172.63 154.21 c -172.04 153.59 171.74 152.78 171.74 151.79 c -171.74 150.82 172.00 150.03 172.52 149.42 c -173.05 148.80 173.71 148.49 174.53 148.49 c -175.29 148.49 175.89 148.76 176.31 149.31 c -176.73 149.86 176.94 150.63 176.94 151.64 c -176.93 152.00 l -172.92 152.00 l -173.09 153.51 173.83 154.27 175.14 154.27 c -175.62 154.27 176.21 154.14 176.91 153.88 c -h -172.97 151.13 m -175.78 151.13 l -175.78 149.95 175.34 149.36 174.46 149.36 c -173.57 149.36 173.07 149.95 172.97 151.13 c -h -182.61 155.22 m -181.26 155.22 180.22 154.82 179.49 154.03 c -178.75 153.24 178.39 152.12 178.39 150.67 c -178.39 149.22 178.76 148.10 179.51 147.31 c -180.26 146.51 181.31 146.11 182.67 146.11 c -183.45 146.11 184.36 146.24 185.40 146.49 c -185.40 147.65 l -184.21 147.24 183.30 147.03 182.65 147.03 c -181.71 147.03 180.98 147.35 180.47 147.99 c -179.95 148.62 179.69 149.52 179.69 150.68 c -179.69 151.79 179.97 152.66 180.52 153.30 c -181.07 153.94 181.82 154.26 182.78 154.26 c -183.60 154.26 184.47 154.00 185.41 153.50 c -185.41 154.55 l -184.56 155.00 183.62 155.22 182.61 155.22 c -h -190.52 154.19 m -189.82 154.83 189.16 155.15 188.52 155.15 c -187.99 155.15 187.55 154.98 187.21 154.65 c -186.86 154.32 186.68 153.90 186.68 153.40 c -186.68 152.71 186.98 152.17 187.56 151.80 c -188.14 151.42 188.98 151.24 190.07 151.24 c -190.35 151.24 l -190.35 150.47 l -190.35 149.73 189.97 149.36 189.21 149.36 c -188.60 149.36 187.94 149.55 187.23 149.93 c -187.23 148.97 l -188.01 148.65 188.74 148.50 189.42 148.50 c -190.13 148.50 190.66 148.66 190.99 148.98 c -191.33 149.30 191.50 149.79 191.50 150.47 c -191.50 153.35 l -191.50 154.01 191.70 154.34 192.11 154.34 c -192.16 154.34 192.23 154.34 192.33 154.32 c -192.41 154.96 l -192.15 155.08 191.86 155.15 191.55 155.15 c -191.01 155.15 190.66 154.83 190.52 154.19 c -h -190.35 153.56 m -190.35 151.92 l -189.96 151.91 l -189.33 151.91 188.81 152.03 188.42 152.27 c -188.03 152.51 187.84 152.82 187.84 153.21 c -187.84 153.49 187.94 153.72 188.13 153.92 c -188.33 154.11 188.56 154.20 188.85 154.20 c -189.33 154.20 189.83 153.99 190.35 153.56 c -h -195.77 155.15 m -195.19 155.15 194.73 154.98 194.40 154.64 c -194.07 154.31 193.91 153.84 193.91 153.24 c -193.91 149.50 l -193.11 149.50 l -193.11 148.64 l -193.91 148.64 l -193.91 147.48 l -195.06 147.37 l -195.06 148.64 l -196.73 148.64 l -196.73 149.50 l -195.06 149.50 l -195.06 153.03 l -195.06 153.86 195.42 154.28 196.14 154.28 c -196.29 154.28 196.48 154.25 196.70 154.20 c -196.70 155.00 l -196.34 155.10 196.03 155.15 195.77 155.15 c -h -201.63 154.19 m -200.94 154.83 200.27 155.15 199.63 155.15 c -199.11 155.15 198.67 154.98 198.32 154.65 c -197.97 154.32 197.80 153.90 197.80 153.40 c -197.80 152.71 198.09 152.17 198.67 151.80 c -199.26 151.42 200.10 151.24 201.19 151.24 c -201.46 151.24 l -201.46 150.47 l -201.46 149.73 201.08 149.36 200.32 149.36 c -199.71 149.36 199.06 149.55 198.35 149.93 c -198.35 148.97 l -199.13 148.65 199.86 148.50 200.54 148.50 c -201.25 148.50 201.77 148.66 202.11 148.98 c -202.45 149.30 202.62 149.79 202.62 150.47 c -202.62 153.35 l -202.62 154.01 202.82 154.34 203.22 154.34 c -203.28 154.34 203.35 154.34 203.45 154.32 c -203.53 154.96 l -203.27 155.08 202.98 155.15 202.66 155.15 c -202.12 155.15 201.78 154.83 201.63 154.19 c -h -201.46 153.56 m -201.46 151.92 l -201.07 151.91 l -200.44 151.91 199.93 152.03 199.54 152.27 c -199.15 152.51 198.95 152.82 198.95 153.21 c -198.95 153.49 199.05 153.72 199.25 153.92 c -199.44 154.11 199.68 154.20 199.96 154.20 c -200.44 154.20 200.94 153.99 201.46 153.56 c -h -204.98 155.00 m -204.98 145.75 l -206.13 145.75 l -206.13 155.00 l -h -210.94 155.15 m -210.03 155.15 209.30 154.84 208.76 154.24 c -208.21 153.64 207.94 152.83 207.94 151.82 c -207.94 150.79 208.21 149.99 208.76 149.39 c -209.30 148.79 210.04 148.50 210.98 148.50 c -211.91 148.50 212.65 148.79 213.19 149.39 c -213.74 149.99 214.01 150.79 214.01 151.81 c -214.01 152.85 213.74 153.66 213.19 154.26 c -212.64 154.85 211.89 155.15 210.94 155.15 c -h -210.95 154.28 m -212.18 154.28 212.79 153.46 212.79 151.81 c -212.79 150.18 212.18 149.36 210.98 149.36 c -209.77 149.36 209.17 150.18 209.17 151.82 c -209.17 153.46 209.77 154.28 210.95 154.28 c -h -215.84 157.12 m -215.97 156.11 l -216.64 156.43 217.30 156.59 217.95 156.59 c -219.25 156.59 219.90 155.90 219.90 154.52 c -219.90 153.52 l -219.47 154.41 218.78 154.85 217.80 154.85 c -217.04 154.85 216.44 154.58 215.99 154.02 c -215.54 153.47 215.31 152.72 215.31 151.78 c -215.31 150.81 215.57 150.02 216.08 149.41 c -216.59 148.80 217.25 148.50 218.07 148.50 c -218.78 148.50 219.39 148.79 219.90 149.36 c -219.90 148.64 l -221.06 148.64 l -221.06 153.27 l -221.06 154.26 221.01 155.00 220.91 155.48 c -220.80 155.96 220.61 156.35 220.32 156.65 c -219.82 157.19 219.04 157.46 217.97 157.46 c -217.23 157.46 216.52 157.34 215.84 157.12 c -h -219.90 152.80 m -219.90 150.15 l -219.39 149.63 218.84 149.36 218.24 149.36 c -217.71 149.36 217.29 149.58 216.99 150.00 c -216.69 150.43 216.54 151.01 216.54 151.75 c -216.54 153.15 217.03 153.85 218.01 153.85 c -218.68 153.85 219.31 153.50 219.90 152.80 c -h -223.30 156.73 m -223.30 145.75 l -225.61 145.75 l -225.61 146.62 l -224.31 146.62 l -224.31 155.87 l -225.61 155.87 l -225.61 156.73 l -h -226.98 148.93 m -226.70 145.75 l -228.14 145.75 l -227.85 148.93 l -h -232.43 155.15 m -231.57 155.15 230.86 154.83 230.29 154.19 c -229.73 153.55 229.45 152.75 229.45 151.78 c -229.45 150.75 229.73 149.94 230.29 149.36 c -230.85 148.79 231.63 148.50 232.63 148.50 c -233.13 148.50 233.68 148.56 234.30 148.70 c -234.30 149.67 l -233.64 149.48 233.11 149.38 232.70 149.38 c -232.11 149.38 231.64 149.60 231.28 150.05 c -230.92 150.49 230.75 151.08 230.75 151.82 c -230.75 152.53 230.93 153.11 231.30 153.55 c -231.66 153.99 232.14 154.21 232.74 154.21 c -233.27 154.21 233.81 154.08 234.37 153.81 c -234.37 154.81 l -233.62 155.03 232.98 155.15 232.43 155.15 c -h -238.59 155.15 m -237.68 155.15 236.95 154.84 236.41 154.24 c -235.86 153.64 235.59 152.83 235.59 151.82 c -235.59 150.79 235.86 149.99 236.41 149.39 c -236.95 148.79 237.69 148.50 238.63 148.50 c -239.56 148.50 240.30 148.79 240.84 149.39 c -241.39 149.99 241.66 150.79 241.66 151.81 c -241.66 152.85 241.39 153.66 240.84 154.26 c -240.29 154.85 239.54 155.15 238.59 155.15 c -h -238.60 154.28 m -239.83 154.28 240.44 153.46 240.44 151.81 c -240.44 150.18 239.83 149.36 238.63 149.36 c -237.42 149.36 236.82 150.18 236.82 151.82 c -236.82 153.46 237.42 154.28 238.60 154.28 c -h -243.47 155.00 m -243.47 148.64 l -244.62 148.64 l -244.62 149.83 l -245.18 148.94 245.91 148.50 246.79 148.50 c -247.64 148.50 248.22 148.94 248.53 149.83 c -249.08 148.94 249.79 148.49 250.66 148.49 c -251.22 148.49 251.66 148.66 251.97 148.99 c -252.28 149.32 252.43 149.78 252.43 150.37 c -252.43 155.00 l -251.27 155.00 l -251.27 150.55 l -251.27 149.83 250.98 149.46 250.41 149.46 c -249.81 149.46 249.19 149.89 248.53 150.73 c -248.53 155.00 l -247.37 155.00 l -247.37 150.55 l -247.37 149.82 247.08 149.46 246.49 149.46 c -245.91 149.46 245.29 149.88 244.62 150.73 c -244.62 155.00 l -h -254.67 157.31 m -254.67 148.64 l -255.82 148.64 l -255.82 149.83 l -256.30 148.94 257.01 148.50 257.95 148.50 c -258.72 148.50 259.32 148.78 259.76 149.33 c -260.20 149.89 260.42 150.66 260.42 151.62 c -260.42 152.68 260.17 153.53 259.67 154.18 c -259.17 154.82 258.52 155.15 257.71 155.15 c -256.95 155.15 256.32 154.86 255.82 154.28 c -255.82 157.31 l -h -255.82 153.48 m -256.42 154.01 256.98 154.28 257.52 154.28 c -258.63 154.28 259.19 153.43 259.19 151.74 c -259.19 150.25 258.70 149.50 257.71 149.50 c -257.07 149.50 256.44 149.85 255.82 150.55 c -h -266.20 155.00 m -266.20 153.80 l -265.59 154.70 264.84 155.15 263.97 155.15 c -263.41 155.15 262.97 154.97 262.64 154.62 c -262.32 154.27 262.15 153.80 262.15 153.21 c -262.15 148.64 l -263.31 148.64 l -263.31 152.83 l -263.31 153.31 263.38 153.65 263.51 153.85 c -263.65 154.05 263.88 154.15 264.21 154.15 c -264.91 154.15 265.58 153.69 266.20 152.76 c -266.20 148.64 l -267.36 148.64 l -267.36 155.00 l -h -271.58 155.15 m -270.99 155.15 270.54 154.98 270.21 154.64 c -269.88 154.31 269.72 153.84 269.72 153.24 c -269.72 149.50 l -268.92 149.50 l -268.92 148.64 l -269.72 148.64 l -269.72 147.48 l -270.87 147.37 l -270.87 148.64 l -272.54 148.64 l -272.54 149.50 l -270.87 149.50 l -270.87 153.03 l -270.87 153.86 271.23 154.28 271.95 154.28 c -272.10 154.28 272.29 154.25 272.51 154.20 c -272.51 155.00 l -272.15 155.10 271.84 155.15 271.58 155.15 c -h -278.82 154.79 m -278.05 155.03 277.39 155.15 276.84 155.15 c -275.90 155.15 275.13 154.83 274.54 154.21 c -273.95 153.59 273.65 152.78 273.65 151.79 c -273.65 150.82 273.92 150.03 274.44 149.42 c -274.96 148.80 275.62 148.49 276.44 148.49 c -277.21 148.49 277.80 148.76 278.22 149.31 c -278.64 149.86 278.85 150.63 278.85 151.64 c -278.85 152.00 l -274.83 152.00 l -275.00 153.51 275.74 154.27 277.05 154.27 c -277.53 154.27 278.12 154.14 278.82 153.88 c -h -274.88 151.13 m -277.69 151.13 l -277.69 149.95 277.25 149.36 276.37 149.36 c -275.48 149.36 274.99 149.95 274.88 151.13 c -h -280.63 148.93 m -280.34 145.75 l -281.79 145.75 l -281.49 148.93 l -h -285.19 156.73 m -285.19 145.75 l -282.87 145.75 l -282.87 146.62 l -284.17 146.62 l -284.17 155.87 l -282.87 155.87 l -282.87 156.73 l -h -f -79.639 203.15 m -78.779 203.15 78.066 202.83 77.500 202.19 c -76.934 201.55 76.650 200.75 76.650 199.78 c -76.650 198.75 76.931 197.94 77.491 197.36 c -78.052 196.79 78.834 196.50 79.838 196.50 c -80.334 196.50 80.889 196.56 81.502 196.70 c -81.502 197.67 l -80.850 197.48 80.318 197.38 79.908 197.38 c -79.318 197.38 78.845 197.60 78.487 198.05 c -78.130 198.49 77.951 199.08 77.951 199.82 c -77.951 200.53 78.135 201.11 78.502 201.55 c -78.869 201.99 79.350 202.21 79.943 202.21 c -80.471 202.21 81.014 202.08 81.572 201.81 c -81.572 202.81 l -80.826 203.03 80.182 203.15 79.639 203.15 c -h -83.301 203.00 m -83.301 196.64 l -84.455 196.64 l -84.455 197.83 l -84.912 196.94 85.576 196.50 86.447 196.50 c -86.564 196.50 86.688 196.51 86.816 196.53 c -86.816 197.60 l -86.617 197.54 86.441 197.50 86.289 197.50 c -85.559 197.50 84.947 197.94 84.455 198.80 c -84.455 203.00 l -h -92.875 202.79 m -92.102 203.03 91.439 203.15 90.889 203.15 c -89.951 203.15 89.187 202.83 88.595 202.21 c -88.003 201.59 87.707 200.78 87.707 199.79 c -87.707 198.82 87.968 198.03 88.489 197.42 c -89.011 196.80 89.678 196.49 90.490 196.49 c -91.260 196.49 91.854 196.76 92.274 197.31 c -92.694 197.86 92.904 198.63 92.904 199.64 c -92.898 200.00 l -88.885 200.00 l -89.053 201.51 89.793 202.27 91.105 202.27 c -91.586 202.27 92.176 202.14 92.875 201.88 c -h -88.938 199.13 m -91.744 199.13 l -91.744 197.95 91.303 197.36 90.420 197.36 c -89.533 197.36 89.039 197.95 88.938 199.13 c -h -98.178 202.19 m -97.486 202.83 96.820 203.15 96.180 203.15 c -95.652 203.15 95.215 202.98 94.867 202.65 c -94.520 202.32 94.346 201.90 94.346 201.40 c -94.346 200.71 94.638 200.17 95.222 199.80 c -95.806 199.42 96.643 199.24 97.732 199.24 c -98.008 199.24 l -98.008 198.47 l -98.008 197.73 97.629 197.36 96.871 197.36 c -96.262 197.36 95.604 197.55 94.896 197.93 c -94.896 196.97 l -95.674 196.65 96.402 196.50 97.082 196.50 c -97.793 196.50 98.317 196.66 98.655 196.98 c -98.993 197.30 99.162 197.79 99.162 198.47 c -99.162 201.35 l -99.162 202.01 99.365 202.34 99.771 202.34 c -99.822 202.34 99.896 202.34 99.994 202.32 c -100.08 202.96 l -99.814 203.08 99.525 203.15 99.209 203.15 c -98.670 203.15 98.326 202.83 98.178 202.19 c -h -98.008 201.56 m -98.008 199.92 l -97.621 199.91 l -96.988 199.91 96.477 200.03 96.086 200.27 c -95.695 200.51 95.500 200.82 95.500 201.21 c -95.500 201.49 95.598 201.72 95.793 201.92 c -95.988 202.11 96.227 202.20 96.508 202.20 c -96.988 202.20 97.488 201.99 98.008 201.56 c -h -103.43 203.15 m -102.85 203.15 102.39 202.98 102.06 202.64 c -101.73 202.31 101.57 201.84 101.57 201.24 c -101.57 197.50 l -100.77 197.50 l -100.77 196.64 l -101.57 196.64 l -101.57 195.48 l -102.72 195.37 l -102.72 196.64 l -104.39 196.64 l -104.39 197.50 l -102.72 197.50 l -102.72 201.03 l -102.72 201.86 103.08 202.28 103.80 202.28 c -103.96 202.28 104.14 202.25 104.36 202.20 c -104.36 203.00 l -104.00 203.10 103.70 203.15 103.43 203.15 c -h -110.68 202.79 m -109.90 203.03 109.24 203.15 108.69 203.15 c -107.75 203.15 106.99 202.83 106.40 202.21 c -105.80 201.59 105.51 200.78 105.51 199.79 c -105.51 198.82 105.77 198.03 106.29 197.42 c -106.81 196.80 107.48 196.49 108.29 196.49 c -109.06 196.49 109.66 196.76 110.08 197.31 c -110.50 197.86 110.71 198.63 110.71 199.64 c -110.70 200.00 l -106.69 200.00 l -106.85 201.51 107.59 202.27 108.91 202.27 c -109.39 202.27 109.98 202.14 110.68 201.88 c -h -106.74 199.13 m -109.54 199.13 l -109.54 197.95 109.10 197.36 108.22 197.36 c -107.33 197.36 106.84 197.95 106.74 199.13 c -h -112.66 203.00 m -112.66 194.33 l -113.89 194.33 l -113.89 203.00 l -h -116.15 203.00 m -116.15 196.64 l -117.31 196.64 l -117.31 197.83 l -117.92 196.94 118.66 196.50 119.55 196.50 c -120.10 196.50 120.54 196.67 120.87 197.02 c -121.19 197.37 121.36 197.84 121.36 198.43 c -121.36 203.00 l -120.20 203.00 l -120.20 198.80 l -120.20 198.33 120.13 198.00 120.00 197.79 c -119.86 197.59 119.63 197.49 119.31 197.49 c -118.60 197.49 117.93 197.96 117.31 198.88 c -117.31 203.00 l -h -125.29 203.15 m -124.76 203.15 124.12 203.02 123.37 202.78 c -123.37 201.72 l -124.12 202.09 124.78 202.28 125.34 202.28 c -125.67 202.28 125.94 202.19 126.16 202.01 c -126.38 201.83 126.49 201.61 126.49 201.34 c -126.49 200.94 126.18 200.62 125.57 200.36 c -124.90 200.07 l -123.90 199.66 123.40 199.06 123.40 198.28 c -123.40 197.73 123.60 197.29 123.99 196.97 c -124.38 196.66 124.92 196.50 125.61 196.50 c -125.96 196.50 126.40 196.54 126.92 196.64 c -127.16 196.69 l -127.16 197.65 l -126.52 197.46 126.01 197.36 125.63 197.36 c -124.89 197.36 124.52 197.63 124.52 198.17 c -124.52 198.52 124.80 198.81 125.36 199.05 c -125.92 199.29 l -126.54 199.55 126.99 199.83 127.25 200.13 c -127.51 200.42 127.64 200.79 127.64 201.23 c -127.64 201.79 127.42 202.25 126.98 202.61 c -126.54 202.97 125.98 203.15 125.29 203.15 c -h -131.63 203.15 m -131.04 203.15 130.59 202.98 130.26 202.64 c -129.93 202.31 129.77 201.84 129.77 201.24 c -129.77 197.50 l -128.97 197.50 l -128.97 196.64 l -129.77 196.64 l -129.77 195.48 l -130.92 195.37 l -130.92 196.64 l -132.58 196.64 l -132.58 197.50 l -130.92 197.50 l -130.92 201.03 l -130.92 201.86 131.28 202.28 132.00 202.28 c -132.15 202.28 132.34 202.25 132.55 202.20 c -132.55 203.00 l -132.20 203.10 131.89 203.15 131.63 203.15 c -h -137.49 202.19 m -136.80 202.83 136.13 203.15 135.49 203.15 c -134.96 203.15 134.53 202.98 134.18 202.65 c -133.83 202.32 133.66 201.90 133.66 201.40 c -133.66 200.71 133.95 200.17 134.53 199.80 c -135.12 199.42 135.95 199.24 137.04 199.24 c -137.32 199.24 l -137.32 198.47 l -137.32 197.73 136.94 197.36 136.18 197.36 c -135.57 197.36 134.91 197.55 134.21 197.93 c -134.21 196.97 l -134.98 196.65 135.71 196.50 136.39 196.50 c -137.10 196.50 137.63 196.66 137.97 196.98 c -138.30 197.30 138.47 197.79 138.47 198.47 c -138.47 201.35 l -138.47 202.01 138.68 202.34 139.08 202.34 c -139.13 202.34 139.21 202.34 139.30 202.32 c -139.39 202.96 l -139.12 203.08 138.84 203.15 138.52 203.15 c -137.98 203.15 137.64 202.83 137.49 202.19 c -h -137.32 201.56 m -137.32 199.92 l -136.93 199.91 l -136.30 199.91 135.79 200.03 135.40 200.27 c -135.01 200.51 134.81 200.82 134.81 201.21 c -134.81 201.49 134.91 201.72 135.10 201.92 c -135.30 202.11 135.54 202.20 135.82 202.20 c -136.30 202.20 136.80 201.99 137.32 201.56 c -h -140.83 203.00 m -140.83 196.64 l -141.99 196.64 l -141.99 197.83 l -142.60 196.94 143.34 196.50 144.23 196.50 c -144.78 196.50 145.22 196.67 145.54 197.02 c -145.87 197.37 146.04 197.84 146.04 198.43 c -146.04 203.00 l -144.88 203.00 l -144.88 198.80 l -144.88 198.33 144.81 198.00 144.67 197.79 c -144.54 197.59 144.31 197.49 143.99 197.49 c -143.28 197.49 142.61 197.96 141.99 198.88 c -141.99 203.00 l -h -150.77 203.15 m -149.91 203.15 149.19 202.83 148.63 202.19 c -148.06 201.55 147.78 200.75 147.78 199.78 c -147.78 198.75 148.06 197.94 148.62 197.36 c -149.18 196.79 149.96 196.50 150.96 196.50 c -151.46 196.50 152.02 196.56 152.63 196.70 c -152.63 197.67 l -151.98 197.48 151.45 197.38 151.04 197.38 c -150.45 197.38 149.97 197.60 149.61 198.05 c -149.26 198.49 149.08 199.08 149.08 199.82 c -149.08 200.53 149.26 201.11 149.63 201.55 c -150.00 201.99 150.48 202.21 151.07 202.21 c -151.60 202.21 152.14 202.08 152.70 201.81 c -152.70 202.81 l -151.95 203.03 151.31 203.15 150.77 203.15 c -h -159.09 202.79 m -158.32 203.03 157.66 203.15 157.11 203.15 c -156.17 203.15 155.40 202.83 154.81 202.21 c -154.22 201.59 153.92 200.78 153.92 199.79 c -153.92 198.82 154.18 198.03 154.71 197.42 c -155.23 196.80 155.89 196.49 156.71 196.49 c -157.48 196.49 158.07 196.76 158.49 197.31 c -158.91 197.86 159.12 198.63 159.12 199.64 c -159.12 200.00 l -155.10 200.00 l -155.27 201.51 156.01 202.27 157.32 202.27 c -157.80 202.27 158.39 202.14 159.09 201.88 c -h -155.15 199.13 m -157.96 199.13 l -157.96 197.95 157.52 197.36 156.64 197.36 c -155.75 197.36 155.26 197.95 155.15 199.13 c -h -168.20 203.00 m -161.26 199.53 l -168.20 196.06 l -168.20 197.03 l -163.20 199.53 l -168.20 202.03 l -h -172.56 203.15 m -171.98 203.15 171.52 202.98 171.19 202.64 c -170.86 202.31 170.70 201.84 170.70 201.24 c -170.70 197.50 l -169.90 197.50 l -169.90 196.64 l -170.70 196.64 l -170.70 195.48 l -171.85 195.37 l -171.85 196.64 l -173.52 196.64 l -173.52 197.50 l -171.85 197.50 l -171.85 201.03 l -171.85 201.86 172.21 202.28 172.93 202.28 c -173.08 202.28 173.27 202.25 173.49 202.20 c -173.49 203.00 l -173.13 203.10 172.82 203.15 172.56 203.15 c -h -177.63 203.15 m -176.72 203.15 175.99 202.84 175.45 202.24 c -174.91 201.64 174.64 200.83 174.64 199.82 c -174.64 198.79 174.91 197.99 175.45 197.39 c -176.00 196.79 176.74 196.50 177.67 196.50 c -178.61 196.50 179.34 196.79 179.89 197.39 c -180.43 197.99 180.71 198.79 180.71 199.81 c -180.71 200.85 180.43 201.66 179.89 202.26 c -179.34 202.85 178.59 203.15 177.63 203.15 c -h -177.65 202.28 m -178.87 202.28 179.48 201.46 179.48 199.81 c -179.48 198.18 178.88 197.36 177.67 197.36 c -176.47 197.36 175.87 198.18 175.87 199.82 c -175.87 201.46 176.46 202.28 177.65 202.28 c -h -182.51 203.00 m -182.51 193.75 l -183.67 193.75 l -183.67 199.72 l -186.36 196.64 l -187.60 196.64 l -185.03 199.61 l -188.14 203.00 l -186.66 203.00 l -183.67 199.74 l -183.67 203.00 l -h -194.19 202.79 m -193.42 203.03 192.75 203.15 192.20 203.15 c -191.27 203.15 190.50 202.83 189.91 202.21 c -189.32 201.59 189.02 200.78 189.02 199.79 c -189.02 198.82 189.28 198.03 189.80 197.42 c -190.33 196.80 190.99 196.49 191.80 196.49 c -192.57 196.49 193.17 196.76 193.59 197.31 c -194.01 197.86 194.22 198.63 194.22 199.64 c -194.21 200.00 l -190.20 200.00 l -190.37 201.51 191.11 202.27 192.42 202.27 c -192.90 202.27 193.49 202.14 194.19 201.88 c -h -190.25 199.13 m -193.06 199.13 l -193.06 197.95 192.62 197.36 191.73 197.36 c -190.85 197.36 190.35 197.95 190.25 199.13 c -h -196.21 203.00 m -196.21 196.64 l -197.37 196.64 l -197.37 197.83 l -197.97 196.94 198.72 196.50 199.60 196.50 c -200.15 196.50 200.59 196.67 200.92 197.02 c -201.25 197.37 201.41 197.84 201.41 198.43 c -201.41 203.00 l -200.26 203.00 l -200.26 198.80 l -200.26 198.33 200.19 198.00 200.05 197.79 c -199.91 197.59 199.68 197.49 199.36 197.49 c -198.66 197.49 197.99 197.96 197.37 198.88 c -197.37 203.00 l -h -203.68 204.88 m -203.68 204.45 l -204.05 204.34 204.24 203.90 204.24 203.12 c -204.24 203.00 l -203.68 203.00 l -203.68 201.55 l -205.12 201.55 l -205.12 202.81 l -205.12 204.09 204.64 204.78 203.68 204.88 c -h -213.16 203.15 m -212.58 203.15 212.12 202.98 211.79 202.64 c -211.46 202.31 211.30 201.84 211.30 201.24 c -211.30 197.50 l -210.50 197.50 l -210.50 196.64 l -211.30 196.64 l -211.30 195.48 l -212.45 195.37 l -212.45 196.64 l -214.12 196.64 l -214.12 197.50 l -212.45 197.50 l -212.45 201.03 l -212.45 201.86 212.81 202.28 213.53 202.28 c -213.68 202.28 213.87 202.25 214.09 202.20 c -214.09 203.00 l -213.73 203.10 213.42 203.15 213.16 203.15 c -h -220.40 202.79 m -219.63 203.03 218.97 203.15 218.42 203.15 c -217.48 203.15 216.72 202.83 216.12 202.21 c -215.53 201.59 215.24 200.78 215.24 199.79 c -215.24 198.82 215.50 198.03 216.02 197.42 c -216.54 196.80 217.21 196.49 218.02 196.49 c -218.79 196.49 219.38 196.76 219.80 197.31 c -220.22 197.86 220.43 198.63 220.43 199.64 c -220.43 200.00 l -216.41 200.00 l -216.58 201.51 217.32 202.27 218.63 202.27 c -219.12 202.27 219.71 202.14 220.40 201.88 c -h -216.47 199.13 m -219.27 199.13 l -219.27 197.95 218.83 197.36 217.95 197.36 c -217.06 197.36 216.57 197.95 216.47 199.13 c -h -222.43 203.00 m -222.43 196.64 l -223.58 196.64 l -223.58 197.83 l -224.19 196.94 224.94 196.50 225.82 196.50 c -226.37 196.50 226.81 196.67 227.14 197.02 c -227.46 197.37 227.63 197.84 227.63 198.43 c -227.63 203.00 l -226.47 203.00 l -226.47 198.80 l -226.47 198.33 226.41 198.00 226.27 197.79 c -226.13 197.59 225.90 197.49 225.58 197.49 c -224.87 197.49 224.21 197.96 223.58 198.88 c -223.58 203.00 l -h -233.15 202.19 m -232.46 202.83 231.80 203.15 231.16 203.15 c -230.63 203.15 230.19 202.98 229.84 202.65 c -229.50 202.32 229.32 201.90 229.32 201.40 c -229.32 200.71 229.61 200.17 230.20 199.80 c -230.78 199.42 231.62 199.24 232.71 199.24 c -232.98 199.24 l -232.98 198.47 l -232.98 197.73 232.61 197.36 231.85 197.36 c -231.24 197.36 230.58 197.55 229.87 197.93 c -229.87 196.97 l -230.65 196.65 231.38 196.50 232.06 196.50 c -232.77 196.50 233.29 196.66 233.63 196.98 c -233.97 197.30 234.14 197.79 234.14 198.47 c -234.14 201.35 l -234.14 202.01 234.34 202.34 234.75 202.34 c -234.80 202.34 234.87 202.34 234.97 202.32 c -235.05 202.96 l -234.79 203.08 234.50 203.15 234.19 203.15 c -233.65 203.15 233.30 202.83 233.15 202.19 c -h -232.98 201.56 m -232.98 199.92 l -232.60 199.91 l -231.96 199.91 231.45 200.03 231.06 200.27 c -230.67 200.51 230.48 200.82 230.48 201.21 c -230.48 201.49 230.57 201.72 230.77 201.92 c -230.96 202.11 231.20 202.20 231.48 202.20 c -231.96 202.20 232.46 201.99 232.98 201.56 c -h -236.50 203.00 m -236.50 196.64 l -237.65 196.64 l -237.65 197.83 l -238.26 196.94 239.01 196.50 239.89 196.50 c -240.44 196.50 240.88 196.67 241.21 197.02 c -241.54 197.37 241.70 197.84 241.70 198.43 c -241.70 203.00 l -240.55 203.00 l -240.55 198.80 l -240.55 198.33 240.48 198.00 240.34 197.79 c -240.20 197.59 239.97 197.49 239.65 197.49 c -238.95 197.49 238.28 197.96 237.65 198.88 c -237.65 203.00 l -h -245.86 203.15 m -245.27 203.15 244.81 202.98 244.49 202.64 c -244.16 202.31 243.99 201.84 243.99 201.24 c -243.99 197.50 l -243.20 197.50 l -243.20 196.64 l -243.99 196.64 l -243.99 195.48 l -245.15 195.37 l -245.15 196.64 l -246.81 196.64 l -246.81 197.50 l -245.15 197.50 l -245.15 201.03 l -245.15 201.86 245.51 202.28 246.23 202.28 c -246.38 202.28 246.56 202.25 246.78 202.20 c -246.78 203.00 l -246.43 203.10 246.12 203.15 245.86 203.15 c -h -247.28 205.02 m -247.28 204.15 l -253.28 204.15 l -253.28 205.02 l -h -254.44 203.00 m -254.44 196.64 l -255.59 196.64 l -255.59 203.00 l -h -254.44 195.48 m -254.44 194.33 l -255.59 194.33 l -255.59 195.48 l -h -261.99 203.00 m -261.99 201.80 l -261.52 202.70 260.81 203.15 259.87 203.15 c -259.10 203.15 258.50 202.87 258.06 202.31 c -257.62 201.75 257.40 200.99 257.40 200.02 c -257.40 198.96 257.65 198.11 258.15 197.46 c -258.65 196.82 259.30 196.50 260.11 196.50 c -260.87 196.50 261.49 196.79 261.99 197.36 c -261.99 193.75 l -263.15 193.75 l -263.15 203.00 l -h -261.99 198.15 m -261.39 197.63 260.82 197.36 260.29 197.36 c -259.18 197.36 258.63 198.21 258.63 199.90 c -258.63 201.39 259.12 202.13 260.11 202.13 c -260.75 202.13 261.38 201.78 261.99 201.08 c -h -265.60 203.00 m -272.54 199.53 l -265.60 196.06 l -265.60 197.03 l -270.60 199.53 l -265.60 202.03 l -h -f -[] 0.0000 d -70.500 206.50 m -623.50 206.50 l -S -623.00 206.00 m -617.00 200.00 l -617.00 212.00 l -h -f* -80.436 98.191 m -79.744 98.828 79.078 99.146 78.438 99.146 c -77.910 99.146 77.473 98.981 77.125 98.651 c -76.777 98.321 76.604 97.904 76.604 97.400 c -76.604 96.705 76.896 96.171 77.479 95.798 c -78.063 95.425 78.900 95.238 79.990 95.238 c -80.266 95.238 l -80.266 94.471 l -80.266 93.732 79.887 93.363 79.129 93.363 c -78.520 93.363 77.861 93.551 77.154 93.926 c -77.154 92.971 l -77.932 92.654 78.660 92.496 79.340 92.496 c -80.051 92.496 80.575 92.656 80.913 92.977 c -81.251 93.297 81.420 93.795 81.420 94.471 c -81.420 97.354 l -81.420 98.014 81.623 98.344 82.029 98.344 c -82.080 98.344 82.154 98.336 82.252 98.320 c -82.334 98.959 l -82.072 99.084 81.783 99.146 81.467 99.146 c -80.928 99.146 80.584 98.828 80.436 98.191 c -h -80.266 97.564 m -80.266 95.918 l -79.879 95.906 l -79.246 95.906 78.734 96.026 78.344 96.267 c -77.953 96.507 77.758 96.822 77.758 97.213 c -77.758 97.490 77.855 97.725 78.051 97.916 c -78.246 98.107 78.484 98.203 78.766 98.203 c -79.246 98.203 79.746 97.990 80.266 97.564 c -h -87.760 99.000 m -87.760 97.805 l -87.146 98.699 86.402 99.146 85.527 99.146 c -84.973 99.146 84.531 98.972 84.203 98.622 c -83.875 98.272 83.711 97.801 83.711 97.207 c -83.711 92.637 l -84.865 92.637 l -84.865 96.832 l -84.865 97.309 84.935 97.647 85.073 97.849 c -85.212 98.050 85.443 98.150 85.768 98.150 c -86.471 98.150 87.135 97.688 87.760 96.762 c -87.760 92.637 l -88.914 92.637 l -88.914 99.000 l -h -93.139 99.146 m -92.553 99.146 92.096 98.979 91.768 98.643 c -91.439 98.307 91.275 97.840 91.275 97.242 c -91.275 93.504 l -90.479 93.504 l -90.479 92.637 l -91.275 92.637 l -91.275 91.482 l -92.430 91.371 l -92.430 92.637 l -94.094 92.637 l -94.094 93.504 l -92.430 93.504 l -92.430 97.031 l -92.430 97.863 92.789 98.279 93.508 98.279 c -93.660 98.279 93.846 98.254 94.064 98.203 c -94.064 99.000 l -93.709 99.098 93.400 99.146 93.139 99.146 c -h -95.717 99.000 m -95.717 89.748 l -96.871 89.748 l -96.871 93.832 l -97.480 92.941 98.227 92.496 99.109 92.496 c -99.660 92.496 100.10 92.671 100.43 93.021 c -100.76 93.370 100.92 93.840 100.92 94.430 c -100.92 99.000 l -99.766 99.000 l -99.766 94.805 l -99.766 94.332 99.696 93.995 99.558 93.794 c -99.419 93.593 99.189 93.492 98.869 93.492 c -98.162 93.492 97.496 93.955 96.871 94.881 c -96.871 99.000 l -h -110.25 99.000 m -103.31 95.531 l -110.25 92.062 l -110.25 93.029 l -105.25 95.531 l -110.25 98.027 l -h -116.68 99.000 m -116.68 97.805 l -116.07 98.699 115.32 99.146 114.45 99.146 c -113.89 99.146 113.45 98.972 113.12 98.622 c -112.80 98.272 112.63 97.801 112.63 97.207 c -112.63 92.637 l -113.79 92.637 l -113.79 96.832 l -113.79 97.309 113.86 97.647 114.00 97.849 c -114.13 98.050 114.37 98.150 114.69 98.150 c -115.39 98.150 116.06 97.688 116.68 96.762 c -116.68 92.637 l -117.84 92.637 l -117.84 99.000 l -h -121.84 99.146 m -121.31 99.146 120.67 99.023 119.92 98.777 c -119.92 97.717 l -120.67 98.092 121.33 98.279 121.88 98.279 c -122.22 98.279 122.49 98.189 122.71 98.010 c -122.93 97.830 123.04 97.605 123.04 97.336 c -123.04 96.941 122.73 96.615 122.12 96.357 c -121.45 96.070 l -120.45 95.656 119.95 95.061 119.95 94.283 c -119.95 93.729 120.15 93.292 120.54 92.974 c -120.93 92.655 121.47 92.496 122.15 92.496 c -122.51 92.496 122.95 92.545 123.47 92.643 c -123.71 92.689 l -123.71 93.650 l -123.07 93.459 122.56 93.363 122.18 93.363 c -121.44 93.363 121.06 93.633 121.06 94.172 c -121.06 94.520 121.35 94.812 121.91 95.051 c -122.46 95.285 l -123.09 95.551 123.54 95.831 123.80 96.126 c -124.06 96.421 124.19 96.789 124.19 97.230 c -124.19 97.789 123.97 98.248 123.53 98.607 c -123.09 98.967 122.53 99.146 121.84 99.146 c -h -130.93 98.795 m -130.16 99.029 129.50 99.146 128.95 99.146 c -128.01 99.146 127.24 98.835 126.65 98.212 c -126.06 97.589 125.76 96.781 125.76 95.789 c -125.76 94.824 126.02 94.033 126.55 93.416 c -127.07 92.799 127.73 92.490 128.55 92.490 c -129.32 92.490 129.91 92.764 130.33 93.311 c -130.75 93.857 130.96 94.635 130.96 95.643 c -130.96 96.000 l -126.94 96.000 l -127.11 97.512 127.85 98.268 129.16 98.268 c -129.64 98.268 130.23 98.139 130.93 97.881 c -h -126.99 95.133 m -129.80 95.133 l -129.80 93.949 129.36 93.357 128.48 93.357 c -127.59 93.357 127.10 93.949 126.99 95.133 c -h -132.95 99.000 m -132.95 92.637 l -134.11 92.637 l -134.11 93.832 l -134.56 92.941 135.23 92.496 136.10 92.496 c -136.22 92.496 136.34 92.506 136.47 92.525 c -136.47 93.604 l -136.27 93.537 136.09 93.504 135.94 93.504 c -135.21 93.504 134.60 93.938 134.11 94.805 c -134.11 99.000 l -h -137.88 100.88 m -137.88 100.45 l -138.26 100.34 138.44 99.898 138.44 99.117 c -138.44 99.000 l -137.88 99.000 l -137.88 97.553 l -139.33 97.553 l -139.33 98.807 l -139.33 100.09 138.85 100.78 137.88 100.88 c -h -145.46 101.31 m -145.46 92.637 l -146.61 92.637 l -146.61 93.832 l -147.08 92.941 147.79 92.496 148.74 92.496 c -149.50 92.496 150.11 92.775 150.55 93.334 c -150.99 93.893 151.21 94.656 151.21 95.625 c -151.21 96.680 150.96 97.530 150.46 98.177 c -149.96 98.823 149.30 99.146 148.49 99.146 c -147.74 99.146 147.11 98.857 146.61 98.279 c -146.61 101.31 l -h -146.61 97.482 m -147.21 98.014 147.77 98.279 148.31 98.279 c -149.42 98.279 149.97 97.434 149.97 95.742 c -149.97 94.250 149.48 93.504 148.50 93.504 c -147.85 93.504 147.22 93.854 146.61 94.553 c -h -156.29 98.191 m -155.60 98.828 154.93 99.146 154.29 99.146 c -153.77 99.146 153.33 98.981 152.98 98.651 c -152.63 98.321 152.46 97.904 152.46 97.400 c -152.46 96.705 152.75 96.171 153.33 95.798 c -153.92 95.425 154.76 95.238 155.85 95.238 c -156.12 95.238 l -156.12 94.471 l -156.12 93.732 155.74 93.363 154.98 93.363 c -154.38 93.363 153.72 93.551 153.01 93.926 c -153.01 92.971 l -153.79 92.654 154.52 92.496 155.20 92.496 c -155.91 92.496 156.43 92.656 156.77 92.977 c -157.11 93.297 157.28 93.795 157.28 94.471 c -157.28 97.354 l -157.28 98.014 157.48 98.344 157.88 98.344 c -157.94 98.344 158.01 98.336 158.11 98.320 c -158.19 98.959 l -157.93 99.084 157.64 99.146 157.32 99.146 c -156.78 99.146 156.44 98.828 156.29 98.191 c -h -156.12 97.564 m -156.12 95.918 l -155.73 95.906 l -155.10 95.906 154.59 96.026 154.20 96.267 c -153.81 96.507 153.61 96.822 153.61 97.213 c -153.61 97.490 153.71 97.725 153.91 97.916 c -154.10 98.107 154.34 98.203 154.62 98.203 c -155.10 98.203 155.60 97.990 156.12 97.564 c -h -161.32 99.146 m -160.80 99.146 160.16 99.023 159.40 98.777 c -159.40 97.717 l -160.16 98.092 160.81 98.279 161.37 98.279 c -161.70 98.279 161.98 98.189 162.20 98.010 c -162.42 97.830 162.53 97.605 162.53 97.336 c -162.53 96.941 162.22 96.615 161.61 96.357 c -160.93 96.070 l -159.94 95.656 159.44 95.061 159.44 94.283 c -159.44 93.729 159.63 93.292 160.03 92.974 c -160.42 92.655 160.96 92.496 161.64 92.496 c -162.00 92.496 162.44 92.545 162.96 92.643 c -163.20 92.689 l -163.20 93.650 l -162.55 93.459 162.04 93.363 161.66 93.363 c -160.92 93.363 160.55 93.633 160.55 94.172 c -160.55 94.520 160.83 94.812 161.39 95.051 c -161.95 95.285 l -162.58 95.551 163.03 95.831 163.29 96.126 c -163.55 96.421 163.68 96.789 163.68 97.230 c -163.68 97.789 163.46 98.248 163.02 98.607 c -162.58 98.967 162.01 99.146 161.32 99.146 c -h -167.44 99.146 m -166.91 99.146 166.27 99.023 165.52 98.777 c -165.52 97.717 l -166.27 98.092 166.93 98.279 167.49 98.279 c -167.82 98.279 168.10 98.189 168.31 98.010 c -168.53 97.830 168.64 97.605 168.64 97.336 c -168.64 96.941 168.34 96.615 167.72 96.357 c -167.05 96.070 l -166.05 95.656 165.55 95.061 165.55 94.283 c -165.55 93.729 165.75 93.292 166.14 92.974 c -166.54 92.655 167.07 92.496 167.76 92.496 c -168.11 92.496 168.55 92.545 169.08 92.643 c -169.32 92.689 l -169.32 93.650 l -168.67 93.459 168.16 93.363 167.78 93.363 c -167.04 93.363 166.67 93.633 166.67 94.172 c -166.67 94.520 166.95 94.812 167.51 95.051 c -168.07 95.285 l -168.70 95.551 169.14 95.831 169.40 96.126 c -169.67 96.421 169.80 96.789 169.80 97.230 c -169.80 97.789 169.58 98.248 169.13 98.607 c -168.69 98.967 168.13 99.146 167.44 99.146 c -h -172.61 99.000 m -170.79 92.637 l -171.92 92.637 l -173.31 97.564 l -174.82 92.637 l -175.97 92.637 l -177.29 97.564 l -178.89 92.637 l -179.88 92.637 l -177.81 99.000 l -176.65 99.000 l -175.30 94.072 l -173.78 99.000 l -h -183.61 99.146 m -182.70 99.146 181.97 98.845 181.43 98.241 c -180.88 97.638 180.61 96.830 180.61 95.818 c -180.61 94.795 180.89 93.985 181.43 93.390 c -181.98 92.794 182.71 92.496 183.65 92.496 c -184.58 92.496 185.32 92.794 185.87 93.390 c -186.41 93.985 186.68 94.791 186.68 95.807 c -186.68 96.846 186.41 97.662 185.86 98.256 c -185.32 98.850 184.56 99.146 183.61 99.146 c -h -183.62 98.279 m -184.85 98.279 185.46 97.455 185.46 95.807 c -185.46 94.178 184.86 93.363 183.65 93.363 c -182.45 93.363 181.84 94.182 181.84 95.818 c -181.84 97.459 182.44 98.279 183.62 98.279 c -h -188.49 99.000 m -188.49 92.637 l -189.64 92.637 l -189.64 93.832 l -190.10 92.941 190.76 92.496 191.63 92.496 c -191.75 92.496 191.88 92.506 192.00 92.525 c -192.00 93.604 l -191.80 93.537 191.63 93.504 191.48 93.504 c -190.75 93.504 190.13 93.938 189.64 94.805 c -189.64 99.000 l -h -197.48 99.000 m -197.48 97.805 l -197.01 98.699 196.31 99.146 195.36 99.146 c -194.60 99.146 193.99 98.867 193.55 98.309 c -193.11 97.750 192.89 96.986 192.89 96.018 c -192.89 94.959 193.14 94.107 193.64 93.463 c -194.14 92.818 194.79 92.496 195.61 92.496 c -196.36 92.496 196.99 92.785 197.48 93.363 c -197.48 89.748 l -198.64 89.748 l -198.64 99.000 l -h -197.48 94.154 m -196.88 93.627 196.32 93.363 195.78 93.363 c -194.68 93.363 194.12 94.209 194.12 95.900 c -194.12 97.389 194.62 98.133 195.60 98.133 c -196.24 98.133 196.87 97.783 197.48 97.084 c -h -200.97 100.88 m -200.97 100.45 l -201.34 100.34 201.53 99.898 201.53 99.117 c -201.53 99.000 l -200.97 99.000 l -200.97 97.553 l -202.42 97.553 l -202.42 98.807 l -202.42 100.09 201.93 100.78 200.97 100.88 c -h -210.46 99.146 m -209.87 99.146 209.41 98.979 209.08 98.643 c -208.76 98.307 208.59 97.840 208.59 97.242 c -208.59 93.504 l -207.79 93.504 l -207.79 92.637 l -208.59 92.637 l -208.59 91.482 l -209.75 91.371 l -209.75 92.637 l -211.41 92.637 l -211.41 93.504 l -209.75 93.504 l -209.75 97.031 l -209.75 97.863 210.11 98.279 210.82 98.279 c -210.98 98.279 211.16 98.254 211.38 98.203 c -211.38 99.000 l -211.03 99.098 210.72 99.146 210.46 99.146 c -h -217.70 98.795 m -216.92 99.029 216.26 99.146 215.71 99.146 c -214.77 99.146 214.01 98.835 213.42 98.212 c -212.83 97.589 212.53 96.781 212.53 95.789 c -212.53 94.824 212.79 94.033 213.31 93.416 c -213.83 92.799 214.50 92.490 215.31 92.490 c -216.08 92.490 216.68 92.764 217.10 93.311 c -217.52 93.857 217.73 94.635 217.73 95.643 c -217.72 96.000 l -213.71 96.000 l -213.88 97.512 214.62 98.268 215.93 98.268 c -216.41 98.268 217.00 98.139 217.70 97.881 c -h -213.76 95.133 m -216.57 95.133 l -216.57 93.949 216.12 93.357 215.24 93.357 c -214.36 93.357 213.86 93.949 213.76 95.133 c -h -219.72 99.000 m -219.72 92.637 l -220.87 92.637 l -220.87 93.832 l -221.48 92.941 222.23 92.496 223.11 92.496 c -223.66 92.496 224.10 92.671 224.43 93.021 c -224.76 93.370 224.92 93.840 224.92 94.430 c -224.92 99.000 l -223.77 99.000 l -223.77 94.805 l -223.77 94.332 223.70 93.995 223.56 93.794 c -223.42 93.593 223.19 93.492 222.87 93.492 c -222.16 93.492 221.50 93.955 220.87 94.881 c -220.87 99.000 l -h -230.45 98.191 m -229.76 98.828 229.09 99.146 228.45 99.146 c -227.92 99.146 227.48 98.981 227.14 98.651 c -226.79 98.321 226.62 97.904 226.62 97.400 c -226.62 96.705 226.91 96.171 227.49 95.798 c -228.08 95.425 228.91 95.238 230.00 95.238 c -230.28 95.238 l -230.28 94.471 l -230.28 93.732 229.90 93.363 229.14 93.363 c -228.53 93.363 227.87 93.551 227.17 93.926 c -227.17 92.971 l -227.94 92.654 228.67 92.496 229.35 92.496 c -230.06 92.496 230.59 92.656 230.92 92.977 c -231.26 93.297 231.43 93.795 231.43 94.471 c -231.43 97.354 l -231.43 98.014 231.63 98.344 232.04 98.344 c -232.09 98.344 232.17 98.336 232.26 98.320 c -232.35 98.959 l -232.08 99.084 231.79 99.146 231.48 99.146 c -230.94 99.146 230.60 98.828 230.45 98.191 c -h -230.28 97.564 m -230.28 95.918 l -229.89 95.906 l -229.26 95.906 228.75 96.026 228.36 96.267 c -227.96 96.507 227.77 96.822 227.77 97.213 c -227.77 97.490 227.87 97.725 228.06 97.916 c -228.26 98.107 228.50 98.203 228.78 98.203 c -229.26 98.203 229.76 97.990 230.28 97.564 c -h -233.79 99.000 m -233.79 92.637 l -234.95 92.637 l -234.95 93.832 l -235.56 92.941 236.30 92.496 237.19 92.496 c -237.74 92.496 238.18 92.671 238.50 93.021 c -238.83 93.370 239.00 93.840 239.00 94.430 c -239.00 99.000 l -237.84 99.000 l -237.84 94.805 l -237.84 94.332 237.77 93.995 237.63 93.794 c -237.50 93.593 237.27 93.492 236.95 93.492 c -236.24 93.492 235.57 93.955 234.95 94.881 c -234.95 99.000 l -h -243.15 99.146 m -242.56 99.146 242.11 98.979 241.78 98.643 c -241.45 98.307 241.29 97.840 241.29 97.242 c -241.29 93.504 l -240.49 93.504 l -240.49 92.637 l -241.29 92.637 l -241.29 91.482 l -242.44 91.371 l -242.44 92.637 l -244.11 92.637 l -244.11 93.504 l -242.44 93.504 l -242.44 97.031 l -242.44 97.863 242.80 98.279 243.52 98.279 c -243.67 98.279 243.86 98.254 244.08 98.203 c -244.08 99.000 l -243.72 99.098 243.41 99.146 243.15 99.146 c -h -245.88 99.000 m -252.81 95.531 l -245.88 92.062 l -245.88 93.029 l -250.87 95.531 l -245.88 98.027 l -h -f -70.500 102.50 m -444.50 102.50 l -S -444.00 102.00 m -438.00 96.000 l -438.00 108.00 l -h -f* -76.996 403.00 m -76.996 401.99 l -77.332 401.20 78.012 400.35 79.035 399.42 c -79.697 398.83 l -80.549 398.06 80.975 397.29 80.975 396.54 c -80.975 396.05 80.829 395.67 80.538 395.39 c -80.247 395.12 79.848 394.98 79.340 394.98 c -78.738 394.98 78.029 395.21 77.213 395.68 c -77.213 394.66 l -77.982 394.29 78.746 394.11 79.504 394.11 c -80.316 394.11 80.969 394.33 81.461 394.77 c -81.953 395.21 82.199 395.79 82.199 396.51 c -82.199 397.03 82.075 397.49 81.827 397.89 c -81.579 398.29 81.117 398.78 80.441 399.36 c -79.996 399.74 l -79.070 400.52 78.535 401.27 78.391 401.99 c -82.158 401.99 l -82.158 403.00 l -h -87.350 403.22 m -86.455 403.22 85.731 402.80 85.179 401.95 c -84.626 401.11 84.350 400.01 84.350 398.66 c -84.350 397.29 84.628 396.19 85.185 395.36 c -85.741 394.53 86.475 394.11 87.385 394.11 c -88.295 394.11 89.028 394.53 89.585 395.36 c -90.142 396.19 90.420 397.29 90.420 398.64 c -90.420 400.03 90.142 401.14 89.585 401.97 c -89.028 402.80 88.283 403.22 87.350 403.22 c -h -87.361 402.35 m -88.584 402.35 89.195 401.11 89.195 398.62 c -89.195 396.19 88.592 394.98 87.385 394.98 c -86.182 394.98 85.580 396.21 85.580 398.66 c -85.580 401.12 86.174 402.35 87.361 402.35 c -h -94.938 403.22 m -94.043 403.22 93.319 402.80 92.767 401.95 c -92.214 401.11 91.938 400.01 91.938 398.66 c -91.938 397.29 92.216 396.19 92.772 395.36 c -93.329 394.53 94.062 394.11 94.973 394.11 c -95.883 394.11 96.616 394.53 97.173 395.36 c -97.729 396.19 98.008 397.29 98.008 398.64 c -98.008 400.03 97.729 401.14 97.173 401.97 c -96.616 402.80 95.871 403.22 94.938 403.22 c -h -94.949 402.35 m -96.172 402.35 96.783 401.11 96.783 398.62 c -96.783 396.19 96.180 394.98 94.973 394.98 c -93.770 394.98 93.168 396.21 93.168 398.66 c -93.168 401.12 93.762 402.35 94.949 402.35 c -h -107.17 403.22 m -105.97 403.22 105.00 402.80 104.27 401.97 c -103.54 401.13 103.17 400.03 103.17 398.66 c -103.17 397.28 103.54 396.18 104.27 395.35 c -105.01 394.52 105.99 394.11 107.22 394.11 c -108.45 394.11 109.43 394.52 110.17 395.35 c -110.91 396.17 111.28 397.27 111.28 398.65 c -111.28 400.05 110.91 401.16 110.17 401.98 c -109.43 402.81 108.43 403.22 107.17 403.22 c -h -107.19 402.30 m -108.08 402.30 108.76 401.98 109.25 401.34 c -109.73 400.70 109.97 399.80 109.97 398.63 c -109.97 397.51 109.73 396.62 109.24 395.99 c -108.76 395.35 108.08 395.03 107.22 395.03 c -106.36 395.03 105.69 395.35 105.20 395.99 c -104.72 396.63 104.48 397.52 104.48 398.65 c -104.48 399.79 104.72 400.68 105.20 401.32 c -105.68 401.97 106.34 402.30 107.19 402.30 c -h -113.00 403.00 m -113.00 394.33 l -114.16 394.33 l -114.16 398.59 l -117.67 394.33 l -118.90 394.33 l -115.50 398.46 l -119.51 403.00 l -117.95 403.00 l -114.16 398.61 l -114.16 403.00 l -h -f -318.06 117.15 m -317.48 117.15 317.02 116.98 316.69 116.64 c -316.37 116.31 316.20 115.84 316.20 115.24 c -316.20 111.50 l -315.40 111.50 l -315.40 110.64 l -316.20 110.64 l -316.20 109.48 l -317.36 109.37 l -317.36 110.64 l -319.02 110.64 l -319.02 111.50 l -317.36 111.50 l -317.36 115.03 l -317.36 115.86 317.71 116.28 318.43 116.28 c -318.59 116.28 318.77 116.25 318.99 116.20 c -318.99 117.00 l -318.63 117.10 318.33 117.15 318.06 117.15 c -h -323.13 117.15 m -322.22 117.15 321.50 116.84 320.95 116.24 c -320.41 115.64 320.14 114.83 320.14 113.82 c -320.14 112.79 320.41 111.99 320.96 111.39 c -321.50 110.79 322.24 110.50 323.17 110.50 c -324.11 110.50 324.85 110.79 325.39 111.39 c -325.94 111.99 326.21 112.79 326.21 113.81 c -326.21 114.85 325.94 115.66 325.39 116.26 c -324.84 116.85 324.09 117.15 323.13 117.15 c -h -323.15 116.28 m -324.37 116.28 324.98 115.46 324.98 113.81 c -324.98 112.18 324.38 111.36 323.17 111.36 c -321.97 111.36 321.37 112.18 321.37 113.82 c -321.37 115.46 321.96 116.28 323.15 116.28 c -h -328.01 117.00 m -328.01 107.75 l -329.17 107.75 l -329.17 113.72 l -331.86 110.64 l -333.11 110.64 l -330.53 113.61 l -333.64 117.00 l -332.16 117.00 l -329.17 113.74 l -329.17 117.00 l -h -339.69 116.79 m -338.92 117.03 338.26 117.15 337.71 117.15 c -336.77 117.15 336.00 116.83 335.41 116.21 c -334.82 115.59 334.52 114.78 334.52 113.79 c -334.52 112.82 334.78 112.03 335.31 111.42 c -335.83 110.80 336.49 110.49 337.31 110.49 c -338.08 110.49 338.67 110.76 339.09 111.31 c -339.51 111.86 339.72 112.63 339.72 113.64 c -339.71 114.00 l -335.70 114.00 l -335.87 115.51 336.61 116.27 337.92 116.27 c -338.40 116.27 338.99 116.14 339.69 115.88 c -h -335.75 113.13 m -338.56 113.13 l -338.56 111.95 338.12 111.36 337.24 111.36 c -336.35 111.36 335.86 111.95 335.75 113.13 c -h -341.71 117.00 m -341.71 110.64 l -342.87 110.64 l -342.87 111.83 l -343.48 110.94 344.22 110.50 345.11 110.50 c -345.66 110.50 346.10 110.67 346.42 111.02 c -346.75 111.37 346.92 111.84 346.92 112.43 c -346.92 117.00 l -345.76 117.00 l -345.76 112.80 l -345.76 112.33 345.69 112.00 345.55 111.79 c -345.42 111.59 345.19 111.49 344.87 111.49 c -344.16 111.49 343.49 111.96 342.87 112.88 c -342.87 117.00 l -h -349.18 118.88 m -349.18 118.45 l -349.55 118.34 349.74 117.90 349.74 117.12 c -349.74 117.00 l -349.18 117.00 l -349.18 115.55 l -350.62 115.55 l -350.62 116.81 l -350.62 118.09 350.14 118.78 349.18 118.88 c -h -358.44 117.15 m -357.91 117.15 357.27 117.02 356.52 116.78 c -356.52 115.72 l -357.27 116.09 357.93 116.28 358.49 116.28 c -358.82 116.28 359.10 116.19 359.31 116.01 c -359.53 115.83 359.64 115.61 359.64 115.34 c -359.64 114.94 359.34 114.62 358.72 114.36 c -358.05 114.07 l -357.05 113.66 356.55 113.06 356.55 112.28 c -356.55 111.73 356.75 111.29 357.14 110.97 c -357.54 110.66 358.07 110.50 358.76 110.50 c -359.11 110.50 359.55 110.54 360.08 110.64 c -360.32 110.69 l -360.32 111.65 l -359.67 111.46 359.16 111.36 358.78 111.36 c -358.04 111.36 357.67 111.63 357.67 112.17 c -357.67 112.52 357.95 112.81 358.51 113.05 c -359.07 113.29 l -359.70 113.55 360.14 113.83 360.40 114.13 c -360.67 114.42 360.80 114.79 360.80 115.23 c -360.80 115.79 360.58 116.25 360.13 116.61 c -359.69 116.97 359.13 117.15 358.44 117.15 c -h -367.54 116.79 m -366.76 117.03 366.10 117.15 365.55 117.15 c -364.61 117.15 363.85 116.83 363.25 116.21 c -362.66 115.59 362.37 114.78 362.37 113.79 c -362.37 112.82 362.63 112.03 363.15 111.42 c -363.67 110.80 364.34 110.49 365.15 110.49 c -365.92 110.49 366.51 110.76 366.93 111.31 c -367.35 111.86 367.56 112.63 367.56 113.64 c -367.56 114.00 l -363.54 114.00 l -363.71 115.51 364.45 116.27 365.77 116.27 c -366.25 116.27 366.84 116.14 367.54 115.88 c -h -363.60 113.13 m -366.40 113.13 l -366.40 111.95 365.96 111.36 365.08 111.36 c -364.19 111.36 363.70 111.95 363.60 113.13 c -h -369.56 117.00 m -369.56 110.64 l -370.71 110.64 l -370.71 111.83 l -371.17 110.94 371.83 110.50 372.70 110.50 c -372.82 110.50 372.94 110.51 373.07 110.53 c -373.07 111.60 l -372.87 111.54 372.70 111.50 372.54 111.50 c -371.81 111.50 371.20 111.94 370.71 112.80 c -370.71 117.00 l -h -375.79 117.00 m -373.42 110.64 l -374.57 110.64 l -376.42 115.59 l -378.38 110.64 l -379.45 110.64 l -376.94 117.00 l -h -380.68 117.00 m -380.68 110.64 l -381.83 110.64 l -381.83 117.00 l -h -380.68 109.48 m -380.68 108.33 l -381.83 108.33 l -381.83 109.48 l -h -386.63 117.15 m -385.77 117.15 385.06 116.83 384.49 116.19 c -383.93 115.55 383.64 114.75 383.64 113.78 c -383.64 112.75 383.92 111.94 384.48 111.36 c -385.04 110.79 385.83 110.50 386.83 110.50 c -387.33 110.50 387.88 110.56 388.49 110.70 c -388.49 111.67 l -387.84 111.48 387.31 111.38 386.90 111.38 c -386.31 111.38 385.84 111.60 385.48 112.05 c -385.12 112.49 384.94 113.08 384.94 113.82 c -384.94 114.53 385.13 115.11 385.49 115.55 c -385.86 115.99 386.34 116.21 386.94 116.21 c -387.46 116.21 388.01 116.08 388.56 115.81 c -388.56 116.81 l -387.82 117.03 387.17 117.15 386.63 117.15 c -h -394.96 116.79 m -394.18 117.03 393.52 117.15 392.97 117.15 c -392.03 117.15 391.27 116.83 390.68 116.21 c -390.08 115.59 389.79 114.78 389.79 113.79 c -389.79 112.82 390.05 112.03 390.57 111.42 c -391.09 110.80 391.76 110.49 392.57 110.49 c -393.34 110.49 393.94 110.76 394.36 111.31 c -394.78 111.86 394.99 112.63 394.99 113.64 c -394.98 114.00 l -390.97 114.00 l -391.13 115.51 391.88 116.27 393.19 116.27 c -393.67 116.27 394.26 116.14 394.96 115.88 c -h -391.02 113.13 m -393.83 113.13 l -393.83 111.95 393.38 111.36 392.50 111.36 c -391.62 111.36 391.12 111.95 391.02 113.13 c -h -400.66 117.22 m -399.31 117.22 398.27 116.82 397.54 116.03 c -396.80 115.24 396.43 114.12 396.43 112.67 c -396.43 111.22 396.81 110.10 397.56 109.31 c -398.30 108.51 399.36 108.11 400.72 108.11 c -401.49 108.11 402.40 108.24 403.45 108.49 c -403.45 109.65 l -402.26 109.24 401.34 109.03 400.70 109.03 c -399.76 109.03 399.03 109.35 398.51 109.99 c -398.00 110.62 397.74 111.52 397.74 112.68 c -397.74 113.79 398.02 114.66 398.57 115.30 c -399.12 115.94 399.87 116.26 400.82 116.26 c -401.64 116.26 402.52 116.00 403.46 115.50 c -403.46 116.55 l -402.60 117.00 401.67 117.22 400.66 117.22 c -h -408.56 116.19 m -407.87 116.83 407.21 117.15 406.56 117.15 c -406.04 117.15 405.60 116.98 405.25 116.65 c -404.90 116.32 404.73 115.90 404.73 115.40 c -404.73 114.71 405.02 114.17 405.61 113.80 c -406.19 113.42 407.03 113.24 408.12 113.24 c -408.39 113.24 l -408.39 112.47 l -408.39 111.73 408.01 111.36 407.26 111.36 c -406.65 111.36 405.99 111.55 405.28 111.93 c -405.28 110.97 l -406.06 110.65 406.79 110.50 407.47 110.50 c -408.18 110.50 408.70 110.66 409.04 110.98 c -409.38 111.30 409.55 111.79 409.55 112.47 c -409.55 115.35 l -409.55 116.01 409.75 116.34 410.16 116.34 c -410.21 116.34 410.28 116.34 410.38 116.32 c -410.46 116.96 l -410.20 117.08 409.91 117.15 409.59 117.15 c -409.05 117.15 408.71 116.83 408.56 116.19 c -h -408.39 115.56 m -408.39 113.92 l -408.01 113.91 l -407.37 113.91 406.86 114.03 406.47 114.27 c -406.08 114.51 405.88 114.82 405.88 115.21 c -405.88 115.49 405.98 115.72 406.18 115.92 c -406.37 116.11 406.61 116.20 406.89 116.20 c -407.37 116.20 407.87 115.99 408.39 115.56 c -h -413.82 117.15 m -413.23 117.15 412.78 116.98 412.45 116.64 c -412.12 116.31 411.96 115.84 411.96 115.24 c -411.96 111.50 l -411.16 111.50 l -411.16 110.64 l -411.96 110.64 l -411.96 109.48 l -413.11 109.37 l -413.11 110.64 l -414.77 110.64 l -414.77 111.50 l -413.11 111.50 l -413.11 115.03 l -413.11 115.86 413.47 116.28 414.19 116.28 c -414.34 116.28 414.53 116.25 414.74 116.20 c -414.74 117.00 l -414.39 117.10 414.08 117.15 413.82 117.15 c -h -419.68 116.19 m -418.99 116.83 418.32 117.15 417.68 117.15 c -417.15 117.15 416.71 116.98 416.37 116.65 c -416.02 116.32 415.85 115.90 415.85 115.40 c -415.85 114.71 416.14 114.17 416.72 113.80 c -417.31 113.42 418.14 113.24 419.23 113.24 c -419.51 113.24 l -419.51 112.47 l -419.51 111.73 419.13 111.36 418.37 111.36 c -417.76 111.36 417.10 111.55 416.40 111.93 c -416.40 110.97 l -417.17 110.65 417.90 110.50 418.58 110.50 c -419.29 110.50 419.82 110.66 420.16 110.98 c -420.49 111.30 420.66 111.79 420.66 112.47 c -420.66 115.35 l -420.66 116.01 420.87 116.34 421.27 116.34 c -421.32 116.34 421.40 116.34 421.49 116.32 c -421.58 116.96 l -421.31 117.08 421.03 117.15 420.71 117.15 c -420.17 117.15 419.83 116.83 419.68 116.19 c -h -419.51 115.56 m -419.51 113.92 l -419.12 113.91 l -418.49 113.91 417.98 114.03 417.59 114.27 c -417.20 114.51 417.00 114.82 417.00 115.21 c -417.00 115.49 417.10 115.72 417.29 115.92 c -417.49 116.11 417.73 116.20 418.01 116.20 c -418.49 116.20 418.99 115.99 419.51 115.56 c -h -423.02 117.00 m -423.02 107.75 l -424.18 107.75 l -424.18 117.00 l -h -428.98 117.15 m -428.07 117.15 427.35 116.84 426.80 116.24 c -426.26 115.64 425.99 114.83 425.99 113.82 c -425.99 112.79 426.26 111.99 426.81 111.39 c -427.35 110.79 428.09 110.50 429.02 110.50 c -429.96 110.50 430.70 110.79 431.24 111.39 c -431.79 111.99 432.06 112.79 432.06 113.81 c -432.06 114.85 431.79 115.66 431.24 116.26 c -430.69 116.85 429.94 117.15 428.98 117.15 c -h -429.00 116.28 m -430.22 116.28 430.83 115.46 430.83 113.81 c -430.83 112.18 430.23 111.36 429.02 111.36 c -427.82 111.36 427.22 112.18 427.22 113.82 c -427.22 115.46 427.81 116.28 429.00 116.28 c -h -433.89 119.12 m -434.02 118.11 l -434.69 118.43 435.35 118.59 436.00 118.59 c -437.30 118.59 437.95 117.90 437.95 116.52 c -437.95 115.52 l -437.52 116.41 436.82 116.85 435.85 116.85 c -435.09 116.85 434.48 116.58 434.03 116.02 c -433.58 115.47 433.36 114.72 433.36 113.78 c -433.36 112.81 433.62 112.02 434.13 111.41 c -434.64 110.80 435.30 110.50 436.11 110.50 c -436.82 110.50 437.44 110.79 437.95 111.36 c -437.95 110.64 l -439.11 110.64 l -439.11 115.27 l -439.11 116.26 439.06 117.00 438.95 117.48 c -438.85 117.96 438.65 118.35 438.37 118.65 c -437.87 119.19 437.08 119.46 436.02 119.46 c -435.28 119.46 434.57 119.34 433.89 119.12 c -h -437.95 114.80 m -437.95 112.15 l -437.44 111.63 436.89 111.36 436.29 111.36 c -435.76 111.36 435.34 111.58 435.04 112.00 c -434.74 112.43 434.59 113.01 434.59 113.75 c -434.59 115.15 435.08 115.85 436.06 115.85 c -436.73 115.85 437.36 115.50 437.95 114.80 c -h -f -[ 5.0000 5.0000] 0.0000 d -444.50 120.50 m -70.500 120.50 l -S -[] 0.0000 d -70.000 120.00 m -76.000 114.00 l -76.000 126.00 l -h -f* -470.47 271.00 m -468.11 264.64 l -469.26 264.64 l -471.11 269.59 l -473.06 264.64 l -474.14 264.64 l -471.63 271.00 l -h -478.65 270.19 m -477.96 270.83 477.29 271.15 476.65 271.15 c -476.12 271.15 475.68 270.98 475.34 270.65 c -474.99 270.32 474.81 269.90 474.81 269.40 c -474.81 268.71 475.11 268.17 475.69 267.80 c -476.27 267.42 477.11 267.24 478.20 267.24 c -478.48 267.24 l -478.48 266.47 l -478.48 265.73 478.10 265.36 477.34 265.36 c -476.73 265.36 476.07 265.55 475.37 265.93 c -475.37 264.97 l -476.14 264.65 476.87 264.50 477.55 264.50 c -478.26 264.50 478.79 264.66 479.12 264.98 c -479.46 265.30 479.63 265.79 479.63 266.47 c -479.63 269.35 l -479.63 270.01 479.83 270.34 480.24 270.34 c -480.29 270.34 480.37 270.34 480.46 270.32 c -480.54 270.96 l -480.28 271.08 479.99 271.15 479.68 271.15 c -479.14 271.15 478.79 270.83 478.65 270.19 c -h -478.48 269.56 m -478.48 267.92 l -478.09 267.91 l -477.46 267.91 476.95 268.03 476.55 268.27 c -476.16 268.51 475.97 268.82 475.97 269.21 c -475.97 269.49 476.07 269.72 476.26 269.92 c -476.46 270.11 476.70 270.20 476.98 270.20 c -477.46 270.20 477.96 269.99 478.48 269.56 c -h -481.99 271.00 m -481.99 261.75 l -483.15 261.75 l -483.15 271.00 l -h -485.46 271.00 m -485.46 264.64 l -486.62 264.64 l -486.62 271.00 l -h -485.46 263.48 m -485.46 262.33 l -486.62 262.33 l -486.62 263.48 l -h -493.01 271.00 m -493.01 269.80 l -492.54 270.70 491.84 271.15 490.89 271.15 c -490.13 271.15 489.52 270.87 489.08 270.31 c -488.65 269.75 488.43 268.99 488.43 268.02 c -488.43 266.96 488.67 266.11 489.17 265.46 c -489.67 264.82 490.33 264.50 491.14 264.50 c -491.89 264.50 492.52 264.79 493.01 265.36 c -493.01 261.75 l -494.17 261.75 l -494.17 271.00 l -h -493.01 266.15 m -492.42 265.63 491.85 265.36 491.31 265.36 c -490.21 265.36 489.66 266.21 489.66 267.90 c -489.66 269.39 490.15 270.13 491.13 270.13 c -491.77 270.13 492.40 269.78 493.01 269.08 c -h -499.76 270.19 m -499.07 270.83 498.41 271.15 497.77 271.15 c -497.24 271.15 496.80 270.98 496.45 270.65 c -496.11 270.32 495.93 269.90 495.93 269.40 c -495.93 268.71 496.22 268.17 496.81 267.80 c -497.39 267.42 498.23 267.24 499.32 267.24 c -499.59 267.24 l -499.59 266.47 l -499.59 265.73 499.21 265.36 498.46 265.36 c -497.85 265.36 497.19 265.55 496.48 265.93 c -496.48 264.97 l -497.26 264.65 497.99 264.50 498.67 264.50 c -499.38 264.50 499.90 264.66 500.24 264.98 c -500.58 265.30 500.75 265.79 500.75 266.47 c -500.75 269.35 l -500.75 270.01 500.95 270.34 501.36 270.34 c -501.41 270.34 501.48 270.34 501.58 270.32 c -501.66 270.96 l -501.40 271.08 501.11 271.15 500.79 271.15 c -500.26 271.15 499.91 270.83 499.76 270.19 c -h -499.59 269.56 m -499.59 267.92 l -499.21 267.91 l -498.57 267.91 498.06 268.03 497.67 268.27 c -497.28 268.51 497.09 268.82 497.09 269.21 c -497.09 269.49 497.18 269.72 497.38 269.92 c -497.57 270.11 497.81 270.20 498.09 270.20 c -498.57 270.20 499.07 269.99 499.59 269.56 c -h -505.02 271.15 m -504.43 271.15 503.98 270.98 503.65 270.64 c -503.32 270.31 503.16 269.84 503.16 269.24 c -503.16 265.50 l -502.36 265.50 l -502.36 264.64 l -503.16 264.64 l -503.16 263.48 l -504.31 263.37 l -504.31 264.64 l -505.97 264.64 l -505.97 265.50 l -504.31 265.50 l -504.31 269.03 l -504.31 269.86 504.67 270.28 505.39 270.28 c -505.54 270.28 505.73 270.25 505.95 270.20 c -505.95 271.00 l -505.59 271.10 505.28 271.15 505.02 271.15 c -h -512.26 270.79 m -511.49 271.03 510.83 271.15 510.28 271.15 c -509.34 271.15 508.57 270.83 507.98 270.21 c -507.39 269.59 507.09 268.78 507.09 267.79 c -507.09 266.82 507.35 266.03 507.88 265.42 c -508.40 264.80 509.06 264.49 509.88 264.49 c -510.65 264.49 511.24 264.76 511.66 265.31 c -512.08 265.86 512.29 266.63 512.29 267.64 c -512.29 268.00 l -508.27 268.00 l -508.44 269.51 509.18 270.27 510.49 270.27 c -510.97 270.27 511.56 270.14 512.26 269.88 c -h -508.32 267.13 m -511.13 267.13 l -511.13 265.95 510.69 265.36 509.81 265.36 c -508.92 265.36 508.43 265.95 508.32 267.13 c -h -521.37 271.00 m -514.43 267.53 l -521.37 264.06 l -521.37 265.03 l -516.37 267.53 l -521.37 270.03 l -h -525.73 271.15 m -525.15 271.15 524.69 270.98 524.36 270.64 c -524.03 270.31 523.87 269.84 523.87 269.24 c -523.87 265.50 l -523.07 265.50 l -523.07 264.64 l -523.87 264.64 l -523.87 263.48 l -525.02 263.37 l -525.02 264.64 l -526.69 264.64 l -526.69 265.50 l -525.02 265.50 l -525.02 269.03 l -525.02 269.86 525.38 270.28 526.10 270.28 c -526.25 270.28 526.44 270.25 526.66 270.20 c -526.66 271.00 l -526.30 271.10 525.99 271.15 525.73 271.15 c -h -530.80 271.15 m -529.89 271.15 529.16 270.84 528.62 270.24 c -528.08 269.64 527.81 268.83 527.81 267.82 c -527.81 266.79 528.08 265.99 528.62 265.39 c -529.17 264.79 529.91 264.50 530.84 264.50 c -531.78 264.50 532.51 264.79 533.06 265.39 c -533.60 265.99 533.88 266.79 533.88 267.81 c -533.88 268.85 533.60 269.66 533.06 270.26 c -532.51 270.85 531.76 271.15 530.80 271.15 c -h -530.82 270.28 m -532.04 270.28 532.65 269.46 532.65 267.81 c -532.65 266.18 532.05 265.36 530.84 265.36 c -529.64 265.36 529.04 266.18 529.04 267.82 c -529.04 269.46 529.63 270.28 530.82 270.28 c -h -535.68 271.00 m -535.68 261.75 l -536.84 261.75 l -536.84 267.72 l -539.53 264.64 l -540.77 264.64 l -538.20 267.61 l -541.31 271.00 l -539.83 271.00 l -536.84 267.74 l -536.84 271.00 l -h -547.36 270.79 m -546.59 271.03 545.92 271.15 545.37 271.15 c -544.44 271.15 543.67 270.83 543.08 270.21 c -542.49 269.59 542.19 268.78 542.19 267.79 c -542.19 266.82 542.45 266.03 542.97 265.42 c -543.50 264.80 544.16 264.49 544.97 264.49 c -545.74 264.49 546.34 264.76 546.76 265.31 c -547.18 265.86 547.39 266.63 547.39 267.64 c -547.38 268.00 l -543.37 268.00 l -543.54 269.51 544.28 270.27 545.59 270.27 c -546.07 270.27 546.66 270.14 547.36 269.88 c -h -543.42 267.13 m -546.23 267.13 l -546.23 265.95 545.79 265.36 544.90 265.36 c -544.02 265.36 543.52 265.95 543.42 267.13 c -h -549.38 271.00 m -549.38 264.64 l -550.54 264.64 l -550.54 265.83 l -551.14 264.94 551.89 264.50 552.77 264.50 c -553.32 264.50 553.76 264.67 554.09 265.02 c -554.42 265.37 554.58 265.84 554.58 266.43 c -554.58 271.00 l -553.43 271.00 l -553.43 266.80 l -553.43 266.33 553.36 266.00 553.22 265.79 c -553.08 265.59 552.85 265.49 552.53 265.49 c -551.83 265.49 551.16 265.96 550.54 266.88 c -550.54 271.00 l -h -556.85 272.88 m -556.85 272.45 l -557.22 272.34 557.41 271.90 557.41 271.12 c -557.41 271.00 l -556.85 271.00 l -556.85 269.55 l -558.29 269.55 l -558.29 270.81 l -558.29 272.09 557.81 272.78 556.85 272.88 c -h -564.42 272.73 m -564.42 261.75 l -566.74 261.75 l -566.74 262.62 l -565.44 262.62 l -565.44 271.87 l -566.74 271.87 l -566.74 272.73 l -h -570.23 271.15 m -569.65 271.15 569.19 270.98 568.86 270.64 c -568.54 270.31 568.37 269.84 568.37 269.24 c -568.37 265.50 l -567.57 265.50 l -567.57 264.64 l -568.37 264.64 l -568.37 263.48 l -569.53 263.37 l -569.53 264.64 l -571.19 264.64 l -571.19 265.50 l -569.53 265.50 l -569.53 269.03 l -569.53 269.86 569.88 270.28 570.60 270.28 c -570.76 270.28 570.94 270.25 571.16 270.20 c -571.16 271.00 l -570.80 271.10 570.50 271.15 570.23 271.15 c -h -577.48 270.79 m -576.70 271.03 576.04 271.15 575.49 271.15 c -574.55 271.15 573.79 270.83 573.20 270.21 c -572.60 269.59 572.31 268.78 572.31 267.79 c -572.31 266.82 572.57 266.03 573.09 265.42 c -573.61 264.80 574.28 264.49 575.09 264.49 c -575.86 264.49 576.46 264.76 576.88 265.31 c -577.30 265.86 577.51 266.63 577.51 267.64 c -577.50 268.00 l -573.49 268.00 l -573.65 269.51 574.39 270.27 575.71 270.27 c -576.19 270.27 576.78 270.14 577.48 269.88 c -h -573.54 267.13 m -576.35 267.13 l -576.35 265.95 575.90 265.36 575.02 265.36 c -574.13 265.36 573.64 265.95 573.54 267.13 c -h -579.50 271.00 m -579.50 264.64 l -580.65 264.64 l -580.65 265.83 l -581.26 264.94 582.01 264.50 582.89 264.50 c -583.44 264.50 583.88 264.67 584.21 265.02 c -584.54 265.37 584.70 265.84 584.70 266.43 c -584.70 271.00 l -583.55 271.00 l -583.55 266.80 l -583.55 266.33 583.48 266.00 583.34 265.79 c -583.20 265.59 582.97 265.49 582.65 265.49 c -581.94 265.49 581.28 265.96 580.65 266.88 c -580.65 271.00 l -h -590.23 270.19 m -589.54 270.83 588.87 271.15 588.23 271.15 c -587.70 271.15 587.26 270.98 586.92 270.65 c -586.57 270.32 586.39 269.90 586.39 269.40 c -586.39 268.71 586.69 268.17 587.27 267.80 c -587.85 267.42 588.69 267.24 589.78 267.24 c -590.06 267.24 l -590.06 266.47 l -590.06 265.73 589.68 265.36 588.92 265.36 c -588.31 265.36 587.65 265.55 586.95 265.93 c -586.95 264.97 l -587.72 264.65 588.45 264.50 589.13 264.50 c -589.84 264.50 590.37 264.66 590.70 264.98 c -591.04 265.30 591.21 265.79 591.21 266.47 c -591.21 269.35 l -591.21 270.01 591.41 270.34 591.82 270.34 c -591.87 270.34 591.95 270.34 592.04 270.32 c -592.12 270.96 l -591.86 271.08 591.57 271.15 591.26 271.15 c -590.72 271.15 590.38 270.83 590.23 270.19 c -h -590.06 269.56 m -590.06 267.92 l -589.67 267.91 l -589.04 267.91 588.53 268.03 588.13 268.27 c -587.74 268.51 587.55 268.82 587.55 269.21 c -587.55 269.49 587.65 269.72 587.84 269.92 c -588.04 270.11 588.28 270.20 588.56 270.20 c -589.04 270.20 589.54 269.99 590.06 269.56 c -h -593.57 271.00 m -593.57 264.64 l -594.73 264.64 l -594.73 265.83 l -595.34 264.94 596.08 264.50 596.96 264.50 c -597.52 264.50 597.96 264.67 598.28 265.02 c -598.61 265.37 598.78 265.84 598.78 266.43 c -598.78 271.00 l -597.62 271.00 l -597.62 266.80 l -597.62 266.33 597.55 266.00 597.41 265.79 c -597.27 265.59 597.04 265.49 596.72 265.49 c -596.02 265.49 595.35 265.96 594.73 266.88 c -594.73 271.00 l -h -602.93 271.15 m -602.34 271.15 601.89 270.98 601.56 270.64 c -601.23 270.31 601.07 269.84 601.07 269.24 c -601.07 265.50 l -600.27 265.50 l -600.27 264.64 l -601.07 264.64 l -601.07 263.48 l -602.22 263.37 l -602.22 264.64 l -603.88 264.64 l -603.88 265.50 l -602.22 265.50 l -602.22 269.03 l -602.22 269.86 602.58 270.28 603.30 270.28 c -603.45 270.28 603.64 270.25 603.86 270.20 c -603.86 271.00 l -603.50 271.10 603.19 271.15 602.93 271.15 c -h -607.10 272.73 m -607.10 261.75 l -604.79 261.75 l -604.79 262.62 l -606.09 262.62 l -606.09 271.87 l -604.79 271.87 l -604.79 272.73 l -h -609.56 271.00 m -616.49 267.53 l -609.56 264.06 l -609.56 265.03 l -614.55 267.53 l -609.56 270.03 l -h -f -623.50 274.50 m -452.50 274.50 l -S -452.00 274.00 m -458.00 268.00 l -458.00 280.00 l -h -f* -575.84 365.15 m -575.31 365.15 574.67 365.02 573.92 364.78 c -573.92 363.72 l -574.67 364.09 575.33 364.28 575.89 364.28 c -576.22 364.28 576.50 364.19 576.71 364.01 c -576.93 363.83 577.04 363.61 577.04 363.34 c -577.04 362.94 576.74 362.62 576.12 362.36 c -575.45 362.07 l -574.45 361.66 573.96 361.06 573.96 360.28 c -573.96 359.73 574.15 359.29 574.54 358.97 c -574.94 358.66 575.47 358.50 576.16 358.50 c -576.51 358.50 576.95 358.54 577.48 358.64 c -577.72 358.69 l -577.72 359.65 l -577.07 359.46 576.56 359.36 576.18 359.36 c -575.44 359.36 575.07 359.63 575.07 360.17 c -575.07 360.52 575.35 360.81 575.91 361.05 c -576.47 361.29 l -577.10 361.55 577.54 361.83 577.80 362.13 c -578.07 362.42 578.20 362.79 578.20 363.23 c -578.20 363.79 577.98 364.25 577.54 364.61 c -577.09 364.97 576.53 365.15 575.84 365.15 c -h -584.25 365.00 m -584.25 363.80 l -583.64 364.70 582.89 365.15 582.02 365.15 c -581.46 365.15 581.02 364.97 580.69 364.62 c -580.37 364.27 580.20 363.80 580.20 363.21 c -580.20 358.64 l -581.36 358.64 l -581.36 362.83 l -581.36 363.31 581.42 363.65 581.56 363.85 c -581.70 364.05 581.93 364.15 582.26 364.15 c -582.96 364.15 583.62 363.69 584.25 362.76 c -584.25 358.64 l -585.40 358.64 l -585.40 365.00 l -h -590.20 365.15 m -589.34 365.15 588.63 364.83 588.06 364.19 c -587.50 363.55 587.21 362.75 587.21 361.78 c -587.21 360.75 587.50 359.94 588.06 359.36 c -588.62 358.79 589.40 358.50 590.40 358.50 c -590.90 358.50 591.45 358.56 592.07 358.70 c -592.07 359.67 l -591.41 359.48 590.88 359.38 590.47 359.38 c -589.88 359.38 589.41 359.60 589.05 360.05 c -588.69 360.49 588.52 361.08 588.52 361.82 c -588.52 362.53 588.70 363.11 589.07 363.55 c -589.43 363.99 589.91 364.21 590.51 364.21 c -591.04 364.21 591.58 364.08 592.14 363.81 c -592.14 364.81 l -591.39 365.03 590.75 365.15 590.20 365.15 c -h -596.35 365.15 m -595.49 365.15 594.78 364.83 594.21 364.19 c -593.64 363.55 593.36 362.75 593.36 361.78 c -593.36 360.75 593.64 359.94 594.20 359.36 c -594.76 358.79 595.54 358.50 596.55 358.50 c -597.04 358.50 597.60 358.56 598.21 358.70 c -598.21 359.67 l -597.56 359.48 597.03 359.38 596.62 359.38 c -596.03 359.38 595.56 359.60 595.20 360.05 c -594.84 360.49 594.66 361.08 594.66 361.82 c -594.66 362.53 594.85 363.11 595.21 363.55 c -595.58 363.99 596.06 364.21 596.65 364.21 c -597.18 364.21 597.72 364.08 598.28 363.81 c -598.28 364.81 l -597.54 365.03 596.89 365.15 596.35 365.15 c -h -604.68 364.79 m -603.90 365.03 603.24 365.15 602.69 365.15 c -601.75 365.15 600.99 364.83 600.40 364.21 c -599.80 363.59 599.51 362.78 599.51 361.79 c -599.51 360.82 599.77 360.03 600.29 359.42 c -600.81 358.80 601.48 358.49 602.29 358.49 c -603.06 358.49 603.66 358.76 604.08 359.31 c -604.50 359.86 604.71 360.63 604.71 361.64 c -604.70 362.00 l -600.69 362.00 l -600.85 363.51 601.59 364.27 602.91 364.27 c -603.39 364.27 603.98 364.14 604.68 363.88 c -h -600.74 361.13 m -603.54 361.13 l -603.54 359.95 603.10 359.36 602.22 359.36 c -601.33 359.36 600.84 359.95 600.74 361.13 c -h -608.38 365.15 m -607.86 365.15 607.22 365.02 606.46 364.78 c -606.46 363.72 l -607.22 364.09 607.87 364.28 608.43 364.28 c -608.76 364.28 609.04 364.19 609.26 364.01 c -609.48 363.83 609.59 363.61 609.59 363.34 c -609.59 362.94 609.28 362.62 608.67 362.36 c -607.99 362.07 l -607.00 361.66 606.50 361.06 606.50 360.28 c -606.50 359.73 606.69 359.29 607.09 358.97 c -607.48 358.66 608.02 358.50 608.70 358.50 c -609.06 358.50 609.50 358.54 610.02 358.64 c -610.26 358.69 l -610.26 359.65 l -609.62 359.46 609.10 359.36 608.72 359.36 c -607.98 359.36 607.61 359.63 607.61 360.17 c -607.61 360.52 607.89 360.81 608.46 361.05 c -609.01 361.29 l -609.64 361.55 610.09 361.83 610.35 362.13 c -610.61 362.42 610.74 362.79 610.74 363.23 c -610.74 363.79 610.52 364.25 610.08 364.61 c -609.64 364.97 609.07 365.15 608.38 365.15 c -h -614.50 365.15 m -613.97 365.15 613.33 365.02 612.58 364.78 c -612.58 363.72 l -613.33 364.09 613.99 364.28 614.55 364.28 c -614.88 364.28 615.16 364.19 615.38 364.01 c -615.59 363.83 615.70 363.61 615.70 363.34 c -615.70 362.94 615.40 362.62 614.78 362.36 c -614.11 362.07 l -613.11 361.66 612.62 361.06 612.62 360.28 c -612.62 359.73 612.81 359.29 613.20 358.97 c -613.60 358.66 614.13 358.50 614.82 358.50 c -615.17 358.50 615.61 358.54 616.14 358.64 c -616.38 358.69 l -616.38 359.65 l -615.73 359.46 615.22 359.36 614.84 359.36 c -614.10 359.36 613.73 359.63 613.73 360.17 c -613.73 360.52 614.01 360.81 614.57 361.05 c -615.13 361.29 l -615.76 361.55 616.20 361.83 616.46 362.13 c -616.73 362.42 616.86 362.79 616.86 363.23 c -616.86 363.79 616.64 364.25 616.20 364.61 c -615.75 364.97 615.19 365.15 614.50 365.15 c -h -f -[ 5.0000 5.0000] 0.0000 d -623.50 368.50 m -70.500 368.50 l -S -[] 0.0000 d -70.000 368.00 m -76.000 362.00 l -76.000 374.00 l -h -f* -463.13 289.00 m -463.13 287.80 l -462.52 288.70 461.78 289.15 460.90 289.15 c -460.35 289.15 459.90 288.97 459.58 288.62 c -459.25 288.27 459.08 287.80 459.08 287.21 c -459.08 282.64 l -460.24 282.64 l -460.24 286.83 l -460.24 287.31 460.31 287.65 460.45 287.85 c -460.58 288.05 460.82 288.15 461.14 288.15 c -461.84 288.15 462.51 287.69 463.13 286.76 c -463.13 282.64 l -464.29 282.64 l -464.29 289.00 l -h -468.29 289.15 m -467.76 289.15 467.12 289.02 466.37 288.78 c -466.37 287.72 l -467.12 288.09 467.78 288.28 468.34 288.28 c -468.67 288.28 468.94 288.19 469.16 288.01 c -469.38 287.83 469.49 287.61 469.49 287.34 c -469.49 286.94 469.18 286.62 468.57 286.36 c -467.90 286.07 l -466.90 285.66 466.40 285.06 466.40 284.28 c -466.40 283.73 466.60 283.29 466.99 282.97 c -467.38 282.66 467.92 282.50 468.61 282.50 c -468.96 282.50 469.40 282.54 469.92 282.64 c -470.16 282.69 l -470.16 283.65 l -469.52 283.46 469.01 283.36 468.63 283.36 c -467.89 283.36 467.52 283.63 467.52 284.17 c -467.52 284.52 467.80 284.81 468.36 285.05 c -468.92 285.29 l -469.54 285.55 469.99 285.83 470.25 286.13 c -470.51 286.42 470.64 286.79 470.64 287.23 c -470.64 287.79 470.42 288.25 469.98 288.61 c -469.54 288.97 468.98 289.15 468.29 289.15 c -h -477.38 288.79 m -476.61 289.03 475.95 289.15 475.40 289.15 c -474.46 289.15 473.69 288.83 473.10 288.21 c -472.51 287.59 472.21 286.78 472.21 285.79 c -472.21 284.82 472.48 284.03 473.00 283.42 c -473.52 282.80 474.19 282.49 475.00 282.49 c -475.77 282.49 476.36 282.76 476.78 283.31 c -477.20 283.86 477.41 284.63 477.41 285.64 c -477.41 286.00 l -473.39 286.00 l -473.56 287.51 474.30 288.27 475.61 288.27 c -476.09 288.27 476.68 288.14 477.38 287.88 c -h -473.45 285.13 m -476.25 285.13 l -476.25 283.95 475.81 283.36 474.93 283.36 c -474.04 283.36 473.55 283.95 473.45 285.13 c -h -479.40 289.00 m -479.40 282.64 l -480.56 282.64 l -480.56 283.83 l -481.02 282.94 481.68 282.50 482.55 282.50 c -482.67 282.50 482.79 282.51 482.92 282.53 c -482.92 283.60 l -482.72 283.54 482.54 283.50 482.39 283.50 c -481.66 283.50 481.05 283.94 480.56 284.80 c -480.56 289.00 l -h -484.33 290.88 m -484.33 290.45 l -484.71 290.34 484.89 289.90 484.89 289.12 c -484.89 289.00 l -484.33 289.00 l -484.33 287.55 l -485.78 287.55 l -485.78 288.81 l -485.78 290.09 485.30 290.78 484.33 290.88 c -h -491.91 289.00 m -491.91 282.64 l -493.06 282.64 l -493.06 283.83 l -493.52 282.94 494.18 282.50 495.05 282.50 c -495.17 282.50 495.29 282.51 495.42 282.53 c -495.42 283.60 l -495.22 283.54 495.05 283.50 494.90 283.50 c -494.17 283.50 493.55 283.94 493.06 284.80 c -493.06 289.00 l -h -499.31 289.15 m -498.40 289.15 497.67 288.84 497.13 288.24 c -496.59 287.64 496.31 286.83 496.31 285.82 c -496.31 284.79 496.59 283.99 497.13 283.39 c -497.68 282.79 498.42 282.50 499.35 282.50 c -500.28 282.50 501.02 282.79 501.57 283.39 c -502.11 283.99 502.38 284.79 502.38 285.81 c -502.38 286.85 502.11 287.66 501.56 288.26 c -501.02 288.85 500.27 289.15 499.31 289.15 c -h -499.33 288.28 m -500.55 288.28 501.16 287.46 501.16 285.81 c -501.16 284.18 500.56 283.36 499.35 283.36 c -498.15 283.36 497.54 284.18 497.54 285.82 c -497.54 287.46 498.14 288.28 499.33 288.28 c -h -504.19 289.00 m -504.19 279.75 l -505.34 279.75 l -505.34 289.00 l -h -512.32 288.79 m -511.55 289.03 510.89 289.15 510.34 289.15 c -509.40 289.15 508.63 288.83 508.04 288.21 c -507.45 287.59 507.15 286.78 507.15 285.79 c -507.15 284.82 507.42 284.03 507.94 283.42 c -508.46 282.80 509.12 282.49 509.94 282.49 c -510.71 282.49 511.30 282.76 511.72 283.31 c -512.14 283.86 512.35 284.63 512.35 285.64 c -512.35 286.00 l -508.33 286.00 l -508.50 287.51 509.24 288.27 510.55 288.27 c -511.03 288.27 511.62 288.14 512.32 287.88 c -h -508.38 285.13 m -511.19 285.13 l -511.19 283.95 510.75 283.36 509.87 283.36 c -508.98 283.36 508.49 283.95 508.38 285.13 c -h -516.03 289.15 m -515.50 289.15 514.86 289.02 514.11 288.78 c -514.11 287.72 l -514.86 288.09 515.52 288.28 516.08 288.28 c -516.41 288.28 516.69 288.19 516.90 288.01 c -517.12 287.83 517.23 287.61 517.23 287.34 c -517.23 286.94 516.93 286.62 516.31 286.36 c -515.64 286.07 l -514.64 285.66 514.14 285.06 514.14 284.28 c -514.14 283.73 514.34 283.29 514.73 282.97 c -515.13 282.66 515.66 282.50 516.35 282.50 c -516.70 282.50 517.14 282.54 517.67 282.64 c -517.91 282.69 l -517.91 283.65 l -517.26 283.46 516.75 283.36 516.37 283.36 c -515.63 283.36 515.26 283.63 515.26 284.17 c -515.26 284.52 515.54 284.81 516.10 285.05 c -516.66 285.29 l -517.29 285.55 517.73 285.83 517.99 286.13 c -518.26 286.42 518.39 286.79 518.39 287.23 c -518.39 287.79 518.17 288.25 517.72 288.61 c -517.28 288.97 516.72 289.15 516.03 289.15 c -h -f -[ 5.0000 5.0000] 0.0000 d -452.50 292.50 m -623.50 292.50 l -S -[] 0.0000 d -623.00 292.00 m -617.00 286.00 l -617.00 298.00 l -h -f* -641.44 326.19 m -640.74 326.83 640.08 327.15 639.44 327.15 c -638.91 327.15 638.47 326.98 638.12 326.65 c -637.78 326.32 637.60 325.90 637.60 325.40 c -637.60 324.71 637.90 324.17 638.48 323.80 c -639.06 323.42 639.90 323.24 640.99 323.24 c -641.27 323.24 l -641.27 322.47 l -641.27 321.73 640.89 321.36 640.13 321.36 c -639.52 321.36 638.86 321.55 638.15 321.93 c -638.15 320.97 l -638.93 320.65 639.66 320.50 640.34 320.50 c -641.05 320.50 641.58 320.66 641.91 320.98 c -642.25 321.30 642.42 321.79 642.42 322.47 c -642.42 325.35 l -642.42 326.01 642.62 326.34 643.03 326.34 c -643.08 326.34 643.15 326.34 643.25 326.32 c -643.33 326.96 l -643.07 327.08 642.78 327.15 642.47 327.15 c -641.93 327.15 641.58 326.83 641.44 326.19 c -h -641.27 325.56 m -641.27 323.92 l -640.88 323.91 l -640.25 323.91 639.73 324.03 639.34 324.27 c -638.95 324.51 638.76 324.82 638.76 325.21 c -638.76 325.49 638.86 325.72 639.05 325.92 c -639.25 326.11 639.48 326.20 639.77 326.20 c -640.25 326.20 640.75 325.99 641.27 325.56 c -h -648.76 327.00 m -648.76 325.80 l -648.15 326.70 647.40 327.15 646.53 327.15 c -645.97 327.15 645.53 326.97 645.20 326.62 c -644.88 326.27 644.71 325.80 644.71 325.21 c -644.71 320.64 l -645.87 320.64 l -645.87 324.83 l -645.87 325.31 645.93 325.65 646.07 325.85 c -646.21 326.05 646.44 326.15 646.77 326.15 c -647.47 326.15 648.13 325.69 648.76 324.76 c -648.76 320.64 l -649.91 320.64 l -649.91 327.00 l -h -654.14 327.15 m -653.55 327.15 653.10 326.98 652.77 326.64 c -652.44 326.31 652.28 325.84 652.28 325.24 c -652.28 321.50 l -651.48 321.50 l -651.48 320.64 l -652.28 320.64 l -652.28 319.48 l -653.43 319.37 l -653.43 320.64 l -655.09 320.64 l -655.09 321.50 l -653.43 321.50 l -653.43 325.03 l -653.43 325.86 653.79 326.28 654.51 326.28 c -654.66 326.28 654.85 326.25 655.06 326.20 c -655.06 327.00 l -654.71 327.10 654.40 327.15 654.14 327.15 c -h -656.72 327.00 m -656.72 317.75 l -657.87 317.75 l -657.87 321.83 l -658.48 320.94 659.23 320.50 660.11 320.50 c -660.66 320.50 661.10 320.67 661.43 321.02 c -661.76 321.37 661.92 321.84 661.92 322.43 c -661.92 327.00 l -660.77 327.00 l -660.77 322.80 l -660.77 322.33 660.70 322.00 660.56 321.79 c -660.42 321.59 660.19 321.49 659.87 321.49 c -659.16 321.49 658.50 321.96 657.87 322.88 c -657.87 327.00 l -h -666.65 327.15 m -665.74 327.15 665.02 326.84 664.47 326.24 c -663.93 325.64 663.66 324.83 663.66 323.82 c -663.66 322.79 663.93 321.99 664.48 321.39 c -665.02 320.79 665.76 320.50 666.70 320.50 c -667.63 320.50 668.37 320.79 668.91 321.39 c -669.46 321.99 669.73 322.79 669.73 323.81 c -669.73 324.85 669.46 325.66 668.91 326.26 c -668.36 326.85 667.61 327.15 666.65 327.15 c -h -666.67 326.28 m -667.89 326.28 668.51 325.46 668.51 323.81 c -668.51 322.18 667.90 321.36 666.70 321.36 c -665.49 321.36 664.89 322.18 664.89 323.82 c -664.89 325.46 665.48 326.28 666.67 326.28 c -h -671.54 327.00 m -671.54 320.64 l -672.69 320.64 l -672.69 321.83 l -673.15 320.94 673.81 320.50 674.68 320.50 c -674.80 320.50 674.92 320.51 675.05 320.53 c -675.05 321.60 l -674.85 321.54 674.68 321.50 674.52 321.50 c -673.79 321.50 673.18 321.94 672.69 322.80 c -672.69 327.00 l -h -676.45 327.00 m -676.45 320.64 l -677.60 320.64 l -677.60 327.00 l -h -676.45 319.48 m -676.45 318.33 l -677.60 318.33 l -677.60 319.48 l -h -679.48 327.00 m -679.48 326.13 l -683.42 321.50 l -679.66 321.50 l -679.66 320.64 l -684.84 320.64 l -684.84 321.50 l -680.90 326.13 l -684.92 326.13 l -684.92 327.00 l -h -691.46 326.79 m -690.68 327.03 690.02 327.15 689.47 327.15 c -688.53 327.15 687.77 326.83 687.18 326.21 c -686.58 325.59 686.29 324.78 686.29 323.79 c -686.29 322.82 686.55 322.03 687.07 321.42 c -687.59 320.80 688.26 320.49 689.07 320.49 c -689.84 320.49 690.44 320.76 690.86 321.31 c -691.28 321.86 691.49 322.63 691.49 323.64 c -691.48 324.00 l -687.47 324.00 l -687.63 325.51 688.38 326.27 689.69 326.27 c -690.17 326.27 690.76 326.14 691.46 325.88 c -h -687.52 323.13 m -690.33 323.13 l -690.33 321.95 689.88 321.36 689.00 321.36 c -688.12 321.36 687.62 321.95 687.52 323.13 c -h -697.42 325.05 m -697.42 324.18 l -704.36 324.18 l -704.36 325.05 l -h -697.42 322.88 m -697.42 322.01 l -704.36 322.01 l -704.36 322.88 l -h -713.10 327.15 m -712.24 327.15 711.52 326.83 710.96 326.19 c -710.39 325.55 710.11 324.75 710.11 323.78 c -710.11 322.75 710.39 321.94 710.95 321.36 c -711.51 320.79 712.29 320.50 713.29 320.50 c -713.79 320.50 714.35 320.56 714.96 320.70 c -714.96 321.67 l -714.31 321.48 713.78 321.38 713.37 321.38 c -712.78 321.38 712.30 321.60 711.94 322.05 c -711.59 322.49 711.41 323.08 711.41 323.82 c -711.41 324.53 711.59 325.11 711.96 325.55 c -712.33 325.99 712.81 326.21 713.40 326.21 c -713.93 326.21 714.47 326.08 715.03 325.81 c -715.03 326.81 l -714.28 327.03 713.64 327.15 713.10 327.15 c -h -720.04 326.19 m -719.35 326.83 718.68 327.15 718.04 327.15 c -717.51 327.15 717.08 326.98 716.73 326.65 c -716.38 326.32 716.21 325.90 716.21 325.40 c -716.21 324.71 716.50 324.17 717.08 323.80 c -717.67 323.42 718.50 323.24 719.59 323.24 c -719.87 323.24 l -719.87 322.47 l -719.87 321.73 719.49 321.36 718.73 321.36 c -718.12 321.36 717.46 321.55 716.76 321.93 c -716.76 320.97 l -717.54 320.65 718.26 320.50 718.94 320.50 c -719.65 320.50 720.18 320.66 720.52 320.98 c -720.85 321.30 721.02 321.79 721.02 322.47 c -721.02 325.35 l -721.02 326.01 721.23 326.34 721.63 326.34 c -721.68 326.34 721.76 326.34 721.86 326.32 c -721.94 326.96 l -721.68 327.08 721.39 327.15 721.07 327.15 c -720.53 327.15 720.19 326.83 720.04 326.19 c -h -719.87 325.56 m -719.87 323.92 l -719.48 323.91 l -718.85 323.91 718.34 324.03 717.95 324.27 c -717.56 324.51 717.36 324.82 717.36 325.21 c -717.36 325.49 717.46 325.72 717.65 325.92 c -717.85 326.11 718.09 326.20 718.37 326.20 c -718.85 326.20 719.35 325.99 719.87 325.56 c -h -723.38 327.00 m -723.38 320.64 l -724.54 320.64 l -724.54 321.83 l -725.15 320.94 725.89 320.50 726.78 320.50 c -727.33 320.50 727.77 320.67 728.10 321.02 c -728.42 321.37 728.59 321.84 728.59 322.43 c -728.59 327.00 l -727.43 327.00 l -727.43 322.80 l -727.43 322.33 727.36 322.00 727.23 321.79 c -727.09 321.59 726.86 321.49 726.54 321.49 c -725.83 321.49 725.16 321.96 724.54 322.88 c -724.54 327.00 l -h -729.68 329.02 m -729.68 328.15 l -735.68 328.15 l -735.68 329.02 l -h -736.83 327.00 m -736.83 317.75 l -737.99 317.75 l -737.99 321.83 l -738.60 320.94 739.34 320.50 740.22 320.50 c -740.78 320.50 741.21 320.67 741.54 321.02 c -741.87 321.37 742.04 321.84 742.04 322.43 c -742.04 327.00 l -740.88 327.00 l -740.88 322.80 l -740.88 322.33 740.81 322.00 740.67 321.79 c -740.53 321.59 740.30 321.49 739.98 321.49 c -739.28 321.49 738.61 321.96 737.99 322.88 c -737.99 327.00 l -h -747.56 326.19 m -746.87 326.83 746.20 327.15 745.56 327.15 c -745.04 327.15 744.60 326.98 744.25 326.65 c -743.90 326.32 743.73 325.90 743.73 325.40 c -743.73 324.71 744.02 324.17 744.60 323.80 c -745.19 323.42 746.03 323.24 747.12 323.24 c -747.39 323.24 l -747.39 322.47 l -747.39 321.73 747.01 321.36 746.25 321.36 c -745.64 321.36 744.99 321.55 744.28 321.93 c -744.28 320.97 l -745.06 320.65 745.79 320.50 746.46 320.50 c -747.18 320.50 747.70 320.66 748.04 320.98 c -748.38 321.30 748.54 321.79 748.54 322.47 c -748.54 325.35 l -748.54 326.01 748.75 326.34 749.15 326.34 c -749.21 326.34 749.28 326.34 749.38 326.32 c -749.46 326.96 l -749.20 327.08 748.91 327.15 748.59 327.15 c -748.05 327.15 747.71 326.83 747.56 326.19 c -h -747.39 325.56 m -747.39 323.92 l -747.00 323.91 l -746.37 323.91 745.86 324.03 745.47 324.27 c -745.08 324.51 744.88 324.82 744.88 325.21 c -744.88 325.49 744.98 325.72 745.18 325.92 c -745.37 326.11 745.61 326.20 745.89 326.20 c -746.37 326.20 746.87 325.99 747.39 325.56 c -h -750.47 327.00 m -750.47 326.13 l -754.41 321.50 l -750.65 321.50 l -750.65 320.64 l -755.83 320.64 l -755.83 321.50 l -751.90 326.13 l -755.91 326.13 l -755.91 327.00 l -h -760.10 327.94 m -760.10 328.73 l -759.26 328.16 758.59 327.38 758.09 326.39 c -757.60 325.41 757.35 324.36 757.35 323.24 c -757.35 322.12 757.60 321.08 758.09 320.09 c -758.59 319.10 759.26 318.32 760.10 317.75 c -760.10 318.54 l -759.53 319.17 759.12 319.84 758.87 320.56 c -758.63 321.28 758.51 322.17 758.51 323.24 c -758.51 324.31 758.63 325.20 758.87 325.92 c -759.12 326.64 759.53 327.31 760.10 327.94 c -h -764.17 327.15 m -763.31 327.15 762.60 326.83 762.03 326.19 c -761.47 325.55 761.18 324.75 761.18 323.78 c -761.18 322.75 761.46 321.94 762.02 321.36 c -762.58 320.79 763.37 320.50 764.37 320.50 c -764.87 320.50 765.42 320.56 766.04 320.70 c -766.04 321.67 l -765.38 321.48 764.85 321.38 764.44 321.38 c -763.85 321.38 763.38 321.60 763.02 322.05 c -762.66 322.49 762.48 323.08 762.48 323.82 c -762.48 324.53 762.67 325.11 763.04 325.55 c -763.40 325.99 763.88 326.21 764.48 326.21 c -765.00 326.21 765.55 326.08 766.11 325.81 c -766.11 326.81 l -765.36 327.03 764.71 327.15 764.17 327.15 c -h -770.32 327.15 m -769.41 327.15 768.69 326.84 768.14 326.24 c -767.60 325.64 767.33 324.83 767.33 323.82 c -767.33 322.79 767.60 321.99 768.15 321.39 c -768.69 320.79 769.43 320.50 770.37 320.50 c -771.30 320.50 772.04 320.79 772.58 321.39 c -773.13 321.99 773.40 322.79 773.40 323.81 c -773.40 324.85 773.13 325.66 772.58 326.26 c -772.03 326.85 771.28 327.15 770.32 327.15 c -h -770.34 326.28 m -771.56 326.28 772.18 325.46 772.18 323.81 c -772.18 322.18 771.57 321.36 770.37 321.36 c -769.16 321.36 768.56 322.18 768.56 323.82 c -768.56 325.46 769.15 326.28 770.34 326.28 c -h -775.21 327.00 m -775.21 320.64 l -776.36 320.64 l -776.36 321.83 l -776.97 320.94 777.71 320.50 778.60 320.50 c -779.15 320.50 779.59 320.67 779.92 321.02 c -780.24 321.37 780.41 321.84 780.41 322.43 c -780.41 327.00 l -779.25 327.00 l -779.25 322.80 l -779.25 322.33 779.18 322.00 779.05 321.79 c -778.91 321.59 778.68 321.49 778.36 321.49 c -777.65 321.49 776.98 321.96 776.36 322.88 c -776.36 327.00 l -h -784.56 327.15 m -783.98 327.15 783.52 326.98 783.19 326.64 c -782.86 326.31 782.70 325.84 782.70 325.24 c -782.70 321.50 l -781.90 321.50 l -781.90 320.64 l -782.70 320.64 l -782.70 319.48 l -783.85 319.37 l -783.85 320.64 l -785.52 320.64 l -785.52 321.50 l -783.85 321.50 l -783.85 325.03 l -783.85 325.86 784.21 326.28 784.93 326.28 c -785.08 326.28 785.27 326.25 785.49 326.20 c -785.49 327.00 l -785.13 327.10 784.82 327.15 784.56 327.15 c -h -791.80 326.79 m -791.03 327.03 790.37 327.15 789.82 327.15 c -788.88 327.15 788.12 326.83 787.52 326.21 c -786.93 325.59 786.64 324.78 786.64 323.79 c -786.64 322.82 786.90 322.03 787.42 321.42 c -787.94 320.80 788.61 320.49 789.42 320.49 c -790.19 320.49 790.78 320.76 791.20 321.31 c -791.62 321.86 791.83 322.63 791.83 323.64 c -791.83 324.00 l -787.81 324.00 l -787.98 325.51 788.72 326.27 790.04 326.27 c -790.52 326.27 791.11 326.14 791.80 325.88 c -h -787.87 323.13 m -790.67 323.13 l -790.67 321.95 790.23 321.36 789.35 321.36 c -788.46 321.36 787.97 321.95 787.87 323.13 c -h -793.19 327.00 m -795.61 323.71 l -793.26 320.64 l -794.63 320.64 l -796.49 323.09 l -798.17 320.64 l -799.29 320.64 l -797.09 323.87 l -799.49 327.00 l -798.12 327.00 l -796.20 324.48 l -794.35 327.00 l -h -803.10 327.15 m -802.51 327.15 802.05 326.98 801.72 326.64 c -801.40 326.31 801.23 325.84 801.23 325.24 c -801.23 321.50 l -800.44 321.50 l -800.44 320.64 l -801.23 320.64 l -801.23 319.48 l -802.39 319.37 l -802.39 320.64 l -804.05 320.64 l -804.05 321.50 l -802.39 321.50 l -802.39 325.03 l -802.39 325.86 802.75 326.28 803.46 326.28 c -803.62 326.28 803.80 326.25 804.02 326.20 c -804.02 327.00 l -803.67 327.10 803.36 327.15 803.10 327.15 c -h -805.69 328.88 m -805.69 328.45 l -806.07 328.34 806.25 327.90 806.25 327.12 c -806.25 327.00 l -805.69 327.00 l -805.69 325.55 l -807.14 325.55 l -807.14 326.81 l -807.14 328.09 806.66 328.78 805.69 328.88 c -h -817.25 327.00 m -817.25 325.80 l -816.63 326.70 815.89 327.15 815.01 327.15 c -814.46 327.15 814.02 326.97 813.69 326.62 c -813.36 326.27 813.20 325.80 813.20 325.21 c -813.20 320.64 l -814.35 320.64 l -814.35 324.83 l -814.35 325.31 814.42 325.65 814.56 325.85 c -814.70 326.05 814.93 326.15 815.25 326.15 c -815.96 326.15 816.62 325.69 817.25 324.76 c -817.25 320.64 l -818.40 320.64 l -818.40 327.00 l -h -822.40 327.15 m -821.88 327.15 821.23 327.02 820.48 326.78 c -820.48 325.72 l -821.23 326.09 821.89 326.28 822.45 326.28 c -822.78 326.28 823.06 326.19 823.28 326.01 c -823.49 325.83 823.60 325.61 823.60 325.34 c -823.60 324.94 823.30 324.62 822.68 324.36 c -822.01 324.07 l -821.01 323.66 820.52 323.06 820.52 322.28 c -820.52 321.73 820.71 321.29 821.10 320.97 c -821.50 320.66 822.04 320.50 822.72 320.50 c -823.07 320.50 823.51 320.54 824.04 320.64 c -824.28 320.69 l -824.28 321.65 l -823.63 321.46 823.12 321.36 822.74 321.36 c -822.00 321.36 821.63 321.63 821.63 322.17 c -821.63 322.52 821.91 322.81 822.47 323.05 c -823.03 323.29 l -823.66 323.55 824.10 323.83 824.37 324.13 c -824.63 324.42 824.76 324.79 824.76 325.23 c -824.76 325.79 824.54 326.25 824.10 326.61 c -823.65 326.97 823.09 327.15 822.40 327.15 c -h -831.50 326.79 m -830.72 327.03 830.06 327.15 829.51 327.15 c -828.57 327.15 827.81 326.83 827.22 326.21 c -826.62 325.59 826.33 324.78 826.33 323.79 c -826.33 322.82 826.59 322.03 827.11 321.42 c -827.63 320.80 828.30 320.49 829.11 320.49 c -829.88 320.49 830.48 320.76 830.90 321.31 c -831.32 321.86 831.53 322.63 831.53 323.64 c -831.52 324.00 l -827.51 324.00 l -827.67 325.51 828.41 326.27 829.73 326.27 c -830.21 326.27 830.80 326.14 831.50 325.88 c -h -827.56 323.13 m -830.37 323.13 l -830.37 321.95 829.92 321.36 829.04 321.36 c -828.15 321.36 827.66 321.95 827.56 323.13 c -h -833.52 327.00 m -833.52 320.64 l -834.67 320.64 l -834.67 321.83 l -835.13 320.94 835.79 320.50 836.66 320.50 c -836.78 320.50 836.90 320.51 837.03 320.53 c -837.03 321.60 l -836.83 321.54 836.66 321.50 836.51 321.50 c -835.78 321.50 835.16 321.94 834.67 322.80 c -834.67 327.00 l -h -838.45 328.88 m -838.45 328.45 l -838.82 328.34 839.01 327.90 839.01 327.12 c -839.01 327.00 l -838.45 327.00 l -838.45 325.55 l -839.89 325.55 l -839.89 326.81 l -839.89 328.09 839.41 328.78 838.45 328.88 c -h -845.80 320.93 m -845.52 317.75 l -846.96 317.75 l -846.67 320.93 l -h -851.25 327.15 m -850.39 327.15 849.68 326.83 849.12 326.19 c -848.55 325.55 848.27 324.75 848.27 323.78 c -848.27 322.75 848.55 321.94 849.11 321.36 c -849.67 320.79 850.45 320.50 851.45 320.50 c -851.95 320.50 852.50 320.56 853.12 320.70 c -853.12 321.67 l -852.46 321.48 851.93 321.38 851.52 321.38 c -850.93 321.38 850.46 321.60 850.10 322.05 c -849.75 322.49 849.57 323.08 849.57 323.82 c -849.57 324.53 849.75 325.11 850.12 325.55 c -850.48 325.99 850.96 326.21 851.56 326.21 c -852.09 326.21 852.63 326.08 853.19 325.81 c -853.19 326.81 l -852.44 327.03 851.80 327.15 851.25 327.15 c -h -854.92 327.00 m -854.92 320.64 l -856.07 320.64 l -856.07 321.83 l -856.53 320.94 857.19 320.50 858.06 320.50 c -858.18 320.50 858.30 320.51 858.43 320.53 c -858.43 321.60 l -858.23 321.54 858.06 321.50 857.90 321.50 c -857.17 321.50 856.56 321.94 856.07 322.80 c -856.07 327.00 l -h -864.49 326.79 m -863.72 327.03 863.05 327.15 862.50 327.15 c -861.57 327.15 860.80 326.83 860.21 326.21 c -859.62 325.59 859.32 324.78 859.32 323.79 c -859.32 322.82 859.58 322.03 860.10 321.42 c -860.63 320.80 861.29 320.49 862.11 320.49 c -862.88 320.49 863.47 320.76 863.89 321.31 c -864.31 321.86 864.52 322.63 864.52 323.64 c -864.51 324.00 l -860.50 324.00 l -860.67 325.51 861.41 326.27 862.72 326.27 c -863.20 326.27 863.79 326.14 864.49 325.88 c -h -860.55 323.13 m -863.36 323.13 l -863.36 321.95 862.92 321.36 862.04 321.36 c -861.15 321.36 860.65 321.95 860.55 323.13 c -h -869.79 326.19 m -869.10 326.83 868.44 327.15 867.79 327.15 c -867.27 327.15 866.83 326.98 866.48 326.65 c -866.13 326.32 865.96 325.90 865.96 325.40 c -865.96 324.71 866.25 324.17 866.84 323.80 c -867.42 323.42 868.26 323.24 869.35 323.24 c -869.62 323.24 l -869.62 322.47 l -869.62 321.73 869.24 321.36 868.49 321.36 c -867.88 321.36 867.22 321.55 866.51 321.93 c -866.51 320.97 l -867.29 320.65 868.02 320.50 868.70 320.50 c -869.41 320.50 869.93 320.66 870.27 320.98 c -870.61 321.30 870.78 321.79 870.78 322.47 c -870.78 325.35 l -870.78 326.01 870.98 326.34 871.39 326.34 c -871.44 326.34 871.51 326.34 871.61 326.32 c -871.69 326.96 l -871.43 327.08 871.14 327.15 870.82 327.15 c -870.29 327.15 869.94 326.83 869.79 326.19 c -h -869.62 325.56 m -869.62 323.92 l -869.24 323.91 l -868.60 323.91 868.09 324.03 867.70 324.27 c -867.31 324.51 867.12 324.82 867.12 325.21 c -867.12 325.49 867.21 325.72 867.41 325.92 c -867.60 326.11 867.84 326.20 868.12 326.20 c -868.60 326.20 869.10 325.99 869.62 325.56 c -h -875.05 327.15 m -874.46 327.15 874.01 326.98 873.68 326.64 c -873.35 326.31 873.19 325.84 873.19 325.24 c -873.19 321.50 l -872.39 321.50 l -872.39 320.64 l -873.19 320.64 l -873.19 319.48 l -874.34 319.37 l -874.34 320.64 l -876.00 320.64 l -876.00 321.50 l -874.34 321.50 l -874.34 325.03 l -874.34 325.86 874.70 326.28 875.42 326.28 c -875.57 326.28 875.76 326.25 875.97 326.20 c -875.97 327.00 l -875.62 327.10 875.31 327.15 875.05 327.15 c -h -882.29 326.79 m -881.52 327.03 880.86 327.15 880.30 327.15 c -879.37 327.15 878.60 326.83 878.01 326.21 c -877.42 325.59 877.12 324.78 877.12 323.79 c -877.12 322.82 877.38 322.03 877.91 321.42 c -878.43 320.80 879.09 320.49 879.91 320.49 c -880.68 320.49 881.27 320.76 881.69 321.31 c -882.11 321.86 882.32 322.63 882.32 323.64 c -882.31 324.00 l -878.30 324.00 l -878.47 325.51 879.21 326.27 880.52 326.27 c -881.00 326.27 881.59 326.14 882.29 325.88 c -h -878.35 323.13 m -881.16 323.13 l -881.16 321.95 880.72 321.36 879.84 321.36 c -878.95 321.36 878.46 321.95 878.35 323.13 c -h -883.16 329.02 m -883.16 328.15 l -889.16 328.15 l -889.16 329.02 l -h -890.31 327.00 m -890.31 320.64 l -891.47 320.64 l -891.47 327.00 l -h -890.31 319.48 m -890.31 318.33 l -891.47 318.33 l -891.47 319.48 l -h -893.78 327.00 m -893.78 320.64 l -894.94 320.64 l -894.94 321.83 l -895.54 320.94 896.29 320.50 897.17 320.50 c -897.72 320.50 898.16 320.67 898.49 321.02 c -898.82 321.37 898.98 321.84 898.98 322.43 c -898.98 327.00 l -897.83 327.00 l -897.83 322.80 l -897.83 322.33 897.76 322.00 897.62 321.79 c -897.48 321.59 897.25 321.49 896.93 321.49 c -896.23 321.49 895.56 321.96 894.94 322.88 c -894.94 327.00 l -h -902.92 327.15 m -902.39 327.15 901.75 327.02 900.99 326.78 c -900.99 325.72 l -901.75 326.09 902.40 326.28 902.96 326.28 c -903.29 326.28 903.57 326.19 903.79 326.01 c -904.01 325.83 904.12 325.61 904.12 325.34 c -904.12 324.94 903.81 324.62 903.20 324.36 c -902.52 324.07 l -901.53 323.66 901.03 323.06 901.03 322.28 c -901.03 321.73 901.23 321.29 901.62 320.97 c -902.01 320.66 902.55 320.50 903.23 320.50 c -903.59 320.50 904.03 320.54 904.55 320.64 c -904.79 320.69 l -904.79 321.65 l -904.15 321.46 903.63 321.36 903.26 321.36 c -902.51 321.36 902.14 321.63 902.14 322.17 c -902.14 322.52 902.42 322.81 902.99 323.05 c -903.54 323.29 l -904.17 323.55 904.62 323.83 904.88 324.13 c -905.14 324.42 905.27 324.79 905.27 325.23 c -905.27 325.79 905.05 326.25 904.61 326.61 c -904.17 326.97 903.60 327.15 902.92 327.15 c -h -909.26 327.15 m -908.67 327.15 908.21 326.98 907.88 326.64 c -907.56 326.31 907.39 325.84 907.39 325.24 c -907.39 321.50 l -906.60 321.50 l -906.60 320.64 l -907.39 320.64 l -907.39 319.48 l -908.55 319.37 l -908.55 320.64 l -910.21 320.64 l -910.21 321.50 l -908.55 321.50 l -908.55 325.03 l -908.55 325.86 908.91 326.28 909.62 326.28 c -909.78 326.28 909.96 326.25 910.18 326.20 c -910.18 327.00 l -909.83 327.10 909.52 327.15 909.26 327.15 c -h -915.12 326.19 m -914.42 326.83 913.76 327.15 913.12 327.15 c -912.59 327.15 912.15 326.98 911.80 326.65 c -911.46 326.32 911.28 325.90 911.28 325.40 c -911.28 324.71 911.58 324.17 912.16 323.80 c -912.74 323.42 913.58 323.24 914.67 323.24 c -914.95 323.24 l -914.95 322.47 l -914.95 321.73 914.57 321.36 913.81 321.36 c -913.20 321.36 912.54 321.55 911.83 321.93 c -911.83 320.97 l -912.61 320.65 913.34 320.50 914.02 320.50 c -914.73 320.50 915.25 320.66 915.59 320.98 c -915.93 321.30 916.10 321.79 916.10 322.47 c -916.10 325.35 l -916.10 326.01 916.30 326.34 916.71 326.34 c -916.76 326.34 916.83 326.34 916.93 326.32 c -917.01 326.96 l -916.75 327.08 916.46 327.15 916.15 327.15 c -915.61 327.15 915.26 326.83 915.12 326.19 c -h -914.95 325.56 m -914.95 323.92 l -914.56 323.91 l -913.93 323.91 913.41 324.03 913.02 324.27 c -912.63 324.51 912.44 324.82 912.44 325.21 c -912.44 325.49 912.54 325.72 912.73 325.92 c -912.93 326.11 913.16 326.20 913.45 326.20 c -913.93 326.20 914.43 325.99 914.95 325.56 c -h -918.46 327.00 m -918.46 320.64 l -919.62 320.64 l -919.62 321.83 l -920.22 320.94 920.97 320.50 921.85 320.50 c -922.40 320.50 922.84 320.67 923.17 321.02 c -923.50 321.37 923.66 321.84 923.66 322.43 c -923.66 327.00 l -922.51 327.00 l -922.51 322.80 l -922.51 322.33 922.44 322.00 922.30 321.79 c -922.16 321.59 921.93 321.49 921.61 321.49 c -920.91 321.49 920.24 321.96 919.62 322.88 c -919.62 327.00 l -h -928.39 327.15 m -927.53 327.15 926.82 326.83 926.25 326.19 c -925.69 325.55 925.40 324.75 925.40 323.78 c -925.40 322.75 925.68 321.94 926.25 321.36 c -926.81 320.79 927.59 320.50 928.59 320.50 c -929.09 320.50 929.64 320.56 930.26 320.70 c -930.26 321.67 l -929.60 321.48 929.07 321.38 928.66 321.38 c -928.07 321.38 927.60 321.60 927.24 322.05 c -926.88 322.49 926.71 323.08 926.71 323.82 c -926.71 324.53 926.89 325.11 927.26 325.55 c -927.62 325.99 928.10 326.21 928.70 326.21 c -929.22 326.21 929.77 326.08 930.33 325.81 c -930.33 326.81 l -929.58 327.03 928.94 327.15 928.39 327.15 c -h -936.72 326.79 m -935.95 327.03 935.28 327.15 934.73 327.15 c -933.79 327.15 933.03 326.83 932.44 326.21 c -931.85 325.59 931.55 324.78 931.55 323.79 c -931.55 322.82 931.81 322.03 932.33 321.42 c -932.85 320.80 933.52 320.49 934.33 320.49 c -935.10 320.49 935.70 320.76 936.12 321.31 c -936.54 321.86 936.75 322.63 936.75 323.64 c -936.74 324.00 l -932.73 324.00 l -932.90 325.51 933.64 326.27 934.95 326.27 c -935.43 326.27 936.02 326.14 936.72 325.88 c -h -932.78 323.13 m -935.59 323.13 l -935.59 321.95 935.15 321.36 934.26 321.36 c -933.38 321.36 932.88 321.95 932.78 323.13 c -h -938.52 320.93 m -938.24 317.75 l -939.68 317.75 l -939.39 320.93 l -h -941.51 328.88 m -941.51 328.45 l -941.88 328.34 942.07 327.90 942.07 327.12 c -942.07 327.00 l -941.51 327.00 l -941.51 325.55 l -942.95 325.55 l -942.95 326.81 l -942.95 328.09 942.47 328.78 941.51 328.88 c -h -950.99 327.15 m -950.41 327.15 949.95 326.98 949.62 326.64 c -949.29 326.31 949.13 325.84 949.13 325.24 c -949.13 321.50 l -948.33 321.50 l -948.33 320.64 l -949.13 320.64 l -949.13 319.48 l -950.28 319.37 l -950.28 320.64 l -951.95 320.64 l -951.95 321.50 l -950.28 321.50 l -950.28 325.03 l -950.28 325.86 950.64 326.28 951.36 326.28 c -951.51 326.28 951.70 326.25 951.92 326.20 c -951.92 327.00 l -951.56 327.10 951.25 327.15 950.99 327.15 c -h -958.23 326.79 m -957.46 327.03 956.80 327.15 956.25 327.15 c -955.31 327.15 954.55 326.83 953.95 326.21 c -953.36 325.59 953.07 324.78 953.07 323.79 c -953.07 322.82 953.33 322.03 953.85 321.42 c -954.37 320.80 955.04 320.49 955.85 320.49 c -956.62 320.49 957.21 320.76 957.63 321.31 c -958.05 321.86 958.26 322.63 958.26 323.64 c -958.26 324.00 l -954.24 324.00 l -954.41 325.51 955.15 326.27 956.46 326.27 c -956.95 326.27 957.54 326.14 958.23 325.88 c -h -954.30 323.13 m -957.10 323.13 l -957.10 321.95 956.66 321.36 955.78 321.36 c -954.89 321.36 954.40 321.95 954.30 323.13 c -h -960.26 327.00 m -960.26 320.64 l -961.41 320.64 l -961.41 321.83 l -962.02 320.94 962.77 320.50 963.65 320.50 c -964.20 320.50 964.64 320.67 964.97 321.02 c -965.29 321.37 965.46 321.84 965.46 322.43 c -965.46 327.00 l -964.30 327.00 l -964.30 322.80 l -964.30 322.33 964.24 322.00 964.10 321.79 c -963.96 321.59 963.73 321.49 963.41 321.49 c -962.70 321.49 962.04 321.96 961.41 322.88 c -961.41 327.00 l -h -970.98 326.19 m -970.29 326.83 969.63 327.15 968.99 327.15 c -968.46 327.15 968.02 326.98 967.67 326.65 c -967.33 326.32 967.15 325.90 967.15 325.40 c -967.15 324.71 967.44 324.17 968.03 323.80 c -968.61 323.42 969.45 323.24 970.54 323.24 c -970.81 323.24 l -970.81 322.47 l -970.81 321.73 970.44 321.36 969.68 321.36 c -969.07 321.36 968.41 321.55 967.70 321.93 c -967.70 320.97 l -968.48 320.65 969.21 320.50 969.89 320.50 c -970.60 320.50 971.12 320.66 971.46 320.98 c -971.80 321.30 971.97 321.79 971.97 322.47 c -971.97 325.35 l -971.97 326.01 972.17 326.34 972.58 326.34 c -972.63 326.34 972.70 326.34 972.80 326.32 c -972.88 326.96 l -972.62 327.08 972.33 327.15 972.02 327.15 c -971.48 327.15 971.13 326.83 970.98 326.19 c -h -970.81 325.56 m -970.81 323.92 l -970.43 323.91 l -969.79 323.91 969.28 324.03 968.89 324.27 c -968.50 324.51 968.31 324.82 968.31 325.21 c -968.31 325.49 968.40 325.72 968.60 325.92 c -968.79 326.11 969.03 326.20 969.31 326.20 c -969.79 326.20 970.29 325.99 970.81 325.56 c -h -974.33 327.00 m -974.33 320.64 l -975.48 320.64 l -975.48 321.83 l -976.09 320.94 976.84 320.50 977.72 320.50 c -978.27 320.50 978.71 320.67 979.04 321.02 c -979.37 321.37 979.53 321.84 979.53 322.43 c -979.53 327.00 l -978.38 327.00 l -978.38 322.80 l -978.38 322.33 978.31 322.00 978.17 321.79 c -978.03 321.59 977.80 321.49 977.48 321.49 c -976.78 321.49 976.11 321.96 975.48 322.88 c -975.48 327.00 l -h -983.69 327.15 m -983.10 327.15 982.64 326.98 982.32 326.64 c -981.99 326.31 981.82 325.84 981.82 325.24 c -981.82 321.50 l -981.03 321.50 l -981.03 320.64 l -981.82 320.64 l -981.82 319.48 l -982.98 319.37 l -982.98 320.64 l -984.64 320.64 l -984.64 321.50 l -982.98 321.50 l -982.98 325.03 l -982.98 325.86 983.34 326.28 984.06 326.28 c -984.21 326.28 984.39 326.25 984.61 326.20 c -984.61 327.00 l -984.26 327.10 983.95 327.15 983.69 327.15 c -h -985.11 329.02 m -985.11 328.15 l -991.11 328.15 l -991.11 329.02 l -h -992.27 327.00 m -992.27 320.64 l -993.42 320.64 l -993.42 327.00 l -h -992.27 319.48 m -992.27 318.33 l -993.42 318.33 l -993.42 319.48 l -h -999.82 327.00 m -999.82 325.80 l -999.35 326.70 998.64 327.15 997.70 327.15 c -996.93 327.15 996.33 326.87 995.89 326.31 c -995.45 325.75 995.23 324.99 995.23 324.02 c -995.23 322.96 995.48 322.11 995.98 321.46 c -996.48 320.82 997.13 320.50 997.94 320.50 c -998.70 320.50 999.32 320.79 999.82 321.36 c -999.82 317.75 l -1001.0 317.75 l -1001.0 327.00 l -h -999.82 322.15 m -999.22 321.63 998.65 321.36 998.12 321.36 c -997.01 321.36 996.46 322.21 996.46 323.90 c -996.46 325.39 996.95 326.13 997.94 326.13 c -998.58 326.13 999.21 325.78 999.82 325.08 c -h -1002.6 327.94 m -1002.6 328.73 l -1003.4 328.16 1004.1 327.38 1004.6 326.39 c -1005.1 325.41 1005.3 324.36 1005.3 323.24 c -1005.3 322.12 1005.1 321.08 1004.6 320.09 c -1004.1 319.10 1003.4 318.32 1002.6 317.75 c -1002.6 318.54 l -1003.1 319.17 1003.5 319.84 1003.8 320.56 c -1004.0 321.28 1004.2 322.17 1004.2 323.24 c -1004.2 324.31 1004.0 325.20 1003.8 325.92 c -1003.5 326.64 1003.1 327.31 1002.6 327.94 c -h -f -642.82 346.79 m -642.04 347.03 641.38 347.15 640.83 347.15 c -639.89 347.15 639.13 346.83 638.54 346.21 c -637.95 345.59 637.65 344.78 637.65 343.79 c -637.65 342.82 637.91 342.03 638.43 341.42 c -638.95 340.80 639.62 340.49 640.43 340.49 c -641.20 340.49 641.80 340.76 642.22 341.31 c -642.64 341.86 642.85 342.63 642.85 343.64 c -642.84 344.00 l -638.83 344.00 l -639.00 345.51 639.74 346.27 641.05 346.27 c -641.53 346.27 642.12 346.14 642.82 345.88 c -h -638.88 343.13 m -641.69 343.13 l -641.69 341.95 641.25 341.36 640.36 341.36 c -639.48 341.36 638.98 341.95 638.88 343.13 c -h -644.20 347.00 m -646.62 343.71 l -644.27 340.64 l -645.64 340.64 l -647.50 343.09 l -649.18 340.64 l -650.31 340.64 l -648.10 343.87 l -650.50 347.00 l -649.13 347.00 l -647.21 344.48 l -645.36 347.00 l -h -656.86 346.79 m -656.09 347.03 655.43 347.15 654.88 347.15 c -653.94 347.15 653.17 346.83 652.58 346.21 c -651.99 345.59 651.70 344.78 651.70 343.79 c -651.70 342.82 651.96 342.03 652.48 341.42 c -653.00 340.80 653.67 340.49 654.48 340.49 c -655.25 340.49 655.84 340.76 656.26 341.31 c -656.68 341.86 656.89 342.63 656.89 343.64 c -656.89 344.00 l -652.87 344.00 l -653.04 345.51 653.78 346.27 655.09 346.27 c -655.57 346.27 656.16 346.14 656.86 345.88 c -h -652.93 343.13 m -655.73 343.13 l -655.73 341.95 655.29 341.36 654.41 341.36 c -653.52 341.36 653.03 341.95 652.93 343.13 c -h -661.37 347.15 m -660.51 347.15 659.80 346.83 659.23 346.19 c -658.66 345.55 658.38 344.75 658.38 343.78 c -658.38 342.75 658.66 341.94 659.22 341.36 c -659.78 340.79 660.56 340.50 661.57 340.50 c -662.06 340.50 662.62 340.56 663.23 340.70 c -663.23 341.67 l -662.58 341.48 662.05 341.38 661.64 341.38 c -661.05 341.38 660.58 341.60 660.22 342.05 c -659.86 342.49 659.68 343.08 659.68 343.82 c -659.68 344.53 659.87 345.11 660.23 345.55 c -660.60 345.99 661.08 346.21 661.67 346.21 c -662.20 346.21 662.74 346.08 663.30 345.81 c -663.30 346.81 l -662.56 347.03 661.91 347.15 661.37 347.15 c -h -669.01 347.00 m -669.01 345.80 l -668.40 346.70 667.65 347.15 666.78 347.15 c -666.22 347.15 665.78 346.97 665.45 346.62 c -665.12 346.27 664.96 345.80 664.96 345.21 c -664.96 340.64 l -666.12 340.64 l -666.12 344.83 l -666.12 345.31 666.18 345.65 666.32 345.85 c -666.46 346.05 666.69 346.15 667.02 346.15 c -667.72 346.15 668.38 345.69 669.01 344.76 c -669.01 340.64 l -670.16 340.64 l -670.16 347.00 l -h -674.39 347.15 m -673.80 347.15 673.35 346.98 673.02 346.64 c -672.69 346.31 672.53 345.84 672.53 345.24 c -672.53 341.50 l -671.73 341.50 l -671.73 340.64 l -672.53 340.64 l -672.53 339.48 l -673.68 339.37 l -673.68 340.64 l -675.34 340.64 l -675.34 341.50 l -673.68 341.50 l -673.68 345.03 l -673.68 345.86 674.04 346.28 674.76 346.28 c -674.91 346.28 675.10 346.25 675.31 346.20 c -675.31 347.00 l -674.96 347.10 674.65 347.15 674.39 347.15 c -h -681.63 346.79 m -680.86 347.03 680.20 347.15 679.64 347.15 c -678.71 347.15 677.94 346.83 677.35 346.21 c -676.76 345.59 676.46 344.78 676.46 343.79 c -676.46 342.82 676.72 342.03 677.25 341.42 c -677.77 340.80 678.43 340.49 679.25 340.49 c -680.02 340.49 680.61 340.76 681.03 341.31 c -681.45 341.86 681.66 342.63 681.66 343.64 c -681.65 344.00 l -677.64 344.00 l -677.81 345.51 678.55 346.27 679.86 346.27 c -680.34 346.27 680.93 346.14 681.63 345.88 c -h -677.69 343.13 m -680.50 343.13 l -680.50 341.95 680.06 341.36 679.18 341.36 c -678.29 341.36 677.79 341.95 677.69 343.13 c -h -689.93 347.15 m -689.07 347.15 688.36 346.83 687.79 346.19 c -687.23 345.55 686.95 344.75 686.95 343.78 c -686.95 342.75 687.23 341.94 687.79 341.36 c -688.35 340.79 689.13 340.50 690.13 340.50 c -690.63 340.50 691.18 340.56 691.80 340.70 c -691.80 341.67 l -691.14 341.48 690.61 341.38 690.20 341.38 c -689.61 341.38 689.14 341.60 688.78 342.05 c -688.42 342.49 688.25 343.08 688.25 343.82 c -688.25 344.53 688.43 345.11 688.80 345.55 c -689.16 345.99 689.64 346.21 690.24 346.21 c -690.77 346.21 691.31 346.08 691.87 345.81 c -691.87 346.81 l -691.12 347.03 690.48 347.15 689.93 347.15 c -h -693.60 347.00 m -693.60 340.64 l -694.75 340.64 l -694.75 341.83 l -695.21 340.94 695.87 340.50 696.74 340.50 c -696.86 340.50 696.98 340.51 697.11 340.53 c -697.11 341.60 l -696.91 341.54 696.74 341.50 696.58 341.50 c -695.85 341.50 695.24 341.94 694.75 342.80 c -694.75 347.00 l -h -703.17 346.79 m -702.40 347.03 701.73 347.15 701.18 347.15 c -700.25 347.15 699.48 346.83 698.89 346.21 c -698.30 345.59 698.00 344.78 698.00 343.79 c -698.00 342.82 698.26 342.03 698.78 341.42 c -699.31 340.80 699.97 340.49 700.79 340.49 c -701.55 340.49 702.15 340.76 702.57 341.31 c -702.99 341.86 703.20 342.63 703.20 343.64 c -703.19 344.00 l -699.18 344.00 l -699.35 345.51 700.09 346.27 701.40 346.27 c -701.88 346.27 702.47 346.14 703.17 345.88 c -h -699.23 343.13 m -702.04 343.13 l -702.04 341.95 701.60 341.36 700.71 341.36 c -699.83 341.36 699.33 341.95 699.23 343.13 c -h -708.47 346.19 m -707.78 346.83 707.12 347.15 706.47 347.15 c -705.95 347.15 705.51 346.98 705.16 346.65 c -704.81 346.32 704.64 345.90 704.64 345.40 c -704.64 344.71 704.93 344.17 705.52 343.80 c -706.10 343.42 706.94 343.24 708.03 343.24 c -708.30 343.24 l -708.30 342.47 l -708.30 341.73 707.92 341.36 707.17 341.36 c -706.56 341.36 705.90 341.55 705.19 341.93 c -705.19 340.97 l -705.97 340.65 706.70 340.50 707.38 340.50 c -708.09 340.50 708.61 340.66 708.95 340.98 c -709.29 341.30 709.46 341.79 709.46 342.47 c -709.46 345.35 l -709.46 346.01 709.66 346.34 710.07 346.34 c -710.12 346.34 710.19 346.34 710.29 346.32 c -710.37 346.96 l -710.11 347.08 709.82 347.15 709.50 347.15 c -708.96 347.15 708.62 346.83 708.47 346.19 c -h -708.30 345.56 m -708.30 343.92 l -707.92 343.91 l -707.28 343.91 706.77 344.03 706.38 344.27 c -705.99 344.51 705.79 344.82 705.79 345.21 c -705.79 345.49 705.89 345.72 706.09 345.92 c -706.28 346.11 706.52 346.20 706.80 346.20 c -707.28 346.20 707.78 345.99 708.30 345.56 c -h -713.73 347.15 m -713.14 347.15 712.69 346.98 712.36 346.64 c -712.03 346.31 711.87 345.84 711.87 345.24 c -711.87 341.50 l -711.07 341.50 l -711.07 340.64 l -711.87 340.64 l -711.87 339.48 l -713.02 339.37 l -713.02 340.64 l -714.68 340.64 l -714.68 341.50 l -713.02 341.50 l -713.02 345.03 l -713.02 345.86 713.38 346.28 714.10 346.28 c -714.25 346.28 714.44 346.25 714.65 346.20 c -714.65 347.00 l -714.30 347.10 713.99 347.15 713.73 347.15 c -h -720.97 346.79 m -720.20 347.03 719.54 347.15 718.98 347.15 c -718.05 347.15 717.28 346.83 716.69 346.21 c -716.10 345.59 715.80 344.78 715.80 343.79 c -715.80 342.82 716.06 342.03 716.58 341.42 c -717.11 340.80 717.77 340.49 718.59 340.49 c -719.36 340.49 719.95 340.76 720.37 341.31 c -720.79 341.86 721.00 342.63 721.00 343.64 c -720.99 344.00 l -716.98 344.00 l -717.15 345.51 717.89 346.27 719.20 346.27 c -719.68 346.27 720.27 346.14 720.97 345.88 c -h -717.03 343.13 m -719.84 343.13 l -719.84 341.95 719.40 341.36 718.52 341.36 c -717.63 341.36 717.13 341.95 717.03 343.13 c -h -721.84 349.02 m -721.84 348.15 l -727.84 348.15 l -727.84 349.02 l -h -728.99 347.00 m -728.99 340.64 l -730.15 340.64 l -730.15 347.00 l -h -728.99 339.48 m -728.99 338.33 l -730.15 338.33 l -730.15 339.48 l -h -732.46 347.00 m -732.46 340.64 l -733.62 340.64 l -733.62 341.83 l -734.22 340.94 734.97 340.50 735.85 340.50 c -736.40 340.50 736.84 340.67 737.17 341.02 c -737.50 341.37 737.66 341.84 737.66 342.43 c -737.66 347.00 l -736.51 347.00 l -736.51 342.80 l -736.51 342.33 736.44 342.00 736.30 341.79 c -736.16 341.59 735.93 341.49 735.61 341.49 c -734.91 341.49 734.24 341.96 733.62 342.88 c -733.62 347.00 l -h -741.60 347.15 m -741.07 347.15 740.43 347.02 739.67 346.78 c -739.67 345.72 l -740.43 346.09 741.08 346.28 741.64 346.28 c -741.97 346.28 742.25 346.19 742.47 346.01 c -742.69 345.83 742.80 345.61 742.80 345.34 c -742.80 344.94 742.49 344.62 741.88 344.36 c -741.20 344.07 l -740.21 343.66 739.71 343.06 739.71 342.28 c -739.71 341.73 739.91 341.29 740.30 340.97 c -740.69 340.66 741.23 340.50 741.91 340.50 c -742.27 340.50 742.71 340.54 743.23 340.64 c -743.47 340.69 l -743.47 341.65 l -742.83 341.46 742.31 341.36 741.94 341.36 c -741.19 341.36 740.82 341.63 740.82 342.17 c -740.82 342.52 741.10 342.81 741.67 343.05 c -742.22 343.29 l -742.85 343.55 743.30 343.83 743.56 344.13 c -743.82 344.42 743.95 344.79 743.95 345.23 c -743.95 345.79 743.73 346.25 743.29 346.61 c -742.85 346.97 742.28 347.15 741.60 347.15 c -h -747.94 347.15 m -747.35 347.15 746.89 346.98 746.56 346.64 c -746.24 346.31 746.07 345.84 746.07 345.24 c -746.07 341.50 l -745.28 341.50 l -745.28 340.64 l -746.07 340.64 l -746.07 339.48 l -747.23 339.37 l -747.23 340.64 l -748.89 340.64 l -748.89 341.50 l -747.23 341.50 l -747.23 345.03 l -747.23 345.86 747.59 346.28 748.30 346.28 c -748.46 346.28 748.64 346.25 748.86 346.20 c -748.86 347.00 l -748.51 347.10 748.20 347.15 747.94 347.15 c -h -753.79 346.19 m -753.10 346.83 752.44 347.15 751.80 347.15 c -751.27 347.15 750.83 346.98 750.48 346.65 c -750.14 346.32 749.96 345.90 749.96 345.40 c -749.96 344.71 750.25 344.17 750.84 343.80 c -751.42 343.42 752.26 343.24 753.35 343.24 c -753.62 343.24 l -753.62 342.47 l -753.62 341.73 753.25 341.36 752.49 341.36 c -751.88 341.36 751.22 341.55 750.51 341.93 c -750.51 340.97 l -751.29 340.65 752.02 340.50 752.70 340.50 c -753.41 340.50 753.93 340.66 754.27 340.98 c -754.61 341.30 754.78 341.79 754.78 342.47 c -754.78 345.35 l -754.78 346.01 754.98 346.34 755.39 346.34 c -755.44 346.34 755.51 346.34 755.61 346.32 c -755.69 346.96 l -755.43 347.08 755.14 347.15 754.83 347.15 c -754.29 347.15 753.94 346.83 753.79 346.19 c -h -753.62 345.56 m -753.62 343.92 l -753.24 343.91 l -752.61 343.91 752.09 344.03 751.70 344.27 c -751.31 344.51 751.12 344.82 751.12 345.21 c -751.12 345.49 751.21 345.72 751.41 345.92 c -751.61 346.11 751.84 346.20 752.12 346.20 c -752.61 346.20 753.11 345.99 753.62 345.56 c -h -757.14 347.00 m -757.14 340.64 l -758.29 340.64 l -758.29 341.83 l -758.90 340.94 759.65 340.50 760.53 340.50 c -761.08 340.50 761.52 340.67 761.85 341.02 c -762.18 341.37 762.34 341.84 762.34 342.43 c -762.34 347.00 l -761.19 347.00 l -761.19 342.80 l -761.19 342.33 761.12 342.00 760.98 341.79 c -760.84 341.59 760.61 341.49 760.29 341.49 c -759.59 341.49 758.92 341.96 758.29 342.88 c -758.29 347.00 l -h -767.07 347.15 m -766.21 347.15 765.50 346.83 764.93 346.19 c -764.37 345.55 764.08 344.75 764.08 343.78 c -764.08 342.75 764.36 341.94 764.92 341.36 c -765.49 340.79 766.27 340.50 767.27 340.50 c -767.77 340.50 768.32 340.56 768.94 340.70 c -768.94 341.67 l -768.28 341.48 767.75 341.38 767.34 341.38 c -766.75 341.38 766.28 341.60 765.92 342.05 c -765.56 342.49 765.38 343.08 765.38 343.82 c -765.38 344.53 765.57 345.11 765.94 345.55 c -766.30 345.99 766.78 346.21 767.38 346.21 c -767.90 346.21 768.45 346.08 769.01 345.81 c -769.01 346.81 l -768.26 347.03 767.62 347.15 767.07 347.15 c -h -775.40 346.79 m -774.62 347.03 773.96 347.15 773.41 347.15 c -772.47 347.15 771.71 346.83 771.12 346.21 c -770.53 345.59 770.23 344.78 770.23 343.79 c -770.23 342.82 770.49 342.03 771.01 341.42 c -771.53 340.80 772.20 340.49 773.01 340.49 c -773.78 340.49 774.38 340.76 774.80 341.31 c -775.22 341.86 775.43 342.63 775.43 343.64 c -775.42 344.00 l -771.41 344.00 l -771.58 345.51 772.32 346.27 773.63 346.27 c -774.11 346.27 774.70 346.14 775.40 345.88 c -h -771.46 343.13 m -774.27 343.13 l -774.27 341.95 773.83 341.36 772.94 341.36 c -772.06 341.36 771.56 341.95 771.46 343.13 c -h -f -640.06 223.15 m -639.48 223.15 639.02 222.98 638.69 222.64 c -638.37 222.31 638.20 221.84 638.20 221.24 c -638.20 217.50 l -637.40 217.50 l -637.40 216.64 l -638.20 216.64 l -638.20 215.48 l -639.36 215.37 l -639.36 216.64 l -641.02 216.64 l -641.02 217.50 l -639.36 217.50 l -639.36 221.03 l -639.36 221.86 639.71 222.28 640.43 222.28 c -640.59 222.28 640.77 222.25 640.99 222.20 c -640.99 223.00 l -640.63 223.10 640.33 223.15 640.06 223.15 c -h -647.31 222.79 m -646.53 223.03 645.87 223.15 645.32 223.15 c -644.38 223.15 643.62 222.83 643.03 222.21 c -642.43 221.59 642.14 220.78 642.14 219.79 c -642.14 218.82 642.40 218.03 642.92 217.42 c -643.44 216.80 644.11 216.49 644.92 216.49 c -645.69 216.49 646.29 216.76 646.71 217.31 c -647.13 217.86 647.34 218.63 647.34 219.64 c -647.33 220.00 l -643.32 220.00 l -643.48 221.51 644.22 222.27 645.54 222.27 c -646.02 222.27 646.61 222.14 647.31 221.88 c -h -643.37 219.13 m -646.18 219.13 l -646.18 217.95 645.73 217.36 644.85 217.36 c -643.96 217.36 643.47 217.95 643.37 219.13 c -h -649.33 223.00 m -649.33 216.64 l -650.48 216.64 l -650.48 217.83 l -651.09 216.94 651.84 216.50 652.72 216.50 c -653.27 216.50 653.71 216.67 654.04 217.02 c -654.37 217.37 654.53 217.84 654.53 218.43 c -654.53 223.00 l -653.38 223.00 l -653.38 218.80 l -653.38 218.33 653.31 218.00 653.17 217.79 c -653.03 217.59 652.80 217.49 652.48 217.49 c -651.77 217.49 651.11 217.96 650.48 218.88 c -650.48 223.00 l -h -660.06 222.19 m -659.37 222.83 658.70 223.15 658.06 223.15 c -657.53 223.15 657.09 222.98 656.75 222.65 c -656.40 222.32 656.22 221.90 656.22 221.40 c -656.22 220.71 656.52 220.17 657.10 219.80 c -657.68 219.42 658.52 219.24 659.61 219.24 c -659.89 219.24 l -659.89 218.47 l -659.89 217.73 659.51 217.36 658.75 217.36 c -658.14 217.36 657.48 217.55 656.78 217.93 c -656.78 216.97 l -657.55 216.65 658.28 216.50 658.96 216.50 c -659.67 216.50 660.20 216.66 660.53 216.98 c -660.87 217.30 661.04 217.79 661.04 218.47 c -661.04 221.35 l -661.04 222.01 661.24 222.34 661.65 222.34 c -661.70 222.34 661.78 222.34 661.87 222.32 c -661.96 222.96 l -661.69 223.08 661.40 223.15 661.09 223.15 c -660.55 223.15 660.21 222.83 660.06 222.19 c -h -659.89 221.56 m -659.89 219.92 l -659.50 219.91 l -658.87 219.91 658.36 220.03 657.96 220.27 c -657.57 220.51 657.38 220.82 657.38 221.21 c -657.38 221.49 657.48 221.72 657.67 221.92 c -657.87 222.11 658.11 222.20 658.39 222.20 c -658.87 222.20 659.37 221.99 659.89 221.56 c -h -663.40 223.00 m -663.40 216.64 l -664.56 216.64 l -664.56 217.83 l -665.17 216.94 665.91 216.50 666.79 216.50 c -667.35 216.50 667.79 216.67 668.11 217.02 c -668.44 217.37 668.61 217.84 668.61 218.43 c -668.61 223.00 l -667.45 223.00 l -667.45 218.80 l -667.45 218.33 667.38 218.00 667.24 217.79 c -667.10 217.59 666.88 217.49 666.55 217.49 c -665.85 217.49 665.18 217.96 664.56 218.88 c -664.56 223.00 l -h -672.76 223.15 m -672.17 223.15 671.72 222.98 671.39 222.64 c -671.06 222.31 670.90 221.84 670.90 221.24 c -670.90 217.50 l -670.10 217.50 l -670.10 216.64 l -670.90 216.64 l -670.90 215.48 l -672.05 215.37 l -672.05 216.64 l -673.71 216.64 l -673.71 217.50 l -672.05 217.50 l -672.05 221.03 l -672.05 221.86 672.41 222.28 673.13 222.28 c -673.28 222.28 673.47 222.25 673.69 222.20 c -673.69 223.00 l -673.33 223.10 673.02 223.15 672.76 223.15 c -h -679.28 221.05 m -679.28 220.18 l -686.22 220.18 l -686.22 221.05 l -h -679.28 218.88 m -679.28 218.01 l -686.22 218.01 l -686.22 218.88 l -h -692.47 225.31 m -692.47 216.64 l -693.62 216.64 l -693.62 217.83 l -694.10 216.94 694.81 216.50 695.75 216.50 c -696.52 216.50 697.12 216.78 697.56 217.33 c -698.00 217.89 698.22 218.66 698.22 219.62 c -698.22 220.68 697.97 221.53 697.47 222.18 c -696.97 222.82 696.32 223.15 695.51 223.15 c -694.75 223.15 694.12 222.86 693.62 222.28 c -693.62 225.31 l -h -693.62 221.48 m -694.22 222.01 694.79 222.28 695.32 222.28 c -696.43 222.28 696.99 221.43 696.99 219.74 c -696.99 218.25 696.50 217.50 695.51 217.50 c -694.87 217.50 694.24 217.85 693.62 218.55 c -h -703.30 222.19 m -702.61 222.83 701.95 223.15 701.31 223.15 c -700.78 223.15 700.34 222.98 699.99 222.65 c -699.65 222.32 699.47 221.90 699.47 221.40 c -699.47 220.71 699.76 220.17 700.35 219.80 c -700.93 219.42 701.77 219.24 702.86 219.24 c -703.13 219.24 l -703.13 218.47 l -703.13 217.73 702.76 217.36 702.00 217.36 c -701.39 217.36 700.73 217.55 700.02 217.93 c -700.02 216.97 l -700.80 216.65 701.53 216.50 702.21 216.50 c -702.92 216.50 703.44 216.66 703.78 216.98 c -704.12 217.30 704.29 217.79 704.29 218.47 c -704.29 221.35 l -704.29 222.01 704.49 222.34 704.90 222.34 c -704.95 222.34 705.02 222.34 705.12 222.32 c -705.20 222.96 l -704.94 223.08 704.65 223.15 704.34 223.15 c -703.80 223.15 703.45 222.83 703.30 222.19 c -h -703.13 221.56 m -703.13 219.92 l -702.75 219.91 l -702.12 219.91 701.60 220.03 701.21 220.27 c -700.82 220.51 700.63 220.82 700.63 221.21 c -700.63 221.49 700.72 221.72 700.92 221.92 c -701.12 222.11 701.35 222.20 701.63 222.20 c -702.12 222.20 702.62 221.99 703.13 221.56 c -h -706.65 223.00 m -706.65 216.64 l -707.80 216.64 l -707.80 217.83 l -708.26 216.94 708.93 216.50 709.80 216.50 c -709.91 216.50 710.04 216.51 710.17 216.53 c -710.17 217.60 l -709.97 217.54 709.79 217.50 709.64 217.50 c -708.91 217.50 708.30 217.94 707.80 218.80 c -707.80 223.00 l -h -713.25 223.15 m -712.72 223.15 712.08 223.02 711.33 222.78 c -711.33 221.72 l -712.08 222.09 712.74 222.28 713.29 222.28 c -713.63 222.28 713.90 222.19 714.12 222.01 c -714.34 221.83 714.45 221.61 714.45 221.34 c -714.45 220.94 714.14 220.62 713.53 220.36 c -712.86 220.07 l -711.86 219.66 711.36 219.06 711.36 218.28 c -711.36 217.73 711.56 217.29 711.95 216.97 c -712.34 216.66 712.88 216.50 713.56 216.50 c -713.92 216.50 714.36 216.54 714.88 216.64 c -715.12 216.69 l -715.12 217.65 l -714.48 217.46 713.97 217.36 713.59 217.36 c -712.85 217.36 712.47 217.63 712.47 218.17 c -712.47 218.52 712.76 218.81 713.32 219.05 c -713.88 219.29 l -714.50 219.55 714.95 219.83 715.21 220.13 c -715.47 220.42 715.60 220.79 715.60 221.23 c -715.60 221.79 715.38 222.25 714.94 222.61 c -714.50 222.97 713.94 223.15 713.25 223.15 c -h -722.34 222.79 m -721.57 223.03 720.91 223.15 720.36 223.15 c -719.42 223.15 718.65 222.83 718.06 222.21 c -717.47 221.59 717.17 220.78 717.17 219.79 c -717.17 218.82 717.43 218.03 717.96 217.42 c -718.48 216.80 719.14 216.49 719.96 216.49 c -720.73 216.49 721.32 216.76 721.74 217.31 c -722.16 217.86 722.37 218.63 722.37 219.64 c -722.37 220.00 l -718.35 220.00 l -718.52 221.51 719.26 222.27 720.57 222.27 c -721.05 222.27 721.64 222.14 722.34 221.88 c -h -718.40 219.13 m -721.21 219.13 l -721.21 217.95 720.77 217.36 719.89 217.36 c -719.00 217.36 718.51 217.95 718.40 219.13 c -h -726.68 223.94 m -726.68 224.73 l -725.83 224.16 725.17 223.38 724.67 222.39 c -724.18 221.41 723.93 220.36 723.93 219.24 c -723.93 218.12 724.18 217.08 724.67 216.09 c -725.17 215.10 725.83 214.32 726.68 213.75 c -726.68 214.54 l -726.10 215.17 725.69 215.84 725.45 216.56 c -725.21 217.28 725.08 218.17 725.08 219.24 c -725.08 220.31 725.21 221.20 725.45 221.92 c -725.69 222.64 726.10 223.31 726.68 223.94 c -h -732.24 223.00 m -732.24 221.80 l -731.63 222.70 730.89 223.15 730.01 223.15 c -729.46 223.15 729.02 222.97 728.69 222.62 c -728.36 222.27 728.20 221.80 728.20 221.21 c -728.20 216.64 l -729.35 216.64 l -729.35 220.83 l -729.35 221.31 729.42 221.65 729.56 221.85 c -729.70 222.05 729.93 222.15 730.25 222.15 c -730.96 222.15 731.62 221.69 732.24 220.76 c -732.24 216.64 l -733.40 216.64 l -733.40 223.00 l -h -735.71 223.00 m -735.71 216.64 l -736.87 216.64 l -736.87 217.83 l -737.32 216.94 737.99 216.50 738.86 216.50 c -738.98 216.50 739.10 216.51 739.23 216.53 c -739.23 217.60 l -739.03 217.54 738.85 217.50 738.70 217.50 c -737.97 217.50 737.36 217.94 736.87 218.80 c -736.87 223.00 l -h -740.62 223.00 m -740.62 213.75 l -741.78 213.75 l -741.78 223.00 l -h -743.37 223.94 m -743.37 224.73 l -744.21 224.16 744.88 223.38 745.38 222.39 c -745.87 221.41 746.12 220.36 746.12 219.24 c -746.12 218.12 745.87 217.08 745.38 216.09 c -744.88 215.10 744.21 214.32 743.37 213.75 c -743.37 214.54 l -743.95 215.17 744.35 215.84 744.60 216.56 c -744.84 217.28 744.96 218.17 744.96 219.24 c -744.96 220.31 744.84 221.20 744.60 221.92 c -744.35 222.64 743.95 223.31 743.37 223.94 c -h -f -17.000 7.0000 m -121.00 7.0000 l -121.00 44.000 l -17.000 44.000 l -17.000 7.0000 l -h -f -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -15.000 5.0000 m -117.00 5.0000 l -117.00 40.000 l -15.000 40.000 l -15.000 5.0000 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -15.500 5.5000 m -117.50 5.5000 l -117.50 40.500 l -15.500 40.500 l -15.500 5.5000 l -h -S -36.500 24.500 m -96.500 24.500 l -S -39.639 22.146 m -38.779 22.146 38.066 21.828 37.500 21.191 c -36.934 20.555 36.650 19.752 36.650 18.783 c -36.650 17.748 36.931 16.941 37.491 16.363 c -38.052 15.785 38.834 15.496 39.838 15.496 c -40.334 15.496 40.889 15.564 41.502 15.701 c -41.502 16.668 l -40.850 16.477 40.318 16.381 39.908 16.381 c -39.318 16.381 38.845 16.603 38.487 17.046 c -38.130 17.489 37.951 18.080 37.951 18.818 c -37.951 19.533 38.135 20.111 38.502 20.553 c -38.869 20.994 39.350 21.215 39.943 21.215 c -40.471 21.215 41.014 21.080 41.572 20.811 c -41.572 21.807 l -40.826 22.033 40.182 22.146 39.639 22.146 c -h -43.301 22.000 m -43.301 12.748 l -44.455 12.748 l -44.455 22.000 l -h -46.770 22.000 m -46.770 15.637 l -47.924 15.637 l -47.924 22.000 l -h -46.770 14.482 m -46.770 13.328 l -47.924 13.328 l -47.924 14.482 l -h -54.902 21.795 m -54.129 22.029 53.467 22.146 52.916 22.146 c -51.979 22.146 51.214 21.835 50.622 21.212 c -50.030 20.589 49.734 19.781 49.734 18.789 c -49.734 17.824 49.995 17.033 50.517 16.416 c -51.038 15.799 51.705 15.490 52.518 15.490 c -53.287 15.490 53.882 15.764 54.302 16.311 c -54.722 16.857 54.932 17.635 54.932 18.643 c -54.926 19.000 l -50.912 19.000 l -51.080 20.512 51.820 21.268 53.133 21.268 c -53.613 21.268 54.203 21.139 54.902 20.881 c -h -50.965 18.133 m -53.771 18.133 l -53.771 16.949 53.330 16.357 52.447 16.357 c -51.561 16.357 51.066 16.949 50.965 18.133 c -h -56.924 22.000 m -56.924 15.637 l -58.078 15.637 l -58.078 16.832 l -58.688 15.941 59.434 15.496 60.316 15.496 c -60.867 15.496 61.307 15.671 61.635 16.021 c -61.963 16.370 62.127 16.840 62.127 17.430 c -62.127 22.000 l -60.973 22.000 l -60.973 17.805 l -60.973 17.332 60.903 16.995 60.765 16.794 c -60.626 16.593 60.396 16.492 60.076 16.492 c -59.369 16.492 58.703 16.955 58.078 17.881 c -58.078 22.000 l -h -66.281 22.146 m -65.695 22.146 65.238 21.979 64.910 21.643 c -64.582 21.307 64.418 20.840 64.418 20.242 c -64.418 16.504 l -63.621 16.504 l -63.621 15.637 l -64.418 15.637 l -64.418 14.482 l -65.572 14.371 l -65.572 15.637 l -67.236 15.637 l -67.236 16.504 l -65.572 16.504 l -65.572 20.031 l -65.572 20.863 65.932 21.279 66.650 21.279 c -66.803 21.279 66.988 21.254 67.207 21.203 c -67.207 22.000 l -66.852 22.098 66.543 22.146 66.281 22.146 c -h -69.023 22.000 m -69.023 20.846 l -70.178 20.846 l -70.178 22.000 l -h -69.023 16.797 m -69.023 15.637 l -70.178 15.637 l -70.178 16.797 l -h -72.551 13.328 m -73.781 13.328 l -73.781 18.801 l -73.781 19.672 73.942 20.306 74.265 20.702 c -74.587 21.099 75.100 21.297 75.803 21.297 c -76.490 21.297 76.978 21.110 77.265 20.737 c -77.552 20.364 77.695 19.732 77.695 18.842 c -77.695 13.328 l -78.773 13.328 l -78.773 18.824 l -78.773 20.008 78.530 20.869 78.044 21.408 c -77.558 21.947 76.783 22.217 75.721 22.217 c -74.639 22.217 73.840 21.938 73.324 21.379 c -72.809 20.820 72.551 19.957 72.551 18.789 c -h -82.658 22.146 m -82.131 22.146 81.490 22.023 80.736 21.777 c -80.736 20.717 l -81.490 21.092 82.146 21.279 82.705 21.279 c -83.037 21.279 83.312 21.189 83.531 21.010 c -83.750 20.830 83.859 20.605 83.859 20.336 c -83.859 19.941 83.553 19.615 82.939 19.357 c -82.266 19.070 l -81.270 18.656 80.771 18.061 80.771 17.283 c -80.771 16.729 80.968 16.292 81.360 15.974 c -81.753 15.655 82.291 15.496 82.975 15.496 c -83.330 15.496 83.770 15.545 84.293 15.643 c -84.533 15.689 l -84.533 16.650 l -83.889 16.459 83.377 16.363 82.998 16.363 c -82.256 16.363 81.885 16.633 81.885 17.172 c -81.885 17.520 82.166 17.812 82.729 18.051 c -83.285 18.285 l -83.914 18.551 84.359 18.831 84.621 19.126 c -84.883 19.421 85.014 19.789 85.014 20.230 c -85.014 20.789 84.793 21.248 84.352 21.607 c -83.910 21.967 83.346 22.146 82.658 22.146 c -h -91.752 21.795 m -90.979 22.029 90.316 22.146 89.766 22.146 c -88.828 22.146 88.063 21.835 87.472 21.212 c -86.880 20.589 86.584 19.781 86.584 18.789 c -86.584 17.824 86.845 17.033 87.366 16.416 c -87.888 15.799 88.555 15.490 89.367 15.490 c -90.137 15.490 90.731 15.764 91.151 16.311 c -91.571 16.857 91.781 17.635 91.781 18.643 c -91.775 19.000 l -87.762 19.000 l -87.930 20.512 88.670 21.268 89.982 21.268 c -90.463 21.268 91.053 21.139 91.752 20.881 c -h -87.814 18.133 m -90.621 18.133 l -90.621 16.949 90.180 16.357 89.297 16.357 c -88.410 16.357 87.916 16.949 87.814 18.133 c -h -93.773 22.000 m -93.773 15.637 l -94.928 15.637 l -94.928 16.832 l -95.385 15.941 96.049 15.496 96.920 15.496 c -97.037 15.496 97.160 15.506 97.289 15.525 c -97.289 16.604 l -97.090 16.537 96.914 16.504 96.762 16.504 c -96.031 16.504 95.420 16.938 94.928 17.805 c -94.928 22.000 l -h -f -397.00 7.0000 m -506.00 7.0000 l -506.00 44.000 l -397.00 44.000 l -397.00 7.0000 l -h -f -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -395.00 5.0000 m -502.00 5.0000 l -502.00 40.000 l -395.00 40.000 l -395.00 5.0000 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -395.50 5.5000 m -502.50 5.5000 l -502.50 40.500 l -395.50 40.500 l -395.50 5.5000 l -h -S -401.50 24.500 m -496.50 24.500 l -S -402.15 22.000 m -402.15 12.748 l -403.31 12.748 l -403.31 18.725 l -406.00 15.637 l -407.25 15.637 l -404.67 18.607 l -407.78 22.000 l -406.30 22.000 l -403.31 18.736 l -403.31 22.000 l -h -413.83 21.795 m -413.06 22.029 412.40 22.146 411.85 22.146 c -410.91 22.146 410.14 21.835 409.55 21.212 c -408.96 20.589 408.66 19.781 408.66 18.789 c -408.66 17.824 408.92 17.033 409.45 16.416 c -409.97 15.799 410.63 15.490 411.45 15.490 c -412.22 15.490 412.81 15.764 413.23 16.311 c -413.65 16.857 413.86 17.635 413.86 18.643 c -413.86 19.000 l -409.84 19.000 l -410.01 20.512 410.75 21.268 412.06 21.268 c -412.54 21.268 413.13 21.139 413.83 20.881 c -h -409.89 18.133 m -412.70 18.133 l -412.70 16.949 412.26 16.357 411.38 16.357 c -410.49 16.357 410.00 16.949 409.89 18.133 c -h -416.22 24.314 m -417.25 22.000 l -414.79 15.637 l -416.04 15.637 l -417.86 20.430 l -419.81 15.637 l -420.90 15.637 l -417.42 24.314 l -h -423.81 22.146 m -423.28 22.146 422.64 22.023 421.89 21.777 c -421.89 20.717 l -422.64 21.092 423.30 21.279 423.86 21.279 c -424.19 21.279 424.46 21.189 424.68 21.010 c -424.90 20.830 425.01 20.605 425.01 20.336 c -425.01 19.941 424.71 19.615 424.09 19.357 c -423.42 19.070 l -422.42 18.656 421.92 18.061 421.92 17.283 c -421.92 16.729 422.12 16.292 422.51 15.974 c -422.91 15.655 423.44 15.496 424.13 15.496 c -424.48 15.496 424.92 15.545 425.45 15.643 c -425.69 15.689 l -425.69 16.650 l -425.04 16.459 424.53 16.363 424.15 16.363 c -423.41 16.363 423.04 16.633 423.04 17.172 c -423.04 17.520 423.32 17.812 423.88 18.051 c -424.44 18.285 l -425.07 18.551 425.51 18.831 425.77 19.126 c -426.04 19.421 426.17 19.789 426.17 20.230 c -426.17 20.789 425.95 21.248 425.50 21.607 c -425.06 21.967 424.50 22.146 423.81 22.146 c -h -430.15 22.146 m -429.56 22.146 429.11 21.979 428.78 21.643 c -428.45 21.307 428.29 20.840 428.29 20.242 c -428.29 16.504 l -427.49 16.504 l -427.49 15.637 l -428.29 15.637 l -428.29 14.482 l -429.44 14.371 l -429.44 15.637 l -431.11 15.637 l -431.11 16.504 l -429.44 16.504 l -429.44 20.031 l -429.44 20.863 429.80 21.279 430.52 21.279 c -430.67 21.279 430.86 21.254 431.08 21.203 c -431.08 22.000 l -430.72 22.098 430.41 22.146 430.15 22.146 c -h -435.22 22.146 m -434.31 22.146 433.58 21.845 433.04 21.241 c -432.50 20.638 432.22 19.830 432.22 18.818 c -432.22 17.795 432.50 16.985 433.04 16.390 c -433.59 15.794 434.33 15.496 435.26 15.496 c -436.19 15.496 436.93 15.794 437.48 16.390 c -438.02 16.985 438.29 17.791 438.29 18.807 c -438.29 19.846 438.02 20.662 437.47 21.256 c -436.93 21.850 436.18 22.146 435.22 22.146 c -h -435.24 21.279 m -436.46 21.279 437.07 20.455 437.07 18.807 c -437.07 17.178 436.47 16.363 435.26 16.363 c -434.06 16.363 433.46 17.182 433.46 18.818 c -433.46 20.459 434.05 21.279 435.24 21.279 c -h -440.10 22.000 m -440.10 15.637 l -441.25 15.637 l -441.25 16.832 l -441.86 15.941 442.61 15.496 443.49 15.496 c -444.04 15.496 444.48 15.671 444.81 16.021 c -445.14 16.370 445.30 16.840 445.30 17.430 c -445.30 22.000 l -444.15 22.000 l -444.15 17.805 l -444.15 17.332 444.08 16.995 443.94 16.794 c -443.80 16.593 443.57 16.492 443.25 16.492 c -442.54 16.492 441.88 16.955 441.25 17.881 c -441.25 22.000 l -h -452.21 21.795 m -451.44 22.029 450.78 22.146 450.22 22.146 c -449.29 22.146 448.52 21.835 447.93 21.212 c -447.34 20.589 447.04 19.781 447.04 18.789 c -447.04 17.824 447.30 17.033 447.83 16.416 c -448.35 15.799 449.01 15.490 449.83 15.490 c -450.60 15.490 451.19 15.764 451.61 16.311 c -452.03 16.857 452.24 17.635 452.24 18.643 c -452.23 19.000 l -448.22 19.000 l -448.39 20.512 449.13 21.268 450.44 21.268 c -450.92 21.268 451.51 21.139 452.21 20.881 c -h -448.27 18.133 m -451.08 18.133 l -451.08 16.949 450.64 16.357 449.76 16.357 c -448.87 16.357 448.38 16.949 448.27 18.133 c -h -454.40 22.000 m -454.40 20.846 l -455.55 20.846 l -455.55 22.000 l -h -454.40 16.797 m -454.40 15.637 l -455.55 15.637 l -455.55 16.797 l -h -459.69 22.217 m -459.11 22.217 458.37 22.090 457.46 21.836 c -457.46 20.617 l -458.44 21.070 459.24 21.297 459.87 21.297 c -460.35 21.297 460.74 21.170 461.04 20.916 c -461.33 20.662 461.48 20.328 461.48 19.914 c -461.48 19.574 461.38 19.285 461.19 19.047 c -461.00 18.809 460.64 18.543 460.12 18.250 c -459.52 17.904 l -458.79 17.482 458.26 17.085 457.96 16.712 c -457.66 16.339 457.51 15.904 457.51 15.408 c -457.51 14.740 457.75 14.190 458.23 13.759 c -458.72 13.327 459.34 13.111 460.09 13.111 c -460.75 13.111 461.46 13.223 462.20 13.445 c -462.20 14.570 l -461.29 14.211 460.61 14.031 460.16 14.031 c -459.73 14.031 459.38 14.145 459.10 14.371 c -458.82 14.598 458.69 14.883 458.69 15.227 c -458.69 15.516 458.79 15.771 458.99 15.994 c -459.19 16.217 459.56 16.482 460.10 16.791 c -460.72 17.143 l -461.47 17.568 462.00 17.971 462.29 18.350 c -462.59 18.729 462.74 19.184 462.74 19.715 c -462.74 20.469 462.46 21.074 461.91 21.531 c -461.35 21.988 460.61 22.217 459.69 22.217 c -h -469.16 21.795 m -468.38 22.029 467.72 22.146 467.17 22.146 c -466.23 22.146 465.47 21.835 464.88 21.212 c -464.28 20.589 463.99 19.781 463.99 18.789 c -463.99 17.824 464.25 17.033 464.77 16.416 c -465.29 15.799 465.96 15.490 466.77 15.490 c -467.54 15.490 468.14 15.764 468.56 16.311 c -468.98 16.857 469.19 17.635 469.19 18.643 c -469.18 19.000 l -465.17 19.000 l -465.33 20.512 466.07 21.268 467.39 21.268 c -467.87 21.268 468.46 21.139 469.16 20.881 c -h -465.22 18.133 m -468.03 18.133 l -468.03 16.949 467.58 16.357 466.70 16.357 c -465.81 16.357 465.32 16.949 465.22 18.133 c -h -471.18 22.000 m -471.18 15.637 l -472.33 15.637 l -472.33 16.832 l -472.79 15.941 473.45 15.496 474.32 15.496 c -474.44 15.496 474.56 15.506 474.69 15.525 c -474.69 16.604 l -474.49 16.537 474.32 16.504 474.17 16.504 c -473.44 16.504 472.82 16.938 472.33 17.805 c -472.33 22.000 l -h -477.41 22.000 m -475.04 15.637 l -476.19 15.637 l -478.04 20.588 l -480.00 15.637 l -481.07 15.637 l -478.56 22.000 l -h -482.30 22.000 m -482.30 15.637 l -483.45 15.637 l -483.45 22.000 l -h -482.30 14.482 m -482.30 13.328 l -483.45 13.328 l -483.45 14.482 l -h -488.25 22.146 m -487.39 22.146 486.68 21.828 486.11 21.191 c -485.55 20.555 485.26 19.752 485.26 18.783 c -485.26 17.748 485.54 16.941 486.10 16.363 c -486.67 15.785 487.45 15.496 488.45 15.496 c -488.95 15.496 489.50 15.564 490.12 15.701 c -490.12 16.668 l -489.46 16.477 488.93 16.381 488.52 16.381 c -487.93 16.381 487.46 16.603 487.10 17.046 c -486.74 17.489 486.56 18.080 486.56 18.818 c -486.56 19.533 486.75 20.111 487.12 20.553 c -487.48 20.994 487.96 21.215 488.56 21.215 c -489.08 21.215 489.63 21.080 490.19 20.811 c -490.19 21.807 l -489.44 22.033 488.79 22.146 488.25 22.146 c -h -496.58 21.795 m -495.80 22.029 495.14 22.146 494.59 22.146 c -493.65 22.146 492.89 21.835 492.30 21.212 c -491.71 20.589 491.41 19.781 491.41 18.789 c -491.41 17.824 491.67 17.033 492.19 16.416 c -492.71 15.799 493.38 15.490 494.19 15.490 c -494.96 15.490 495.56 15.764 495.98 16.311 c -496.40 16.857 496.61 17.635 496.61 18.643 c -496.60 19.000 l -492.59 19.000 l -492.76 20.512 493.50 21.268 494.81 21.268 c -495.29 21.268 495.88 21.139 496.58 20.881 c -h -492.64 18.133 m -495.45 18.133 l -495.45 16.949 495.01 16.357 494.12 16.357 c -493.24 16.357 492.74 16.949 492.64 18.133 c -h -f -578.00 7.0000 m -682.00 7.0000 l -682.00 44.000 l -578.00 44.000 l -578.00 7.0000 l -h -f -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -576.00 5.0000 m -678.00 5.0000 l -678.00 40.000 l -576.00 40.000 l -576.00 5.0000 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -576.50 5.5000 m -678.50 5.5000 l -678.50 40.500 l -576.50 40.500 l -576.50 5.5000 l -h -S -592.50 24.500 m -663.50 24.500 l -S -593.15 22.000 m -593.15 15.637 l -594.31 15.637 l -594.31 16.832 l -594.92 15.941 595.66 15.496 596.55 15.496 c -597.10 15.496 597.54 15.671 597.87 16.021 c -598.19 16.370 598.36 16.840 598.36 17.430 c -598.36 22.000 l -597.20 22.000 l -597.20 17.805 l -597.20 17.332 597.13 16.995 597.00 16.794 c -596.86 16.593 596.63 16.492 596.31 16.492 c -595.60 16.492 594.93 16.955 594.31 17.881 c -594.31 22.000 l -h -603.09 22.146 m -602.18 22.146 601.46 21.845 600.91 21.241 c -600.37 20.638 600.10 19.830 600.10 18.818 c -600.10 17.795 600.37 16.985 600.92 16.390 c -601.46 15.794 602.20 15.496 603.13 15.496 c -604.07 15.496 604.81 15.794 605.35 16.390 c -605.90 16.985 606.17 17.791 606.17 18.807 c -606.17 19.846 605.89 20.662 605.35 21.256 c -604.80 21.850 604.05 22.146 603.09 22.146 c -h -603.11 21.279 m -604.33 21.279 604.94 20.455 604.94 18.807 c -604.94 17.178 604.34 16.363 603.13 16.363 c -601.93 16.363 601.33 17.182 601.33 18.818 c -601.33 20.459 601.92 21.279 603.11 21.279 c -h -609.29 22.000 m -606.92 15.637 l -608.08 15.637 l -609.93 20.588 l -611.88 15.637 l -612.96 15.637 l -610.45 22.000 l -h -617.46 21.191 m -616.77 21.828 616.11 22.146 615.47 22.146 c -614.94 22.146 614.50 21.981 614.15 21.651 c -613.81 21.321 613.63 20.904 613.63 20.400 c -613.63 19.705 613.92 19.171 614.51 18.798 c -615.09 18.425 615.93 18.238 617.02 18.238 c -617.29 18.238 l -617.29 17.471 l -617.29 16.732 616.92 16.363 616.16 16.363 c -615.55 16.363 614.89 16.551 614.18 16.926 c -614.18 15.971 l -614.96 15.654 615.69 15.496 616.37 15.496 c -617.08 15.496 617.60 15.656 617.94 15.977 c -618.28 16.297 618.45 16.795 618.45 17.471 c -618.45 20.354 l -618.45 21.014 618.65 21.344 619.06 21.344 c -619.11 21.344 619.18 21.336 619.28 21.320 c -619.36 21.959 l -619.10 22.084 618.81 22.146 618.50 22.146 c -617.96 22.146 617.61 21.828 617.46 21.191 c -h -617.29 20.564 m -617.29 18.918 l -616.91 18.906 l -616.28 18.906 615.76 19.026 615.37 19.267 c -614.98 19.507 614.79 19.822 614.79 20.213 c -614.79 20.490 614.88 20.725 615.08 20.916 c -615.28 21.107 615.51 21.203 615.79 21.203 c -616.28 21.203 616.78 20.990 617.29 20.564 c -h -620.97 22.000 m -620.97 20.846 l -622.13 20.846 l -622.13 22.000 l -h -620.97 16.797 m -620.97 15.637 l -622.13 15.637 l -622.13 16.797 l -h -626.27 22.217 m -625.69 22.217 624.95 22.090 624.04 21.836 c -624.04 20.617 l -625.02 21.070 625.82 21.297 626.45 21.297 c -626.93 21.297 627.32 21.170 627.62 20.916 c -627.91 20.662 628.06 20.328 628.06 19.914 c -628.06 19.574 627.96 19.285 627.77 19.047 c -627.58 18.809 627.22 18.543 626.70 18.250 c -626.10 17.904 l -625.36 17.482 624.84 17.085 624.54 16.712 c -624.24 16.339 624.09 15.904 624.09 15.408 c -624.09 14.740 624.33 14.190 624.81 13.759 c -625.30 13.327 625.91 13.111 626.66 13.111 c -627.33 13.111 628.04 13.223 628.78 13.445 c -628.78 14.570 l -627.87 14.211 627.18 14.031 626.73 14.031 c -626.31 14.031 625.96 14.145 625.68 14.371 c -625.40 14.598 625.26 14.883 625.26 15.227 c -625.26 15.516 625.37 15.771 625.57 15.994 c -625.77 16.217 626.14 16.482 626.68 16.791 c -627.30 17.143 l -628.05 17.568 628.58 17.971 628.87 18.350 c -629.17 18.729 629.32 19.184 629.32 19.715 c -629.32 20.469 629.04 21.074 628.48 21.531 c -627.93 21.988 627.19 22.217 626.27 22.217 c -h -635.73 21.795 m -634.96 22.029 634.30 22.146 633.75 22.146 c -632.81 22.146 632.05 21.835 631.45 21.212 c -630.86 20.589 630.57 19.781 630.57 18.789 c -630.57 17.824 630.83 17.033 631.35 16.416 c -631.87 15.799 632.54 15.490 633.35 15.490 c -634.12 15.490 634.71 15.764 635.13 16.311 c -635.55 16.857 635.76 17.635 635.76 18.643 c -635.76 19.000 l -631.74 19.000 l -631.91 20.512 632.65 21.268 633.96 21.268 c -634.45 21.268 635.04 21.139 635.73 20.881 c -h -631.80 18.133 m -634.60 18.133 l -634.60 16.949 634.16 16.357 633.28 16.357 c -632.39 16.357 631.90 16.949 631.80 18.133 c -h -637.76 22.000 m -637.76 15.637 l -638.91 15.637 l -638.91 16.832 l -639.37 15.941 640.03 15.496 640.90 15.496 c -641.02 15.496 641.14 15.506 641.27 15.525 c -641.27 16.604 l -641.07 16.537 640.90 16.504 640.74 16.504 c -640.01 16.504 639.40 16.938 638.91 17.805 c -638.91 22.000 l -h -643.98 22.000 m -641.62 15.637 l -642.77 15.637 l -644.62 20.588 l -646.57 15.637 l -647.65 15.637 l -645.14 22.000 l -h -648.88 22.000 m -648.88 15.637 l -650.03 15.637 l -650.03 22.000 l -h -648.88 14.482 m -648.88 13.328 l -650.03 13.328 l -650.03 14.482 l -h -654.83 22.146 m -653.97 22.146 653.26 21.828 652.69 21.191 c -652.12 20.555 651.84 19.752 651.84 18.783 c -651.84 17.748 652.12 16.941 652.68 16.363 c -653.24 15.785 654.03 15.496 655.03 15.496 c -655.53 15.496 656.08 15.564 656.69 15.701 c -656.69 16.668 l -656.04 16.477 655.51 16.381 655.10 16.381 c -654.51 16.381 654.04 16.603 653.68 17.046 c -653.32 17.489 653.14 18.080 653.14 18.818 c -653.14 19.533 653.33 20.111 653.69 20.553 c -654.06 20.994 654.54 21.215 655.13 21.215 c -655.66 21.215 656.21 21.080 656.76 20.811 c -656.76 21.807 l -656.02 22.033 655.37 22.146 654.83 22.146 c -h -663.16 21.795 m -662.38 22.029 661.72 22.146 661.17 22.146 c -660.23 22.146 659.47 21.835 658.88 21.212 c -658.28 20.589 657.99 19.781 657.99 18.789 c -657.99 17.824 658.25 17.033 658.77 16.416 c -659.29 15.799 659.96 15.490 660.77 15.490 c -661.54 15.490 662.14 15.764 662.56 16.311 c -662.98 16.857 663.19 17.635 663.19 18.643 c -663.18 19.000 l -659.17 19.000 l -659.33 20.512 660.07 21.268 661.39 21.268 c -661.87 21.268 662.46 21.139 663.16 20.881 c -h -659.22 18.133 m -662.03 18.133 l -662.03 16.949 661.58 16.357 660.70 16.357 c -659.81 16.357 659.32 16.949 659.22 18.133 c -h -f -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -60.000 62.000 m -142.00 62.000 l -142.00 79.000 l -60.000 79.000 l -60.000 62.000 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -2.0000 w -60.500 62.500 m -470.50 62.500 l -470.50 130.50 l -60.500 130.50 l -60.500 62.500 l -h -S -60.500 79.500 m -141.50 79.500 l -S -141.50 79.500 m -143.50 75.500 l -S -143.50 75.500 m -143.50 62.500 l -S -67.589 75.251 m -66.963 75.856 66.292 76.159 65.577 76.159 c -64.968 76.159 64.473 75.972 64.092 75.600 c -63.711 75.228 63.521 74.745 63.521 74.153 c -63.521 73.383 63.828 72.789 64.444 72.372 c -65.060 71.955 65.941 71.747 67.088 71.747 c -67.589 71.747 l -67.589 71.112 l -67.589 70.389 67.177 70.027 66.352 70.027 c -65.619 70.027 64.879 70.234 64.130 70.649 c -64.130 69.354 l -64.980 69.032 65.823 68.872 66.656 68.872 c -68.480 68.872 69.392 69.597 69.392 71.049 c -69.392 74.134 l -69.392 74.680 69.568 74.953 69.919 74.953 c -69.982 74.953 70.065 74.944 70.167 74.927 c -70.211 75.981 l -69.813 76.099 69.462 76.159 69.157 76.159 c -68.387 76.159 67.892 75.856 67.672 75.251 c -h -67.589 74.242 m -67.589 72.826 l -67.145 72.826 l -65.931 72.826 65.323 73.207 65.323 73.969 c -65.323 74.227 65.411 74.444 65.587 74.619 c -65.762 74.795 65.979 74.883 66.237 74.883 c -66.677 74.883 67.128 74.669 67.589 74.242 c -h -76.127 76.000 m -76.127 74.686 l -75.518 75.668 74.726 76.159 73.753 76.159 c -73.131 76.159 72.640 75.962 72.280 75.568 c -71.921 75.175 71.741 74.637 71.741 73.956 c -71.741 69.030 l -73.620 69.030 l -73.620 73.493 l -73.620 74.284 73.884 74.680 74.413 74.680 c -75.006 74.680 75.577 74.259 76.127 73.417 c -76.127 69.030 l -78.006 69.030 l -78.006 76.000 l -h -83.947 75.962 m -83.499 76.093 83.145 76.159 82.887 76.159 c -81.258 76.159 80.443 75.397 80.443 73.874 c -80.443 70.205 l -79.663 70.205 l -79.663 69.030 l -80.443 69.030 l -80.443 67.856 l -82.322 67.640 l -82.322 69.030 l -83.814 69.030 l -83.814 70.205 l -82.322 70.205 l -82.322 73.626 l -82.322 74.481 82.671 74.908 83.370 74.908 c -83.530 74.908 83.723 74.879 83.947 74.819 c -h -85.623 76.000 m -85.623 65.977 l -87.502 65.977 l -87.502 70.344 l -88.116 69.362 88.907 68.872 89.876 68.872 c -90.498 68.872 90.989 69.068 91.349 69.462 c -91.708 69.855 91.888 70.393 91.888 71.074 c -91.888 76.000 l -90.009 76.000 l -90.009 71.538 l -90.009 70.746 89.747 70.351 89.222 70.351 c -88.625 70.351 88.052 70.772 87.502 71.614 c -87.502 76.000 l -h -99.835 75.765 m -98.943 76.028 98.096 76.159 97.296 76.159 c -96.133 76.159 95.214 75.829 94.542 75.168 c -93.869 74.508 93.532 73.607 93.532 72.464 c -93.532 71.385 93.840 70.517 94.456 69.859 c -95.072 69.201 95.885 68.872 96.896 68.872 c -97.916 68.872 98.661 69.193 99.131 69.836 c -99.601 70.480 99.835 71.497 99.835 72.890 c -95.513 72.890 l -95.640 74.218 96.370 74.883 97.703 74.883 c -98.333 74.883 99.044 74.737 99.835 74.445 c -h -95.487 71.830 m -97.988 71.830 l -97.988 70.640 97.605 70.046 96.839 70.046 c -96.061 70.046 95.610 70.640 95.487 71.830 c -h -101.78 76.000 m -101.78 69.030 l -103.66 69.030 l -103.66 70.344 l -104.27 69.362 105.06 68.872 106.03 68.872 c -106.65 68.872 107.14 69.068 107.50 69.462 c -107.86 69.855 108.04 70.393 108.04 71.074 c -108.04 76.000 l -106.16 76.000 l -106.16 71.538 l -106.16 70.746 105.90 70.351 105.38 70.351 c -104.78 70.351 104.21 70.772 103.66 71.614 c -103.66 76.000 l -h -113.91 75.962 m -113.46 76.093 113.11 76.159 112.85 76.159 c -111.22 76.159 110.40 75.397 110.40 73.874 c -110.40 70.205 l -109.62 70.205 l -109.62 69.030 l -110.40 69.030 l -110.40 67.856 l -112.28 67.640 l -112.28 69.030 l -113.77 69.030 l -113.77 70.205 l -112.28 70.205 l -112.28 73.626 l -112.28 74.481 112.63 74.908 113.33 74.908 c -113.49 74.908 113.68 74.879 113.91 74.819 c -h -115.58 76.000 m -115.58 69.030 l -117.46 69.030 l -117.46 76.000 l -h -115.58 67.856 m -115.58 66.288 l -117.46 66.288 l -117.46 67.856 l -h -124.95 75.848 m -124.17 76.055 123.45 76.159 122.79 76.159 c -121.68 76.159 120.80 75.832 120.15 75.178 c -119.51 74.524 119.18 73.634 119.18 72.509 c -119.18 71.370 119.52 70.480 120.18 69.836 c -120.84 69.193 121.76 68.872 122.93 68.872 c -123.50 68.872 124.16 68.963 124.90 69.145 c -124.90 70.503 l -124.13 70.253 123.51 70.128 123.05 70.128 c -122.49 70.128 122.03 70.344 121.69 70.776 c -121.35 71.208 121.18 71.781 121.18 72.496 c -121.18 73.228 121.36 73.814 121.73 74.254 c -122.10 74.694 122.60 74.915 123.21 74.915 c -123.78 74.915 124.36 74.792 124.95 74.546 c -h -130.15 75.251 m -129.52 75.856 128.85 76.159 128.13 76.159 c -127.52 76.159 127.03 75.972 126.65 75.600 c -126.27 75.228 126.08 74.745 126.08 74.153 c -126.08 73.383 126.38 72.789 127.00 72.372 c -127.62 71.955 128.50 71.747 129.64 71.747 c -130.15 71.747 l -130.15 71.112 l -130.15 70.389 129.73 70.027 128.91 70.027 c -128.18 70.027 127.44 70.234 126.69 70.649 c -126.69 69.354 l -127.54 69.032 128.38 68.872 129.21 68.872 c -131.04 68.872 131.95 69.597 131.95 71.049 c -131.95 74.134 l -131.95 74.680 132.12 74.953 132.48 74.953 c -132.54 74.953 132.62 74.944 132.72 74.927 c -132.77 75.981 l -132.37 76.099 132.02 76.159 131.71 76.159 c -130.94 76.159 130.45 75.856 130.23 75.251 c -h -130.15 74.242 m -130.15 72.826 l -129.70 72.826 l -128.49 72.826 127.88 73.207 127.88 73.969 c -127.88 74.227 127.97 74.444 128.14 74.619 c -128.32 74.795 128.54 74.883 128.79 74.883 c -129.23 74.883 129.68 74.669 130.15 74.242 c -h -137.97 75.962 m -137.52 76.093 137.16 76.159 136.91 76.159 c -135.28 76.159 134.46 75.397 134.46 73.874 c -134.46 70.205 l -133.68 70.205 l -133.68 69.030 l -134.46 69.030 l -134.46 67.856 l -136.34 67.640 l -136.34 69.030 l -137.83 69.030 l -137.83 70.205 l -136.34 70.205 l -136.34 73.626 l -136.34 74.481 136.69 74.908 137.39 74.908 c -137.55 74.908 137.74 74.879 137.97 74.819 c -h -145.32 75.765 m -144.42 76.028 143.58 76.159 142.78 76.159 c -141.61 76.159 140.70 75.829 140.02 75.168 c -139.35 74.508 139.01 73.607 139.01 72.464 c -139.01 71.385 139.32 70.517 139.94 69.859 c -140.55 69.201 141.37 68.872 142.38 68.872 c -143.40 68.872 144.14 69.193 144.61 69.836 c -145.08 70.480 145.32 71.497 145.32 72.890 c -140.99 72.890 l -141.12 74.218 141.85 74.883 143.18 74.883 c -143.81 74.883 144.53 74.737 145.32 74.445 c -h -140.97 71.830 m -143.47 71.830 l -143.47 70.640 143.09 70.046 142.32 70.046 c -141.54 70.046 141.09 70.640 140.97 71.830 c -h -f -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -50.000 166.00 m -151.00 166.00 l -151.00 183.00 l -50.000 183.00 l -50.000 166.00 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -50.500 166.50 m -1031.5 166.50 l -1031.5 378.50 l -50.500 378.50 l -50.500 166.50 l -h -S -50.500 183.50 m -150.50 183.50 l -S -150.50 183.50 m -152.50 179.50 l -S -152.50 179.50 m -152.50 166.50 l -S -59.316 179.85 m -58.537 180.06 57.816 180.16 57.151 180.16 c -56.038 180.16 55.159 179.83 54.514 179.18 c -53.869 178.52 53.546 177.63 53.546 176.51 c -53.546 175.37 53.878 174.48 54.542 173.84 c -55.207 173.19 56.125 172.87 57.297 172.87 c -57.864 172.87 58.518 172.96 59.259 173.14 c -59.259 174.50 l -58.489 174.25 57.873 174.13 57.412 174.13 c -56.849 174.13 56.396 174.34 56.053 174.78 c -55.710 175.21 55.539 175.78 55.539 176.50 c -55.539 177.23 55.724 177.81 56.094 178.25 c -56.465 178.69 56.959 178.91 57.577 178.91 c -58.139 178.91 58.719 178.79 59.316 178.55 c -h -61.093 180.00 m -61.093 173.03 l -62.972 173.03 l -62.972 174.34 l -63.459 173.36 64.199 172.87 65.194 172.87 c -65.312 172.87 65.429 172.88 65.543 172.91 c -65.543 174.59 l -65.276 174.49 65.029 174.44 64.800 174.44 c -64.051 174.44 63.442 174.82 62.972 175.58 c -62.972 180.00 l -h -72.678 179.77 m -71.785 180.03 70.938 180.16 70.139 180.16 c -68.975 180.16 68.057 179.83 67.384 179.17 c -66.711 178.51 66.375 177.61 66.375 176.46 c -66.375 175.39 66.682 174.52 67.298 173.86 c -67.914 173.20 68.727 172.87 69.739 172.87 c -70.759 172.87 71.503 173.19 71.973 173.84 c -72.443 174.48 72.678 175.50 72.678 176.89 c -68.355 176.89 l -68.482 178.22 69.212 178.88 70.545 178.88 c -71.175 178.88 71.886 178.74 72.678 178.44 c -h -68.330 175.83 m -70.831 175.83 l -70.831 174.64 70.448 174.05 69.682 174.05 c -68.903 174.05 68.452 174.64 68.330 175.83 c -h -78.035 179.25 m -77.409 179.86 76.738 180.16 76.023 180.16 c -75.414 180.16 74.918 179.97 74.538 179.60 c -74.157 179.23 73.966 178.75 73.966 178.15 c -73.966 177.38 74.274 176.79 74.890 176.37 c -75.506 175.96 76.387 175.75 77.534 175.75 c -78.035 175.75 l -78.035 175.11 l -78.035 174.39 77.623 174.03 76.797 174.03 c -76.065 174.03 75.325 174.23 74.576 174.65 c -74.576 173.35 l -75.426 173.03 76.268 172.87 77.102 172.87 c -78.926 172.87 79.838 173.60 79.838 175.05 c -79.838 178.13 l -79.838 178.68 80.014 178.95 80.365 178.95 c -80.428 178.95 80.511 178.94 80.612 178.93 c -80.657 179.98 l -80.259 180.10 79.908 180.16 79.603 180.16 c -78.833 180.16 78.338 179.86 78.118 179.25 c -h -78.035 178.24 m -78.035 176.83 l -77.591 176.83 l -76.376 176.83 75.769 177.21 75.769 177.97 c -75.769 178.23 75.857 178.44 76.032 178.62 c -76.208 178.80 76.425 178.88 76.683 178.88 c -77.123 178.88 77.574 178.67 78.035 178.24 c -h -85.855 179.96 m -85.407 180.09 85.054 180.16 84.795 180.16 c -83.166 180.16 82.352 179.40 82.352 177.87 c -82.352 174.20 l -81.571 174.20 l -81.571 173.03 l -82.352 173.03 l -82.352 171.86 l -84.230 171.64 l -84.230 173.03 l -85.722 173.03 l -85.722 174.20 l -84.230 174.20 l -84.230 177.63 l -84.230 178.48 84.580 178.91 85.278 178.91 c -85.439 178.91 85.631 178.88 85.855 178.82 c -h -93.206 179.77 m -92.313 180.03 91.467 180.16 90.667 180.16 c -89.503 180.16 88.585 179.83 87.912 179.17 c -87.239 178.51 86.903 177.61 86.903 176.46 c -86.903 175.39 87.211 174.52 87.826 173.86 c -88.442 173.20 89.256 172.87 90.267 172.87 c -91.287 172.87 92.032 173.19 92.501 173.84 c -92.971 174.48 93.206 175.50 93.206 176.89 c -88.883 176.89 l -89.010 178.22 89.740 178.88 91.073 178.88 c -91.704 178.88 92.415 178.74 93.206 178.44 c -h -88.858 175.83 m -91.359 175.83 l -91.359 174.64 90.976 174.05 90.210 174.05 c -89.431 174.05 88.981 174.64 88.858 175.83 c -h -93.974 182.27 m -93.974 181.25 l -100.47 181.25 l -100.47 182.27 l -h -101.65 180.00 m -101.65 173.03 l -103.53 173.03 l -103.53 180.00 l -h -101.65 171.86 m -101.65 170.29 l -103.53 170.29 l -103.53 171.86 l -h -105.88 180.00 m -105.88 173.03 l -107.75 173.03 l -107.75 174.34 l -108.37 173.36 109.16 172.87 110.13 172.87 c -110.75 172.87 111.24 173.07 111.60 173.46 c -111.96 173.86 112.14 174.39 112.14 175.07 c -112.14 180.00 l -110.26 180.00 l -110.26 175.54 l -110.26 174.75 110.00 174.35 109.48 174.35 c -108.88 174.35 108.31 174.77 107.75 175.61 c -107.75 180.00 l -h -114.23 179.78 m -114.23 178.40 l -115.16 178.79 115.96 178.98 116.62 178.98 c -117.39 178.98 117.77 178.72 117.77 178.20 c -117.77 177.86 117.45 177.56 116.82 177.31 c -116.18 177.05 l -115.49 176.78 115.00 176.47 114.71 176.15 c -114.41 175.83 114.26 175.43 114.26 174.96 c -114.26 174.30 114.51 173.79 115.02 173.42 c -115.52 173.05 116.22 172.87 117.13 172.87 c -117.70 172.87 118.37 172.95 119.16 173.12 c -119.16 174.44 l -118.40 174.18 117.78 174.05 117.28 174.05 c -116.50 174.05 116.11 174.29 116.11 174.77 c -116.11 175.09 116.40 175.36 116.98 175.58 c -117.52 175.79 l -118.34 176.09 118.91 176.41 119.23 176.72 c -119.55 177.04 119.71 177.45 119.71 177.95 c -119.71 178.61 119.44 179.14 118.89 179.55 c -118.35 179.95 117.64 180.16 116.77 180.16 c -115.93 180.16 115.08 180.03 114.23 179.78 c -h -125.36 179.96 m -124.91 180.09 124.56 180.16 124.30 180.16 c -122.67 180.16 121.85 179.40 121.85 177.87 c -121.85 174.20 l -121.07 174.20 l -121.07 173.03 l -121.85 173.03 l -121.85 171.86 l -123.73 171.64 l -123.73 173.03 l -125.22 173.03 l -125.22 174.20 l -123.73 174.20 l -123.73 177.63 l -123.73 178.48 124.08 178.91 124.78 178.91 c -124.94 178.91 125.13 178.88 125.36 178.82 c -h -130.45 179.25 m -129.82 179.86 129.15 180.16 128.44 180.16 c -127.83 180.16 127.33 179.97 126.95 179.60 c -126.57 179.23 126.38 178.75 126.38 178.15 c -126.38 177.38 126.69 176.79 127.30 176.37 c -127.92 175.96 128.80 175.75 129.95 175.75 c -130.45 175.75 l -130.45 175.11 l -130.45 174.39 130.04 174.03 129.21 174.03 c -128.48 174.03 127.74 174.23 126.99 174.65 c -126.99 173.35 l -127.84 173.03 128.68 172.87 129.51 172.87 c -131.34 172.87 132.25 173.60 132.25 175.05 c -132.25 178.13 l -132.25 178.68 132.43 178.95 132.78 178.95 c -132.84 178.95 132.92 178.94 133.02 178.93 c -133.07 179.98 l -132.67 180.10 132.32 180.16 132.02 180.16 c -131.25 180.16 130.75 179.86 130.53 179.25 c -h -130.45 178.24 m -130.45 176.83 l -130.00 176.83 l -128.79 176.83 128.18 177.21 128.18 177.97 c -128.18 178.23 128.27 178.44 128.45 178.62 c -128.62 178.80 128.84 178.88 129.10 178.88 c -129.54 178.88 129.99 178.67 130.45 178.24 c -h -134.68 180.00 m -134.68 173.03 l -136.55 173.03 l -136.55 174.34 l -137.17 173.36 137.96 172.87 138.93 172.87 c -139.55 172.87 140.04 173.07 140.40 173.46 c -140.76 173.86 140.94 174.39 140.94 175.07 c -140.94 180.00 l -139.06 180.00 l -139.06 175.54 l -139.06 174.75 138.80 174.35 138.27 174.35 c -137.68 174.35 137.10 174.77 136.55 175.61 c -136.55 180.00 l -h -148.35 179.85 m -147.58 180.06 146.85 180.16 146.19 180.16 c -145.08 180.16 144.20 179.83 143.55 179.18 c -142.91 178.52 142.58 177.63 142.58 176.51 c -142.58 175.37 142.92 174.48 143.58 173.84 c -144.25 173.19 145.16 172.87 146.34 172.87 c -146.90 172.87 147.56 172.96 148.30 173.14 c -148.30 174.50 l -147.53 174.25 146.91 174.13 146.45 174.13 c -145.89 174.13 145.43 174.34 145.09 174.78 c -144.75 175.21 144.58 175.78 144.58 176.50 c -144.58 177.23 144.76 177.81 145.13 178.25 c -145.50 178.69 146.00 178.91 146.62 178.91 c -147.18 178.91 147.76 178.79 148.35 178.55 c -h -155.81 179.77 m -154.91 180.03 154.07 180.16 153.27 180.16 c -152.10 180.16 151.19 179.83 150.51 179.17 c -149.84 178.51 149.50 177.61 149.50 176.46 c -149.50 175.39 149.81 174.52 150.43 173.86 c -151.04 173.20 151.86 172.87 152.87 172.87 c -153.89 172.87 154.63 173.19 155.10 173.84 c -155.57 174.48 155.81 175.50 155.81 176.89 c -151.48 176.89 l -151.61 178.22 152.34 178.88 153.67 178.88 c -154.30 178.88 155.02 178.74 155.81 178.44 c -h -151.46 175.83 m -153.96 175.83 l -153.96 174.64 153.58 174.05 152.81 174.05 c -152.03 174.05 151.58 174.64 151.46 175.83 c -h -f -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -442.00 234.00 m -553.00 234.00 l -553.00 251.00 l -442.00 251.00 l -442.00 234.00 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -442.50 234.50 m -649.50 234.50 l -649.50 302.50 l -442.50 302.50 l -442.50 234.50 l -h -S -442.50 251.50 m -552.50 251.50 l -S -552.50 251.50 m -554.50 247.50 l -S -554.50 247.50 m -554.50 234.50 l -S -449.59 247.25 m -448.96 247.86 448.29 248.16 447.58 248.16 c -446.97 248.16 446.47 247.97 446.09 247.60 c -445.71 247.23 445.52 246.75 445.52 246.15 c -445.52 245.38 445.83 244.79 446.44 244.37 c -447.06 243.96 447.94 243.75 449.09 243.75 c -449.59 243.75 l -449.59 243.11 l -449.59 242.39 449.18 242.03 448.35 242.03 c -447.62 242.03 446.88 242.23 446.13 242.65 c -446.13 241.35 l -446.98 241.03 447.82 240.87 448.66 240.87 c -450.48 240.87 451.39 241.60 451.39 243.05 c -451.39 246.13 l -451.39 246.68 451.57 246.95 451.92 246.95 c -451.98 246.95 452.06 246.94 452.17 246.93 c -452.21 247.98 l -451.81 248.10 451.46 248.16 451.16 248.16 c -450.39 248.16 449.89 247.86 449.67 247.25 c -h -449.59 246.24 m -449.59 244.83 l -449.15 244.83 l -447.93 244.83 447.32 245.21 447.32 245.97 c -447.32 246.23 447.41 246.44 447.59 246.62 c -447.76 246.80 447.98 246.88 448.24 246.88 c -448.68 246.88 449.13 246.67 449.59 246.24 c -h -458.13 248.00 m -458.13 246.69 l -457.52 247.67 456.73 248.16 455.75 248.16 c -455.13 248.16 454.64 247.96 454.28 247.57 c -453.92 247.17 453.74 246.64 453.74 245.96 c -453.74 241.03 l -455.62 241.03 l -455.62 245.49 l -455.62 246.28 455.88 246.68 456.41 246.68 c -457.01 246.68 457.58 246.26 458.13 245.42 c -458.13 241.03 l -460.01 241.03 l -460.01 248.00 l -h -465.95 247.96 m -465.50 248.09 465.15 248.16 464.89 248.16 c -463.26 248.16 462.44 247.40 462.44 245.87 c -462.44 242.20 l -461.66 242.20 l -461.66 241.03 l -462.44 241.03 l -462.44 239.86 l -464.32 239.64 l -464.32 241.03 l -465.81 241.03 l -465.81 242.20 l -464.32 242.20 l -464.32 245.63 l -464.32 246.48 464.67 246.91 465.37 246.91 c -465.53 246.91 465.72 246.88 465.95 246.82 c -h -467.62 248.00 m -467.62 237.98 l -469.50 237.98 l -469.50 242.34 l -470.12 241.36 470.91 240.87 471.88 240.87 c -472.50 240.87 472.99 241.07 473.35 241.46 c -473.71 241.86 473.89 242.39 473.89 243.07 c -473.89 248.00 l -472.01 248.00 l -472.01 243.54 l -472.01 242.75 471.75 242.35 471.22 242.35 c -470.63 242.35 470.05 242.77 469.50 243.61 c -469.50 248.00 l -h -474.99 250.27 m -474.99 249.25 l -481.49 249.25 l -481.49 250.27 l -h -482.66 248.00 m -482.66 241.03 l -484.46 241.03 l -484.46 242.34 l -485.01 241.36 485.80 240.87 486.81 240.87 c -487.34 240.87 487.77 241.00 488.11 241.26 c -488.45 241.52 488.65 241.88 488.73 242.34 c -489.38 241.36 490.17 240.87 491.09 240.87 c -492.36 240.87 493.00 241.57 493.00 242.98 c -493.00 248.00 l -491.20 248.00 l -491.20 243.59 l -491.20 242.77 490.92 242.36 490.37 242.36 c -489.81 242.36 489.26 242.76 488.73 243.58 c -488.73 248.00 l -486.93 248.00 l -486.93 243.59 l -486.93 242.77 486.65 242.35 486.09 242.35 c -485.54 242.35 485.00 242.76 484.46 243.58 c -484.46 248.00 l -h -495.27 248.00 m -495.27 241.03 l -497.15 241.03 l -497.15 248.00 l -h -495.27 239.86 m -495.27 238.29 l -497.15 238.29 l -497.15 239.86 l -h -503.88 248.00 m -503.88 246.69 l -503.40 247.67 502.64 248.16 501.60 248.16 c -500.76 248.16 500.11 247.85 499.63 247.24 c -499.15 246.62 498.91 245.78 498.91 244.71 c -498.91 243.54 499.18 242.61 499.72 241.91 c -500.26 241.22 500.98 240.87 501.89 240.87 c -502.62 240.87 503.28 241.16 503.88 241.73 c -503.88 237.98 l -505.77 237.98 l -505.77 248.00 l -h -503.88 242.85 m -503.43 242.34 502.94 242.09 502.43 242.09 c -501.97 242.09 501.60 242.31 501.32 242.76 c -501.05 243.20 500.91 243.80 500.91 244.55 c -500.91 245.97 501.37 246.69 502.28 246.69 c -502.84 246.69 503.37 246.33 503.88 245.61 c -h -512.49 248.00 m -512.49 246.69 l -512.01 247.67 511.25 248.16 510.22 248.16 c -509.38 248.16 508.72 247.85 508.24 247.24 c -507.76 246.62 507.52 245.78 507.52 244.71 c -507.52 243.54 507.79 242.61 508.33 241.91 c -508.87 241.22 509.60 240.87 510.51 240.87 c -511.24 240.87 511.90 241.16 512.49 241.73 c -512.49 237.98 l -514.38 237.98 l -514.38 248.00 l -h -512.49 242.85 m -512.04 242.34 511.56 242.09 511.04 242.09 c -510.58 242.09 510.21 242.31 509.94 242.76 c -509.66 243.20 509.52 243.80 509.52 244.55 c -509.52 245.97 509.98 246.69 510.90 246.69 c -511.45 246.69 511.99 246.33 512.49 245.61 c -h -516.72 248.00 m -516.72 237.98 l -518.60 237.98 l -518.60 248.00 l -h -526.62 247.77 m -525.73 248.03 524.89 248.16 524.09 248.16 c -522.92 248.16 522.00 247.83 521.33 247.17 c -520.66 246.51 520.32 245.61 520.32 244.46 c -520.32 243.39 520.63 242.52 521.24 241.86 c -521.86 241.20 522.67 240.87 523.69 240.87 c -524.71 240.87 525.45 241.19 525.92 241.84 c -526.39 242.48 526.62 243.50 526.62 244.89 c -522.30 244.89 l -522.43 246.22 523.16 246.88 524.49 246.88 c -525.12 246.88 525.83 246.74 526.62 246.44 c -h -522.28 243.83 m -524.78 243.83 l -524.78 242.64 524.39 242.05 523.63 242.05 c -522.85 242.05 522.40 242.64 522.28 243.83 c -h -529.80 248.00 m -527.86 241.03 l -529.61 241.03 l -530.98 245.91 l -532.44 241.03 l -534.06 241.03 l -535.38 245.94 l -536.86 241.03 l -538.16 241.03 l -536.10 248.00 l -534.29 248.00 l -533.02 243.22 l -531.60 248.00 l -h -543.20 247.25 m -542.57 247.86 541.90 248.16 541.19 248.16 c -540.58 248.16 540.08 247.97 539.70 247.60 c -539.32 247.23 539.13 246.75 539.13 246.15 c -539.13 245.38 539.44 244.79 540.05 244.37 c -540.67 243.96 541.55 243.75 542.70 243.75 c -543.20 243.75 l -543.20 243.11 l -543.20 242.39 542.79 242.03 541.96 242.03 c -541.23 242.03 540.49 242.23 539.74 242.65 c -539.74 241.35 l -540.59 241.03 541.43 240.87 542.27 240.87 c -544.09 240.87 545.00 241.60 545.00 243.05 c -545.00 246.13 l -545.00 246.68 545.18 246.95 545.53 246.95 c -545.59 246.95 545.67 246.94 545.78 246.93 c -545.82 247.98 l -545.42 248.10 545.07 248.16 544.77 248.16 c -544.00 248.16 543.50 247.86 543.28 247.25 c -h -543.20 246.24 m -543.20 244.83 l -542.75 244.83 l -541.54 244.83 540.93 245.21 540.93 245.97 c -540.93 246.23 541.02 246.44 541.20 246.62 c -541.37 246.80 541.59 246.88 541.85 246.88 c -542.29 246.88 542.74 246.67 543.20 246.24 c -h -547.43 248.00 m -547.43 241.03 l -549.30 241.03 l -549.30 242.34 l -549.79 241.36 550.53 240.87 551.53 240.87 c -551.64 240.87 551.76 240.88 551.88 240.91 c -551.88 242.59 l -551.61 242.49 551.36 242.44 551.13 242.44 c -550.38 242.44 549.77 242.82 549.30 243.58 c -549.30 248.00 l -h -559.01 247.77 m -558.12 248.03 557.27 248.16 556.47 248.16 c -555.31 248.16 554.39 247.83 553.72 247.17 c -553.04 246.51 552.71 245.61 552.71 244.46 c -552.71 243.39 553.01 242.52 553.63 241.86 c -554.25 241.20 555.06 240.87 556.07 240.87 c -557.09 240.87 557.84 241.19 558.31 241.84 c -558.78 242.48 559.01 243.50 559.01 244.89 c -554.69 244.89 l -554.81 246.22 555.54 246.88 556.88 246.88 c -557.51 246.88 558.22 246.74 559.01 246.44 c -h -554.66 243.83 m -557.16 243.83 l -557.16 242.64 556.78 242.05 556.01 242.05 c -555.24 242.05 554.78 242.64 554.66 243.83 c -h -f -Q -Q -Q - -endstream -endobj - -8 0 obj - 160165 -endobj - -3 0 obj - << - /Parent null - /Type /Pages - /MediaBox [0.0000 0.0000 842.00 595.00] - /Resources 9 0 R - /Kids [6 0 R] - /Count 1 - >> -endobj - -10 0 obj - [/PDF /Text /ImageC] -endobj - -11 0 obj - << - /Alpha1 - << - /ca 1.0000 - /CA 1.0000 - /BM /Normal - /AIS false - >> - >> -endobj - -9 0 obj - << - /ProcSet 10 0 R - /ExtGState 11 0 R - >> -endobj - -4 0 obj - << - /Type /Outlines - /First 12 0 R - /Last 12 0 R - >> -endobj - -12 0 obj - << - /Parent 4 0 R - /Title (Page 1 \(untitled\)) - /Prev null - /Next null - /Dest [6 0 R /Fit] - >> -endobj - -xref -0 13 -0000000000 65535 f -0000000016 00000 n -0000000343 00000 n -0000160949 00000 n -0000161380 00000 n -0000000525 00000 n -0000000602 00000 n -0000000691 00000 n -0000160923 00000 n -0000161305 00000 n -0000161120 00000 n -0000161161 00000 n -0000161470 00000 n - -trailer -<< - /Size 12 - /Root 2 0 R - /Info 1 0 R ->> - -startxref -161614 - -%%EOF diff --git a/doc/design/use_case_2.png b/doc/design/use_case_2.png deleted file mode 100644 index 23881eb41a..0000000000 Binary files a/doc/design/use_case_2.png and /dev/null differ diff --git a/doc/design/use_case_2.sdx b/doc/design/use_case_2.sdx deleted file mode 100644 index 67ea0d3594..0000000000 --- a/doc/design/use_case_2.sdx +++ /dev/null @@ -1,76 +0,0 @@ - - - -[/c] - - -client:endpoint=serviceCatalog['compute'] - -[c:create_instance] -client:success=nova.createInstance -nova:tenant = parse(url) -[c:auth_middleware] -nova:user, roles=keystone.validate -[/c] -nova:authorize = can_haz(context, user, 'create_instance', tenant_id) -nova:execute create_instance -[/c] -client:200 OK]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/design/use_case_3.pdf b/doc/design/use_case_3.pdf deleted file mode 100644 index b3e05db4b3..0000000000 --- a/doc/design/use_case_3.pdf +++ /dev/null @@ -1,7995 +0,0 @@ -%PDF-1.4 -%âãÏÓ - -1 0 obj - << - /Title () - /Author () - /Subject () - /Keywords () - /Creator (FreeHEP Graphics2D Driver) - /Producer (org.freehep.graphicsio.pdf.PDFGraphics2D Revision: 10516 ) - /CreationDate (D:20111117212601-06'00') - /ModDate (D:20111117212601-06'00') - /Trapped /False - >> -endobj - -2 0 obj - << - /Type /Catalog - /Pages 3 0 R - /Outlines 4 0 R - /PageMode /UseOutlines - /ViewerPreferences 5 0 R - /OpenAction [6 0 R /Fit] - >> -endobj - -5 0 obj - << - /FitWindow true - /CenterWindow false - >> -endobj - -6 0 obj - << - /Parent 3 0 R - /Type /Page - /Contents 7 0 R - >> -endobj - -7 0 obj - << - /Length 8 0 R - >> -stream -.93277 0.0000 0.0000 -.93277 28.303 575.00 cm -q -0.0000 0.0000 m -842.00 0.0000 l -842.00 595.00 l -0.0000 595.00 l -h -W -n -q -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -1.0000 0.0000 0.0000 1.0000 0.0000 0.0000 cm -Q -q -1.0000 0.0000 0.0000 1.0000 0.0000 0.0000 cm -0.0000 0.0000 m -0.0000 595.00 l -842.00 595.00 l -842.00 0.0000 l -h -W -n -q -.66772 0.0000 0.0000 .66772 0.0000 0.0000 cm -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -0.0000 0.0000 m -1261.0 0.0000 l -1261.0 892.00 l -0.0000 892.00 l -0.0000 0.0000 l -h -f -1.0000 0.0000 0.0000 1.0000 0.0000 0.0000 cm -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -0 J -1 j -1.0000 M -[ 1.0000 2.0000] 0.0000 d -66.500 44.500 m -66.500 54.500 l -S -66.500 44.500 m -66.500 54.500 l -S -[] 0.0000 d -/Alpha1 gs -.93333 .93333 .93333 rg -.93333 .93333 .93333 RG -62.000 54.000 m -70.000 54.000 l -70.000 510.00 l -62.000 510.00 l -62.000 54.000 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -62.500 54.500 m -70.500 54.500 l -70.500 510.50 l -62.500 510.50 l -62.500 54.500 l -h -S -[ 1.0000 2.0000] 0.0000 d -66.500 510.50 m -66.500 510.50 l -S -534.50 44.500 m -534.50 226.50 l -S -534.50 44.500 m -534.50 226.50 l -S -[] 0.0000 d -/Alpha1 gs -.93333 .93333 .93333 rg -.93333 .93333 .93333 RG -530.00 226.00 m -538.00 226.00 l -538.00 244.00 l -530.00 244.00 l -530.00 226.00 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -530.50 226.50 m -538.50 226.50 l -538.50 244.50 l -530.50 244.50 l -530.50 226.50 l -h -S -[ 1.0000 2.0000] 0.0000 d -534.50 244.50 m -534.50 378.50 l -S -[] 0.0000 d -/Alpha1 gs -.93333 .93333 .93333 rg -.93333 .93333 .93333 RG -530.00 378.00 m -538.00 378.00 l -538.00 396.00 l -530.00 396.00 l -530.00 378.00 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -530.50 378.50 m -538.50 378.50 l -538.50 396.50 l -530.50 396.50 l -530.50 378.50 l -h -S -[ 1.0000 2.0000] 0.0000 d -534.50 396.50 m -534.50 510.50 l -S -854.50 44.500 m -854.50 102.50 l -S -854.50 44.500 m -854.50 102.50 l -S -[] 0.0000 d -/Alpha1 gs -.93333 .93333 .93333 rg -.93333 .93333 .93333 RG -850.00 102.00 m -858.00 102.00 l -858.00 120.00 l -850.00 120.00 l -850.00 102.00 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -850.50 102.50 m -858.50 102.50 l -858.50 120.50 l -850.50 120.50 l -850.50 102.50 l -h -S -[ 1.0000 2.0000] 0.0000 d -854.50 120.50 m -854.50 310.50 l -S -[] 0.0000 d -/Alpha1 gs -.93333 .93333 .93333 rg -.93333 .93333 .93333 RG -850.00 310.00 m -858.00 310.00 l -858.00 472.00 l -850.00 472.00 l -850.00 310.00 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -850.50 310.50 m -858.50 310.50 l -858.50 472.50 l -850.50 472.50 l -850.50 310.50 l -h -S -[ 1.0000 2.0000] 0.0000 d -854.50 472.50 m -854.50 510.50 l -S -79.639 307.15 m -78.779 307.15 78.066 306.83 77.500 306.19 c -76.934 305.55 76.650 304.75 76.650 303.78 c -76.650 302.75 76.931 301.94 77.491 301.36 c -78.052 300.79 78.834 300.50 79.838 300.50 c -80.334 300.50 80.889 300.56 81.502 300.70 c -81.502 301.67 l -80.850 301.48 80.318 301.38 79.908 301.38 c -79.318 301.38 78.845 301.60 78.487 302.05 c -78.130 302.49 77.951 303.08 77.951 303.82 c -77.951 304.53 78.135 305.11 78.502 305.55 c -78.869 305.99 79.350 306.21 79.943 306.21 c -80.471 306.21 81.014 306.08 81.572 305.81 c -81.572 306.81 l -80.826 307.03 80.182 307.15 79.639 307.15 c -h -83.301 307.00 m -83.301 300.64 l -84.455 300.64 l -84.455 301.83 l -84.912 300.94 85.576 300.50 86.447 300.50 c -86.564 300.50 86.688 300.51 86.816 300.53 c -86.816 301.60 l -86.617 301.54 86.441 301.50 86.289 301.50 c -85.559 301.50 84.947 301.94 84.455 302.80 c -84.455 307.00 l -h -92.875 306.79 m -92.102 307.03 91.439 307.15 90.889 307.15 c -89.951 307.15 89.187 306.83 88.595 306.21 c -88.003 305.59 87.707 304.78 87.707 303.79 c -87.707 302.82 87.968 302.03 88.489 301.42 c -89.011 300.80 89.678 300.49 90.490 300.49 c -91.260 300.49 91.854 300.76 92.274 301.31 c -92.694 301.86 92.904 302.63 92.904 303.64 c -92.898 304.00 l -88.885 304.00 l -89.053 305.51 89.793 306.27 91.105 306.27 c -91.586 306.27 92.176 306.14 92.875 305.88 c -h -88.938 303.13 m -91.744 303.13 l -91.744 301.95 91.303 301.36 90.420 301.36 c -89.533 301.36 89.039 301.95 88.938 303.13 c -h -98.178 306.19 m -97.486 306.83 96.820 307.15 96.180 307.15 c -95.652 307.15 95.215 306.98 94.867 306.65 c -94.520 306.32 94.346 305.90 94.346 305.40 c -94.346 304.71 94.638 304.17 95.222 303.80 c -95.806 303.42 96.643 303.24 97.732 303.24 c -98.008 303.24 l -98.008 302.47 l -98.008 301.73 97.629 301.36 96.871 301.36 c -96.262 301.36 95.604 301.55 94.896 301.93 c -94.896 300.97 l -95.674 300.65 96.402 300.50 97.082 300.50 c -97.793 300.50 98.317 300.66 98.655 300.98 c -98.993 301.30 99.162 301.79 99.162 302.47 c -99.162 305.35 l -99.162 306.01 99.365 306.34 99.771 306.34 c -99.822 306.34 99.896 306.34 99.994 306.32 c -100.08 306.96 l -99.814 307.08 99.525 307.15 99.209 307.15 c -98.670 307.15 98.326 306.83 98.178 306.19 c -h -98.008 305.56 m -98.008 303.92 l -97.621 303.91 l -96.988 303.91 96.477 304.03 96.086 304.27 c -95.695 304.51 95.500 304.82 95.500 305.21 c -95.500 305.49 95.598 305.72 95.793 305.92 c -95.988 306.11 96.227 306.20 96.508 306.20 c -96.988 306.20 97.488 305.99 98.008 305.56 c -h -103.43 307.15 m -102.85 307.15 102.39 306.98 102.06 306.64 c -101.73 306.31 101.57 305.84 101.57 305.24 c -101.57 301.50 l -100.77 301.50 l -100.77 300.64 l -101.57 300.64 l -101.57 299.48 l -102.72 299.37 l -102.72 300.64 l -104.39 300.64 l -104.39 301.50 l -102.72 301.50 l -102.72 305.03 l -102.72 305.86 103.08 306.28 103.80 306.28 c -103.96 306.28 104.14 306.25 104.36 306.20 c -104.36 307.00 l -104.00 307.10 103.70 307.15 103.43 307.15 c -h -110.68 306.79 m -109.90 307.03 109.24 307.15 108.69 307.15 c -107.75 307.15 106.99 306.83 106.40 306.21 c -105.80 305.59 105.51 304.78 105.51 303.79 c -105.51 302.82 105.77 302.03 106.29 301.42 c -106.81 300.80 107.48 300.49 108.29 300.49 c -109.06 300.49 109.66 300.76 110.08 301.31 c -110.50 301.86 110.71 302.63 110.71 303.64 c -110.70 304.00 l -106.69 304.00 l -106.85 305.51 107.59 306.27 108.91 306.27 c -109.39 306.27 109.98 306.14 110.68 305.88 c -h -106.74 303.13 m -109.54 303.13 l -109.54 301.95 109.10 301.36 108.22 301.36 c -107.33 301.36 106.84 301.95 106.74 303.13 c -h -112.66 307.00 m -112.66 298.33 l -113.89 298.33 l -113.89 307.00 l -h -116.15 307.00 m -116.15 300.64 l -117.31 300.64 l -117.31 301.83 l -117.92 300.94 118.66 300.50 119.55 300.50 c -120.10 300.50 120.54 300.67 120.87 301.02 c -121.19 301.37 121.36 301.84 121.36 302.43 c -121.36 307.00 l -120.20 307.00 l -120.20 302.80 l -120.20 302.33 120.13 302.00 120.00 301.79 c -119.86 301.59 119.63 301.49 119.31 301.49 c -118.60 301.49 117.93 301.96 117.31 302.88 c -117.31 307.00 l -h -125.29 307.15 m -124.76 307.15 124.12 307.02 123.37 306.78 c -123.37 305.72 l -124.12 306.09 124.78 306.28 125.34 306.28 c -125.67 306.28 125.94 306.19 126.16 306.01 c -126.38 305.83 126.49 305.61 126.49 305.34 c -126.49 304.94 126.18 304.62 125.57 304.36 c -124.90 304.07 l -123.90 303.66 123.40 303.06 123.40 302.28 c -123.40 301.73 123.60 301.29 123.99 300.97 c -124.38 300.66 124.92 300.50 125.61 300.50 c -125.96 300.50 126.40 300.54 126.92 300.64 c -127.16 300.69 l -127.16 301.65 l -126.52 301.46 126.01 301.36 125.63 301.36 c -124.89 301.36 124.52 301.63 124.52 302.17 c -124.52 302.52 124.80 302.81 125.36 303.05 c -125.92 303.29 l -126.54 303.55 126.99 303.83 127.25 304.13 c -127.51 304.42 127.64 304.79 127.64 305.23 c -127.64 305.79 127.42 306.25 126.98 306.61 c -126.54 306.97 125.98 307.15 125.29 307.15 c -h -131.63 307.15 m -131.04 307.15 130.59 306.98 130.26 306.64 c -129.93 306.31 129.77 305.84 129.77 305.24 c -129.77 301.50 l -128.97 301.50 l -128.97 300.64 l -129.77 300.64 l -129.77 299.48 l -130.92 299.37 l -130.92 300.64 l -132.58 300.64 l -132.58 301.50 l -130.92 301.50 l -130.92 305.03 l -130.92 305.86 131.28 306.28 132.00 306.28 c -132.15 306.28 132.34 306.25 132.55 306.20 c -132.55 307.00 l -132.20 307.10 131.89 307.15 131.63 307.15 c -h -137.49 306.19 m -136.80 306.83 136.13 307.15 135.49 307.15 c -134.96 307.15 134.53 306.98 134.18 306.65 c -133.83 306.32 133.66 305.90 133.66 305.40 c -133.66 304.71 133.95 304.17 134.53 303.80 c -135.12 303.42 135.95 303.24 137.04 303.24 c -137.32 303.24 l -137.32 302.47 l -137.32 301.73 136.94 301.36 136.18 301.36 c -135.57 301.36 134.91 301.55 134.21 301.93 c -134.21 300.97 l -134.98 300.65 135.71 300.50 136.39 300.50 c -137.10 300.50 137.63 300.66 137.97 300.98 c -138.30 301.30 138.47 301.79 138.47 302.47 c -138.47 305.35 l -138.47 306.01 138.68 306.34 139.08 306.34 c -139.13 306.34 139.21 306.34 139.30 306.32 c -139.39 306.96 l -139.12 307.08 138.84 307.15 138.52 307.15 c -137.98 307.15 137.64 306.83 137.49 306.19 c -h -137.32 305.56 m -137.32 303.92 l -136.93 303.91 l -136.30 303.91 135.79 304.03 135.40 304.27 c -135.01 304.51 134.81 304.82 134.81 305.21 c -134.81 305.49 134.91 305.72 135.10 305.92 c -135.30 306.11 135.54 306.20 135.82 306.20 c -136.30 306.20 136.80 305.99 137.32 305.56 c -h -140.83 307.00 m -140.83 300.64 l -141.99 300.64 l -141.99 301.83 l -142.60 300.94 143.34 300.50 144.23 300.50 c -144.78 300.50 145.22 300.67 145.54 301.02 c -145.87 301.37 146.04 301.84 146.04 302.43 c -146.04 307.00 l -144.88 307.00 l -144.88 302.80 l -144.88 302.33 144.81 302.00 144.67 301.79 c -144.54 301.59 144.31 301.49 143.99 301.49 c -143.28 301.49 142.61 301.96 141.99 302.88 c -141.99 307.00 l -h -150.77 307.15 m -149.91 307.15 149.19 306.83 148.63 306.19 c -148.06 305.55 147.78 304.75 147.78 303.78 c -147.78 302.75 148.06 301.94 148.62 301.36 c -149.18 300.79 149.96 300.50 150.96 300.50 c -151.46 300.50 152.02 300.56 152.63 300.70 c -152.63 301.67 l -151.98 301.48 151.45 301.38 151.04 301.38 c -150.45 301.38 149.97 301.60 149.61 302.05 c -149.26 302.49 149.08 303.08 149.08 303.82 c -149.08 304.53 149.26 305.11 149.63 305.55 c -150.00 305.99 150.48 306.21 151.07 306.21 c -151.60 306.21 152.14 306.08 152.70 305.81 c -152.70 306.81 l -151.95 307.03 151.31 307.15 150.77 307.15 c -h -159.09 306.79 m -158.32 307.03 157.66 307.15 157.11 307.15 c -156.17 307.15 155.40 306.83 154.81 306.21 c -154.22 305.59 153.92 304.78 153.92 303.79 c -153.92 302.82 154.18 302.03 154.71 301.42 c -155.23 300.80 155.89 300.49 156.71 300.49 c -157.48 300.49 158.07 300.76 158.49 301.31 c -158.91 301.86 159.12 302.63 159.12 303.64 c -159.12 304.00 l -155.10 304.00 l -155.27 305.51 156.01 306.27 157.32 306.27 c -157.80 306.27 158.39 306.14 159.09 305.88 c -h -155.15 303.13 m -157.96 303.13 l -157.96 301.95 157.52 301.36 156.64 301.36 c -155.75 301.36 155.26 301.95 155.15 303.13 c -h -168.20 307.00 m -161.26 303.53 l -168.20 300.06 l -168.20 301.03 l -163.20 303.53 l -168.20 306.03 l -h -172.56 307.15 m -171.98 307.15 171.52 306.98 171.19 306.64 c -170.86 306.31 170.70 305.84 170.70 305.24 c -170.70 301.50 l -169.90 301.50 l -169.90 300.64 l -170.70 300.64 l -170.70 299.48 l -171.85 299.37 l -171.85 300.64 l -173.52 300.64 l -173.52 301.50 l -171.85 301.50 l -171.85 305.03 l -171.85 305.86 172.21 306.28 172.93 306.28 c -173.08 306.28 173.27 306.25 173.49 306.20 c -173.49 307.00 l -173.13 307.10 172.82 307.15 172.56 307.15 c -h -177.63 307.15 m -176.72 307.15 175.99 306.84 175.45 306.24 c -174.91 305.64 174.64 304.83 174.64 303.82 c -174.64 302.79 174.91 301.99 175.45 301.39 c -176.00 300.79 176.74 300.50 177.67 300.50 c -178.61 300.50 179.34 300.79 179.89 301.39 c -180.43 301.99 180.71 302.79 180.71 303.81 c -180.71 304.85 180.43 305.66 179.89 306.26 c -179.34 306.85 178.59 307.15 177.63 307.15 c -h -177.65 306.28 m -178.87 306.28 179.48 305.46 179.48 303.81 c -179.48 302.18 178.88 301.36 177.67 301.36 c -176.47 301.36 175.87 302.18 175.87 303.82 c -175.87 305.46 176.46 306.28 177.65 306.28 c -h -182.51 307.00 m -182.51 297.75 l -183.67 297.75 l -183.67 303.72 l -186.36 300.64 l -187.60 300.64 l -185.03 303.61 l -188.14 307.00 l -186.66 307.00 l -183.67 303.74 l -183.67 307.00 l -h -194.19 306.79 m -193.42 307.03 192.75 307.15 192.20 307.15 c -191.27 307.15 190.50 306.83 189.91 306.21 c -189.32 305.59 189.02 304.78 189.02 303.79 c -189.02 302.82 189.28 302.03 189.80 301.42 c -190.33 300.80 190.99 300.49 191.80 300.49 c -192.57 300.49 193.17 300.76 193.59 301.31 c -194.01 301.86 194.22 302.63 194.22 303.64 c -194.21 304.00 l -190.20 304.00 l -190.37 305.51 191.11 306.27 192.42 306.27 c -192.90 306.27 193.49 306.14 194.19 305.88 c -h -190.25 303.13 m -193.06 303.13 l -193.06 301.95 192.62 301.36 191.73 301.36 c -190.85 301.36 190.35 301.95 190.25 303.13 c -h -196.21 307.00 m -196.21 300.64 l -197.37 300.64 l -197.37 301.83 l -197.97 300.94 198.72 300.50 199.60 300.50 c -200.15 300.50 200.59 300.67 200.92 301.02 c -201.25 301.37 201.41 301.84 201.41 302.43 c -201.41 307.00 l -200.26 307.00 l -200.26 302.80 l -200.26 302.33 200.19 302.00 200.05 301.79 c -199.91 301.59 199.68 301.49 199.36 301.49 c -198.66 301.49 197.99 301.96 197.37 302.88 c -197.37 307.00 l -h -203.68 308.88 m -203.68 308.45 l -204.05 308.34 204.24 307.90 204.24 307.12 c -204.24 307.00 l -203.68 307.00 l -203.68 305.55 l -205.12 305.55 l -205.12 306.81 l -205.12 308.09 204.64 308.78 203.68 308.88 c -h -213.16 307.15 m -212.58 307.15 212.12 306.98 211.79 306.64 c -211.46 306.31 211.30 305.84 211.30 305.24 c -211.30 301.50 l -210.50 301.50 l -210.50 300.64 l -211.30 300.64 l -211.30 299.48 l -212.45 299.37 l -212.45 300.64 l -214.12 300.64 l -214.12 301.50 l -212.45 301.50 l -212.45 305.03 l -212.45 305.86 212.81 306.28 213.53 306.28 c -213.68 306.28 213.87 306.25 214.09 306.20 c -214.09 307.00 l -213.73 307.10 213.42 307.15 213.16 307.15 c -h -220.40 306.79 m -219.63 307.03 218.97 307.15 218.42 307.15 c -217.48 307.15 216.72 306.83 216.12 306.21 c -215.53 305.59 215.24 304.78 215.24 303.79 c -215.24 302.82 215.50 302.03 216.02 301.42 c -216.54 300.80 217.21 300.49 218.02 300.49 c -218.79 300.49 219.38 300.76 219.80 301.31 c -220.22 301.86 220.43 302.63 220.43 303.64 c -220.43 304.00 l -216.41 304.00 l -216.58 305.51 217.32 306.27 218.63 306.27 c -219.12 306.27 219.71 306.14 220.40 305.88 c -h -216.47 303.13 m -219.27 303.13 l -219.27 301.95 218.83 301.36 217.95 301.36 c -217.06 301.36 216.57 301.95 216.47 303.13 c -h -222.43 307.00 m -222.43 300.64 l -223.58 300.64 l -223.58 301.83 l -224.19 300.94 224.94 300.50 225.82 300.50 c -226.37 300.50 226.81 300.67 227.14 301.02 c -227.46 301.37 227.63 301.84 227.63 302.43 c -227.63 307.00 l -226.47 307.00 l -226.47 302.80 l -226.47 302.33 226.41 302.00 226.27 301.79 c -226.13 301.59 225.90 301.49 225.58 301.49 c -224.87 301.49 224.21 301.96 223.58 302.88 c -223.58 307.00 l -h -233.15 306.19 m -232.46 306.83 231.80 307.15 231.16 307.15 c -230.63 307.15 230.19 306.98 229.84 306.65 c -229.50 306.32 229.32 305.90 229.32 305.40 c -229.32 304.71 229.61 304.17 230.20 303.80 c -230.78 303.42 231.62 303.24 232.71 303.24 c -232.98 303.24 l -232.98 302.47 l -232.98 301.73 232.61 301.36 231.85 301.36 c -231.24 301.36 230.58 301.55 229.87 301.93 c -229.87 300.97 l -230.65 300.65 231.38 300.50 232.06 300.50 c -232.77 300.50 233.29 300.66 233.63 300.98 c -233.97 301.30 234.14 301.79 234.14 302.47 c -234.14 305.35 l -234.14 306.01 234.34 306.34 234.75 306.34 c -234.80 306.34 234.87 306.34 234.97 306.32 c -235.05 306.96 l -234.79 307.08 234.50 307.15 234.19 307.15 c -233.65 307.15 233.30 306.83 233.15 306.19 c -h -232.98 305.56 m -232.98 303.92 l -232.60 303.91 l -231.96 303.91 231.45 304.03 231.06 304.27 c -230.67 304.51 230.48 304.82 230.48 305.21 c -230.48 305.49 230.57 305.72 230.77 305.92 c -230.96 306.11 231.20 306.20 231.48 306.20 c -231.96 306.20 232.46 305.99 232.98 305.56 c -h -236.50 307.00 m -236.50 300.64 l -237.65 300.64 l -237.65 301.83 l -238.26 300.94 239.01 300.50 239.89 300.50 c -240.44 300.50 240.88 300.67 241.21 301.02 c -241.54 301.37 241.70 301.84 241.70 302.43 c -241.70 307.00 l -240.55 307.00 l -240.55 302.80 l -240.55 302.33 240.48 302.00 240.34 301.79 c -240.20 301.59 239.97 301.49 239.65 301.49 c -238.95 301.49 238.28 301.96 237.65 302.88 c -237.65 307.00 l -h -245.86 307.15 m -245.27 307.15 244.81 306.98 244.49 306.64 c -244.16 306.31 243.99 305.84 243.99 305.24 c -243.99 301.50 l -243.20 301.50 l -243.20 300.64 l -243.99 300.64 l -243.99 299.48 l -245.15 299.37 l -245.15 300.64 l -246.81 300.64 l -246.81 301.50 l -245.15 301.50 l -245.15 305.03 l -245.15 305.86 245.51 306.28 246.23 306.28 c -246.38 306.28 246.56 306.25 246.78 306.20 c -246.78 307.00 l -246.43 307.10 246.12 307.15 245.86 307.15 c -h -247.28 309.02 m -247.28 308.15 l -253.28 308.15 l -253.28 309.02 l -h -254.44 307.00 m -254.44 300.64 l -255.59 300.64 l -255.59 307.00 l -h -254.44 299.48 m -254.44 298.33 l -255.59 298.33 l -255.59 299.48 l -h -261.99 307.00 m -261.99 305.80 l -261.52 306.70 260.81 307.15 259.87 307.15 c -259.10 307.15 258.50 306.87 258.06 306.31 c -257.62 305.75 257.40 304.99 257.40 304.02 c -257.40 302.96 257.65 302.11 258.15 301.46 c -258.65 300.82 259.30 300.50 260.11 300.50 c -260.87 300.50 261.49 300.79 261.99 301.36 c -261.99 297.75 l -263.15 297.75 l -263.15 307.00 l -h -261.99 302.15 m -261.39 301.63 260.82 301.36 260.29 301.36 c -259.18 301.36 258.63 302.21 258.63 303.90 c -258.63 305.39 259.12 306.13 260.11 306.13 c -260.75 306.13 261.38 305.78 261.99 305.08 c -h -265.60 307.00 m -272.54 303.53 l -265.60 300.06 l -265.60 301.03 l -270.60 303.53 l -265.60 306.03 l -h -f -[] 0.0000 d -70.500 310.50 m -850.50 310.50 l -S -850.00 310.00 m -844.00 304.00 l -844.00 316.00 l -h -f* -79.639 99.146 m -78.779 99.146 78.066 98.828 77.500 98.191 c -76.934 97.555 76.650 96.752 76.650 95.783 c -76.650 94.748 76.931 93.941 77.491 93.363 c -78.052 92.785 78.834 92.496 79.838 92.496 c -80.334 92.496 80.889 92.564 81.502 92.701 c -81.502 93.668 l -80.850 93.477 80.318 93.381 79.908 93.381 c -79.318 93.381 78.845 93.603 78.487 94.046 c -78.130 94.489 77.951 95.080 77.951 95.818 c -77.951 96.533 78.135 97.111 78.502 97.553 c -78.869 97.994 79.350 98.215 79.943 98.215 c -80.471 98.215 81.014 98.080 81.572 97.811 c -81.572 98.807 l -80.826 99.033 80.182 99.146 79.639 99.146 c -h -83.301 99.000 m -83.301 92.637 l -84.455 92.637 l -84.455 93.832 l -84.912 92.941 85.576 92.496 86.447 92.496 c -86.564 92.496 86.688 92.506 86.816 92.525 c -86.816 93.604 l -86.617 93.537 86.441 93.504 86.289 93.504 c -85.559 93.504 84.947 93.938 84.455 94.805 c -84.455 99.000 l -h -92.875 98.795 m -92.102 99.029 91.439 99.146 90.889 99.146 c -89.951 99.146 89.187 98.835 88.595 98.212 c -88.003 97.589 87.707 96.781 87.707 95.789 c -87.707 94.824 87.968 94.033 88.489 93.416 c -89.011 92.799 89.678 92.490 90.490 92.490 c -91.260 92.490 91.854 92.764 92.274 93.311 c -92.694 93.857 92.904 94.635 92.904 95.643 c -92.898 96.000 l -88.885 96.000 l -89.053 97.512 89.793 98.268 91.105 98.268 c -91.586 98.268 92.176 98.139 92.875 97.881 c -h -88.938 95.133 m -91.744 95.133 l -91.744 93.949 91.303 93.357 90.420 93.357 c -89.533 93.357 89.039 93.949 88.938 95.133 c -h -98.178 98.191 m -97.486 98.828 96.820 99.146 96.180 99.146 c -95.652 99.146 95.215 98.981 94.867 98.651 c -94.520 98.321 94.346 97.904 94.346 97.400 c -94.346 96.705 94.638 96.171 95.222 95.798 c -95.806 95.425 96.643 95.238 97.732 95.238 c -98.008 95.238 l -98.008 94.471 l -98.008 93.732 97.629 93.363 96.871 93.363 c -96.262 93.363 95.604 93.551 94.896 93.926 c -94.896 92.971 l -95.674 92.654 96.402 92.496 97.082 92.496 c -97.793 92.496 98.317 92.656 98.655 92.977 c -98.993 93.297 99.162 93.795 99.162 94.471 c -99.162 97.354 l -99.162 98.014 99.365 98.344 99.771 98.344 c -99.822 98.344 99.896 98.336 99.994 98.320 c -100.08 98.959 l -99.814 99.084 99.525 99.146 99.209 99.146 c -98.670 99.146 98.326 98.828 98.178 98.191 c -h -98.008 97.564 m -98.008 95.918 l -97.621 95.906 l -96.988 95.906 96.477 96.026 96.086 96.267 c -95.695 96.507 95.500 96.822 95.500 97.213 c -95.500 97.490 95.598 97.725 95.793 97.916 c -95.988 98.107 96.227 98.203 96.508 98.203 c -96.988 98.203 97.488 97.990 98.008 97.564 c -h -103.43 99.146 m -102.85 99.146 102.39 98.979 102.06 98.643 c -101.73 98.307 101.57 97.840 101.57 97.242 c -101.57 93.504 l -100.77 93.504 l -100.77 92.637 l -101.57 92.637 l -101.57 91.482 l -102.72 91.371 l -102.72 92.637 l -104.39 92.637 l -104.39 93.504 l -102.72 93.504 l -102.72 97.031 l -102.72 97.863 103.08 98.279 103.80 98.279 c -103.96 98.279 104.14 98.254 104.36 98.203 c -104.36 99.000 l -104.00 99.098 103.70 99.146 103.43 99.146 c -h -110.68 98.795 m -109.90 99.029 109.24 99.146 108.69 99.146 c -107.75 99.146 106.99 98.835 106.40 98.212 c -105.80 97.589 105.51 96.781 105.51 95.789 c -105.51 94.824 105.77 94.033 106.29 93.416 c -106.81 92.799 107.48 92.490 108.29 92.490 c -109.06 92.490 109.66 92.764 110.08 93.311 c -110.50 93.857 110.71 94.635 110.71 95.643 c -110.70 96.000 l -106.69 96.000 l -106.85 97.512 107.59 98.268 108.91 98.268 c -109.39 98.268 109.98 98.139 110.68 97.881 c -h -106.74 95.133 m -109.54 95.133 l -109.54 93.949 109.10 93.357 108.22 93.357 c -107.33 93.357 106.84 93.949 106.74 95.133 c -h -112.66 99.000 m -112.66 90.328 l -113.89 90.328 l -113.89 99.000 l -h -116.15 99.000 m -116.15 92.637 l -117.31 92.637 l -117.31 93.832 l -117.92 92.941 118.66 92.496 119.55 92.496 c -120.10 92.496 120.54 92.671 120.87 93.021 c -121.19 93.370 121.36 93.840 121.36 94.430 c -121.36 99.000 l -120.20 99.000 l -120.20 94.805 l -120.20 94.332 120.13 93.995 120.00 93.794 c -119.86 93.593 119.63 93.492 119.31 93.492 c -118.60 93.492 117.93 93.955 117.31 94.881 c -117.31 99.000 l -h -125.29 99.146 m -124.76 99.146 124.12 99.023 123.37 98.777 c -123.37 97.717 l -124.12 98.092 124.78 98.279 125.34 98.279 c -125.67 98.279 125.94 98.189 126.16 98.010 c -126.38 97.830 126.49 97.605 126.49 97.336 c -126.49 96.941 126.18 96.615 125.57 96.357 c -124.90 96.070 l -123.90 95.656 123.40 95.061 123.40 94.283 c -123.40 93.729 123.60 93.292 123.99 92.974 c -124.38 92.655 124.92 92.496 125.61 92.496 c -125.96 92.496 126.40 92.545 126.92 92.643 c -127.16 92.689 l -127.16 93.650 l -126.52 93.459 126.01 93.363 125.63 93.363 c -124.89 93.363 124.52 93.633 124.52 94.172 c -124.52 94.520 124.80 94.812 125.36 95.051 c -125.92 95.285 l -126.54 95.551 126.99 95.831 127.25 96.126 c -127.51 96.421 127.64 96.789 127.64 97.230 c -127.64 97.789 127.42 98.248 126.98 98.607 c -126.54 98.967 125.98 99.146 125.29 99.146 c -h -131.63 99.146 m -131.04 99.146 130.59 98.979 130.26 98.643 c -129.93 98.307 129.77 97.840 129.77 97.242 c -129.77 93.504 l -128.97 93.504 l -128.97 92.637 l -129.77 92.637 l -129.77 91.482 l -130.92 91.371 l -130.92 92.637 l -132.58 92.637 l -132.58 93.504 l -130.92 93.504 l -130.92 97.031 l -130.92 97.863 131.28 98.279 132.00 98.279 c -132.15 98.279 132.34 98.254 132.55 98.203 c -132.55 99.000 l -132.20 99.098 131.89 99.146 131.63 99.146 c -h -137.49 98.191 m -136.80 98.828 136.13 99.146 135.49 99.146 c -134.96 99.146 134.53 98.981 134.18 98.651 c -133.83 98.321 133.66 97.904 133.66 97.400 c -133.66 96.705 133.95 96.171 134.53 95.798 c -135.12 95.425 135.95 95.238 137.04 95.238 c -137.32 95.238 l -137.32 94.471 l -137.32 93.732 136.94 93.363 136.18 93.363 c -135.57 93.363 134.91 93.551 134.21 93.926 c -134.21 92.971 l -134.98 92.654 135.71 92.496 136.39 92.496 c -137.10 92.496 137.63 92.656 137.97 92.977 c -138.30 93.297 138.47 93.795 138.47 94.471 c -138.47 97.354 l -138.47 98.014 138.68 98.344 139.08 98.344 c -139.13 98.344 139.21 98.336 139.30 98.320 c -139.39 98.959 l -139.12 99.084 138.84 99.146 138.52 99.146 c -137.98 99.146 137.64 98.828 137.49 98.191 c -h -137.32 97.564 m -137.32 95.918 l -136.93 95.906 l -136.30 95.906 135.79 96.026 135.40 96.267 c -135.01 96.507 134.81 96.822 134.81 97.213 c -134.81 97.490 134.91 97.725 135.10 97.916 c -135.30 98.107 135.54 98.203 135.82 98.203 c -136.30 98.203 136.80 97.990 137.32 97.564 c -h -140.83 99.000 m -140.83 92.637 l -141.99 92.637 l -141.99 93.832 l -142.60 92.941 143.34 92.496 144.23 92.496 c -144.78 92.496 145.22 92.671 145.54 93.021 c -145.87 93.370 146.04 93.840 146.04 94.430 c -146.04 99.000 l -144.88 99.000 l -144.88 94.805 l -144.88 94.332 144.81 93.995 144.67 93.794 c -144.54 93.593 144.31 93.492 143.99 93.492 c -143.28 93.492 142.61 93.955 141.99 94.881 c -141.99 99.000 l -h -150.77 99.146 m -149.91 99.146 149.19 98.828 148.63 98.191 c -148.06 97.555 147.78 96.752 147.78 95.783 c -147.78 94.748 148.06 93.941 148.62 93.363 c -149.18 92.785 149.96 92.496 150.96 92.496 c -151.46 92.496 152.02 92.564 152.63 92.701 c -152.63 93.668 l -151.98 93.477 151.45 93.381 151.04 93.381 c -150.45 93.381 149.97 93.603 149.61 94.046 c -149.26 94.489 149.08 95.080 149.08 95.818 c -149.08 96.533 149.26 97.111 149.63 97.553 c -150.00 97.994 150.48 98.215 151.07 98.215 c -151.60 98.215 152.14 98.080 152.70 97.811 c -152.70 98.807 l -151.95 99.033 151.31 99.146 150.77 99.146 c -h -159.09 98.795 m -158.32 99.029 157.66 99.146 157.11 99.146 c -156.17 99.146 155.40 98.835 154.81 98.212 c -154.22 97.589 153.92 96.781 153.92 95.789 c -153.92 94.824 154.18 94.033 154.71 93.416 c -155.23 92.799 155.89 92.490 156.71 92.490 c -157.48 92.490 158.07 92.764 158.49 93.311 c -158.91 93.857 159.12 94.635 159.12 95.643 c -159.12 96.000 l -155.10 96.000 l -155.27 97.512 156.01 98.268 157.32 98.268 c -157.80 98.268 158.39 98.139 159.09 97.881 c -h -155.15 95.133 m -157.96 95.133 l -157.96 93.949 157.52 93.357 156.64 93.357 c -155.75 93.357 155.26 93.949 155.15 95.133 c -h -168.20 99.000 m -161.26 95.531 l -168.20 92.062 l -168.20 93.029 l -163.20 95.531 l -168.20 98.027 l -h -172.56 99.146 m -171.98 99.146 171.52 98.979 171.19 98.643 c -170.86 98.307 170.70 97.840 170.70 97.242 c -170.70 93.504 l -169.90 93.504 l -169.90 92.637 l -170.70 92.637 l -170.70 91.482 l -171.85 91.371 l -171.85 92.637 l -173.52 92.637 l -173.52 93.504 l -171.85 93.504 l -171.85 97.031 l -171.85 97.863 172.21 98.279 172.93 98.279 c -173.08 98.279 173.27 98.254 173.49 98.203 c -173.49 99.000 l -173.13 99.098 172.82 99.146 172.56 99.146 c -h -179.80 98.795 m -179.03 99.029 178.37 99.146 177.82 99.146 c -176.88 99.146 176.12 98.835 175.52 98.212 c -174.93 97.589 174.64 96.781 174.64 95.789 c -174.64 94.824 174.90 94.033 175.42 93.416 c -175.94 92.799 176.61 92.490 177.42 92.490 c -178.19 92.490 178.78 92.764 179.20 93.311 c -179.62 93.857 179.83 94.635 179.83 95.643 c -179.83 96.000 l -175.81 96.000 l -175.98 97.512 176.72 98.268 178.04 98.268 c -178.52 98.268 179.11 98.139 179.80 97.881 c -h -175.87 95.133 m -178.67 95.133 l -178.67 93.949 178.23 93.357 177.35 93.357 c -176.46 93.357 175.97 93.949 175.87 95.133 c -h -181.83 99.000 m -181.83 92.637 l -182.98 92.637 l -182.98 93.832 l -183.59 92.941 184.34 92.496 185.22 92.496 c -185.77 92.496 186.21 92.671 186.54 93.021 c -186.87 93.370 187.03 93.840 187.03 94.430 c -187.03 99.000 l -185.88 99.000 l -185.88 94.805 l -185.88 94.332 185.81 93.995 185.67 93.794 c -185.53 93.593 185.30 93.492 184.98 93.492 c -184.27 93.492 183.61 93.955 182.98 94.881 c -182.98 99.000 l -h -192.55 98.191 m -191.86 98.828 191.20 99.146 190.56 99.146 c -190.03 99.146 189.59 98.981 189.24 98.651 c -188.90 98.321 188.72 97.904 188.72 97.400 c -188.72 96.705 189.01 96.171 189.60 95.798 c -190.18 95.425 191.02 95.238 192.11 95.238 c -192.38 95.238 l -192.38 94.471 l -192.38 93.732 192.01 93.363 191.25 93.363 c -190.64 93.363 189.98 93.551 189.27 93.926 c -189.27 92.971 l -190.05 92.654 190.78 92.496 191.46 92.496 c -192.17 92.496 192.69 92.656 193.03 92.977 c -193.37 93.297 193.54 93.795 193.54 94.471 c -193.54 97.354 l -193.54 98.014 193.74 98.344 194.15 98.344 c -194.20 98.344 194.27 98.336 194.37 98.320 c -194.45 98.959 l -194.19 99.084 193.90 99.146 193.59 99.146 c -193.05 99.146 192.70 98.828 192.55 98.191 c -h -192.38 97.564 m -192.38 95.918 l -192.00 95.906 l -191.37 95.906 190.85 96.026 190.46 96.267 c -190.07 96.507 189.88 96.822 189.88 97.213 c -189.88 97.490 189.97 97.725 190.17 97.916 c -190.37 98.107 190.60 98.203 190.88 98.203 c -191.37 98.203 191.87 97.990 192.38 97.564 c -h -195.90 99.000 m -195.90 92.637 l -197.05 92.637 l -197.05 93.832 l -197.66 92.941 198.41 92.496 199.29 92.496 c -199.84 92.496 200.28 92.671 200.61 93.021 c -200.94 93.370 201.10 93.840 201.10 94.430 c -201.10 99.000 l -199.95 99.000 l -199.95 94.805 l -199.95 94.332 199.88 93.995 199.74 93.794 c -199.60 93.593 199.37 93.492 199.05 93.492 c -198.35 93.492 197.68 93.955 197.05 94.881 c -197.05 99.000 l -h -205.26 99.146 m -204.67 99.146 204.21 98.979 203.89 98.643 c -203.56 98.307 203.39 97.840 203.39 97.242 c -203.39 93.504 l -202.60 93.504 l -202.60 92.637 l -203.39 92.637 l -203.39 91.482 l -204.55 91.371 l -204.55 92.637 l -206.21 92.637 l -206.21 93.504 l -204.55 93.504 l -204.55 97.031 l -204.55 97.863 204.91 98.279 205.63 98.279 c -205.78 98.279 205.96 98.254 206.18 98.203 c -206.18 99.000 l -205.83 99.098 205.52 99.146 205.26 99.146 c -h -206.68 101.02 m -206.68 100.15 l -212.68 100.15 l -212.68 101.02 l -h -213.84 99.000 m -213.84 92.637 l -214.99 92.637 l -214.99 99.000 l -h -213.84 91.482 m -213.84 90.328 l -214.99 90.328 l -214.99 91.482 l -h -221.39 99.000 m -221.39 97.805 l -220.92 98.699 220.21 99.146 219.27 99.146 c -218.50 99.146 217.90 98.867 217.46 98.309 c -217.02 97.750 216.80 96.986 216.80 96.018 c -216.80 94.959 217.05 94.107 217.55 93.463 c -218.05 92.818 218.70 92.496 219.51 92.496 c -220.27 92.496 220.89 92.785 221.39 93.363 c -221.39 89.748 l -222.55 89.748 l -222.55 99.000 l -h -221.39 94.154 m -220.79 93.627 220.22 93.363 219.69 93.363 c -218.58 93.363 218.03 94.209 218.03 95.900 c -218.03 97.389 218.52 98.133 219.51 98.133 c -220.15 98.133 220.78 97.783 221.39 97.084 c -h -225.00 99.000 m -231.94 95.531 l -225.00 92.062 l -225.00 93.029 l -230.00 95.531 l -225.00 98.027 l -h -f -70.500 102.50 m -850.50 102.50 l -S -850.00 102.00 m -844.00 96.000 l -844.00 108.00 l -h -f* -80.436 222.19 m -79.744 222.83 79.078 223.15 78.438 223.15 c -77.910 223.15 77.473 222.98 77.125 222.65 c -76.777 222.32 76.604 221.90 76.604 221.40 c -76.604 220.71 76.896 220.17 77.479 219.80 c -78.063 219.42 78.900 219.24 79.990 219.24 c -80.266 219.24 l -80.266 218.47 l -80.266 217.73 79.887 217.36 79.129 217.36 c -78.520 217.36 77.861 217.55 77.154 217.93 c -77.154 216.97 l -77.932 216.65 78.660 216.50 79.340 216.50 c -80.051 216.50 80.575 216.66 80.913 216.98 c -81.251 217.30 81.420 217.79 81.420 218.47 c -81.420 221.35 l -81.420 222.01 81.623 222.34 82.029 222.34 c -82.080 222.34 82.154 222.34 82.252 222.32 c -82.334 222.96 l -82.072 223.08 81.783 223.15 81.467 223.15 c -80.928 223.15 80.584 222.83 80.436 222.19 c -h -80.266 221.56 m -80.266 219.92 l -79.879 219.91 l -79.246 219.91 78.734 220.03 78.344 220.27 c -77.953 220.51 77.758 220.82 77.758 221.21 c -77.758 221.49 77.855 221.72 78.051 221.92 c -78.246 222.11 78.484 222.20 78.766 222.20 c -79.246 222.20 79.746 221.99 80.266 221.56 c -h -87.760 223.00 m -87.760 221.80 l -87.146 222.70 86.402 223.15 85.527 223.15 c -84.973 223.15 84.531 222.97 84.203 222.62 c -83.875 222.27 83.711 221.80 83.711 221.21 c -83.711 216.64 l -84.865 216.64 l -84.865 220.83 l -84.865 221.31 84.935 221.65 85.073 221.85 c -85.212 222.05 85.443 222.15 85.768 222.15 c -86.471 222.15 87.135 221.69 87.760 220.76 c -87.760 216.64 l -88.914 216.64 l -88.914 223.00 l -h -93.139 223.15 m -92.553 223.15 92.096 222.98 91.768 222.64 c -91.439 222.31 91.275 221.84 91.275 221.24 c -91.275 217.50 l -90.479 217.50 l -90.479 216.64 l -91.275 216.64 l -91.275 215.48 l -92.430 215.37 l -92.430 216.64 l -94.094 216.64 l -94.094 217.50 l -92.430 217.50 l -92.430 221.03 l -92.430 221.86 92.789 222.28 93.508 222.28 c -93.660 222.28 93.846 222.25 94.064 222.20 c -94.064 223.00 l -93.709 223.10 93.400 223.15 93.139 223.15 c -h -95.717 223.00 m -95.717 213.75 l -96.871 213.75 l -96.871 217.83 l -97.480 216.94 98.227 216.50 99.109 216.50 c -99.660 216.50 100.10 216.67 100.43 217.02 c -100.76 217.37 100.92 217.84 100.92 218.43 c -100.92 223.00 l -99.766 223.00 l -99.766 218.80 l -99.766 218.33 99.696 218.00 99.558 217.79 c -99.419 217.59 99.189 217.49 98.869 217.49 c -98.162 217.49 97.496 217.96 96.871 218.88 c -96.871 223.00 l -h -110.25 223.00 m -103.31 219.53 l -110.25 216.06 l -110.25 217.03 l -105.25 219.53 l -110.25 222.03 l -h -116.68 223.00 m -116.68 221.80 l -116.07 222.70 115.32 223.15 114.45 223.15 c -113.89 223.15 113.45 222.97 113.12 222.62 c -112.80 222.27 112.63 221.80 112.63 221.21 c -112.63 216.64 l -113.79 216.64 l -113.79 220.83 l -113.79 221.31 113.86 221.65 114.00 221.85 c -114.13 222.05 114.37 222.15 114.69 222.15 c -115.39 222.15 116.06 221.69 116.68 220.76 c -116.68 216.64 l -117.84 216.64 l -117.84 223.00 l -h -121.84 223.15 m -121.31 223.15 120.67 223.02 119.92 222.78 c -119.92 221.72 l -120.67 222.09 121.33 222.28 121.88 222.28 c -122.22 222.28 122.49 222.19 122.71 222.01 c -122.93 221.83 123.04 221.61 123.04 221.34 c -123.04 220.94 122.73 220.62 122.12 220.36 c -121.45 220.07 l -120.45 219.66 119.95 219.06 119.95 218.28 c -119.95 217.73 120.15 217.29 120.54 216.97 c -120.93 216.66 121.47 216.50 122.15 216.50 c -122.51 216.50 122.95 216.54 123.47 216.64 c -123.71 216.69 l -123.71 217.65 l -123.07 217.46 122.56 217.36 122.18 217.36 c -121.44 217.36 121.06 217.63 121.06 218.17 c -121.06 218.52 121.35 218.81 121.91 219.05 c -122.46 219.29 l -123.09 219.55 123.54 219.83 123.80 220.13 c -124.06 220.42 124.19 220.79 124.19 221.23 c -124.19 221.79 123.97 222.25 123.53 222.61 c -123.09 222.97 122.53 223.15 121.84 223.15 c -h -130.93 222.79 m -130.16 223.03 129.50 223.15 128.95 223.15 c -128.01 223.15 127.24 222.83 126.65 222.21 c -126.06 221.59 125.76 220.78 125.76 219.79 c -125.76 218.82 126.02 218.03 126.55 217.42 c -127.07 216.80 127.73 216.49 128.55 216.49 c -129.32 216.49 129.91 216.76 130.33 217.31 c -130.75 217.86 130.96 218.63 130.96 219.64 c -130.96 220.00 l -126.94 220.00 l -127.11 221.51 127.85 222.27 129.16 222.27 c -129.64 222.27 130.23 222.14 130.93 221.88 c -h -126.99 219.13 m -129.80 219.13 l -129.80 217.95 129.36 217.36 128.48 217.36 c -127.59 217.36 127.10 217.95 126.99 219.13 c -h -132.95 223.00 m -132.95 216.64 l -134.11 216.64 l -134.11 217.83 l -134.56 216.94 135.23 216.50 136.10 216.50 c -136.22 216.50 136.34 216.51 136.47 216.53 c -136.47 217.60 l -136.27 217.54 136.09 217.50 135.94 217.50 c -135.21 217.50 134.60 217.94 134.11 218.80 c -134.11 223.00 l -h -137.88 224.88 m -137.88 224.45 l -138.26 224.34 138.44 223.90 138.44 223.12 c -138.44 223.00 l -137.88 223.00 l -137.88 221.55 l -139.33 221.55 l -139.33 222.81 l -139.33 224.09 138.85 224.78 137.88 224.88 c -h -147.94 223.15 m -147.08 223.15 146.37 222.83 145.80 222.19 c -145.24 221.55 144.95 220.75 144.95 219.78 c -144.95 218.75 145.23 217.94 145.79 217.36 c -146.35 216.79 147.14 216.50 148.14 216.50 c -148.64 216.50 149.19 216.56 149.80 216.70 c -149.80 217.67 l -149.15 217.48 148.62 217.38 148.21 217.38 c -147.62 217.38 147.15 217.60 146.79 218.05 c -146.43 218.49 146.25 219.08 146.25 219.82 c -146.25 220.53 146.44 221.11 146.80 221.55 c -147.17 221.99 147.65 222.21 148.25 222.21 c -148.77 222.21 149.32 222.08 149.88 221.81 c -149.88 222.81 l -149.13 223.03 148.48 223.15 147.94 223.15 c -h -151.60 223.00 m -151.60 216.64 l -152.76 216.64 l -152.76 217.83 l -153.21 216.94 153.88 216.50 154.75 216.50 c -154.87 216.50 154.99 216.51 155.12 216.53 c -155.12 217.60 l -154.92 217.54 154.74 217.50 154.59 217.50 c -153.86 217.50 153.25 217.94 152.76 218.80 c -152.76 223.00 l -h -161.18 222.79 m -160.40 223.03 159.74 223.15 159.19 223.15 c -158.25 223.15 157.49 222.83 156.90 222.21 c -156.31 221.59 156.01 220.78 156.01 219.79 c -156.01 218.82 156.27 218.03 156.79 217.42 c -157.31 216.80 157.98 216.49 158.79 216.49 c -159.56 216.49 160.16 216.76 160.58 217.31 c -161.00 217.86 161.21 218.63 161.21 219.64 c -161.20 220.00 l -157.19 220.00 l -157.36 221.51 158.10 222.27 159.41 222.27 c -159.89 222.27 160.48 222.14 161.18 221.88 c -h -157.24 219.13 m -160.05 219.13 l -160.05 217.95 159.61 217.36 158.72 217.36 c -157.84 217.36 157.34 217.95 157.24 219.13 c -h -167.28 223.00 m -167.28 221.80 l -166.81 222.70 166.11 223.15 165.16 223.15 c -164.40 223.15 163.79 222.87 163.35 222.31 c -162.92 221.75 162.70 220.99 162.70 220.02 c -162.70 218.96 162.94 218.11 163.44 217.46 c -163.94 216.82 164.60 216.50 165.41 216.50 c -166.16 216.50 166.79 216.79 167.28 217.36 c -167.28 213.75 l -168.44 213.75 l -168.44 223.00 l -h -167.28 218.15 m -166.69 217.63 166.12 217.36 165.58 217.36 c -164.48 217.36 163.93 218.21 163.93 219.90 c -163.93 221.39 164.42 222.13 165.40 222.13 c -166.04 222.13 166.67 221.78 167.28 221.08 c -h -172.44 223.15 m -171.91 223.15 171.27 223.02 170.52 222.78 c -170.52 221.72 l -171.27 222.09 171.93 222.28 172.49 222.28 c -172.82 222.28 173.09 222.19 173.31 222.01 c -173.53 221.83 173.64 221.61 173.64 221.34 c -173.64 220.94 173.33 220.62 172.72 220.36 c -172.05 220.07 l -171.05 219.66 170.55 219.06 170.55 218.28 c -170.55 217.73 170.75 217.29 171.14 216.97 c -171.53 216.66 172.07 216.50 172.76 216.50 c -173.11 216.50 173.55 216.54 174.07 216.64 c -174.31 216.69 l -174.31 217.65 l -173.67 217.46 173.16 217.36 172.78 217.36 c -172.04 217.36 171.67 217.63 171.67 218.17 c -171.67 218.52 171.95 218.81 172.51 219.05 c -173.07 219.29 l -173.70 219.55 174.14 219.83 174.40 220.13 c -174.66 220.42 174.79 220.79 174.79 221.23 c -174.79 221.79 174.57 222.25 174.13 222.61 c -173.69 222.97 173.13 223.15 172.44 223.15 c -h -176.89 224.88 m -176.89 224.45 l -177.26 224.34 177.45 223.90 177.45 223.12 c -177.45 223.00 l -176.89 223.00 l -176.89 221.55 l -178.33 221.55 l -178.33 222.81 l -178.33 224.09 177.85 224.78 176.89 224.88 c -h -186.37 223.15 m -185.79 223.15 185.33 222.98 185.00 222.64 c -184.67 222.31 184.51 221.84 184.51 221.24 c -184.51 217.50 l -183.71 217.50 l -183.71 216.64 l -184.51 216.64 l -184.51 215.48 l -185.66 215.37 l -185.66 216.64 l -187.33 216.64 l -187.33 217.50 l -185.66 217.50 l -185.66 221.03 l -185.66 221.86 186.02 222.28 186.74 222.28 c -186.89 222.28 187.08 222.25 187.30 222.20 c -187.30 223.00 l -186.94 223.10 186.63 223.15 186.37 223.15 c -h -193.62 222.79 m -192.84 223.03 192.18 223.15 191.63 223.15 c -190.69 223.15 189.93 222.83 189.33 222.21 c -188.74 221.59 188.45 220.78 188.45 219.79 c -188.45 218.82 188.71 218.03 189.23 217.42 c -189.75 216.80 190.42 216.49 191.23 216.49 c -192.00 216.49 192.59 216.76 193.01 217.31 c -193.43 217.86 193.64 218.63 193.64 219.64 c -193.64 220.00 l -189.62 220.00 l -189.79 221.51 190.53 222.27 191.85 222.27 c -192.33 222.27 192.92 222.14 193.62 221.88 c -h -189.68 219.13 m -192.48 219.13 l -192.48 217.95 192.04 217.36 191.16 217.36 c -190.27 217.36 189.78 217.95 189.68 219.13 c -h -195.64 223.00 m -195.64 216.64 l -196.79 216.64 l -196.79 217.83 l -197.40 216.94 198.15 216.50 199.03 216.50 c -199.58 216.50 200.02 216.67 200.35 217.02 c -200.68 217.37 200.84 217.84 200.84 218.43 c -200.84 223.00 l -199.69 223.00 l -199.69 218.80 l -199.69 218.33 199.62 218.00 199.48 217.79 c -199.34 217.59 199.11 217.49 198.79 217.49 c -198.08 217.49 197.42 217.96 196.79 218.88 c -196.79 223.00 l -h -206.37 222.19 m -205.67 222.83 205.01 223.15 204.37 223.15 c -203.84 223.15 203.40 222.98 203.05 222.65 c -202.71 222.32 202.53 221.90 202.53 221.40 c -202.53 220.71 202.83 220.17 203.41 219.80 c -203.99 219.42 204.83 219.24 205.92 219.24 c -206.20 219.24 l -206.20 218.47 l -206.20 217.73 205.82 217.36 205.06 217.36 c -204.45 217.36 203.79 217.55 203.08 217.93 c -203.08 216.97 l -203.86 216.65 204.59 216.50 205.27 216.50 c -205.98 216.50 206.50 216.66 206.84 216.98 c -207.18 217.30 207.35 217.79 207.35 218.47 c -207.35 221.35 l -207.35 222.01 207.55 222.34 207.96 222.34 c -208.01 222.34 208.08 222.34 208.18 222.32 c -208.26 222.96 l -208.00 223.08 207.71 223.15 207.40 223.15 c -206.86 223.15 206.51 222.83 206.37 222.19 c -h -206.20 221.56 m -206.20 219.92 l -205.81 219.91 l -205.18 219.91 204.66 220.03 204.27 220.27 c -203.88 220.51 203.69 220.82 203.69 221.21 c -203.69 221.49 203.79 221.72 203.98 221.92 c -204.18 222.11 204.41 222.20 204.70 222.20 c -205.18 222.20 205.68 221.99 206.20 221.56 c -h -209.71 223.00 m -209.71 216.64 l -210.87 216.64 l -210.87 217.83 l -211.47 216.94 212.22 216.50 213.10 216.50 c -213.65 216.50 214.09 216.67 214.42 217.02 c -214.75 217.37 214.91 217.84 214.91 218.43 c -214.91 223.00 l -213.76 223.00 l -213.76 218.80 l -213.76 218.33 213.69 218.00 213.55 217.79 c -213.41 217.59 213.18 217.49 212.86 217.49 c -212.16 217.49 211.49 217.96 210.87 218.88 c -210.87 223.00 l -h -219.07 223.15 m -218.48 223.15 218.03 222.98 217.70 222.64 c -217.37 222.31 217.21 221.84 217.21 221.24 c -217.21 217.50 l -216.41 217.50 l -216.41 216.64 l -217.21 216.64 l -217.21 215.48 l -218.36 215.37 l -218.36 216.64 l -220.02 216.64 l -220.02 217.50 l -218.36 217.50 l -218.36 221.03 l -218.36 221.86 218.72 222.28 219.44 222.28 c -219.59 222.28 219.78 222.25 219.99 222.20 c -219.99 223.00 l -219.64 223.10 219.33 223.15 219.07 223.15 c -h -221.79 223.00 m -228.73 219.53 l -221.79 216.06 l -221.79 217.03 l -226.79 219.53 l -221.79 222.03 l -h -f -70.500 226.50 m -530.50 226.50 l -S -530.00 226.00 m -524.00 220.00 l -524.00 232.00 l -h -f* -76.996 507.00 m -76.996 505.99 l -77.332 505.20 78.012 504.35 79.035 503.42 c -79.697 502.83 l -80.549 502.06 80.975 501.29 80.975 500.54 c -80.975 500.05 80.829 499.67 80.538 499.39 c -80.247 499.12 79.848 498.98 79.340 498.98 c -78.738 498.98 78.029 499.21 77.213 499.68 c -77.213 498.66 l -77.982 498.29 78.746 498.11 79.504 498.11 c -80.316 498.11 80.969 498.33 81.461 498.77 c -81.953 499.21 82.199 499.79 82.199 500.51 c -82.199 501.03 82.075 501.49 81.827 501.89 c -81.579 502.29 81.117 502.78 80.441 503.36 c -79.996 503.74 l -79.070 504.52 78.535 505.27 78.391 505.99 c -82.158 505.99 l -82.158 507.00 l -h -87.350 507.22 m -86.455 507.22 85.731 506.80 85.179 505.95 c -84.626 505.11 84.350 504.01 84.350 502.66 c -84.350 501.29 84.628 500.19 85.185 499.36 c -85.741 498.53 86.475 498.11 87.385 498.11 c -88.295 498.11 89.028 498.53 89.585 499.36 c -90.142 500.19 90.420 501.29 90.420 502.64 c -90.420 504.03 90.142 505.14 89.585 505.97 c -89.028 506.80 88.283 507.22 87.350 507.22 c -h -87.361 506.35 m -88.584 506.35 89.195 505.11 89.195 502.62 c -89.195 500.19 88.592 498.98 87.385 498.98 c -86.182 498.98 85.580 500.21 85.580 502.66 c -85.580 505.12 86.174 506.35 87.361 506.35 c -h -94.938 507.22 m -94.043 507.22 93.319 506.80 92.767 505.95 c -92.214 505.11 91.938 504.01 91.938 502.66 c -91.938 501.29 92.216 500.19 92.772 499.36 c -93.329 498.53 94.062 498.11 94.973 498.11 c -95.883 498.11 96.616 498.53 97.173 499.36 c -97.729 500.19 98.008 501.29 98.008 502.64 c -98.008 504.03 97.729 505.14 97.173 505.97 c -96.616 506.80 95.871 507.22 94.938 507.22 c -h -94.949 506.35 m -96.172 506.35 96.783 505.11 96.783 502.62 c -96.783 500.19 96.180 498.98 94.973 498.98 c -93.770 498.98 93.168 500.21 93.168 502.66 c -93.168 505.12 93.762 506.35 94.949 506.35 c -h -107.17 507.22 m -105.97 507.22 105.00 506.80 104.27 505.97 c -103.54 505.13 103.17 504.03 103.17 502.66 c -103.17 501.28 103.54 500.18 104.27 499.35 c -105.01 498.52 105.99 498.11 107.22 498.11 c -108.45 498.11 109.43 498.52 110.17 499.35 c -110.91 500.17 111.28 501.27 111.28 502.65 c -111.28 504.05 110.91 505.16 110.17 505.98 c -109.43 506.81 108.43 507.22 107.17 507.22 c -h -107.19 506.30 m -108.08 506.30 108.76 505.98 109.25 505.34 c -109.73 504.70 109.97 503.80 109.97 502.63 c -109.97 501.51 109.73 500.62 109.24 499.99 c -108.76 499.35 108.08 499.03 107.22 499.03 c -106.36 499.03 105.69 499.35 105.20 499.99 c -104.72 500.63 104.48 501.52 104.48 502.65 c -104.48 503.79 104.72 504.68 105.20 505.32 c -105.68 505.97 106.34 506.30 107.19 506.30 c -h -113.00 507.00 m -113.00 498.33 l -114.16 498.33 l -114.16 502.59 l -117.67 498.33 l -118.90 498.33 l -115.50 502.46 l -119.51 507.00 l -117.95 507.00 l -114.16 502.61 l -114.16 507.00 l -h -f -78.244 157.00 m -76.012 148.33 l -77.195 148.33 l -78.971 155.18 l -80.605 148.33 l -81.789 148.33 l -83.336 155.08 l -85.246 148.33 l -86.248 148.33 l -83.816 157.00 l -82.592 157.00 l -81.062 150.31 l -79.463 157.00 l -h -88.510 157.00 m -86.277 148.33 l -87.461 148.33 l -89.236 155.18 l -90.871 148.33 l -92.055 148.33 l -93.602 155.08 l -95.512 148.33 l -96.514 148.33 l -94.082 157.00 l -92.857 157.00 l -91.328 150.31 l -89.729 157.00 l -h -98.775 157.00 m -96.543 148.33 l -97.727 148.33 l -99.502 155.18 l -101.14 148.33 l -102.32 148.33 l -103.87 155.08 l -105.78 148.33 l -106.78 148.33 l -104.35 157.00 l -103.12 157.00 l -101.59 150.31 l -99.994 157.00 l -h -107.66 153.96 m -107.66 153.10 l -112.87 153.10 l -112.87 153.96 l -h -119.25 153.68 m -117.78 149.95 l -116.29 153.68 l -h -120.55 157.00 m -119.61 154.60 l -115.94 154.60 l -114.98 157.00 l -113.84 157.00 l -117.28 148.33 l -118.50 148.33 l -121.88 157.00 l -h -127.15 157.00 m -127.15 155.80 l -126.54 156.70 125.79 157.15 124.92 157.15 c -124.37 157.15 123.92 156.97 123.60 156.62 c -123.27 156.27 123.10 155.80 123.10 155.21 c -123.10 150.64 l -124.26 150.64 l -124.26 154.83 l -124.26 155.31 124.33 155.65 124.47 155.85 c -124.60 156.05 124.84 156.15 125.16 156.15 c -125.86 156.15 126.53 155.69 127.15 154.76 c -127.15 150.64 l -128.31 150.64 l -128.31 157.00 l -h -132.53 157.15 m -131.95 157.15 131.49 156.98 131.16 156.64 c -130.83 156.31 130.67 155.84 130.67 155.24 c -130.67 151.50 l -129.87 151.50 l -129.87 150.64 l -130.67 150.64 l -130.67 149.48 l -131.82 149.37 l -131.82 150.64 l -133.49 150.64 l -133.49 151.50 l -131.82 151.50 l -131.82 155.03 l -131.82 155.86 132.18 156.28 132.90 156.28 c -133.05 156.28 133.24 156.25 133.46 156.20 c -133.46 157.00 l -133.10 157.10 132.79 157.15 132.53 157.15 c -h -135.11 157.00 m -135.11 147.75 l -136.26 147.75 l -136.26 151.83 l -136.87 150.94 137.62 150.50 138.50 150.50 c -139.05 150.50 139.49 150.67 139.82 151.02 c -140.15 151.37 140.31 151.84 140.31 152.43 c -140.31 157.00 l -139.16 157.00 l -139.16 152.80 l -139.16 152.33 139.09 152.00 138.95 151.79 c -138.81 151.59 138.58 151.49 138.26 151.49 c -137.55 151.49 136.89 151.96 136.26 152.88 c -136.26 157.00 l -h -147.22 156.79 m -146.45 157.03 145.79 157.15 145.23 157.15 c -144.30 157.15 143.53 156.83 142.94 156.21 c -142.35 155.59 142.05 154.78 142.05 153.79 c -142.05 152.82 142.31 152.03 142.83 151.42 c -143.36 150.80 144.02 150.49 144.84 150.49 c -145.61 150.49 146.20 150.76 146.62 151.31 c -147.04 151.86 147.25 152.63 147.25 153.64 c -147.24 154.00 l -143.23 154.00 l -143.40 155.51 144.14 156.27 145.45 156.27 c -145.93 156.27 146.52 156.14 147.22 155.88 c -h -143.28 153.13 m -146.09 153.13 l -146.09 151.95 145.65 151.36 144.77 151.36 c -143.88 151.36 143.38 151.95 143.28 153.13 c -h -149.24 157.00 m -149.24 150.64 l -150.40 150.64 l -150.40 151.83 l -151.01 150.94 151.75 150.50 152.63 150.50 c -153.19 150.50 153.62 150.67 153.95 151.02 c -154.28 151.37 154.45 151.84 154.45 152.43 c -154.45 157.00 l -153.29 157.00 l -153.29 152.80 l -153.29 152.33 153.22 152.00 153.08 151.79 c -152.94 151.59 152.71 151.49 152.39 151.49 c -151.69 151.49 151.02 151.96 150.40 152.88 c -150.40 157.00 l -h -158.60 157.15 m -158.01 157.15 157.56 156.98 157.23 156.64 c -156.90 156.31 156.74 155.84 156.74 155.24 c -156.74 151.50 l -155.94 151.50 l -155.94 150.64 l -156.74 150.64 l -156.74 149.48 l -157.89 149.37 l -157.89 150.64 l -159.55 150.64 l -159.55 151.50 l -157.89 151.50 l -157.89 155.03 l -157.89 155.86 158.25 156.28 158.97 156.28 c -159.12 156.28 159.31 156.25 159.53 156.20 c -159.53 157.00 l -159.17 157.10 158.86 157.15 158.60 157.15 c -h -161.18 157.00 m -161.18 150.64 l -162.33 150.64 l -162.33 157.00 l -h -161.18 149.48 m -161.18 148.33 l -162.33 148.33 l -162.33 149.48 l -h -167.13 157.15 m -166.27 157.15 165.56 156.83 164.99 156.19 c -164.43 155.55 164.14 154.75 164.14 153.78 c -164.14 152.75 164.42 151.94 164.98 151.36 c -165.54 150.79 166.33 150.50 167.33 150.50 c -167.83 150.50 168.38 150.56 168.99 150.70 c -168.99 151.67 l -168.34 151.48 167.81 151.38 167.40 151.38 c -166.81 151.38 166.34 151.60 165.98 152.05 c -165.62 152.49 165.44 153.08 165.44 153.82 c -165.44 154.53 165.63 155.11 165.99 155.55 c -166.36 155.99 166.84 156.21 167.44 156.21 c -167.96 156.21 168.51 156.08 169.06 155.81 c -169.06 156.81 l -168.32 157.03 167.67 157.15 167.13 157.15 c -h -174.07 156.19 m -173.38 156.83 172.72 157.15 172.08 157.15 c -171.55 157.15 171.11 156.98 170.76 156.65 c -170.42 156.32 170.24 155.90 170.24 155.40 c -170.24 154.71 170.53 154.17 171.12 153.80 c -171.70 153.42 172.54 153.24 173.63 153.24 c -173.90 153.24 l -173.90 152.47 l -173.90 151.73 173.53 151.36 172.77 151.36 c -172.16 151.36 171.50 151.55 170.79 151.93 c -170.79 150.97 l -171.57 150.65 172.30 150.50 172.98 150.50 c -173.69 150.50 174.21 150.66 174.55 150.98 c -174.89 151.30 175.06 151.79 175.06 152.47 c -175.06 155.35 l -175.06 156.01 175.26 156.34 175.67 156.34 c -175.72 156.34 175.79 156.34 175.89 156.32 c -175.97 156.96 l -175.71 157.08 175.42 157.15 175.11 157.15 c -174.57 157.15 174.22 156.83 174.07 156.19 c -h -173.90 155.56 m -173.90 153.92 l -173.52 153.91 l -172.88 153.91 172.37 154.03 171.98 154.27 c -171.59 154.51 171.40 154.82 171.40 155.21 c -171.40 155.49 171.49 155.72 171.69 155.92 c -171.88 156.11 172.12 156.20 172.40 156.20 c -172.88 156.20 173.38 155.99 173.90 155.56 c -h -179.33 157.15 m -178.74 157.15 178.29 156.98 177.96 156.64 c -177.63 156.31 177.47 155.84 177.47 155.24 c -177.47 151.50 l -176.67 151.50 l -176.67 150.64 l -177.47 150.64 l -177.47 149.48 l -178.62 149.37 l -178.62 150.64 l -180.29 150.64 l -180.29 151.50 l -178.62 151.50 l -178.62 155.03 l -178.62 155.86 178.98 156.28 179.70 156.28 c -179.85 156.28 180.04 156.25 180.26 156.20 c -180.26 157.00 l -179.90 157.10 179.59 157.15 179.33 157.15 c -h -186.57 156.79 m -185.80 157.03 185.14 157.15 184.59 157.15 c -183.65 157.15 182.88 156.83 182.29 156.21 c -181.70 155.59 181.40 154.78 181.40 153.79 c -181.40 152.82 181.67 152.03 182.19 151.42 c -182.71 150.80 183.38 150.49 184.19 150.49 c -184.96 150.49 185.55 150.76 185.97 151.31 c -186.39 151.86 186.60 152.63 186.60 153.64 c -186.60 154.00 l -182.58 154.00 l -182.75 155.51 183.49 156.27 184.80 156.27 c -185.28 156.27 185.87 156.14 186.57 155.88 c -h -182.63 153.13 m -185.44 153.13 l -185.44 151.95 185.00 151.36 184.12 151.36 c -183.23 151.36 182.74 151.95 182.63 153.13 c -h -188.76 157.00 m -188.76 155.85 l -189.91 155.85 l -189.91 157.00 l -h -188.76 151.80 m -188.76 150.64 l -189.91 150.64 l -189.91 151.80 l -h -196.15 157.00 m -196.15 148.33 l -197.31 148.33 l -197.31 152.59 l -200.82 148.33 l -202.05 148.33 l -198.65 152.46 l -202.66 157.00 l -201.10 157.00 l -197.31 152.61 l -197.31 157.00 l -h -208.69 156.79 m -207.91 157.03 207.25 157.15 206.70 157.15 c -205.76 157.15 205.00 156.83 204.41 156.21 c -203.81 155.59 203.52 154.78 203.52 153.79 c -203.52 152.82 203.78 152.03 204.30 151.42 c -204.82 150.80 205.49 150.49 206.30 150.49 c -207.07 150.49 207.67 150.76 208.08 151.31 c -208.50 151.86 208.71 152.63 208.71 153.64 c -208.71 154.00 l -204.70 154.00 l -204.86 155.51 205.60 156.27 206.92 156.27 c -207.40 156.27 207.99 156.14 208.69 155.88 c -h -204.75 153.13 m -207.55 153.13 l -207.55 151.95 207.11 151.36 206.23 151.36 c -205.34 151.36 204.85 151.95 204.75 153.13 c -h -211.08 159.31 m -212.11 157.00 l -209.65 150.64 l -210.89 150.64 l -212.72 155.43 l -214.66 150.64 l -215.75 150.64 l -212.28 159.31 l -h -218.66 157.15 m -218.14 157.15 217.50 157.02 216.74 156.78 c -216.74 155.72 l -217.50 156.09 218.15 156.28 218.71 156.28 c -219.04 156.28 219.32 156.19 219.54 156.01 c -219.76 155.83 219.87 155.61 219.87 155.34 c -219.87 154.94 219.56 154.62 218.95 154.36 c -218.27 154.07 l -217.28 153.66 216.78 153.06 216.78 152.28 c -216.78 151.73 216.97 151.29 217.37 150.97 c -217.76 150.66 218.30 150.50 218.98 150.50 c -219.34 150.50 219.78 150.54 220.30 150.64 c -220.54 150.69 l -220.54 151.65 l -219.89 151.46 219.38 151.36 219.00 151.36 c -218.26 151.36 217.89 151.63 217.89 152.17 c -217.89 152.52 218.17 152.81 218.73 153.05 c -219.29 153.29 l -219.92 153.55 220.37 153.83 220.63 154.13 c -220.89 154.42 221.02 154.79 221.02 155.23 c -221.02 155.79 220.80 156.25 220.36 156.61 c -219.92 156.97 219.35 157.15 218.66 157.15 c -h -225.00 157.15 m -224.42 157.15 223.96 156.98 223.63 156.64 c -223.30 156.31 223.14 155.84 223.14 155.24 c -223.14 151.50 l -222.34 151.50 l -222.34 150.64 l -223.14 150.64 l -223.14 149.48 l -224.29 149.37 l -224.29 150.64 l -225.96 150.64 l -225.96 151.50 l -224.29 151.50 l -224.29 155.03 l -224.29 155.86 224.65 156.28 225.37 156.28 c -225.53 156.28 225.71 156.25 225.93 156.20 c -225.93 157.00 l -225.57 157.10 225.27 157.15 225.00 157.15 c -h -230.07 157.15 m -229.16 157.15 228.44 156.84 227.89 156.24 c -227.35 155.64 227.08 154.83 227.08 153.82 c -227.08 152.79 227.35 151.99 227.90 151.39 c -228.44 150.79 229.18 150.50 230.11 150.50 c -231.05 150.50 231.79 150.79 232.33 151.39 c -232.88 151.99 233.15 152.79 233.15 153.81 c -233.15 154.85 232.88 155.66 232.33 156.26 c -231.78 156.85 231.03 157.15 230.07 157.15 c -h -230.09 156.28 m -231.31 156.28 231.92 155.46 231.92 153.81 c -231.92 152.18 231.32 151.36 230.11 151.36 c -228.91 151.36 228.31 152.18 228.31 153.82 c -228.31 155.46 228.90 156.28 230.09 156.28 c -h -234.95 157.00 m -234.95 150.64 l -236.11 150.64 l -236.11 151.83 l -236.72 150.94 237.46 150.50 238.35 150.50 c -238.90 150.50 239.34 150.67 239.66 151.02 c -239.99 151.37 240.16 151.84 240.16 152.43 c -240.16 157.00 l -239.00 157.00 l -239.00 152.80 l -239.00 152.33 238.93 152.00 238.79 151.79 c -238.66 151.59 238.43 151.49 238.11 151.49 c -237.40 151.49 236.73 151.96 236.11 152.88 c -236.11 157.00 l -h -247.06 156.79 m -246.29 157.03 245.63 157.15 245.08 157.15 c -244.14 157.15 243.38 156.83 242.78 156.21 c -242.19 155.59 241.90 154.78 241.90 153.79 c -241.90 152.82 242.16 152.03 242.68 151.42 c -243.20 150.80 243.87 150.49 244.68 150.49 c -245.45 150.49 246.04 150.76 246.46 151.31 c -246.88 151.86 247.09 152.63 247.09 153.64 c -247.09 154.00 l -243.07 154.00 l -243.24 155.51 243.98 156.27 245.29 156.27 c -245.78 156.27 246.37 156.14 247.06 155.88 c -h -243.13 153.13 m -245.93 153.13 l -245.93 151.95 245.49 151.36 244.61 151.36 c -243.72 151.36 243.23 151.95 243.13 153.13 c -h -256.86 157.00 m -256.86 155.80 l -256.25 156.70 255.50 157.15 254.63 157.15 c -254.07 157.15 253.63 156.97 253.30 156.62 c -252.98 156.27 252.81 155.80 252.81 155.21 c -252.81 150.64 l -253.97 150.64 l -253.97 154.83 l -253.97 155.31 254.04 155.65 254.17 155.85 c -254.31 156.05 254.54 156.15 254.87 156.15 c -255.57 156.15 256.24 155.69 256.86 154.76 c -256.86 150.64 l -258.02 150.64 l -258.02 157.00 l -h -260.33 157.00 m -260.33 150.64 l -261.48 150.64 l -261.48 151.83 l -261.94 150.94 262.61 150.50 263.48 150.50 c -263.59 150.50 263.72 150.51 263.85 150.53 c -263.85 151.60 l -263.65 151.54 263.47 151.50 263.32 151.50 c -262.59 151.50 261.98 151.94 261.48 152.80 c -261.48 157.00 l -h -265.24 157.00 m -265.24 150.64 l -266.39 150.64 l -266.39 157.00 l -h -265.24 149.48 m -265.24 148.33 l -266.39 148.33 l -266.39 149.48 l -h -268.86 155.05 m -268.86 154.18 l -275.79 154.18 l -275.79 155.05 l -h -268.86 152.88 m -268.86 152.01 l -275.79 152.01 l -275.79 152.88 l -h -277.89 150.64 m -277.74 147.75 l -278.90 147.75 l -278.76 150.64 l -h -279.91 150.64 m -279.77 147.75 l -280.93 147.75 l -280.78 150.64 l -h -286.71 157.00 m -286.71 155.80 l -286.10 156.70 285.35 157.15 284.48 157.15 c -283.92 157.15 283.48 156.97 283.15 156.62 c -282.82 156.27 282.66 155.80 282.66 155.21 c -282.66 150.64 l -283.81 150.64 l -283.81 154.83 l -283.81 155.31 283.88 155.65 284.02 155.85 c -284.16 156.05 284.39 156.15 284.72 156.15 c -285.42 156.15 286.08 155.69 286.71 154.76 c -286.71 150.64 l -287.86 150.64 l -287.86 157.00 l -h -290.18 157.00 m -290.18 150.64 l -291.33 150.64 l -291.33 151.83 l -291.79 150.94 292.45 150.50 293.32 150.50 c -293.44 150.50 293.56 150.51 293.69 150.53 c -293.69 151.60 l -293.49 151.54 293.32 151.50 293.17 151.50 c -292.44 151.50 291.82 151.94 291.33 152.80 c -291.33 157.00 l -h -295.09 157.00 m -295.09 147.75 l -296.24 147.75 l -296.24 157.00 l -h -297.40 159.02 m -297.40 158.15 l -303.40 158.15 l -303.40 159.02 l -h -306.47 157.15 m -305.88 157.15 305.42 156.98 305.10 156.64 c -304.77 156.31 304.60 155.84 304.60 155.24 c -304.60 151.50 l -303.81 151.50 l -303.81 150.64 l -304.60 150.64 l -304.60 149.48 l -305.76 149.37 l -305.76 150.64 l -307.42 150.64 l -307.42 151.50 l -305.76 151.50 l -305.76 155.03 l -305.76 155.86 306.12 156.28 306.84 156.28 c -306.99 156.28 307.17 156.25 307.39 156.20 c -307.39 157.00 l -307.04 157.10 306.73 157.15 306.47 157.15 c -h -311.54 157.15 m -310.62 157.15 309.90 156.84 309.36 156.24 c -308.81 155.64 308.54 154.83 308.54 153.82 c -308.54 152.79 308.81 151.99 309.36 151.39 c -309.90 150.79 310.64 150.50 311.58 150.50 c -312.51 150.50 313.25 150.79 313.79 151.39 c -314.34 151.99 314.61 152.79 314.61 153.81 c -314.61 154.85 314.34 155.66 313.79 156.26 c -313.24 156.85 312.49 157.15 311.54 157.15 c -h -311.55 156.28 m -312.78 156.28 313.39 155.46 313.39 153.81 c -313.39 152.18 312.78 151.36 311.58 151.36 c -310.37 151.36 309.77 152.18 309.77 153.82 c -309.77 155.46 310.37 156.28 311.55 156.28 c -h -315.26 159.02 m -315.26 158.15 l -321.26 158.15 l -321.26 159.02 l -h -322.42 157.00 m -322.42 147.75 l -323.57 147.75 l -323.57 153.72 l -326.27 150.64 l -327.51 150.64 l -324.94 153.61 l -328.04 157.00 l -326.56 157.00 l -323.57 153.74 l -323.57 157.00 l -h -334.09 156.79 m -333.32 157.03 332.66 157.15 332.11 157.15 c -331.17 157.15 330.41 156.83 329.81 156.21 c -329.22 155.59 328.93 154.78 328.93 153.79 c -328.93 152.82 329.19 152.03 329.71 151.42 c -330.23 150.80 330.90 150.49 331.71 150.49 c -332.48 150.49 333.07 150.76 333.49 151.31 c -333.91 151.86 334.12 152.63 334.12 153.64 c -334.12 154.00 l -330.10 154.00 l -330.27 155.51 331.01 156.27 332.32 156.27 c -332.80 156.27 333.39 156.14 334.09 155.88 c -h -330.16 153.13 m -332.96 153.13 l -332.96 151.95 332.52 151.36 331.64 151.36 c -330.75 151.36 330.26 151.95 330.16 153.13 c -h -336.48 159.31 m -337.52 157.00 l -335.05 150.64 l -336.30 150.64 l -338.12 155.43 l -340.07 150.64 l -341.16 150.64 l -337.69 159.31 l -h -344.07 157.15 m -343.54 157.15 342.90 157.02 342.15 156.78 c -342.15 155.72 l -342.90 156.09 343.56 156.28 344.12 156.28 c -344.45 156.28 344.73 156.19 344.95 156.01 c -345.16 155.83 345.27 155.61 345.27 155.34 c -345.27 154.94 344.97 154.62 344.35 154.36 c -343.68 154.07 l -342.68 153.66 342.19 153.06 342.19 152.28 c -342.19 151.73 342.38 151.29 342.77 150.97 c -343.17 150.66 343.71 150.50 344.39 150.50 c -344.74 150.50 345.18 150.54 345.71 150.64 c -345.95 150.69 l -345.95 151.65 l -345.30 151.46 344.79 151.36 344.41 151.36 c -343.67 151.36 343.30 151.63 343.30 152.17 c -343.30 152.52 343.58 152.81 344.14 153.05 c -344.70 153.29 l -345.33 153.55 345.77 153.83 346.04 154.13 c -346.30 154.42 346.43 154.79 346.43 155.23 c -346.43 155.79 346.21 156.25 345.77 156.61 c -345.32 156.97 344.76 157.15 344.07 157.15 c -h -350.41 157.15 m -349.83 157.15 349.37 156.98 349.04 156.64 c -348.71 156.31 348.55 155.84 348.55 155.24 c -348.55 151.50 l -347.75 151.50 l -347.75 150.64 l -348.55 150.64 l -348.55 149.48 l -349.70 149.37 l -349.70 150.64 l -351.37 150.64 l -351.37 151.50 l -349.70 151.50 l -349.70 155.03 l -349.70 155.86 350.06 156.28 350.78 156.28 c -350.93 156.28 351.12 156.25 351.34 156.20 c -351.34 157.00 l -350.98 157.10 350.67 157.15 350.41 157.15 c -h -355.48 157.15 m -354.57 157.15 353.84 156.84 353.30 156.24 c -352.76 155.64 352.49 154.83 352.49 153.82 c -352.49 152.79 352.76 151.99 353.30 151.39 c -353.85 150.79 354.59 150.50 355.52 150.50 c -356.46 150.50 357.19 150.79 357.74 151.39 c -358.28 151.99 358.56 152.79 358.56 153.81 c -358.56 154.85 358.28 155.66 357.74 156.26 c -357.19 156.85 356.44 157.15 355.48 157.15 c -h -355.50 156.28 m -356.72 156.28 357.33 155.46 357.33 153.81 c -357.33 152.18 356.73 151.36 355.52 151.36 c -354.32 151.36 353.72 152.18 353.72 153.82 c -353.72 155.46 354.31 156.28 355.50 156.28 c -h -360.36 157.00 m -360.36 150.64 l -361.52 150.64 l -361.52 151.83 l -362.12 150.94 362.87 150.50 363.75 150.50 c -364.30 150.50 364.74 150.67 365.07 151.02 c -365.40 151.37 365.56 151.84 365.56 152.43 c -365.56 157.00 l -364.41 157.00 l -364.41 152.80 l -364.41 152.33 364.34 152.00 364.20 151.79 c -364.06 151.59 363.83 151.49 363.51 151.49 c -362.81 151.49 362.14 151.96 361.52 152.88 c -361.52 157.00 l -h -372.47 156.79 m -371.70 157.03 371.04 157.15 370.49 157.15 c -369.55 157.15 368.78 156.83 368.19 156.21 c -367.60 155.59 367.30 154.78 367.30 153.79 c -367.30 152.82 367.57 152.03 368.09 151.42 c -368.61 150.80 369.28 150.49 370.09 150.49 c -370.86 150.49 371.45 150.76 371.87 151.31 c -372.29 151.86 372.50 152.63 372.50 153.64 c -372.50 154.00 l -368.48 154.00 l -368.65 155.51 369.39 156.27 370.70 156.27 c -371.18 156.27 371.77 156.14 372.47 155.88 c -h -368.54 153.13 m -371.34 153.13 l -371.34 151.95 370.90 151.36 370.02 151.36 c -369.13 151.36 368.64 151.95 368.54 153.13 c -h -374.14 150.64 m -373.99 147.75 l -375.14 147.75 l -375.00 150.64 l -h -376.16 150.64 m -376.01 147.75 l -377.17 147.75 l -377.03 150.64 l -h -f -80.518 137.00 m -80.518 134.54 l -76.615 134.54 l -76.615 133.67 l -80.518 128.33 l -81.602 128.33 l -81.602 133.60 l -82.762 133.60 l -82.762 134.54 l -81.602 134.54 l -81.602 137.00 l -h -77.746 133.60 m -80.594 133.60 l -80.594 129.75 l -h -87.350 137.22 m -86.455 137.22 85.731 136.80 85.179 135.95 c -84.626 135.11 84.350 134.01 84.350 132.66 c -84.350 131.29 84.628 130.19 85.185 129.36 c -85.741 128.53 86.475 128.11 87.385 128.11 c -88.295 128.11 89.028 128.53 89.585 129.36 c -90.142 130.19 90.420 131.29 90.420 132.64 c -90.420 134.03 90.142 135.14 89.585 135.97 c -89.028 136.80 88.283 137.22 87.350 137.22 c -h -87.361 136.35 m -88.584 136.35 89.195 135.11 89.195 132.62 c -89.195 130.19 88.592 128.98 87.385 128.98 c -86.182 128.98 85.580 130.21 85.580 132.66 c -85.580 135.12 86.174 136.35 87.361 136.35 c -h -92.980 137.00 m -92.980 136.13 l -94.715 136.13 l -94.715 129.29 l -92.980 129.72 l -92.980 128.83 l -95.875 128.11 l -95.875 136.13 l -97.609 136.13 l -97.609 137.00 l -h -103.61 128.33 m -104.84 128.33 l -104.84 133.80 l -104.84 134.67 105.00 135.31 105.32 135.70 c -105.65 136.10 106.16 136.30 106.86 136.30 c -107.55 136.30 108.04 136.11 108.32 135.74 c -108.61 135.36 108.75 134.73 108.75 133.84 c -108.75 128.33 l -109.83 128.33 l -109.83 133.82 l -109.83 135.01 109.59 135.87 109.10 136.41 c -108.62 136.95 107.84 137.22 106.78 137.22 c -105.70 137.22 104.90 136.94 104.38 136.38 c -103.87 135.82 103.61 134.96 103.61 133.79 c -h -112.03 137.00 m -112.03 130.64 l -113.18 130.64 l -113.18 131.83 l -113.79 130.94 114.54 130.50 115.42 130.50 c -115.97 130.50 116.41 130.67 116.74 131.02 c -117.07 131.37 117.23 131.84 117.23 132.43 c -117.23 137.00 l -116.08 137.00 l -116.08 132.80 l -116.08 132.33 116.01 132.00 115.87 131.79 c -115.73 131.59 115.50 131.49 115.18 131.49 c -114.47 131.49 113.81 131.96 113.18 132.88 c -113.18 137.00 l -h -122.76 136.19 m -122.07 136.83 121.40 137.15 120.76 137.15 c -120.23 137.15 119.79 136.98 119.45 136.65 c -119.10 136.32 118.93 135.90 118.93 135.40 c -118.93 134.71 119.22 134.17 119.80 133.80 c -120.39 133.42 121.22 133.24 122.31 133.24 c -122.59 133.24 l -122.59 132.47 l -122.59 131.73 122.21 131.36 121.45 131.36 c -120.84 131.36 120.18 131.55 119.48 131.93 c -119.48 130.97 l -120.25 130.65 120.98 130.50 121.66 130.50 c -122.37 130.50 122.90 130.66 123.24 130.98 c -123.57 131.30 123.74 131.79 123.74 132.47 c -123.74 135.35 l -123.74 136.01 123.95 136.34 124.35 136.34 c -124.40 136.34 124.48 136.34 124.57 136.32 c -124.66 136.96 l -124.39 137.08 124.11 137.15 123.79 137.15 c -123.25 137.15 122.91 136.83 122.76 136.19 c -h -122.59 135.56 m -122.59 133.92 l -122.20 133.91 l -121.57 133.91 121.06 134.03 120.67 134.27 c -120.28 134.51 120.08 134.82 120.08 135.21 c -120.08 135.49 120.18 135.72 120.37 135.92 c -120.57 136.11 120.81 136.20 121.09 136.20 c -121.57 136.20 122.07 135.99 122.59 135.56 c -h -130.08 137.00 m -130.08 135.80 l -129.47 136.70 128.72 137.15 127.85 137.15 c -127.29 137.15 126.85 136.97 126.53 136.62 c -126.20 136.27 126.03 135.80 126.03 135.21 c -126.03 130.64 l -127.19 130.64 l -127.19 134.83 l -127.19 135.31 127.26 135.65 127.40 135.85 c -127.53 136.05 127.77 136.15 128.09 136.15 c -128.79 136.15 129.46 135.69 130.08 134.76 c -130.08 130.64 l -131.24 130.64 l -131.24 137.00 l -h -135.46 137.15 m -134.88 137.15 134.42 136.98 134.09 136.64 c -133.76 136.31 133.60 135.84 133.60 135.24 c -133.60 131.50 l -132.80 131.50 l -132.80 130.64 l -133.60 130.64 l -133.60 129.48 l -134.75 129.37 l -134.75 130.64 l -136.42 130.64 l -136.42 131.50 l -134.75 131.50 l -134.75 135.03 l -134.75 135.86 135.11 136.28 135.83 136.28 c -135.98 136.28 136.17 136.25 136.39 136.20 c -136.39 137.00 l -136.03 137.10 135.72 137.15 135.46 137.15 c -h -138.04 137.00 m -138.04 127.75 l -139.19 127.75 l -139.19 131.83 l -139.80 130.94 140.55 130.50 141.43 130.50 c -141.98 130.50 142.42 130.67 142.75 131.02 c -143.08 131.37 143.24 131.84 143.24 132.43 c -143.24 137.00 l -142.09 137.00 l -142.09 132.80 l -142.09 132.33 142.02 132.00 141.88 131.79 c -141.74 131.59 141.51 131.49 141.19 131.49 c -140.48 131.49 139.82 131.96 139.19 132.88 c -139.19 137.00 l -h -147.98 137.15 m -147.07 137.15 146.34 136.84 145.80 136.24 c -145.25 135.64 144.98 134.83 144.98 133.82 c -144.98 132.79 145.25 131.99 145.80 131.39 c -146.34 130.79 147.08 130.50 148.02 130.50 c -148.95 130.50 149.69 130.79 150.24 131.39 c -150.78 131.99 151.05 132.79 151.05 133.81 c -151.05 134.85 150.78 135.66 150.23 136.26 c -149.69 136.85 148.93 137.15 147.98 137.15 c -h -147.99 136.28 m -149.22 136.28 149.83 135.46 149.83 133.81 c -149.83 132.18 149.22 131.36 148.02 131.36 c -146.81 131.36 146.21 132.18 146.21 133.82 c -146.21 135.46 146.81 136.28 147.99 136.28 c -h -152.86 137.00 m -152.86 130.64 l -154.01 130.64 l -154.01 131.83 l -154.47 130.94 155.13 130.50 156.00 130.50 c -156.12 130.50 156.24 130.51 156.37 130.53 c -156.37 131.60 l -156.17 131.54 156.00 131.50 155.85 131.50 c -155.12 131.50 154.50 131.94 154.01 132.80 c -154.01 137.00 l -h -157.77 137.00 m -157.77 130.64 l -158.92 130.64 l -158.92 137.00 l -h -157.77 129.48 m -157.77 128.33 l -158.92 128.33 l -158.92 129.48 l -h -160.80 137.00 m -160.80 136.13 l -164.74 131.50 l -160.98 131.50 l -160.98 130.64 l -166.16 130.64 l -166.16 131.50 l -162.23 136.13 l -166.24 136.13 l -166.24 137.00 l -h -172.78 136.79 m -172.01 137.03 171.34 137.15 170.79 137.15 c -169.86 137.15 169.09 136.83 168.50 136.21 c -167.91 135.59 167.61 134.78 167.61 133.79 c -167.61 132.82 167.87 132.03 168.39 131.42 c -168.92 130.80 169.58 130.49 170.39 130.49 c -171.16 130.49 171.76 130.76 172.18 131.31 c -172.60 131.86 172.81 132.63 172.81 133.64 c -172.80 134.00 l -168.79 134.00 l -168.96 135.51 169.70 136.27 171.01 136.27 c -171.49 136.27 172.08 136.14 172.78 135.88 c -h -168.84 133.13 m -171.65 133.13 l -171.65 131.95 171.21 131.36 170.32 131.36 c -169.44 131.36 168.94 131.95 168.84 133.13 c -h -178.88 137.00 m -178.88 135.80 l -178.42 136.70 177.71 137.15 176.76 137.15 c -176.00 137.15 175.40 136.87 174.96 136.31 c -174.52 135.75 174.30 134.99 174.30 134.02 c -174.30 132.96 174.55 132.11 175.04 131.46 c -175.54 130.82 176.20 130.50 177.01 130.50 c -177.76 130.50 178.39 130.79 178.88 131.36 c -178.88 127.75 l -180.04 127.75 l -180.04 137.00 l -h -178.88 132.15 m -178.29 131.63 177.72 131.36 177.19 131.36 c -176.08 131.36 175.53 132.21 175.53 133.90 c -175.53 135.39 176.02 136.13 177.00 136.13 c -177.64 136.13 178.27 135.78 178.88 135.08 c -h -f -404.06 241.15 m -403.48 241.15 403.02 240.98 402.69 240.64 c -402.37 240.31 402.20 239.84 402.20 239.24 c -402.20 235.50 l -401.40 235.50 l -401.40 234.64 l -402.20 234.64 l -402.20 233.48 l -403.36 233.37 l -403.36 234.64 l -405.02 234.64 l -405.02 235.50 l -403.36 235.50 l -403.36 239.03 l -403.36 239.86 403.71 240.28 404.43 240.28 c -404.59 240.28 404.77 240.25 404.99 240.20 c -404.99 241.00 l -404.63 241.10 404.33 241.15 404.06 241.15 c -h -409.13 241.15 m -408.22 241.15 407.50 240.84 406.95 240.24 c -406.41 239.64 406.14 238.83 406.14 237.82 c -406.14 236.79 406.41 235.99 406.96 235.39 c -407.50 234.79 408.24 234.50 409.17 234.50 c -410.11 234.50 410.85 234.79 411.39 235.39 c -411.94 235.99 412.21 236.79 412.21 237.81 c -412.21 238.85 411.94 239.66 411.39 240.26 c -410.84 240.85 410.09 241.15 409.13 241.15 c -h -409.15 240.28 m -410.37 240.28 410.98 239.46 410.98 237.81 c -410.98 236.18 410.38 235.36 409.17 235.36 c -407.97 235.36 407.37 236.18 407.37 237.82 c -407.37 239.46 407.96 240.28 409.15 240.28 c -h -414.01 241.00 m -414.01 231.75 l -415.17 231.75 l -415.17 237.72 l -417.86 234.64 l -419.11 234.64 l -416.53 237.61 l -419.64 241.00 l -418.16 241.00 l -415.17 237.74 l -415.17 241.00 l -h -425.69 240.79 m -424.92 241.03 424.26 241.15 423.71 241.15 c -422.77 241.15 422.00 240.83 421.41 240.21 c -420.82 239.59 420.52 238.78 420.52 237.79 c -420.52 236.82 420.78 236.03 421.31 235.42 c -421.83 234.80 422.49 234.49 423.31 234.49 c -424.08 234.49 424.67 234.76 425.09 235.31 c -425.51 235.86 425.72 236.63 425.72 237.64 c -425.71 238.00 l -421.70 238.00 l -421.87 239.51 422.61 240.27 423.92 240.27 c -424.40 240.27 424.99 240.14 425.69 239.88 c -h -421.75 237.13 m -424.56 237.13 l -424.56 235.95 424.12 235.36 423.24 235.36 c -422.35 235.36 421.86 235.95 421.75 237.13 c -h -427.71 241.00 m -427.71 234.64 l -428.87 234.64 l -428.87 235.83 l -429.48 234.94 430.22 234.50 431.11 234.50 c -431.66 234.50 432.10 234.67 432.42 235.02 c -432.75 235.37 432.92 235.84 432.92 236.43 c -432.92 241.00 l -431.76 241.00 l -431.76 236.80 l -431.76 236.33 431.69 236.00 431.55 235.79 c -431.42 235.59 431.19 235.49 430.87 235.49 c -430.16 235.49 429.49 235.96 428.87 236.88 c -428.87 241.00 l -h -435.18 242.88 m -435.18 242.45 l -435.55 242.34 435.74 241.90 435.74 241.12 c -435.74 241.00 l -435.18 241.00 l -435.18 239.55 l -436.62 239.55 l -436.62 240.81 l -436.62 242.09 436.14 242.78 435.18 242.88 c -h -444.44 241.15 m -443.91 241.15 443.27 241.02 442.52 240.78 c -442.52 239.72 l -443.27 240.09 443.93 240.28 444.49 240.28 c -444.82 240.28 445.10 240.19 445.31 240.01 c -445.53 239.83 445.64 239.61 445.64 239.34 c -445.64 238.94 445.34 238.62 444.72 238.36 c -444.05 238.07 l -443.05 237.66 442.55 237.06 442.55 236.28 c -442.55 235.73 442.75 235.29 443.14 234.97 c -443.54 234.66 444.07 234.50 444.76 234.50 c -445.11 234.50 445.55 234.54 446.08 234.64 c -446.32 234.69 l -446.32 235.65 l -445.67 235.46 445.16 235.36 444.78 235.36 c -444.04 235.36 443.67 235.63 443.67 236.17 c -443.67 236.52 443.95 236.81 444.51 237.05 c -445.07 237.29 l -445.70 237.55 446.14 237.83 446.40 238.13 c -446.67 238.42 446.80 238.79 446.80 239.23 c -446.80 239.79 446.58 240.25 446.13 240.61 c -445.69 240.97 445.13 241.15 444.44 241.15 c -h -453.54 240.79 m -452.76 241.03 452.10 241.15 451.55 241.15 c -450.61 241.15 449.85 240.83 449.25 240.21 c -448.66 239.59 448.37 238.78 448.37 237.79 c -448.37 236.82 448.63 236.03 449.15 235.42 c -449.67 234.80 450.34 234.49 451.15 234.49 c -451.92 234.49 452.51 234.76 452.93 235.31 c -453.35 235.86 453.56 236.63 453.56 237.64 c -453.56 238.00 l -449.54 238.00 l -449.71 239.51 450.45 240.27 451.77 240.27 c -452.25 240.27 452.84 240.14 453.54 239.88 c -h -449.60 237.13 m -452.40 237.13 l -452.40 235.95 451.96 235.36 451.08 235.36 c -450.19 235.36 449.70 235.95 449.60 237.13 c -h -455.56 241.00 m -455.56 234.64 l -456.71 234.64 l -456.71 235.83 l -457.17 234.94 457.83 234.50 458.70 234.50 c -458.82 234.50 458.94 234.51 459.07 234.53 c -459.07 235.60 l -458.87 235.54 458.70 235.50 458.54 235.50 c -457.81 235.50 457.20 235.94 456.71 236.80 c -456.71 241.00 l -h -461.79 241.00 m -459.42 234.64 l -460.57 234.64 l -462.42 239.59 l -464.38 234.64 l -465.45 234.64 l -462.94 241.00 l -h -466.68 241.00 m -466.68 234.64 l -467.83 234.64 l -467.83 241.00 l -h -466.68 233.48 m -466.68 232.33 l -467.83 232.33 l -467.83 233.48 l -h -472.63 241.15 m -471.77 241.15 471.06 240.83 470.49 240.19 c -469.93 239.55 469.64 238.75 469.64 237.78 c -469.64 236.75 469.92 235.94 470.48 235.36 c -471.04 234.79 471.83 234.50 472.83 234.50 c -473.33 234.50 473.88 234.56 474.49 234.70 c -474.49 235.67 l -473.84 235.48 473.31 235.38 472.90 235.38 c -472.31 235.38 471.84 235.60 471.48 236.05 c -471.12 236.49 470.94 237.08 470.94 237.82 c -470.94 238.53 471.13 239.11 471.49 239.55 c -471.86 239.99 472.34 240.21 472.94 240.21 c -473.46 240.21 474.01 240.08 474.56 239.81 c -474.56 240.81 l -473.82 241.03 473.17 241.15 472.63 241.15 c -h -480.96 240.79 m -480.18 241.03 479.52 241.15 478.97 241.15 c -478.03 241.15 477.27 240.83 476.68 240.21 c -476.08 239.59 475.79 238.78 475.79 237.79 c -475.79 236.82 476.05 236.03 476.57 235.42 c -477.09 234.80 477.76 234.49 478.57 234.49 c -479.34 234.49 479.94 234.76 480.36 235.31 c -480.78 235.86 480.99 236.63 480.99 237.64 c -480.98 238.00 l -476.97 238.00 l -477.13 239.51 477.88 240.27 479.19 240.27 c -479.67 240.27 480.26 240.14 480.96 239.88 c -h -477.02 237.13 m -479.83 237.13 l -479.83 235.95 479.38 235.36 478.50 235.36 c -477.62 235.36 477.12 235.95 477.02 237.13 c -h -486.66 241.22 m -485.31 241.22 484.27 240.82 483.54 240.03 c -482.80 239.24 482.43 238.12 482.43 236.67 c -482.43 235.22 482.81 234.10 483.56 233.31 c -484.30 232.51 485.36 232.11 486.72 232.11 c -487.49 232.11 488.40 232.24 489.45 232.49 c -489.45 233.65 l -488.26 233.24 487.34 233.03 486.70 233.03 c -485.76 233.03 485.03 233.35 484.51 233.99 c -484.00 234.62 483.74 235.52 483.74 236.68 c -483.74 237.79 484.02 238.66 484.57 239.30 c -485.12 239.94 485.87 240.26 486.82 240.26 c -487.64 240.26 488.52 240.00 489.46 239.50 c -489.46 240.55 l -488.60 241.00 487.67 241.22 486.66 241.22 c -h -494.56 240.19 m -493.87 240.83 493.21 241.15 492.56 241.15 c -492.04 241.15 491.60 240.98 491.25 240.65 c -490.90 240.32 490.73 239.90 490.73 239.40 c -490.73 238.71 491.02 238.17 491.61 237.80 c -492.19 237.42 493.03 237.24 494.12 237.24 c -494.39 237.24 l -494.39 236.47 l -494.39 235.73 494.01 235.36 493.26 235.36 c -492.65 235.36 491.99 235.55 491.28 235.93 c -491.28 234.97 l -492.06 234.65 492.79 234.50 493.47 234.50 c -494.18 234.50 494.70 234.66 495.04 234.98 c -495.38 235.30 495.55 235.79 495.55 236.47 c -495.55 239.35 l -495.55 240.01 495.75 240.34 496.16 240.34 c -496.21 240.34 496.28 240.34 496.38 240.32 c -496.46 240.96 l -496.20 241.08 495.91 241.15 495.59 241.15 c -495.05 241.15 494.71 240.83 494.56 240.19 c -h -494.39 239.56 m -494.39 237.92 l -494.01 237.91 l -493.37 237.91 492.86 238.03 492.47 238.27 c -492.08 238.51 491.88 238.82 491.88 239.21 c -491.88 239.49 491.98 239.72 492.18 239.92 c -492.37 240.11 492.61 240.20 492.89 240.20 c -493.37 240.20 493.87 239.99 494.39 239.56 c -h -499.82 241.15 m -499.23 241.15 498.78 240.98 498.45 240.64 c -498.12 240.31 497.96 239.84 497.96 239.24 c -497.96 235.50 l -497.16 235.50 l -497.16 234.64 l -497.96 234.64 l -497.96 233.48 l -499.11 233.37 l -499.11 234.64 l -500.77 234.64 l -500.77 235.50 l -499.11 235.50 l -499.11 239.03 l -499.11 239.86 499.47 240.28 500.19 240.28 c -500.34 240.28 500.53 240.25 500.74 240.20 c -500.74 241.00 l -500.39 241.10 500.08 241.15 499.82 241.15 c -h -505.68 240.19 m -504.99 240.83 504.32 241.15 503.68 241.15 c -503.15 241.15 502.71 240.98 502.37 240.65 c -502.02 240.32 501.85 239.90 501.85 239.40 c -501.85 238.71 502.14 238.17 502.72 237.80 c -503.31 237.42 504.14 237.24 505.23 237.24 c -505.51 237.24 l -505.51 236.47 l -505.51 235.73 505.13 235.36 504.37 235.36 c -503.76 235.36 503.10 235.55 502.40 235.93 c -502.40 234.97 l -503.17 234.65 503.90 234.50 504.58 234.50 c -505.29 234.50 505.82 234.66 506.16 234.98 c -506.49 235.30 506.66 235.79 506.66 236.47 c -506.66 239.35 l -506.66 240.01 506.87 240.34 507.27 240.34 c -507.32 240.34 507.40 240.34 507.49 240.32 c -507.58 240.96 l -507.31 241.08 507.03 241.15 506.71 241.15 c -506.17 241.15 505.83 240.83 505.68 240.19 c -h -505.51 239.56 m -505.51 237.92 l -505.12 237.91 l -504.49 237.91 503.98 238.03 503.59 238.27 c -503.20 238.51 503.00 238.82 503.00 239.21 c -503.00 239.49 503.10 239.72 503.29 239.92 c -503.49 240.11 503.73 240.20 504.01 240.20 c -504.49 240.20 504.99 239.99 505.51 239.56 c -h -509.02 241.00 m -509.02 231.75 l -510.18 231.75 l -510.18 241.00 l -h -514.98 241.15 m -514.07 241.15 513.35 240.84 512.80 240.24 c -512.26 239.64 511.99 238.83 511.99 237.82 c -511.99 236.79 512.26 235.99 512.81 235.39 c -513.35 234.79 514.09 234.50 515.02 234.50 c -515.96 234.50 516.70 234.79 517.24 235.39 c -517.79 235.99 518.06 236.79 518.06 237.81 c -518.06 238.85 517.79 239.66 517.24 240.26 c -516.69 240.85 515.94 241.15 514.98 241.15 c -h -515.00 240.28 m -516.22 240.28 516.83 239.46 516.83 237.81 c -516.83 236.18 516.23 235.36 515.02 235.36 c -513.82 235.36 513.22 236.18 513.22 237.82 c -513.22 239.46 513.81 240.28 515.00 240.28 c -h -519.89 243.12 m -520.02 242.11 l -520.69 242.43 521.35 242.59 522.00 242.59 c -523.30 242.59 523.95 241.90 523.95 240.52 c -523.95 239.52 l -523.52 240.41 522.82 240.85 521.85 240.85 c -521.09 240.85 520.48 240.58 520.03 240.02 c -519.58 239.47 519.36 238.72 519.36 237.78 c -519.36 236.81 519.62 236.02 520.13 235.41 c -520.64 234.80 521.30 234.50 522.11 234.50 c -522.82 234.50 523.44 234.79 523.95 235.36 c -523.95 234.64 l -525.11 234.64 l -525.11 239.27 l -525.11 240.26 525.06 241.00 524.95 241.48 c -524.85 241.96 524.65 242.35 524.37 242.65 c -523.87 243.19 523.08 243.46 522.02 243.46 c -521.28 243.46 520.57 243.34 519.89 243.12 c -h -523.95 238.80 m -523.95 236.15 l -523.44 235.63 522.89 235.36 522.29 235.36 c -521.76 235.36 521.34 235.58 521.04 236.00 c -520.74 236.43 520.59 237.01 520.59 237.75 c -520.59 239.15 521.08 239.85 522.06 239.85 c -522.73 239.85 523.36 239.50 523.95 238.80 c -h -f -[ 5.0000 5.0000] 0.0000 d -530.50 244.50 m -70.500 244.50 l -S -[] 0.0000 d -70.000 244.00 m -76.000 238.00 l -76.000 250.00 l -h -f* -697.47 375.00 m -695.11 368.64 l -696.26 368.64 l -698.11 373.59 l -700.06 368.64 l -701.14 368.64 l -698.63 375.00 l -h -705.65 374.19 m -704.96 374.83 704.29 375.15 703.65 375.15 c -703.12 375.15 702.68 374.98 702.34 374.65 c -701.99 374.32 701.81 373.90 701.81 373.40 c -701.81 372.71 702.11 372.17 702.69 371.80 c -703.27 371.42 704.11 371.24 705.20 371.24 c -705.48 371.24 l -705.48 370.47 l -705.48 369.73 705.10 369.36 704.34 369.36 c -703.73 369.36 703.07 369.55 702.37 369.93 c -702.37 368.97 l -703.14 368.65 703.87 368.50 704.55 368.50 c -705.26 368.50 705.79 368.66 706.12 368.98 c -706.46 369.30 706.63 369.79 706.63 370.47 c -706.63 373.35 l -706.63 374.01 706.83 374.34 707.24 374.34 c -707.29 374.34 707.37 374.34 707.46 374.32 c -707.54 374.96 l -707.28 375.08 706.99 375.15 706.68 375.15 c -706.14 375.15 705.79 374.83 705.65 374.19 c -h -705.48 373.56 m -705.48 371.92 l -705.09 371.91 l -704.46 371.91 703.95 372.03 703.55 372.27 c -703.16 372.51 702.97 372.82 702.97 373.21 c -702.97 373.49 703.07 373.72 703.26 373.92 c -703.46 374.11 703.70 374.20 703.98 374.20 c -704.46 374.20 704.96 373.99 705.48 373.56 c -h -708.99 375.00 m -708.99 365.75 l -710.15 365.75 l -710.15 375.00 l -h -712.46 375.00 m -712.46 368.64 l -713.62 368.64 l -713.62 375.00 l -h -712.46 367.48 m -712.46 366.33 l -713.62 366.33 l -713.62 367.48 l -h -720.01 375.00 m -720.01 373.80 l -719.54 374.70 718.84 375.15 717.89 375.15 c -717.13 375.15 716.52 374.87 716.08 374.31 c -715.65 373.75 715.43 372.99 715.43 372.02 c -715.43 370.96 715.67 370.11 716.17 369.46 c -716.67 368.82 717.33 368.50 718.14 368.50 c -718.89 368.50 719.52 368.79 720.01 369.36 c -720.01 365.75 l -721.17 365.75 l -721.17 375.00 l -h -720.01 370.15 m -719.42 369.63 718.85 369.36 718.31 369.36 c -717.21 369.36 716.66 370.21 716.66 371.90 c -716.66 373.39 717.15 374.13 718.13 374.13 c -718.77 374.13 719.40 373.78 720.01 373.08 c -h -726.76 374.19 m -726.07 374.83 725.41 375.15 724.77 375.15 c -724.24 375.15 723.80 374.98 723.45 374.65 c -723.11 374.32 722.93 373.90 722.93 373.40 c -722.93 372.71 723.22 372.17 723.81 371.80 c -724.39 371.42 725.23 371.24 726.32 371.24 c -726.59 371.24 l -726.59 370.47 l -726.59 369.73 726.21 369.36 725.46 369.36 c -724.85 369.36 724.19 369.55 723.48 369.93 c -723.48 368.97 l -724.26 368.65 724.99 368.50 725.67 368.50 c -726.38 368.50 726.90 368.66 727.24 368.98 c -727.58 369.30 727.75 369.79 727.75 370.47 c -727.75 373.35 l -727.75 374.01 727.95 374.34 728.36 374.34 c -728.41 374.34 728.48 374.34 728.58 374.32 c -728.66 374.96 l -728.40 375.08 728.11 375.15 727.79 375.15 c -727.26 375.15 726.91 374.83 726.76 374.19 c -h -726.59 373.56 m -726.59 371.92 l -726.21 371.91 l -725.57 371.91 725.06 372.03 724.67 372.27 c -724.28 372.51 724.09 372.82 724.09 373.21 c -724.09 373.49 724.18 373.72 724.38 373.92 c -724.57 374.11 724.81 374.20 725.09 374.20 c -725.57 374.20 726.07 373.99 726.59 373.56 c -h -732.02 375.15 m -731.43 375.15 730.98 374.98 730.65 374.64 c -730.32 374.31 730.16 373.84 730.16 373.24 c -730.16 369.50 l -729.36 369.50 l -729.36 368.64 l -730.16 368.64 l -730.16 367.48 l -731.31 367.37 l -731.31 368.64 l -732.97 368.64 l -732.97 369.50 l -731.31 369.50 l -731.31 373.03 l -731.31 373.86 731.67 374.28 732.39 374.28 c -732.54 374.28 732.73 374.25 732.95 374.20 c -732.95 375.00 l -732.59 375.10 732.28 375.15 732.02 375.15 c -h -739.26 374.79 m -738.49 375.03 737.83 375.15 737.28 375.15 c -736.34 375.15 735.57 374.83 734.98 374.21 c -734.39 373.59 734.09 372.78 734.09 371.79 c -734.09 370.82 734.35 370.03 734.88 369.42 c -735.40 368.80 736.06 368.49 736.88 368.49 c -737.65 368.49 738.24 368.76 738.66 369.31 c -739.08 369.86 739.29 370.63 739.29 371.64 c -739.29 372.00 l -735.27 372.00 l -735.44 373.51 736.18 374.27 737.49 374.27 c -737.97 374.27 738.56 374.14 739.26 373.88 c -h -735.32 371.13 m -738.13 371.13 l -738.13 369.95 737.69 369.36 736.81 369.36 c -735.92 369.36 735.43 369.95 735.32 371.13 c -h -748.37 375.00 m -741.43 371.53 l -748.37 368.06 l -748.37 369.03 l -743.37 371.53 l -748.37 374.03 l -h -752.73 375.15 m -752.15 375.15 751.69 374.98 751.36 374.64 c -751.03 374.31 750.87 373.84 750.87 373.24 c -750.87 369.50 l -750.07 369.50 l -750.07 368.64 l -750.87 368.64 l -750.87 367.48 l -752.02 367.37 l -752.02 368.64 l -753.69 368.64 l -753.69 369.50 l -752.02 369.50 l -752.02 373.03 l -752.02 373.86 752.38 374.28 753.10 374.28 c -753.25 374.28 753.44 374.25 753.66 374.20 c -753.66 375.00 l -753.30 375.10 752.99 375.15 752.73 375.15 c -h -757.80 375.15 m -756.89 375.15 756.16 374.84 755.62 374.24 c -755.08 373.64 754.81 372.83 754.81 371.82 c -754.81 370.79 755.08 369.99 755.62 369.39 c -756.17 368.79 756.91 368.50 757.84 368.50 c -758.78 368.50 759.51 368.79 760.06 369.39 c -760.60 369.99 760.88 370.79 760.88 371.81 c -760.88 372.85 760.60 373.66 760.06 374.26 c -759.51 374.85 758.76 375.15 757.80 375.15 c -h -757.82 374.28 m -759.04 374.28 759.65 373.46 759.65 371.81 c -759.65 370.18 759.05 369.36 757.84 369.36 c -756.64 369.36 756.04 370.18 756.04 371.82 c -756.04 373.46 756.63 374.28 757.82 374.28 c -h -762.68 375.00 m -762.68 365.75 l -763.84 365.75 l -763.84 371.72 l -766.53 368.64 l -767.77 368.64 l -765.20 371.61 l -768.31 375.00 l -766.83 375.00 l -763.84 371.74 l -763.84 375.00 l -h -774.36 374.79 m -773.59 375.03 772.92 375.15 772.37 375.15 c -771.44 375.15 770.67 374.83 770.08 374.21 c -769.49 373.59 769.19 372.78 769.19 371.79 c -769.19 370.82 769.45 370.03 769.97 369.42 c -770.50 368.80 771.16 368.49 771.97 368.49 c -772.74 368.49 773.34 368.76 773.76 369.31 c -774.18 369.86 774.39 370.63 774.39 371.64 c -774.38 372.00 l -770.37 372.00 l -770.54 373.51 771.28 374.27 772.59 374.27 c -773.07 374.27 773.66 374.14 774.36 373.88 c -h -770.42 371.13 m -773.23 371.13 l -773.23 369.95 772.79 369.36 771.90 369.36 c -771.02 369.36 770.52 369.95 770.42 371.13 c -h -776.38 375.00 m -776.38 368.64 l -777.54 368.64 l -777.54 369.83 l -778.14 368.94 778.89 368.50 779.77 368.50 c -780.32 368.50 780.76 368.67 781.09 369.02 c -781.42 369.37 781.58 369.84 781.58 370.43 c -781.58 375.00 l -780.43 375.00 l -780.43 370.80 l -780.43 370.33 780.36 370.00 780.22 369.79 c -780.08 369.59 779.85 369.49 779.53 369.49 c -778.83 369.49 778.16 369.96 777.54 370.88 c -777.54 375.00 l -h -783.85 376.88 m -783.85 376.45 l -784.22 376.34 784.41 375.90 784.41 375.12 c -784.41 375.00 l -783.85 375.00 l -783.85 373.55 l -785.29 373.55 l -785.29 374.81 l -785.29 376.09 784.81 376.78 783.85 376.88 c -h -791.42 376.73 m -791.42 365.75 l -793.74 365.75 l -793.74 366.62 l -792.44 366.62 l -792.44 375.87 l -793.74 375.87 l -793.74 376.73 l -h -797.23 375.15 m -796.65 375.15 796.19 374.98 795.86 374.64 c -795.54 374.31 795.37 373.84 795.37 373.24 c -795.37 369.50 l -794.57 369.50 l -794.57 368.64 l -795.37 368.64 l -795.37 367.48 l -796.53 367.37 l -796.53 368.64 l -798.19 368.64 l -798.19 369.50 l -796.53 369.50 l -796.53 373.03 l -796.53 373.86 796.88 374.28 797.60 374.28 c -797.76 374.28 797.94 374.25 798.16 374.20 c -798.16 375.00 l -797.80 375.10 797.50 375.15 797.23 375.15 c -h -804.48 374.79 m -803.70 375.03 803.04 375.15 802.49 375.15 c -801.55 375.15 800.79 374.83 800.20 374.21 c -799.60 373.59 799.31 372.78 799.31 371.79 c -799.31 370.82 799.57 370.03 800.09 369.42 c -800.61 368.80 801.28 368.49 802.09 368.49 c -802.86 368.49 803.46 368.76 803.88 369.31 c -804.30 369.86 804.51 370.63 804.51 371.64 c -804.50 372.00 l -800.49 372.00 l -800.65 373.51 801.39 374.27 802.71 374.27 c -803.19 374.27 803.78 374.14 804.48 373.88 c -h -800.54 371.13 m -803.35 371.13 l -803.35 369.95 802.90 369.36 802.02 369.36 c -801.13 369.36 800.64 369.95 800.54 371.13 c -h -806.50 375.00 m -806.50 368.64 l -807.65 368.64 l -807.65 369.83 l -808.26 368.94 809.01 368.50 809.89 368.50 c -810.44 368.50 810.88 368.67 811.21 369.02 c -811.54 369.37 811.70 369.84 811.70 370.43 c -811.70 375.00 l -810.55 375.00 l -810.55 370.80 l -810.55 370.33 810.48 370.00 810.34 369.79 c -810.20 369.59 809.97 369.49 809.65 369.49 c -808.94 369.49 808.28 369.96 807.65 370.88 c -807.65 375.00 l -h -817.23 374.19 m -816.54 374.83 815.87 375.15 815.23 375.15 c -814.70 375.15 814.26 374.98 813.92 374.65 c -813.57 374.32 813.39 373.90 813.39 373.40 c -813.39 372.71 813.69 372.17 814.27 371.80 c -814.85 371.42 815.69 371.24 816.78 371.24 c -817.06 371.24 l -817.06 370.47 l -817.06 369.73 816.68 369.36 815.92 369.36 c -815.31 369.36 814.65 369.55 813.95 369.93 c -813.95 368.97 l -814.72 368.65 815.45 368.50 816.13 368.50 c -816.84 368.50 817.37 368.66 817.70 368.98 c -818.04 369.30 818.21 369.79 818.21 370.47 c -818.21 373.35 l -818.21 374.01 818.41 374.34 818.82 374.34 c -818.87 374.34 818.95 374.34 819.04 374.32 c -819.12 374.96 l -818.86 375.08 818.57 375.15 818.26 375.15 c -817.72 375.15 817.38 374.83 817.23 374.19 c -h -817.06 373.56 m -817.06 371.92 l -816.67 371.91 l -816.04 371.91 815.53 372.03 815.13 372.27 c -814.74 372.51 814.55 372.82 814.55 373.21 c -814.55 373.49 814.65 373.72 814.84 373.92 c -815.04 374.11 815.28 374.20 815.56 374.20 c -816.04 374.20 816.54 373.99 817.06 373.56 c -h -820.57 375.00 m -820.57 368.64 l -821.73 368.64 l -821.73 369.83 l -822.34 368.94 823.08 368.50 823.96 368.50 c -824.52 368.50 824.96 368.67 825.28 369.02 c -825.61 369.37 825.78 369.84 825.78 370.43 c -825.78 375.00 l -824.62 375.00 l -824.62 370.80 l -824.62 370.33 824.55 370.00 824.41 369.79 c -824.27 369.59 824.04 369.49 823.72 369.49 c -823.02 369.49 822.35 369.96 821.73 370.88 c -821.73 375.00 l -h -829.93 375.15 m -829.34 375.15 828.89 374.98 828.56 374.64 c -828.23 374.31 828.07 373.84 828.07 373.24 c -828.07 369.50 l -827.27 369.50 l -827.27 368.64 l -828.07 368.64 l -828.07 367.48 l -829.22 367.37 l -829.22 368.64 l -830.88 368.64 l -830.88 369.50 l -829.22 369.50 l -829.22 373.03 l -829.22 373.86 829.58 374.28 830.30 374.28 c -830.45 374.28 830.64 374.25 830.86 374.20 c -830.86 375.00 l -830.50 375.10 830.19 375.15 829.93 375.15 c -h -834.10 376.73 m -834.10 365.75 l -831.79 365.75 l -831.79 366.62 l -833.09 366.62 l -833.09 375.87 l -831.79 375.87 l -831.79 376.73 l -h -836.56 375.00 m -843.49 371.53 l -836.56 368.06 l -836.56 369.03 l -841.55 371.53 l -836.56 374.03 l -h -f -850.50 378.50 m -538.50 378.50 l -S -538.00 378.00 m -544.00 372.00 l -544.00 384.00 l -h -f* -549.13 393.00 m -549.13 391.80 l -548.52 392.70 547.78 393.15 546.90 393.15 c -546.35 393.15 545.90 392.97 545.58 392.62 c -545.25 392.27 545.08 391.80 545.08 391.21 c -545.08 386.64 l -546.24 386.64 l -546.24 390.83 l -546.24 391.31 546.31 391.65 546.45 391.85 c -546.58 392.05 546.82 392.15 547.14 392.15 c -547.84 392.15 548.51 391.69 549.13 390.76 c -549.13 386.64 l -550.29 386.64 l -550.29 393.00 l -h -554.29 393.15 m -553.76 393.15 553.12 393.02 552.37 392.78 c -552.37 391.72 l -553.12 392.09 553.78 392.28 554.34 392.28 c -554.67 392.28 554.94 392.19 555.16 392.01 c -555.38 391.83 555.49 391.61 555.49 391.34 c -555.49 390.94 555.18 390.62 554.57 390.36 c -553.90 390.07 l -552.90 389.66 552.40 389.06 552.40 388.28 c -552.40 387.73 552.60 387.29 552.99 386.97 c -553.38 386.66 553.92 386.50 554.61 386.50 c -554.96 386.50 555.40 386.54 555.92 386.64 c -556.16 386.69 l -556.16 387.65 l -555.52 387.46 555.01 387.36 554.63 387.36 c -553.89 387.36 553.52 387.63 553.52 388.17 c -553.52 388.52 553.80 388.81 554.36 389.05 c -554.92 389.29 l -555.54 389.55 555.99 389.83 556.25 390.13 c -556.51 390.42 556.64 390.79 556.64 391.23 c -556.64 391.79 556.42 392.25 555.98 392.61 c -555.54 392.97 554.98 393.15 554.29 393.15 c -h -563.38 392.79 m -562.61 393.03 561.95 393.15 561.40 393.15 c -560.46 393.15 559.69 392.83 559.10 392.21 c -558.51 391.59 558.21 390.78 558.21 389.79 c -558.21 388.82 558.48 388.03 559.00 387.42 c -559.52 386.80 560.19 386.49 561.00 386.49 c -561.77 386.49 562.36 386.76 562.78 387.31 c -563.20 387.86 563.41 388.63 563.41 389.64 c -563.41 390.00 l -559.39 390.00 l -559.56 391.51 560.30 392.27 561.61 392.27 c -562.09 392.27 562.68 392.14 563.38 391.88 c -h -559.45 389.13 m -562.25 389.13 l -562.25 387.95 561.81 387.36 560.93 387.36 c -560.04 387.36 559.55 387.95 559.45 389.13 c -h -565.40 393.00 m -565.40 386.64 l -566.56 386.64 l -566.56 387.83 l -567.02 386.94 567.68 386.50 568.55 386.50 c -568.67 386.50 568.79 386.51 568.92 386.53 c -568.92 387.60 l -568.72 387.54 568.54 387.50 568.39 387.50 c -567.66 387.50 567.05 387.94 566.56 388.80 c -566.56 393.00 l -h -570.33 394.88 m -570.33 394.45 l -570.71 394.34 570.89 393.90 570.89 393.12 c -570.89 393.00 l -570.33 393.00 l -570.33 391.55 l -571.78 391.55 l -571.78 392.81 l -571.78 394.09 571.30 394.78 570.33 394.88 c -h -577.91 393.00 m -577.91 386.64 l -579.06 386.64 l -579.06 387.83 l -579.52 386.94 580.18 386.50 581.05 386.50 c -581.17 386.50 581.29 386.51 581.42 386.53 c -581.42 387.60 l -581.22 387.54 581.05 387.50 580.90 387.50 c -580.17 387.50 579.55 387.94 579.06 388.80 c -579.06 393.00 l -h -585.31 393.15 m -584.40 393.15 583.67 392.84 583.13 392.24 c -582.59 391.64 582.31 390.83 582.31 389.82 c -582.31 388.79 582.59 387.99 583.13 387.39 c -583.68 386.79 584.42 386.50 585.35 386.50 c -586.28 386.50 587.02 386.79 587.57 387.39 c -588.11 387.99 588.38 388.79 588.38 389.81 c -588.38 390.85 588.11 391.66 587.56 392.26 c -587.02 392.85 586.27 393.15 585.31 393.15 c -h -585.33 392.28 m -586.55 392.28 587.16 391.46 587.16 389.81 c -587.16 388.18 586.56 387.36 585.35 387.36 c -584.15 387.36 583.54 388.18 583.54 389.82 c -583.54 391.46 584.14 392.28 585.33 392.28 c -h -590.19 393.00 m -590.19 383.75 l -591.34 383.75 l -591.34 393.00 l -h -598.32 392.79 m -597.55 393.03 596.89 393.15 596.34 393.15 c -595.40 393.15 594.63 392.83 594.04 392.21 c -593.45 391.59 593.15 390.78 593.15 389.79 c -593.15 388.82 593.42 388.03 593.94 387.42 c -594.46 386.80 595.12 386.49 595.94 386.49 c -596.71 386.49 597.30 386.76 597.72 387.31 c -598.14 387.86 598.35 388.63 598.35 389.64 c -598.35 390.00 l -594.33 390.00 l -594.50 391.51 595.24 392.27 596.55 392.27 c -597.03 392.27 597.62 392.14 598.32 391.88 c -h -594.38 389.13 m -597.19 389.13 l -597.19 387.95 596.75 387.36 595.87 387.36 c -594.98 387.36 594.49 387.95 594.38 389.13 c -h -602.03 393.15 m -601.50 393.15 600.86 393.02 600.11 392.78 c -600.11 391.72 l -600.86 392.09 601.52 392.28 602.08 392.28 c -602.41 392.28 602.69 392.19 602.90 392.01 c -603.12 391.83 603.23 391.61 603.23 391.34 c -603.23 390.94 602.93 390.62 602.31 390.36 c -601.64 390.07 l -600.64 389.66 600.14 389.06 600.14 388.28 c -600.14 387.73 600.34 387.29 600.73 386.97 c -601.13 386.66 601.66 386.50 602.35 386.50 c -602.70 386.50 603.14 386.54 603.67 386.64 c -603.91 386.69 l -603.91 387.65 l -603.26 387.46 602.75 387.36 602.37 387.36 c -601.63 387.36 601.26 387.63 601.26 388.17 c -601.26 388.52 601.54 388.81 602.10 389.05 c -602.66 389.29 l -603.29 389.55 603.73 389.83 603.99 390.13 c -604.26 390.42 604.39 390.79 604.39 391.23 c -604.39 391.79 604.17 392.25 603.72 392.61 c -603.28 392.97 602.72 393.15 602.03 393.15 c -h -f -[ 5.0000 5.0000] 0.0000 d -538.50 396.50 m -850.50 396.50 l -S -[] 0.0000 d -850.00 396.00 m -844.00 390.00 l -844.00 402.00 l -h -f* -552.15 118.73 m -552.15 107.75 l -554.47 107.75 l -554.47 108.62 l -553.17 108.62 l -553.17 117.87 l -554.47 117.87 l -554.47 118.73 l -h -557.74 117.15 m -557.22 117.15 556.58 117.02 555.82 116.78 c -555.82 115.72 l -556.58 116.09 557.23 116.28 557.79 116.28 c -558.12 116.28 558.40 116.19 558.62 116.01 c -558.84 115.83 558.95 115.61 558.95 115.34 c -558.95 114.94 558.64 114.62 558.03 114.36 c -557.35 114.07 l -556.36 113.66 555.86 113.06 555.86 112.28 c -555.86 111.73 556.05 111.29 556.45 110.97 c -556.84 110.66 557.38 110.50 558.06 110.50 c -558.42 110.50 558.86 110.54 559.38 110.64 c -559.62 110.69 l -559.62 111.65 l -558.97 111.46 558.46 111.36 558.08 111.36 c -557.34 111.36 556.97 111.63 556.97 112.17 c -556.97 112.52 557.25 112.81 557.81 113.05 c -558.37 113.29 l -559.00 113.55 559.45 113.83 559.71 114.13 c -559.97 114.42 560.10 114.79 560.10 115.23 c -560.10 115.79 559.88 116.25 559.44 116.61 c -559.00 116.97 558.43 117.15 557.74 117.15 c -h -564.08 117.15 m -563.50 117.15 563.04 116.98 562.71 116.64 c -562.38 116.31 562.22 115.84 562.22 115.24 c -562.22 111.50 l -561.42 111.50 l -561.42 110.64 l -562.22 110.64 l -562.22 109.48 l -563.38 109.37 l -563.38 110.64 l -565.04 110.64 l -565.04 111.50 l -563.38 111.50 l -563.38 115.03 l -563.38 115.86 563.73 116.28 564.45 116.28 c -564.61 116.28 564.79 116.25 565.01 116.20 c -565.01 117.00 l -564.65 117.10 564.35 117.15 564.08 117.15 c -h -569.94 116.19 m -569.25 116.83 568.59 117.15 567.95 117.15 c -567.42 117.15 566.98 116.98 566.63 116.65 c -566.29 116.32 566.11 115.90 566.11 115.40 c -566.11 114.71 566.40 114.17 566.99 113.80 c -567.57 113.42 568.41 113.24 569.50 113.24 c -569.77 113.24 l -569.77 112.47 l -569.77 111.73 569.39 111.36 568.64 111.36 c -568.03 111.36 567.37 111.55 566.66 111.93 c -566.66 110.97 l -567.44 110.65 568.17 110.50 568.85 110.50 c -569.56 110.50 570.08 110.66 570.42 110.98 c -570.76 111.30 570.93 111.79 570.93 112.47 c -570.93 115.35 l -570.93 116.01 571.13 116.34 571.54 116.34 c -571.59 116.34 571.66 116.34 571.76 116.32 c -571.84 116.96 l -571.58 117.08 571.29 117.15 570.97 117.15 c -570.44 117.15 570.09 116.83 569.94 116.19 c -h -569.77 115.56 m -569.77 113.92 l -569.39 113.91 l -568.75 113.91 568.24 114.03 567.85 114.27 c -567.46 114.51 567.27 114.82 567.27 115.21 c -567.27 115.49 567.36 115.72 567.56 115.92 c -567.75 116.11 567.99 116.20 568.27 116.20 c -568.75 116.20 569.25 115.99 569.77 115.56 c -h -573.29 117.00 m -573.29 110.64 l -574.44 110.64 l -574.44 111.83 l -575.05 110.94 575.80 110.50 576.68 110.50 c -577.23 110.50 577.67 110.67 578.00 111.02 c -578.33 111.37 578.49 111.84 578.49 112.43 c -578.49 117.00 l -577.34 117.00 l -577.34 112.80 l -577.34 112.33 577.27 112.00 577.13 111.79 c -576.99 111.59 576.76 111.49 576.44 111.49 c -575.73 111.49 575.07 111.96 574.44 112.88 c -574.44 117.00 l -h -584.82 117.00 m -584.82 115.80 l -584.35 116.70 583.64 117.15 582.70 117.15 c -581.93 117.15 581.33 116.87 580.89 116.31 c -580.45 115.75 580.23 114.99 580.23 114.02 c -580.23 112.96 580.48 112.11 580.98 111.46 c -581.48 110.82 582.13 110.50 582.95 110.50 c -583.70 110.50 584.32 110.79 584.82 111.36 c -584.82 107.75 l -585.98 107.75 l -585.98 117.00 l -h -584.82 112.15 m -584.22 111.63 583.66 111.36 583.12 111.36 c -582.02 111.36 581.46 112.21 581.46 113.90 c -581.46 115.39 581.96 116.13 582.94 116.13 c -583.58 116.13 584.21 115.78 584.82 115.08 c -h -591.57 116.19 m -590.88 116.83 590.21 117.15 589.57 117.15 c -589.04 117.15 588.61 116.98 588.26 116.65 c -587.91 116.32 587.74 115.90 587.74 115.40 c -587.74 114.71 588.03 114.17 588.61 113.80 c -589.20 113.42 590.04 113.24 591.12 113.24 c -591.40 113.24 l -591.40 112.47 l -591.40 111.73 591.02 111.36 590.26 111.36 c -589.65 111.36 589.00 111.55 588.29 111.93 c -588.29 110.97 l -589.07 110.65 589.79 110.50 590.47 110.50 c -591.19 110.50 591.71 110.66 592.05 110.98 c -592.39 111.30 592.55 111.79 592.55 112.47 c -592.55 115.35 l -592.55 116.01 592.76 116.34 593.16 116.34 c -593.21 116.34 593.29 116.34 593.39 116.32 c -593.47 116.96 l -593.21 117.08 592.92 117.15 592.60 117.15 c -592.06 117.15 591.72 116.83 591.57 116.19 c -h -591.40 115.56 m -591.40 113.92 l -591.01 113.91 l -590.38 113.91 589.87 114.03 589.48 114.27 c -589.09 114.51 588.89 114.82 588.89 115.21 c -588.89 115.49 588.99 115.72 589.19 115.92 c -589.38 116.11 589.62 116.20 589.90 116.20 c -590.38 116.20 590.88 115.99 591.40 115.56 c -h -594.92 117.00 m -594.92 110.64 l -596.07 110.64 l -596.07 111.83 l -596.53 110.94 597.19 110.50 598.06 110.50 c -598.18 110.50 598.30 110.51 598.43 110.53 c -598.43 111.60 l -598.23 111.54 598.06 111.50 597.90 111.50 c -597.17 111.50 596.56 111.94 596.07 112.80 c -596.07 117.00 l -h -603.91 117.00 m -603.91 115.80 l -603.44 116.70 602.73 117.15 601.79 117.15 c -601.02 117.15 600.42 116.87 599.98 116.31 c -599.54 115.75 599.32 114.99 599.32 114.02 c -599.32 112.96 599.57 112.11 600.07 111.46 c -600.57 110.82 601.22 110.50 602.04 110.50 c -602.79 110.50 603.41 110.79 603.91 111.36 c -603.91 107.75 l -605.07 107.75 l -605.07 117.00 l -h -603.91 112.15 m -603.31 111.63 602.75 111.36 602.21 111.36 c -601.11 111.36 600.55 112.21 600.55 113.90 c -600.55 115.39 601.04 116.13 602.03 116.13 c -602.67 116.13 603.30 115.78 603.91 115.08 c -h -611.18 117.00 m -611.18 107.75 l -612.33 107.75 l -612.33 111.83 l -612.94 110.94 613.69 110.50 614.57 110.50 c -615.12 110.50 615.56 110.67 615.89 111.02 c -616.21 111.37 616.38 111.84 616.38 112.43 c -616.38 117.00 l -615.22 117.00 l -615.22 112.80 l -615.22 112.33 615.16 112.00 615.02 111.79 c -614.88 111.59 614.65 111.49 614.33 111.49 c -613.62 111.49 612.96 111.96 612.33 112.88 c -612.33 117.00 l -h -620.53 117.15 m -619.95 117.15 619.49 116.98 619.16 116.64 c -618.83 116.31 618.67 115.84 618.67 115.24 c -618.67 111.50 l -617.87 111.50 l -617.87 110.64 l -618.67 110.64 l -618.67 109.48 l -619.82 109.37 l -619.82 110.64 l -621.49 110.64 l -621.49 111.50 l -619.82 111.50 l -619.82 115.03 l -619.82 115.86 620.18 116.28 620.90 116.28 c -621.05 116.28 621.24 116.25 621.46 116.20 c -621.46 117.00 l -621.10 117.10 620.79 117.15 620.53 117.15 c -h -625.02 117.15 m -624.44 117.15 623.98 116.98 623.65 116.64 c -623.32 116.31 623.16 115.84 623.16 115.24 c -623.16 111.50 l -622.36 111.50 l -622.36 110.64 l -623.16 110.64 l -623.16 109.48 l -624.31 109.37 l -624.31 110.64 l -625.98 110.64 l -625.98 111.50 l -624.31 111.50 l -624.31 115.03 l -624.31 115.86 624.67 116.28 625.39 116.28 c -625.54 116.28 625.73 116.25 625.95 116.20 c -625.95 117.00 l -625.59 117.10 625.28 117.15 625.02 117.15 c -h -627.60 119.31 m -627.60 110.64 l -628.75 110.64 l -628.75 111.83 l -629.23 110.94 629.94 110.50 630.88 110.50 c -631.65 110.50 632.25 110.78 632.69 111.33 c -633.13 111.89 633.35 112.66 633.35 113.62 c -633.35 114.68 633.10 115.53 632.60 116.18 c -632.10 116.82 631.45 117.15 630.63 117.15 c -629.88 117.15 629.25 116.86 628.75 116.28 c -628.75 119.31 l -h -628.75 115.48 m -629.35 116.01 629.91 116.28 630.45 116.28 c -631.56 116.28 632.12 115.43 632.12 113.74 c -632.12 112.25 631.62 111.50 630.64 111.50 c -630.00 111.50 629.37 111.85 628.75 112.55 c -h -638.95 117.00 m -638.95 110.64 l -640.10 110.64 l -640.10 111.83 l -640.56 110.94 641.22 110.50 642.10 110.50 c -642.21 110.50 642.34 110.51 642.46 110.53 c -642.46 111.60 l -642.27 111.54 642.09 111.50 641.94 111.50 c -641.21 111.50 640.60 111.94 640.10 112.80 c -640.10 117.00 l -h -648.52 116.79 m -647.75 117.03 647.09 117.15 646.54 117.15 c -645.60 117.15 644.83 116.83 644.24 116.21 c -643.65 115.59 643.36 114.78 643.36 113.79 c -643.36 112.82 643.62 112.03 644.14 111.42 c -644.66 110.80 645.33 110.49 646.14 110.49 c -646.91 110.49 647.50 110.76 647.92 111.31 c -648.34 111.86 648.55 112.63 648.55 113.64 c -648.55 114.00 l -644.53 114.00 l -644.70 115.51 645.44 116.27 646.75 116.27 c -647.23 116.27 647.82 116.14 648.52 115.88 c -h -644.59 113.13 m -647.39 113.13 l -647.39 111.95 646.95 111.36 646.07 111.36 c -645.18 111.36 644.69 111.95 644.59 113.13 c -h -652.23 117.15 m -651.71 117.15 651.06 117.02 650.31 116.78 c -650.31 115.72 l -651.06 116.09 651.72 116.28 652.28 116.28 c -652.61 116.28 652.89 116.19 653.11 116.01 c -653.32 115.83 653.43 115.61 653.43 115.34 c -653.43 114.94 653.13 114.62 652.51 114.36 c -651.84 114.07 l -650.84 113.66 650.35 113.06 650.35 112.28 c -650.35 111.73 650.54 111.29 650.93 110.97 c -651.33 110.66 651.87 110.50 652.55 110.50 c -652.90 110.50 653.34 110.54 653.87 110.64 c -654.11 110.69 l -654.11 111.65 l -653.46 111.46 652.95 111.36 652.57 111.36 c -651.83 111.36 651.46 111.63 651.46 112.17 c -651.46 112.52 651.74 112.81 652.30 113.05 c -652.86 113.29 l -653.49 113.55 653.93 113.83 654.20 114.13 c -654.46 114.42 654.59 114.79 654.59 115.23 c -654.59 115.79 654.37 116.25 653.93 116.61 c -653.48 116.97 652.92 117.15 652.23 117.15 c -h -656.66 119.31 m -656.66 110.64 l -657.82 110.64 l -657.82 111.83 l -658.29 110.94 659.00 110.50 659.94 110.50 c -660.71 110.50 661.31 110.78 661.75 111.33 c -662.19 111.89 662.41 112.66 662.41 113.62 c -662.41 114.68 662.16 115.53 661.66 116.18 c -661.17 116.82 660.51 117.15 659.70 117.15 c -658.94 117.15 658.32 116.86 657.82 116.28 c -657.82 119.31 l -h -657.82 115.48 m -658.41 116.01 658.98 116.28 659.52 116.28 c -660.62 116.28 661.18 115.43 661.18 113.74 c -661.18 112.25 660.69 111.50 659.70 111.50 c -659.06 111.50 658.43 111.85 657.82 112.55 c -h -666.71 117.15 m -665.79 117.15 665.07 116.84 664.53 116.24 c -663.98 115.64 663.71 114.83 663.71 113.82 c -663.71 112.79 663.98 111.99 664.53 111.39 c -665.07 110.79 665.81 110.50 666.75 110.50 c -667.68 110.50 668.42 110.79 668.96 111.39 c -669.51 111.99 669.78 112.79 669.78 113.81 c -669.78 114.85 669.51 115.66 668.96 116.26 c -668.41 116.85 667.66 117.15 666.71 117.15 c -h -666.72 116.28 m -667.95 116.28 668.56 115.46 668.56 113.81 c -668.56 112.18 667.95 111.36 666.75 111.36 c -665.54 111.36 664.94 112.18 664.94 113.82 c -664.94 115.46 665.54 116.28 666.72 116.28 c -h -671.59 117.00 m -671.59 110.64 l -672.74 110.64 l -672.74 111.83 l -673.35 110.94 674.10 110.50 674.98 110.50 c -675.53 110.50 675.97 110.67 676.30 111.02 c -676.62 111.37 676.79 111.84 676.79 112.43 c -676.79 117.00 l -675.63 117.00 l -675.63 112.80 l -675.63 112.33 675.57 112.00 675.43 111.79 c -675.29 111.59 675.06 111.49 674.74 111.49 c -674.03 111.49 673.37 111.96 672.74 112.88 c -672.74 117.00 l -h -680.72 117.15 m -680.19 117.15 679.55 117.02 678.80 116.78 c -678.80 115.72 l -679.55 116.09 680.21 116.28 680.77 116.28 c -681.10 116.28 681.38 116.19 681.59 116.01 c -681.81 115.83 681.92 115.61 681.92 115.34 c -681.92 114.94 681.62 114.62 681.00 114.36 c -680.33 114.07 l -679.33 113.66 678.83 113.06 678.83 112.28 c -678.83 111.73 679.03 111.29 679.42 110.97 c -679.82 110.66 680.35 110.50 681.04 110.50 c -681.39 110.50 681.83 110.54 682.36 110.64 c -682.60 110.69 l -682.60 111.65 l -681.95 111.46 681.44 111.36 681.06 111.36 c -680.32 111.36 679.95 111.63 679.95 112.17 c -679.95 112.52 680.23 112.81 680.79 113.05 c -681.35 113.29 l -681.98 113.55 682.42 113.83 682.68 114.13 c -682.95 114.42 683.08 114.79 683.08 115.23 c -683.08 115.79 682.86 116.25 682.41 116.61 c -681.97 116.97 681.41 117.15 680.72 117.15 c -h -689.81 116.79 m -689.04 117.03 688.38 117.15 687.83 117.15 c -686.89 117.15 686.13 116.83 685.53 116.21 c -684.94 115.59 684.65 114.78 684.65 113.79 c -684.65 112.82 684.91 112.03 685.43 111.42 c -685.95 110.80 686.62 110.49 687.43 110.49 c -688.20 110.49 688.79 110.76 689.21 111.31 c -689.63 111.86 689.84 112.63 689.84 113.64 c -689.84 114.00 l -685.82 114.00 l -685.99 115.51 686.73 116.27 688.04 116.27 c -688.53 116.27 689.12 116.14 689.81 115.88 c -h -685.88 113.13 m -688.68 113.13 l -688.68 111.95 688.24 111.36 687.36 111.36 c -686.47 111.36 685.98 111.95 685.88 113.13 c -h -695.63 117.00 m -695.63 110.64 l -696.79 110.64 l -696.79 111.83 l -697.24 110.94 697.91 110.50 698.78 110.50 c -698.90 110.50 699.02 110.51 699.15 110.53 c -699.15 111.60 l -698.95 111.54 698.77 111.50 698.62 111.50 c -697.89 111.50 697.28 111.94 696.79 112.80 c -696.79 117.00 l -h -705.21 116.79 m -704.43 117.03 703.77 117.15 703.22 117.15 c -702.28 117.15 701.52 116.83 700.93 116.21 c -700.33 115.59 700.04 114.78 700.04 113.79 c -700.04 112.82 700.30 112.03 700.82 111.42 c -701.34 110.80 702.01 110.49 702.82 110.49 c -703.59 110.49 704.19 110.76 704.61 111.31 c -705.03 111.86 705.24 112.63 705.24 113.64 c -705.23 114.00 l -701.22 114.00 l -701.38 115.51 702.12 116.27 703.44 116.27 c -703.92 116.27 704.51 116.14 705.21 115.88 c -h -701.27 113.13 m -704.08 113.13 l -704.08 111.95 703.63 111.36 702.75 111.36 c -701.87 111.36 701.37 111.95 701.27 113.13 c -h -711.31 119.31 m -711.31 115.80 l -710.84 116.70 710.14 117.15 709.19 117.15 c -708.43 117.15 707.82 116.87 707.38 116.31 c -706.94 115.75 706.72 114.99 706.72 114.02 c -706.72 112.96 706.97 112.11 707.47 111.46 c -707.97 110.82 708.62 110.50 709.44 110.50 c -710.19 110.50 710.82 110.79 711.31 111.36 c -711.31 110.64 l -712.47 110.64 l -712.47 119.31 l -h -711.31 112.15 m -710.71 111.63 710.15 111.36 709.61 111.36 c -708.51 111.36 707.96 112.21 707.96 113.90 c -707.96 115.39 708.45 116.13 709.43 116.13 c -710.07 116.13 710.70 115.78 711.31 115.08 c -h -718.76 117.00 m -718.76 115.80 l -718.15 116.70 717.40 117.15 716.53 117.15 c -715.97 117.15 715.53 116.97 715.20 116.62 c -714.88 116.27 714.71 115.80 714.71 115.21 c -714.71 110.64 l -715.87 110.64 l -715.87 114.83 l -715.87 115.31 715.93 115.65 716.07 115.85 c -716.21 116.05 716.44 116.15 716.77 116.15 c -717.47 116.15 718.13 115.69 718.76 114.76 c -718.76 110.64 l -719.91 110.64 l -719.91 117.00 l -h -726.89 116.79 m -726.12 117.03 725.46 117.15 724.91 117.15 c -723.97 117.15 723.20 116.83 722.61 116.21 c -722.02 115.59 721.72 114.78 721.72 113.79 c -721.72 112.82 721.99 112.03 722.51 111.42 c -723.03 110.80 723.70 110.49 724.51 110.49 c -725.28 110.49 725.87 110.76 726.29 111.31 c -726.71 111.86 726.92 112.63 726.92 113.64 c -726.92 114.00 l -722.90 114.00 l -723.07 115.51 723.81 116.27 725.12 116.27 c -725.60 116.27 726.19 116.14 726.89 115.88 c -h -722.96 113.13 m -725.76 113.13 l -725.76 111.95 725.32 111.36 724.44 111.36 c -723.55 111.36 723.06 111.95 722.96 113.13 c -h -730.60 117.15 m -730.07 117.15 729.43 117.02 728.68 116.78 c -728.68 115.72 l -729.43 116.09 730.09 116.28 730.65 116.28 c -730.98 116.28 731.26 116.19 731.47 116.01 c -731.69 115.83 731.80 115.61 731.80 115.34 c -731.80 114.94 731.50 114.62 730.88 114.36 c -730.21 114.07 l -729.21 113.66 728.71 113.06 728.71 112.28 c -728.71 111.73 728.91 111.29 729.30 110.97 c -729.70 110.66 730.23 110.50 730.92 110.50 c -731.27 110.50 731.71 110.54 732.24 110.64 c -732.48 110.69 l -732.48 111.65 l -731.83 111.46 731.32 111.36 730.94 111.36 c -730.20 111.36 729.83 111.63 729.83 112.17 c -729.83 112.52 730.11 112.81 730.67 113.05 c -731.23 113.29 l -731.86 113.55 732.30 113.83 732.56 114.13 c -732.83 114.42 732.96 114.79 732.96 115.23 c -732.96 115.79 732.74 116.25 732.29 116.61 c -731.85 116.97 731.29 117.15 730.60 117.15 c -h -736.94 117.15 m -736.36 117.15 735.90 116.98 735.57 116.64 c -735.24 116.31 735.08 115.84 735.08 115.24 c -735.08 111.50 l -734.28 111.50 l -734.28 110.64 l -735.08 110.64 l -735.08 109.48 l -736.23 109.37 l -736.23 110.64 l -737.90 110.64 l -737.90 111.50 l -736.23 111.50 l -736.23 115.03 l -736.23 115.86 736.59 116.28 737.31 116.28 c -737.46 116.28 737.65 116.25 737.87 116.20 c -737.87 117.00 l -737.51 117.10 737.20 117.15 736.94 117.15 c -h -739.52 117.00 m -739.52 110.64 l -740.67 110.64 l -740.67 117.00 l -h -739.52 109.48 m -739.52 108.33 l -740.67 108.33 l -740.67 109.48 l -h -742.99 117.00 m -742.99 110.64 l -744.14 110.64 l -744.14 111.83 l -744.75 110.94 745.50 110.50 746.38 110.50 c -746.93 110.50 747.37 110.67 747.70 111.02 c -748.03 111.37 748.19 111.84 748.19 112.43 c -748.19 117.00 l -747.04 117.00 l -747.04 112.80 l -747.04 112.33 746.97 112.00 746.83 111.79 c -746.69 111.59 746.46 111.49 746.14 111.49 c -745.43 111.49 744.77 111.96 744.14 112.88 c -744.14 117.00 l -h -750.46 119.12 m -750.59 118.11 l -751.26 118.43 751.92 118.59 752.57 118.59 c -753.87 118.59 754.52 117.90 754.52 116.52 c -754.52 115.52 l -754.09 116.41 753.39 116.85 752.42 116.85 c -751.66 116.85 751.05 116.58 750.61 116.02 c -750.16 115.47 749.93 114.72 749.93 113.78 c -749.93 112.81 750.19 112.02 750.70 111.41 c -751.21 110.80 751.87 110.50 752.69 110.50 c -753.40 110.50 754.01 110.79 754.52 111.36 c -754.52 110.64 l -755.68 110.64 l -755.68 115.27 l -755.68 116.26 755.63 117.00 755.52 117.48 c -755.42 117.96 755.23 118.35 754.94 118.65 c -754.44 119.19 753.65 119.46 752.59 119.46 c -751.85 119.46 751.14 119.34 750.46 119.12 c -h -754.52 114.80 m -754.52 112.15 l -754.01 111.63 753.46 111.36 752.86 111.36 c -752.33 111.36 751.91 111.58 751.61 112.00 c -751.31 112.43 751.16 113.01 751.16 113.75 c -751.16 115.15 751.65 115.85 752.63 115.85 c -753.30 115.85 753.93 115.50 754.52 114.80 c -h -765.00 116.19 m -764.30 116.83 763.64 117.15 763.00 117.15 c -762.47 117.15 762.03 116.98 761.69 116.65 c -761.34 116.32 761.16 115.90 761.16 115.40 c -761.16 114.71 761.46 114.17 762.04 113.80 c -762.62 113.42 763.46 113.24 764.55 113.24 c -764.83 113.24 l -764.83 112.47 l -764.83 111.73 764.45 111.36 763.69 111.36 c -763.08 111.36 762.42 111.55 761.71 111.93 c -761.71 110.97 l -762.49 110.65 763.22 110.50 763.90 110.50 c -764.61 110.50 765.14 110.66 765.47 110.98 c -765.81 111.30 765.98 111.79 765.98 112.47 c -765.98 115.35 l -765.98 116.01 766.18 116.34 766.59 116.34 c -766.64 116.34 766.71 116.34 766.81 116.32 c -766.89 116.96 l -766.63 117.08 766.34 117.15 766.03 117.15 c -765.49 117.15 765.14 116.83 765.00 116.19 c -h -764.83 115.56 m -764.83 113.92 l -764.44 113.91 l -763.81 113.91 763.29 114.03 762.90 114.27 c -762.51 114.51 762.32 114.82 762.32 115.21 c -762.32 115.49 762.42 115.72 762.61 115.92 c -762.81 116.11 763.04 116.20 763.33 116.20 c -763.81 116.20 764.31 115.99 764.83 115.56 c -h -772.32 117.00 m -772.32 115.80 l -771.71 116.70 770.96 117.15 770.09 117.15 c -769.53 117.15 769.09 116.97 768.76 116.62 c -768.44 116.27 768.27 115.80 768.27 115.21 c -768.27 110.64 l -769.43 110.64 l -769.43 114.83 l -769.43 115.31 769.50 115.65 769.63 115.85 c -769.77 116.05 770.00 116.15 770.33 116.15 c -771.03 116.15 771.70 115.69 772.32 114.76 c -772.32 110.64 l -773.47 110.64 l -773.47 117.00 l -h -777.70 117.15 m -777.11 117.15 776.66 116.98 776.33 116.64 c -776.00 116.31 775.84 115.84 775.84 115.24 c -775.84 111.50 l -775.04 111.50 l -775.04 110.64 l -775.84 110.64 l -775.84 109.48 l -776.99 109.37 l -776.99 110.64 l -778.65 110.64 l -778.65 111.50 l -776.99 111.50 l -776.99 115.03 l -776.99 115.86 777.35 116.28 778.07 116.28 c -778.22 116.28 778.41 116.25 778.62 116.20 c -778.62 117.00 l -778.27 117.10 777.96 117.15 777.70 117.15 c -h -780.28 117.00 m -780.28 107.75 l -781.43 107.75 l -781.43 111.83 l -782.04 110.94 782.79 110.50 783.67 110.50 c -784.22 110.50 784.66 110.67 784.99 111.02 c -785.32 111.37 785.48 111.84 785.48 112.43 c -785.48 117.00 l -784.33 117.00 l -784.33 112.80 l -784.33 112.33 784.26 112.00 784.12 111.79 c -783.98 111.59 783.75 111.49 783.43 111.49 c -782.72 111.49 782.06 111.96 781.43 112.88 c -781.43 117.00 l -h -792.39 116.79 m -791.62 117.03 790.95 117.15 790.40 117.15 c -789.46 117.15 788.70 116.83 788.11 116.21 c -787.52 115.59 787.22 114.78 787.22 113.79 c -787.22 112.82 787.48 112.03 788.00 111.42 c -788.52 110.80 789.19 110.49 790.00 110.49 c -790.77 110.49 791.37 110.76 791.79 111.31 c -792.21 111.86 792.42 112.63 792.42 113.64 c -792.41 114.00 l -788.40 114.00 l -788.57 115.51 789.31 116.27 790.62 116.27 c -791.10 116.27 791.69 116.14 792.39 115.88 c -h -788.45 113.13 m -791.26 113.13 l -791.26 111.95 790.82 111.36 789.93 111.36 c -789.05 111.36 788.55 111.95 788.45 113.13 c -h -794.41 117.00 m -794.41 110.64 l -795.56 110.64 l -795.56 111.83 l -796.17 110.94 796.92 110.50 797.80 110.50 c -798.35 110.50 798.79 110.67 799.12 111.02 c -799.45 111.37 799.61 111.84 799.61 112.43 c -799.61 117.00 l -798.46 117.00 l -798.46 112.80 l -798.46 112.33 798.39 112.00 798.25 111.79 c -798.11 111.59 797.88 111.49 797.56 111.49 c -796.86 111.49 796.19 111.96 795.56 112.88 c -795.56 117.00 l -h -803.77 117.15 m -803.18 117.15 802.72 116.98 802.40 116.64 c -802.07 116.31 801.90 115.84 801.90 115.24 c -801.90 111.50 l -801.11 111.50 l -801.11 110.64 l -801.90 110.64 l -801.90 109.48 l -803.06 109.37 l -803.06 110.64 l -804.72 110.64 l -804.72 111.50 l -803.06 111.50 l -803.06 115.03 l -803.06 115.86 803.42 116.28 804.14 116.28 c -804.29 116.28 804.47 116.25 804.69 116.20 c -804.69 117.00 l -804.34 117.10 804.03 117.15 803.77 117.15 c -h -806.35 117.00 m -806.35 110.64 l -807.50 110.64 l -807.50 117.00 l -h -806.35 109.48 m -806.35 108.33 l -807.50 108.33 l -807.50 109.48 l -h -812.30 117.15 m -811.44 117.15 810.73 116.83 810.16 116.19 c -809.59 115.55 809.31 114.75 809.31 113.78 c -809.31 112.75 809.59 111.94 810.15 111.36 c -810.71 110.79 811.49 110.50 812.50 110.50 c -812.99 110.50 813.55 110.56 814.16 110.70 c -814.16 111.67 l -813.51 111.48 812.98 111.38 812.57 111.38 c -811.98 111.38 811.50 111.60 811.15 112.05 c -810.79 112.49 810.61 113.08 810.61 113.82 c -810.61 114.53 810.79 115.11 811.16 115.55 c -811.53 115.99 812.01 116.21 812.60 116.21 c -813.13 116.21 813.67 116.08 814.23 115.81 c -814.23 116.81 l -813.49 117.03 812.84 117.15 812.30 117.15 c -h -819.24 116.19 m -818.55 116.83 817.88 117.15 817.24 117.15 c -816.72 117.15 816.28 116.98 815.93 116.65 c -815.58 116.32 815.41 115.90 815.41 115.40 c -815.41 114.71 815.70 114.17 816.29 113.80 c -816.87 113.42 817.71 113.24 818.80 113.24 c -819.07 113.24 l -819.07 112.47 l -819.07 111.73 818.69 111.36 817.94 111.36 c -817.33 111.36 816.67 111.55 815.96 111.93 c -815.96 110.97 l -816.74 110.65 817.47 110.50 818.15 110.50 c -818.86 110.50 819.38 110.66 819.72 110.98 c -820.06 111.30 820.23 111.79 820.23 112.47 c -820.23 115.35 l -820.23 116.01 820.43 116.34 820.84 116.34 c -820.89 116.34 820.96 116.34 821.06 116.32 c -821.14 116.96 l -820.88 117.08 820.59 117.15 820.27 117.15 c -819.73 117.15 819.39 116.83 819.24 116.19 c -h -819.07 115.56 m -819.07 113.92 l -818.69 113.91 l -818.05 113.91 817.54 114.03 817.15 114.27 c -816.76 114.51 816.56 114.82 816.56 115.21 c -816.56 115.49 816.66 115.72 816.86 115.92 c -817.05 116.11 817.29 116.20 817.57 116.20 c -818.05 116.20 818.55 115.99 819.07 115.56 c -h -824.50 117.15 m -823.91 117.15 823.46 116.98 823.13 116.64 c -822.80 116.31 822.63 115.84 822.63 115.24 c -822.63 111.50 l -821.84 111.50 l -821.84 110.64 l -822.63 110.64 l -822.63 109.48 l -823.79 109.37 l -823.79 110.64 l -825.45 110.64 l -825.45 111.50 l -823.79 111.50 l -823.79 115.03 l -823.79 115.86 824.15 116.28 824.87 116.28 c -825.02 116.28 825.21 116.25 825.42 116.20 c -825.42 117.00 l -825.07 117.10 824.76 117.15 824.50 117.15 c -h -827.08 117.00 m -827.08 110.64 l -828.23 110.64 l -828.23 117.00 l -h -827.08 109.48 m -827.08 108.33 l -828.23 108.33 l -828.23 109.48 l -h -833.04 117.15 m -832.12 117.15 831.40 116.84 830.86 116.24 c -830.31 115.64 830.04 114.83 830.04 113.82 c -830.04 112.79 830.31 111.99 830.86 111.39 c -831.40 110.79 832.14 110.50 833.08 110.50 c -834.01 110.50 834.75 110.79 835.29 111.39 c -835.84 111.99 836.11 112.79 836.11 113.81 c -836.11 114.85 835.84 115.66 835.29 116.26 c -834.74 116.85 833.99 117.15 833.04 117.15 c -h -833.05 116.28 m -834.28 116.28 834.89 115.46 834.89 113.81 c -834.89 112.18 834.28 111.36 833.08 111.36 c -831.87 111.36 831.27 112.18 831.27 113.82 c -831.27 115.46 831.87 116.28 833.05 116.28 c -h -837.92 117.00 m -837.92 110.64 l -839.07 110.64 l -839.07 111.83 l -839.68 110.94 840.43 110.50 841.31 110.50 c -841.86 110.50 842.30 110.67 842.63 111.02 c -842.96 111.37 843.12 111.84 843.12 112.43 c -843.12 117.00 l -841.96 117.00 l -841.96 112.80 l -841.96 112.33 841.90 112.00 841.76 111.79 c -841.62 111.59 841.39 111.49 841.07 111.49 c -840.36 111.49 839.70 111.96 839.07 112.88 c -839.07 117.00 l -h -846.96 118.73 m -846.96 107.75 l -844.64 107.75 l -844.64 108.62 l -845.94 108.62 l -845.94 117.87 l -844.64 117.87 l -844.64 118.73 l -h -f -[ 5.0000 5.0000] 0.0000 d -850.50 120.50 m -70.500 120.50 l -S -[] 0.0000 d -70.000 120.00 m -76.000 114.00 l -76.000 126.00 l -h -f* -802.84 469.15 m -802.31 469.15 801.67 469.02 800.92 468.78 c -800.92 467.72 l -801.67 468.09 802.33 468.28 802.89 468.28 c -803.22 468.28 803.50 468.19 803.71 468.01 c -803.93 467.83 804.04 467.61 804.04 467.34 c -804.04 466.94 803.74 466.62 803.12 466.36 c -802.45 466.07 l -801.45 465.66 800.96 465.06 800.96 464.28 c -800.96 463.73 801.15 463.29 801.54 462.97 c -801.94 462.66 802.47 462.50 803.16 462.50 c -803.51 462.50 803.95 462.54 804.48 462.64 c -804.72 462.69 l -804.72 463.65 l -804.07 463.46 803.56 463.36 803.18 463.36 c -802.44 463.36 802.07 463.63 802.07 464.17 c -802.07 464.52 802.35 464.81 802.91 465.05 c -803.47 465.29 l -804.10 465.55 804.54 465.83 804.80 466.13 c -805.07 466.42 805.20 466.79 805.20 467.23 c -805.20 467.79 804.98 468.25 804.54 468.61 c -804.09 468.97 803.53 469.15 802.84 469.15 c -h -811.25 469.00 m -811.25 467.80 l -810.64 468.70 809.89 469.15 809.02 469.15 c -808.46 469.15 808.02 468.97 807.69 468.62 c -807.37 468.27 807.20 467.80 807.20 467.21 c -807.20 462.64 l -808.36 462.64 l -808.36 466.83 l -808.36 467.31 808.42 467.65 808.56 467.85 c -808.70 468.05 808.93 468.15 809.26 468.15 c -809.96 468.15 810.62 467.69 811.25 466.76 c -811.25 462.64 l -812.40 462.64 l -812.40 469.00 l -h -817.20 469.15 m -816.34 469.15 815.63 468.83 815.06 468.19 c -814.50 467.55 814.21 466.75 814.21 465.78 c -814.21 464.75 814.50 463.94 815.06 463.36 c -815.62 462.79 816.40 462.50 817.40 462.50 c -817.90 462.50 818.45 462.56 819.07 462.70 c -819.07 463.67 l -818.41 463.48 817.88 463.38 817.47 463.38 c -816.88 463.38 816.41 463.60 816.05 464.05 c -815.69 464.49 815.52 465.08 815.52 465.82 c -815.52 466.53 815.70 467.11 816.07 467.55 c -816.43 467.99 816.91 468.21 817.51 468.21 c -818.04 468.21 818.58 468.08 819.14 467.81 c -819.14 468.81 l -818.39 469.03 817.75 469.15 817.20 469.15 c -h -823.35 469.15 m -822.49 469.15 821.78 468.83 821.21 468.19 c -820.64 467.55 820.36 466.75 820.36 465.78 c -820.36 464.75 820.64 463.94 821.20 463.36 c -821.76 462.79 822.54 462.50 823.55 462.50 c -824.04 462.50 824.60 462.56 825.21 462.70 c -825.21 463.67 l -824.56 463.48 824.03 463.38 823.62 463.38 c -823.03 463.38 822.56 463.60 822.20 464.05 c -821.84 464.49 821.66 465.08 821.66 465.82 c -821.66 466.53 821.85 467.11 822.21 467.55 c -822.58 467.99 823.06 468.21 823.65 468.21 c -824.18 468.21 824.72 468.08 825.28 467.81 c -825.28 468.81 l -824.54 469.03 823.89 469.15 823.35 469.15 c -h -831.68 468.79 m -830.90 469.03 830.24 469.15 829.69 469.15 c -828.75 469.15 827.99 468.83 827.40 468.21 c -826.80 467.59 826.51 466.78 826.51 465.79 c -826.51 464.82 826.77 464.03 827.29 463.42 c -827.81 462.80 828.48 462.49 829.29 462.49 c -830.06 462.49 830.66 462.76 831.08 463.31 c -831.50 463.86 831.71 464.63 831.71 465.64 c -831.70 466.00 l -827.69 466.00 l -827.85 467.51 828.59 468.27 829.91 468.27 c -830.39 468.27 830.98 468.14 831.68 467.88 c -h -827.74 465.13 m -830.54 465.13 l -830.54 463.95 830.10 463.36 829.22 463.36 c -828.33 463.36 827.84 463.95 827.74 465.13 c -h -835.38 469.15 m -834.86 469.15 834.22 469.02 833.46 468.78 c -833.46 467.72 l -834.22 468.09 834.87 468.28 835.43 468.28 c -835.76 468.28 836.04 468.19 836.26 468.01 c -836.48 467.83 836.59 467.61 836.59 467.34 c -836.59 466.94 836.28 466.62 835.67 466.36 c -834.99 466.07 l -834.00 465.66 833.50 465.06 833.50 464.28 c -833.50 463.73 833.69 463.29 834.09 462.97 c -834.48 462.66 835.02 462.50 835.70 462.50 c -836.06 462.50 836.50 462.54 837.02 462.64 c -837.26 462.69 l -837.26 463.65 l -836.62 463.46 836.10 463.36 835.72 463.36 c -834.98 463.36 834.61 463.63 834.61 464.17 c -834.61 464.52 834.89 464.81 835.46 465.05 c -836.01 465.29 l -836.64 465.55 837.09 465.83 837.35 466.13 c -837.61 466.42 837.74 466.79 837.74 467.23 c -837.74 467.79 837.52 468.25 837.08 468.61 c -836.64 468.97 836.07 469.15 835.38 469.15 c -h -841.50 469.15 m -840.97 469.15 840.33 469.02 839.58 468.78 c -839.58 467.72 l -840.33 468.09 840.99 468.28 841.55 468.28 c -841.88 468.28 842.16 468.19 842.38 468.01 c -842.59 467.83 842.70 467.61 842.70 467.34 c -842.70 466.94 842.40 466.62 841.78 466.36 c -841.11 466.07 l -840.11 465.66 839.62 465.06 839.62 464.28 c -839.62 463.73 839.81 463.29 840.20 462.97 c -840.60 462.66 841.13 462.50 841.82 462.50 c -842.17 462.50 842.61 462.54 843.14 462.64 c -843.38 462.69 l -843.38 463.65 l -842.73 463.46 842.22 463.36 841.84 463.36 c -841.10 463.36 840.73 463.63 840.73 464.17 c -840.73 464.52 841.01 464.81 841.57 465.05 c -842.13 465.29 l -842.76 465.55 843.20 465.83 843.46 466.13 c -843.73 466.42 843.86 466.79 843.86 467.23 c -843.86 467.79 843.64 468.25 843.20 468.61 c -842.75 468.97 842.19 469.15 841.50 469.15 c -h -f -[ 5.0000 5.0000] 0.0000 d -850.50 472.50 m -70.500 472.50 l -S -[] 0.0000 d -70.000 472.00 m -76.000 466.00 l -76.000 478.00 l -h -f* -869.82 450.79 m -869.04 451.03 868.38 451.15 867.83 451.15 c -866.89 451.15 866.13 450.83 865.54 450.21 c -864.95 449.59 864.65 448.78 864.65 447.79 c -864.65 446.82 864.91 446.03 865.43 445.42 c -865.95 444.80 866.62 444.49 867.43 444.49 c -868.20 444.49 868.80 444.76 869.22 445.31 c -869.64 445.86 869.85 446.63 869.85 447.64 c -869.84 448.00 l -865.83 448.00 l -866.00 449.51 866.74 450.27 868.05 450.27 c -868.53 450.27 869.12 450.14 869.82 449.88 c -h -865.88 447.13 m -868.69 447.13 l -868.69 445.95 868.25 445.36 867.36 445.36 c -866.48 445.36 865.98 445.95 865.88 447.13 c -h -871.20 451.00 m -873.62 447.71 l -871.27 444.64 l -872.64 444.64 l -874.50 447.09 l -876.18 444.64 l -877.31 444.64 l -875.10 447.87 l -877.50 451.00 l -876.13 451.00 l -874.21 448.48 l -872.36 451.00 l -h -883.86 450.79 m -883.09 451.03 882.43 451.15 881.88 451.15 c -880.94 451.15 880.17 450.83 879.58 450.21 c -878.99 449.59 878.70 448.78 878.70 447.79 c -878.70 446.82 878.96 446.03 879.48 445.42 c -880.00 444.80 880.67 444.49 881.48 444.49 c -882.25 444.49 882.84 444.76 883.26 445.31 c -883.68 445.86 883.89 446.63 883.89 447.64 c -883.89 448.00 l -879.87 448.00 l -880.04 449.51 880.78 450.27 882.09 450.27 c -882.57 450.27 883.16 450.14 883.86 449.88 c -h -879.93 447.13 m -882.73 447.13 l -882.73 445.95 882.29 445.36 881.41 445.36 c -880.52 445.36 880.03 445.95 879.93 447.13 c -h -888.37 451.15 m -887.51 451.15 886.80 450.83 886.23 450.19 c -885.66 449.55 885.38 448.75 885.38 447.78 c -885.38 446.75 885.66 445.94 886.22 445.36 c -886.78 444.79 887.56 444.50 888.57 444.50 c -889.06 444.50 889.62 444.56 890.23 444.70 c -890.23 445.67 l -889.58 445.48 889.05 445.38 888.64 445.38 c -888.05 445.38 887.58 445.60 887.22 446.05 c -886.86 446.49 886.68 447.08 886.68 447.82 c -886.68 448.53 886.87 449.11 887.23 449.55 c -887.60 449.99 888.08 450.21 888.67 450.21 c -889.20 450.21 889.74 450.08 890.30 449.81 c -890.30 450.81 l -889.56 451.03 888.91 451.15 888.37 451.15 c -h -896.01 451.00 m -896.01 449.80 l -895.40 450.70 894.65 451.15 893.78 451.15 c -893.22 451.15 892.78 450.97 892.45 450.62 c -892.12 450.27 891.96 449.80 891.96 449.21 c -891.96 444.64 l -893.12 444.64 l -893.12 448.83 l -893.12 449.31 893.18 449.65 893.32 449.85 c -893.46 450.05 893.69 450.15 894.02 450.15 c -894.72 450.15 895.38 449.69 896.01 448.76 c -896.01 444.64 l -897.16 444.64 l -897.16 451.00 l -h -901.39 451.15 m -900.80 451.15 900.35 450.98 900.02 450.64 c -899.69 450.31 899.53 449.84 899.53 449.24 c -899.53 445.50 l -898.73 445.50 l -898.73 444.64 l -899.53 444.64 l -899.53 443.48 l -900.68 443.37 l -900.68 444.64 l -902.34 444.64 l -902.34 445.50 l -900.68 445.50 l -900.68 449.03 l -900.68 449.86 901.04 450.28 901.76 450.28 c -901.91 450.28 902.10 450.25 902.31 450.20 c -902.31 451.00 l -901.96 451.10 901.65 451.15 901.39 451.15 c -h -908.63 450.79 m -907.86 451.03 907.20 451.15 906.64 451.15 c -905.71 451.15 904.94 450.83 904.35 450.21 c -903.76 449.59 903.46 448.78 903.46 447.79 c -903.46 446.82 903.72 446.03 904.25 445.42 c -904.77 444.80 905.43 444.49 906.25 444.49 c -907.02 444.49 907.61 444.76 908.03 445.31 c -908.45 445.86 908.66 446.63 908.66 447.64 c -908.65 448.00 l -904.64 448.00 l -904.81 449.51 905.55 450.27 906.86 450.27 c -907.34 450.27 907.93 450.14 908.63 449.88 c -h -904.69 447.13 m -907.50 447.13 l -907.50 445.95 907.06 445.36 906.18 445.36 c -905.29 445.36 904.79 445.95 904.69 447.13 c -h -916.93 451.15 m -916.07 451.15 915.36 450.83 914.79 450.19 c -914.23 449.55 913.95 448.75 913.95 447.78 c -913.95 446.75 914.23 445.94 914.79 445.36 c -915.35 444.79 916.13 444.50 917.13 444.50 c -917.63 444.50 918.18 444.56 918.80 444.70 c -918.80 445.67 l -918.14 445.48 917.61 445.38 917.20 445.38 c -916.61 445.38 916.14 445.60 915.78 446.05 c -915.42 446.49 915.25 447.08 915.25 447.82 c -915.25 448.53 915.43 449.11 915.80 449.55 c -916.16 449.99 916.64 450.21 917.24 450.21 c -917.77 450.21 918.31 450.08 918.87 449.81 c -918.87 450.81 l -918.12 451.03 917.48 451.15 916.93 451.15 c -h -920.60 451.00 m -920.60 444.64 l -921.75 444.64 l -921.75 445.83 l -922.21 444.94 922.87 444.50 923.74 444.50 c -923.86 444.50 923.98 444.51 924.11 444.53 c -924.11 445.60 l -923.91 445.54 923.74 445.50 923.58 445.50 c -922.85 445.50 922.24 445.94 921.75 446.80 c -921.75 451.00 l -h -930.17 450.79 m -929.40 451.03 928.73 451.15 928.18 451.15 c -927.25 451.15 926.48 450.83 925.89 450.21 c -925.30 449.59 925.00 448.78 925.00 447.79 c -925.00 446.82 925.26 446.03 925.78 445.42 c -926.31 444.80 926.97 444.49 927.79 444.49 c -928.55 444.49 929.15 444.76 929.57 445.31 c -929.99 445.86 930.20 446.63 930.20 447.64 c -930.19 448.00 l -926.18 448.00 l -926.35 449.51 927.09 450.27 928.40 450.27 c -928.88 450.27 929.47 450.14 930.17 449.88 c -h -926.23 447.13 m -929.04 447.13 l -929.04 445.95 928.60 445.36 927.71 445.36 c -926.83 445.36 926.33 445.95 926.23 447.13 c -h -935.47 450.19 m -934.78 450.83 934.12 451.15 933.47 451.15 c -932.95 451.15 932.51 450.98 932.16 450.65 c -931.81 450.32 931.64 449.90 931.64 449.40 c -931.64 448.71 931.93 448.17 932.52 447.80 c -933.10 447.42 933.94 447.24 935.03 447.24 c -935.30 447.24 l -935.30 446.47 l -935.30 445.73 934.92 445.36 934.17 445.36 c -933.56 445.36 932.90 445.55 932.19 445.93 c -932.19 444.97 l -932.97 444.65 933.70 444.50 934.38 444.50 c -935.09 444.50 935.61 444.66 935.95 444.98 c -936.29 445.30 936.46 445.79 936.46 446.47 c -936.46 449.35 l -936.46 450.01 936.66 450.34 937.07 450.34 c -937.12 450.34 937.19 450.34 937.29 450.32 c -937.37 450.96 l -937.11 451.08 936.82 451.15 936.50 451.15 c -935.96 451.15 935.62 450.83 935.47 450.19 c -h -935.30 449.56 m -935.30 447.92 l -934.92 447.91 l -934.28 447.91 933.77 448.03 933.38 448.27 c -932.99 448.51 932.79 448.82 932.79 449.21 c -932.79 449.49 932.89 449.72 933.09 449.92 c -933.28 450.11 933.52 450.20 933.80 450.20 c -934.28 450.20 934.78 449.99 935.30 449.56 c -h -940.73 451.15 m -940.14 451.15 939.69 450.98 939.36 450.64 c -939.03 450.31 938.87 449.84 938.87 449.24 c -938.87 445.50 l -938.07 445.50 l -938.07 444.64 l -938.87 444.64 l -938.87 443.48 l -940.02 443.37 l -940.02 444.64 l -941.68 444.64 l -941.68 445.50 l -940.02 445.50 l -940.02 449.03 l -940.02 449.86 940.38 450.28 941.10 450.28 c -941.25 450.28 941.44 450.25 941.65 450.20 c -941.65 451.00 l -941.30 451.10 940.99 451.15 940.73 451.15 c -h -947.97 450.79 m -947.20 451.03 946.54 451.15 945.98 451.15 c -945.05 451.15 944.28 450.83 943.69 450.21 c -943.10 449.59 942.80 448.78 942.80 447.79 c -942.80 446.82 943.06 446.03 943.58 445.42 c -944.11 444.80 944.77 444.49 945.59 444.49 c -946.36 444.49 946.95 444.76 947.37 445.31 c -947.79 445.86 948.00 446.63 948.00 447.64 c -947.99 448.00 l -943.98 448.00 l -944.15 449.51 944.89 450.27 946.20 450.27 c -946.68 450.27 947.27 450.14 947.97 449.88 c -h -944.03 447.13 m -946.84 447.13 l -946.84 445.95 946.40 445.36 945.52 445.36 c -944.63 445.36 944.13 445.95 944.03 447.13 c -h -948.84 453.02 m -948.84 452.15 l -954.84 452.15 l -954.84 453.02 l -h -955.99 451.00 m -955.99 444.64 l -957.15 444.64 l -957.15 451.00 l -h -955.99 443.48 m -955.99 442.33 l -957.15 442.33 l -957.15 443.48 l -h -959.46 451.00 m -959.46 444.64 l -960.62 444.64 l -960.62 445.83 l -961.22 444.94 961.97 444.50 962.85 444.50 c -963.40 444.50 963.84 444.67 964.17 445.02 c -964.50 445.37 964.66 445.84 964.66 446.43 c -964.66 451.00 l -963.51 451.00 l -963.51 446.80 l -963.51 446.33 963.44 446.00 963.30 445.79 c -963.16 445.59 962.93 445.49 962.61 445.49 c -961.91 445.49 961.24 445.96 960.62 446.88 c -960.62 451.00 l -h -968.60 451.15 m -968.07 451.15 967.43 451.02 966.67 450.78 c -966.67 449.72 l -967.43 450.09 968.08 450.28 968.64 450.28 c -968.97 450.28 969.25 450.19 969.47 450.01 c -969.69 449.83 969.80 449.61 969.80 449.34 c -969.80 448.94 969.49 448.62 968.88 448.36 c -968.20 448.07 l -967.21 447.66 966.71 447.06 966.71 446.28 c -966.71 445.73 966.91 445.29 967.30 444.97 c -967.69 444.66 968.23 444.50 968.91 444.50 c -969.27 444.50 969.71 444.54 970.23 444.64 c -970.47 444.69 l -970.47 445.65 l -969.83 445.46 969.31 445.36 968.94 445.36 c -968.19 445.36 967.82 445.63 967.82 446.17 c -967.82 446.52 968.10 446.81 968.67 447.05 c -969.22 447.29 l -969.85 447.55 970.30 447.83 970.56 448.13 c -970.82 448.42 970.95 448.79 970.95 449.23 c -970.95 449.79 970.73 450.25 970.29 450.61 c -969.85 450.97 969.28 451.15 968.60 451.15 c -h -974.94 451.15 m -974.35 451.15 973.89 450.98 973.56 450.64 c -973.24 450.31 973.07 449.84 973.07 449.24 c -973.07 445.50 l -972.28 445.50 l -972.28 444.64 l -973.07 444.64 l -973.07 443.48 l -974.23 443.37 l -974.23 444.64 l -975.89 444.64 l -975.89 445.50 l -974.23 445.50 l -974.23 449.03 l -974.23 449.86 974.59 450.28 975.30 450.28 c -975.46 450.28 975.64 450.25 975.86 450.20 c -975.86 451.00 l -975.51 451.10 975.20 451.15 974.94 451.15 c -h -980.79 450.19 m -980.10 450.83 979.44 451.15 978.80 451.15 c -978.27 451.15 977.83 450.98 977.48 450.65 c -977.14 450.32 976.96 449.90 976.96 449.40 c -976.96 448.71 977.25 448.17 977.84 447.80 c -978.42 447.42 979.26 447.24 980.35 447.24 c -980.62 447.24 l -980.62 446.47 l -980.62 445.73 980.25 445.36 979.49 445.36 c -978.88 445.36 978.22 445.55 977.51 445.93 c -977.51 444.97 l -978.29 444.65 979.02 444.50 979.70 444.50 c -980.41 444.50 980.93 444.66 981.27 444.98 c -981.61 445.30 981.78 445.79 981.78 446.47 c -981.78 449.35 l -981.78 450.01 981.98 450.34 982.39 450.34 c -982.44 450.34 982.51 450.34 982.61 450.32 c -982.69 450.96 l -982.43 451.08 982.14 451.15 981.83 451.15 c -981.29 451.15 980.94 450.83 980.79 450.19 c -h -980.62 449.56 m -980.62 447.92 l -980.24 447.91 l -979.61 447.91 979.09 448.03 978.70 448.27 c -978.31 448.51 978.12 448.82 978.12 449.21 c -978.12 449.49 978.21 449.72 978.41 449.92 c -978.61 450.11 978.84 450.20 979.12 450.20 c -979.61 450.20 980.11 449.99 980.62 449.56 c -h -984.14 451.00 m -984.14 444.64 l -985.29 444.64 l -985.29 445.83 l -985.90 444.94 986.65 444.50 987.53 444.50 c -988.08 444.50 988.52 444.67 988.85 445.02 c -989.18 445.37 989.34 445.84 989.34 446.43 c -989.34 451.00 l -988.19 451.00 l -988.19 446.80 l -988.19 446.33 988.12 446.00 987.98 445.79 c -987.84 445.59 987.61 445.49 987.29 445.49 c -986.59 445.49 985.92 445.96 985.29 446.88 c -985.29 451.00 l -h -994.07 451.15 m -993.21 451.15 992.50 450.83 991.93 450.19 c -991.37 449.55 991.08 448.75 991.08 447.78 c -991.08 446.75 991.36 445.94 991.92 445.36 c -992.49 444.79 993.27 444.50 994.27 444.50 c -994.77 444.50 995.32 444.56 995.94 444.70 c -995.94 445.67 l -995.28 445.48 994.75 445.38 994.34 445.38 c -993.75 445.38 993.28 445.60 992.92 446.05 c -992.56 446.49 992.38 447.08 992.38 447.82 c -992.38 448.53 992.57 449.11 992.94 449.55 c -993.30 449.99 993.78 450.21 994.38 450.21 c -994.90 450.21 995.45 450.08 996.01 449.81 c -996.01 450.81 l -995.26 451.03 994.62 451.15 994.07 451.15 c -h -1002.4 450.79 m -1001.6 451.03 1001.0 451.15 1000.4 451.15 c -999.47 451.15 998.71 450.83 998.12 450.21 c -997.53 449.59 997.23 448.78 997.23 447.79 c -997.23 446.82 997.49 446.03 998.01 445.42 c -998.53 444.80 999.20 444.49 1000.0 444.49 c -1000.8 444.49 1001.4 444.76 1001.8 445.31 c -1002.2 445.86 1002.4 446.63 1002.4 447.64 c -1002.4 448.00 l -998.41 448.00 l -998.58 449.51 999.32 450.27 1000.6 450.27 c -1001.1 450.27 1001.7 450.14 1002.4 449.88 c -h -998.46 447.13 m -1001.3 447.13 l -1001.3 445.95 1000.8 445.36 999.94 445.36 c -999.06 445.36 998.56 445.95 998.46 447.13 c -h -f -868.44 430.19 m -867.74 430.83 867.08 431.15 866.44 431.15 c -865.91 431.15 865.47 430.98 865.12 430.65 c -864.78 430.32 864.60 429.90 864.60 429.40 c -864.60 428.71 864.90 428.17 865.48 427.80 c -866.06 427.42 866.90 427.24 867.99 427.24 c -868.27 427.24 l -868.27 426.47 l -868.27 425.73 867.89 425.36 867.13 425.36 c -866.52 425.36 865.86 425.55 865.15 425.93 c -865.15 424.97 l -865.93 424.65 866.66 424.50 867.34 424.50 c -868.05 424.50 868.58 424.66 868.91 424.98 c -869.25 425.30 869.42 425.79 869.42 426.47 c -869.42 429.35 l -869.42 430.01 869.62 430.34 870.03 430.34 c -870.08 430.34 870.15 430.34 870.25 430.32 c -870.33 430.96 l -870.07 431.08 869.78 431.15 869.47 431.15 c -868.93 431.15 868.58 430.83 868.44 430.19 c -h -868.27 429.56 m -868.27 427.92 l -867.88 427.91 l -867.25 427.91 866.73 428.03 866.34 428.27 c -865.95 428.51 865.76 428.82 865.76 429.21 c -865.76 429.49 865.86 429.72 866.05 429.92 c -866.25 430.11 866.48 430.20 866.77 430.20 c -867.25 430.20 867.75 429.99 868.27 429.56 c -h -875.76 431.00 m -875.76 429.80 l -875.15 430.70 874.40 431.15 873.53 431.15 c -872.97 431.15 872.53 430.97 872.20 430.62 c -871.88 430.27 871.71 429.80 871.71 429.21 c -871.71 424.64 l -872.87 424.64 l -872.87 428.83 l -872.87 429.31 872.93 429.65 873.07 429.85 c -873.21 430.05 873.44 430.15 873.77 430.15 c -874.47 430.15 875.13 429.69 875.76 428.76 c -875.76 424.64 l -876.91 424.64 l -876.91 431.00 l -h -881.14 431.15 m -880.55 431.15 880.10 430.98 879.77 430.64 c -879.44 430.31 879.28 429.84 879.28 429.24 c -879.28 425.50 l -878.48 425.50 l -878.48 424.64 l -879.28 424.64 l -879.28 423.48 l -880.43 423.37 l -880.43 424.64 l -882.09 424.64 l -882.09 425.50 l -880.43 425.50 l -880.43 429.03 l -880.43 429.86 880.79 430.28 881.51 430.28 c -881.66 430.28 881.85 430.25 882.06 430.20 c -882.06 431.00 l -881.71 431.10 881.40 431.15 881.14 431.15 c -h -883.72 431.00 m -883.72 421.75 l -884.87 421.75 l -884.87 425.83 l -885.48 424.94 886.23 424.50 887.11 424.50 c -887.66 424.50 888.10 424.67 888.43 425.02 c -888.76 425.37 888.92 425.84 888.92 426.43 c -888.92 431.00 l -887.77 431.00 l -887.77 426.80 l -887.77 426.33 887.70 426.00 887.56 425.79 c -887.42 425.59 887.19 425.49 886.87 425.49 c -886.16 425.49 885.50 425.96 884.87 426.88 c -884.87 431.00 l -h -893.65 431.15 m -892.74 431.15 892.02 430.84 891.47 430.24 c -890.93 429.64 890.66 428.83 890.66 427.82 c -890.66 426.79 890.93 425.99 891.48 425.39 c -892.02 424.79 892.76 424.50 893.70 424.50 c -894.63 424.50 895.37 424.79 895.91 425.39 c -896.46 425.99 896.73 426.79 896.73 427.81 c -896.73 428.85 896.46 429.66 895.91 430.26 c -895.36 430.85 894.61 431.15 893.65 431.15 c -h -893.67 430.28 m -894.89 430.28 895.51 429.46 895.51 427.81 c -895.51 426.18 894.90 425.36 893.70 425.36 c -892.49 425.36 891.89 426.18 891.89 427.82 c -891.89 429.46 892.48 430.28 893.67 430.28 c -h -898.54 431.00 m -898.54 424.64 l -899.69 424.64 l -899.69 425.83 l -900.15 424.94 900.81 424.50 901.68 424.50 c -901.80 424.50 901.92 424.51 902.05 424.53 c -902.05 425.60 l -901.85 425.54 901.68 425.50 901.52 425.50 c -900.79 425.50 900.18 425.94 899.69 426.80 c -899.69 431.00 l -h -903.45 431.00 m -903.45 424.64 l -904.60 424.64 l -904.60 431.00 l -h -903.45 423.48 m -903.45 422.33 l -904.60 422.33 l -904.60 423.48 l -h -906.48 431.00 m -906.48 430.13 l -910.42 425.50 l -906.66 425.50 l -906.66 424.64 l -911.84 424.64 l -911.84 425.50 l -907.90 430.13 l -911.92 430.13 l -911.92 431.00 l -h -918.46 430.79 m -917.68 431.03 917.02 431.15 916.47 431.15 c -915.53 431.15 914.77 430.83 914.18 430.21 c -913.58 429.59 913.29 428.78 913.29 427.79 c -913.29 426.82 913.55 426.03 914.07 425.42 c -914.59 424.80 915.26 424.49 916.07 424.49 c -916.84 424.49 917.44 424.76 917.86 425.31 c -918.28 425.86 918.49 426.63 918.49 427.64 c -918.48 428.00 l -914.47 428.00 l -914.63 429.51 915.38 430.27 916.69 430.27 c -917.17 430.27 917.76 430.14 918.46 429.88 c -h -914.52 427.13 m -917.33 427.13 l -917.33 425.95 916.88 425.36 916.00 425.36 c -915.12 425.36 914.62 425.95 914.52 427.13 c -h -920.62 429.05 m -920.62 428.18 l -927.56 428.18 l -927.56 429.05 l -h -920.62 426.88 m -920.62 426.01 l -927.56 426.01 l -927.56 426.88 l -h -932.50 431.15 m -931.64 431.15 930.93 430.83 930.36 430.19 c -929.80 429.55 929.51 428.75 929.51 427.78 c -929.51 426.75 929.79 425.94 930.35 425.36 c -930.92 424.79 931.70 424.50 932.70 424.50 c -933.20 424.50 933.75 424.56 934.37 424.70 c -934.37 425.67 l -933.71 425.48 933.18 425.38 932.77 425.38 c -932.18 425.38 931.71 425.60 931.35 426.05 c -930.99 426.49 930.81 427.08 930.81 427.82 c -930.81 428.53 931.00 429.11 931.37 429.55 c -931.73 429.99 932.21 430.21 932.81 430.21 c -933.33 430.21 933.88 430.08 934.44 429.81 c -934.44 430.81 l -933.69 431.03 933.04 431.15 932.50 431.15 c -h -939.45 430.19 m -938.75 430.83 938.09 431.15 937.45 431.15 c -936.92 431.15 936.48 430.98 936.13 430.65 c -935.79 430.32 935.61 429.90 935.61 429.40 c -935.61 428.71 935.91 428.17 936.49 427.80 c -937.07 427.42 937.91 427.24 939.00 427.24 c -939.28 427.24 l -939.28 426.47 l -939.28 425.73 938.90 425.36 938.14 425.36 c -937.53 425.36 936.87 425.55 936.16 425.93 c -936.16 424.97 l -936.94 424.65 937.67 424.50 938.35 424.50 c -939.06 424.50 939.58 424.66 939.92 424.98 c -940.26 425.30 940.43 425.79 940.43 426.47 c -940.43 429.35 l -940.43 430.01 940.63 430.34 941.04 430.34 c -941.09 430.34 941.16 430.34 941.26 430.32 c -941.34 430.96 l -941.08 431.08 940.79 431.15 940.48 431.15 c -939.94 431.15 939.59 430.83 939.45 430.19 c -h -939.28 429.56 m -939.28 427.92 l -938.89 427.91 l -938.26 427.91 937.74 428.03 937.35 428.27 c -936.96 428.51 936.77 428.82 936.77 429.21 c -936.77 429.49 936.87 429.72 937.06 429.92 c -937.26 430.11 937.49 430.20 937.78 430.20 c -938.26 430.20 938.76 429.99 939.28 429.56 c -h -942.79 431.00 m -942.79 424.64 l -943.95 424.64 l -943.95 425.83 l -944.55 424.94 945.30 424.50 946.18 424.50 c -946.73 424.50 947.17 424.67 947.50 425.02 c -947.83 425.37 947.99 425.84 947.99 426.43 c -947.99 431.00 l -946.84 431.00 l -946.84 426.80 l -946.84 426.33 946.77 426.00 946.63 425.79 c -946.49 425.59 946.26 425.49 945.94 425.49 c -945.24 425.49 944.57 425.96 943.95 426.88 c -943.95 431.00 l -h -949.08 433.02 m -949.08 432.15 l -955.08 432.15 l -955.08 433.02 l -h -956.24 431.00 m -956.24 421.75 l -957.39 421.75 l -957.39 425.83 l -958.00 424.94 958.75 424.50 959.63 424.50 c -960.18 424.50 960.62 424.67 960.95 425.02 c -961.28 425.37 961.44 425.84 961.44 426.43 c -961.44 431.00 l -960.29 431.00 l -960.29 426.80 l -960.29 426.33 960.22 426.00 960.08 425.79 c -959.94 425.59 959.71 425.49 959.39 425.49 c -958.68 425.49 958.02 425.96 957.39 426.88 c -957.39 431.00 l -h -966.97 430.19 m -966.28 430.83 965.61 431.15 964.97 431.15 c -964.44 431.15 964.00 430.98 963.66 430.65 c -963.31 430.32 963.13 429.90 963.13 429.40 c -963.13 428.71 963.43 428.17 964.01 427.80 c -964.59 427.42 965.43 427.24 966.52 427.24 c -966.80 427.24 l -966.80 426.47 l -966.80 425.73 966.42 425.36 965.66 425.36 c -965.05 425.36 964.39 425.55 963.69 425.93 c -963.69 424.97 l -964.46 424.65 965.19 424.50 965.87 424.50 c -966.58 424.50 967.11 424.66 967.44 424.98 c -967.78 425.30 967.95 425.79 967.95 426.47 c -967.95 429.35 l -967.95 430.01 968.15 430.34 968.56 430.34 c -968.61 430.34 968.69 430.34 968.78 430.32 c -968.87 430.96 l -968.60 431.08 968.31 431.15 968.00 431.15 c -967.46 431.15 967.12 430.83 966.97 430.19 c -h -966.80 429.56 m -966.80 427.92 l -966.41 427.91 l -965.78 427.91 965.27 428.03 964.88 428.27 c -964.48 428.51 964.29 428.82 964.29 429.21 c -964.29 429.49 964.39 429.72 964.58 429.92 c -964.78 430.11 965.02 430.20 965.30 430.20 c -965.78 430.20 966.28 429.99 966.80 429.56 c -h -969.88 431.00 m -969.88 430.13 l -973.82 425.50 l -970.06 425.50 l -970.06 424.64 l -975.24 424.64 l -975.24 425.50 l -971.30 430.13 l -975.32 430.13 l -975.32 431.00 l -h -979.51 431.94 m -979.51 432.73 l -978.66 432.16 977.99 431.38 977.50 430.39 c -977.00 429.41 976.76 428.36 976.76 427.24 c -976.76 426.12 977.00 425.08 977.50 424.09 c -977.99 423.10 978.66 422.32 979.51 421.75 c -979.51 422.54 l -978.93 423.17 978.52 423.84 978.28 424.56 c -978.03 425.28 977.91 426.17 977.91 427.24 c -977.91 428.31 978.03 429.20 978.28 429.92 c -978.52 430.64 978.93 431.31 979.51 431.94 c -h -983.58 431.15 m -982.72 431.15 982.01 430.83 981.44 430.19 c -980.87 429.55 980.59 428.75 980.59 427.78 c -980.59 426.75 980.87 425.94 981.43 425.36 c -981.99 424.79 982.77 424.50 983.78 424.50 c -984.27 424.50 984.83 424.56 985.44 424.70 c -985.44 425.67 l -984.79 425.48 984.26 425.38 983.85 425.38 c -983.26 425.38 982.78 425.60 982.43 426.05 c -982.07 426.49 981.89 427.08 981.89 427.82 c -981.89 428.53 982.07 429.11 982.44 429.55 c -982.81 429.99 983.29 430.21 983.88 430.21 c -984.41 430.21 984.95 430.08 985.51 429.81 c -985.51 430.81 l -984.77 431.03 984.12 431.15 983.58 431.15 c -h -989.73 431.15 m -988.82 431.15 988.09 430.84 987.55 430.24 c -987.01 429.64 986.74 428.83 986.74 427.82 c -986.74 426.79 987.01 425.99 987.55 425.39 c -988.10 424.79 988.84 424.50 989.77 424.50 c -990.71 424.50 991.44 424.79 991.99 425.39 c -992.53 425.99 992.81 426.79 992.81 427.81 c -992.81 428.85 992.53 429.66 991.99 430.26 c -991.44 430.85 990.69 431.15 989.73 431.15 c -h -989.75 430.28 m -990.97 430.28 991.58 429.46 991.58 427.81 c -991.58 426.18 990.98 425.36 989.77 425.36 c -988.57 425.36 987.97 426.18 987.97 427.82 c -987.97 429.46 988.56 430.28 989.75 430.28 c -h -994.61 431.00 m -994.61 424.64 l -995.77 424.64 l -995.77 425.83 l -996.38 424.94 997.12 424.50 998.00 424.50 c -998.55 424.50 998.99 424.67 999.32 425.02 c -999.65 425.37 999.81 425.84 999.81 426.43 c -999.81 431.00 l -998.66 431.00 l -998.66 426.80 l -998.66 426.33 998.59 426.00 998.45 425.79 c -998.31 425.59 998.08 425.49 997.76 425.49 c -997.06 425.49 996.39 425.96 995.77 426.88 c -995.77 431.00 l -h -1004.0 431.15 m -1003.4 431.15 1002.9 430.98 1002.6 430.64 c -1002.3 430.31 1002.1 429.84 1002.1 429.24 c -1002.1 425.50 l -1001.3 425.50 l -1001.3 424.64 l -1002.1 424.64 l -1002.1 423.48 l -1003.3 423.37 l -1003.3 424.64 l -1004.9 424.64 l -1004.9 425.50 l -1003.3 425.50 l -1003.3 429.03 l -1003.3 429.86 1003.6 430.28 1004.3 430.28 c -1004.5 430.28 1004.7 430.25 1004.9 430.20 c -1004.9 431.00 l -1004.5 431.10 1004.2 431.15 1004.0 431.15 c -h -1011.2 430.79 m -1010.4 431.03 1009.8 431.15 1009.2 431.15 c -1008.3 431.15 1007.5 430.83 1006.9 430.21 c -1006.3 429.59 1006.0 428.78 1006.0 427.79 c -1006.0 426.82 1006.3 426.03 1006.8 425.42 c -1007.3 424.80 1008.0 424.49 1008.8 424.49 c -1009.6 424.49 1010.2 424.76 1010.6 425.31 c -1011.0 425.86 1011.2 426.63 1011.2 427.64 c -1011.2 428.00 l -1007.2 428.00 l -1007.4 429.51 1008.1 430.27 1009.4 430.27 c -1009.9 430.27 1010.5 430.14 1011.2 429.88 c -h -1007.3 427.13 m -1010.1 427.13 l -1010.1 425.95 1009.6 425.36 1008.8 425.36 c -1007.9 425.36 1007.4 425.95 1007.3 427.13 c -h -1012.6 431.00 m -1015.0 427.71 l -1012.7 424.64 l -1014.0 424.64 l -1015.9 427.09 l -1017.6 424.64 l -1018.7 424.64 l -1016.5 427.87 l -1018.9 431.00 l -1017.5 431.00 l -1015.6 428.48 l -1013.8 431.00 l -h -1022.5 431.15 m -1021.9 431.15 1021.5 430.98 1021.1 430.64 c -1020.8 430.31 1020.6 429.84 1020.6 429.24 c -1020.6 425.50 l -1019.8 425.50 l -1019.8 424.64 l -1020.6 424.64 l -1020.6 423.48 l -1021.8 423.37 l -1021.8 424.64 l -1023.5 424.64 l -1023.5 425.50 l -1021.8 425.50 l -1021.8 429.03 l -1021.8 429.86 1022.2 430.28 1022.9 430.28 c -1023.0 430.28 1023.2 430.25 1023.4 430.20 c -1023.4 431.00 l -1023.1 431.10 1022.8 431.15 1022.5 431.15 c -h -1025.1 432.88 m -1025.1 432.45 l -1025.5 432.34 1025.7 431.90 1025.7 431.12 c -1025.7 431.00 l -1025.1 431.00 l -1025.1 429.55 l -1026.5 429.55 l -1026.5 430.81 l -1026.5 432.09 1026.1 432.78 1025.1 432.88 c -h -1036.7 431.00 m -1036.7 429.80 l -1036.0 430.70 1035.3 431.15 1034.4 431.15 c -1033.9 431.15 1033.4 430.97 1033.1 430.62 c -1032.8 430.27 1032.6 429.80 1032.6 429.21 c -1032.6 424.64 l -1033.8 424.64 l -1033.8 428.83 l -1033.8 429.31 1033.8 429.65 1034.0 429.85 c -1034.1 430.05 1034.3 430.15 1034.7 430.15 c -1035.4 430.15 1036.0 429.69 1036.7 428.76 c -1036.7 424.64 l -1037.8 424.64 l -1037.8 431.00 l -h -1041.8 431.15 m -1041.3 431.15 1040.6 431.02 1039.9 430.78 c -1039.9 429.72 l -1040.6 430.09 1041.3 430.28 1041.9 430.28 c -1042.2 430.28 1042.5 430.19 1042.7 430.01 c -1042.9 429.83 1043.0 429.61 1043.0 429.34 c -1043.0 428.94 1042.7 428.62 1042.1 428.36 c -1041.4 428.07 l -1040.4 427.66 1039.9 427.06 1039.9 426.28 c -1039.9 425.73 1040.1 425.29 1040.5 424.97 c -1040.9 424.66 1041.4 424.50 1042.1 424.50 c -1042.5 424.50 1042.9 424.54 1043.4 424.64 c -1043.7 424.69 l -1043.7 425.65 l -1043.0 425.46 1042.5 425.36 1042.1 425.36 c -1041.4 425.36 1041.0 425.63 1041.0 426.17 c -1041.0 426.52 1041.3 426.81 1041.9 427.05 c -1042.4 427.29 l -1043.1 427.55 1043.5 427.83 1043.8 428.13 c -1044.0 428.42 1044.2 428.79 1044.2 429.23 c -1044.2 429.79 1043.9 430.25 1043.5 430.61 c -1043.1 430.97 1042.5 431.15 1041.8 431.15 c -h -1050.9 430.79 m -1050.1 431.03 1049.5 431.15 1048.9 431.15 c -1048.0 431.15 1047.2 430.83 1046.6 430.21 c -1046.0 429.59 1045.7 428.78 1045.7 427.79 c -1045.7 426.82 1046.0 426.03 1046.5 425.42 c -1047.0 424.80 1047.7 424.49 1048.5 424.49 c -1049.3 424.49 1049.9 424.76 1050.3 425.31 c -1050.7 425.86 1050.9 426.63 1050.9 427.64 c -1050.9 428.00 l -1046.9 428.00 l -1047.1 429.51 1047.8 430.27 1049.1 430.27 c -1049.6 430.27 1050.2 430.14 1050.9 429.88 c -h -1047.0 427.13 m -1049.8 427.13 l -1049.8 425.95 1049.3 425.36 1048.4 425.36 c -1047.6 425.36 1047.1 425.95 1047.0 427.13 c -h -1052.9 431.00 m -1052.9 424.64 l -1054.1 424.64 l -1054.1 425.83 l -1054.5 424.94 1055.2 424.50 1056.1 424.50 c -1056.2 424.50 1056.3 424.51 1056.4 424.53 c -1056.4 425.60 l -1056.2 425.54 1056.1 425.50 1055.9 425.50 c -1055.2 425.50 1054.6 425.94 1054.1 426.80 c -1054.1 431.00 l -h -1057.9 432.88 m -1057.9 432.45 l -1058.2 432.34 1058.4 431.90 1058.4 431.12 c -1058.4 431.00 l -1057.9 431.00 l -1057.9 429.55 l -1059.3 429.55 l -1059.3 430.81 l -1059.3 432.09 1058.8 432.78 1057.9 432.88 c -h -1065.2 424.93 m -1064.9 421.75 l -1066.4 421.75 l -1066.1 424.93 l -h -1070.7 431.15 m -1069.8 431.15 1069.1 430.83 1068.5 430.19 c -1068.0 429.55 1067.7 428.75 1067.7 427.78 c -1067.7 426.75 1068.0 425.94 1068.5 425.36 c -1069.1 424.79 1069.9 424.50 1070.9 424.50 c -1071.4 424.50 1071.9 424.56 1072.5 424.70 c -1072.5 425.67 l -1071.9 425.48 1071.3 425.38 1070.9 425.38 c -1070.3 425.38 1069.9 425.60 1069.5 426.05 c -1069.2 426.49 1069.0 427.08 1069.0 427.82 c -1069.0 428.53 1069.2 429.11 1069.5 429.55 c -1069.9 429.99 1070.4 430.21 1071.0 430.21 c -1071.5 430.21 1072.0 430.08 1072.6 429.81 c -1072.6 430.81 l -1071.8 431.03 1071.2 431.15 1070.7 431.15 c -h -1074.3 431.00 m -1074.3 424.64 l -1075.5 424.64 l -1075.5 425.83 l -1075.9 424.94 1076.6 424.50 1077.5 424.50 c -1077.6 424.50 1077.7 424.51 1077.8 424.53 c -1077.8 425.60 l -1077.6 425.54 1077.5 425.50 1077.3 425.50 c -1076.6 425.50 1076.0 425.94 1075.5 426.80 c -1075.5 431.00 l -h -1083.9 430.79 m -1083.1 431.03 1082.5 431.15 1081.9 431.15 c -1081.0 431.15 1080.2 430.83 1079.6 430.21 c -1079.0 429.59 1078.7 428.78 1078.7 427.79 c -1078.7 426.82 1079.0 426.03 1079.5 425.42 c -1080.0 424.80 1080.7 424.49 1081.5 424.49 c -1082.3 424.49 1082.9 424.76 1083.3 425.31 c -1083.7 425.86 1083.9 426.63 1083.9 427.64 c -1083.9 428.00 l -1079.9 428.00 l -1080.1 429.51 1080.8 430.27 1082.1 430.27 c -1082.6 430.27 1083.2 430.14 1083.9 429.88 c -h -1080.0 427.13 m -1082.8 427.13 l -1082.8 425.95 1082.3 425.36 1081.4 425.36 c -1080.6 425.36 1080.1 425.95 1080.0 427.13 c -h -1089.2 430.19 m -1088.5 430.83 1087.8 431.15 1087.2 431.15 c -1086.7 431.15 1086.2 430.98 1085.9 430.65 c -1085.5 430.32 1085.4 429.90 1085.4 429.40 c -1085.4 428.71 1085.7 428.17 1086.2 427.80 c -1086.8 427.42 1087.7 427.24 1088.8 427.24 c -1089.0 427.24 l -1089.0 426.47 l -1089.0 425.73 1088.7 425.36 1087.9 425.36 c -1087.3 425.36 1086.6 425.55 1085.9 425.93 c -1085.9 424.97 l -1086.7 424.65 1087.4 424.50 1088.1 424.50 c -1088.8 424.50 1089.3 424.66 1089.7 424.98 c -1090.0 425.30 1090.2 425.79 1090.2 426.47 c -1090.2 429.35 l -1090.2 430.01 1090.4 430.34 1090.8 430.34 c -1090.8 430.34 1090.9 430.34 1091.0 430.32 c -1091.1 430.96 l -1090.8 431.08 1090.5 431.15 1090.2 431.15 c -1089.7 431.15 1089.3 430.83 1089.2 430.19 c -h -1089.0 429.56 m -1089.0 427.92 l -1088.6 427.91 l -1088.0 427.91 1087.5 428.03 1087.1 428.27 c -1086.7 428.51 1086.5 428.82 1086.5 429.21 c -1086.5 429.49 1086.6 429.72 1086.8 429.92 c -1087.0 430.11 1087.2 430.20 1087.5 430.20 c -1088.0 430.20 1088.5 429.99 1089.0 429.56 c -h -1094.5 431.15 m -1093.9 431.15 1093.4 430.98 1093.1 430.64 c -1092.8 430.31 1092.6 429.84 1092.6 429.24 c -1092.6 425.50 l -1091.8 425.50 l -1091.8 424.64 l -1092.6 424.64 l -1092.6 423.48 l -1093.7 423.37 l -1093.7 424.64 l -1095.4 424.64 l -1095.4 425.50 l -1093.7 425.50 l -1093.7 429.03 l -1093.7 429.86 1094.1 430.28 1094.8 430.28 c -1095.0 430.28 1095.2 430.25 1095.4 430.20 c -1095.4 431.00 l -1095.0 431.10 1094.7 431.15 1094.5 431.15 c -h -1101.7 430.79 m -1100.9 431.03 1100.3 431.15 1099.7 431.15 c -1098.8 431.15 1098.0 430.83 1097.4 430.21 c -1096.8 429.59 1096.5 428.78 1096.5 427.79 c -1096.5 426.82 1096.8 426.03 1097.3 425.42 c -1097.8 424.80 1098.5 424.49 1099.3 424.49 c -1100.1 424.49 1100.7 424.76 1101.1 425.31 c -1101.5 425.86 1101.7 426.63 1101.7 427.64 c -1101.7 428.00 l -1097.7 428.00 l -1097.9 429.51 1098.6 430.27 1099.9 430.27 c -1100.4 430.27 1101.0 430.14 1101.7 429.88 c -h -1097.8 427.13 m -1100.6 427.13 l -1100.6 425.95 1100.1 425.36 1099.2 425.36 c -1098.4 425.36 1097.9 425.95 1097.8 427.13 c -h -1102.6 433.02 m -1102.6 432.15 l -1108.6 432.15 l -1108.6 433.02 l -h -1109.7 431.00 m -1109.7 424.64 l -1110.9 424.64 l -1110.9 431.00 l -h -1109.7 423.48 m -1109.7 422.33 l -1110.9 422.33 l -1110.9 423.48 l -h -1113.2 431.00 m -1113.2 424.64 l -1114.3 424.64 l -1114.3 425.83 l -1115.0 424.94 1115.7 424.50 1116.6 424.50 c -1117.1 424.50 1117.6 424.67 1117.9 425.02 c -1118.2 425.37 1118.4 425.84 1118.4 426.43 c -1118.4 431.00 l -1117.2 431.00 l -1117.2 426.80 l -1117.2 426.33 1117.2 426.00 1117.0 425.79 c -1116.9 425.59 1116.7 425.49 1116.3 425.49 c -1115.6 425.49 1115.0 425.96 1114.3 426.88 c -1114.3 431.00 l -h -1122.3 431.15 m -1121.8 431.15 1121.2 431.02 1120.4 430.78 c -1120.4 429.72 l -1121.2 430.09 1121.8 430.28 1122.4 430.28 c -1122.7 430.28 1123.0 430.19 1123.2 430.01 c -1123.4 429.83 1123.5 429.61 1123.5 429.34 c -1123.5 428.94 1123.2 428.62 1122.6 428.36 c -1121.9 428.07 l -1120.9 427.66 1120.4 427.06 1120.4 426.28 c -1120.4 425.73 1120.6 425.29 1121.0 424.97 c -1121.4 424.66 1122.0 424.50 1122.6 424.50 c -1123.0 424.50 1123.4 424.54 1124.0 424.64 c -1124.2 424.69 l -1124.2 425.65 l -1123.6 425.46 1123.0 425.36 1122.7 425.36 c -1121.9 425.36 1121.5 425.63 1121.5 426.17 c -1121.5 426.52 1121.8 426.81 1122.4 427.05 c -1122.9 427.29 l -1123.6 427.55 1124.0 427.83 1124.3 428.13 c -1124.5 428.42 1124.7 428.79 1124.7 429.23 c -1124.7 429.79 1124.5 430.25 1124.0 430.61 c -1123.6 430.97 1123.0 431.15 1122.3 431.15 c -h -1128.7 431.15 m -1128.1 431.15 1127.6 430.98 1127.3 430.64 c -1127.0 430.31 1126.8 429.84 1126.8 429.24 c -1126.8 425.50 l -1126.0 425.50 l -1126.0 424.64 l -1126.8 424.64 l -1126.8 423.48 l -1128.0 423.37 l -1128.0 424.64 l -1129.6 424.64 l -1129.6 425.50 l -1128.0 425.50 l -1128.0 429.03 l -1128.0 429.86 1128.3 430.28 1129.0 430.28 c -1129.2 430.28 1129.4 430.25 1129.6 430.20 c -1129.6 431.00 l -1129.2 431.10 1128.9 431.15 1128.7 431.15 c -h -1134.5 430.19 m -1133.8 430.83 1133.2 431.15 1132.5 431.15 c -1132.0 431.15 1131.6 430.98 1131.2 430.65 c -1130.9 430.32 1130.7 429.90 1130.7 429.40 c -1130.7 428.71 1131.0 428.17 1131.6 427.80 c -1132.1 427.42 1133.0 427.24 1134.1 427.24 c -1134.4 427.24 l -1134.4 426.47 l -1134.4 425.73 1134.0 425.36 1133.2 425.36 c -1132.6 425.36 1131.9 425.55 1131.2 425.93 c -1131.2 424.97 l -1132.0 424.65 1132.7 424.50 1133.4 424.50 c -1134.1 424.50 1134.7 424.66 1135.0 424.98 c -1135.3 425.30 1135.5 425.79 1135.5 426.47 c -1135.5 429.35 l -1135.5 430.01 1135.7 430.34 1136.1 430.34 c -1136.2 430.34 1136.2 430.34 1136.3 430.32 c -1136.4 430.96 l -1136.2 431.08 1135.9 431.15 1135.6 431.15 c -1135.0 431.15 1134.7 430.83 1134.5 430.19 c -h -1134.4 429.56 m -1134.4 427.92 l -1134.0 427.91 l -1133.3 427.91 1132.8 428.03 1132.4 428.27 c -1132.0 428.51 1131.8 428.82 1131.8 429.21 c -1131.8 429.49 1131.9 429.72 1132.1 429.92 c -1132.3 430.11 1132.6 430.20 1132.9 430.20 c -1133.3 430.20 1133.8 429.99 1134.4 429.56 c -h -1137.9 431.00 m -1137.9 424.64 l -1139.0 424.64 l -1139.0 425.83 l -1139.6 424.94 1140.4 424.50 1141.3 424.50 c -1141.8 424.50 1142.2 424.67 1142.6 425.02 c -1142.9 425.37 1143.1 425.84 1143.1 426.43 c -1143.1 431.00 l -1141.9 431.00 l -1141.9 426.80 l -1141.9 426.33 1141.8 426.00 1141.7 425.79 c -1141.6 425.59 1141.3 425.49 1141.0 425.49 c -1140.3 425.49 1139.6 425.96 1139.0 426.88 c -1139.0 431.00 l -h -1147.8 431.15 m -1146.9 431.15 1146.2 430.83 1145.7 430.19 c -1145.1 429.55 1144.8 428.75 1144.8 427.78 c -1144.8 426.75 1145.1 425.94 1145.7 425.36 c -1146.2 424.79 1147.0 424.50 1148.0 424.50 c -1148.5 424.50 1149.0 424.56 1149.7 424.70 c -1149.7 425.67 l -1149.0 425.48 1148.5 425.38 1148.1 425.38 c -1147.5 425.38 1147.0 425.60 1146.6 426.05 c -1146.3 426.49 1146.1 427.08 1146.1 427.82 c -1146.1 428.53 1146.3 429.11 1146.7 429.55 c -1147.0 429.99 1147.5 430.21 1148.1 430.21 c -1148.6 430.21 1149.2 430.08 1149.7 429.81 c -1149.7 430.81 l -1149.0 431.03 1148.3 431.15 1147.8 431.15 c -h -1156.1 430.79 m -1155.4 431.03 1154.7 431.15 1154.1 431.15 c -1153.2 431.15 1152.4 430.83 1151.8 430.21 c -1151.3 429.59 1151.0 428.78 1151.0 427.79 c -1151.0 426.82 1151.2 426.03 1151.7 425.42 c -1152.3 424.80 1152.9 424.49 1153.7 424.49 c -1154.5 424.49 1155.1 424.76 1155.5 425.31 c -1155.9 425.86 1156.2 426.63 1156.2 427.64 c -1156.1 428.00 l -1152.1 428.00 l -1152.3 429.51 1153.0 430.27 1154.4 430.27 c -1154.8 430.27 1155.4 430.14 1156.1 429.88 c -h -1152.2 427.13 m -1155.0 427.13 l -1155.0 425.95 1154.6 425.36 1153.7 425.36 c -1152.8 425.36 1152.3 425.95 1152.2 427.13 c -h -1157.9 424.93 m -1157.6 421.75 l -1159.1 421.75 l -1158.8 424.93 l -h -1160.9 432.88 m -1160.9 432.45 l -1161.3 432.34 1161.5 431.90 1161.5 431.12 c -1161.5 431.00 l -1160.9 431.00 l -1160.9 429.55 l -1162.4 429.55 l -1162.4 430.81 l -1162.4 432.09 1161.9 432.78 1160.9 432.88 c -h -1170.4 431.15 m -1169.8 431.15 1169.4 430.98 1169.0 430.64 c -1168.7 430.31 1168.5 429.84 1168.5 429.24 c -1168.5 425.50 l -1167.7 425.50 l -1167.7 424.64 l -1168.5 424.64 l -1168.5 423.48 l -1169.7 423.37 l -1169.7 424.64 l -1171.4 424.64 l -1171.4 425.50 l -1169.7 425.50 l -1169.7 429.03 l -1169.7 429.86 1170.0 430.28 1170.8 430.28 c -1170.9 430.28 1171.1 430.25 1171.3 430.20 c -1171.3 431.00 l -1171.0 431.10 1170.7 431.15 1170.4 431.15 c -h -1177.6 430.79 m -1176.9 431.03 1176.2 431.15 1175.7 431.15 c -1174.7 431.15 1174.0 430.83 1173.4 430.21 c -1172.8 429.59 1172.5 428.78 1172.5 427.79 c -1172.5 426.82 1172.7 426.03 1173.3 425.42 c -1173.8 424.80 1174.4 424.49 1175.3 424.49 c -1176.0 424.49 1176.6 424.76 1177.0 425.31 c -1177.5 425.86 1177.7 426.63 1177.7 427.64 c -1177.7 428.00 l -1173.7 428.00 l -1173.8 429.51 1174.6 430.27 1175.9 430.27 c -1176.4 430.27 1176.9 430.14 1177.6 429.88 c -h -1173.7 427.13 m -1176.5 427.13 l -1176.5 425.95 1176.1 425.36 1175.2 425.36 c -1174.3 425.36 1173.8 425.95 1173.7 427.13 c -h -1179.7 431.00 m -1179.7 424.64 l -1180.8 424.64 l -1180.8 425.83 l -1181.4 424.94 1182.2 424.50 1183.1 424.50 c -1183.6 424.50 1184.0 424.67 1184.4 425.02 c -1184.7 425.37 1184.9 425.84 1184.9 426.43 c -1184.9 431.00 l -1183.7 431.00 l -1183.7 426.80 l -1183.7 426.33 1183.6 426.00 1183.5 425.79 c -1183.4 425.59 1183.1 425.49 1182.8 425.49 c -1182.1 425.49 1181.4 425.96 1180.8 426.88 c -1180.8 431.00 l -h -1190.4 430.19 m -1189.7 430.83 1189.0 431.15 1188.4 431.15 c -1187.9 431.15 1187.4 430.98 1187.1 430.65 c -1186.7 430.32 1186.6 429.90 1186.6 429.40 c -1186.6 428.71 1186.9 428.17 1187.4 427.80 c -1188.0 427.42 1188.9 427.24 1189.9 427.24 c -1190.2 427.24 l -1190.2 426.47 l -1190.2 425.73 1189.8 425.36 1189.1 425.36 c -1188.5 425.36 1187.8 425.55 1187.1 425.93 c -1187.1 424.97 l -1187.9 424.65 1188.6 424.50 1189.3 424.50 c -1190.0 424.50 1190.5 424.66 1190.9 424.98 c -1191.2 425.30 1191.4 425.79 1191.4 426.47 c -1191.4 429.35 l -1191.4 430.01 1191.6 430.34 1192.0 430.34 c -1192.0 430.34 1192.1 430.34 1192.2 430.32 c -1192.3 430.96 l -1192.0 431.08 1191.7 431.15 1191.4 431.15 c -1190.9 431.15 1190.5 430.83 1190.4 430.19 c -h -1190.2 429.56 m -1190.2 427.92 l -1189.8 427.91 l -1189.2 427.91 1188.7 428.03 1188.3 428.27 c -1187.9 428.51 1187.7 428.82 1187.7 429.21 c -1187.7 429.49 1187.8 429.72 1188.0 429.92 c -1188.2 430.11 1188.4 430.20 1188.7 430.20 c -1189.2 430.20 1189.7 429.99 1190.2 429.56 c -h -1193.7 431.00 m -1193.7 424.64 l -1194.9 424.64 l -1194.9 425.83 l -1195.5 424.94 1196.2 424.50 1197.1 424.50 c -1197.7 424.50 1198.1 424.67 1198.4 425.02 c -1198.8 425.37 1198.9 425.84 1198.9 426.43 c -1198.9 431.00 l -1197.8 431.00 l -1197.8 426.80 l -1197.8 426.33 1197.7 426.00 1197.6 425.79 c -1197.4 425.59 1197.2 425.49 1196.9 425.49 c -1196.2 425.49 1195.5 425.96 1194.9 426.88 c -1194.9 431.00 l -h -1203.1 431.15 m -1202.5 431.15 1202.1 430.98 1201.7 430.64 c -1201.4 430.31 1201.2 429.84 1201.2 429.24 c -1201.2 425.50 l -1200.4 425.50 l -1200.4 424.64 l -1201.2 424.64 l -1201.2 423.48 l -1202.4 423.37 l -1202.4 424.64 l -1204.0 424.64 l -1204.0 425.50 l -1202.4 425.50 l -1202.4 429.03 l -1202.4 429.86 1202.7 430.28 1203.5 430.28 c -1203.6 430.28 1203.8 430.25 1204.0 430.20 c -1204.0 431.00 l -1203.7 431.10 1203.4 431.15 1203.1 431.15 c -h -1204.5 433.02 m -1204.5 432.15 l -1210.5 432.15 l -1210.5 433.02 l -h -1211.7 431.00 m -1211.7 424.64 l -1212.8 424.64 l -1212.8 431.00 l -h -1211.7 423.48 m -1211.7 422.33 l -1212.8 422.33 l -1212.8 423.48 l -h -1219.2 431.00 m -1219.2 429.80 l -1218.8 430.70 1218.0 431.15 1217.1 431.15 c -1216.3 431.15 1215.7 430.87 1215.3 430.31 c -1214.9 429.75 1214.6 428.99 1214.6 428.02 c -1214.6 426.96 1214.9 426.11 1215.4 425.46 c -1215.9 424.82 1216.5 424.50 1217.3 424.50 c -1218.1 424.50 1218.7 424.79 1219.2 425.36 c -1219.2 421.75 l -1220.4 421.75 l -1220.4 431.00 l -h -1219.2 426.15 m -1218.6 425.63 1218.1 425.36 1217.5 425.36 c -1216.4 425.36 1215.9 426.21 1215.9 427.90 c -1215.9 429.39 1216.4 430.13 1217.3 430.13 c -1218.0 430.13 1218.6 429.78 1219.2 429.08 c -h -1222.0 431.94 m -1222.0 432.73 l -1222.8 432.16 1223.5 431.38 1224.0 430.39 c -1224.5 429.41 1224.7 428.36 1224.7 427.24 c -1224.7 426.12 1224.5 425.08 1224.0 424.09 c -1223.5 423.10 1222.8 422.32 1222.0 421.75 c -1222.0 422.54 l -1222.5 423.17 1223.0 423.84 1223.2 424.56 c -1223.4 425.28 1223.6 426.17 1223.6 427.24 c -1223.6 428.31 1223.4 429.20 1223.2 429.92 c -1223.0 430.64 1222.5 431.31 1222.0 431.94 c -h -f -867.06 327.15 m -866.48 327.15 866.02 326.98 865.69 326.64 c -865.37 326.31 865.20 325.84 865.20 325.24 c -865.20 321.50 l -864.40 321.50 l -864.40 320.64 l -865.20 320.64 l -865.20 319.48 l -866.36 319.37 l -866.36 320.64 l -868.02 320.64 l -868.02 321.50 l -866.36 321.50 l -866.36 325.03 l -866.36 325.86 866.71 326.28 867.43 326.28 c -867.59 326.28 867.77 326.25 867.99 326.20 c -867.99 327.00 l -867.63 327.10 867.33 327.15 867.06 327.15 c -h -874.31 326.79 m -873.53 327.03 872.87 327.15 872.32 327.15 c -871.38 327.15 870.62 326.83 870.03 326.21 c -869.43 325.59 869.14 324.78 869.14 323.79 c -869.14 322.82 869.40 322.03 869.92 321.42 c -870.44 320.80 871.11 320.49 871.92 320.49 c -872.69 320.49 873.29 320.76 873.71 321.31 c -874.13 321.86 874.34 322.63 874.34 323.64 c -874.33 324.00 l -870.32 324.00 l -870.48 325.51 871.22 326.27 872.54 326.27 c -873.02 326.27 873.61 326.14 874.31 325.88 c -h -870.37 323.13 m -873.18 323.13 l -873.18 321.95 872.73 321.36 871.85 321.36 c -870.96 321.36 870.47 321.95 870.37 323.13 c -h -876.33 327.00 m -876.33 320.64 l -877.48 320.64 l -877.48 321.83 l -878.09 320.94 878.84 320.50 879.72 320.50 c -880.27 320.50 880.71 320.67 881.04 321.02 c -881.37 321.37 881.53 321.84 881.53 322.43 c -881.53 327.00 l -880.38 327.00 l -880.38 322.80 l -880.38 322.33 880.31 322.00 880.17 321.79 c -880.03 321.59 879.80 321.49 879.48 321.49 c -878.77 321.49 878.11 321.96 877.48 322.88 c -877.48 327.00 l -h -887.06 326.19 m -886.37 326.83 885.70 327.15 885.06 327.15 c -884.53 327.15 884.09 326.98 883.75 326.65 c -883.40 326.32 883.22 325.90 883.22 325.40 c -883.22 324.71 883.52 324.17 884.10 323.80 c -884.68 323.42 885.52 323.24 886.61 323.24 c -886.89 323.24 l -886.89 322.47 l -886.89 321.73 886.51 321.36 885.75 321.36 c -885.14 321.36 884.48 321.55 883.78 321.93 c -883.78 320.97 l -884.55 320.65 885.28 320.50 885.96 320.50 c -886.67 320.50 887.20 320.66 887.53 320.98 c -887.87 321.30 888.04 321.79 888.04 322.47 c -888.04 325.35 l -888.04 326.01 888.24 326.34 888.65 326.34 c -888.70 326.34 888.78 326.34 888.87 326.32 c -888.96 326.96 l -888.69 327.08 888.40 327.15 888.09 327.15 c -887.55 327.15 887.21 326.83 887.06 326.19 c -h -886.89 325.56 m -886.89 323.92 l -886.50 323.91 l -885.87 323.91 885.36 324.03 884.96 324.27 c -884.57 324.51 884.38 324.82 884.38 325.21 c -884.38 325.49 884.48 325.72 884.67 325.92 c -884.87 326.11 885.11 326.20 885.39 326.20 c -885.87 326.20 886.37 325.99 886.89 325.56 c -h -890.40 327.00 m -890.40 320.64 l -891.56 320.64 l -891.56 321.83 l -892.17 320.94 892.91 320.50 893.79 320.50 c -894.35 320.50 894.79 320.67 895.11 321.02 c -895.44 321.37 895.61 321.84 895.61 322.43 c -895.61 327.00 l -894.45 327.00 l -894.45 322.80 l -894.45 322.33 894.38 322.00 894.24 321.79 c -894.10 321.59 893.88 321.49 893.55 321.49 c -892.85 321.49 892.18 321.96 891.56 322.88 c -891.56 327.00 l -h -899.76 327.15 m -899.17 327.15 898.72 326.98 898.39 326.64 c -898.06 326.31 897.90 325.84 897.90 325.24 c -897.90 321.50 l -897.10 321.50 l -897.10 320.64 l -897.90 320.64 l -897.90 319.48 l -899.05 319.37 l -899.05 320.64 l -900.71 320.64 l -900.71 321.50 l -899.05 321.50 l -899.05 325.03 l -899.05 325.86 899.41 326.28 900.13 326.28 c -900.28 326.28 900.47 326.25 900.69 326.20 c -900.69 327.00 l -900.33 327.10 900.02 327.15 899.76 327.15 c -h -906.28 325.05 m -906.28 324.18 l -913.22 324.18 l -913.22 325.05 l -h -906.28 322.88 m -906.28 322.01 l -913.22 322.01 l -913.22 322.88 l -h -919.47 329.31 m -919.47 320.64 l -920.62 320.64 l -920.62 321.83 l -921.10 320.94 921.81 320.50 922.75 320.50 c -923.52 320.50 924.12 320.78 924.56 321.33 c -925.00 321.89 925.22 322.66 925.22 323.62 c -925.22 324.68 924.97 325.53 924.47 326.18 c -923.97 326.82 923.32 327.15 922.51 327.15 c -921.75 327.15 921.12 326.86 920.62 326.28 c -920.62 329.31 l -h -920.62 325.48 m -921.22 326.01 921.79 326.28 922.32 326.28 c -923.43 326.28 923.99 325.43 923.99 323.74 c -923.99 322.25 923.50 321.50 922.51 321.50 c -921.87 321.50 921.24 321.85 920.62 322.55 c -h -930.30 326.19 m -929.61 326.83 928.95 327.15 928.31 327.15 c -927.78 327.15 927.34 326.98 926.99 326.65 c -926.65 326.32 926.47 325.90 926.47 325.40 c -926.47 324.71 926.76 324.17 927.35 323.80 c -927.93 323.42 928.77 323.24 929.86 323.24 c -930.13 323.24 l -930.13 322.47 l -930.13 321.73 929.76 321.36 929.00 321.36 c -928.39 321.36 927.73 321.55 927.02 321.93 c -927.02 320.97 l -927.80 320.65 928.53 320.50 929.21 320.50 c -929.92 320.50 930.44 320.66 930.78 320.98 c -931.12 321.30 931.29 321.79 931.29 322.47 c -931.29 325.35 l -931.29 326.01 931.49 326.34 931.90 326.34 c -931.95 326.34 932.02 326.34 932.12 326.32 c -932.20 326.96 l -931.94 327.08 931.65 327.15 931.34 327.15 c -930.80 327.15 930.45 326.83 930.30 326.19 c -h -930.13 325.56 m -930.13 323.92 l -929.75 323.91 l -929.12 323.91 928.60 324.03 928.21 324.27 c -927.82 324.51 927.63 324.82 927.63 325.21 c -927.63 325.49 927.72 325.72 927.92 325.92 c -928.12 326.11 928.35 326.20 928.63 326.20 c -929.12 326.20 929.62 325.99 930.13 325.56 c -h -933.65 327.00 m -933.65 320.64 l -934.80 320.64 l -934.80 321.83 l -935.26 320.94 935.93 320.50 936.80 320.50 c -936.91 320.50 937.04 320.51 937.17 320.53 c -937.17 321.60 l -936.97 321.54 936.79 321.50 936.64 321.50 c -935.91 321.50 935.30 321.94 934.80 322.80 c -934.80 327.00 l -h -940.25 327.15 m -939.72 327.15 939.08 327.02 938.33 326.78 c -938.33 325.72 l -939.08 326.09 939.74 326.28 940.29 326.28 c -940.63 326.28 940.90 326.19 941.12 326.01 c -941.34 325.83 941.45 325.61 941.45 325.34 c -941.45 324.94 941.14 324.62 940.53 324.36 c -939.86 324.07 l -938.86 323.66 938.36 323.06 938.36 322.28 c -938.36 321.73 938.56 321.29 938.95 320.97 c -939.34 320.66 939.88 320.50 940.56 320.50 c -940.92 320.50 941.36 320.54 941.88 320.64 c -942.12 320.69 l -942.12 321.65 l -941.48 321.46 940.97 321.36 940.59 321.36 c -939.85 321.36 939.47 321.63 939.47 322.17 c -939.47 322.52 939.76 322.81 940.32 323.05 c -940.88 323.29 l -941.50 323.55 941.95 323.83 942.21 324.13 c -942.47 324.42 942.60 324.79 942.60 325.23 c -942.60 325.79 942.38 326.25 941.94 326.61 c -941.50 326.97 940.94 327.15 940.25 327.15 c -h -949.34 326.79 m -948.57 327.03 947.91 327.15 947.36 327.15 c -946.42 327.15 945.65 326.83 945.06 326.21 c -944.47 325.59 944.17 324.78 944.17 323.79 c -944.17 322.82 944.43 322.03 944.96 321.42 c -945.48 320.80 946.14 320.49 946.96 320.49 c -947.73 320.49 948.32 320.76 948.74 321.31 c -949.16 321.86 949.37 322.63 949.37 323.64 c -949.37 324.00 l -945.35 324.00 l -945.52 325.51 946.26 326.27 947.57 326.27 c -948.05 326.27 948.64 326.14 949.34 325.88 c -h -945.40 323.13 m -948.21 323.13 l -948.21 321.95 947.77 321.36 946.89 321.36 c -946.00 321.36 945.51 321.95 945.40 323.13 c -h -953.68 327.94 m -953.68 328.73 l -952.83 328.16 952.17 327.38 951.67 326.39 c -951.18 325.41 950.93 324.36 950.93 323.24 c -950.93 322.12 951.18 321.08 951.67 320.09 c -952.17 319.10 952.83 318.32 953.68 317.75 c -953.68 318.54 l -953.10 319.17 952.69 319.84 952.45 320.56 c -952.21 321.28 952.08 322.17 952.08 323.24 c -952.08 324.31 952.21 325.20 952.45 325.92 c -952.69 326.64 953.10 327.31 953.68 327.94 c -h -959.24 327.00 m -959.24 325.80 l -958.63 326.70 957.89 327.15 957.01 327.15 c -956.46 327.15 956.02 326.97 955.69 326.62 c -955.36 326.27 955.20 325.80 955.20 325.21 c -955.20 320.64 l -956.35 320.64 l -956.35 324.83 l -956.35 325.31 956.42 325.65 956.56 325.85 c -956.70 326.05 956.93 326.15 957.25 326.15 c -957.96 326.15 958.62 325.69 959.24 324.76 c -959.24 320.64 l -960.40 320.64 l -960.40 327.00 l -h -962.71 327.00 m -962.71 320.64 l -963.87 320.64 l -963.87 321.83 l -964.32 320.94 964.99 320.50 965.86 320.50 c -965.98 320.50 966.10 320.51 966.23 320.53 c -966.23 321.60 l -966.03 321.54 965.85 321.50 965.70 321.50 c -964.97 321.50 964.36 321.94 963.87 322.80 c -963.87 327.00 l -h -967.62 327.00 m -967.62 317.75 l -968.78 317.75 l -968.78 327.00 l -h -970.37 327.94 m -970.37 328.73 l -971.21 328.16 971.88 327.38 972.38 326.39 c -972.87 325.41 973.12 324.36 973.12 323.24 c -973.12 322.12 972.87 321.08 972.38 320.09 c -971.88 319.10 971.21 318.32 970.37 317.75 c -970.37 318.54 l -970.95 319.17 971.35 319.84 971.60 320.56 c -971.84 321.28 971.96 322.17 971.96 323.24 c -971.96 324.31 971.84 325.20 971.60 325.92 c -971.35 326.64 970.95 327.31 970.37 327.94 c -h -f -17.000 7.0000 m -121.00 7.0000 l -121.00 44.000 l -17.000 44.000 l -17.000 7.0000 l -h -f -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -15.000 5.0000 m -117.00 5.0000 l -117.00 40.000 l -15.000 40.000 l -15.000 5.0000 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -15.500 5.5000 m -117.50 5.5000 l -117.50 40.500 l -15.500 40.500 l -15.500 5.5000 l -h -S -36.500 24.500 m -96.500 24.500 l -S -39.639 22.146 m -38.779 22.146 38.066 21.828 37.500 21.191 c -36.934 20.555 36.650 19.752 36.650 18.783 c -36.650 17.748 36.931 16.941 37.491 16.363 c -38.052 15.785 38.834 15.496 39.838 15.496 c -40.334 15.496 40.889 15.564 41.502 15.701 c -41.502 16.668 l -40.850 16.477 40.318 16.381 39.908 16.381 c -39.318 16.381 38.845 16.603 38.487 17.046 c -38.130 17.489 37.951 18.080 37.951 18.818 c -37.951 19.533 38.135 20.111 38.502 20.553 c -38.869 20.994 39.350 21.215 39.943 21.215 c -40.471 21.215 41.014 21.080 41.572 20.811 c -41.572 21.807 l -40.826 22.033 40.182 22.146 39.639 22.146 c -h -43.301 22.000 m -43.301 12.748 l -44.455 12.748 l -44.455 22.000 l -h -46.770 22.000 m -46.770 15.637 l -47.924 15.637 l -47.924 22.000 l -h -46.770 14.482 m -46.770 13.328 l -47.924 13.328 l -47.924 14.482 l -h -54.902 21.795 m -54.129 22.029 53.467 22.146 52.916 22.146 c -51.979 22.146 51.214 21.835 50.622 21.212 c -50.030 20.589 49.734 19.781 49.734 18.789 c -49.734 17.824 49.995 17.033 50.517 16.416 c -51.038 15.799 51.705 15.490 52.518 15.490 c -53.287 15.490 53.882 15.764 54.302 16.311 c -54.722 16.857 54.932 17.635 54.932 18.643 c -54.926 19.000 l -50.912 19.000 l -51.080 20.512 51.820 21.268 53.133 21.268 c -53.613 21.268 54.203 21.139 54.902 20.881 c -h -50.965 18.133 m -53.771 18.133 l -53.771 16.949 53.330 16.357 52.447 16.357 c -51.561 16.357 51.066 16.949 50.965 18.133 c -h -56.924 22.000 m -56.924 15.637 l -58.078 15.637 l -58.078 16.832 l -58.688 15.941 59.434 15.496 60.316 15.496 c -60.867 15.496 61.307 15.671 61.635 16.021 c -61.963 16.370 62.127 16.840 62.127 17.430 c -62.127 22.000 l -60.973 22.000 l -60.973 17.805 l -60.973 17.332 60.903 16.995 60.765 16.794 c -60.626 16.593 60.396 16.492 60.076 16.492 c -59.369 16.492 58.703 16.955 58.078 17.881 c -58.078 22.000 l -h -66.281 22.146 m -65.695 22.146 65.238 21.979 64.910 21.643 c -64.582 21.307 64.418 20.840 64.418 20.242 c -64.418 16.504 l -63.621 16.504 l -63.621 15.637 l -64.418 15.637 l -64.418 14.482 l -65.572 14.371 l -65.572 15.637 l -67.236 15.637 l -67.236 16.504 l -65.572 16.504 l -65.572 20.031 l -65.572 20.863 65.932 21.279 66.650 21.279 c -66.803 21.279 66.988 21.254 67.207 21.203 c -67.207 22.000 l -66.852 22.098 66.543 22.146 66.281 22.146 c -h -69.023 22.000 m -69.023 20.846 l -70.178 20.846 l -70.178 22.000 l -h -69.023 16.797 m -69.023 15.637 l -70.178 15.637 l -70.178 16.797 l -h -72.551 13.328 m -73.781 13.328 l -73.781 18.801 l -73.781 19.672 73.942 20.306 74.265 20.702 c -74.587 21.099 75.100 21.297 75.803 21.297 c -76.490 21.297 76.978 21.110 77.265 20.737 c -77.552 20.364 77.695 19.732 77.695 18.842 c -77.695 13.328 l -78.773 13.328 l -78.773 18.824 l -78.773 20.008 78.530 20.869 78.044 21.408 c -77.558 21.947 76.783 22.217 75.721 22.217 c -74.639 22.217 73.840 21.938 73.324 21.379 c -72.809 20.820 72.551 19.957 72.551 18.789 c -h -82.658 22.146 m -82.131 22.146 81.490 22.023 80.736 21.777 c -80.736 20.717 l -81.490 21.092 82.146 21.279 82.705 21.279 c -83.037 21.279 83.312 21.189 83.531 21.010 c -83.750 20.830 83.859 20.605 83.859 20.336 c -83.859 19.941 83.553 19.615 82.939 19.357 c -82.266 19.070 l -81.270 18.656 80.771 18.061 80.771 17.283 c -80.771 16.729 80.968 16.292 81.360 15.974 c -81.753 15.655 82.291 15.496 82.975 15.496 c -83.330 15.496 83.770 15.545 84.293 15.643 c -84.533 15.689 l -84.533 16.650 l -83.889 16.459 83.377 16.363 82.998 16.363 c -82.256 16.363 81.885 16.633 81.885 17.172 c -81.885 17.520 82.166 17.812 82.729 18.051 c -83.285 18.285 l -83.914 18.551 84.359 18.831 84.621 19.126 c -84.883 19.421 85.014 19.789 85.014 20.230 c -85.014 20.789 84.793 21.248 84.352 21.607 c -83.910 21.967 83.346 22.146 82.658 22.146 c -h -91.752 21.795 m -90.979 22.029 90.316 22.146 89.766 22.146 c -88.828 22.146 88.063 21.835 87.472 21.212 c -86.880 20.589 86.584 19.781 86.584 18.789 c -86.584 17.824 86.845 17.033 87.366 16.416 c -87.888 15.799 88.555 15.490 89.367 15.490 c -90.137 15.490 90.731 15.764 91.151 16.311 c -91.571 16.857 91.781 17.635 91.781 18.643 c -91.775 19.000 l -87.762 19.000 l -87.930 20.512 88.670 21.268 89.982 21.268 c -90.463 21.268 91.053 21.139 91.752 20.881 c -h -87.814 18.133 m -90.621 18.133 l -90.621 16.949 90.180 16.357 89.297 16.357 c -88.410 16.357 87.916 16.949 87.814 18.133 c -h -93.773 22.000 m -93.773 15.637 l -94.928 15.637 l -94.928 16.832 l -95.385 15.941 96.049 15.496 96.920 15.496 c -97.037 15.496 97.160 15.506 97.289 15.525 c -97.289 16.604 l -97.090 16.537 96.914 16.504 96.762 16.504 c -96.031 16.504 95.420 16.938 94.928 17.805 c -94.928 22.000 l -h -f -483.00 7.0000 m -592.00 7.0000 l -592.00 44.000 l -483.00 44.000 l -483.00 7.0000 l -h -f -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -481.00 5.0000 m -588.00 5.0000 l -588.00 40.000 l -481.00 40.000 l -481.00 5.0000 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -481.50 5.5000 m -588.50 5.5000 l -588.50 40.500 l -481.50 40.500 l -481.50 5.5000 l -h -S -487.50 24.500 m -582.50 24.500 l -S -488.15 22.000 m -488.15 12.748 l -489.31 12.748 l -489.31 18.725 l -492.00 15.637 l -493.25 15.637 l -490.67 18.607 l -493.78 22.000 l -492.30 22.000 l -489.31 18.736 l -489.31 22.000 l -h -499.83 21.795 m -499.06 22.029 498.40 22.146 497.85 22.146 c -496.91 22.146 496.14 21.835 495.55 21.212 c -494.96 20.589 494.66 19.781 494.66 18.789 c -494.66 17.824 494.92 17.033 495.45 16.416 c -495.97 15.799 496.63 15.490 497.45 15.490 c -498.22 15.490 498.81 15.764 499.23 16.311 c -499.65 16.857 499.86 17.635 499.86 18.643 c -499.86 19.000 l -495.84 19.000 l -496.01 20.512 496.75 21.268 498.06 21.268 c -498.54 21.268 499.13 21.139 499.83 20.881 c -h -495.89 18.133 m -498.70 18.133 l -498.70 16.949 498.26 16.357 497.38 16.357 c -496.49 16.357 496.00 16.949 495.89 18.133 c -h -502.22 24.314 m -503.25 22.000 l -500.79 15.637 l -502.04 15.637 l -503.86 20.430 l -505.81 15.637 l -506.90 15.637 l -503.42 24.314 l -h -509.81 22.146 m -509.28 22.146 508.64 22.023 507.89 21.777 c -507.89 20.717 l -508.64 21.092 509.30 21.279 509.86 21.279 c -510.19 21.279 510.46 21.189 510.68 21.010 c -510.90 20.830 511.01 20.605 511.01 20.336 c -511.01 19.941 510.71 19.615 510.09 19.357 c -509.42 19.070 l -508.42 18.656 507.92 18.061 507.92 17.283 c -507.92 16.729 508.12 16.292 508.51 15.974 c -508.91 15.655 509.44 15.496 510.13 15.496 c -510.48 15.496 510.92 15.545 511.45 15.643 c -511.69 15.689 l -511.69 16.650 l -511.04 16.459 510.53 16.363 510.15 16.363 c -509.41 16.363 509.04 16.633 509.04 17.172 c -509.04 17.520 509.32 17.812 509.88 18.051 c -510.44 18.285 l -511.07 18.551 511.51 18.831 511.77 19.126 c -512.04 19.421 512.17 19.789 512.17 20.230 c -512.17 20.789 511.95 21.248 511.50 21.607 c -511.06 21.967 510.50 22.146 509.81 22.146 c -h -516.15 22.146 m -515.56 22.146 515.11 21.979 514.78 21.643 c -514.45 21.307 514.29 20.840 514.29 20.242 c -514.29 16.504 l -513.49 16.504 l -513.49 15.637 l -514.29 15.637 l -514.29 14.482 l -515.44 14.371 l -515.44 15.637 l -517.11 15.637 l -517.11 16.504 l -515.44 16.504 l -515.44 20.031 l -515.44 20.863 515.80 21.279 516.52 21.279 c -516.67 21.279 516.86 21.254 517.08 21.203 c -517.08 22.000 l -516.72 22.098 516.41 22.146 516.15 22.146 c -h -521.22 22.146 m -520.31 22.146 519.58 21.845 519.04 21.241 c -518.50 20.638 518.22 19.830 518.22 18.818 c -518.22 17.795 518.50 16.985 519.04 16.390 c -519.59 15.794 520.33 15.496 521.26 15.496 c -522.19 15.496 522.93 15.794 523.48 16.390 c -524.02 16.985 524.29 17.791 524.29 18.807 c -524.29 19.846 524.02 20.662 523.47 21.256 c -522.93 21.850 522.18 22.146 521.22 22.146 c -h -521.24 21.279 m -522.46 21.279 523.07 20.455 523.07 18.807 c -523.07 17.178 522.47 16.363 521.26 16.363 c -520.06 16.363 519.46 17.182 519.46 18.818 c -519.46 20.459 520.05 21.279 521.24 21.279 c -h -526.10 22.000 m -526.10 15.637 l -527.25 15.637 l -527.25 16.832 l -527.86 15.941 528.61 15.496 529.49 15.496 c -530.04 15.496 530.48 15.671 530.81 16.021 c -531.14 16.370 531.30 16.840 531.30 17.430 c -531.30 22.000 l -530.15 22.000 l -530.15 17.805 l -530.15 17.332 530.08 16.995 529.94 16.794 c -529.80 16.593 529.57 16.492 529.25 16.492 c -528.54 16.492 527.88 16.955 527.25 17.881 c -527.25 22.000 l -h -538.21 21.795 m -537.44 22.029 536.78 22.146 536.22 22.146 c -535.29 22.146 534.52 21.835 533.93 21.212 c -533.34 20.589 533.04 19.781 533.04 18.789 c -533.04 17.824 533.30 17.033 533.83 16.416 c -534.35 15.799 535.01 15.490 535.83 15.490 c -536.60 15.490 537.19 15.764 537.61 16.311 c -538.03 16.857 538.24 17.635 538.24 18.643 c -538.23 19.000 l -534.22 19.000 l -534.39 20.512 535.13 21.268 536.44 21.268 c -536.92 21.268 537.51 21.139 538.21 20.881 c -h -534.27 18.133 m -537.08 18.133 l -537.08 16.949 536.64 16.357 535.76 16.357 c -534.87 16.357 534.38 16.949 534.27 18.133 c -h -540.40 22.000 m -540.40 20.846 l -541.55 20.846 l -541.55 22.000 l -h -540.40 16.797 m -540.40 15.637 l -541.55 15.637 l -541.55 16.797 l -h -545.69 22.217 m -545.11 22.217 544.37 22.090 543.46 21.836 c -543.46 20.617 l -544.44 21.070 545.24 21.297 545.87 21.297 c -546.35 21.297 546.74 21.170 547.04 20.916 c -547.33 20.662 547.48 20.328 547.48 19.914 c -547.48 19.574 547.38 19.285 547.19 19.047 c -547.00 18.809 546.64 18.543 546.12 18.250 c -545.52 17.904 l -544.79 17.482 544.26 17.085 543.96 16.712 c -543.66 16.339 543.51 15.904 543.51 15.408 c -543.51 14.740 543.75 14.190 544.23 13.759 c -544.72 13.327 545.34 13.111 546.09 13.111 c -546.75 13.111 547.46 13.223 548.20 13.445 c -548.20 14.570 l -547.29 14.211 546.61 14.031 546.16 14.031 c -545.73 14.031 545.38 14.145 545.10 14.371 c -544.82 14.598 544.69 14.883 544.69 15.227 c -544.69 15.516 544.79 15.771 544.99 15.994 c -545.19 16.217 545.56 16.482 546.10 16.791 c -546.72 17.143 l -547.47 17.568 548.00 17.971 548.29 18.350 c -548.59 18.729 548.74 19.184 548.74 19.715 c -548.74 20.469 548.46 21.074 547.91 21.531 c -547.35 21.988 546.61 22.217 545.69 22.217 c -h -555.16 21.795 m -554.38 22.029 553.72 22.146 553.17 22.146 c -552.23 22.146 551.47 21.835 550.88 21.212 c -550.28 20.589 549.99 19.781 549.99 18.789 c -549.99 17.824 550.25 17.033 550.77 16.416 c -551.29 15.799 551.96 15.490 552.77 15.490 c -553.54 15.490 554.14 15.764 554.56 16.311 c -554.98 16.857 555.19 17.635 555.19 18.643 c -555.18 19.000 l -551.17 19.000 l -551.33 20.512 552.07 21.268 553.39 21.268 c -553.87 21.268 554.46 21.139 555.16 20.881 c -h -551.22 18.133 m -554.03 18.133 l -554.03 16.949 553.58 16.357 552.70 16.357 c -551.81 16.357 551.32 16.949 551.22 18.133 c -h -557.18 22.000 m -557.18 15.637 l -558.33 15.637 l -558.33 16.832 l -558.79 15.941 559.45 15.496 560.32 15.496 c -560.44 15.496 560.56 15.506 560.69 15.525 c -560.69 16.604 l -560.49 16.537 560.32 16.504 560.17 16.504 c -559.44 16.504 558.82 16.938 558.33 17.805 c -558.33 22.000 l -h -563.41 22.000 m -561.04 15.637 l -562.19 15.637 l -564.04 20.588 l -566.00 15.637 l -567.07 15.637 l -564.56 22.000 l -h -568.30 22.000 m -568.30 15.637 l -569.45 15.637 l -569.45 22.000 l -h -568.30 14.482 m -568.30 13.328 l -569.45 13.328 l -569.45 14.482 l -h -574.25 22.146 m -573.39 22.146 572.68 21.828 572.11 21.191 c -571.55 20.555 571.26 19.752 571.26 18.783 c -571.26 17.748 571.54 16.941 572.10 16.363 c -572.67 15.785 573.45 15.496 574.45 15.496 c -574.95 15.496 575.50 15.564 576.12 15.701 c -576.12 16.668 l -575.46 16.477 574.93 16.381 574.52 16.381 c -573.93 16.381 573.46 16.603 573.10 17.046 c -572.74 17.489 572.56 18.080 572.56 18.818 c -572.56 19.533 572.75 20.111 573.12 20.553 c -573.48 20.994 573.96 21.215 574.56 21.215 c -575.08 21.215 575.63 21.080 576.19 20.811 c -576.19 21.807 l -575.44 22.033 574.79 22.146 574.25 22.146 c -h -582.58 21.795 m -581.80 22.029 581.14 22.146 580.59 22.146 c -579.65 22.146 578.89 21.835 578.30 21.212 c -577.71 20.589 577.41 19.781 577.41 18.789 c -577.41 17.824 577.67 17.033 578.19 16.416 c -578.71 15.799 579.38 15.490 580.19 15.490 c -580.96 15.490 581.56 15.764 581.98 16.311 c -582.40 16.857 582.61 17.635 582.61 18.643 c -582.60 19.000 l -578.59 19.000 l -578.76 20.512 579.50 21.268 580.81 21.268 c -581.29 21.268 581.88 21.139 582.58 20.881 c -h -578.64 18.133 m -581.45 18.133 l -581.45 16.949 581.01 16.357 580.12 16.357 c -579.24 16.357 578.74 16.949 578.64 18.133 c -h -f -805.00 7.0000 m -909.00 7.0000 l -909.00 44.000 l -805.00 44.000 l -805.00 7.0000 l -h -f -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -803.00 5.0000 m -905.00 5.0000 l -905.00 40.000 l -803.00 40.000 l -803.00 5.0000 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -803.50 5.5000 m -905.50 5.5000 l -905.50 40.500 l -803.50 40.500 l -803.50 5.5000 l -h -S -819.50 24.500 m -890.50 24.500 l -S -820.15 22.000 m -820.15 15.637 l -821.31 15.637 l -821.31 16.832 l -821.92 15.941 822.66 15.496 823.55 15.496 c -824.10 15.496 824.54 15.671 824.87 16.021 c -825.19 16.370 825.36 16.840 825.36 17.430 c -825.36 22.000 l -824.20 22.000 l -824.20 17.805 l -824.20 17.332 824.13 16.995 824.00 16.794 c -823.86 16.593 823.63 16.492 823.31 16.492 c -822.60 16.492 821.93 16.955 821.31 17.881 c -821.31 22.000 l -h -830.09 22.146 m -829.18 22.146 828.46 21.845 827.91 21.241 c -827.37 20.638 827.10 19.830 827.10 18.818 c -827.10 17.795 827.37 16.985 827.92 16.390 c -828.46 15.794 829.20 15.496 830.13 15.496 c -831.07 15.496 831.81 15.794 832.35 16.390 c -832.90 16.985 833.17 17.791 833.17 18.807 c -833.17 19.846 832.89 20.662 832.35 21.256 c -831.80 21.850 831.05 22.146 830.09 22.146 c -h -830.11 21.279 m -831.33 21.279 831.94 20.455 831.94 18.807 c -831.94 17.178 831.34 16.363 830.13 16.363 c -828.93 16.363 828.33 17.182 828.33 18.818 c -828.33 20.459 828.92 21.279 830.11 21.279 c -h -836.29 22.000 m -833.92 15.637 l -835.08 15.637 l -836.93 20.588 l -838.88 15.637 l -839.96 15.637 l -837.45 22.000 l -h -844.46 21.191 m -843.77 21.828 843.11 22.146 842.47 22.146 c -841.94 22.146 841.50 21.981 841.15 21.651 c -840.81 21.321 840.63 20.904 840.63 20.400 c -840.63 19.705 840.92 19.171 841.51 18.798 c -842.09 18.425 842.93 18.238 844.02 18.238 c -844.29 18.238 l -844.29 17.471 l -844.29 16.732 843.92 16.363 843.16 16.363 c -842.55 16.363 841.89 16.551 841.18 16.926 c -841.18 15.971 l -841.96 15.654 842.69 15.496 843.37 15.496 c -844.08 15.496 844.60 15.656 844.94 15.977 c -845.28 16.297 845.45 16.795 845.45 17.471 c -845.45 20.354 l -845.45 21.014 845.65 21.344 846.06 21.344 c -846.11 21.344 846.18 21.336 846.28 21.320 c -846.36 21.959 l -846.10 22.084 845.81 22.146 845.50 22.146 c -844.96 22.146 844.61 21.828 844.46 21.191 c -h -844.29 20.564 m -844.29 18.918 l -843.91 18.906 l -843.28 18.906 842.76 19.026 842.37 19.267 c -841.98 19.507 841.79 19.822 841.79 20.213 c -841.79 20.490 841.88 20.725 842.08 20.916 c -842.28 21.107 842.51 21.203 842.79 21.203 c -843.28 21.203 843.78 20.990 844.29 20.564 c -h -847.97 22.000 m -847.97 20.846 l -849.13 20.846 l -849.13 22.000 l -h -847.97 16.797 m -847.97 15.637 l -849.13 15.637 l -849.13 16.797 l -h -853.27 22.217 m -852.69 22.217 851.95 22.090 851.04 21.836 c -851.04 20.617 l -852.02 21.070 852.82 21.297 853.45 21.297 c -853.93 21.297 854.32 21.170 854.62 20.916 c -854.91 20.662 855.06 20.328 855.06 19.914 c -855.06 19.574 854.96 19.285 854.77 19.047 c -854.58 18.809 854.22 18.543 853.70 18.250 c -853.10 17.904 l -852.36 17.482 851.84 17.085 851.54 16.712 c -851.24 16.339 851.09 15.904 851.09 15.408 c -851.09 14.740 851.33 14.190 851.81 13.759 c -852.30 13.327 852.91 13.111 853.66 13.111 c -854.33 13.111 855.04 13.223 855.78 13.445 c -855.78 14.570 l -854.87 14.211 854.18 14.031 853.73 14.031 c -853.31 14.031 852.96 14.145 852.68 14.371 c -852.40 14.598 852.26 14.883 852.26 15.227 c -852.26 15.516 852.37 15.771 852.57 15.994 c -852.77 16.217 853.14 16.482 853.68 16.791 c -854.30 17.143 l -855.05 17.568 855.58 17.971 855.87 18.350 c -856.17 18.729 856.32 19.184 856.32 19.715 c -856.32 20.469 856.04 21.074 855.48 21.531 c -854.93 21.988 854.19 22.217 853.27 22.217 c -h -862.73 21.795 m -861.96 22.029 861.30 22.146 860.75 22.146 c -859.81 22.146 859.05 21.835 858.45 21.212 c -857.86 20.589 857.57 19.781 857.57 18.789 c -857.57 17.824 857.83 17.033 858.35 16.416 c -858.87 15.799 859.54 15.490 860.35 15.490 c -861.12 15.490 861.71 15.764 862.13 16.311 c -862.55 16.857 862.76 17.635 862.76 18.643 c -862.76 19.000 l -858.74 19.000 l -858.91 20.512 859.65 21.268 860.96 21.268 c -861.45 21.268 862.04 21.139 862.73 20.881 c -h -858.80 18.133 m -861.60 18.133 l -861.60 16.949 861.16 16.357 860.28 16.357 c -859.39 16.357 858.90 16.949 858.80 18.133 c -h -864.76 22.000 m -864.76 15.637 l -865.91 15.637 l -865.91 16.832 l -866.37 15.941 867.03 15.496 867.90 15.496 c -868.02 15.496 868.14 15.506 868.27 15.525 c -868.27 16.604 l -868.07 16.537 867.90 16.504 867.74 16.504 c -867.01 16.504 866.40 16.938 865.91 17.805 c -865.91 22.000 l -h -870.98 22.000 m -868.62 15.637 l -869.77 15.637 l -871.62 20.588 l -873.57 15.637 l -874.65 15.637 l -872.14 22.000 l -h -875.88 22.000 m -875.88 15.637 l -877.03 15.637 l -877.03 22.000 l -h -875.88 14.482 m -875.88 13.328 l -877.03 13.328 l -877.03 14.482 l -h -881.83 22.146 m -880.97 22.146 880.26 21.828 879.69 21.191 c -879.12 20.555 878.84 19.752 878.84 18.783 c -878.84 17.748 879.12 16.941 879.68 16.363 c -880.24 15.785 881.03 15.496 882.03 15.496 c -882.53 15.496 883.08 15.564 883.69 15.701 c -883.69 16.668 l -883.04 16.477 882.51 16.381 882.10 16.381 c -881.51 16.381 881.04 16.603 880.68 17.046 c -880.32 17.489 880.14 18.080 880.14 18.818 c -880.14 19.533 880.33 20.111 880.69 20.553 c -881.06 20.994 881.54 21.215 882.13 21.215 c -882.66 21.215 883.21 21.080 883.76 20.811 c -883.76 21.807 l -883.02 22.033 882.37 22.146 881.83 22.146 c -h -890.16 21.795 m -889.38 22.029 888.72 22.146 888.17 22.146 c -887.23 22.146 886.47 21.835 885.88 21.212 c -885.28 20.589 884.99 19.781 884.99 18.789 c -884.99 17.824 885.25 17.033 885.77 16.416 c -886.29 15.799 886.96 15.490 887.77 15.490 c -888.54 15.490 889.14 15.764 889.56 16.311 c -889.98 16.857 890.19 17.635 890.19 18.643 c -890.18 19.000 l -886.17 19.000 l -886.33 20.512 887.07 21.268 888.39 21.268 c -888.87 21.268 889.46 21.139 890.16 20.881 c -h -886.22 18.133 m -889.03 18.133 l -889.03 16.949 888.58 16.357 887.70 16.357 c -886.81 16.357 886.32 16.949 886.22 18.133 c -h -f -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -60.000 62.000 m -189.00 62.000 l -189.00 79.000 l -60.000 79.000 l -60.000 62.000 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -2.0000 w -60.500 62.500 m -876.50 62.500 l -876.50 170.50 l -60.500 170.50 l -60.500 62.500 l -h -S -60.500 79.500 m -188.50 79.500 l -S -188.50 79.500 m -190.50 75.500 l -S -190.50 75.500 m -190.50 62.500 l -S -68.484 76.000 m -68.484 74.686 l -67.875 75.668 67.084 76.159 66.110 76.159 c -65.488 76.159 64.997 75.962 64.638 75.568 c -64.278 75.175 64.098 74.637 64.098 73.956 c -64.098 69.030 l -65.977 69.030 l -65.977 73.493 l -65.977 74.284 66.242 74.680 66.771 74.680 c -67.363 74.680 67.934 74.259 68.484 73.417 c -68.484 69.030 l -70.363 69.030 l -70.363 76.000 l -h -72.712 76.000 m -72.712 69.030 l -74.591 69.030 l -74.591 70.344 l -75.204 69.362 75.996 68.872 76.965 68.872 c -77.587 68.872 78.078 69.068 78.438 69.462 c -78.797 69.855 78.977 70.393 78.977 71.074 c -78.977 76.000 l -77.098 76.000 l -77.098 71.538 l -77.098 70.746 76.836 70.351 76.311 70.351 c -75.714 70.351 75.141 70.772 74.591 71.614 c -74.591 76.000 l -h -84.665 75.251 m -84.038 75.856 83.368 76.159 82.652 76.159 c -82.043 76.159 81.548 75.972 81.167 75.600 c -80.786 75.228 80.596 74.745 80.596 74.153 c -80.596 73.383 80.904 72.789 81.519 72.372 c -82.135 71.955 83.016 71.747 84.163 71.747 c -84.665 71.747 l -84.665 71.112 l -84.665 70.389 84.252 70.027 83.427 70.027 c -82.695 70.027 81.954 70.234 81.205 70.649 c -81.205 69.354 l -82.056 69.032 82.898 68.872 83.731 68.872 c -85.555 68.872 86.467 69.597 86.467 71.049 c -86.467 74.134 l -86.467 74.680 86.643 74.953 86.994 74.953 c -87.058 74.953 87.140 74.944 87.242 74.927 c -87.286 75.981 l -86.888 76.099 86.537 76.159 86.232 76.159 c -85.462 76.159 84.967 75.856 84.747 75.251 c -h -84.665 74.242 m -84.665 72.826 l -84.220 72.826 l -83.006 72.826 82.398 73.207 82.398 73.969 c -82.398 74.227 82.486 74.444 82.662 74.619 c -82.837 74.795 83.054 74.883 83.312 74.883 c -83.753 74.883 84.203 74.669 84.665 74.242 c -h -93.202 76.000 m -93.202 74.686 l -92.593 75.668 91.801 76.159 90.828 76.159 c -90.206 76.159 89.715 75.962 89.355 75.568 c -88.996 75.175 88.816 74.637 88.816 73.956 c -88.816 69.030 l -90.695 69.030 l -90.695 73.493 l -90.695 74.284 90.959 74.680 91.488 74.680 c -92.081 74.680 92.652 74.259 93.202 73.417 c -93.202 69.030 l -95.081 69.030 l -95.081 76.000 l -h -101.02 75.962 m -100.57 76.093 100.22 76.159 99.962 76.159 c -98.333 76.159 97.519 75.397 97.519 73.874 c -97.519 70.205 l -96.738 70.205 l -96.738 69.030 l -97.519 69.030 l -97.519 67.856 l -99.397 67.640 l -99.397 69.030 l -100.89 69.030 l -100.89 70.205 l -99.397 70.205 l -99.397 73.626 l -99.397 74.481 99.747 74.908 100.44 74.908 c -100.61 74.908 100.80 74.879 101.02 74.819 c -h -102.70 76.000 m -102.70 65.977 l -104.58 65.977 l -104.58 70.344 l -105.19 69.362 105.98 68.872 106.95 68.872 c -107.57 68.872 108.06 69.068 108.42 69.462 c -108.78 69.855 108.96 70.393 108.96 71.074 c -108.96 76.000 l -107.08 76.000 l -107.08 71.538 l -107.08 70.746 106.82 70.351 106.30 70.351 c -105.70 70.351 105.13 70.772 104.58 71.614 c -104.58 76.000 l -h -116.91 75.765 m -116.02 76.028 115.17 76.159 114.37 76.159 c -113.21 76.159 112.29 75.829 111.62 75.168 c -110.94 74.508 110.61 73.607 110.61 72.464 c -110.61 71.385 110.92 70.517 111.53 69.859 c -112.15 69.201 112.96 68.872 113.97 68.872 c -114.99 68.872 115.74 69.193 116.21 69.836 c -116.68 70.480 116.91 71.497 116.91 72.890 c -112.59 72.890 l -112.71 74.218 113.44 74.883 114.78 74.883 c -115.41 74.883 116.12 74.737 116.91 74.445 c -h -112.56 71.830 m -115.06 71.830 l -115.06 70.640 114.68 70.046 113.91 70.046 c -113.14 70.046 112.69 70.640 112.56 71.830 c -h -118.85 76.000 m -118.85 69.030 l -120.73 69.030 l -120.73 70.344 l -121.35 69.362 122.14 68.872 123.11 68.872 c -123.73 68.872 124.22 69.068 124.58 69.462 c -124.94 69.855 125.12 70.393 125.12 71.074 c -125.12 76.000 l -123.24 76.000 l -123.24 71.538 l -123.24 70.746 122.98 70.351 122.45 70.351 c -121.86 70.351 121.28 70.772 120.73 71.614 c -120.73 76.000 l -h -130.98 75.962 m -130.53 76.093 130.18 76.159 129.92 76.159 c -128.29 76.159 127.48 75.397 127.48 73.874 c -127.48 70.205 l -126.70 70.205 l -126.70 69.030 l -127.48 69.030 l -127.48 67.856 l -129.36 67.640 l -129.36 69.030 l -130.85 69.030 l -130.85 70.205 l -129.36 70.205 l -129.36 73.626 l -129.36 74.481 129.71 74.908 130.41 74.908 c -130.57 74.908 130.76 74.879 130.98 74.819 c -h -132.66 76.000 m -132.66 69.030 l -134.54 69.030 l -134.54 76.000 l -h -132.66 67.856 m -132.66 66.288 l -134.54 66.288 l -134.54 67.856 l -h -142.03 75.848 m -141.25 76.055 140.53 76.159 139.86 76.159 c -138.75 76.159 137.87 75.832 137.23 75.178 c -136.58 74.524 136.26 73.634 136.26 72.509 c -136.26 71.370 136.59 70.480 137.25 69.836 c -137.92 69.193 138.84 68.872 140.01 68.872 c -140.58 68.872 141.23 68.963 141.97 69.145 c -141.97 70.503 l -141.20 70.253 140.59 70.128 140.12 70.128 c -139.56 70.128 139.11 70.344 138.77 70.776 c -138.42 71.208 138.25 71.781 138.25 72.496 c -138.25 73.228 138.44 73.814 138.81 74.254 c -139.18 74.694 139.67 74.915 140.29 74.915 c -140.85 74.915 141.43 74.792 142.03 74.546 c -h -147.22 75.251 m -146.59 75.856 145.92 76.159 145.21 76.159 c -144.60 76.159 144.10 75.972 143.72 75.600 c -143.34 75.228 143.15 74.745 143.15 74.153 c -143.15 73.383 143.46 72.789 144.08 72.372 c -144.69 71.955 145.57 71.747 146.72 71.747 c -147.22 71.747 l -147.22 71.112 l -147.22 70.389 146.81 70.027 145.98 70.027 c -145.25 70.027 144.51 70.234 143.76 70.649 c -143.76 69.354 l -144.61 69.032 145.45 68.872 146.29 68.872 c -148.11 68.872 149.02 69.597 149.02 71.049 c -149.02 74.134 l -149.02 74.680 149.20 74.953 149.55 74.953 c -149.61 74.953 149.70 74.944 149.80 74.927 c -149.84 75.981 l -149.44 76.099 149.09 76.159 148.79 76.159 c -148.02 76.159 147.52 75.856 147.30 75.251 c -h -147.22 74.242 m -147.22 72.826 l -146.78 72.826 l -145.56 72.826 144.95 73.207 144.95 73.969 c -144.95 74.227 145.04 74.444 145.22 74.619 c -145.39 74.795 145.61 74.883 145.87 74.883 c -146.31 74.883 146.76 74.669 147.22 74.242 c -h -155.04 75.962 m -154.59 76.093 154.24 76.159 153.98 76.159 c -152.35 76.159 151.54 75.397 151.54 73.874 c -151.54 70.205 l -150.76 70.205 l -150.76 69.030 l -151.54 69.030 l -151.54 67.856 l -153.42 67.640 l -153.42 69.030 l -154.91 69.030 l -154.91 70.205 l -153.42 70.205 l -153.42 73.626 l -153.42 74.481 153.77 74.908 154.46 74.908 c -154.62 74.908 154.82 74.879 155.04 74.819 c -h -162.39 75.765 m -161.50 76.028 160.65 76.159 159.85 76.159 c -158.69 76.159 157.77 75.829 157.10 75.168 c -156.42 74.508 156.09 73.607 156.09 72.464 c -156.09 71.385 156.40 70.517 157.01 69.859 c -157.63 69.201 158.44 68.872 159.45 68.872 c -160.47 68.872 161.22 69.193 161.69 69.836 c -162.16 70.480 162.39 71.497 162.39 72.890 c -158.07 72.890 l -158.20 74.218 158.93 74.883 160.26 74.883 c -160.89 74.883 161.60 74.737 162.39 74.445 c -h -158.04 71.830 m -160.54 71.830 l -160.54 70.640 160.16 70.046 159.40 70.046 c -158.62 70.046 158.17 70.640 158.04 71.830 c -h -168.72 76.000 m -168.72 74.686 l -168.24 75.668 167.48 76.159 166.44 76.159 c -165.60 76.159 164.95 75.852 164.47 75.238 c -163.99 74.625 163.75 73.780 163.75 72.706 c -163.75 71.538 164.02 70.607 164.56 69.913 c -165.10 69.219 165.82 68.872 166.73 68.872 c -167.46 68.872 168.12 69.159 168.72 69.735 c -168.72 65.977 l -170.61 65.977 l -170.61 76.000 l -h -168.72 70.852 m -168.27 70.344 167.78 70.090 167.27 70.090 c -166.81 70.090 166.44 70.312 166.16 70.757 c -165.89 71.201 165.75 71.798 165.75 72.547 c -165.75 73.973 166.21 74.686 167.12 74.686 c -167.68 74.686 168.21 74.328 168.72 73.613 c -h -171.77 78.272 m -171.77 77.250 l -178.27 77.250 l -178.27 78.272 l -h -184.59 75.848 m -183.81 76.055 183.09 76.159 182.42 76.159 c -181.31 76.159 180.43 75.832 179.79 75.178 c -179.14 74.524 178.82 73.634 178.82 72.509 c -178.82 71.370 179.15 70.480 179.82 69.836 c -180.48 69.193 181.40 68.872 182.57 68.872 c -183.14 68.872 183.79 68.963 184.53 69.145 c -184.53 70.503 l -183.76 70.253 183.15 70.128 182.69 70.128 c -182.12 70.128 181.67 70.344 181.33 70.776 c -180.98 71.208 180.81 71.781 180.81 72.496 c -180.81 73.228 181.00 73.814 181.37 74.254 c -181.74 74.694 182.23 74.915 182.85 74.915 c -183.41 74.915 183.99 74.792 184.59 74.546 c -h -189.78 75.251 m -189.16 75.856 188.48 76.159 187.77 76.159 c -187.16 76.159 186.67 75.972 186.28 75.600 c -185.90 75.228 185.71 74.745 185.71 74.153 c -185.71 73.383 186.02 72.789 186.64 72.372 c -187.25 71.955 188.13 71.747 189.28 71.747 c -189.78 71.747 l -189.78 71.112 l -189.78 70.389 189.37 70.027 188.54 70.027 c -187.81 70.027 187.07 70.234 186.32 70.649 c -186.32 69.354 l -187.17 69.032 188.01 68.872 188.85 68.872 c -190.67 68.872 191.58 69.597 191.58 71.049 c -191.58 74.134 l -191.58 74.680 191.76 74.953 192.11 74.953 c -192.17 74.953 192.26 74.944 192.36 74.927 c -192.40 75.981 l -192.01 76.099 191.65 76.159 191.35 76.159 c -190.58 76.159 190.08 75.856 189.86 75.251 c -h -189.78 74.242 m -189.78 72.826 l -189.34 72.826 l -188.12 72.826 187.52 73.207 187.52 73.969 c -187.52 74.227 187.60 74.444 187.78 74.619 c -187.95 74.795 188.17 74.883 188.43 74.883 c -188.87 74.883 189.32 74.669 189.78 74.242 c -h -194.01 76.000 m -194.01 65.977 l -195.89 65.977 l -195.89 76.000 l -h -198.24 76.000 m -198.24 65.977 l -200.12 65.977 l -200.12 76.000 l -h -f -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -60.000 186.00 m -142.00 186.00 l -142.00 203.00 l -60.000 203.00 l -60.000 186.00 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -60.500 186.50 m -556.50 186.50 l -556.50 254.50 l -60.500 254.50 l -60.500 186.50 l -h -S -60.500 203.50 m -141.50 203.50 l -S -141.50 203.50 m -143.50 199.50 l -S -143.50 199.50 m -143.50 186.50 l -S -67.589 199.25 m -66.963 199.86 66.292 200.16 65.577 200.16 c -64.968 200.16 64.473 199.97 64.092 199.60 c -63.711 199.23 63.521 198.75 63.521 198.15 c -63.521 197.38 63.828 196.79 64.444 196.37 c -65.060 195.96 65.941 195.75 67.088 195.75 c -67.589 195.75 l -67.589 195.11 l -67.589 194.39 67.177 194.03 66.352 194.03 c -65.619 194.03 64.879 194.23 64.130 194.65 c -64.130 193.35 l -64.980 193.03 65.823 192.87 66.656 192.87 c -68.480 192.87 69.392 193.60 69.392 195.05 c -69.392 198.13 l -69.392 198.68 69.568 198.95 69.919 198.95 c -69.982 198.95 70.065 198.94 70.167 198.93 c -70.211 199.98 l -69.813 200.10 69.462 200.16 69.157 200.16 c -68.387 200.16 67.892 199.86 67.672 199.25 c -h -67.589 198.24 m -67.589 196.83 l -67.145 196.83 l -65.931 196.83 65.323 197.21 65.323 197.97 c -65.323 198.23 65.411 198.44 65.587 198.62 c -65.762 198.80 65.979 198.88 66.237 198.88 c -66.677 198.88 67.128 198.67 67.589 198.24 c -h -76.127 200.00 m -76.127 198.69 l -75.518 199.67 74.726 200.16 73.753 200.16 c -73.131 200.16 72.640 199.96 72.280 199.57 c -71.921 199.17 71.741 198.64 71.741 197.96 c -71.741 193.03 l -73.620 193.03 l -73.620 197.49 l -73.620 198.28 73.884 198.68 74.413 198.68 c -75.006 198.68 75.577 198.26 76.127 197.42 c -76.127 193.03 l -78.006 193.03 l -78.006 200.00 l -h -83.947 199.96 m -83.499 200.09 83.145 200.16 82.887 200.16 c -81.258 200.16 80.443 199.40 80.443 197.87 c -80.443 194.20 l -79.663 194.20 l -79.663 193.03 l -80.443 193.03 l -80.443 191.86 l -82.322 191.64 l -82.322 193.03 l -83.814 193.03 l -83.814 194.20 l -82.322 194.20 l -82.322 197.63 l -82.322 198.48 82.671 198.91 83.370 198.91 c -83.530 198.91 83.723 198.88 83.947 198.82 c -h -85.623 200.00 m -85.623 189.98 l -87.502 189.98 l -87.502 194.34 l -88.116 193.36 88.907 192.87 89.876 192.87 c -90.498 192.87 90.989 193.07 91.349 193.46 c -91.708 193.86 91.888 194.39 91.888 195.07 c -91.888 200.00 l -90.009 200.00 l -90.009 195.54 l -90.009 194.75 89.747 194.35 89.222 194.35 c -88.625 194.35 88.052 194.77 87.502 195.61 c -87.502 200.00 l -h -99.835 199.77 m -98.943 200.03 98.096 200.16 97.296 200.16 c -96.133 200.16 95.214 199.83 94.542 199.17 c -93.869 198.51 93.532 197.61 93.532 196.46 c -93.532 195.39 93.840 194.52 94.456 193.86 c -95.072 193.20 95.885 192.87 96.896 192.87 c -97.916 192.87 98.661 193.19 99.131 193.84 c -99.601 194.48 99.835 195.50 99.835 196.89 c -95.513 196.89 l -95.640 198.22 96.370 198.88 97.703 198.88 c -98.333 198.88 99.044 198.74 99.835 198.44 c -h -95.487 195.83 m -97.988 195.83 l -97.988 194.64 97.605 194.05 96.839 194.05 c -96.061 194.05 95.610 194.64 95.487 195.83 c -h -101.78 200.00 m -101.78 193.03 l -103.66 193.03 l -103.66 194.34 l -104.27 193.36 105.06 192.87 106.03 192.87 c -106.65 192.87 107.14 193.07 107.50 193.46 c -107.86 193.86 108.04 194.39 108.04 195.07 c -108.04 200.00 l -106.16 200.00 l -106.16 195.54 l -106.16 194.75 105.90 194.35 105.38 194.35 c -104.78 194.35 104.21 194.77 103.66 195.61 c -103.66 200.00 l -h -113.91 199.96 m -113.46 200.09 113.11 200.16 112.85 200.16 c -111.22 200.16 110.40 199.40 110.40 197.87 c -110.40 194.20 l -109.62 194.20 l -109.62 193.03 l -110.40 193.03 l -110.40 191.86 l -112.28 191.64 l -112.28 193.03 l -113.77 193.03 l -113.77 194.20 l -112.28 194.20 l -112.28 197.63 l -112.28 198.48 112.63 198.91 113.33 198.91 c -113.49 198.91 113.68 198.88 113.91 198.82 c -h -115.58 200.00 m -115.58 193.03 l -117.46 193.03 l -117.46 200.00 l -h -115.58 191.86 m -115.58 190.29 l -117.46 190.29 l -117.46 191.86 l -h -124.95 199.85 m -124.17 200.06 123.45 200.16 122.79 200.16 c -121.68 200.16 120.80 199.83 120.15 199.18 c -119.51 198.52 119.18 197.63 119.18 196.51 c -119.18 195.37 119.52 194.48 120.18 193.84 c -120.84 193.19 121.76 192.87 122.93 192.87 c -123.50 192.87 124.16 192.96 124.90 193.14 c -124.90 194.50 l -124.13 194.25 123.51 194.13 123.05 194.13 c -122.49 194.13 122.03 194.34 121.69 194.78 c -121.35 195.21 121.18 195.78 121.18 196.50 c -121.18 197.23 121.36 197.81 121.73 198.25 c -122.10 198.69 122.60 198.91 123.21 198.91 c -123.78 198.91 124.36 198.79 124.95 198.55 c -h -130.15 199.25 m -129.52 199.86 128.85 200.16 128.13 200.16 c -127.52 200.16 127.03 199.97 126.65 199.60 c -126.27 199.23 126.08 198.75 126.08 198.15 c -126.08 197.38 126.38 196.79 127.00 196.37 c -127.62 195.96 128.50 195.75 129.64 195.75 c -130.15 195.75 l -130.15 195.11 l -130.15 194.39 129.73 194.03 128.91 194.03 c -128.18 194.03 127.44 194.23 126.69 194.65 c -126.69 193.35 l -127.54 193.03 128.38 192.87 129.21 192.87 c -131.04 192.87 131.95 193.60 131.95 195.05 c -131.95 198.13 l -131.95 198.68 132.12 198.95 132.48 198.95 c -132.54 198.95 132.62 198.94 132.72 198.93 c -132.77 199.98 l -132.37 200.10 132.02 200.16 131.71 200.16 c -130.94 200.16 130.45 199.86 130.23 199.25 c -h -130.15 198.24 m -130.15 196.83 l -129.70 196.83 l -128.49 196.83 127.88 197.21 127.88 197.97 c -127.88 198.23 127.97 198.44 128.14 198.62 c -128.32 198.80 128.54 198.88 128.79 198.88 c -129.23 198.88 129.68 198.67 130.15 198.24 c -h -137.97 199.96 m -137.52 200.09 137.16 200.16 136.91 200.16 c -135.28 200.16 134.46 199.40 134.46 197.87 c -134.46 194.20 l -133.68 194.20 l -133.68 193.03 l -134.46 193.03 l -134.46 191.86 l -136.34 191.64 l -136.34 193.03 l -137.83 193.03 l -137.83 194.20 l -136.34 194.20 l -136.34 197.63 l -136.34 198.48 136.69 198.91 137.39 198.91 c -137.55 198.91 137.74 198.88 137.97 198.82 c -h -145.32 199.77 m -144.42 200.03 143.58 200.16 142.78 200.16 c -141.61 200.16 140.70 199.83 140.02 199.17 c -139.35 198.51 139.01 197.61 139.01 196.46 c -139.01 195.39 139.32 194.52 139.94 193.86 c -140.55 193.20 141.37 192.87 142.38 192.87 c -143.40 192.87 144.14 193.19 144.61 193.84 c -145.08 194.48 145.32 195.50 145.32 196.89 c -140.99 196.89 l -141.12 198.22 141.85 198.88 143.18 198.88 c -143.81 198.88 144.53 198.74 145.32 198.44 c -h -140.97 195.83 m -143.47 195.83 l -143.47 194.64 143.09 194.05 142.32 194.05 c -141.54 194.05 141.09 194.64 140.97 195.83 c -h -f -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -50.000 270.00 m -184.00 270.00 l -184.00 287.00 l -50.000 287.00 l -50.000 270.00 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -50.500 270.50 m -1250.5 270.50 l -1250.5 482.50 l -50.500 482.50 l -50.500 270.50 l -h -S -50.500 287.50 m -183.50 287.50 l -S -183.50 287.50 m -185.50 283.50 l -S -185.50 283.50 m -185.50 270.50 l -S -54.174 284.00 m -54.174 277.03 l -56.053 277.03 l -56.053 278.34 l -56.540 277.36 57.280 276.87 58.275 276.87 c -58.393 276.87 58.510 276.88 58.624 276.91 c -58.624 278.59 l -58.357 278.49 58.110 278.44 57.881 278.44 c -57.132 278.44 56.523 278.82 56.053 279.58 c -56.053 284.00 l -h -65.759 283.77 m -64.866 284.03 64.020 284.16 63.220 284.16 c -62.056 284.16 61.138 283.83 60.465 283.17 c -59.792 282.51 59.456 281.61 59.456 280.46 c -59.456 279.39 59.763 278.52 60.379 277.86 c -60.995 277.20 61.808 276.87 62.820 276.87 c -63.840 276.87 64.584 277.19 65.054 277.84 c -65.524 278.48 65.759 279.50 65.759 280.89 c -61.436 280.89 l -61.563 282.22 62.293 282.88 63.626 282.88 c -64.257 282.88 64.967 282.74 65.759 282.44 c -h -61.411 279.83 m -63.912 279.83 l -63.912 278.64 63.529 278.05 62.763 278.05 c -61.984 278.05 61.533 278.64 61.411 279.83 c -h -71.294 283.96 m -70.845 284.09 70.492 284.16 70.234 284.16 c -68.605 284.16 67.790 283.40 67.790 281.87 c -67.790 278.20 l -67.009 278.20 l -67.009 277.03 l -67.790 277.03 l -67.790 275.86 l -69.669 275.64 l -69.669 277.03 l -71.161 277.03 l -71.161 278.20 l -69.669 278.20 l -69.669 281.63 l -69.669 282.48 70.018 282.91 70.716 282.91 c -70.877 282.91 71.070 282.88 71.294 282.82 c -h -72.970 284.00 m -72.970 277.03 l -74.849 277.03 l -74.849 278.34 l -75.335 277.36 76.076 276.87 77.070 276.87 c -77.189 276.87 77.305 276.88 77.419 276.91 c -77.419 278.59 l -77.153 278.49 76.905 278.44 76.677 278.44 c -75.928 278.44 75.318 278.82 74.849 279.58 c -74.849 284.00 l -h -78.905 286.51 m -80.485 284.00 l -77.902 277.03 l -79.920 277.03 l -81.647 281.69 l -83.697 277.03 l -85.125 277.03 l -80.822 286.51 l -h -85.284 286.27 m -85.284 285.25 l -91.784 285.25 l -91.784 286.27 l -h -98.100 283.85 m -97.321 284.06 96.600 284.16 95.936 284.16 c -94.823 284.16 93.943 283.83 93.298 283.18 c -92.653 282.52 92.330 281.63 92.330 280.51 c -92.330 279.37 92.662 278.48 93.327 277.84 c -93.991 277.19 94.909 276.87 96.082 276.87 c -96.649 276.87 97.302 276.96 98.043 277.14 c -98.043 278.50 l -97.273 278.25 96.657 278.13 96.196 278.13 c -95.633 278.13 95.180 278.34 94.837 278.78 c -94.495 279.21 94.323 279.78 94.323 280.50 c -94.323 281.23 94.508 281.81 94.879 282.25 c -95.249 282.69 95.743 282.91 96.361 282.91 c -96.924 282.91 97.503 282.79 98.100 282.55 c -h -99.877 284.00 m -99.877 277.03 l -101.76 277.03 l -101.76 278.34 l -102.24 277.36 102.98 276.87 103.98 276.87 c -104.10 276.87 104.21 276.88 104.33 276.91 c -104.33 278.59 l -104.06 278.49 103.81 278.44 103.58 278.44 c -102.84 278.44 102.23 278.82 101.76 279.58 c -101.76 284.00 l -h -111.46 283.77 m -110.57 284.03 109.72 284.16 108.92 284.16 c -107.76 284.16 106.84 283.83 106.17 283.17 c -105.50 282.51 105.16 281.61 105.16 280.46 c -105.16 279.39 105.47 278.52 106.08 277.86 c -106.70 277.20 107.51 276.87 108.52 276.87 c -109.54 276.87 110.29 277.19 110.76 277.84 c -111.23 278.48 111.46 279.50 111.46 280.89 c -107.14 280.89 l -107.27 282.22 108.00 282.88 109.33 282.88 c -109.96 282.88 110.67 282.74 111.46 282.44 c -h -107.11 279.83 m -109.61 279.83 l -109.61 278.64 109.23 278.05 108.47 278.05 c -107.69 278.05 107.24 278.64 107.11 279.83 c -h -116.82 283.25 m -116.19 283.86 115.52 284.16 114.81 284.16 c -114.20 284.16 113.70 283.97 113.32 283.60 c -112.94 283.23 112.75 282.75 112.75 282.15 c -112.75 281.38 113.06 280.79 113.67 280.37 c -114.29 279.96 115.17 279.75 116.32 279.75 c -116.82 279.75 l -116.82 279.11 l -116.82 278.39 116.41 278.03 115.58 278.03 c -114.85 278.03 114.11 278.23 113.36 278.65 c -113.36 277.35 l -114.21 277.03 115.05 276.87 115.89 276.87 c -117.71 276.87 118.62 277.60 118.62 279.05 c -118.62 282.13 l -118.62 282.68 118.80 282.95 119.15 282.95 c -119.21 282.95 119.29 282.94 119.40 282.93 c -119.44 283.98 l -119.04 284.10 118.69 284.16 118.39 284.16 c -117.62 284.16 117.12 283.86 116.90 283.25 c -h -116.82 282.24 m -116.82 280.83 l -116.38 280.83 l -115.16 280.83 114.55 281.21 114.55 281.97 c -114.55 282.23 114.64 282.44 114.82 282.62 c -114.99 282.80 115.21 282.88 115.47 282.88 c -115.91 282.88 116.36 282.67 116.82 282.24 c -h -124.64 283.96 m -124.19 284.09 123.84 284.16 123.58 284.16 c -121.95 284.16 121.14 283.40 121.14 281.87 c -121.14 278.20 l -120.35 278.20 l -120.35 277.03 l -121.14 277.03 l -121.14 275.86 l -123.01 275.64 l -123.01 277.03 l -124.51 277.03 l -124.51 278.20 l -123.01 278.20 l -123.01 281.63 l -123.01 282.48 123.36 282.91 124.06 282.91 c -124.22 282.91 124.42 282.88 124.64 282.82 c -h -131.99 283.77 m -131.10 284.03 130.25 284.16 129.45 284.16 c -128.29 284.16 127.37 283.83 126.70 283.17 c -126.02 282.51 125.69 281.61 125.69 280.46 c -125.69 279.39 125.99 278.52 126.61 277.86 c -127.23 277.20 128.04 276.87 129.05 276.87 c -130.07 276.87 130.82 277.19 131.29 277.84 c -131.76 278.48 131.99 279.50 131.99 280.89 c -127.67 280.89 l -127.79 282.22 128.52 282.88 129.86 282.88 c -130.49 282.88 131.20 282.74 131.99 282.44 c -h -127.64 279.83 m -130.14 279.83 l -130.14 278.64 129.76 278.05 128.99 278.05 c -128.22 278.05 127.76 278.64 127.64 279.83 c -h -132.76 286.27 m -132.76 285.25 l -139.26 285.25 l -139.26 286.27 l -h -140.43 284.00 m -140.43 277.03 l -142.31 277.03 l -142.31 284.00 l -h -140.43 275.86 m -140.43 274.29 l -142.31 274.29 l -142.31 275.86 l -h -144.66 284.00 m -144.66 277.03 l -146.54 277.03 l -146.54 278.34 l -147.15 277.36 147.94 276.87 148.91 276.87 c -149.54 276.87 150.03 277.07 150.39 277.46 c -150.75 277.86 150.93 278.39 150.93 279.07 c -150.93 284.00 l -149.05 284.00 l -149.05 279.54 l -149.05 278.75 148.78 278.35 148.26 278.35 c -147.66 278.35 147.09 278.77 146.54 279.61 c -146.54 284.00 l -h -153.01 283.78 m -153.01 282.40 l -153.94 282.79 154.74 282.98 155.40 282.98 c -156.17 282.98 156.56 282.72 156.56 282.20 c -156.56 281.86 156.24 281.56 155.60 281.31 c -154.97 281.05 l -154.28 280.78 153.79 280.47 153.49 280.15 c -153.19 279.83 153.05 279.43 153.05 278.96 c -153.05 278.30 153.30 277.79 153.80 277.42 c -154.30 277.05 155.01 276.87 155.91 276.87 c -156.48 276.87 157.16 276.95 157.94 277.12 c -157.94 278.44 l -157.19 278.18 156.56 278.05 156.07 278.05 c -155.29 278.05 154.90 278.29 154.90 278.77 c -154.90 279.09 155.19 279.36 155.76 279.58 c -156.31 279.79 l -157.12 280.09 157.69 280.41 158.01 280.72 c -158.33 281.04 158.49 281.45 158.49 281.95 c -158.49 282.61 158.22 283.14 157.68 283.55 c -157.13 283.95 156.42 284.16 155.55 284.16 c -154.71 284.16 153.87 284.03 153.01 283.78 c -h -164.14 283.96 m -163.69 284.09 163.34 284.16 163.08 284.16 c -161.45 284.16 160.64 283.40 160.64 281.87 c -160.64 278.20 l -159.86 278.20 l -159.86 277.03 l -160.64 277.03 l -160.64 275.86 l -162.52 275.64 l -162.52 277.03 l -164.01 277.03 l -164.01 278.20 l -162.52 278.20 l -162.52 281.63 l -162.52 282.48 162.87 282.91 163.56 282.91 c -163.72 282.91 163.92 282.88 164.14 282.82 c -h -169.23 283.25 m -168.61 283.86 167.93 284.16 167.22 284.16 c -166.61 284.16 166.12 283.97 165.73 283.60 c -165.35 283.23 165.16 282.75 165.16 282.15 c -165.16 281.38 165.47 280.79 166.09 280.37 c -166.70 279.96 167.58 279.75 168.73 279.75 c -169.23 279.75 l -169.23 279.11 l -169.23 278.39 168.82 278.03 167.99 278.03 c -167.26 278.03 166.52 278.23 165.77 278.65 c -165.77 277.35 l -166.62 277.03 167.47 276.87 168.30 276.87 c -170.12 276.87 171.03 277.60 171.03 279.05 c -171.03 282.13 l -171.03 282.68 171.21 282.95 171.56 282.95 c -171.62 282.95 171.71 282.94 171.81 282.93 c -171.85 283.98 l -171.46 284.10 171.10 284.16 170.80 284.16 c -170.03 284.16 169.53 283.86 169.31 283.25 c -h -169.23 282.24 m -169.23 280.83 l -168.79 280.83 l -167.57 280.83 166.97 281.21 166.97 281.97 c -166.97 282.23 167.05 282.44 167.23 282.62 c -167.40 282.80 167.62 282.88 167.88 282.88 c -168.32 282.88 168.77 282.67 169.23 282.24 c -h -173.46 284.00 m -173.46 277.03 l -175.34 277.03 l -175.34 278.34 l -175.95 277.36 176.74 276.87 177.71 276.87 c -178.33 276.87 178.83 277.07 179.19 277.46 c -179.54 277.86 179.72 278.39 179.72 279.07 c -179.72 284.00 l -177.85 284.00 l -177.85 279.54 l -177.85 278.75 177.58 278.35 177.06 278.35 c -176.46 278.35 175.89 278.77 175.34 279.61 c -175.34 284.00 l -h -187.14 283.85 m -186.36 284.06 185.64 284.16 184.97 284.16 c -183.86 284.16 182.98 283.83 182.34 283.18 c -181.69 282.52 181.37 281.63 181.37 280.51 c -181.37 279.37 181.70 278.48 182.37 277.84 c -183.03 277.19 183.95 276.87 185.12 276.87 c -185.69 276.87 186.34 276.96 187.08 277.14 c -187.08 278.50 l -186.31 278.25 185.70 278.13 185.23 278.13 c -184.67 278.13 184.22 278.34 183.88 278.78 c -183.53 279.21 183.36 279.78 183.36 280.50 c -183.36 281.23 183.55 281.81 183.92 282.25 c -184.29 282.69 184.78 282.91 185.40 282.91 c -185.96 282.91 186.54 282.79 187.14 282.55 c -h -194.59 283.77 m -193.70 284.03 192.85 284.16 192.05 284.16 c -190.89 284.16 189.97 283.83 189.30 283.17 c -188.62 282.51 188.29 281.61 188.29 280.46 c -188.29 279.39 188.60 278.52 189.21 277.86 c -189.83 277.20 190.64 276.87 191.65 276.87 c -192.67 276.87 193.42 277.19 193.89 277.84 c -194.36 278.48 194.59 279.50 194.59 280.89 c -190.27 280.89 l -190.40 282.22 191.12 282.88 192.46 282.88 c -193.09 282.88 193.80 282.74 194.59 282.44 c -h -190.24 279.83 m -192.74 279.83 l -192.74 278.64 192.36 278.05 191.59 278.05 c -190.82 278.05 190.37 278.64 190.24 279.83 c -h -f -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -528.00 338.00 m -639.00 338.00 l -639.00 355.00 l -528.00 355.00 l -528.00 338.00 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -528.50 338.50 m -876.50 338.50 l -876.50 406.50 l -528.50 406.50 l -528.50 338.50 l -h -S -528.50 355.50 m -638.50 355.50 l -S -638.50 355.50 m -640.50 351.50 l -S -640.50 351.50 m -640.50 338.50 l -S -535.59 351.25 m -534.96 351.86 534.29 352.16 533.58 352.16 c -532.97 352.16 532.47 351.97 532.09 351.60 c -531.71 351.23 531.52 350.75 531.52 350.15 c -531.52 349.38 531.83 348.79 532.44 348.37 c -533.06 347.96 533.94 347.75 535.09 347.75 c -535.59 347.75 l -535.59 347.11 l -535.59 346.39 535.18 346.03 534.35 346.03 c -533.62 346.03 532.88 346.23 532.13 346.65 c -532.13 345.35 l -532.98 345.03 533.82 344.87 534.66 344.87 c -536.48 344.87 537.39 345.60 537.39 347.05 c -537.39 350.13 l -537.39 350.68 537.57 350.95 537.92 350.95 c -537.98 350.95 538.06 350.94 538.17 350.93 c -538.21 351.98 l -537.81 352.10 537.46 352.16 537.16 352.16 c -536.39 352.16 535.89 351.86 535.67 351.25 c -h -535.59 350.24 m -535.59 348.83 l -535.15 348.83 l -533.93 348.83 533.32 349.21 533.32 349.97 c -533.32 350.23 533.41 350.44 533.59 350.62 c -533.76 350.80 533.98 350.88 534.24 350.88 c -534.68 350.88 535.13 350.67 535.59 350.24 c -h -544.13 352.00 m -544.13 350.69 l -543.52 351.67 542.73 352.16 541.75 352.16 c -541.13 352.16 540.64 351.96 540.28 351.57 c -539.92 351.17 539.74 350.64 539.74 349.96 c -539.74 345.03 l -541.62 345.03 l -541.62 349.49 l -541.62 350.28 541.88 350.68 542.41 350.68 c -543.01 350.68 543.58 350.26 544.13 349.42 c -544.13 345.03 l -546.01 345.03 l -546.01 352.00 l -h -551.95 351.96 m -551.50 352.09 551.15 352.16 550.89 352.16 c -549.26 352.16 548.44 351.40 548.44 349.87 c -548.44 346.20 l -547.66 346.20 l -547.66 345.03 l -548.44 345.03 l -548.44 343.86 l -550.32 343.64 l -550.32 345.03 l -551.81 345.03 l -551.81 346.20 l -550.32 346.20 l -550.32 349.63 l -550.32 350.48 550.67 350.91 551.37 350.91 c -551.53 350.91 551.72 350.88 551.95 350.82 c -h -553.62 352.00 m -553.62 341.98 l -555.50 341.98 l -555.50 346.34 l -556.12 345.36 556.91 344.87 557.88 344.87 c -558.50 344.87 558.99 345.07 559.35 345.46 c -559.71 345.86 559.89 346.39 559.89 347.07 c -559.89 352.00 l -558.01 352.00 l -558.01 347.54 l -558.01 346.75 557.75 346.35 557.22 346.35 c -556.63 346.35 556.05 346.77 555.50 347.61 c -555.50 352.00 l -h -560.99 354.27 m -560.99 353.25 l -567.49 353.25 l -567.49 354.27 l -h -568.66 352.00 m -568.66 345.03 l -570.46 345.03 l -570.46 346.34 l -571.01 345.36 571.80 344.87 572.81 344.87 c -573.34 344.87 573.77 345.00 574.11 345.26 c -574.45 345.52 574.65 345.88 574.73 346.34 c -575.38 345.36 576.17 344.87 577.09 344.87 c -578.36 344.87 579.00 345.57 579.00 346.98 c -579.00 352.00 l -577.20 352.00 l -577.20 347.59 l -577.20 346.77 576.92 346.36 576.37 346.36 c -575.81 346.36 575.26 346.76 574.73 347.58 c -574.73 352.00 l -572.93 352.00 l -572.93 347.59 l -572.93 346.77 572.65 346.35 572.09 346.35 c -571.54 346.35 571.00 346.76 570.46 347.58 c -570.46 352.00 l -h -581.27 352.00 m -581.27 345.03 l -583.15 345.03 l -583.15 352.00 l -h -581.27 343.86 m -581.27 342.29 l -583.15 342.29 l -583.15 343.86 l -h -589.88 352.00 m -589.88 350.69 l -589.40 351.67 588.64 352.16 587.60 352.16 c -586.76 352.16 586.11 351.85 585.63 351.24 c -585.15 350.62 584.91 349.78 584.91 348.71 c -584.91 347.54 585.18 346.61 585.72 345.91 c -586.26 345.22 586.98 344.87 587.89 344.87 c -588.62 344.87 589.28 345.16 589.88 345.73 c -589.88 341.98 l -591.77 341.98 l -591.77 352.00 l -h -589.88 346.85 m -589.43 346.34 588.94 346.09 588.43 346.09 c -587.97 346.09 587.60 346.31 587.32 346.76 c -587.05 347.20 586.91 347.80 586.91 348.55 c -586.91 349.97 587.37 350.69 588.28 350.69 c -588.84 350.69 589.37 350.33 589.88 349.61 c -h -598.49 352.00 m -598.49 350.69 l -598.01 351.67 597.25 352.16 596.22 352.16 c -595.38 352.16 594.72 351.85 594.24 351.24 c -593.76 350.62 593.52 349.78 593.52 348.71 c -593.52 347.54 593.79 346.61 594.33 345.91 c -594.87 345.22 595.60 344.87 596.51 344.87 c -597.24 344.87 597.90 345.16 598.49 345.73 c -598.49 341.98 l -600.38 341.98 l -600.38 352.00 l -h -598.49 346.85 m -598.04 346.34 597.56 346.09 597.04 346.09 c -596.58 346.09 596.21 346.31 595.94 346.76 c -595.66 347.20 595.52 347.80 595.52 348.55 c -595.52 349.97 595.98 350.69 596.90 350.69 c -597.45 350.69 597.99 350.33 598.49 349.61 c -h -602.72 352.00 m -602.72 341.98 l -604.60 341.98 l -604.60 352.00 l -h -612.62 351.77 m -611.73 352.03 610.89 352.16 610.09 352.16 c -608.92 352.16 608.00 351.83 607.33 351.17 c -606.66 350.51 606.32 349.61 606.32 348.46 c -606.32 347.39 606.63 346.52 607.24 345.86 c -607.86 345.20 608.67 344.87 609.69 344.87 c -610.71 344.87 611.45 345.19 611.92 345.84 c -612.39 346.48 612.62 347.50 612.62 348.89 c -608.30 348.89 l -608.43 350.22 609.16 350.88 610.49 350.88 c -611.12 350.88 611.83 350.74 612.62 350.44 c -h -608.28 347.83 m -610.78 347.83 l -610.78 346.64 610.39 346.05 609.63 346.05 c -608.85 346.05 608.40 346.64 608.28 347.83 c -h -615.80 352.00 m -613.86 345.03 l -615.61 345.03 l -616.98 349.91 l -618.44 345.03 l -620.06 345.03 l -621.38 349.94 l -622.86 345.03 l -624.16 345.03 l -622.10 352.00 l -620.29 352.00 l -619.02 347.22 l -617.60 352.00 l -h -629.20 351.25 m -628.57 351.86 627.90 352.16 627.19 352.16 c -626.58 352.16 626.08 351.97 625.70 351.60 c -625.32 351.23 625.13 350.75 625.13 350.15 c -625.13 349.38 625.44 348.79 626.05 348.37 c -626.67 347.96 627.55 347.75 628.70 347.75 c -629.20 347.75 l -629.20 347.11 l -629.20 346.39 628.79 346.03 627.96 346.03 c -627.23 346.03 626.49 346.23 625.74 346.65 c -625.74 345.35 l -626.59 345.03 627.43 344.87 628.27 344.87 c -630.09 344.87 631.00 345.60 631.00 347.05 c -631.00 350.13 l -631.00 350.68 631.18 350.95 631.53 350.95 c -631.59 350.95 631.67 350.94 631.78 350.93 c -631.82 351.98 l -631.42 352.10 631.07 352.16 630.77 352.16 c -630.00 352.16 629.50 351.86 629.28 351.25 c -h -629.20 350.24 m -629.20 348.83 l -628.75 348.83 l -627.54 348.83 626.93 349.21 626.93 349.97 c -626.93 350.23 627.02 350.44 627.20 350.62 c -627.37 350.80 627.59 350.88 627.85 350.88 c -628.29 350.88 628.74 350.67 629.20 350.24 c -h -633.43 352.00 m -633.43 345.03 l -635.30 345.03 l -635.30 346.34 l -635.79 345.36 636.53 344.87 637.53 344.87 c -637.64 344.87 637.76 344.88 637.88 344.91 c -637.88 346.59 l -637.61 346.49 637.36 346.44 637.13 346.44 c -636.38 346.44 635.77 346.82 635.30 347.58 c -635.30 352.00 l -h -645.01 351.77 m -644.12 352.03 643.27 352.16 642.47 352.16 c -641.31 352.16 640.39 351.83 639.72 351.17 c -639.04 350.51 638.71 349.61 638.71 348.46 c -638.71 347.39 639.01 346.52 639.63 345.86 c -640.25 345.20 641.06 344.87 642.07 344.87 c -643.09 344.87 643.84 345.19 644.31 345.84 c -644.78 346.48 645.01 347.50 645.01 348.89 c -640.69 348.89 l -640.81 350.22 641.54 350.88 642.88 350.88 c -643.51 350.88 644.22 350.74 645.01 350.44 c -h -640.66 347.83 m -643.16 347.83 l -643.16 346.64 642.78 346.05 642.01 346.05 c -641.24 346.05 640.78 346.64 640.66 347.83 c -h -f -Q -Q -Q - -endstream -endobj - -8 0 obj - 212207 -endobj - -3 0 obj - << - /Parent null - /Type /Pages - /MediaBox [0.0000 0.0000 842.00 595.00] - /Resources 9 0 R - /Kids [6 0 R] - /Count 1 - >> -endobj - -10 0 obj - [/PDF /Text /ImageC] -endobj - -11 0 obj - << - /Alpha1 - << - /ca 1.0000 - /CA 1.0000 - /BM /Normal - /AIS false - >> - >> -endobj - -9 0 obj - << - /ProcSet 10 0 R - /ExtGState 11 0 R - >> -endobj - -4 0 obj - << - /Type /Outlines - /First 12 0 R - /Last 12 0 R - >> -endobj - -12 0 obj - << - /Parent 4 0 R - /Title (Page 1 \(untitled\)) - /Prev null - /Next null - /Dest [6 0 R /Fit] - >> -endobj - -xref -0 13 -0000000000 65535 f -0000000016 00000 n -0000000343 00000 n -0000212991 00000 n -0000213422 00000 n -0000000525 00000 n -0000000602 00000 n -0000000691 00000 n -0000212965 00000 n -0000213347 00000 n -0000213162 00000 n -0000213203 00000 n -0000213512 00000 n - -trailer -<< - /Size 12 - /Root 2 0 R - /Info 1 0 R ->> - -startxref -213656 - -%%EOF diff --git a/doc/design/use_case_3.png b/doc/design/use_case_3.png deleted file mode 100644 index 8a5febd2e1..0000000000 Binary files a/doc/design/use_case_3.png and /dev/null differ diff --git a/doc/design/use_case_3.sdx b/doc/design/use_case_3.sdx deleted file mode 100644 index cef8708b4e..0000000000 --- a/doc/design/use_case_3.sdx +++ /dev/null @@ -1,80 +0,0 @@ - - - -client:401 Unauthorized -client:WWW-Authenticate\: Keystone uri="url_to_keystone" -[/c] - -[c:authenticate] -client:token, serviceCatalog=keystone.auth -[/c] - - -[c:retry_create_instance] -client:success=nova.createInstance - -nova:tenant = parse(url) -[c:auth_middleware] -nova:user, roles=keystone.validate -[/c] -nova:authorize=can_haz(context, user, 'create_instance', tenant_id) -nova:execute create_instance -[/c] -client:200 OK]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/generate_autodoc_index.py b/doc/generate_autodoc_index.py deleted file mode 100755 index 993369b028..0000000000 --- a/doc/generate_autodoc_index.py +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env python -"""Generates files for sphinx documentation using a simple Autodoc based -template. - -To use, just run as a script: - $ python doc/generate_autodoc_index.py -""" - -import os - - -base_dir = os.path.dirname(os.path.abspath(__file__)) -RSTDIR=os.path.join(base_dir, "source", "sourcecode") -SOURCEDIR=os.path.join(base_dir, "..") - -# Exclude these modules from the autodoc results -EXCLUDE_MODULES = ['keystone.backends.sqlalchemy.migrate_repo'] - -def in_exclude_list(module_name): - """Compares a module to the list of excluded modules - - Returns true if the provided module resides in or matches - an excluded module, false otherwise. - """ - for excluded_module in EXCLUDE_MODULES: - if module_name.startswith(excluded_module): - return True - return False - -def find_autodoc_modules(module_name, sourcedir): - """returns a list of modules in the SOURCE directory""" - modlist = [] - os.chdir(os.path.join(sourcedir, module_name)) - for root, dirs, files in os.walk("."): - for filename in files: - if filename.endswith(".py"): - # root = ./keystone/test/unit - # filename = base.py - elements = root.split(os.path.sep) - # replace the leading "." with the module name - elements[0] = module_name - # and get the base module name - base, extension = os.path.splitext(filename) - if not (base == "__init__"): - elements.append(base) - result = (".".join(elements)) - if not in_exclude_list(result): - modlist.append(result) - return modlist - -if not(os.path.exists(RSTDIR)): - os.mkdir(RSTDIR) - -INDEXOUT = open("%s/autoindex.rst" % RSTDIR, "w") -INDEXOUT.write("Source Code Index\n") -INDEXOUT.write("=================\n") -INDEXOUT.write(".. toctree::\n") -INDEXOUT.write(" :maxdepth: 1\n") -INDEXOUT.write("\n") - -for module in find_autodoc_modules('keystone', SOURCEDIR): - generated_file = "%s/%s.rst" % (RSTDIR, module) - - INDEXOUT.write(" %s\n" % module) - FILEOUT = open(generated_file, "w") - FILEOUT.write("The :mod:`%s` Module\n" % module) - FILEOUT.write("==============================" - "==============================" - "==============================\n") - FILEOUT.write(".. automodule:: %s\n" % module) - FILEOUT.write(" :members:\n") - FILEOUT.write(" :undoc-members:\n") - FILEOUT.write(" :show-inheritance:\n") - FILEOUT.close() - -INDEXOUT.close() diff --git a/doc/source/architecture.rst b/doc/source/architecture.rst deleted file mode 100644 index 8de4550256..0000000000 --- a/doc/source/architecture.rst +++ /dev/null @@ -1,97 +0,0 @@ -.. - 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. - -Keystone Architecture -===================== - -Keystone has two major components: Authentication and a Service Catalog. - -Authentication --------------- - -In providing a token-based authentication service for OpenStack, keystone -has several major concepts: - -Tenant - A grouping used in OpenStack to contain relevant OpenStack services. A - tenant maps to a Nova "project-id", and in object storage, a tenant can - have multiple containers. Depending on the installation, a tenant can - represent a customer, account, organization, or project. - -User - Represents an individual within OpenStack for the purposes of - authenticating them to OpenStack services. Users have credentials, and may - be assigned to one or more tenants. When authenticated, a token is - provided that is specific to a single tenant. - -Credentials - Password or other information that uniquely identifies a User to Keystone - for the purposes of providing a token. - -Token - A token is an arbitrary bit of text that is used to share authentication - with other OpenStack services so that Keystone can provide a central - location for authenticating users for access to OpenStack services. A - token may be "scoped" or "unscoped". A scoped token represents a user - authenticated to a Tenant, where an unscoped token represents just the - user. - - Tokens are valid for a limited amount of time and may be revoked at any - time. - -Role - A role is a set of permissions to access and use specific operations for - a given user when applied to a tenant. Roles are logical groupings of - those permissions to enable common permissions to be easily grouped and - bound to users associated with a given tenant. - -Service Catalog ---------------- - -Keystone also provides a list of REST API endpoints as a definitive list for -an OpenStack installation. Key concepts include: - -Service - An OpenStack service such as nova, swift, glance, or keystone. A service - may have one of more endpoints through which users can interact with - OpenStack services and resources. - -Endpoint - A network accessible address (typically a URL) that represents the API - interface to an OpenStack service. Endpoints may also be grouped into - templates which represent a group of consumable OpenStack services - available across regions. - -Template - A collection of endpoints representing a set of consumable OpenStack - service endpoints. - -Components of Keystone ----------------------- - -Keystone includes a command-line interface which interacts with the Keystone -API for administrating keystone and related services. - -* keystone - runs both keystone-admin and keystone-service -* keystone-admin - the administrative API for manipulating keystone -* keystone-service - the user oriented API for authentication -* keystone-manage - the command line interface to manipulate keystone - -Keystone also includes WSGI middelware to provide authentication support -for Nova and Swift. - -Keystone uses a built-in SQLite datastore - and may use an external LDAP -service to authenticate users instead of using stored credentials. diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst deleted file mode 100644 index a98d92f88c..0000000000 --- a/doc/source/configuration.rst +++ /dev/null @@ -1,100 +0,0 @@ -.. - 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. - -==================== -Configuring Keystone -==================== - -.. toctree:: - :maxdepth: 1 - - keystone.conf - man/keystone-manage - -Once Keystone is installed, there are a number of configuration options -available and potentially some initial data to create and set up. - -Sample data / Quick Setup -========================= - -Default sampledata is provided for easy setup and testing in bin/sampeldata. To -set up the sample data run the following command while Keystone is running:: - - $ ./bin/sampledata - -The sample data created comes from the file :doc:`sourcecode/keystone.test.sampledata` - - -Keystone Configuration File -=========================== - -Most configuration is done via configuration files. The default files are -in ``/etc/keystone.conf`` - -When starting up a Keystone server, you can specify the configuration file to -use (see :doc:`controllingservers`). -If you do **not** specify a configuration file, keystone will look in the following -directories for a configuration file, in order: - -* ``~/.keystone`` -* ``~/`` -* ``/etc/keystone`` -* ``/etc`` - -The keystone configuration file should be named ``keystone.conf``. -If you installed keystone via your operating system's -package management system, it is likely that you will have sample -configuration files installed in ``/etc/keystone``. - -In addition to this documentation page, you can check the -``etc/keystone.conf`` sample configuration -files distributed with keystone for example configuration files for each server -application with detailed comments on what each options does. - -Sample Configuration Files --------------------------- - -Keystone ships with sample configuration files in keystone/etc. These files are: - -1. keystone.conf - - A standard configuration file for running keystone in stand-alone mode. - It has a set of default extensions loaded to support administering Keystone - over REST. It uses a local SQLite database. - -2. memcache.conf - - A configuration that uses memcached for storing tokens (but still SQLite for all - other entities). This requires memcached running. - -3. ssl.conf - - A configuration that runs Keystone with SSL (so all URLs are accessed over HTTPS). - -To run any of these configurations, use the `-c` option:: - - ./keystone -c ../etc/ssl.conf - - - -Usefule Links -------------- - -For a sample configuration file with explanations of the settings, see :doc:`keystone.conf` - -For configuring an LDAP backend, see http://mirantis.blogspot.com/2011/08/ldap-identity-store-for-openstack.html - -For configuration settings of middleware components, see :doc:`middleware` \ No newline at end of file diff --git a/doc/source/configuringservices.rst b/doc/source/configuringservices.rst deleted file mode 100644 index 083c3ec5ed..0000000000 --- a/doc/source/configuringservices.rst +++ /dev/null @@ -1,333 +0,0 @@ -.. - 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. - -========================================== -Configuring Services to work with Keystone -========================================== - -.. toctree:: - :maxdepth: 1 - -Once Keystone is installed and running, services need to be configured to work -with it. These are the steps to configure a service to work with Keystone: - -1. Create or get credentials for the service to use - - A set of credentials are needed for each service (they may be - shared if you chose to). Depending on the service, these credentials are - either a username and password or a long-lived token.. - -2. Register the service, endpoints, roles and other entities - - In order for a service to have it's endpoints and roles show in the service - catalog returned by Keystone, a service record needs to be added for the - service. Endpoints and roles associated with that service can then be created. - - This can be done through the REST interface (using the OS-KSCATALOG extension) - or using keystone-manage. - -3. Install and configure middleware for the service to handle authentication - - Clients making calls to the service will pass in an authentication token. The - Keystone middleware will look for and validate that token, taking the - appropriate action. It will also retrive additional information from the token - such as user name, id, tenant name, id, roles, etc... - - The middleware will pass those data down to the service as headers. The - detailed description of this architecture is available here :doc:`middleware_architecture` - -Setting up credentials -====================== - -First admin user - bootstrapping --------------------------------- - -For a default installation of Keystone, before you can use the REST API, you -need to create your first initial user and grant that user the right to -administer Keystone. - -For the keystone service itself, two -Roles are pre-defined in the keystone configuration file -(:doc:`keystone.conf`). - - #Role that allows admin operations (access to all operations) - keystone-admin-role = Admin - - #Role that allows acting as service (validate tokens, register service, - etc...) - keystone-service-admin-role = KeystoneServiceAdmin - -In order to create your first user, once Keystone is running use -the `keystone-manage` command: - - $ keystone-manage user add admin secrete - $ keystone-manage role add Admin - $ keystone-manage role add KeystoneServiceAdmin - $ keystone-manage role grant Admin admin - $ keystone-manage role grant KeystoneServiceAdmin admin - -This creates the `admin` user (with a password of `secrete`), creates -two roles (`Admin` and `KeystoneServiceAdmin`), and assigns those roles to -the `admin` user. From here, you should now have the choice of using the -administrative API (as well as the :doc:`man/keystone-manage` commands) to -further configure keystone. There are a number of examples of how to use -that API at :doc:`adminAPI_curl_examples`. - - -Setting up services -=================== - -Defining Services and Service Endpoints ---------------------------------------- - -Keystone also acts as a service catalog to let other OpenStack systems know -where relevant API endpoints exist for OpenStack Services. The OpenStack -Dashboard, in particular, uses this heavily - and this **must** be configured -for the OpenStack Dashboard to properly function. - -Here's how we define the services:: - - $ keystone-manage service add nova compute "Nova Compute Service" - $ keystone-manage service add glance image "Glance Image Service" - $ keystone-manage service add swift storage "Swift Object Storage Service" - $ keystone-manage service add keystone identity "Keystone Identity Service" - -Once the services are defined, we create endpoints for them. Each service -has three relevant URL's associated with it that are used in the command: - -* the public API URL -* an administrative API URL -* an internal URL - -The "internal URL" is an endpoint the generally offers the same API as the -public URL, but over a high-bandwidth, low-latency, unmetered (free) network. -You would use that to transfer images from nova to glance for example, and -not the Public URL which would go over the internet and be potentially chargeable. - -The "admin URL" is for administering the services and is not exposed or accessible -to customers without the apporpriate privileges. - -An example of setting up the endpoint for Nova:: - - $ keystone-manage endpointTemplates add RegionOne nova \ - http://nova-api.mydomain:8774/v1.1/%tenant_id% \ - http://nova-api.mydomain:8774/v1.1/%tenant_id% \ - http://nova-api.mydomain:8774/v1.1/%tenant_id% \ - 1 1 - -Glance:: - - $ keystone-manage endpointTemplates add RegionOne glance \ - http://glance.mydomain:9292/v1 \ - http://glance.mydomain:9292/v1 \ - http://glance.mydomain:9292/v1 \ - 1 1 - -Swift:: - - $ keystone-manage endpointTemplates add RegionOne swift \ - http://swift.mydomain:8080/v1/AUTH_%tenant_id% \ - http://swift.mydomain:8080/v1.0/ \ - http://swift.mydomain:8080/v1/AUTH_%tenant_id% \ - 1 1 - -And setting up an endpoint for Keystone:: - - $ keystone-manage endpointTemplates add RegionOne keystone \ - http://keystone.mydomain:5000/v2.0 \ - http://keystone.mydomain:35357/v2.0 \ - http://keystone.mydomain:5000/v2.0 \ - 1 1 - - -Defining an Administrative Service Token ----------------------------------------- - -An Administrative Service Token is a bit of arbitrary text which is configured -in Keystone and used (typically configured into) Nova, Swift, Glance, and any -other OpenStack projects, to be able to use Keystone services. - -This token is an arbitrary text string, but must be identical between Keystone -and the services using Keystone. This token is bound to a user and tenant as -well, so those also need to be created prior to setting it up. - -The *admin* user was set up above, but we haven't created a tenant for that -user yet:: - - $ keystone-manage tenant add admin - -and while we're here, let's grant the admin user the 'Admin' role to the -'admin' tenant:: - - $ keystone-manage role add Admin - $ keystone-manage role grant Admin admin admin - -Now we can create a service token:: - - $ keystone-manage token add 999888777666 admin admin 2015-02-05T00:00 - -This creates a service token of '999888777666' associated to the admin user, -admin tenant, and expires on February 5th, 2015. This token will be used when -configuring Nova, Glance, or other OpenStack services. - -Securing Communications with SSL --------------------------------- - -To encrypt traffic between services and Keystone, see :doc:`ssl` - - -Setting up OpenStack users -========================== - -Creating Tenants, Users, and Roles ----------------------------------- - -Let's set up a 'demo' tenant:: - - $ keystone-manage tenant add demo - -And add a 'demo' user with the password 'guest':: - - $ keystone-manage user add demo guest - -Now let's add a role of "Member" and grant 'demo' user that role -as it pertains to the tenant 'demo':: - - $ keystone-manage role add Member - $ keystone-manage role grant Member demo demo - -Let's also add the admin user as an Admin role to the demo tenant:: - - $ keystone-manage role grant Admin admin demo - -Creating EC2 credentials ------------------------- - -To add EC2 credentials for the `admin` and `demo` accounts:: - - $ keystone-manage credentials add admin EC2 'admin' 'secretpassword' - $ keystone-manage credentials add admin EC2 'demo' 'secretpassword' - -If you have a large number of credentials to create, you can put them all -into a single large file and import them using :doc:`man/keystone-import`. The -format of the document looks like:: - - credentials add admin EC2 'username' 'password' - credentials add admin EC2 'username' 'password' - -Then use:: - - $ keystone-import `filename` - - -Setting Up Middleware -===================== - -Keystone Auth-Token Middleware --------------------------------- - -The Keystone auth_token middleware is a WSGI component that can be inserted in -the WSGI pipeline to handle authenticating tokens with Keystone. See :doc:`middleware` -for details on middleware and configuration parameters. - - -Configuring Nova to use Keystone --------------------------------- - -To configure Nova to use Keystone for authentication, the Nova API service -can be run against the api-paste file provided by Keystone. This is most -easily accomplished by setting the `--api_paste_config` flag in nova.conf to -point to `examples/paste/nova-api-paste.ini` from Keystone. This paste file -included references to the WSGI authentication middleware provided with the -keystone installation. - -When configuring Nova, it is important to create a admin service token for -the service (from the Configuration step above) and include that as the key -'admin_token' in the nova-api-paste.ini. See the documented -:doc:`nova-api-paste` file for references. - -Configuring Swift to use Keystone ---------------------------------- - -Similar to Nova, swift can be configured to use Keystone for authentication -rather than it's built in 'tempauth'. - -1. Add a service endpoint for Swift to Keystone - -2. Configure the paste file for swift-proxy (`/etc/swift/swift-proxy.conf`) - -3. Reconfigure Swift's proxy server to use Keystone instead of TempAuth. - Here's an example `/etc/swift/proxy-server.conf`:: - - [DEFAULT] - bind_port = 8888 - user = - - [pipeline:main] - pipeline = catch_errors cache keystone proxy-server - - [app:proxy-server] - use = egg:swift#proxy - account_autocreate = true - - [filter:keystone] - use = egg:keystone#tokenauth - auth_protocol = http - auth_host = 127.0.0.1 - auth_port = 35357 - admin_token = 999888777666 - delay_auth_decision = 0 - service_protocol = http - service_host = 127.0.0.1 - service_port = 8100 - service_pass = dTpw - cache = swift.cache - - [filter:cache] - use = egg:swift#memcache - set log_name = cache - - [filter:catch_errors] - use = egg:swift#catch_errors - - Note that the optional "cache" property in the keystone filter allows any - service (not just Swift) to register its memcache client in the WSGI - environment. If such a cache exists, Keystone middleware will utilize it - to store validated token information, which could result in better overall - performance. - -4. Restart swift - -5. Verify that keystone is providing authentication to Swift - -Use `swift` to check everything works (note: you currently have to create a -container or upload something as your first action to have the account -created; there's a Swift bug to be fixed soon):: - - $ swift -A http://127.0.0.1:5000/v1.0 -U joeuser -K secrete post container - $ swift -A http://127.0.0.1:5000/v1.0 -U joeuser -K secrete stat -v - StorageURL: http://127.0.0.1:8888/v1/AUTH_1234 - Auth Token: 74ce1b05-e839-43b7-bd76-85ef178726c3 - Account: AUTH_1234 - Containers: 1 - Objects: 0 - Bytes: 0 - Accept-Ranges: bytes - X-Trans-Id: tx25c1a6969d8f4372b63912f411de3c3b - -.. WARNING:: - Keystone currently allows any valid token to do anything with any account. - diff --git a/doc/source/developing.rst b/doc/source/developing.rst deleted file mode 100644 index acd2273068..0000000000 --- a/doc/source/developing.rst +++ /dev/null @@ -1,135 +0,0 @@ -.. - 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. - -======================== -Developing with Keystone -======================== - -Get your development environment set up according to :doc:`setup`. - -Running a development instance -============================== - -Setting up a virtualenv ------------------------ - -We recommend establishing a virtualenv to run keystone within. To establish -this environment, use the command:: - - $ python tools/install_venv.py - -This will create a local virtual environment in the directory ``.venv``. -Once created, you can activate this virtualenv for your current shell using:: - - $ source .venv/bin/activate - -The virtual environment can be disabled using the command:: - - $ deactivate - -You can also use ``tools\with_venv.sh`` to prefix commands so that they run -within the virtual environment. For more information on virtual environments, -see virtualenv_. - -.. _virtualenv: http://www.virtualenv.org/ - -Running Keystone ----------------- - -To run the keystone Admin and API server instances, use:: - - $ tools/with_venv.sh bin/keystone - -Running a demo service that uses Keystone ------------------------------------------ - -To run client demo (with all auth middleware running locally on sample service):: - - $ tools/with_venv.sh examples/echo/bin/echod - -which spins up a simple "echo" service on port 8090. To use a simple echo client:: - - $ python examples/echo/echo_client.py - -Interacting with Keystone -========================= - -You can interact with Keystone through the command line using :doc:`man/keystone-manage` -which allows you to establish tenants, users, etc. - -You can also interact with Keystone through it's REST API. There is a python -keystone client library python-keystoneclient_ which interacts exclusively through -the REST API. - -.. _python-keystoneclient: https://github.com/4P/python-keystoneclient - -The easiest way to establish some base information in Keystone to interact with is -to invoke:: - - $ tools/with_venv.sh bin/sampledata - -You can see the details of what that creates in ``keystone/test/sampledata.py`` - -Enabling debugging middleware ------------------------------ - -You can enable a huge amount of additional data (debugging information) about -the request and repsonse objects flowing through Keystone using the debugging -WSGI middleware. - -To enable this, just modify the pipelines in ``etc/keystone.conf``, from:: - - [pipeline:admin] - pipeline = - urlnormalizer - admin_api - - [pipeline:keystone-legacy-auth] - pipeline = - urlnormalizer - legacy_auth - d5_compat - service_api - -... to:: - - [pipeline:admin] - pipeline = - debug - urlnormalizer - d5_compat - admin_api - - [pipeline:keystone-legacy-auth] - pipeline = - debug - urlnormalizer - legacy_auth - d5_compat - service_api - -Two simple and easy debugging tools are using the ``-d`` when you start keystone:: - - $ ./keystone -d - -and the `--trace-calls` flag:: - - $ ./keystone -trace-calls - -The ``-d`` flag outputs debug information to the console. The ``--trace-calls`` flag -outputs extensive, nested trace calls to the console and highlights any errors -in red. - diff --git a/doc/source/images/305.svg b/doc/source/images/305.svg deleted file mode 100644 index 7d79464e2b..0000000000 --- a/doc/source/images/305.svg +++ /dev/null @@ -1,158 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - Request - service directly - - - Auth - Component - 305 - Use proxy to - redirect to Auth - - - - - - - OpenStack - Service - - - diff --git a/doc/source/images/both.svg b/doc/source/images/both.svg deleted file mode 100644 index d29872a4a6..0000000000 --- a/doc/source/images/both.svg +++ /dev/null @@ -1,135 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - Auth - Component - - Auth - Component - - - OpenStack - Service - - - - - diff --git a/doc/source/images/graphs_delegate_accept.svg b/doc/source/images/graphs_delegate_accept.svg deleted file mode 100644 index 1d86cadfc6..0000000000 --- a/doc/source/images/graphs_delegate_accept.svg +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - -DelegateAcceptAuth - - - -AuthComp - -Auth -Component - - -Start->AuthComp - - -Authorization: Basic VTpQ - - -AuthComp->Start - - -200 Okay - - -Service - -OpenStack -Service - - -AuthComp->Service - - -Authorization: Basic dTpw -X-Authorization: Proxy U -X-Identity-Status: Confirmed - - -Service->AuthComp - - -200 Okay - - - diff --git a/doc/source/images/graphs_separate.svg b/doc/source/images/graphs_separate.svg deleted file mode 100644 index 376e59880a..0000000000 --- a/doc/source/images/graphs_separate.svg +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - -Seperate - - -AuthComp - -Auth -Component - - -Service - -OpenStack -Service - - -AuthComp->Service - - - - - diff --git a/doc/source/images/graphs_standard_accept.svg b/doc/source/images/graphs_standard_accept.svg deleted file mode 100644 index bddf4b5f16..0000000000 --- a/doc/source/images/graphs_standard_accept.svg +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - -StandardAcceptAuth - - - -AuthComp - -Auth -Component - - -Start->AuthComp - - -Authorization: Basic VTpQ - - -AuthComp->Start - - -200 Okay - - -Service - -OpenStack -Service - - -AuthComp->Service - - -Authorization: Basic dTpw -X-Authorization: Proxy U - - -Service->AuthComp - - -200 Okay - - - diff --git a/doc/source/images/graphs_standard_reject.svg b/doc/source/images/graphs_standard_reject.svg deleted file mode 100644 index 6020ad67a5..0000000000 --- a/doc/source/images/graphs_standard_reject.svg +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - -StandardRejectAuth - - - -AuthComp - -Auth -Component - - -Start->AuthComp - - -Authorization: Basic Yjpw - - -AuthComp->Start - - -401 Unauthorized -WWW-Authenticate: Basic Realm="API Realm" - - -Service - -OpenStack -Service - - - diff --git a/doc/source/images/graphs_together.svg b/doc/source/images/graphs_together.svg deleted file mode 100644 index 1425a28baa..0000000000 --- a/doc/source/images/graphs_together.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - -Together - - -Together - - -Auth -Component - - -OpenStack -Service - - - diff --git a/doc/source/images/layouts.svg b/doc/source/images/layouts.svg deleted file mode 100644 index fdf61b7da7..0000000000 --- a/doc/source/images/layouts.svg +++ /dev/null @@ -1,215 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - Auth - Component - - - OpenStack - Service - - - Option - ( - b - ) - - - Auth - Component - - - OpenStack - Service - Option - ( - a - ) - - - - diff --git a/doc/source/images/mapper.svg b/doc/source/images/mapper.svg deleted file mode 100644 index b5a2b7b12f..0000000000 --- a/doc/source/images/mapper.svg +++ /dev/null @@ -1,237 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - OpenStack - Service - - - - - - - - - - - - - - - Mapper - - - Auth - 1 - - - Auth - 2 - - - Auth - 3 - - - - - diff --git a/doc/source/images/proxyAuth.svg b/doc/source/images/proxyAuth.svg deleted file mode 100644 index f60b40d813..0000000000 --- a/doc/source/images/proxyAuth.svg +++ /dev/null @@ -1,238 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - Authorization - : - Basic dTpw - X - - - Authorization - : - Proxy U - Authorization - : - Basic VTpQ - 500 - Internal Error - 403 - Proxy Unauthorized - - - - - Auth - Component - - - - - OpenStack - Service - - - - - - - diff --git a/doc/source/keystone.conf.rst b/doc/source/keystone.conf.rst deleted file mode 100644 index c4d4dcd949..0000000000 --- a/doc/source/keystone.conf.rst +++ /dev/null @@ -1,112 +0,0 @@ -.. - 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. - -keystone.conf example -===================== -:: - - [DEFAULT] - # Show more verbose log output (sets INFO log level output) - verbose = False - - # Show debugging output in logs (sets DEBUG log level output) - debug = False - - # Which backend store should Keystone use by default. - # Default: 'sqlite' - # Available choices are 'sqlite' [future will include LDAP, PAM, etc] - default_store = sqlite - - # Log to this file. Make sure you do not set the same log - # file for both the API and registry servers! - log_file = %DEST%/keystone/keystone.log - - # List of backends to be configured - backends = keystone.backends.sqlalchemy - #For LDAP support, add: ,keystone.backends.ldap - - # Dictionary Maps every service to a header.Missing services would get header - # X_(SERVICE_NAME) Key => Service Name, Value => Header Name - service-header-mappings = { - 'nova' : 'X-Server-Management-Url', - 'swift' : 'X-Storage-Url', - 'cdn' : 'X-CDN-Management-Url'} - - #List of extensions currently loaded. - #Refer docs for list of supported extensions. - extensions= osksadm,oskscatalog - - # Address to bind the API server - # TODO Properties defined within app not available via pipeline. - service_host = 0.0.0.0 - - # Port the bind the API server to - service_port = 5000 - - # Address to bind the Admin API server - admin_host = 0.0.0.0 - - # Port the bind the Admin API server to - admin_port = 35357 - - #Role that allows to perform admin operations. - keystone-admin-role = KeystoneAdmin - - #Role that allows to perform service admin operations. - keystone-service-admin-role = KeystoneServiceAdmin - - [keystone.backends.sqlalchemy] - # SQLAlchemy connection string for the reference implementation registry - # server. Any valid SQLAlchemy connection string is fine. - # See: http://bit.ly/ideIpI - #sql_connection = sqlite:///keystone.db - sql_connection = %SQL_CONN% - backend_entities = ['UserRoleAssociation', 'Endpoints', 'Role', 'Tenant', - 'User', 'Credentials', 'EndpointTemplates', 'Token', - 'Service'] - - # Period in seconds after which SQLAlchemy should reestablish its connection - # to the database. - sql_idle_timeout = 30 - - [pipeline:admin] - pipeline = - urlnormalizer - d5_compat - admin_api - - [pipeline:keystone-legacy-auth] - pipeline = - urlnormalizer - legacy_auth - d5_compat - service_api - - [app:service_api] - paste.app_factory = keystone.server:service_app_factory - - [app:admin_api] - paste.app_factory = keystone.server:admin_app_factory - - [filter:urlnormalizer] - paste.filter_factory = keystone.frontends.normalizer:filter_factory - - [filter:legacy_auth] - paste.filter_factory = keystone.frontends.legacy_token_auth:filter_factory - - [filter:d5_compat] - paste.filter_factory = keystone.frontends.d5_compat:filter_factory - diff --git a/doc/source/man/keystone-admin.rst b/doc/source/man/keystone-admin.rst deleted file mode 100644 index c22504e407..0000000000 --- a/doc/source/man/keystone-admin.rst +++ /dev/null @@ -1,87 +0,0 @@ -============== -keystone-admin -============== - ---------------------------- -Keystone Management Utility ---------------------------- - -:Author: keystone@lists.launchpad.net -:Date: 2011-10-31 -:Copyright: OpenStack LLC -:Version: 0.1.2 -:Manual section: 1 -:Manual group: cloud computing - -SYNOPSIS -======== - - keystone-admin [options] - -DESCRIPTION -=========== - -keystone-admin starts the administrative API server for Keystone. -Use :doc:`keystone-control` to stop/start/restart and manage those services -once started. - -USAGE -===== - - ``keystone-admin [options]`` - -Common Options --------------- - - --version show program's version number and exit - -h, --help show this help message and exit - -v, --verbose Print more verbose output - -d, --debug Print debugging output to console - -c PATH, --config-file=PATH Path to the config file to use. When not - specified (the default), we generally look at - the first argument specified to be a config - file, and if that is also missing, we search - standard directories for a config file. - -p BIND_PORT, --port=BIND_PORT, --bind-port=BIND_PORT - specifies port to listen on (default is 5000) - --host=BIND_HOST, --bind-host=BIND_HOST - specifies host address to listen on (default - is all or 0.0.0.0) - -t, --trace-calls Turns on call tracing for troubleshooting - -a PORT, --admin-port=PORT Specifies port for Admin API to listen on - (default is 35357) - -Logging Options ---------------- - -The following configuration options are specific to logging -functionality for this program. - - --log-config=PATH If this option is specified, the logging - configuration file specified is used and - overrides any other logging options specified. - Please see the Python logging module - documentation for details on logging - configuration files. - --log-date-format=FORMAT Format string for %(asctime)s in log records. - Default: %Y-%m-%d %H:%M:%S - --log-file=PATH (Optional) Name of log file to output to. If - not set, logging will go to stdout. - --log-dir=LOG_DIR (Optional) The directory to keep log files in - (will be prepended to --logfile) - -FILES -===== - -None - -SEE ALSO -======== - -* `Keystone `__ - -SOURCE -====== - -* Keystone is sourced in GitHub `Keystone `__ -* Keystone bugs are managed at Launchpad `Launchpad Keystone `__ diff --git a/doc/source/man/keystone-auth.rst b/doc/source/man/keystone-auth.rst deleted file mode 100644 index 1cec5a9524..0000000000 --- a/doc/source/man/keystone-auth.rst +++ /dev/null @@ -1,87 +0,0 @@ -============= -keystone-auth -============= - ---------------------------- -Keystone Management Utility ---------------------------- - -:Author: keystone@lists.launchpad.net -:Date: 2011-10-31 -:Copyright: OpenStack LLC -:Version: 0.1.2 -:Manual section: 1 -:Manual group: cloud computing - -SYNOPSIS -======== - - keystone-auth [options] - -DESCRIPTION -=========== - -keystone-auth starts the service API server for Keystone. -Use :doc:`keystone-control` to stop/start/restart and manage those services -once started. - -USAGE -===== - - ``keystone-auth [options]`` - -Common Options: -^^^^^^^^^^^^^^^ - - --version show program's version number and exit - -h, --help show this help message and exit - -v, --verbose Print more verbose output - -d, --debug Print debugging output to console - -c PATH, --config-file=PATH Path to the config file to use. When not - specified (the default), we generally look at - the first argument specified to be a config - file, and if that is also missing, we search - standard directories for a config file. - -p BIND_PORT, --port=BIND_PORT, --bind-port=BIND_PORT - specifies port to listen on (default is 5000) - --host=BIND_HOST, --bind-host=BIND_HOST - specifies host address to listen on (default - is all or 0.0.0.0) - -t, --trace-calls Turns on call tracing for troubleshooting - -a PORT, --admin-port=PORT Specifies port for Admin API to listen on - (default is 35357) - -Logging Options: -^^^^^^^^^^^^^^^^ - -The following configuration options are specific to logging -functionality for this program. - - --log-config=PATH If this option is specified, the logging - configuration file specified is used and - overrides any other logging options specified. - Please see the Python logging module - documentation for details on logging - configuration files. - --log-date-format=FORMAT Format string for %(asctime)s in log records. - Default: %Y-%m-%d %H:%M:%S - --log-file=PATH (Optional) Name of log file to output to. If - not set, logging will go to stdout. - --log-dir=LOG_DIR (Optional) The directory to keep log files in - (will be prepended to --logfile) - -FILES -===== - -None - -SEE ALSO -======== - -* `Keystone `__ - -SOURCE -====== - -* Keystone is sourced in GitHub `Keystone `__ -* Keystone bugs are managed at Launchpad `Launchpad Keystone `__ diff --git a/doc/source/man/keystone-control.rst b/doc/source/man/keystone-control.rst deleted file mode 100644 index 8927647ed2..0000000000 --- a/doc/source/man/keystone-control.rst +++ /dev/null @@ -1,101 +0,0 @@ -================ -keystone-control -================ - ---------------------------- -Keystone Management Utility ---------------------------- - -:Author: keystone@lists.launchpad.net -:Date: 2011-10-31 -:Copyright: OpenStack LLC -:Version: 0.1.2 -:Manual section: 1 -:Manual group: cloud computing - -SYNOPSIS -======== - - keystone-control [options] () - -DESCRIPTION -=========== - -keystone-control is the command line tool that interacts with the keystone -service to configure Keystone - -USAGE -===== - - ``keystone-control [options] ()`` - -where server is one of: - -* all -* auth -* admin - -and command is one of: - -* start -* stop -* shutdown -* restart -* reload -* force-reload - -Common Options: -^^^^^^^^^^^^^^^ - - --version show program's version number and exit - -h, --help show this help message and exit - -v, --verbose Print more verbose output - -d, --debug Print debugging output to console - -c PATH, --config-file=PATH Path to the config file to use. When not - specified (the default), we generally look at - the first argument specified to be a config - file, and if that is also missing, we search - standard directories for a config file. - -p BIND_PORT, --port=BIND_PORT, --bind-port=BIND_PORT - specifies port to listen on (default is 5000) - --host=BIND_HOST, --bind-host=BIND_HOST - specifies host address to listen on (default - is all or 0.0.0.0) - -t, --trace-calls Turns on call tracing for troubleshooting - -a PORT, --admin-port=PORT Specifies port for Admin API to listen on - (default is 35357) - -Logging Options: -^^^^^^^^^^^^^^^^ - -The following configuration options are specific to logging -functionality for this program. - - --log-config=PATH If this option is specified, the logging - configuration file specified is used and - overrides any other logging options specified. - Please see the Python logging module - documentation for details on logging - configuration files. - --log-date-format=FORMAT Format string for %(asctime)s in log records. - Default: %Y-%m-%d %H:%M:%S - --log-file=PATH (Optional) Name of log file to output to. If - not set, logging will go to stdout. - --log-dir=LOG_DIR (Optional) The directory to keep log files in - (will be prepended to --logfile) - -FILES -===== - -None - -SEE ALSO -======== - -* `Keystone `__ - -SOURCE -====== - -* Keystone is sourced in GitHub `Keystone `__ -* Keystone bugs are managed at Launchpad `Launchpad Keystone `__ diff --git a/doc/source/man/keystone-import.rst b/doc/source/man/keystone-import.rst deleted file mode 100644 index 69abd93032..0000000000 --- a/doc/source/man/keystone-import.rst +++ /dev/null @@ -1,86 +0,0 @@ -=============== -keystone-import -=============== - ---------------------------- -Keystone Management Utility ---------------------------- - -:Author: keystone@lists.launchpad.net -:Date: 2011-10-31 -:Copyright: OpenStack LLC -:Version: 0.1.2 -:Manual section: 1 -:Manual group: cloud computing - -SYNOPSIS -======== - - keystone-import [options] filename - -DESCRIPTION -=========== - -keystone-import takes a file of commands written in the same format as using -:doc:`keystone-manage` and imports that data into Keystone. It is intended to -import users, tenants, and EC2 credentials from nova into keystone. - -USAGE -===== - - ``keystone-import [options] filename`` - -Common Options: -^^^^^^^^^^^^^^^ - --version show program's version number and exit - -h, --help show this help message and exit - -v, --verbose Print more verbose output - -d, --debug Print debugging output to console - -c PATH, --config-file=PATH Path to the config file to use. When not - specified (the default), we generally look at - the first argument specified to be a config - file, and if that is also missing, we search - standard directories for a config file. - -p BIND_PORT, --port=BIND_PORT, --bind-port=BIND_PORT - specifies port to listen on (default is 5000) - --host=BIND_HOST, --bind-host=BIND_HOST - specifies host address to listen on (default - is all or 0.0.0.0) - -t, --trace-calls Turns on call tracing for troubleshooting - -a PORT, --admin-port=PORT Specifies port for Admin API to listen on - (default is 35357) - -Logging Options: -^^^^^^^^^^^^^^^^ - -The following configuration options are specific to logging -functionality for this program. - - --log-config=PATH If this option is specified, the logging - configuration file specified is used and - overrides any other logging options specified. - Please see the Python logging module - documentation for details on logging - configuration files. - --log-date-format=FORMAT Format string for %(asctime)s in log records. - Default: %Y-%m-%d %H:%M:%S - --log-file=PATH (Optional) Name of log file to output to. If - not set, logging will go to stdout. - --log-dir=LOG_DIR (Optional) The directory to keep log files in - (will be prepended to --logfile) - -FILES -===== - -None - -SEE ALSO -======== - -* `Keystone `__ - -SOURCE -====== - -* Keystone is sourced in GitHub `Keystone `__ -* Keystone bugs are managed at Launchpad `Launchpad Keystone `__ diff --git a/doc/source/man/keystone-manage.rst b/doc/source/man/keystone-manage.rst deleted file mode 100644 index 9e9304f04d..0000000000 --- a/doc/source/man/keystone-manage.rst +++ /dev/null @@ -1,192 +0,0 @@ -=============== -keystone-manage -=============== - ---------------------------- -Keystone Management Utility ---------------------------- - -:Author: keystone@lists.launchpad.net -:Date: 2010-11-16 -:Copyright: OpenStack LLC -:Version: 0.1.2 -:Manual section: 1 -:Manual group: cloud computing - -SYNOPSIS -======== - - keystone-manage [options] - -DESCRIPTION -=========== - -keystone-manage is the command line tool that interacts with the keystone -service to configure Keystone - -USAGE -===== - - ``keystone-manage [options] type action [additional args]`` - -user ----- - -* **user add** [username] [password] - - adds a user to Keystone's data store - -* **user list** - - lists all users - -* **user disable** [username] - - disables the user *username* - -tenant ------- - -* **tenant add** [tenant_name] - - adds a tenant to Keystone's data store - -* **tenant list** - - lists all users - -* **tenant disable** [tenant_name] - -role ----- - -Roles are used to associated users to tenants. Two roles are defined related -to the Keystone service in it's configuration file :doc:`../keystone.conf` - -* **role add** [role_name] - - adds a role - -* **role list** ([tenant_name]) - - lists all roles, or all roles for tenant, if tenant_name is provided - -* **role grant** [role_name] [username] ([tenant]) - - grants a role to a specific user. Granted globally if tenant_name is not - provided or granted for a specific tenant if tenant_name is provided. - -service -------- - -* **service add** [name] [type] [description] [owner_id] - - adds a service - -* **service list** - - lists all services with id, name, and type - -endpointTemplate ----------------- - -* **endpointTemplate add** [region] [service_name] [public_url] [admin_url] [internal_url] [enabled] [is_global] - - Add a service endpoint for keystone. - - example:: - - keystone-manage endpointTemplates add RegionOne \ - keystone \ - http://keystone_host:5000/v2.0 \ - http://keystone_host:35357/v2.0 \ - http://keystone_host:5000/v2.0 \ - 1 1 - -* **endpointTemplate list** ([tenant_name]) - - lists endpoint templates with service, region, and public_url. Restricted to - tenant endpoints if tenant_name is provided. - -token ------ - -* **token add** [token] [username] [tenant] [expiration] - - adds a token for a given user and tenant with an expiration - -* **token list** - - lists all tokens - -* **token delete** [token] - - deletes the identified token - -endpoint --------- - -* **endpoint add** [tenant_name] [endpoint_template] - - adds a tenant-specific endpoint - -credentials ------------ - -* **credentials add** [username] [type] [key] [password] ([tenant_name]) - -OPTIONS -======= - - --version show program's version number and exit - -h, --help show this help message and exit - -v, --verbose Print more verbose output - -d, --debug Print debugging output to console - -c PATH, --config-file=PATH Path to the config file to use. When not - specified (the default), we generally look at - the first argument specified to be a config - file, and if that is also missing, we search - standard directories for a config file. - -p BIND_PORT, --port=BIND_PORT, --bind-port=BIND_PORT - specifies port to listen on (default is 5000) - --host=BIND_HOST, --bind-host=BIND_HOST - specifies host address to listen on (default - is all or 0.0.0.0) - -t, --trace-calls Turns on call tracing for troubleshooting - -a PORT, --admin-port=PORT Specifies port for Admin API to listen on - (default is 35357) - -Logging Options: -================ - -The following configuration options are specific to logging -functionality for this program. - - --log-config=PATH If this option is specified, the logging - configuration file specified is used and - overrides any other logging options specified. - Please see the Python logging module - documentation for details on logging - configuration files. - --log-date-format=FORMAT Format string for %(asctime)s in log records. - Default: %Y-%m-%d %H:%M:%S - --log-file=PATH (Optional) Name of log file to output to. If - not set, logging will go to stdout. - --log-dir=LOG_DIR (Optional) The directory to keep log files in - (will be prepended to --logfile) - -FILES -===== - -None - -SEE ALSO -======== - -* `Keystone `__ - -SOURCE -====== - -* Keystone is sourced in GitHub `Keystone `__ -* Keystone bugs are managed at Launchpad `Launchpad Keystone `__ diff --git a/doc/source/man/keystone.rst b/doc/source/man/keystone.rst deleted file mode 100644 index 48e062e436..0000000000 --- a/doc/source/man/keystone.rst +++ /dev/null @@ -1,90 +0,0 @@ -======== -keystone -======== - ---------------------------- -Keystone Management Utility ---------------------------- - -:Author: keystone@lists.launchpad.net -:Date: 2010-11-16 -:Copyright: OpenStack LLC -:Version: 0.1.2 -:Manual section: 1 -:Manual group: cloud computing - -SYNOPSIS -======== - - keystone [options] - -DESCRIPTION -=========== - -keystone starts both the service and administrative API servers for Keystone. -Use :doc:`keystone-control` to stop/start/restart and manage those services -once started. - -USAGE -===== - - keystone ``keystone [options]`` - -Common Options: -^^^^^^^^^^^^^^^ - --version show program's version number and exit - -h, --help show this help message and exit - -The following configuration options are common to all keystone -programs.:: - - -v, --verbose Print more verbose output - -d, --debug Print debugging output to console - -c PATH, --config-file=PATH Path to the config file to use. When not - specified (the default), we generally look at - the first argument specified to be a config - file, and if that is also missing, we search - standard directories for a config file. - -p BIND_PORT, --port=BIND_PORT, --bind-port=BIND_PORT - specifies port to listen on (default is 5000) - --host=BIND_HOST, --bind-host=BIND_HOST - specifies host address to listen on (default - is all or 0.0.0.0) - -t, --trace-calls Turns on call tracing for troubleshooting - -a PORT, --admin-port=PORT Specifies port for Admin API to listen on - (default is 35357) - -Logging Options: -^^^^^^^^^^^^^^^^ - -The following configuration options are specific to logging -functionality for this program.:: - - --log-config=PATH If this option is specified, the logging - configuration file specified is used and - overrides any other logging options specified. - Please see the Python logging module - documentation for details on logging - configuration files. - --log-date-format=FORMAT Format string for %(asctime)s in log records. - Default: %Y-%m-%d %H:%M:%S - --log-file=PATH (Optional) Name of log file to output to. If - not set, logging will go to stdout. - --log-dir=LOG_DIR (Optional) The directory to keep log files in - (will be prepended to --logfile) - -FILES -===== - -None - -SEE ALSO -======== - -* `Keystone `__ - -SOURCE -====== - -* Keystone is sourced in GitHub `Keystone `__ -* Keystone bugs are managed at Launchpad `Launchpad Keystone `__ diff --git a/doc/source/man/sampledata.rst b/doc/source/man/sampledata.rst deleted file mode 100644 index 636d9bdeed..0000000000 --- a/doc/source/man/sampledata.rst +++ /dev/null @@ -1,85 +0,0 @@ -========== -sampledata -========== - ---------------------------- -Keystone Management Utility ---------------------------- - -:Author: keystone@lists.launchpad.net -:Date: 2011-10-31 -:Copyright: OpenStack LLC -:Version: 0.1.2 -:Manual section: 1 -:Manual group: cloud computing - -SYNOPSIS -======== - - sampledata [options] - -DESCRIPTION -=========== - -sampledata creates a development set of sample data for use with testing -keystone. - -USAGE -===== - - ``sampledata [options]`` - -Common Options: -^^^^^^^^^^^^^^^ - --version show program's version number and exit - -h, --help show this help message and exit - -v, --verbose Print more verbose output - -d, --debug Print debugging output to console - -c PATH, --config-file=PATH Path to the config file to use. When not - specified (the default), we generally look at - the first argument specified to be a config - file, and if that is also missing, we search - standard directories for a config file. - -p BIND_PORT, --port=BIND_PORT, --bind-port=BIND_PORT - specifies port to listen on (default is 5000) - --host=BIND_HOST, --bind-host=BIND_HOST - specifies host address to listen on (default - is all or 0.0.0.0) - -t, --trace-calls Turns on call tracing for troubleshooting - -a PORT, --admin-port=PORT Specifies port for Admin API to listen on - (default is 35357) - -Logging Options: -^^^^^^^^^^^^^^^^ - -The following configuration options are specific to logging -functionality for this program. - - --log-config=PATH If this option is specified, the logging - configuration file specified is used and - overrides any other logging options specified. - Please see the Python logging module - documentation for details on logging - configuration files. - --log-date-format=FORMAT Format string for %(asctime)s in log records. - Default: %Y-%m-%d %H:%M:%S - --log-file=PATH (Optional) Name of log file to output to. If - not set, logging will go to stdout. - --log-dir=LOG_DIR (Optional) The directory to keep log files in - (will be prepended to --logfile) - -FILES -===== - -None - -SEE ALSO -======== - -* `Keystone `__ - -SOURCE -====== - -* Keystone is sourced in GitHub `Keystone `__ -* Keystone bugs are managed at Launchpad `Launchpad Keystone `__ diff --git a/doc/source/serviceAPI_curl_examples.rst b/doc/source/serviceAPI_curl_examples.rst deleted file mode 100644 index d05afc9ff3..0000000000 --- a/doc/source/serviceAPI_curl_examples.rst +++ /dev/null @@ -1,69 +0,0 @@ -.. - 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. - -=============================== -Service API Examples Using Curl -=============================== - -The service API is defined to be a subset of the Admin API and, by -default, runs on port 5000. - -GET / -===== - -This call is identical to that documented for the Admin API, except -that it uses port 5000, instead of port 35357, by default:: - - $ curl http://0.0.0.0:5000 - -or:: - - $ curl http://0.0.0.0:5000/v2.0/ - -See the `Admin API Examples Using Curl`_ for more info. - -.. _`Admin API Examples Using Curl`: adminAPI_curl_examples.html - -GET /extensions -=============== - -This call is identical to that documented for the Admin API. - -POST /tokens -============ - -This call is identical to that documented for the Admin API. - -GET /tenants -============ - -List all of the tenants your token can access:: - - $ curl -H "X-Auth-Token:887665443383838" http://localhost:5000/v2.0/tenants - -Returns:: - - { - "tenants_links": [], - "tenants": [ - { - "enabled": true, - "description": "None", - "name": "customer-x", - "id": "1" - } - ] - } diff --git a/doc/source/testing.rst b/doc/source/testing.rst deleted file mode 100644 index 82a3360460..0000000000 --- a/doc/source/testing.rst +++ /dev/null @@ -1,77 +0,0 @@ -================ -Testing Keystone -================ - -Keystone uses a number of testing methodologies to ensure correctness. - -Running Built-In Tests -====================== - -To run the full suites of tests maintained within Keystone, run:: - - $ ./run_tests.sh --with-progress - -This shows realtime feedback during test execution, and iterates over -multiple configuration variations. - -This differs from how tests are executed from the continuous integration -environment. Specifically, Jenkins doesn't care about realtime progress, -and aborts after the first test failure (a fail-fast behavior):: - - $ ./run_tests.sh - -Testing Schema Migrations -========================= - -The application of schema migrations can be tested using SQLAlchemy Migrate’s built-in test runner, one migration at a time. - -.. WARNING:: - - This may leave your database in an inconsistent state; attempt this in non-production environments only! - -This is useful for testing the *next* migration in sequence (both forward & backward) in a database under version control:: - - $ python keystone/backends/sqlalchemy/migrate_repo/manage.py test --url=sqlite:///test.db --repository=keystone/backends/sqlalchemy/migrate_repo/ - -This command refers to a SQLite database used for testing purposes. Depending on the migration, this command alone does not make assertions as to the integrity of your data during migration. - -Writing Tests -============= - -Tests are maintained in the ``keystone.test`` module. Unit tests are -isolated from functional tests. - -Functional Tests ----------------- - -The ``keystone.test.functional.common`` module provides a ``unittest``-based -``httplib`` client which you can extend and use for your own tests. -Generally, functional tests should serve to illustrate intended use cases -and API behaviors. To help make your tests easier to read, the test client: - -- Authenticates with a known user name and password combination -- Asserts 2xx HTTP status codes (unless told otherwise) -- Abstracts keystone REST verbs & resources into single function calls - -Testing Multiple Configurations -------------------------------- - -Several variations of the default configuration are iterated over to -ensure test coverage of mutually exclusive featuresets, such as the -various backend options. - -These configuration templates are maintained in ``keystone/test/etc`` and -are iterated over by ``run_tests.py``. - -Further Testing -=============== - -devstack_ is the *best* way to quickly deploy keystone with the rest of the -OpenStack universe and should be critical step in your development workflow! - -You may also be interested in either the `OpenStack Continuous Integration Project`_ -or the `OpenStack Integration Testing Project`_. - -.. _devstack: http://devstack.org/ -.. _OpenStack Continuous Integration Project: https://github.com/openstack/openstack-ci -.. _OpenStack Integration Testing Project: https://github.com/openstack/openstack-integration-tests diff --git a/doc/source/usingkeystone.rst b/doc/source/usingkeystone.rst deleted file mode 100644 index bb52a94d09..0000000000 --- a/doc/source/usingkeystone.rst +++ /dev/null @@ -1,28 +0,0 @@ -.. - 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. - -============== -Using Keystone -============== - -Curl examples -------------- - -.. toctree:: - :maxdepth: 1 - - adminAPI_curl_examples - serviceAPI_curl_examples diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000000..79861705e5 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,159 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = build +SOURCEDIR = source +SPHINXAPIDOC = sphinx-apidoc + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " autodoc generate the autodoc templates" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + +autodoc: + $(SPHINXAPIDOC) -f -o $(SOURCEDIR) ../keystone + +html: autodoc + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/keystone.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/keystone.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/keystone" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/keystone" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/doc/design/keystone-service-registration.sdx b/docs/keystone_compat_flows.sdx similarity index 66% rename from doc/design/keystone-service-registration.sdx rename to docs/keystone_compat_flows.sdx index 7e91e00760..f1fcc5f02f 100644 --- a/doc/design/keystone-service-registration.sdx +++ b/docs/keystone_compat_flows.sdx @@ -1,37 +1,39 @@ -> -#!Keystone Admin registers an openstack service as a service supported by keystone. -#!Service credentials returned are provided to the actual service. -#! -#!Actual services use the service credentials to fetch the service token to create roles, endpoints templates, -#!endpoints specific to service and also to validate tokens. -#! -#!We could extend this behavior to allow any keystone operations carried on behalf of the service to happen -#!only using the service token. -#! -#!Keystone has its own roles to manage itself.Roles specific to a service are available only for that service. -#!<< -keystone-admin:Actor -/queue:FIFO -openstack:Service -keystone:Service + -openstack:keystone.Add Service Specific Endpoint Templates -openstack:keystone.Add Service Specific Endpoints -openstack:keystone.Validate Token +[c "Validate Token, Unscoped"] +client:{token, user, tenant=None}=compat.GET /v2.0/tokens/$token +compat:{token, user, tenant}=token.get_token($token) +[/c] + +[c "Validate Token, With Tenant"] +client:{token, user, tenant}=compat.GET /v2.0/tokens/$token?belongs_to=$tenant +compat:{token, user, tenant}=token.get_token($token) +[/c] + +[c "Tenants for Token"] +client:{tenants}=compat.(X-Auth-Token: $token) GET /v2.0/tenants +compat:{token, user, tenant}=token.get_token($token) +compat:{token, user, tenant}=identity.get_tenants($user) [/c]]]> diff --git a/doc/source/_templates/.placeholder b/docs/source/_templates/.placeholder similarity index 100% rename from doc/source/_templates/.placeholder rename to docs/source/_templates/.placeholder diff --git a/doc/source/_theme/layout.html b/docs/source/_theme/layout.html similarity index 100% rename from doc/source/_theme/layout.html rename to docs/source/_theme/layout.html diff --git a/doc/source/_theme/theme.conf b/docs/source/_theme/theme.conf similarity index 100% rename from doc/source/_theme/theme.conf rename to docs/source/_theme/theme.conf diff --git a/doc/source/adminAPI_curl_examples.rst b/docs/source/api_curl_examples.rst similarity index 90% rename from doc/source/adminAPI_curl_examples.rst rename to docs/source/api_curl_examples.rst index 81f96c36d3..686e8bd59c 100644 --- a/doc/source/adminAPI_curl_examples.rst +++ b/docs/source/api_curl_examples.rst @@ -14,6 +14,61 @@ License for the specific language governing permissions and limitations under the License. + +=============================== +Service API Examples Using Curl +=============================== + +The service API is defined to be a subset of the Admin API and, by +default, runs on port 5000. + +GET / +===== + +This call is identical to that documented for the Admin API, except +that it uses port 5000, instead of port 35357, by default:: + + $ curl http://0.0.0.0:5000 + +or:: + + $ curl http://0.0.0.0:5000/v2.0/ + +See the `Admin API Examples Using Curl`_ for more info. + +.. _`Admin API Examples Using Curl`: adminAPI_curl_examples.html + +GET /extensions +=============== + +This call is identical to that documented for the Admin API. + +POST /tokens +============ + +This call is identical to that documented for the Admin API. + +GET /tenants +============ + +List all of the tenants your token can access:: + + $ curl -H "X-Auth-Token:887665443383838" http://localhost:5000/v2.0/tenants + +Returns:: + + { + "tenants_links": [], + "tenants": [ + { + "enabled": true, + "description": "None", + "name": "customer-x", + "id": "1" + } + ] + } + ============================= Admin API Examples Using Curl ============================= diff --git a/docs/source/architecture.rst b/docs/source/architecture.rst new file mode 100644 index 0000000000..b308a9e5ad --- /dev/null +++ b/docs/source/architecture.rst @@ -0,0 +1,203 @@ +.. + 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. + +Keystone Architecture +===================== + +Much of the design is precipitated from the expectation that the auth backends +for most deployments will actually be shims in front of existing user systems. + +------------ +The Services +------------ + +Keystone is organized as a group of services exposed on one or many endpoints. +Many of these services are used in a combined fashion by the frontend, for +example an authenticate call will validate user/tenant credentials with the +Identity service and, upon success, create and return a token with the Token +service. + + +Identity +-------- + +The Identity service provides auth credential validation and data about Users, +Tenants and Roles, as well as any associated metadata. + +In the basic case all this data is managed by the service, allowing the service +to manage all the CRUD associated with the data. + +In other cases, this data is pulled, by varying degrees, from an authoritative +backend service. An example of this would be when backending on LDAP. See +`LDAP Backend` below for more details. + + +Token +----- + +The Token service validates and manages Tokens used for authenticating requests +once a user/tenant's credentials have already been verified. + + +Catalog +------- + +The Catalog service provides an endpoint registry used for endpoint discovery. + + +Policy +------ + +The Policy service provides a rule-based authorization engine and the +associated rule management interface. + +---------- +Data Model +---------- + +Keystone was designed from the ground up to be amenable to multiple styles of +backends and as such many of the methods and data types will happily accept +more data than they know what to do with and pass them on to a backend. + +There are a few main data types: + + * **User**: has account credentials, is associated with one or more tenants + * **Tenant**: unit of ownership in openstack, contains one or more users + * **Role**: a first-class piece of metadata associated with many user-tenant pairs. + * **Token**: identifying credential associated with a user or user and tenant + * **Extras**: bucket of key-value metadata associated with a user-tenant pair. + * **Rule**: describes a set of requirements for performing an action. + +While the general data model allows a many-to-many relationship between Users +and Tenants and a many-to-one relationship between Extras and User-Tenant pairs, +the actual backend implementations take varying levels of advantage of that +functionality. + + +KVS Backend +----------- + +A simple backend interface meant to be further backended on anything that can +support primary key lookups, the most trivial implementation being an in-memory +dict. + +Supports all features of the general data model. + + +PAM Backend +----------- + +Extra simple backend that uses the current system's PAM service to authenticate, +providing a one-to-one relationship between Users and Tenants with the `root` +User also having the 'admin' role. + + +Templated Backend +----------------- + +Largely designed for a common use case around service catalogs in the Keystone +project, a Catalog backend that simply expands pre-configured templates to +provide catalog data. + +Example paste.deploy config (uses $ instead of % to avoid ConfigParser's +interpolation):: + + [DEFAULT] + catalog.RegionOne.identity.publicURL = http://localhost:$(public_port)s/v2.0 + catalog.RegionOne.identity.adminURL = http://localhost:$(public_port)s/v2.0 + catalog.RegionOne.identity.internalURL = http://localhost:$(public_port)s/v2.0 + catalog.RegionOne.identity.name = 'Identity Service' + + +---------------- +Approach to CRUD +---------------- + +While it is expected that any "real" deployment at a large company will manage +their users, tenants and other metadata in their existing user systems, a +variety of CRUD operations are provided for the sake of development and testing. + +CRUD is treated as an extension or additional feature to the core feature set in +that it is not required that a backend support it. + + +---------------------------------- +Approach to Authorization (Policy) +---------------------------------- + +Various components in the system require that different actions are allowed +based on whether the user is authorized to perform that action. + +For the purposes of Keystone Light there are only a couple levels of +authorization being checked for: + + * Require that the performing user is considered an admin. + * Require that the performing user matches the user being referenced. + +Other systems wishing to use the policy engine will require additional styles +of checks and will possibly write completely custom backends. Backends included +in Keystone Light are: + + +Trivial True +------------ + +Allows all actions. + + +Simple Match +------------ + +Given a list of matches to check for, simply verify that the credentials +contain the matches. For example:: + + credentials = {'user_id': 'foo', 'is_admin': 1, 'roles': ['nova:netadmin']} + + # An admin only call: + policy_api.can_haz(('is_admin:1',), credentials) + + # An admin or owner call: + policy_api.can_haz(('is_admin:1', 'user_id:foo'), + credentials) + + # A netadmin call: + policy_api.can_haz(('roles:nova:netadmin',), + credentials) + + +Credentials are generally built from the user metadata in the 'extras' part +of the Identity API. So, adding a 'role' to the user just means adding the role +to the user metadata. + + +Capability RBAC +--------------- + +(Not yet implemented.) + +Another approach to authorization can be action-based, with a mapping of roles +to which capabilities are allowed for that role. For example:: + + credentials = {'user_id': 'foo', 'is_admin': 1, 'roles': ['nova:netadmin']} + + # add a policy + policy_api.add_policy('action:nova:add_network', ('roles:nova:netadmin',)) + + policy_api.can_haz(('action:nova:add_network',), credentials) + + +In the backend this would look up the policy for 'action:nova:add_network' and +then do what is effectively a 'Simple Match' style match against the creds. diff --git a/doc/source/community.rst b/docs/source/community.rst similarity index 73% rename from doc/source/community.rst rename to docs/source/community.rst index bbad242147..d3e3217870 100644 --- a/doc/source/community.rst +++ b/docs/source/community.rst @@ -33,20 +33,6 @@ from blueprint designs to documentation to testing to deployment scripts. .. _Launchpad: https://launchpad.net/keystone .. _wiki: http://wiki.openstack.org/ - - -Contributing Code ------------------ - -To contribute code, sign up for a Launchpad account and sign a contributor license agreement, -available on the ``_. Once the CLA is signed you -can contribute code through the Gerrit version control system which is related to your Launchpad account. - -To contribute tests, docs, code, etc, refer to our `Gerrit-Jenkins-Github Workflow`_. - -.. _`Gerrit-Jenkins-Github Workflow`: http://wiki.openstack.org/GerritJenkinsGithub - - #openstack on Freenode IRC Network ---------------------------------- @@ -68,10 +54,10 @@ to write drafts for specs or documentation, describe a blueprint, or collaborate Keystone on Launchpad --------------------- -Launchpad is a code hosting service that hosts the Keystone source code. From -Launchpad you can report bugs, ask questions, and register blueprints (feature requests). +Launchpad is a code hosting that OpenStack is using to track bugs, feature work, and releases of OpenStack. Like other OpenStack projects, Keystone source code is hosted on GitHub -* `Launchpad Keystone Page `_ +* `Keystone Project Page on Launchpad `_ +* `Keystone Source Repository on GitHub `_ OpenStack Blog -------------- @@ -82,9 +68,9 @@ events and posts from OpenStack contributors. `OpenStack Blog `_ -See also: `Planet OpenStack `_, aggregating blogs -about OpenStack from around the internet into a single feed. If you'd like to contribute to this blog -aggregation with your blog posts, there are instructions for `adding your blog `_. +See also: `Planet OpenStack `_, an aggregation of blogs +about OpenStack from around the internet, combined into a web site and RSS feed. If you'd like to +contribute with your blog posts, there are instructions for `adding your blog `_. Twitter ------- diff --git a/doc/source/conf.py b/docs/source/conf.py similarity index 65% rename from doc/source/conf.py rename to docs/source/conf.py index ac43451cd8..fc7d94766b 100644 --- a/doc/source/conf.py +++ b/docs/source/conf.py @@ -1,25 +1,9 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2010 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 +# keystone documentation build configuration file, created by +# sphinx-quickstart on Mon Jan 9 12:02:59 2012. # -# 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. - -# -# Keystone documentation build configuration file, created by -# sphinx-quickstart on Tue May 18 13:50:15 2010. -# -# This file is execfile()'d with the current directory set to it's containing -# dir. +# This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. @@ -27,29 +11,26 @@ # All configuration values have a default; values that are commented out # serve to show the default. -import os import sys +import os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -sys.path = [os.path.abspath('../../keystone'), - os.path.abspath('../..'), - os.path.abspath('../../bin') - ] + sys.path +sys.path.insert(0, os.path.abspath('../..')) -# -- General configuration --------------------------------------------------- +# -- General configuration ----------------------------------------------------- -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +#extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.coverage'] extensions = ['sphinx.ext.autodoc', - 'sphinx.ext.coverage', - 'sphinx.ext.viewcode', - 'sphinx.ext.ifconfig', - 'sphinx.ext.intersphinx', - 'sphinx.ext.pngmath', - 'sphinx.ext.graphviz', - 'sphinx.ext.todo'] + 'sphinx.ext.todo', +# 'sphinx.ect.intersphinx', + 'sphinx.ext.coverage'] todo_include_todos = True @@ -64,25 +45,23 @@ else: source_suffix = '.rst' # The encoding of source files. -#source_encoding = 'utf-8' +#source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. -project = u'Keystone' -copyright = u'2011-present, OpenStack, LLC.' +project = u'keystone' +copyright = u'2012, OpenStack, LLC' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -from keystone import version +version = '2012.1' # The full version, including alpha/beta/rc tags. -release = version.version() -# The short X.Y version. -version = version.canonical_version() +release = '2012.1-dev' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -94,14 +73,11 @@ version = version.canonical_version() # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' -# List of documents that shouldn't be included in the build. -#unused_docs = [] +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = [] -# List of directories, relative to source directory, that shouldn't be searched -# for source files. -exclude_trees = [] - -# The reST default role (for this markup: `text`) to use for all documents. +# The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. @@ -129,23 +105,15 @@ modindex_common_prefix = ['keystone.'] man_pages = [ ('man/keystone-manage', 'keystone-manage', u'Keystone Management Utility', [u'OpenStack'], 1), - ('man/keystone', 'keystone', u'Keystone Startup Command', + ('man/keystone-all', 'keystone-all', u'Keystone Startup Command', [u'OpenStack'], 1), - ('man/keystone-auth', 'keystone-auth', u'Keystone Startup Command', - [u'OpenStack'], 1), - ('man/keystone-admin', 'keystone-admin', u'Keystone Startup Command', - [u'OpenStack'], 1), - ('man/keystone-import', 'keystone-import', u'Keystone Management Utility', - [u'OpenStack'], 1), - ('man/keystone-control', 'keystone-control', - u'Keystone Management Utility', [u'OpenStack'], 1) ] -# -- Options for HTML output ------------------------------------------------- +# -- Options for HTML output --------------------------------------------------- -# The theme to use for HTML and HTML Help pages. Major themes that come with -# Sphinx are currently 'default' and 'sphinxdoc'. +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. html_theme_path = ["."] html_theme = '_theme' @@ -155,7 +123,7 @@ html_theme = '_theme' #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = ['_theme'] +#html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". @@ -176,7 +144,7 @@ html_theme = '_theme' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static', 'images'] +html_static_path = ['static', 'images'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. @@ -194,7 +162,7 @@ html_static_path = ['_static', 'images'] #html_additional_pages = {} # If false, no module index is generated. -#html_use_modindex = True +#html_domain_indices = True # If false, no index is generated. #html_use_index = True @@ -205,32 +173,42 @@ html_static_path = ['_static', 'images'] # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' -# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = '' +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'keystonedoc' -# -- Options for LaTeX output ------------------------------------------------ +# -- Options for LaTeX output -------------------------------------------------- -# The paper size ('letter' or 'a4'). -#latex_paper_size = 'letter' +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). -#latex_font_size = '10pt' +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', +} # Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, -# documentclass [howto/manual]). +# (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('index', 'Keystone.tex', u'Keystone Documentation', - u'Keystone Team', 'manual'), + ('index', 'keystone.tex', u'Keystone Documentation', + u'OpenStack', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of @@ -241,16 +219,55 @@ latex_documents = [ # not chapters. #latex_use_parts = False -# Additional stuff for the LaTeX preamble. -#latex_preamble = '' +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. -#latex_use_modindex = True +#latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'keystone', u'Keystone Documentation', + [u'OpenStack'], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------------ + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'keystone', u'Keystone Documentation', + u'OpenStack', 'keystone', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + # Example configuration for intersphinx: refer to the Python standard library. +#intersphinx_mapping = {'http://docs.python.org/': None} intersphinx_mapping = {'python': ('http://docs.python.org/', None), 'nova': ('http://nova.openstack.org', None), 'swift': ('http://swift.openstack.org', None), diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst new file mode 100644 index 0000000000..22f3748b1f --- /dev/null +++ b/docs/source/configuration.rst @@ -0,0 +1,488 @@ +.. + 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. + +==================== +Configuring Keystone +==================== + +.. toctree:: + :maxdepth: 1 + + man/keystone-manage + man/keystone-all + +Once Keystone is installed, it is configured via a primary configuration file +(``etc/keystone.conf``), possibly a separate logging configuration file, and +initializing data into keystone using the command line client. + + +Keystone Configuration File +=========================== + +The keystone configuration file is an 'ini' file format with sections, +extended from Paste_, a common system used to configure python WSGI based +applications. In addition to the paste config entries, general configuration +values are stored under ``[DEFAULT]``, ``[sql]``, ``[ec2]`` and then drivers +for the various services are included under their individual sections. + +The services include: +* ``[identity]`` - the python module that backends the identity system +* ``[catalog]`` - the python module that backends the service catalog +* ``[token]`` - the python module that backends the token providing mechanisms +* ``[policy]`` - the python module that drives the policy system for RBAC + +The keystone configuration file is expected to be named ``keystone.conf``. +When starting up Keystone, you can specify a different configuration file to +use with ``--config-file``. If you do **not** specify a configuration file, +keystone will look in the following directories for a configuration file, in +order: + +* ``~/.keystone`` +* ``~/`` +* ``/etc/keystone`` +* ``/etc`` + +Logging is configured externally to the rest of keystone, the file specifying +the logging configuration is in the [DEFAULT] section of the keystone conf +file under ``log_config``. If you wish to route all your logging through +syslog, there is a ``use_syslog`` option also in the [DEFAULT] section that +easy. + +A sample logging file is available with the project in the directory +``etc/logging.conf.sample``. Like other OpenStack projects, keystone uses the +`python logging module`, which includes extensive configuration options for +choosing the output levels and formats. + +In addition to this documentation page, you can check the ``etc/keystone.conf`` +sample configuration files distributed with keystone for example configuration +files for each server application. + +.. _Paste: http://pythonpaste.org/ +.. _`python logging module`: http://docs.python.org/library/logging.html + +Sample Configuration Files +-------------------------- + +* ``etc/keystone.conf`` +* ``etc/logging.conf.sample`` + +Running Keystone +================ + +Running keystone is simply starting the services by using the command:: + + keystone-all + +Invoking this command starts up two wsgi.Server instances, configured by the +``keystone.conf`` file as described above. One of these wsgi 'servers' is +``admin`` (the administration API) and the other is ``main`` (the +primary/public API interface). Both of these run in a single process. + +Migrating from legacy versions of keystone +========================================== +Migration support is provided for the following legacy keystone versions: + +* diablo-5 +* stable/diablo +* essex-2 +* essex-3 + +To migrate from legacy versions of keystone, use the following steps: + +Step 1: Configure keystone.conf +------------------------------- +It is important that the database that you specify be different from the one +containing your existing install. + +Step 2: db_sync your new, empty database +---------------------------------------- +Run the following command to configure the most recent schema in your new +keystone installation:: + + keystone-manage db_sync + +Step 3: Import your legacy data +------------------------------- +Use the following command to import your old data:: + + keystone-manage import_legacy [db_url, e.g. 'mysql://root@foobar/keystone'] + +Specify db_url as the connection string that was present in your old +keystone.conf file. + +Step 3: Import your legacy service catalog +------------------------------------------ +While the older keystone stored the service catalog in the database, +the updated version configures the service catalog using a template file. +An example service catalog template file may be found in +etc/default_catalog.templates. + +To import your legacy catalog, run this command:: + + keystone-manage export_legacy_catalog \ + [db_url e.g. 'mysql://root@foobar/keystone'] > \ + [path_to_templates e.g. 'etc/default_catalog.templates'] + +After executing this command, you will need to restart the keystone service to +see your changes. + +Initializing Keystone +===================== + +keystone-manage is designed to execute commands that cannot be administered +through the normal REST api. At the moment, the following calls are supported: + +* ``db_sync``: Sync the database. +* ``import_legacy``: Import a legacy (pre-essex) version of the db. +* ``export_legacy_catalog``: Export service catalog from a legacy (pre-essex) db. + + +Generally, the following is the first step after a source installation:: + + keystone-manage db_sync + +Invoking keystone-manage by itself will give you additional usage information. + +Adding Users, Tenants, and Roles with python-keystoneclient +=========================================================== + +User, tenants, and roles must be administered using admin credentials. +There are two ways to configure python-keystoneclient to use admin +credentials, using the token auth method, or password auth method. + +Token Auth Method +----------------- +To use keystone client using token auth, set the following flags + +* ``--endpoint SERVIVE_ENDPOINT`` : allows you to specify the keystone endpoint to communicate + with. The default endpoint is http://localhost:35357/v2.0' +* ``--token SERVIVE_TOKEN`` : your administrator service token. + +Password Auth Method +-------------------- + +* ``--username OS_USERNAME`` : allows you to specify the keystone endpoint to communicate + with. For example, http://localhost:35357/v2.0' +* ``--password OS_PASSWORD`` : Your administrator password +* ``--tenant_name OS_TENANT_NAME`` : Name of your tenant +* ``--auth_url OS_AUTH_URL`` : url of your keystone auth server, for example +http://localhost:5000/v2.0' + +Example usage +------------- +``keystone`` is set up to expect commands in the general form of +``keystone`` ``command`` ``argument``, followed by flag-like keyword arguments to +provide additional (often optional) information. For example, the command +``user-list`` and ``tenant-create`` can be invoked as follows:: + + # Using token auth env variables + export SERVICE_ENDPOINT=http://127.0.0.1:5000/v2.0/ + export SERVICE_TOKEN=secrete_token + keystone user-list + keystone tenant-create --name=demo + + # Using token auth flags + keystone --token=secrete --endpoint=http://127.0.0.1:5000/v2.0/ user-list + keystone --token=secrete --endpoint=http://127.0.0.1:5000/v2.0/ tenant-create --name=demo + + # Using user + password + tenant_name env variables + export OS_USERNAME=admin + export OS_PASSWORD=secrete + export OS_TENANT_NAME=admin + keystone user-list + keystone tenant-create --name=demo + + # Using user + password + tenant_name flags + keystone --username=admin --password=secrete --tenant_name=admin user-list + keystone --username=admin --password=secrete --tenant_name=admin tenant-create --name=demo + +Tenants +------- + +Tenants are the high level grouping within Keystone that represent groups of +users. A tenant is the grouping that owns virtual machines within Nova, or +containers within Swift. A tenant can have zero or more users, Users can +be associated with more than one tenant, and each tenant - user pairing can +have a role associated with it. + +``tenant-create`` +^^^^^^^^^^^^^^^^^ + +keyword arguments + +* name +* description (optional, defaults to None) +* enabled (optional, defaults to True) + +example:: + + keystone tenant-create --name=demo + +creates a tenant named "demo". + +``tenant-delete`` +^^^^^^^^^^^^^^^^^ + +arguments + +* tenant_id + +example:: + + keystone tenant-delete f2b7b39c860840dfa47d9ee4adffa0b3 + +``tenant-enable`` +^^^^^^^^^^^^^^^^^ + +arguments + +* tenant_id + +example:: + + keystone tenant-enable f2b7b39c860840dfa47d9ee4adffa0b3 + +``tenant-disable`` +^^^^^^^^^^^^^^^^^ + +arguments + +* tenant_id + +example:: + + keystone tenant-disable f2b7b39c860840dfa47d9ee4adffa0b3 + +Users +----- + +``user-create`` +^^^^^^^^^^^^^^^ + +keyword arguments + +* name +* pass +* email +* default_tenant (optional, defaults to None) +* enabled (optional, defaults to True) + +example:: + + keystone user-create + --name=admin \ + --pass=secrete \ + --email=admin@example.com + +``user-delete`` +^^^^^^^^^^^^^^^ + +keyword arguments + +* user + +example:: + + keystone user-delete f2b7b39c860840dfa47d9ee4adffa0b3 + +``user-list`` +^^^^^^^^^^^^^ + +list users in the system, optionally by a specific tenant (identified by tenant_id) + +arguments + +* tenant_id (optional, defaults to None) + +example:: + + keystone user-list + +``user-update-email`` +^^^^^^^^^^^^^^^^^^^^^ + +arguments +* user_id +* email + + +example:: + + keystone user-update-email 03c84b51574841ba9a0d8db7882ac645 "someone@somewhere.com" + +``user-enable`` +^^^^^^^^^^^^^^^^^^^^^^^ + +arguments + +* user_id + +example:: + + keystone user-enable 03c84b51574841ba9a0d8db7882ac645 + +``user-disable`` +^^^^^^^^^^^^^^^^^^^^^^^ + +arguments + +* user_id + +example:: + + keystone user-disable 03c84b51574841ba9a0d8db7882ac645 + + +``user-update-password`` +^^^^^^^^^^^^^^^^^^^^^^^^ + +arguments + +* user_id +* password + +example:: + + keystone user-update-password 03c84b51574841ba9a0d8db7882ac645 foo + +Roles +----- + +``role-create`` +^^^^^^^^^^^^^^^ + +arguments + +* name + +exmaple:: + + keystone role-create --name=demo + +``role-delete`` +^^^^^^^^^^^^^^^ + +arguments + +* role_id + +exmaple:: + + keystone role-delete 19d1d3344873464d819c45f521ff9890 + +``role-list`` +^^^^^^^^^^^^^^^ + +exmaple:: + + keystone role-list + +``role-get`` +^^^^^^^^^^^^ + +arguments + +* role_id + +exmaple:: + + keystone role-get role=19d1d3344873464d819c45f521ff9890 + + +``add-user-role`` +^^^^^^^^^^^^^^^^^^^^^^ + +arguments + +* role_id +* user_id +* tenant_id + +example:: + + keystone role add-user-role \ + 3a751f78ef4c412b827540b829e2d7dd \ + 03c84b51574841ba9a0d8db7882ac645 \ + 20601a7f1d94447daa4dff438cb1c209 + +``remove-user-role`` +^^^^^^^^^^^^^^^^^^^^^^^^^ + +arguments + +* role_id +* user_id +* tenant_id + +example:: + + keystone remove-user-role \ + 19d1d3344873464d819c45f521ff9890 \ + 08741d8ed88242ca88d1f61484a0fe3b \ + 20601a7f1d94447daa4dff438cb1c209 + +Services +-------- + +``service-create`` +^^^^^^^^^^^^^^^^^^ + +keyword arguments + +* name +* type +* description + +example:: + + keystone service create \ + --name=nova \ + --type=compute \ + --description="Nova Compute Service" + +``service-list`` +^^^^^^^^^^^^^^^^ + +arguments + +* service_id + +example:: + + keystone service-list + +``service-get`` +^^^^^^^^^^^^^^^ + +arguments + +* service_id + +example:: + + keystone service-get 08741d8ed88242ca88d1f61484a0fe3b + +``service-delete`` +^^^^^^^^^^^^^^^^^^ + +arguments + +* service_id + +example:: + + keystone service-delete 08741d8ed88242ca88d1f61484a0fe3b + diff --git a/docs/source/configuringservices.rst b/docs/source/configuringservices.rst new file mode 100644 index 0000000000..615187eae8 --- /dev/null +++ b/docs/source/configuringservices.rst @@ -0,0 +1,197 @@ +.. + 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. + +========================================== +Configuring Services to work with Keystone +========================================== + +.. toctree:: + :maxdepth: 1 + + nova-api-paste + middleware_architecture + +Once Keystone is installed and running (see :doc:`configuration`), services +need to be configured to work with it. To do this, we primarily install and +configure middleware for the OpenStack service to handle authentication tasks +or otherwise interact with Keystone. + +In general: +* Clients making calls to the service will pass in an authentication token. +* The Keystone middleware will look for and validate that token, taking the + appropriate action. +* It will also retrive additional information from the token such as user + name, id, tenant name, id, roles, etc... + +The middleware will pass those data down to the service as headers. More +details on the architecture of that setup is described in +:doc:`middleware_architecture` + +Setting up credentials +====================== + +Admin Token +----------- + +For a default installation of Keystone, before you can use the REST API, you +need to define an authorization token. This is configured in ``keystone.conf`` +file under the section ``[DEFAULT]``. In the sample file provided with the +keystone project, the line defining this token is + + [DEFAULT] + admin_token = ADMIN + +This configured token is a "shared secret" between keystone and other +openstack services (for example: nova, swift, glance, or horizon), and will +need to be set the same between those services in order for keystone services +to function correctly. + +Setting up tenants, users, and roles +------------------------------------ + +You need to minimally define a tenant, user, and role to link the tenant and +user as the most basic set of details to get other services authenticating +and authorizing with keystone. See doc:`configuration` for a walk through on +how to create tenants, users, and roles. + +Setting up services +=================== + +Defining Services +----------------- + +Keystone also acts as a service catalog to let other OpenStack systems know +where relevant API endpoints exist for OpenStack Services. The OpenStack +Dashboard, in particular, uses this heavily - and this **must** be configured +for the OpenStack Dashboard to properly function. + +Here's how we define the services:: + + keystone service-create --name=nova \ + --type=compute \ + --description="Nova Compute Service" + keystone service-create --name=ec2 \ + --type=ec2 \ + --description="EC2 Compatibility Layer" + keystone service-create --name=glance \ + --type=image \ + --description="Glance Image Service" + keystone service-create --name=keystone \ + --type=identity \ + --description="Keystone Identity Service" + keystone service-create --name=swift \ + --type=object-store \ + --description="Swift Service" + +The endpoints for these services are defined in a template, an example of +which is in the project as the file ``etc/default_catalog.templates``. + +Setting Up Middleware +===================== + +Keystone Auth-Token Middleware +-------------------------------- + +The Keystone auth_token middleware is a WSGI component that can be inserted in +the WSGI pipeline to handle authenticating tokens with Keystone. + +Configuring Nova to use Keystone +-------------------------------- + +To configure Nova to use Keystone for authentication, the Nova API service +can be run against the api-paste file provided by Keystone. This is most +easily accomplished by setting the `--api_paste_config` flag in nova.conf to +point to `examples/paste/nova-api-paste.ini` from Keystone. This paste file +included references to the WSGI authentication middleware provided with the +keystone installation. + +When configuring Nova, it is important to create a admin service token for +the service (from the Configuration step above) and include that as the key +'admin_token' in the nova-api-paste.ini. See the documented +:doc:`nova-api-paste` file for references. + +Configuring Swift to use Keystone +--------------------------------- + +Similar to Nova, swift can be configured to use Keystone for authentication +rather than it's built in 'tempauth'. + +1. Add a service endpoint for Swift to Keystone + +2. Configure the paste file for swift-proxy (`/etc/swift/swift-proxy.conf`) + +3. Reconfigure Swift's proxy server to use Keystone instead of TempAuth. + Here's an example `/etc/swift/proxy-server.conf`:: + + [DEFAULT] + bind_port = 8888 + user = + + [pipeline:main] + pipeline = catch_errors cache keystone proxy-server + + [app:proxy-server] + use = egg:swift#proxy + account_autocreate = true + + [filter:keystone] + use = egg:keystone#tokenauth + auth_protocol = http + auth_host = 127.0.0.1 + auth_port = 35357 + admin_token = 999888777666 + delay_auth_decision = 0 + service_protocol = http + service_host = 127.0.0.1 + service_port = 8100 + service_pass = dTpw + cache = swift.cache + + [filter:cache] + use = egg:swift#memcache + set log_name = cache + + [filter:catch_errors] + use = egg:swift#catch_errors + + Note that the optional "cache" property in the keystone filter allows any + service (not just Swift) to register its memcache client in the WSGI + environment. If such a cache exists, Keystone middleware will utilize it + to store validated token information, which could result in better overall + performance. + +4. Restart swift + +5. Verify that keystone is providing authentication to Swift + +Use `swift` to check everything works (note: you currently have to create a +container or upload something as your first action to have the account +created; there's a Swift bug to be fixed soon):: + + $ swift -A http://127.0.0.1:5000/v1.0 -U joeuser -K secrete post container + $ swift -A http://127.0.0.1:5000/v1.0 -U joeuser -K secrete stat -v + StorageURL: http://127.0.0.1:8888/v1/AUTH_1234 + Auth Token: 74ce1b05-e839-43b7-bd76-85ef178726c3 + Account: AUTH_1234 + Containers: 1 + Objects: 0 + Bytes: 0 + Accept-Ranges: bytes + X-Trans-Id: tx25c1a6969d8f4372b63912f411de3c3b + +.. WARNING:: + Keystone currently allows any valid token to do anything with any account. + diff --git a/docs/source/developing.rst b/docs/source/developing.rst new file mode 100644 index 0000000000..f7c0b87bf9 --- /dev/null +++ b/docs/source/developing.rst @@ -0,0 +1,150 @@ +.. + 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. + +======================== +Developing with Keystone +======================== + +Contributing Code +================= + +To contribute code, sign up for a Launchpad account and sign a contributor license agreement, +available on the ``_. Once the CLA is signed you +can contribute code through the Gerrit version control system which is related to your Launchpad account. + +To contribute tests, docs, code, etc, refer to our `Gerrit-Jenkins-Github Workflow`_. + +.. _`Gerrit-Jenkins-Github Workflow`: http://wiki.openstack.org/GerritJenkinsGithub + +Setup +----- + +Get your development environment set up according to :doc:`setup`. The instructions from here will +assume that you have installed keystone into a virtualenv. If you chose not to, simply exclude "tools/with_venv.sh" from the example commands below. + +Running Keystone +---------------- + +To run the keystone Admin and API server instances, use:: + + $ tools/with_venv.sh bin/keystone-all + +this runs keystone with the configuration the etc/ directory of the project. See :doc:`configuration` for details on how Keystone is configured. + +Interacting with Keystone +------------------------- + +You can interact with Keystone through the command line using :doc:`man/keystone-manage` +which allows you to establish tenants, users, etc. + +You can also interact with Keystone through it's REST API. There is a python +keystone client library `python-keystoneclient`_ which interacts exclusively through +the REST API, and which keystone itself uses to provide it's command-line interface. + +When initially getting set up, after you've configured which databases to use, +you're probably going to need to run the following to your database schema in place :: + + $ bin/keystone-manage db_sync + + +.. _`python-keystoneclient`: https://github.com/openstack/python-keystoneclient + +Running Tests +============= + +To run the full suites of tests maintained within Keystone, run:: + + $ ./run_tests.sh + +This shows realtime feedback during test execution, iterates over +multiple configuration variations, and uses external projects to do +light integration testing to verify the keystone API against other projects. + +Test Structure +-------------- + +``./run_test.sh`` uses its python cohort (``run_tests.py``) to iterate +through the ``tests`` directory, using Nosetest to collect the tests and +invoke them using an OpenStack custom test running that displays the tests +as well as the time taken to +run those tests. + +Within the tests directory, the general structure of the tests is a basic +set of tests represented under a test class, and then subclasses of those +tests under other classes with different configurations to drive different +backends through the APIs. + +For example, ``test_backend.py`` has a sequence of tests under the class +``IdentityTests`` that will work with the default drivers as configured in +this projects etc/ directory. ``test_backend_sql.py`` subclasses those tests, +changing the configuration by overriding with configuration files stored in +the tests directory aimed at enabling the SQL backend for the Identity module. + +Likewise, ``test_cli.py`` takes advantage of the tests written aainst +``test_keystoneclient`` to verify the same tests function through different +drivers. + +Testing Schema Migrations +------------------------- + +The application of schema migrations can be tested using SQLAlchemy Migrate’s +built-in test runner, one migration at a time. + +.. WARNING:: + + This may leave your database in an inconsistent state; attempt this in non-production environments only! + +This is useful for testing the *next* migration in sequence (both forward & backward) in a database under version control:: + + python keystone/common/sql/migrate_repo/manage.py test \ + --url=sqlite:///test.db \ + --repository=keystone/common/sql/migrate_repo/ + +This command references to a SQLite database (test.db) to be used. Depending on the migration, this command alone does not make assertions as to the integrity of your data during migration. + +Writing Tests +------------- + +To add tests covering all drivers, update the base test class (``test_backend.py``, ``test_legacy_compat.py``, and ``test_keystoneclient.py``). + +To add new drivers, subclass the ``test_backend.py`` (look towards ``test_backend_sql.py`` or ``test_backend_kvs.py`` for examples) and update the configuration of the test class in ``setUp()``. + +Further Testing +--------------- + +devstack_ is the *best* way to quickly deploy keystone with the rest of the +OpenStack universe and should be critical step in your development workflow! + +You may also be interested in either the `OpenStack Continuous Integration Project`_ +or the `OpenStack Integration Testing Project`_. + +.. _devstack: http://devstack.org/ +.. _OpenStack Continuous Integration Project: https://github.com/openstack/openstack-ci +.. _OpenStack Integration Testing Project: https://github.com/openstack/tempest + +Building the Documentation +========================== + +The documentation is all generated with Sphinx from within the docs directory. +To generate the full set of HTML documentation: + + cd docs + make autodoc + make html + make man + +the results are in the docs/build/html and docs/build/man directories +respectively. diff --git a/doc/source/images/authComp.svg b/docs/source/images/authComp.svg similarity index 100% rename from doc/source/images/authComp.svg rename to docs/source/images/authComp.svg diff --git a/doc/source/images/graphs_305.svg b/docs/source/images/graphs_305.svg similarity index 100% rename from doc/source/images/graphs_305.svg rename to docs/source/images/graphs_305.svg diff --git a/doc/source/images/graphs_authComp.svg b/docs/source/images/graphs_authComp.svg similarity index 100% rename from doc/source/images/graphs_authComp.svg rename to docs/source/images/graphs_authComp.svg diff --git a/doc/source/images/graphs_authCompDelegate.svg b/docs/source/images/graphs_authCompDelegate.svg similarity index 100% rename from doc/source/images/graphs_authCompDelegate.svg rename to docs/source/images/graphs_authCompDelegate.svg diff --git a/doc/source/images/graphs_both.svg b/docs/source/images/graphs_both.svg similarity index 100% rename from doc/source/images/graphs_both.svg rename to docs/source/images/graphs_both.svg diff --git a/doc/source/images/graphs_delegate_forbiden_basic.svg b/docs/source/images/graphs_delegate_forbiden_basic.svg similarity index 100% rename from doc/source/images/graphs_delegate_forbiden_basic.svg rename to docs/source/images/graphs_delegate_forbiden_basic.svg diff --git a/doc/source/images/graphs_delegate_forbiden_proxy.svg b/docs/source/images/graphs_delegate_forbiden_proxy.svg similarity index 100% rename from doc/source/images/graphs_delegate_forbiden_proxy.svg rename to docs/source/images/graphs_delegate_forbiden_proxy.svg diff --git a/doc/source/images/graphs_delegate_reject_basic.svg b/docs/source/images/graphs_delegate_reject_basic.svg similarity index 100% rename from doc/source/images/graphs_delegate_reject_basic.svg rename to docs/source/images/graphs_delegate_reject_basic.svg diff --git a/doc/source/images/graphs_delegate_reject_oauth.svg b/docs/source/images/graphs_delegate_reject_oauth.svg similarity index 100% rename from doc/source/images/graphs_delegate_reject_oauth.svg rename to docs/source/images/graphs_delegate_reject_oauth.svg diff --git a/doc/source/images/graphs_delegate_unimplemented.svg b/docs/source/images/graphs_delegate_unimplemented.svg similarity index 100% rename from doc/source/images/graphs_delegate_unimplemented.svg rename to docs/source/images/graphs_delegate_unimplemented.svg diff --git a/doc/source/images/graphs_mapper.svg b/docs/source/images/graphs_mapper.svg similarity index 100% rename from doc/source/images/graphs_mapper.svg rename to docs/source/images/graphs_mapper.svg diff --git a/doc/source/images/graphs_proxyAuth.svg b/docs/source/images/graphs_proxyAuth.svg similarity index 100% rename from doc/source/images/graphs_proxyAuth.svg rename to docs/source/images/graphs_proxyAuth.svg diff --git a/doc/source/images/images_layouts.svg b/docs/source/images/images_layouts.svg similarity index 100% rename from doc/source/images/images_layouts.svg rename to docs/source/images/images_layouts.svg diff --git a/doc/source/index.rst b/docs/source/index.rst similarity index 63% rename from doc/source/index.rst rename to docs/source/index.rst index deb70ebf96..5d7c80faee 100644 --- a/doc/source/index.rst +++ b/docs/source/index.rst @@ -18,9 +18,9 @@ Welcome to Keystone, the OpenStack Identity Service! ==================================================== -Keystone is a cloud identity service written in Python, which provides -authentication, authorization, and an OpenStack service catalog. It -implements `OpenStack's Identity API`_. +Keystone is an OpenStack project that provides Identity, Token, Catalog and +Policy services for use specifically by projects in the OpenStack family. +It implements `OpenStack's Identity API`_. This document describes Keystone for contributors of the project, and assumes that you are already familiar with Keystone from an `end-user perspective`_. @@ -41,81 +41,35 @@ Getting Started .. toctree:: :maxdepth: 1 - releases setup - testing - migration - extensions configuration - controllingservers configuringservices community - usingkeystone - -Administration -============== - -.. toctree:: - :maxdepth: 1 - - backends - migration - controllingservers - configuringservices - ssl - -Entities -======== - -.. toctree:: - :maxdepth: 1 - - endpoints - services - -API Use Case Examples -===================== - -.. toctree:: - :maxdepth: 1 - - adminAPI_curl_examples - serviceAPI_curl_examples - -Configuration File Examples -=========================== - -.. toctree:: - :maxdepth: 1 - - nova-api-paste - keystone.conf Man Pages -========= +--------- .. toctree:: :maxdepth: 1 - man/keystone-manage man/keystone - man/keystone-auth - man/keystone-admin - man/keystone-import - man/keystone-control - man/sampledata - -Developer Docs -============== + man/keystone-manage +Developers Documentation +======================== .. toctree:: - :maxdepth: 1 + :maxdepth: 1 - developing - architecture - middleware - middleware_architecture - sourcecode/autoindex + developing + architecture + api_curl_examples + +Code Documentation +================== +.. toctree:: + :maxdepth: 1 + + modules Indices and tables ================== @@ -123,3 +77,4 @@ Indices and tables * :ref:`genindex` * :ref:`modindex` * :ref:`search` + diff --git a/docs/source/man/keystone-all.rst b/docs/source/man/keystone-all.rst new file mode 100644 index 0000000000..fc2d68d7d2 --- /dev/null +++ b/docs/source/man/keystone-all.rst @@ -0,0 +1,83 @@ +======== +keystone +======== + +--------------------------- +Keystone Management Utility +--------------------------- + +:Author: keystone@lists.launchpad.net +:Date: 2010-11-16 +:Copyright: OpenStack LLC +:Version: 0.1.2 +:Manual section: 1 +:Manual group: cloud computing + +SYNOPSIS +======== + + keystone-all [options] + +DESCRIPTION +=========== + +keystone-all starts both the service and administrative APIs in a single +process to provide catalog, authorization, and authentication services for +OpenStack. + +USAGE +===== + + ``keystone-all [options]`` + +Common Options: +^^^^^^^^^^^^^^^ + -h, --help show this help message and exit + +The following configuration options are common to all keystone +programs.:: + + -h, --help show this help message and exit + --config-file=PATH Path to a config file to use. Multiple config files + can be specified, with values in later files taking + precedence. The default files used are: [] + -d, --debug Print debugging output + --nodebug Print debugging output + -v, --verbose Print more verbose output + --noverbose Print more verbose output + --log-config=PATH If this option is specified, the logging configuration + file specified is used and overrides any other logging + options specified. Please see the Python logging + module documentation for details on logging + configuration files. + --log-format=FORMAT A logging.Formatter log message format string which + may use any of the available logging.LogRecord + attributes. Default: none + --log-date-format=DATE_FORMAT + Format string for %(asctime)s in log records. Default: + none + --log-file=PATH (Optional) Name of log file to output to. If not set, + logging will go to stdout. + --log-dir=LOG_DIR (Optional) The directory to keep log files in (will be + prepended to --logfile) + --syslog-log-facility=SYSLOG_LOG_FACILITY + (Optional) The syslog facility to use when logging to + syslog (defaults to LOG_USER) + --use-syslog Use syslog for logging. + --nouse-syslog Use syslog for logging. + +FILES +===== + +None + +SEE ALSO +======== + +* `Keystone `__ + +SOURCE +====== + +* Keystone source is managed in GitHub `Keystone `__ +* Keystone bugs are managed at Launchpad `Launchpad Keystone `__ diff --git a/docs/source/man/keystone-manage.rst b/docs/source/man/keystone-manage.rst new file mode 100644 index 0000000000..91f2b9e753 --- /dev/null +++ b/docs/source/man/keystone-manage.rst @@ -0,0 +1,97 @@ +=============== +keystone-manage +=============== + +--------------------------- +Keystone Management Utility +--------------------------- + +:Author: keystone@lists.launchpad.net +:Date: 2010-11-16 +:Copyright: OpenStack LLC +:Version: 0.1.2 +:Manual section: 1 +:Manual group: cloud computing + +SYNOPSIS +======== + + keystone-manage [options] + +DESCRIPTION +=========== + +keystone-manage is the command line tool that interacts with the keystone +service to initialize and update data within Keystone. Generally, +keystone-manage is only used for operations that can not be accomplished +with through the keystone REST api, such data import/export and schema +migrations. + + +USAGE +===== + + ``keystone-manage [options] action [additional args]`` + + +General keystone-manage options: +-------------------------------- + +* ``--help`` : display verbose help output. + +Invoking keystone-manage by itself will give you some usage information. + +Available keystone-manage commands: + db_sync: Sync the database. + import_legacy: Import a legacy (pre-essex) version of the db. + export_legacy_catalog: Export service catalog from a legacy (pre-essex) db. + + +OPTIONS +======= + +Options: + -h, --help show this help message and exit + --config-file=PATH Path to a config file to use. Multiple config files + can be specified, with values in later files taking + precedence. The default files used are: [] + -d, --debug Print debugging output + --nodebug Print debugging output + -v, --verbose Print more verbose output + --noverbose Print more verbose output + --log-config=PATH If this option is specified, the logging configuration + file specified is used and overrides any other logging + options specified. Please see the Python logging + module documentation for details on logging + configuration files. + --log-format=FORMAT A logging.Formatter log message format string which + may use any of the available logging.LogRecord + attributes. Default: none + --log-date-format=DATE_FORMAT + Format string for %(asctime)s in log records. Default: + none + --log-file=PATH (Optional) Name of log file to output to. If not set, + logging will go to stdout. + --log-dir=LOG_DIR (Optional) The directory to keep log files in (will be + prepended to --logfile) + --syslog-log-facility=SYSLOG_LOG_FACILITY + (Optional) The syslog facility to use when logging to + syslog (defaults to LOG_USER) + --use-syslog Use syslog for logging. + --nouse-syslog Use syslog for logging. + +FILES +===== + +None + +SEE ALSO +======== + +* `Keystone `__ + +SOURCE +====== + +* Keystone is sourced in GitHub `Keystone `__ +* Keystone bugs are managed at Launchpad `Launchpad Keystone `__ diff --git a/doc/source/middleware_architecture.rst b/docs/source/middleware_architecture.rst similarity index 100% rename from doc/source/middleware_architecture.rst rename to docs/source/middleware_architecture.rst diff --git a/docs/source/nova-api-paste.rst b/docs/source/nova-api-paste.rst new file mode 100644 index 0000000000..602895ac1e --- /dev/null +++ b/docs/source/nova-api-paste.rst @@ -0,0 +1,143 @@ +.. + 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. + +nova-api-paste example +====================== +:: + + ####### + # EC2 # + ####### + + [composite:ec2] + use = egg:Paste#urlmap + /: ec2versions + /services/Cloud: ec2cloud + /services/Admin: ec2admin + /latest: ec2metadata + /2007-01-19: ec2metadata + /2007-03-01: ec2metadata + /2007-08-29: ec2metadata + /2007-10-10: ec2metadata + /2007-12-15: ec2metadata + /2008-02-01: ec2metadata + /2008-09-01: ec2metadata + /2009-04-04: ec2metadata + /1.0: ec2metadata + + [pipeline:ec2cloud] + pipeline = logrequest totoken authtoken keystonecontext cloudrequest authorizer ec2executor + + [pipeline:ec2admin] + pipeline = logrequest totoken authtoken keystonecontext adminrequest authorizer ec2executor + + [pipeline:ec2metadata] + pipeline = logrequest ec2md + + [pipeline:ec2versions] + pipeline = logrequest ec2ver + + [filter:logrequest] + paste.filter_factory = nova.api.ec2:RequestLogging.factory + + [filter:ec2lockout] + paste.filter_factory = nova.api.ec2:Lockout.factory + + [filter:totoken] + paste.filter_factory = keystone.middleware.ec2_token:EC2Token.factory + + [filter:ec2noauth] + paste.filter_factory = nova.api.ec2:NoAuth.factory + + [filter:authenticate] + paste.filter_factory = nova.api.ec2:Authenticate.factory + + [filter:cloudrequest] + controller = nova.api.ec2.cloud.CloudController + paste.filter_factory = nova.api.ec2:Requestify.factory + + [filter:adminrequest] + controller = nova.api.ec2.admin.AdminController + paste.filter_factory = nova.api.ec2:Requestify.factory + + [filter:authorizer] + paste.filter_factory = nova.api.ec2:Authorizer.factory + + [app:ec2executor] + paste.app_factory = nova.api.ec2:Executor.factory + + [app:ec2ver] + paste.app_factory = nova.api.ec2:Versions.factory + + [app:ec2md] + paste.app_factory = nova.api.ec2.metadatarequesthandler:MetadataRequestHandler.factory + + ############# + # Openstack # + ############# + + [composite:osapi] + use = egg:Paste#urlmap + /: osversions + /v1.1: openstackapi + + [pipeline:openstackapi] + pipeline = faultwrap authtoken keystonecontext ratelimit extensions osapiapp + + [filter:faultwrap] + paste.filter_factory = nova.api.openstack:FaultWrapper.factory + + [filter:auth] + paste.filter_factory = nova.api.openstack.auth:AuthMiddleware.factory + + [filter:noauth] + paste.filter_factory = nova.api.openstack.auth:NoAuthMiddleware.factory + + [filter:ratelimit] + paste.filter_factory = nova.api.openstack.limits:RateLimitingMiddleware.factory + + [filter:extensions] + paste.filter_factory = nova.api.openstack.extensions:ExtensionMiddleware.factory + + [app:osapiapp] + paste.app_factory = nova.api.openstack:APIRouter.factory + + [pipeline:osversions] + pipeline = faultwrap osversionapp + + [app:osversionapp] + paste.app_factory = nova.api.openstack.versions:Versions.factory + + ########## + # Shared # + ########## + + [filter:keystonecontext] + paste.filter_factory = keystone.middleware.nova_keystone_context:NovaKeystoneContext.factory + + [filter:authtoken] + paste.filter_factory = keystone.middleware.auth_token:filter_factory + service_protocol = http + service_host = 127.0.0.1 + service_port = 5000 + auth_host = 127.0.0.1 + auth_port = 35357 + auth_protocol = http + auth_uri = http://your_keystone_host.com:5000/ + ;identical to the admin token defined in keystone.conf + admin_token = 999888777666 + ;Uncomment next line and check ip:port to use memcached to cache token requests + ;memcache_hosts = 127.0.0.1:11211 diff --git a/doc/source/backends.rst b/docs/source/old/backends.rst similarity index 100% rename from doc/source/backends.rst rename to docs/source/old/backends.rst diff --git a/doc/source/controllingservers.rst b/docs/source/old/controllingservers.rst similarity index 100% rename from doc/source/controllingservers.rst rename to docs/source/old/controllingservers.rst diff --git a/doc/source/endpoints.rst b/docs/source/old/endpoints.rst similarity index 100% rename from doc/source/endpoints.rst rename to docs/source/old/endpoints.rst diff --git a/doc/source/extensions.rst b/docs/source/old/extensions.rst similarity index 100% rename from doc/source/extensions.rst rename to docs/source/old/extensions.rst diff --git a/doc/source/middleware.rst b/docs/source/old/middleware.rst similarity index 100% rename from doc/source/middleware.rst rename to docs/source/old/middleware.rst diff --git a/doc/source/migration.rst b/docs/source/old/migration.rst similarity index 100% rename from doc/source/migration.rst rename to docs/source/old/migration.rst diff --git a/doc/source/releases.rst b/docs/source/old/releases.rst similarity index 100% rename from doc/source/releases.rst rename to docs/source/old/releases.rst diff --git a/doc/source/services.rst b/docs/source/old/services.rst similarity index 100% rename from doc/source/services.rst rename to docs/source/old/services.rst diff --git a/doc/source/ssl.rst b/docs/source/old/ssl.rst similarity index 100% rename from doc/source/ssl.rst rename to docs/source/old/ssl.rst diff --git a/doc/source/setup.rst b/docs/source/setup.rst similarity index 61% rename from doc/source/setup.rst rename to docs/source/setup.rst index 5b9976bbcf..7d03fd6669 100644 --- a/doc/source/setup.rst +++ b/docs/source/setup.rst @@ -18,12 +18,12 @@ Setting up a Keystone development environment ============================================= -This document describes setting up keystone directly from GitHub_ +This document describes getting the source from keystone's `GitHub repository`_ for development purposes. To install keystone from packaging, refer instead to Keystone's `User Documentation`_. -.. _GitHub: http://github.com/openstack/keystone +.. _`GitHub Repository`: http://github.com/openstack/keystone .. _`User Documentation`: http://docs.openstack.org/ Prerequisites @@ -31,7 +31,7 @@ Prerequisites This document assumes you are using: -- Ubuntu 11.10, Fedora 15, openSUSE 11.4, 12.1 or Mac OS X Lion +- Ubuntu 11.10, Fedora 15, or Mac OS X Lion - `Python 2.7`_ .. _`Python 2.7`: http://www.python.org/ @@ -51,7 +51,7 @@ different version of the above, please document your configuration here! Getting the latest code ======================= -You can clone our latest code from our `Github repository`:: +Make a clone of the code from our `Github repository`:: $ git clone https://github.com/openstack/keystone.git @@ -59,13 +59,17 @@ When that is complete, you can:: $ cd keystone -.. _`Github repository`: https://github.com/openstack/keystone - Installing dependencies ======================= -Keystone maintains a list of PyPi_ dependencies, designed for use by -pip_. +Keystone maintains two lists of dependencies:: + + tools/pip-requires + tools/pip-requires-test + +The first is the list of dependencies needed for running keystone, the second list includes dependencies used for active development and testing of keystone itself. + +These depdendencies can be installed from PyPi_ using the python tool pip_. .. _PyPi: http://pypi.python.org/ .. _pip: http://pypi.python.org/pypi/pip @@ -83,53 +87,65 @@ Fedora 15:: $ sudo yum install python-sqlite2 python-lxml python-greenlet-devel python-ldap -openSUSE 11.4, 12.1:: - - $ sudo zypper in python-devel python-xml gcc libxslt-devel python-ldap openldap2-devel - Mac OS X Lion (requires MacPorts_):: $ sudo port install py-ldap .. _MacPorts: http://www.macports.org/ -PyPi Packages -------------- +PyPi Packages and VirtualEnv +---------------------------- -Assuming you have any necessary binary packages & header files available -on your system, you can then install PyPi dependencies. +We recommend establishing a virtualenv to run keystone within. Virtualenv limits the python environment +to just what you're installing as depdendencies, useful to keep a clean environment for working on +Keystone. The tools directory in keystone has a script already created to make this very simple:: -You may also need to prefix `pip install` with `sudo`, depending on your -environment:: + $ python tools/install_venv.py - # Describe dependencies (including non-PyPi dependencies) - $ cat tools/pip-requires +This will create a local virtual environment in the directory ``.venv``. +Once created, you can activate this virtualenv for your current shell using:: - # Install all PyPi dependencies (for production, testing, and development) + $ source .venv/bin/activate + +The virtual environment can be disabled using the command:: + + $ deactivate + +You can also use ``tools\with_venv.sh`` to prefix commands so that they run +within the virtual environment. For more information on virtual environments, +see virtualenv_. + +.. _virtualenv: http://www.virtualenv.org/ + +If you want to run keystone outside of a virtualenv, you can install the dependencies directly +into your system from the requires files:: + + # Install the dependencies for running keystone $ pip install -r tools/pip-requires -Updating your PYTHONPATH -======================== - -There are a number of methods for getting Keystone into your PYTHON PATH, -the easiest of which is:: + # Install the dependencies for developing, testing, and running keystone + $ pip install -r tools/pip-requires-test # Fake-install the project by symlinking Keystone into your Python site-packages $ python setup.py develop + +Verifying Keystone is set up +============================ + +Once set up, either directly or within a virtualenv, you should be able to invoke python and import +the libraries. If you're using a virtualenv, don't forget to activate it:: + + $ source .venv/bin/activate + $ python + You should then be able to `import keystone` from your Python shell without issue:: - >>> import keystone.version + >>> import keystone >>> -If you want to check the version of Keystone you are running: - - >>> print keystone.version.version() - 2012.1-dev - - -If you can import keystone successfully, you should be ready to move on to :doc:`testing`. +If you can import keystone successfully, you should be ready to move on to :doc:`developing` Troubleshooting =============== diff --git a/doc/source/_static/basic.css b/docs/source/static/basic.css similarity index 100% rename from doc/source/_static/basic.css rename to docs/source/static/basic.css diff --git a/doc/source/_static/default.css b/docs/source/static/default.css similarity index 100% rename from doc/source/_static/default.css rename to docs/source/static/default.css diff --git a/doc/source/_static/jquery.tweet.js b/docs/source/static/jquery.tweet.js similarity index 100% rename from doc/source/_static/jquery.tweet.js rename to docs/source/static/jquery.tweet.js diff --git a/doc/source/_static/tweaks.css b/docs/source/static/tweaks.css similarity index 100% rename from doc/source/_static/tweaks.css rename to docs/source/static/tweaks.css diff --git a/etc/default_catalog.templates b/etc/default_catalog.templates new file mode 100644 index 0000000000..c12b5c4ca7 --- /dev/null +++ b/etc/default_catalog.templates @@ -0,0 +1,12 @@ +# config for TemplatedCatalog, using camelCase because I don't want to do +# translations for keystone compat +catalog.RegionOne.identity.publicURL = http://localhost:$(public_port)s/v2.0 +catalog.RegionOne.identity.adminURL = http://localhost:$(admin_port)s/v2.0 +catalog.RegionOne.identity.internalURL = http://localhost:$(admin_port)s/v2.0 +catalog.RegionOne.identity.name = 'Identity Service' + +# fake compute service for now to help novaclient tests work +catalog.RegionOne.compute.publicURL = http://localhost:$(compute_port)s/v1.1/$(tenant_id)s +catalog.RegionOne.compute.adminURL = http://localhost:$(compute_port)s/v1.1/$(tenant_id)s +catalog.RegionOne.compute.internalURL = http://localhost:$(compute_port)s/v1.1/$(tenant_id)s +catalog.RegionOne.compute.name = 'Compute Service' diff --git a/etc/keystone.conf b/etc/keystone.conf index 9ba2cfb802..dd78b4a957 100644 --- a/etc/keystone.conf +++ b/etc/keystone.conf @@ -1,125 +1,94 @@ [DEFAULT] -# Show more verbose log output (sets INFO log level output) -verbose = False - -# Show debugging output in logs (sets DEBUG log level output) -debug = False - -# Which backend store should Keystone use by default. -# Default: 'sqlite' -# Available choices are 'sqlite' [future will include LDAP, PAM, etc] -default_store = sqlite - -# Log to this file. Make sure you do not set the same log -# file for both the API and registry servers! -log_file = keystone.log -log_dir = . - -# Dictionary Maps every service to a header.Missing services would get header -# X_(SERVICE_NAME) Key => Service Name, Value => Header Name -service_header_mappings = { - 'nova' : 'X-Server-Management-Url', - 'swift' : 'X-Storage-Url', - 'cdn' : 'X-CDN-Management-Url'} - -#List of extensions currently loaded. -#Refer docs for list of supported extensions. -extensions = osksadm,oskscatalog,hpidm,osksvalidate - -# Address to bind the API server -# TODO Properties defined within app not available via pipeline. -service_host = 0.0.0.0 - -# Port the bind the API server to -service_port = 5000 - -# SSL for API server -service_ssl = False - -# Address to bind the Admin API server -admin_host = 0.0.0.0 - -# Port the bind the Admin API server to +public_port = 5000 admin_port = 35357 +admin_token = ADMIN +compute_port = 3000 +verbose = True +debug = True +#log_config = /etc/keystone/logging.conf -# SSL for API Admin server -admin_ssl = False +# ================= Syslog Options ============================ +# Send logs to syslog (/dev/log) instead of to file specified +# by `log-file` +use_syslog = False -# List of backends to be configured -backends = keystone.backends.sqlalchemy -#For LDAP support, add: ,keystone.backends.ldap +# Facility to use. If unset defaults to LOG_USER. +# syslog_log_facility = LOG_LOCAL0 -# Keystone certificate file (modify as needed) -# Only required if *_ssl is set to True -certfile = /etc/keystone/ssl/certs/keystone.pem +[sql] +connection = sqlite:///bla.db +idle_timeout = 200 +min_pool_size = 5 +max_pool_size = 10 +pool_timeout = 200 -# Keystone private key file (modify as needed) -# Only required if *_ssl is set to True -keyfile = /etc/keystone/ssl/private/keystonekey.pem +[identity] +driver = keystone.identity.backends.kvs.Identity -# Keystone trusted CA certificates (modify as needed) -# Only required if *_ssl is set to True -ca_certs = /etc/keystone/ssl/certs/ca.pem +[catalog] +driver = keystone.catalog.backends.templated.TemplatedCatalog +template_file = ./etc/default_catalog.templates -# Client certificate required -# Only relevant if *_ssl is set to True -cert_required = True +[token] +driver = keystone.token.backends.kvs.Token -#Role that allows to perform admin operations. -keystone_admin_role = Admin +# Amount of time a token should remain valid (in seconds) +expiration = 86400 -#Role that allows to perform service admin operations. -keystone_service_admin_role = KeystoneServiceAdmin +[policy] +driver = keystone.policy.backends.simple.SimpleMatch -#Tells whether password user need to be hashed in the backend -hash_password = True - -global_service_id = - -[keystone.backends.sqlalchemy] -# SQLAlchemy connection string for the reference implementation registry -# server. Any valid SQLAlchemy connection string is fine. -# See: http://bit.ly/ideIpI -sql_connection = sqlite:///keystone.db -backend_entities = ['UserRoleAssociation', 'Endpoints', 'Role', 'Tenant', - 'User', 'Credentials', 'EndpointTemplates', 'Token', - 'Service'] - -# Period in seconds after which SQLAlchemy should reestablish its connection -# to the database. -sql_idle_timeout = 30 - -[pipeline:admin] -pipeline = - urlnormalizefilter - d5_compat - admin_api - -[pipeline:keystone-legacy-auth] -pipeline = - urlnormalizefilter - legacy_auth - d5_compat - service_api - -[app:service_api] -paste.app_factory = keystone.server:service_app_factory - -[app:admin_api] -paste.app_factory = keystone.server:admin_app_factory - -[filter:urlnormalizefilter] -paste.filter_factory = keystone.frontends.normalizer:filter_factory - -[filter:legacy_auth] -paste.filter_factory = keystone.frontends.legacy_token_auth:filter_factory - -[filter:d5_compat] -paste.filter_factory = keystone.frontends.d5_compat:filter_factory +[ec2] +driver = keystone.contrib.ec2.backends.kvs.Ec2 [filter:debug] -paste.filter_factory = keystone.common.wsgi:debug_filter_factory +paste.filter_factory = keystone.common.wsgi:Debug.factory + +[filter:token_auth] +paste.filter_factory = keystone.middleware:TokenAuthMiddleware.factory + +[filter:admin_token_auth] +paste.filter_factory = keystone.middleware:AdminTokenAuthMiddleware.factory + +[filter:json_body] +paste.filter_factory = keystone.middleware:JsonBodyMiddleware.factory + +[filter:crud_extension] +paste.filter_factory = keystone.contrib.admin_crud:CrudExtension.factory + +[filter:ec2_extension] +paste.filter_factory = keystone.contrib.ec2:Ec2Extension.factory + +[app:public_service] +paste.app_factory = keystone.service:public_app_factory + +[app:admin_service] +paste.app_factory = keystone.service:admin_app_factory + +[pipeline:public_api] +pipeline = token_auth admin_token_auth json_body debug ec2_extension public_service + +[pipeline:admin_api] +pipeline = token_auth admin_token_auth json_body debug ec2_extension crud_extension admin_service + +[app:public_version_service] +paste.app_factory = keystone.service:public_version_app_factory + +[app:admin_version_service] +paste.app_factory = keystone.service:admin_version_app_factory + +[pipeline:public_version_api] +pipeline = public_version_service + +[pipeline:admin_version_api] +pipeline = admin_version_service [composite:main] use = egg:Paste#urlmap -/v2.0 = keystone-legacy-auth +/v2.0 = public_api +/ = public_version_api + +[composite:admin] +use = egg:Paste#urlmap +/v2.0 = admin_api +/ = admin_version_service diff --git a/etc/ldap.conf b/etc/ldap.conf deleted file mode 100644 index d070e325bc..0000000000 --- a/etc/ldap.conf +++ /dev/null @@ -1,64 +0,0 @@ -# -# SAMPLE CONFIG FILE TO TEST LDAP TOKEN STORE -# -# TO USE: -# in /bin, run: -# ./keystone -c ../etc/ldap.conf -# also run -# ./sampledata -c ../etc/ldap.conf -# -[DEFAULT] -verbose = False -debug = False -default_store = sqlite -log_file = keystone.ldap.log -log_dir = . -backends = keystone.backends.sqlalchemy,keystone.backends.ldap -extensions= osksadm, oskscatalog, hpidm -service-header-mappings = { - 'nova' : 'X-Server-Management-Url', - 'swift' : 'X-Storage-Url', - 'cdn' : 'X-CDN-Management-Url'} -service_host = 0.0.0.0 -service_port = 5000 -service_ssl = False -admin_host = 0.0.0.0 -admin_port = 35357 -admin_ssl = False -keystone-admin-role = Admin -keystone-service-admin-role = KeystoneServiceAdmin -hash-password = True - -[keystone.backends.sqlalchemy] -sql_connection = sqlite:// -sql_idle_timeout = 30 -backend_entities = ['Endpoints', 'Credentials', 'EndpointTemplates', 'Token', 'Service'] - -[keystone.backends.ldap] -ldap_url = fake://memory -ldap_user = cn=Admin -ldap_password = password -backend_entities = ['Tenant', 'User', 'UserRoleAssociation', 'Role'] - -[pipeline:admin] -pipeline = - urlnormalizer - admin_api - -[pipeline:keystone-legacy-auth] -pipeline = - urlnormalizer - legacy_auth - service_api - -[app:service_api] -paste.app_factory = keystone.server:service_app_factory - -[app:admin_api] -paste.app_factory = keystone.server:admin_app_factory - -[filter:urlnormalizer] -paste.filter_factory = keystone.frontends.normalizer:filter_factory - -[filter:legacy_auth] -paste.filter_factory = keystone.frontends.legacy_token_auth:filter_factory diff --git a/etc/logging.cnf b/etc/logging.conf.sample similarity index 81% rename from etc/logging.cnf rename to etc/logging.conf.sample index b6820ca637..6e3d1a042b 100644 --- a/etc/logging.cnf +++ b/etc/logging.conf.sample @@ -1,5 +1,5 @@ [loggers] -keys=root,keystone,combined +keys=root,api,registry,combined [formatters] keys=normal,normal_with_name,debug @@ -11,15 +11,20 @@ keys=production,file,devel level=NOTSET handlers=devel -[logger_keystone] +[logger_api] level=DEBUG handlers=devel -qualname=keystone +qualname=glance-api + +[logger_registry] +level=DEBUG +handlers=devel +qualname=glance-registry [logger_combined] level=DEBUG handlers=devel -qualname=keystone-combined +qualname=glance-combined [handler_production] class=handlers.SysLogHandler @@ -31,7 +36,7 @@ args=(('localhost', handlers.SYSLOG_UDP_PORT), handlers.SysLogHandler.LOG_USER) class=FileHandler level=DEBUG formatter=normal_with_name -args=('keystone.log', 'w') +args=('glance.log', 'w') [handler_devel] class=StreamHandler diff --git a/etc/memcache.conf b/etc/memcache.conf deleted file mode 100644 index 1d27636f08..0000000000 --- a/etc/memcache.conf +++ /dev/null @@ -1,76 +0,0 @@ -# -# SAMPLE CONFIG FILE TO TEST MEMCACHE TOKEN STORE -# -# TO USE: -# in /bin, run: -# ./keystone -c ../etc/memcache.conf -# also run -# ./sampledata -c ../etc/memcache.conf -# -# Note: since the data in this config is distributed between a sqlite database -# and memcache, when the memcache server is restarted the token data is lost -# but the other sample data remains in the sqlite database. This results in -# the sample data being partially erased which prevents many samples from -# running and also results in calls to sampldata failing because values already -# exist. TO address this, delete the keystone.memcache.db file and rerun -# sampledata. - - -[DEFAULT] -verbose = False -debug = False -default_store = sqlite -log_file = keystone.memcache.log -log_dir = . -backends = keystone.backends.sqlalchemy,keystone.backends.memcache -extensions= osksadm, oskscatalog, hpidm -service-header-mappings = { - 'nova' : 'X-Server-Management-Url', - 'swift' : 'X-Storage-Url', - 'cdn' : 'X-CDN-Management-Url'} -service_host = 0.0.0.0 -service_port = 5000 -service_ssl = False -admin_host = 0.0.0.0 -admin_port = 35357 -admin_ssl = False -keystone-admin-role = Admin -keystone-service-admin-role = KeystoneServiceAdmin - -[keystone.backends.sqlalchemy] -sql_connection = sqlite:///keystone.memcache.db -sql_idle_timeout = 30 -backend_entities = ['Endpoints', 'Credentials', 'EndpointTemplates', 'Tenant', 'User', 'UserRoleAssociation', 'Role', 'Service'] - -[keystone.backends.memcache] -memcache_hosts = 127.0.0.1:11211 -backend_entities = ['Token'] -cache_time = 86400 - -[pipeline:admin] -pipeline = - urlnormalizer - d5_compat - admin_api - -[pipeline:keystone-legacy-auth] -pipeline = - urlnormalizer - legacy_auth - d5_compat - service_api - -[app:service_api] -paste.app_factory = keystone.server:service_app_factory - -[app:admin_api] -paste.app_factory = keystone.server:admin_app_factory - -[filter:urlnormalizer] -paste.filter_factory = keystone.frontends.normalizer:filter_factory - -[filter:d5_compat] -paste.filter_factory = keystone.frontends.d5_compat:filter_factory - -[filter:legacy_auth] -paste.filter_factory = keystone.frontends.legacy_token_auth:filter_factory diff --git a/etc/ssl.conf b/etc/ssl.conf deleted file mode 100644 index 5ae67c6c43..0000000000 --- a/etc/ssl.conf +++ /dev/null @@ -1,74 +0,0 @@ -# -# SAMPLE CONFIG FILE TO TEST SSL -# -# TO USE: -# in /bin, run: -# ./keystone -c ../etc/ssl.conf -# also run -# ./sampledata -c ../etc/ssl.conf -# -# Note: this uses the same database as the default conf file. The echo app -# is not designed to support SSL and won't work in this config. -# -# To verify the server is started: -# curl -k https://localhost:35357/ - - -[DEFAULT] -verbose = False -debug = False -default_store = sqlite -log_file = keystone.ssl.log -log_dir = . -backends = keystone.backends.sqlalchemy -extensions= osksadm, oskscatalog, hpidm -service-header-mappings = { - 'nova' : 'X-Server-Management-Url', - 'swift' : 'X-Storage-Url', - 'cdn' : 'X-CDN-Management-Url'} -service_host = 0.0.0.0 -service_port = 5000 -service_ssl = True -admin_host = 0.0.0.0 -admin_port = 35357 -admin_ssl = True -keystone-admin-role = Admin -keystone-service-admin-role = KeystoneServiceAdmin -hash-password = True -certfile = ../examples/ssl/certs/keystone.pem -keyfile = ../examples/ssl/private/keystonekey.pem -ca_certs = ../examples/ssl/certs/ca.pem -cert_required = True - -[keystone.backends.sqlalchemy] -sql_connection = sqlite:///keystone.db -sql_idle_timeout = 30 -backend_entities = ['Endpoints', 'Credentials', 'EndpointTemplates', 'Tenant', 'User', 'UserRoleAssociation', 'Role', 'Token', 'Service'] - -[pipeline:admin] -pipeline = - urlnormalizer - d5_compat - admin_api - -[pipeline:keystone-legacy-auth] -pipeline = - urlnormalizer - legacy_auth - d5_compat - service_api - -[app:service_api] -paste.app_factory = keystone.server:service_app_factory - -[app:admin_api] -paste.app_factory = keystone.server:admin_app_factory - -[filter:urlnormalizer] -paste.filter_factory = keystone.frontends.normalizer:filter_factory - -[filter:d5_compat] -paste.filter_factory = keystone.frontends.d5_compat:filter_factory - -[filter:legacy_auth] -paste.filter_factory = keystone.frontends.legacy_token_auth:filter_factory diff --git a/examples/README.md b/examples/README.md deleted file mode 100644 index 4af4697ea9..0000000000 --- a/examples/README.md +++ /dev/null @@ -1,24 +0,0 @@ -RUNNING THE TEST SERVICE (Echo.py): ----------------------------------- - - Standalone stack (with Auth_Token) - $ cd echo/bin - $ ./echod - - Distributed stack (with RemoteAuth local and Auth_Token remote) - $ cd echo/bin - $ ./echod --remote - - in separate session - $ cd keystone/middleware - $ python auth_token.py - - -DEMO CLIENT: ------------- -A sample client that gets a token from Keystone and then uses it to call Echo (and a few other example calls): - - $ cd echo/echo - $ python echo_client.py - Note: this requires test data. See section TESTING for initializing data - diff --git a/examples/echo/bin/echod b/examples/echo/bin/echod deleted file mode 100755 index d20a1b5eb4..0000000000 --- a/examples/echo/bin/echod +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/sh -# Copyright (C) 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. - -# If ../echo/__init__.py exists, add ../ to the Python search path so -# that it will override whatever may be installed in the default Python -# search path. -script_dir=`dirname $0` -if [ -f "$script_dir/../echo/__init__.py" ] -then - PYTHONPATH="$script_dir/..:$PYTHONPATH" - export PYTHONPATH -fi - -/usr/bin/env python -m echo.server $* diff --git a/examples/echo/docs/echo_flow.pdf b/examples/echo/docs/echo_flow.pdf deleted file mode 100644 index cfc039e606..0000000000 --- a/examples/echo/docs/echo_flow.pdf +++ /dev/null @@ -1,5316 +0,0 @@ -%PDF-1.4 -%âãÏÓ - -1 0 obj - << - /Title () - /Author () - /Subject () - /Keywords () - /Creator (FreeHEP Graphics2D Driver) - /Producer (org.freehep.graphicsio.pdf.PDFGraphics2D Revision: 10516 ) - /CreationDate (D:20110417234558-05'00') - /ModDate (D:20110417234558-05'00') - /Trapped /False - >> -endobj - -2 0 obj - << - /Type /Catalog - /Pages 3 0 R - /Outlines 4 0 R - /PageMode /UseOutlines - /ViewerPreferences 5 0 R - /OpenAction [6 0 R /Fit] - >> -endobj - -5 0 obj - << - /FitWindow true - /CenterWindow false - >> -endobj - -6 0 obj - << - /Parent 3 0 R - /Type /Page - /Contents 7 0 R - >> -endobj - -7 0 obj - << - /Length 8 0 R - >> -stream -.93277 0.0000 0.0000 -.93277 20.000 813.70 cm -q -0.0000 0.0000 m -595.00 0.0000 l -595.00 842.00 l -0.0000 842.00 l -h -W -n -q -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -1.0000 0.0000 0.0000 1.0000 0.0000 0.0000 cm -Q -q -1.0000 0.0000 0.0000 1.0000 0.0000 0.0000 cm -0.0000 0.0000 m -0.0000 842.00 l -595.00 842.00 l -595.00 0.0000 l -h -W -n -q -.86988 0.0000 0.0000 .86988 0.0000 0.0000 cm -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -0.0000 0.0000 m -685.00 0.0000 l -685.00 968.00 l -0.0000 968.00 l -0.0000 0.0000 l -h -f -1.0000 0.0000 0.0000 1.0000 0.0000 0.0000 cm -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -0 J -1 j -1.0000 M -[ 1.0000 2.0000] 0.0000 d -66.500 44.500 m -66.500 54.500 l -S -66.500 44.500 m -66.500 54.500 l -S -[] 0.0000 d -/Alpha1 gs -.93333 .93333 .93333 rg -.93333 .93333 .93333 RG -62.000 54.000 m -70.000 54.000 l -70.000 410.00 l -62.000 410.00 l -62.000 54.000 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -62.500 54.500 m -70.500 54.500 l -70.500 410.50 l -62.500 410.50 l -62.500 54.500 l -h -S -[ 1.0000 2.0000] 0.0000 d -66.500 410.50 m -66.500 410.50 l -S -467.50 44.500 m -467.50 102.50 l -S -467.50 44.500 m -467.50 102.50 l -S -[] 0.0000 d -/Alpha1 gs -.93333 .93333 .93333 rg -.93333 .93333 .93333 RG -463.00 102.00 m -471.00 102.00 l -471.00 120.00 l -463.00 120.00 l -463.00 102.00 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -463.50 102.50 m -471.50 102.50 l -471.50 120.50 l -463.50 120.50 l -463.50 102.50 l -h -S -[ 1.0000 2.0000] 0.0000 d -467.50 120.50 m -467.50 140.50 l -S -[] 0.0000 d -/Alpha1 gs -.93333 .93333 .93333 rg -.93333 .93333 .93333 RG -463.00 140.00 m -471.00 140.00 l -471.00 158.00 l -463.00 158.00 l -463.00 140.00 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -463.50 140.50 m -471.50 140.50 l -471.50 158.50 l -463.50 158.50 l -463.50 140.50 l -h -S -[ 1.0000 2.0000] 0.0000 d -467.50 158.50 m -467.50 224.50 l -S -[] 0.0000 d -/Alpha1 gs -.93333 .93333 .93333 rg -.93333 .93333 .93333 RG -463.00 224.00 m -471.00 224.00 l -471.00 242.00 l -463.00 242.00 l -463.00 224.00 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -463.50 224.50 m -471.50 224.50 l -471.50 242.50 l -463.50 242.50 l -463.50 224.50 l -h -S -[ 1.0000 2.0000] 0.0000 d -467.50 242.50 m -467.50 356.50 l -S -[] 0.0000 d -/Alpha1 gs -.93333 .93333 .93333 rg -.93333 .93333 .93333 RG -463.00 356.00 m -471.00 356.00 l -471.00 374.00 l -463.00 374.00 l -463.00 356.00 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -463.50 356.50 m -471.50 356.50 l -471.50 374.50 l -463.50 374.50 l -463.50 356.50 l -h -S -[ 1.0000 2.0000] 0.0000 d -467.50 374.50 m -467.50 410.50 l -S -622.50 44.500 m -622.50 308.50 l -S -622.50 44.500 m -622.50 308.50 l -S -[] 0.0000 d -/Alpha1 gs -.93333 .93333 .93333 rg -.93333 .93333 .93333 RG -618.00 308.00 m -626.00 308.00 l -626.00 410.00 l -618.00 410.00 l -618.00 308.00 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -618.50 308.50 m -626.50 308.50 l -626.50 410.50 l -618.50 410.50 l -618.50 308.50 l -h -S -[ 1.0000 2.0000] 0.0000 d -622.50 410.50 m -622.50 410.50 l -S -80.436 136.19 m -79.744 136.83 79.078 137.15 78.438 137.15 c -77.910 137.15 77.473 136.98 77.125 136.65 c -76.777 136.32 76.604 135.90 76.604 135.40 c -76.604 134.71 76.896 134.17 77.479 133.80 c -78.063 133.42 78.900 133.24 79.990 133.24 c -80.266 133.24 l -80.266 132.47 l -80.266 131.73 79.887 131.36 79.129 131.36 c -78.520 131.36 77.861 131.55 77.154 131.93 c -77.154 130.97 l -77.932 130.65 78.660 130.50 79.340 130.50 c -80.051 130.50 80.575 130.66 80.913 130.98 c -81.251 131.30 81.420 131.79 81.420 132.47 c -81.420 135.35 l -81.420 136.01 81.623 136.34 82.029 136.34 c -82.080 136.34 82.154 136.34 82.252 136.32 c -82.334 136.96 l -82.072 137.08 81.783 137.15 81.467 137.15 c -80.928 137.15 80.584 136.83 80.436 136.19 c -h -80.266 135.56 m -80.266 133.92 l -79.879 133.91 l -79.246 133.91 78.734 134.03 78.344 134.27 c -77.953 134.51 77.758 134.82 77.758 135.21 c -77.758 135.49 77.855 135.72 78.051 135.92 c -78.246 136.11 78.484 136.20 78.766 136.20 c -79.246 136.20 79.746 135.99 80.266 135.56 c -h -87.865 137.00 m -87.865 135.80 l -87.396 136.70 86.689 137.15 85.744 137.15 c -84.979 137.15 84.376 136.87 83.937 136.31 c -83.497 135.75 83.277 134.99 83.277 134.02 c -83.277 132.96 83.526 132.11 84.024 131.46 c -84.522 130.82 85.178 130.50 85.990 130.50 c -86.744 130.50 87.369 130.79 87.865 131.36 c -87.865 127.75 l -89.025 127.75 l -89.025 137.00 l -h -87.865 132.15 m -87.268 131.63 86.701 131.36 86.166 131.36 c -85.061 131.36 84.508 132.21 84.508 133.90 c -84.508 135.39 85.000 136.13 85.984 136.13 c -86.625 136.13 87.252 135.78 87.865 135.08 c -h -95.418 137.00 m -95.418 135.80 l -94.949 136.70 94.242 137.15 93.297 137.15 c -92.531 137.15 91.929 136.87 91.489 136.31 c -91.050 135.75 90.830 134.99 90.830 134.02 c -90.830 132.96 91.079 132.11 91.577 131.46 c -92.075 130.82 92.730 130.50 93.543 130.50 c -94.297 130.50 94.922 130.79 95.418 131.36 c -95.418 127.75 l -96.578 127.75 l -96.578 137.00 l -h -95.418 132.15 m -94.820 131.63 94.254 131.36 93.719 131.36 c -92.613 131.36 92.061 132.21 92.061 133.90 c -92.061 135.39 92.553 136.13 93.537 136.13 c -94.178 136.13 94.805 135.78 95.418 135.08 c -h -97.732 139.02 m -97.732 138.15 l -103.73 138.15 l -103.73 139.02 l -h -108.87 137.00 m -108.87 135.80 l -108.25 136.70 107.51 137.15 106.63 137.15 c -106.08 137.15 105.64 136.97 105.31 136.62 c -104.98 136.27 104.82 135.80 104.82 135.21 c -104.82 130.64 l -105.97 130.64 l -105.97 134.83 l -105.97 135.31 106.04 135.65 106.18 135.85 c -106.32 136.05 106.55 136.15 106.87 136.15 c -107.58 136.15 108.24 135.69 108.87 134.76 c -108.87 130.64 l -110.02 130.64 l -110.02 137.00 l -h -114.02 137.15 m -113.49 137.15 112.85 137.02 112.10 136.78 c -112.10 135.72 l -112.85 136.09 113.51 136.28 114.07 136.28 c -114.40 136.28 114.68 136.19 114.89 136.01 c -115.11 135.83 115.22 135.61 115.22 135.34 c -115.22 134.94 114.92 134.62 114.30 134.36 c -113.63 134.07 l -112.63 133.66 112.13 133.06 112.13 132.28 c -112.13 131.73 112.33 131.29 112.72 130.97 c -113.12 130.66 113.65 130.50 114.34 130.50 c -114.69 130.50 115.13 130.54 115.66 130.64 c -115.90 130.69 l -115.90 131.65 l -115.25 131.46 114.74 131.36 114.36 131.36 c -113.62 131.36 113.25 131.63 113.25 132.17 c -113.25 132.52 113.53 132.81 114.09 133.05 c -114.65 133.29 l -115.28 133.55 115.72 133.83 115.98 134.13 c -116.25 134.42 116.38 134.79 116.38 135.23 c -116.38 135.79 116.16 136.25 115.71 136.61 c -115.27 136.97 114.71 137.15 114.02 137.15 c -h -123.12 136.79 m -122.34 137.03 121.68 137.15 121.13 137.15 c -120.19 137.15 119.43 136.83 118.83 136.21 c -118.24 135.59 117.95 134.78 117.95 133.79 c -117.95 132.82 118.21 132.03 118.73 131.42 c -119.25 130.80 119.92 130.49 120.73 130.49 c -121.50 130.49 122.09 130.76 122.51 131.31 c -122.93 131.86 123.14 132.63 123.14 133.64 c -123.14 134.00 l -119.12 134.00 l -119.29 135.51 120.03 136.27 121.35 136.27 c -121.83 136.27 122.42 136.14 123.12 135.88 c -h -119.18 133.13 m -121.98 133.13 l -121.98 131.95 121.54 131.36 120.66 131.36 c -119.77 131.36 119.28 131.95 119.18 133.13 c -h -125.14 137.00 m -125.14 130.64 l -126.29 130.64 l -126.29 131.83 l -126.75 130.94 127.41 130.50 128.28 130.50 c -128.40 130.50 128.52 130.51 128.65 130.53 c -128.65 131.60 l -128.45 131.54 128.28 131.50 128.12 131.50 c -127.39 131.50 126.78 131.94 126.29 132.80 c -126.29 137.00 l -h -137.13 137.00 m -130.19 133.53 l -137.13 130.06 l -137.13 131.03 l -132.13 133.53 l -137.13 136.03 l -h -143.56 137.00 m -143.56 135.80 l -142.95 136.70 142.21 137.15 141.33 137.15 c -140.78 137.15 140.34 136.97 140.01 136.62 c -139.68 136.27 139.52 135.80 139.52 135.21 c -139.52 130.64 l -140.67 130.64 l -140.67 134.83 l -140.67 135.31 140.74 135.65 140.88 135.85 c -141.02 136.05 141.25 136.15 141.57 136.15 c -142.28 136.15 142.94 135.69 143.56 134.76 c -143.56 130.64 l -144.72 130.64 l -144.72 137.00 l -h -148.72 137.15 m -148.19 137.15 147.55 137.02 146.80 136.78 c -146.80 135.72 l -147.55 136.09 148.21 136.28 148.77 136.28 c -149.10 136.28 149.38 136.19 149.59 136.01 c -149.81 135.83 149.92 135.61 149.92 135.34 c -149.92 134.94 149.62 134.62 149.00 134.36 c -148.33 134.07 l -147.33 133.66 146.83 133.06 146.83 132.28 c -146.83 131.73 147.03 131.29 147.42 130.97 c -147.82 130.66 148.35 130.50 149.04 130.50 c -149.39 130.50 149.83 130.54 150.36 130.64 c -150.60 130.69 l -150.60 131.65 l -149.95 131.46 149.44 131.36 149.06 131.36 c -148.32 131.36 147.95 131.63 147.95 132.17 c -147.95 132.52 148.23 132.81 148.79 133.05 c -149.35 133.29 l -149.98 133.55 150.42 133.83 150.68 134.13 c -150.95 134.42 151.08 134.79 151.08 135.23 c -151.08 135.79 150.86 136.25 150.41 136.61 c -149.97 136.97 149.41 137.15 148.72 137.15 c -h -157.81 136.79 m -157.04 137.03 156.38 137.15 155.83 137.15 c -154.89 137.15 154.13 136.83 153.53 136.21 c -152.94 135.59 152.65 134.78 152.65 133.79 c -152.65 132.82 152.91 132.03 153.43 131.42 c -153.95 130.80 154.62 130.49 155.43 130.49 c -156.20 130.49 156.79 130.76 157.21 131.31 c -157.63 131.86 157.84 132.63 157.84 133.64 c -157.84 134.00 l -153.82 134.00 l -153.99 135.51 154.73 136.27 156.04 136.27 c -156.53 136.27 157.12 136.14 157.81 135.88 c -h -153.88 133.13 m -156.68 133.13 l -156.68 131.95 156.24 131.36 155.36 131.36 c -154.47 131.36 153.98 131.95 153.88 133.13 c -h -159.84 137.00 m -159.84 130.64 l -160.99 130.64 l -160.99 131.83 l -161.45 130.94 162.11 130.50 162.98 130.50 c -163.10 130.50 163.22 130.51 163.35 130.53 c -163.35 131.60 l -163.15 131.54 162.98 131.50 162.82 131.50 c -162.09 131.50 161.48 131.94 160.99 132.80 c -160.99 137.00 l -h -164.75 137.00 m -164.75 130.64 l -165.90 130.64 l -165.90 131.83 l -166.51 130.94 167.26 130.50 168.14 130.50 c -168.69 130.50 169.13 130.67 169.46 131.02 c -169.79 131.37 169.95 131.84 169.95 132.43 c -169.95 137.00 l -168.79 137.00 l -168.79 132.80 l -168.79 132.33 168.73 132.00 168.59 131.79 c -168.45 131.59 168.22 131.49 167.90 131.49 c -167.19 131.49 166.53 131.96 165.90 132.88 c -165.90 137.00 l -h -175.47 136.19 m -174.78 136.83 174.12 137.15 173.48 137.15 c -172.95 137.15 172.51 136.98 172.16 136.65 c -171.82 136.32 171.64 135.90 171.64 135.40 c -171.64 134.71 171.93 134.17 172.52 133.80 c -173.10 133.42 173.94 133.24 175.03 133.24 c -175.30 133.24 l -175.30 132.47 l -175.30 131.73 174.93 131.36 174.17 131.36 c -173.56 131.36 172.90 131.55 172.19 131.93 c -172.19 130.97 l -172.97 130.65 173.70 130.50 174.38 130.50 c -175.09 130.50 175.61 130.66 175.95 130.98 c -176.29 131.30 176.46 131.79 176.46 132.47 c -176.46 135.35 l -176.46 136.01 176.66 136.34 177.07 136.34 c -177.12 136.34 177.19 136.34 177.29 136.32 c -177.37 136.96 l -177.11 137.08 176.82 137.15 176.51 137.15 c -175.97 137.15 175.62 136.83 175.47 136.19 c -h -175.30 135.56 m -175.30 133.92 l -174.92 133.91 l -174.29 133.91 173.77 134.03 173.38 134.27 c -172.99 134.51 172.80 134.82 172.80 135.21 c -172.80 135.49 172.89 135.72 173.09 135.92 c -173.29 136.11 173.52 136.20 173.80 136.20 c -174.29 136.20 174.79 135.99 175.30 135.56 c -h -178.82 137.00 m -178.82 130.64 l -179.97 130.64 l -179.97 131.83 l -180.54 130.94 181.26 130.50 182.14 130.50 c -182.99 130.50 183.57 130.94 183.88 131.83 c -184.43 130.94 185.14 130.49 186.02 130.49 c -186.58 130.49 187.01 130.66 187.32 130.99 c -187.63 131.32 187.79 131.78 187.79 132.37 c -187.79 137.00 l -186.62 137.00 l -186.62 132.55 l -186.62 131.83 186.34 131.46 185.76 131.46 c -185.17 131.46 184.54 131.89 183.88 132.73 c -183.88 137.00 l -182.72 137.00 l -182.72 132.55 l -182.72 131.82 182.43 131.46 181.84 131.46 c -181.26 131.46 180.64 131.88 179.97 132.73 c -179.97 137.00 l -h -194.69 136.79 m -193.91 137.03 193.25 137.15 192.70 137.15 c -191.76 137.15 191.00 136.83 190.41 136.21 c -189.82 135.59 189.52 134.78 189.52 133.79 c -189.52 132.82 189.78 132.03 190.30 131.42 c -190.82 130.80 191.49 130.49 192.30 130.49 c -193.07 130.49 193.67 130.76 194.09 131.31 c -194.51 131.86 194.72 132.63 194.72 133.64 c -194.71 134.00 l -190.70 134.00 l -190.87 135.51 191.61 136.27 192.92 136.27 c -193.40 136.27 193.99 136.14 194.69 135.88 c -h -190.75 133.13 m -193.56 133.13 l -193.56 131.95 193.12 131.36 192.23 131.36 c -191.35 131.36 190.85 131.95 190.75 133.13 c -h -196.73 138.88 m -196.73 138.45 l -197.10 138.34 197.29 137.90 197.29 137.12 c -197.29 137.00 l -196.73 137.00 l -196.73 135.55 l -198.17 135.55 l -198.17 136.81 l -198.17 138.09 197.69 138.78 196.73 138.88 c -h -204.30 139.31 m -204.30 130.64 l -205.46 130.64 l -205.46 131.83 l -205.93 130.94 206.64 130.50 207.58 130.50 c -208.35 130.50 208.95 130.78 209.39 131.33 c -209.83 131.89 210.05 132.66 210.05 133.62 c -210.05 134.68 209.80 135.53 209.30 136.18 c -208.81 136.82 208.15 137.15 207.34 137.15 c -206.58 137.15 205.96 136.86 205.46 136.28 c -205.46 139.31 l -h -205.46 135.48 m -206.05 136.01 206.62 136.28 207.16 136.28 c -208.27 136.28 208.82 135.43 208.82 133.74 c -208.82 132.25 208.33 131.50 207.34 131.50 c -206.70 131.50 206.07 131.85 205.46 132.55 c -h -215.14 136.19 m -214.45 136.83 213.78 137.15 213.14 137.15 c -212.61 137.15 212.17 136.98 211.83 136.65 c -211.48 136.32 211.30 135.90 211.30 135.40 c -211.30 134.71 211.60 134.17 212.18 133.80 c -212.76 133.42 213.60 133.24 214.69 133.24 c -214.97 133.24 l -214.97 132.47 l -214.97 131.73 214.59 131.36 213.83 131.36 c -213.22 131.36 212.56 131.55 211.86 131.93 c -211.86 130.97 l -212.63 130.65 213.36 130.50 214.04 130.50 c -214.75 130.50 215.28 130.66 215.61 130.98 c -215.95 131.30 216.12 131.79 216.12 132.47 c -216.12 135.35 l -216.12 136.01 216.32 136.34 216.73 136.34 c -216.78 136.34 216.86 136.34 216.95 136.32 c -217.04 136.96 l -216.77 137.08 216.48 137.15 216.17 137.15 c -215.63 137.15 215.29 136.83 215.14 136.19 c -h -214.97 135.56 m -214.97 133.92 l -214.58 133.91 l -213.95 133.91 213.44 134.03 213.04 134.27 c -212.65 134.51 212.46 134.82 212.46 135.21 c -212.46 135.49 212.56 135.72 212.75 135.92 c -212.95 136.11 213.19 136.20 213.47 136.20 c -213.95 136.20 214.45 135.99 214.97 135.56 c -h -220.17 137.15 m -219.64 137.15 219.00 137.02 218.25 136.78 c -218.25 135.72 l -219.00 136.09 219.66 136.28 220.22 136.28 c -220.55 136.28 220.82 136.19 221.04 136.01 c -221.26 135.83 221.37 135.61 221.37 135.34 c -221.37 134.94 221.06 134.62 220.45 134.36 c -219.78 134.07 l -218.78 133.66 218.28 133.06 218.28 132.28 c -218.28 131.73 218.48 131.29 218.87 130.97 c -219.26 130.66 219.80 130.50 220.49 130.50 c -220.84 130.50 221.28 130.54 221.80 130.64 c -222.04 130.69 l -222.04 131.65 l -221.40 131.46 220.89 131.36 220.51 131.36 c -219.77 131.36 219.40 131.63 219.40 132.17 c -219.40 132.52 219.68 132.81 220.24 133.05 c -220.80 133.29 l -221.43 133.55 221.87 133.83 222.13 134.13 c -222.39 134.42 222.53 134.79 222.53 135.23 c -222.53 135.79 222.30 136.25 221.86 136.61 c -221.42 136.97 220.86 137.15 220.17 137.15 c -h -226.29 137.15 m -225.76 137.15 225.12 137.02 224.37 136.78 c -224.37 135.72 l -225.12 136.09 225.78 136.28 226.33 136.28 c -226.67 136.28 226.94 136.19 227.16 136.01 c -227.38 135.83 227.49 135.61 227.49 135.34 c -227.49 134.94 227.18 134.62 226.57 134.36 c -225.89 134.07 l -224.90 133.66 224.40 133.06 224.40 132.28 c -224.40 131.73 224.60 131.29 224.99 130.97 c -225.38 130.66 225.92 130.50 226.60 130.50 c -226.96 130.50 227.40 130.54 227.92 130.64 c -228.16 130.69 l -228.16 131.65 l -227.52 131.46 227.01 131.36 226.63 131.36 c -225.88 131.36 225.51 131.63 225.51 132.17 c -225.51 132.52 225.79 132.81 226.36 133.05 c -226.91 133.29 l -227.54 133.55 227.99 133.83 228.25 134.13 c -228.51 134.42 228.64 134.79 228.64 135.23 c -228.64 135.79 228.42 136.25 227.98 136.61 c -227.54 136.97 226.97 137.15 226.29 137.15 c -h -231.46 137.00 m -229.63 130.64 l -230.76 130.64 l -232.16 135.56 l -233.66 130.64 l -234.82 130.64 l -236.14 135.56 l -237.74 130.64 l -238.73 130.64 l -236.65 137.00 l -235.49 137.00 l -234.14 132.07 l -232.62 137.00 l -h -242.45 137.15 m -241.54 137.15 240.82 136.84 240.27 136.24 c -239.73 135.64 239.46 134.83 239.46 133.82 c -239.46 132.79 239.73 131.99 240.28 131.39 c -240.82 130.79 241.56 130.50 242.49 130.50 c -243.43 130.50 244.17 130.79 244.71 131.39 c -245.26 131.99 245.53 132.79 245.53 133.81 c -245.53 134.85 245.26 135.66 244.71 136.26 c -244.16 136.85 243.41 137.15 242.45 137.15 c -h -242.47 136.28 m -243.69 136.28 244.30 135.46 244.30 133.81 c -244.30 132.18 243.70 131.36 242.49 131.36 c -241.29 131.36 240.69 132.18 240.69 133.82 c -240.69 135.46 241.28 136.28 242.47 136.28 c -h -247.33 137.00 m -247.33 130.64 l -248.49 130.64 l -248.49 131.83 l -248.95 130.94 249.61 130.50 250.48 130.50 c -250.60 130.50 250.72 130.51 250.85 130.53 c -250.85 131.60 l -250.65 131.54 250.47 131.50 250.32 131.50 c -249.59 131.50 248.98 131.94 248.49 132.80 c -248.49 137.00 l -h -256.33 137.00 m -256.33 135.80 l -255.86 136.70 255.15 137.15 254.21 137.15 c -253.44 137.15 252.84 136.87 252.40 136.31 c -251.96 135.75 251.74 134.99 251.74 134.02 c -251.74 132.96 251.99 132.11 252.49 131.46 c -252.99 130.82 253.64 130.50 254.45 130.50 c -255.21 130.50 255.83 130.79 256.33 131.36 c -256.33 127.75 l -257.49 127.75 l -257.49 137.00 l -h -256.33 132.15 m -255.73 131.63 255.16 131.36 254.63 131.36 c -253.52 131.36 252.97 132.21 252.97 133.90 c -252.97 135.39 253.46 136.13 254.45 136.13 c -255.09 136.13 255.71 135.78 256.33 135.08 c -h -259.81 138.88 m -259.81 138.45 l -260.19 138.34 260.38 137.90 260.38 137.12 c -260.38 137.00 l -259.81 137.00 l -259.81 135.55 l -261.26 135.55 l -261.26 136.81 l -261.26 138.09 260.78 138.78 259.81 138.88 c -h -269.30 137.15 m -268.71 137.15 268.26 136.98 267.93 136.64 c -267.60 136.31 267.44 135.84 267.44 135.24 c -267.44 131.50 l -266.64 131.50 l -266.64 130.64 l -267.44 130.64 l -267.44 129.48 l -268.59 129.37 l -268.59 130.64 l -270.26 130.64 l -270.26 131.50 l -268.59 131.50 l -268.59 135.03 l -268.59 135.86 268.95 136.28 269.67 136.28 c -269.82 136.28 270.01 136.25 270.23 136.20 c -270.23 137.00 l -269.87 137.10 269.56 137.15 269.30 137.15 c -h -276.54 136.79 m -275.77 137.03 275.11 137.15 274.56 137.15 c -273.62 137.15 272.85 136.83 272.26 136.21 c -271.67 135.59 271.38 134.78 271.38 133.79 c -271.38 132.82 271.64 132.03 272.16 131.42 c -272.68 130.80 273.35 130.49 274.16 130.49 c -274.93 130.49 275.52 130.76 275.94 131.31 c -276.36 131.86 276.57 132.63 276.57 133.64 c -276.57 134.00 l -272.55 134.00 l -272.72 135.51 273.46 136.27 274.77 136.27 c -275.25 136.27 275.84 136.14 276.54 135.88 c -h -272.61 133.13 m -275.41 133.13 l -275.41 131.95 274.97 131.36 274.09 131.36 c -273.20 131.36 272.71 131.95 272.61 133.13 c -h -278.56 137.00 m -278.56 130.64 l -279.72 130.64 l -279.72 131.83 l -280.33 130.94 281.07 130.50 281.96 130.50 c -282.51 130.50 282.95 130.67 283.28 131.02 c -283.60 131.37 283.77 131.84 283.77 132.43 c -283.77 137.00 l -282.61 137.00 l -282.61 132.80 l -282.61 132.33 282.54 132.00 282.41 131.79 c -282.27 131.59 282.04 131.49 281.72 131.49 c -281.01 131.49 280.34 131.96 279.72 132.88 c -279.72 137.00 l -h -289.29 136.19 m -288.60 136.83 287.94 137.15 287.29 137.15 c -286.77 137.15 286.33 136.98 285.98 136.65 c -285.63 136.32 285.46 135.90 285.46 135.40 c -285.46 134.71 285.75 134.17 286.34 133.80 c -286.92 133.42 287.76 133.24 288.85 133.24 c -289.12 133.24 l -289.12 132.47 l -289.12 131.73 288.74 131.36 287.99 131.36 c -287.38 131.36 286.72 131.55 286.01 131.93 c -286.01 130.97 l -286.79 130.65 287.52 130.50 288.20 130.50 c -288.91 130.50 289.43 130.66 289.77 130.98 c -290.11 131.30 290.28 131.79 290.28 132.47 c -290.28 135.35 l -290.28 136.01 290.48 136.34 290.89 136.34 c -290.94 136.34 291.01 136.34 291.11 136.32 c -291.19 136.96 l -290.93 137.08 290.64 137.15 290.32 137.15 c -289.79 137.15 289.44 136.83 289.29 136.19 c -h -289.12 135.56 m -289.12 133.92 l -288.74 133.91 l -288.10 133.91 287.59 134.03 287.20 134.27 c -286.81 134.51 286.62 134.82 286.62 135.21 c -286.62 135.49 286.71 135.72 286.91 135.92 c -287.10 136.11 287.34 136.20 287.62 136.20 c -288.10 136.20 288.60 135.99 289.12 135.56 c -h -292.64 137.00 m -292.64 130.64 l -293.79 130.64 l -293.79 131.83 l -294.40 130.94 295.15 130.50 296.03 130.50 c -296.58 130.50 297.02 130.67 297.35 131.02 c -297.68 131.37 297.84 131.84 297.84 132.43 c -297.84 137.00 l -296.69 137.00 l -296.69 132.80 l -296.69 132.33 296.62 132.00 296.48 131.79 c -296.34 131.59 296.11 131.49 295.79 131.49 c -295.08 131.49 294.42 131.96 293.79 132.88 c -293.79 137.00 l -h -302.00 137.15 m -301.41 137.15 300.95 136.98 300.62 136.64 c -300.30 136.31 300.13 135.84 300.13 135.24 c -300.13 131.50 l -299.34 131.50 l -299.34 130.64 l -300.13 130.64 l -300.13 129.48 l -301.29 129.37 l -301.29 130.64 l -302.95 130.64 l -302.95 131.50 l -301.29 131.50 l -301.29 135.03 l -301.29 135.86 301.65 136.28 302.37 136.28 c -302.52 136.28 302.70 136.25 302.92 136.20 c -302.92 137.00 l -302.57 137.10 302.26 137.15 302.00 137.15 c -h -303.42 139.02 m -303.42 138.15 l -309.42 138.15 l -309.42 139.02 l -h -310.57 137.00 m -310.57 130.64 l -311.73 130.64 l -311.73 137.00 l -h -310.57 129.48 m -310.57 128.33 l -311.73 128.33 l -311.73 129.48 l -h -318.13 137.00 m -318.13 135.80 l -317.66 136.70 316.95 137.15 316.01 137.15 c -315.24 137.15 314.64 136.87 314.20 136.31 c -313.76 135.75 313.54 134.99 313.54 134.02 c -313.54 132.96 313.79 132.11 314.29 131.46 c -314.78 130.82 315.44 130.50 316.25 130.50 c -317.01 130.50 317.63 130.79 318.13 131.36 c -318.13 127.75 l -319.29 127.75 l -319.29 137.00 l -h -318.13 132.15 m -317.53 131.63 316.96 131.36 316.43 131.36 c -315.32 131.36 314.77 132.21 314.77 133.90 c -314.77 135.39 315.26 136.13 316.25 136.13 c -316.89 136.13 317.51 135.78 318.13 135.08 c -h -321.74 137.00 m -328.68 133.53 l -321.74 130.06 l -321.74 131.03 l -326.74 133.53 l -321.74 136.03 l -h -f -[] 0.0000 d -70.500 140.50 m -463.50 140.50 l -S -463.00 140.00 m -457.00 134.00 l -457.00 146.00 l -h -f* -80.436 304.19 m -79.744 304.83 79.078 305.15 78.438 305.15 c -77.910 305.15 77.473 304.98 77.125 304.65 c -76.777 304.32 76.604 303.90 76.604 303.40 c -76.604 302.71 76.896 302.17 77.479 301.80 c -78.063 301.42 78.900 301.24 79.990 301.24 c -80.266 301.24 l -80.266 300.47 l -80.266 299.73 79.887 299.36 79.129 299.36 c -78.520 299.36 77.861 299.55 77.154 299.93 c -77.154 298.97 l -77.932 298.65 78.660 298.50 79.340 298.50 c -80.051 298.50 80.575 298.66 80.913 298.98 c -81.251 299.30 81.420 299.79 81.420 300.47 c -81.420 303.35 l -81.420 304.01 81.623 304.34 82.029 304.34 c -82.080 304.34 82.154 304.34 82.252 304.32 c -82.334 304.96 l -82.072 305.08 81.783 305.15 81.467 305.15 c -80.928 305.15 80.584 304.83 80.436 304.19 c -h -80.266 303.56 m -80.266 301.92 l -79.879 301.91 l -79.246 301.91 78.734 302.03 78.344 302.27 c -77.953 302.51 77.758 302.82 77.758 303.21 c -77.758 303.49 77.855 303.72 78.051 303.92 c -78.246 304.11 78.484 304.20 78.766 304.20 c -79.246 304.20 79.746 303.99 80.266 303.56 c -h -83.781 305.00 m -83.781 298.64 l -84.936 298.64 l -84.936 299.83 l -85.545 298.94 86.291 298.50 87.174 298.50 c -87.725 298.50 88.164 298.67 88.492 299.02 c -88.820 299.37 88.984 299.84 88.984 300.43 c -88.984 305.00 l -87.830 305.00 l -87.830 300.80 l -87.830 300.33 87.761 300.00 87.622 299.79 c -87.483 299.59 87.254 299.49 86.934 299.49 c -86.227 299.49 85.561 299.96 84.936 300.88 c -84.936 305.00 l -h -91.598 307.31 m -92.629 305.00 l -90.168 298.64 l -91.416 298.64 l -93.238 303.43 l -95.184 298.64 l -96.273 298.64 l -92.799 307.31 l -h -96.344 307.02 m -96.344 306.15 l -102.34 306.15 l -102.34 307.02 l -h -105.98 305.15 m -105.12 305.15 104.41 304.83 103.84 304.19 c -103.28 303.55 102.99 302.75 102.99 301.78 c -102.99 300.75 103.27 299.94 103.83 299.36 c -104.40 298.79 105.18 298.50 106.18 298.50 c -106.68 298.50 107.23 298.56 107.85 298.70 c -107.85 299.67 l -107.19 299.48 106.66 299.38 106.25 299.38 c -105.66 299.38 105.19 299.60 104.83 300.05 c -104.47 300.49 104.29 301.08 104.29 301.82 c -104.29 302.53 104.48 303.11 104.85 303.55 c -105.21 303.99 105.69 304.21 106.29 304.21 c -106.81 304.21 107.36 304.08 107.92 303.81 c -107.92 304.81 l -107.17 305.03 106.53 305.15 105.98 305.15 c -h -112.93 304.19 m -112.23 304.83 111.57 305.15 110.93 305.15 c -110.40 305.15 109.96 304.98 109.62 304.65 c -109.27 304.32 109.09 303.90 109.09 303.40 c -109.09 302.71 109.39 302.17 109.97 301.80 c -110.55 301.42 111.39 301.24 112.48 301.24 c -112.76 301.24 l -112.76 300.47 l -112.76 299.73 112.38 299.36 111.62 299.36 c -111.01 299.36 110.35 299.55 109.64 299.93 c -109.64 298.97 l -110.42 298.65 111.15 298.50 111.83 298.50 c -112.54 298.50 113.07 298.66 113.40 298.98 c -113.74 299.30 113.91 299.79 113.91 300.47 c -113.91 303.35 l -113.91 304.01 114.11 304.34 114.52 304.34 c -114.57 304.34 114.64 304.34 114.74 304.32 c -114.82 304.96 l -114.56 305.08 114.27 305.15 113.96 305.15 c -113.42 305.15 113.07 304.83 112.93 304.19 c -h -112.76 303.56 m -112.76 301.92 l -112.37 301.91 l -111.74 301.91 111.22 302.03 110.83 302.27 c -110.44 302.51 110.25 302.82 110.25 303.21 c -110.25 303.49 110.35 303.72 110.54 303.92 c -110.74 304.11 110.97 304.20 111.26 304.20 c -111.74 304.20 112.24 303.99 112.76 303.56 c -h -116.27 305.00 m -116.27 295.75 l -117.43 295.75 l -117.43 305.00 l -h -119.74 305.00 m -119.74 295.75 l -120.89 295.75 l -120.89 305.00 l -h -130.29 305.00 m -123.36 301.53 l -130.29 298.06 l -130.29 299.03 l -125.29 301.53 l -130.29 304.03 l -h -134.66 305.15 m -134.07 305.15 133.62 304.98 133.29 304.64 c -132.96 304.31 132.79 303.84 132.79 303.24 c -132.79 299.50 l -132.00 299.50 l -132.00 298.64 l -132.79 298.64 l -132.79 297.48 l -133.95 297.37 l -133.95 298.64 l -135.61 298.64 l -135.61 299.50 l -133.95 299.50 l -133.95 303.03 l -133.95 303.86 134.31 304.28 135.03 304.28 c -135.18 304.28 135.37 304.25 135.58 304.20 c -135.58 305.00 l -135.23 305.10 134.92 305.15 134.66 305.15 c -h -139.73 305.15 m -138.82 305.15 138.09 304.84 137.55 304.24 c -137.00 303.64 136.73 302.83 136.73 301.82 c -136.73 300.79 137.00 299.99 137.55 299.39 c -138.09 298.79 138.83 298.50 139.77 298.50 c -140.70 298.50 141.44 298.79 141.99 299.39 c -142.53 299.99 142.80 300.79 142.80 301.81 c -142.80 302.85 142.53 303.66 141.98 304.26 c -141.44 304.85 140.68 305.15 139.73 305.15 c -h -139.74 304.28 m -140.97 304.28 141.58 303.46 141.58 301.81 c -141.58 300.18 140.97 299.36 139.77 299.36 c -138.56 299.36 137.96 300.18 137.96 301.82 c -137.96 303.46 138.56 304.28 139.74 304.28 c -h -144.61 305.00 m -144.61 295.75 l -145.76 295.75 l -145.76 301.72 l -148.46 298.64 l -149.70 298.64 l -147.13 301.61 l -150.23 305.00 l -148.76 305.00 l -145.76 301.74 l -145.76 305.00 l -h -156.29 304.79 m -155.51 305.03 154.85 305.15 154.30 305.15 c -153.36 305.15 152.60 304.83 152.00 304.21 c -151.41 303.59 151.12 302.78 151.12 301.79 c -151.12 300.82 151.38 300.03 151.90 299.42 c -152.42 298.80 153.09 298.49 153.90 298.49 c -154.67 298.49 155.26 298.76 155.68 299.31 c -156.10 299.86 156.31 300.63 156.31 301.64 c -156.31 302.00 l -152.29 302.00 l -152.46 303.51 153.20 304.27 154.52 304.27 c -155.00 304.27 155.59 304.14 156.29 303.88 c -h -152.35 301.13 m -155.15 301.13 l -155.15 299.95 154.71 299.36 153.83 299.36 c -152.94 299.36 152.45 299.95 152.35 301.13 c -h -158.31 305.00 m -158.31 298.64 l -159.46 298.64 l -159.46 299.83 l -160.07 298.94 160.82 298.50 161.70 298.50 c -162.25 298.50 162.69 298.67 163.02 299.02 c -163.35 299.37 163.51 299.84 163.51 300.43 c -163.51 305.00 l -162.36 305.00 l -162.36 300.80 l -162.36 300.33 162.29 300.00 162.15 299.79 c -162.01 299.59 161.78 299.49 161.46 299.49 c -160.75 299.49 160.09 299.96 159.46 300.88 c -159.46 305.00 l -h -165.77 306.88 m -165.77 306.45 l -166.15 306.34 166.33 305.90 166.33 305.12 c -166.33 305.00 l -165.77 305.00 l -165.77 303.55 l -167.22 303.55 l -167.22 304.81 l -167.22 306.09 166.74 306.78 165.77 306.88 c -h -177.43 305.00 m -177.43 303.80 l -176.96 304.70 176.26 305.15 175.31 305.15 c -174.54 305.15 173.94 304.87 173.50 304.31 c -173.06 303.75 172.84 302.99 172.84 302.02 c -172.84 300.96 173.09 300.11 173.59 299.46 c -174.09 298.82 174.74 298.50 175.56 298.50 c -176.31 298.50 176.94 298.79 177.43 299.36 c -177.43 295.75 l -178.59 295.75 l -178.59 305.00 l -h -177.43 300.15 m -176.83 299.63 176.27 299.36 175.73 299.36 c -174.63 299.36 174.07 300.21 174.07 301.90 c -174.07 303.39 174.57 304.13 175.55 304.13 c -176.19 304.13 176.82 303.78 177.43 303.08 c -h -184.18 304.19 m -183.49 304.83 182.82 305.15 182.18 305.15 c -181.66 305.15 181.22 304.98 180.87 304.65 c -180.52 304.32 180.35 303.90 180.35 303.40 c -180.35 302.71 180.64 302.17 181.23 301.80 c -181.81 301.42 182.65 301.24 183.74 301.24 c -184.01 301.24 l -184.01 300.47 l -184.01 299.73 183.63 299.36 182.88 299.36 c -182.27 299.36 181.61 299.55 180.90 299.93 c -180.90 298.97 l -181.68 298.65 182.41 298.50 183.09 298.50 c -183.80 298.50 184.32 298.66 184.66 298.98 c -185.00 299.30 185.17 299.79 185.17 300.47 c -185.17 303.35 l -185.17 304.01 185.37 304.34 185.78 304.34 c -185.83 304.34 185.90 304.34 186.00 304.32 c -186.08 304.96 l -185.82 305.08 185.53 305.15 185.21 305.15 c -184.67 305.15 184.33 304.83 184.18 304.19 c -h -184.01 303.56 m -184.01 301.92 l -183.62 301.91 l -182.99 301.91 182.48 302.03 182.09 302.27 c -181.70 302.51 181.50 302.82 181.50 303.21 c -181.50 303.49 181.60 303.72 181.80 303.92 c -181.99 304.11 182.23 304.20 182.51 304.20 c -182.99 304.20 183.49 303.99 184.01 303.56 c -h -189.44 305.15 m -188.85 305.15 188.39 304.98 188.07 304.64 c -187.74 304.31 187.57 303.84 187.57 303.24 c -187.57 299.50 l -186.78 299.50 l -186.78 298.64 l -187.57 298.64 l -187.57 297.48 l -188.73 297.37 l -188.73 298.64 l -190.39 298.64 l -190.39 299.50 l -188.73 299.50 l -188.73 303.03 l -188.73 303.86 189.09 304.28 189.81 304.28 c -189.96 304.28 190.14 304.25 190.36 304.20 c -190.36 305.00 l -190.01 305.10 189.70 305.15 189.44 305.15 c -h -195.30 304.19 m -194.61 304.83 193.94 305.15 193.30 305.15 c -192.77 305.15 192.33 304.98 191.99 304.65 c -191.64 304.32 191.46 303.90 191.46 303.40 c -191.46 302.71 191.76 302.17 192.34 301.80 c -192.92 301.42 193.76 301.24 194.85 301.24 c -195.13 301.24 l -195.13 300.47 l -195.13 299.73 194.75 299.36 193.99 299.36 c -193.38 299.36 192.72 299.55 192.02 299.93 c -192.02 298.97 l -192.79 298.65 193.52 298.50 194.20 298.50 c -194.91 298.50 195.44 298.66 195.77 298.98 c -196.11 299.30 196.28 299.79 196.28 300.47 c -196.28 303.35 l -196.28 304.01 196.48 304.34 196.89 304.34 c -196.94 304.34 197.02 304.34 197.11 304.32 c -197.20 304.96 l -196.93 305.08 196.64 305.15 196.33 305.15 c -195.79 305.15 195.45 304.83 195.30 304.19 c -h -195.13 303.56 m -195.13 301.92 l -194.74 301.91 l -194.11 301.91 193.60 302.03 193.21 302.27 c -192.81 302.51 192.62 302.82 192.62 303.21 c -192.62 303.49 192.72 303.72 192.91 303.92 c -193.11 304.11 193.35 304.20 193.63 304.20 c -194.11 304.20 194.61 303.99 195.13 303.56 c -h -197.49 307.02 m -197.49 306.15 l -203.49 306.15 l -203.49 307.02 l -h -206.55 305.15 m -205.97 305.15 205.51 304.98 205.18 304.64 c -204.85 304.31 204.69 303.84 204.69 303.24 c -204.69 299.50 l -203.89 299.50 l -203.89 298.64 l -204.69 298.64 l -204.69 297.48 l -205.84 297.37 l -205.84 298.64 l -207.51 298.64 l -207.51 299.50 l -205.84 299.50 l -205.84 303.03 l -205.84 303.86 206.20 304.28 206.92 304.28 c -207.07 304.28 207.26 304.25 207.48 304.20 c -207.48 305.00 l -207.12 305.10 206.81 305.15 206.55 305.15 c -h -211.62 305.15 m -210.71 305.15 209.98 304.84 209.44 304.24 c -208.90 303.64 208.63 302.83 208.63 301.82 c -208.63 300.79 208.90 299.99 209.44 299.39 c -209.99 298.79 210.73 298.50 211.66 298.50 c -212.60 298.50 213.33 298.79 213.88 299.39 c -214.42 299.99 214.70 300.79 214.70 301.81 c -214.70 302.85 214.42 303.66 213.88 304.26 c -213.33 304.85 212.58 305.15 211.62 305.15 c -h -211.64 304.28 m -212.86 304.28 213.47 303.46 213.47 301.81 c -213.47 300.18 212.87 299.36 211.66 299.36 c -210.46 299.36 209.86 300.18 209.86 301.82 c -209.86 303.46 210.45 304.28 211.64 304.28 c -h -215.35 307.02 m -215.35 306.15 l -221.35 306.15 l -221.35 307.02 l -h -227.17 304.79 m -226.39 305.03 225.73 305.15 225.18 305.15 c -224.24 305.15 223.48 304.83 222.89 304.21 c -222.29 303.59 222.00 302.78 222.00 301.79 c -222.00 300.82 222.26 300.03 222.78 299.42 c -223.30 298.80 223.97 298.49 224.78 298.49 c -225.55 298.49 226.15 298.76 226.57 299.31 c -226.99 299.86 227.20 300.63 227.20 301.64 c -227.19 302.00 l -223.18 302.00 l -223.34 303.51 224.08 304.27 225.40 304.27 c -225.88 304.27 226.47 304.14 227.17 303.88 c -h -223.23 301.13 m -226.04 301.13 l -226.04 299.95 225.59 299.36 224.71 299.36 c -223.82 299.36 223.33 299.95 223.23 301.13 c -h -231.67 305.15 m -230.81 305.15 230.10 304.83 229.53 304.19 c -228.97 303.55 228.68 302.75 228.68 301.78 c -228.68 300.75 228.96 299.94 229.52 299.36 c -230.08 298.79 230.87 298.50 231.87 298.50 c -232.37 298.50 232.92 298.56 233.54 298.70 c -233.54 299.67 l -232.88 299.48 232.35 299.38 231.94 299.38 c -231.35 299.38 230.88 299.60 230.52 300.05 c -230.16 300.49 229.98 301.08 229.98 301.82 c -229.98 302.53 230.17 303.11 230.54 303.55 c -230.90 303.99 231.38 304.21 231.98 304.21 c -232.50 304.21 233.05 304.08 233.61 303.81 c -233.61 304.81 l -232.86 305.03 232.21 305.15 231.67 305.15 c -h -235.33 305.00 m -235.33 295.75 l -236.49 295.75 l -236.49 299.83 l -237.10 298.94 237.84 298.50 238.73 298.50 c -239.28 298.50 239.72 298.67 240.04 299.02 c -240.37 299.37 240.54 299.84 240.54 300.43 c -240.54 305.00 l -239.38 305.00 l -239.38 300.80 l -239.38 300.33 239.31 300.00 239.17 299.79 c -239.04 299.59 238.81 299.49 238.49 299.49 c -237.78 299.49 237.11 299.96 236.49 300.88 c -236.49 305.00 l -h -245.27 305.15 m -244.36 305.15 243.63 304.84 243.09 304.24 c -242.55 303.64 242.28 302.83 242.28 301.82 c -242.28 300.79 242.55 299.99 243.09 299.39 c -243.64 298.79 244.38 298.50 245.31 298.50 c -246.25 298.50 246.99 298.79 247.53 299.39 c -248.08 299.99 248.35 300.79 248.35 301.81 c -248.35 302.85 248.07 303.66 247.53 304.26 c -246.98 304.85 246.23 305.15 245.27 305.15 c -h -245.29 304.28 m -246.51 304.28 247.12 303.46 247.12 301.81 c -247.12 300.18 246.52 299.36 245.31 299.36 c -244.11 299.36 243.51 300.18 243.51 301.82 c -243.51 303.46 244.10 304.28 245.29 304.28 c -h -250.30 305.00 m -257.24 301.53 l -250.30 298.06 l -250.30 299.03 l -255.30 301.53 l -250.30 304.03 l -h -f -70.500 308.50 m -618.50 308.50 l -S -618.00 308.00 m -612.00 302.00 l -612.00 314.00 l -h -f* -80.436 220.19 m -79.744 220.83 79.078 221.15 78.438 221.15 c -77.910 221.15 77.473 220.98 77.125 220.65 c -76.777 220.32 76.604 219.90 76.604 219.40 c -76.604 218.71 76.896 218.17 77.479 217.80 c -78.063 217.42 78.900 217.24 79.990 217.24 c -80.266 217.24 l -80.266 216.47 l -80.266 215.73 79.887 215.36 79.129 215.36 c -78.520 215.36 77.861 215.55 77.154 215.93 c -77.154 214.97 l -77.932 214.65 78.660 214.50 79.340 214.50 c -80.051 214.50 80.575 214.66 80.913 214.98 c -81.251 215.30 81.420 215.79 81.420 216.47 c -81.420 219.35 l -81.420 220.01 81.623 220.34 82.029 220.34 c -82.080 220.34 82.154 220.34 82.252 220.32 c -82.334 220.96 l -82.072 221.08 81.783 221.15 81.467 221.15 c -80.928 221.15 80.584 220.83 80.436 220.19 c -h -80.266 219.56 m -80.266 217.92 l -79.879 217.91 l -79.246 217.91 78.734 218.03 78.344 218.27 c -77.953 218.51 77.758 218.82 77.758 219.21 c -77.758 219.49 77.855 219.72 78.051 219.92 c -78.246 220.11 78.484 220.20 78.766 220.20 c -79.246 220.20 79.746 219.99 80.266 219.56 c -h -87.760 221.00 m -87.760 219.80 l -87.146 220.70 86.402 221.15 85.527 221.15 c -84.973 221.15 84.531 220.97 84.203 220.62 c -83.875 220.27 83.711 219.80 83.711 219.21 c -83.711 214.64 l -84.865 214.64 l -84.865 218.83 l -84.865 219.31 84.935 219.65 85.073 219.85 c -85.212 220.05 85.443 220.15 85.768 220.15 c -86.471 220.15 87.135 219.69 87.760 218.76 c -87.760 214.64 l -88.914 214.64 l -88.914 221.00 l -h -93.139 221.15 m -92.553 221.15 92.096 220.98 91.768 220.64 c -91.439 220.31 91.275 219.84 91.275 219.24 c -91.275 215.50 l -90.479 215.50 l -90.479 214.64 l -91.275 214.64 l -91.275 213.48 l -92.430 213.37 l -92.430 214.64 l -94.094 214.64 l -94.094 215.50 l -92.430 215.50 l -92.430 219.03 l -92.430 219.86 92.789 220.28 93.508 220.28 c -93.660 220.28 93.846 220.25 94.064 220.20 c -94.064 221.00 l -93.709 221.10 93.400 221.15 93.139 221.15 c -h -95.717 221.00 m -95.717 211.75 l -96.871 211.75 l -96.871 215.83 l -97.480 214.94 98.227 214.50 99.109 214.50 c -99.660 214.50 100.10 214.67 100.43 215.02 c -100.76 215.37 100.92 215.84 100.92 216.43 c -100.92 221.00 l -99.766 221.00 l -99.766 216.80 l -99.766 216.33 99.696 216.00 99.558 215.79 c -99.419 215.59 99.189 215.49 98.869 215.49 c -98.162 215.49 97.496 215.96 96.871 216.88 c -96.871 221.00 l -h -110.25 221.00 m -103.31 217.53 l -110.25 214.06 l -110.25 215.03 l -105.25 217.53 l -110.25 220.03 l -h -116.68 221.00 m -116.68 219.80 l -116.07 220.70 115.32 221.15 114.45 221.15 c -113.89 221.15 113.45 220.97 113.12 220.62 c -112.80 220.27 112.63 219.80 112.63 219.21 c -112.63 214.64 l -113.79 214.64 l -113.79 218.83 l -113.79 219.31 113.86 219.65 114.00 219.85 c -114.13 220.05 114.37 220.15 114.69 220.15 c -115.39 220.15 116.06 219.69 116.68 218.76 c -116.68 214.64 l -117.84 214.64 l -117.84 221.00 l -h -121.84 221.15 m -121.31 221.15 120.67 221.02 119.92 220.78 c -119.92 219.72 l -120.67 220.09 121.33 220.28 121.88 220.28 c -122.22 220.28 122.49 220.19 122.71 220.01 c -122.93 219.83 123.04 219.61 123.04 219.34 c -123.04 218.94 122.73 218.62 122.12 218.36 c -121.45 218.07 l -120.45 217.66 119.95 217.06 119.95 216.28 c -119.95 215.73 120.15 215.29 120.54 214.97 c -120.93 214.66 121.47 214.50 122.15 214.50 c -122.51 214.50 122.95 214.54 123.47 214.64 c -123.71 214.69 l -123.71 215.65 l -123.07 215.46 122.56 215.36 122.18 215.36 c -121.44 215.36 121.06 215.63 121.06 216.17 c -121.06 216.52 121.35 216.81 121.91 217.05 c -122.46 217.29 l -123.09 217.55 123.54 217.83 123.80 218.13 c -124.06 218.42 124.19 218.79 124.19 219.23 c -124.19 219.79 123.97 220.25 123.53 220.61 c -123.09 220.97 122.53 221.15 121.84 221.15 c -h -130.93 220.79 m -130.16 221.03 129.50 221.15 128.95 221.15 c -128.01 221.15 127.24 220.83 126.65 220.21 c -126.06 219.59 125.76 218.78 125.76 217.79 c -125.76 216.82 126.02 216.03 126.55 215.42 c -127.07 214.80 127.73 214.49 128.55 214.49 c -129.32 214.49 129.91 214.76 130.33 215.31 c -130.75 215.86 130.96 216.63 130.96 217.64 c -130.96 218.00 l -126.94 218.00 l -127.11 219.51 127.85 220.27 129.16 220.27 c -129.64 220.27 130.23 220.14 130.93 219.88 c -h -126.99 217.13 m -129.80 217.13 l -129.80 215.95 129.36 215.36 128.48 215.36 c -127.59 215.36 127.10 215.95 126.99 217.13 c -h -132.95 221.00 m -132.95 214.64 l -134.11 214.64 l -134.11 215.83 l -134.56 214.94 135.23 214.50 136.10 214.50 c -136.22 214.50 136.34 214.51 136.47 214.53 c -136.47 215.60 l -136.27 215.54 136.09 215.50 135.94 215.50 c -135.21 215.50 134.60 215.94 134.11 216.80 c -134.11 221.00 l -h -137.88 222.88 m -137.88 222.45 l -138.26 222.34 138.44 221.90 138.44 221.12 c -138.44 221.00 l -137.88 221.00 l -137.88 219.55 l -139.33 219.55 l -139.33 220.81 l -139.33 222.09 138.85 222.78 137.88 222.88 c -h -145.46 223.31 m -145.46 214.64 l -146.61 214.64 l -146.61 215.83 l -147.08 214.94 147.79 214.50 148.74 214.50 c -149.50 214.50 150.11 214.78 150.55 215.33 c -150.99 215.89 151.21 216.66 151.21 217.62 c -151.21 218.68 150.96 219.53 150.46 220.18 c -149.96 220.82 149.30 221.15 148.49 221.15 c -147.74 221.15 147.11 220.86 146.61 220.28 c -146.61 223.31 l -h -146.61 219.48 m -147.21 220.01 147.77 220.28 148.31 220.28 c -149.42 220.28 149.97 219.43 149.97 217.74 c -149.97 216.25 149.48 215.50 148.50 215.50 c -147.85 215.50 147.22 215.85 146.61 216.55 c -h -156.29 220.19 m -155.60 220.83 154.93 221.15 154.29 221.15 c -153.77 221.15 153.33 220.98 152.98 220.65 c -152.63 220.32 152.46 219.90 152.46 219.40 c -152.46 218.71 152.75 218.17 153.33 217.80 c -153.92 217.42 154.76 217.24 155.85 217.24 c -156.12 217.24 l -156.12 216.47 l -156.12 215.73 155.74 215.36 154.98 215.36 c -154.38 215.36 153.72 215.55 153.01 215.93 c -153.01 214.97 l -153.79 214.65 154.52 214.50 155.20 214.50 c -155.91 214.50 156.43 214.66 156.77 214.98 c -157.11 215.30 157.28 215.79 157.28 216.47 c -157.28 219.35 l -157.28 220.01 157.48 220.34 157.88 220.34 c -157.94 220.34 158.01 220.34 158.11 220.32 c -158.19 220.96 l -157.93 221.08 157.64 221.15 157.32 221.15 c -156.78 221.15 156.44 220.83 156.29 220.19 c -h -156.12 219.56 m -156.12 217.92 l -155.73 217.91 l -155.10 217.91 154.59 218.03 154.20 218.27 c -153.81 218.51 153.61 218.82 153.61 219.21 c -153.61 219.49 153.71 219.72 153.91 219.92 c -154.10 220.11 154.34 220.20 154.62 220.20 c -155.10 220.20 155.60 219.99 156.12 219.56 c -h -161.32 221.15 m -160.80 221.15 160.16 221.02 159.40 220.78 c -159.40 219.72 l -160.16 220.09 160.81 220.28 161.37 220.28 c -161.70 220.28 161.98 220.19 162.20 220.01 c -162.42 219.83 162.53 219.61 162.53 219.34 c -162.53 218.94 162.22 218.62 161.61 218.36 c -160.93 218.07 l -159.94 217.66 159.44 217.06 159.44 216.28 c -159.44 215.73 159.63 215.29 160.03 214.97 c -160.42 214.66 160.96 214.50 161.64 214.50 c -162.00 214.50 162.44 214.54 162.96 214.64 c -163.20 214.69 l -163.20 215.65 l -162.55 215.46 162.04 215.36 161.66 215.36 c -160.92 215.36 160.55 215.63 160.55 216.17 c -160.55 216.52 160.83 216.81 161.39 217.05 c -161.95 217.29 l -162.58 217.55 163.03 217.83 163.29 218.13 c -163.55 218.42 163.68 218.79 163.68 219.23 c -163.68 219.79 163.46 220.25 163.02 220.61 c -162.58 220.97 162.01 221.15 161.32 221.15 c -h -167.44 221.15 m -166.91 221.15 166.27 221.02 165.52 220.78 c -165.52 219.72 l -166.27 220.09 166.93 220.28 167.49 220.28 c -167.82 220.28 168.10 220.19 168.31 220.01 c -168.53 219.83 168.64 219.61 168.64 219.34 c -168.64 218.94 168.34 218.62 167.72 218.36 c -167.05 218.07 l -166.05 217.66 165.55 217.06 165.55 216.28 c -165.55 215.73 165.75 215.29 166.14 214.97 c -166.54 214.66 167.07 214.50 167.76 214.50 c -168.11 214.50 168.55 214.54 169.08 214.64 c -169.32 214.69 l -169.32 215.65 l -168.67 215.46 168.16 215.36 167.78 215.36 c -167.04 215.36 166.67 215.63 166.67 216.17 c -166.67 216.52 166.95 216.81 167.51 217.05 c -168.07 217.29 l -168.70 217.55 169.14 217.83 169.40 218.13 c -169.67 218.42 169.80 218.79 169.80 219.23 c -169.80 219.79 169.58 220.25 169.13 220.61 c -168.69 220.97 168.13 221.15 167.44 221.15 c -h -172.02 221.00 m -178.96 217.53 l -172.02 214.06 l -172.02 215.03 l -177.02 217.53 l -172.02 220.03 l -h -f -70.500 224.50 m -463.50 224.50 l -S -463.00 224.00 m -457.00 218.00 l -457.00 230.00 l -h -f* -79.639 99.146 m -78.779 99.146 78.066 98.828 77.500 98.191 c -76.934 97.555 76.650 96.752 76.650 95.783 c -76.650 94.748 76.931 93.941 77.491 93.363 c -78.052 92.785 78.834 92.496 79.838 92.496 c -80.334 92.496 80.889 92.564 81.502 92.701 c -81.502 93.668 l -80.850 93.477 80.318 93.381 79.908 93.381 c -79.318 93.381 78.845 93.603 78.487 94.046 c -78.130 94.489 77.951 95.080 77.951 95.818 c -77.951 96.533 78.135 97.111 78.502 97.553 c -78.869 97.994 79.350 98.215 79.943 98.215 c -80.471 98.215 81.014 98.080 81.572 97.811 c -81.572 98.807 l -80.826 99.033 80.182 99.146 79.639 99.146 c -h -83.301 99.000 m -83.301 92.637 l -84.455 92.637 l -84.455 93.832 l -84.912 92.941 85.576 92.496 86.447 92.496 c -86.564 92.496 86.688 92.506 86.816 92.525 c -86.816 93.604 l -86.617 93.537 86.441 93.504 86.289 93.504 c -85.559 93.504 84.947 93.938 84.455 94.805 c -84.455 99.000 l -h -92.875 98.795 m -92.102 99.029 91.439 99.146 90.889 99.146 c -89.951 99.146 89.187 98.835 88.595 98.212 c -88.003 97.589 87.707 96.781 87.707 95.789 c -87.707 94.824 87.968 94.033 88.489 93.416 c -89.011 92.799 89.678 92.490 90.490 92.490 c -91.260 92.490 91.854 92.764 92.274 93.311 c -92.694 93.857 92.904 94.635 92.904 95.643 c -92.898 96.000 l -88.885 96.000 l -89.053 97.512 89.793 98.268 91.105 98.268 c -91.586 98.268 92.176 98.139 92.875 97.881 c -h -88.938 95.133 m -91.744 95.133 l -91.744 93.949 91.303 93.357 90.420 93.357 c -89.533 93.357 89.039 93.949 88.938 95.133 c -h -98.178 98.191 m -97.486 98.828 96.820 99.146 96.180 99.146 c -95.652 99.146 95.215 98.981 94.867 98.651 c -94.520 98.321 94.346 97.904 94.346 97.400 c -94.346 96.705 94.638 96.171 95.222 95.798 c -95.806 95.425 96.643 95.238 97.732 95.238 c -98.008 95.238 l -98.008 94.471 l -98.008 93.732 97.629 93.363 96.871 93.363 c -96.262 93.363 95.604 93.551 94.896 93.926 c -94.896 92.971 l -95.674 92.654 96.402 92.496 97.082 92.496 c -97.793 92.496 98.317 92.656 98.655 92.977 c -98.993 93.297 99.162 93.795 99.162 94.471 c -99.162 97.354 l -99.162 98.014 99.365 98.344 99.771 98.344 c -99.822 98.344 99.896 98.336 99.994 98.320 c -100.08 98.959 l -99.814 99.084 99.525 99.146 99.209 99.146 c -98.670 99.146 98.326 98.828 98.178 98.191 c -h -98.008 97.564 m -98.008 95.918 l -97.621 95.906 l -96.988 95.906 96.477 96.026 96.086 96.267 c -95.695 96.507 95.500 96.822 95.500 97.213 c -95.500 97.490 95.598 97.725 95.793 97.916 c -95.988 98.107 96.227 98.203 96.508 98.203 c -96.988 98.203 97.488 97.990 98.008 97.564 c -h -103.43 99.146 m -102.85 99.146 102.39 98.979 102.06 98.643 c -101.73 98.307 101.57 97.840 101.57 97.242 c -101.57 93.504 l -100.77 93.504 l -100.77 92.637 l -101.57 92.637 l -101.57 91.482 l -102.72 91.371 l -102.72 92.637 l -104.39 92.637 l -104.39 93.504 l -102.72 93.504 l -102.72 97.031 l -102.72 97.863 103.08 98.279 103.80 98.279 c -103.96 98.279 104.14 98.254 104.36 98.203 c -104.36 99.000 l -104.00 99.098 103.70 99.146 103.43 99.146 c -h -110.68 98.795 m -109.90 99.029 109.24 99.146 108.69 99.146 c -107.75 99.146 106.99 98.835 106.40 98.212 c -105.80 97.589 105.51 96.781 105.51 95.789 c -105.51 94.824 105.77 94.033 106.29 93.416 c -106.81 92.799 107.48 92.490 108.29 92.490 c -109.06 92.490 109.66 92.764 110.08 93.311 c -110.50 93.857 110.71 94.635 110.71 95.643 c -110.70 96.000 l -106.69 96.000 l -106.85 97.512 107.59 98.268 108.91 98.268 c -109.39 98.268 109.98 98.139 110.68 97.881 c -h -106.74 95.133 m -109.54 95.133 l -109.54 93.949 109.10 93.357 108.22 93.357 c -107.33 93.357 106.84 93.949 106.74 95.133 c -h -111.54 101.02 m -111.54 100.15 l -117.54 100.15 l -117.54 101.02 l -h -120.61 99.146 m -120.02 99.146 119.56 98.979 119.24 98.643 c -118.91 98.307 118.74 97.840 118.74 97.242 c -118.74 93.504 l -117.95 93.504 l -117.95 92.637 l -118.74 92.637 l -118.74 91.482 l -119.90 91.371 l -119.90 92.637 l -121.56 92.637 l -121.56 93.504 l -119.90 93.504 l -119.90 97.031 l -119.90 97.863 120.26 98.279 120.98 98.279 c -121.13 98.279 121.31 98.254 121.53 98.203 c -121.53 99.000 l -121.18 99.098 120.87 99.146 120.61 99.146 c -h -127.85 98.795 m -127.08 99.029 126.41 99.146 125.86 99.146 c -124.93 99.146 124.16 98.835 123.57 98.212 c -122.98 97.589 122.68 96.781 122.68 95.789 c -122.68 94.824 122.94 94.033 123.46 93.416 c -123.99 92.799 124.65 92.490 125.46 92.490 c -126.23 92.490 126.83 92.764 127.25 93.311 c -127.67 93.857 127.88 94.635 127.88 95.643 c -127.87 96.000 l -123.86 96.000 l -124.03 97.512 124.77 98.268 126.08 98.268 c -126.56 98.268 127.15 98.139 127.85 97.881 c -h -123.91 95.133 m -126.72 95.133 l -126.72 93.949 126.28 93.357 125.39 93.357 c -124.51 93.357 124.01 93.949 123.91 95.133 c -h -129.87 99.000 m -129.87 92.637 l -131.03 92.637 l -131.03 93.832 l -131.63 92.941 132.38 92.496 133.26 92.496 c -133.81 92.496 134.25 92.671 134.58 93.021 c -134.91 93.370 135.07 93.840 135.07 94.430 c -135.07 99.000 l -133.92 99.000 l -133.92 94.805 l -133.92 94.332 133.85 93.995 133.71 93.794 c -133.57 93.593 133.34 93.492 133.02 93.492 c -132.32 93.492 131.65 93.955 131.03 94.881 c -131.03 99.000 l -h -140.60 98.191 m -139.91 98.828 139.24 99.146 138.60 99.146 c -138.07 99.146 137.64 98.981 137.29 98.651 c -136.94 98.321 136.77 97.904 136.77 97.400 c -136.77 96.705 137.06 96.171 137.64 95.798 c -138.23 95.425 139.06 95.238 140.15 95.238 c -140.43 95.238 l -140.43 94.471 l -140.43 93.732 140.05 93.363 139.29 93.363 c -138.68 93.363 138.03 93.551 137.32 93.926 c -137.32 92.971 l -138.10 92.654 138.82 92.496 139.50 92.496 c -140.21 92.496 140.74 92.656 141.08 92.977 c -141.42 93.297 141.58 93.795 141.58 94.471 c -141.58 97.354 l -141.58 98.014 141.79 98.344 142.19 98.344 c -142.24 98.344 142.32 98.336 142.42 98.320 c -142.50 98.959 l -142.24 99.084 141.95 99.146 141.63 99.146 c -141.09 99.146 140.75 98.828 140.60 98.191 c -h -140.43 97.564 m -140.43 95.918 l -140.04 95.906 l -139.41 95.906 138.90 96.026 138.51 96.267 c -138.12 96.507 137.92 96.822 137.92 97.213 c -137.92 97.490 138.02 97.725 138.21 97.916 c -138.41 98.107 138.65 98.203 138.93 98.203 c -139.41 98.203 139.91 97.990 140.43 97.564 c -h -143.95 99.000 m -143.95 92.637 l -145.10 92.637 l -145.10 93.832 l -145.71 92.941 146.46 92.496 147.34 92.496 c -147.89 92.496 148.33 92.671 148.66 93.021 c -148.98 93.370 149.15 93.840 149.15 94.430 c -149.15 99.000 l -147.99 99.000 l -147.99 94.805 l -147.99 94.332 147.92 93.995 147.79 93.794 c -147.65 93.593 147.42 93.492 147.10 93.492 c -146.39 93.492 145.72 93.955 145.10 94.881 c -145.10 99.000 l -h -153.30 99.146 m -152.72 99.146 152.26 98.979 151.93 98.643 c -151.60 98.307 151.44 97.840 151.44 97.242 c -151.44 93.504 l -150.64 93.504 l -150.64 92.637 l -151.44 92.637 l -151.44 91.482 l -152.59 91.371 l -152.59 92.637 l -154.26 92.637 l -154.26 93.504 l -152.59 93.504 l -152.59 97.031 l -152.59 97.863 152.95 98.279 153.67 98.279 c -153.82 98.279 154.01 98.254 154.23 98.203 c -154.23 99.000 l -153.87 99.098 153.56 99.146 153.30 99.146 c -h -162.96 99.000 m -156.03 95.531 l -162.96 92.062 l -162.96 93.029 l -157.97 95.531 l -162.96 98.027 l -h -167.33 99.146 m -166.74 99.146 166.29 98.979 165.96 98.643 c -165.63 98.307 165.47 97.840 165.47 97.242 c -165.47 93.504 l -164.67 93.504 l -164.67 92.637 l -165.47 92.637 l -165.47 91.482 l -166.62 91.371 l -166.62 92.637 l -168.29 92.637 l -168.29 93.504 l -166.62 93.504 l -166.62 97.031 l -166.62 97.863 166.98 98.279 167.70 98.279 c -167.85 98.279 168.04 98.254 168.26 98.203 c -168.26 99.000 l -167.90 99.098 167.59 99.146 167.33 99.146 c -h -174.57 98.795 m -173.80 99.029 173.14 99.146 172.59 99.146 c -171.65 99.146 170.88 98.835 170.29 98.212 c -169.70 97.589 169.40 96.781 169.40 95.789 c -169.40 94.824 169.67 94.033 170.19 93.416 c -170.71 92.799 171.38 92.490 172.19 92.490 c -172.96 92.490 173.55 92.764 173.97 93.311 c -174.39 93.857 174.60 94.635 174.60 95.643 c -174.60 96.000 l -170.58 96.000 l -170.75 97.512 171.49 98.268 172.80 98.268 c -173.28 98.268 173.87 98.139 174.57 97.881 c -h -170.63 95.133 m -173.44 95.133 l -173.44 93.949 173.00 93.357 172.12 93.357 c -171.23 93.357 170.74 93.949 170.63 95.133 c -h -176.59 99.000 m -176.59 92.637 l -177.75 92.637 l -177.75 93.832 l -178.36 92.941 179.10 92.496 179.99 92.496 c -180.54 92.496 180.98 92.671 181.30 93.021 c -181.63 93.370 181.80 93.840 181.80 94.430 c -181.80 99.000 l -180.64 99.000 l -180.64 94.805 l -180.64 94.332 180.57 93.995 180.43 93.794 c -180.30 93.593 180.07 93.492 179.75 93.492 c -179.04 93.492 178.37 93.955 177.75 94.881 c -177.75 99.000 l -h -187.32 98.191 m -186.63 98.828 185.96 99.146 185.32 99.146 c -184.80 99.146 184.36 98.981 184.01 98.651 c -183.66 98.321 183.49 97.904 183.49 97.400 c -183.49 96.705 183.78 96.171 184.37 95.798 c -184.95 95.425 185.79 95.238 186.88 95.238 c -187.15 95.238 l -187.15 94.471 l -187.15 93.732 186.77 93.363 186.02 93.363 c -185.41 93.363 184.75 93.551 184.04 93.926 c -184.04 92.971 l -184.82 92.654 185.55 92.496 186.23 92.496 c -186.94 92.496 187.46 92.656 187.80 92.977 c -188.14 93.297 188.31 93.795 188.31 94.471 c -188.31 97.354 l -188.31 98.014 188.51 98.344 188.92 98.344 c -188.97 98.344 189.04 98.336 189.14 98.320 c -189.22 98.959 l -188.96 99.084 188.67 99.146 188.35 99.146 c -187.81 99.146 187.47 98.828 187.32 98.191 c -h -187.15 97.564 m -187.15 95.918 l -186.77 95.906 l -186.13 95.906 185.62 96.026 185.23 96.267 c -184.84 96.507 184.64 96.822 184.64 97.213 c -184.64 97.490 184.74 97.725 184.94 97.916 c -185.13 98.107 185.37 98.203 185.65 98.203 c -186.13 98.203 186.63 97.990 187.15 97.564 c -h -190.67 99.000 m -190.67 92.637 l -191.82 92.637 l -191.82 93.832 l -192.43 92.941 193.18 92.496 194.06 92.496 c -194.61 92.496 195.05 92.671 195.38 93.021 c -195.71 93.370 195.87 93.840 195.87 94.430 c -195.87 99.000 l -194.72 99.000 l -194.72 94.805 l -194.72 94.332 194.65 93.995 194.51 93.794 c -194.37 93.593 194.14 93.492 193.82 93.492 c -193.11 93.492 192.45 93.955 191.82 94.881 c -191.82 99.000 l -h -200.03 99.146 m -199.44 99.146 198.98 98.979 198.65 98.643 c -198.33 98.307 198.16 97.840 198.16 97.242 c -198.16 93.504 l -197.37 93.504 l -197.37 92.637 l -198.16 92.637 l -198.16 91.482 l -199.32 91.371 l -199.32 92.637 l -200.98 92.637 l -200.98 93.504 l -199.32 93.504 l -199.32 97.031 l -199.32 97.863 199.68 98.279 200.39 98.279 c -200.55 98.279 200.73 98.254 200.95 98.203 c -200.95 99.000 l -200.60 99.098 200.29 99.146 200.03 99.146 c -h -201.45 101.02 m -201.45 100.15 l -207.45 100.15 l -207.45 101.02 l -h -208.60 99.000 m -208.60 92.637 l -209.76 92.637 l -209.76 99.000 l -h -208.60 91.482 m -208.60 90.328 l -209.76 90.328 l -209.76 91.482 l -h -216.16 99.000 m -216.16 97.805 l -215.69 98.699 214.98 99.146 214.04 99.146 c -213.27 99.146 212.67 98.867 212.23 98.309 c -211.79 97.750 211.57 96.986 211.57 96.018 c -211.57 94.959 211.82 94.107 212.32 93.463 c -212.81 92.818 213.47 92.496 214.28 92.496 c -215.04 92.496 215.66 92.785 216.16 93.363 c -216.16 89.748 l -217.32 89.748 l -217.32 99.000 l -h -216.16 94.154 m -215.56 93.627 214.99 93.363 214.46 93.363 c -213.35 93.363 212.80 94.209 212.80 95.900 c -212.80 97.389 213.29 98.133 214.28 98.133 c -214.92 98.133 215.54 97.783 216.16 97.084 c -h -219.64 100.88 m -219.64 100.45 l -220.02 100.34 220.21 99.898 220.21 99.117 c -220.21 99.000 l -219.64 99.000 l -219.64 97.553 l -221.09 97.553 l -221.09 98.807 l -221.09 100.09 220.61 100.78 219.64 100.88 c -h -231.30 99.000 m -231.30 97.805 l -230.83 98.699 230.13 99.146 229.18 99.146 c -228.42 99.146 227.81 98.867 227.37 98.309 c -226.93 97.750 226.71 96.986 226.71 96.018 c -226.71 94.959 226.96 94.107 227.46 93.463 c -227.96 92.818 228.62 92.496 229.43 92.496 c -230.18 92.496 230.81 92.785 231.30 93.363 c -231.30 89.748 l -232.46 89.748 l -232.46 99.000 l -h -231.30 94.154 m -230.71 93.627 230.14 93.363 229.60 93.363 c -228.50 93.363 227.95 94.209 227.95 95.900 c -227.95 97.389 228.44 98.133 229.42 98.133 c -230.06 98.133 230.69 97.783 231.30 97.084 c -h -239.44 98.795 m -238.66 99.029 238.00 99.146 237.45 99.146 c -236.51 99.146 235.75 98.835 235.16 98.212 c -234.56 97.589 234.27 96.781 234.27 95.789 c -234.27 94.824 234.53 94.033 235.05 93.416 c -235.57 92.799 236.24 92.490 237.05 92.490 c -237.82 92.490 238.42 92.764 238.83 93.311 c -239.25 93.857 239.46 94.635 239.46 95.643 c -239.46 96.000 l -235.45 96.000 l -235.61 97.512 236.35 98.268 237.67 98.268 c -238.15 98.268 238.74 98.139 239.44 97.881 c -h -235.50 95.133 m -238.30 95.133 l -238.30 93.949 237.86 93.357 236.98 93.357 c -236.09 93.357 235.60 93.949 235.50 95.133 c -h -243.14 99.146 m -242.62 99.146 241.98 99.023 241.22 98.777 c -241.22 97.717 l -241.98 98.092 242.63 98.279 243.19 98.279 c -243.52 98.279 243.80 98.189 244.02 98.010 c -244.24 97.830 244.35 97.605 244.35 97.336 c -244.35 96.941 244.04 96.615 243.43 96.357 c -242.75 96.070 l -241.76 95.656 241.26 95.061 241.26 94.283 c -241.26 93.729 241.45 93.292 241.85 92.974 c -242.24 92.655 242.78 92.496 243.46 92.496 c -243.82 92.496 244.26 92.545 244.78 92.643 c -245.02 92.689 l -245.02 93.650 l -244.38 93.459 243.86 93.363 243.48 93.363 c -242.74 93.363 242.37 93.633 242.37 94.172 c -242.37 94.520 242.65 94.812 243.21 95.051 c -243.77 95.285 l -244.40 95.551 244.85 95.831 245.11 96.126 c -245.37 96.421 245.50 96.789 245.50 97.230 c -245.50 97.789 245.28 98.248 244.84 98.607 c -244.40 98.967 243.83 99.146 243.14 99.146 c -h -250.06 99.146 m -249.20 99.146 248.49 98.828 247.92 98.191 c -247.35 97.555 247.07 96.752 247.07 95.783 c -247.07 94.748 247.35 93.941 247.91 93.363 c -248.47 92.785 249.25 92.496 250.26 92.496 c -250.75 92.496 251.31 92.564 251.92 92.701 c -251.92 93.668 l -251.27 93.477 250.74 93.381 250.33 93.381 c -249.74 93.381 249.26 93.603 248.91 94.046 c -248.55 94.489 248.37 95.080 248.37 95.818 c -248.37 96.533 248.55 97.111 248.92 97.553 c -249.29 97.994 249.77 98.215 250.36 98.215 c -250.89 98.215 251.43 98.080 251.99 97.811 c -251.99 98.807 l -251.25 99.033 250.60 99.146 250.06 99.146 c -h -253.74 100.88 m -253.74 100.45 l -254.11 100.34 254.30 99.898 254.30 99.117 c -254.30 99.000 l -253.74 99.000 l -253.74 97.553 l -255.19 97.553 l -255.19 98.807 l -255.19 100.09 254.70 100.78 253.74 100.88 c -h -265.98 98.795 m -265.21 99.029 264.54 99.146 263.99 99.146 c -263.05 99.146 262.29 98.835 261.70 98.212 c -261.11 97.589 260.81 96.781 260.81 95.789 c -260.81 94.824 261.07 94.033 261.59 93.416 c -262.11 92.799 262.78 92.490 263.59 92.490 c -264.36 92.490 264.96 92.764 265.38 93.311 c -265.80 93.857 266.01 94.635 266.01 95.643 c -266.00 96.000 l -261.99 96.000 l -262.16 97.512 262.90 98.268 264.21 98.268 c -264.69 98.268 265.28 98.139 265.98 97.881 c -h -262.04 95.133 m -264.85 95.133 l -264.85 93.949 264.41 93.357 263.52 93.357 c -262.64 93.357 262.14 93.949 262.04 95.133 c -h -268.00 99.000 m -268.00 92.637 l -269.15 92.637 l -269.15 93.832 l -269.76 92.941 270.51 92.496 271.39 92.496 c -271.94 92.496 272.38 92.671 272.71 93.021 c -273.04 93.370 273.20 93.840 273.20 94.430 c -273.20 99.000 l -272.05 99.000 l -272.05 94.805 l -272.05 94.332 271.98 93.995 271.84 93.794 c -271.70 93.593 271.47 93.492 271.15 93.492 c -270.45 93.492 269.78 93.955 269.15 94.881 c -269.15 99.000 l -h -278.73 98.191 m -278.04 98.828 277.37 99.146 276.73 99.146 c -276.20 99.146 275.77 98.981 275.42 98.651 c -275.07 98.321 274.90 97.904 274.90 97.400 c -274.90 96.705 275.19 96.171 275.77 95.798 c -276.36 95.425 277.19 95.238 278.28 95.238 c -278.56 95.238 l -278.56 94.471 l -278.56 93.732 278.18 93.363 277.42 93.363 c -276.81 93.363 276.15 93.551 275.45 93.926 c -275.45 92.971 l -276.22 92.654 276.95 92.496 277.63 92.496 c -278.34 92.496 278.87 92.656 279.21 92.977 c -279.54 93.297 279.71 93.795 279.71 94.471 c -279.71 97.354 l -279.71 98.014 279.92 98.344 280.32 98.344 c -280.37 98.344 280.45 98.336 280.54 98.320 c -280.63 98.959 l -280.37 99.084 280.08 99.146 279.76 99.146 c -279.22 99.146 278.88 98.828 278.73 98.191 c -h -278.56 97.564 m -278.56 95.918 l -278.17 95.906 l -277.54 95.906 277.03 96.026 276.64 96.267 c -276.25 96.507 276.05 96.822 276.05 97.213 c -276.05 97.490 276.15 97.725 276.34 97.916 c -276.54 98.107 276.78 98.203 277.06 98.203 c -277.54 98.203 278.04 97.990 278.56 97.564 c -h -282.07 99.070 m -282.07 89.748 l -283.23 89.748 l -283.23 93.832 l -283.70 92.941 284.41 92.496 285.36 92.496 c -286.12 92.496 286.72 92.775 287.16 93.334 c -287.60 93.893 287.82 94.656 287.82 95.625 c -287.82 96.680 287.57 97.530 287.08 98.177 c -286.58 98.823 285.92 99.146 285.11 99.146 c -284.36 99.146 283.73 98.857 283.23 98.279 c -283.09 99.070 l -h -283.23 97.482 m -283.82 98.014 284.39 98.279 284.93 98.279 c -286.04 98.279 286.59 97.434 286.59 95.742 c -286.59 94.250 286.10 93.504 285.12 93.504 c -284.47 93.504 283.84 93.854 283.23 94.553 c -h -289.63 99.000 m -289.63 89.748 l -290.78 89.748 l -290.78 99.000 l -h -297.76 98.795 m -296.99 99.029 296.32 99.146 295.77 99.146 c -294.84 99.146 294.07 98.835 293.48 98.212 c -292.89 97.589 292.59 96.781 292.59 95.789 c -292.59 94.824 292.85 94.033 293.37 93.416 c -293.90 92.799 294.56 92.490 295.38 92.490 c -296.14 92.490 296.74 92.764 297.16 93.311 c -297.58 93.857 297.79 94.635 297.79 95.643 c -297.78 96.000 l -293.77 96.000 l -293.94 97.512 294.68 98.268 295.99 98.268 c -296.47 98.268 297.06 98.139 297.76 97.881 c -h -293.82 95.133 m -296.63 95.133 l -296.63 93.949 296.19 93.357 295.30 93.357 c -294.42 93.357 293.92 93.949 293.82 95.133 c -h -303.87 99.000 m -303.87 97.805 l -303.40 98.699 302.69 99.146 301.74 99.146 c -300.98 99.146 300.38 98.867 299.94 98.309 c -299.50 97.750 299.28 96.986 299.28 96.018 c -299.28 94.959 299.53 94.107 300.02 93.463 c -300.52 92.818 301.18 92.496 301.99 92.496 c -302.74 92.496 303.37 92.785 303.87 93.363 c -303.87 89.748 l -305.03 89.748 l -305.03 99.000 l -h -303.87 94.154 m -303.27 93.627 302.70 93.363 302.17 93.363 c -301.06 93.363 300.51 94.209 300.51 95.900 c -300.51 97.389 301.00 98.133 301.98 98.133 c -302.62 98.133 303.25 97.783 303.87 97.084 c -h -307.48 99.000 m -314.42 95.531 l -307.48 92.062 l -307.48 93.029 l -312.48 95.531 l -307.48 98.027 l -h -f -70.500 102.50 m -463.50 102.50 l -S -463.00 102.00 m -457.00 96.000 l -457.00 108.00 l -h -f* -382.84 155.15 m -382.31 155.15 381.67 155.02 380.92 154.78 c -380.92 153.72 l -381.67 154.09 382.33 154.28 382.89 154.28 c -383.22 154.28 383.50 154.19 383.71 154.01 c -383.93 153.83 384.04 153.61 384.04 153.34 c -384.04 152.94 383.74 152.62 383.12 152.36 c -382.45 152.07 l -381.45 151.66 380.96 151.06 380.96 150.28 c -380.96 149.73 381.15 149.29 381.54 148.97 c -381.94 148.66 382.47 148.50 383.16 148.50 c -383.51 148.50 383.95 148.54 384.48 148.64 c -384.72 148.69 l -384.72 149.65 l -384.07 149.46 383.56 149.36 383.18 149.36 c -382.44 149.36 382.07 149.63 382.07 150.17 c -382.07 150.52 382.35 150.81 382.91 151.05 c -383.47 151.29 l -384.10 151.55 384.54 151.83 384.80 152.13 c -385.07 152.42 385.20 152.79 385.20 153.23 c -385.20 153.79 384.98 154.25 384.54 154.61 c -384.09 154.97 383.53 155.15 382.84 155.15 c -h -391.25 155.00 m -391.25 153.80 l -390.64 154.70 389.89 155.15 389.02 155.15 c -388.46 155.15 388.02 154.97 387.69 154.62 c -387.37 154.27 387.20 153.80 387.20 153.21 c -387.20 148.64 l -388.36 148.64 l -388.36 152.83 l -388.36 153.31 388.42 153.65 388.56 153.85 c -388.70 154.05 388.93 154.15 389.26 154.15 c -389.96 154.15 390.62 153.69 391.25 152.76 c -391.25 148.64 l -392.40 148.64 l -392.40 155.00 l -h -397.20 155.15 m -396.34 155.15 395.63 154.83 395.06 154.19 c -394.50 153.55 394.21 152.75 394.21 151.78 c -394.21 150.75 394.50 149.94 395.06 149.36 c -395.62 148.79 396.40 148.50 397.40 148.50 c -397.90 148.50 398.45 148.56 399.07 148.70 c -399.07 149.67 l -398.41 149.48 397.88 149.38 397.47 149.38 c -396.88 149.38 396.41 149.60 396.05 150.05 c -395.69 150.49 395.52 151.08 395.52 151.82 c -395.52 152.53 395.70 153.11 396.07 153.55 c -396.43 153.99 396.91 154.21 397.51 154.21 c -398.04 154.21 398.58 154.08 399.14 153.81 c -399.14 154.81 l -398.39 155.03 397.75 155.15 397.20 155.15 c -h -403.35 155.15 m -402.49 155.15 401.78 154.83 401.21 154.19 c -400.64 153.55 400.36 152.75 400.36 151.78 c -400.36 150.75 400.64 149.94 401.20 149.36 c -401.76 148.79 402.54 148.50 403.55 148.50 c -404.04 148.50 404.60 148.56 405.21 148.70 c -405.21 149.67 l -404.56 149.48 404.03 149.38 403.62 149.38 c -403.03 149.38 402.56 149.60 402.20 150.05 c -401.84 150.49 401.66 151.08 401.66 151.82 c -401.66 152.53 401.85 153.11 402.21 153.55 c -402.58 153.99 403.06 154.21 403.65 154.21 c -404.18 154.21 404.72 154.08 405.28 153.81 c -405.28 154.81 l -404.54 155.03 403.89 155.15 403.35 155.15 c -h -411.68 154.79 m -410.90 155.03 410.24 155.15 409.69 155.15 c -408.75 155.15 407.99 154.83 407.40 154.21 c -406.80 153.59 406.51 152.78 406.51 151.79 c -406.51 150.82 406.77 150.03 407.29 149.42 c -407.81 148.80 408.48 148.49 409.29 148.49 c -410.06 148.49 410.66 148.76 411.08 149.31 c -411.50 149.86 411.71 150.63 411.71 151.64 c -411.70 152.00 l -407.69 152.00 l -407.85 153.51 408.59 154.27 409.91 154.27 c -410.39 154.27 410.98 154.14 411.68 153.88 c -h -407.74 151.13 m -410.54 151.13 l -410.54 149.95 410.10 149.36 409.22 149.36 c -408.33 149.36 407.84 149.95 407.74 151.13 c -h -415.38 155.15 m -414.86 155.15 414.22 155.02 413.46 154.78 c -413.46 153.72 l -414.22 154.09 414.87 154.28 415.43 154.28 c -415.76 154.28 416.04 154.19 416.26 154.01 c -416.48 153.83 416.59 153.61 416.59 153.34 c -416.59 152.94 416.28 152.62 415.67 152.36 c -414.99 152.07 l -414.00 151.66 413.50 151.06 413.50 150.28 c -413.50 149.73 413.69 149.29 414.09 148.97 c -414.48 148.66 415.02 148.50 415.70 148.50 c -416.06 148.50 416.50 148.54 417.02 148.64 c -417.26 148.69 l -417.26 149.65 l -416.62 149.46 416.10 149.36 415.72 149.36 c -414.98 149.36 414.61 149.63 414.61 150.17 c -414.61 150.52 414.89 150.81 415.46 151.05 c -416.01 151.29 l -416.64 151.55 417.09 151.83 417.35 152.13 c -417.61 152.42 417.74 152.79 417.74 153.23 c -417.74 153.79 417.52 154.25 417.08 154.61 c -416.64 154.97 416.07 155.15 415.38 155.15 c -h -421.50 155.15 m -420.97 155.15 420.33 155.02 419.58 154.78 c -419.58 153.72 l -420.33 154.09 420.99 154.28 421.55 154.28 c -421.88 154.28 422.16 154.19 422.38 154.01 c -422.59 153.83 422.70 153.61 422.70 153.34 c -422.70 152.94 422.40 152.62 421.78 152.36 c -421.11 152.07 l -420.11 151.66 419.62 151.06 419.62 150.28 c -419.62 149.73 419.81 149.29 420.20 148.97 c -420.60 148.66 421.13 148.50 421.82 148.50 c -422.17 148.50 422.61 148.54 423.14 148.64 c -423.38 148.69 l -423.38 149.65 l -422.73 149.46 422.22 149.36 421.84 149.36 c -421.10 149.36 420.73 149.63 420.73 150.17 c -420.73 150.52 421.01 150.81 421.57 151.05 c -422.13 151.29 l -422.76 151.55 423.20 151.83 423.46 152.13 c -423.73 152.42 423.86 152.79 423.86 153.23 c -423.86 153.79 423.64 154.25 423.20 154.61 c -422.75 154.97 422.19 155.15 421.50 155.15 c -h -425.95 156.88 m -425.95 156.45 l -426.32 156.34 426.51 155.90 426.51 155.12 c -426.51 155.00 l -425.95 155.00 l -425.95 153.55 l -427.40 153.55 l -427.40 154.81 l -427.40 156.09 426.91 156.78 425.95 156.88 c -h -437.50 155.00 m -437.50 153.80 l -436.89 154.70 436.15 155.15 435.27 155.15 c -434.72 155.15 434.28 154.97 433.95 154.62 c -433.62 154.27 433.46 153.80 433.46 153.21 c -433.46 148.64 l -434.61 148.64 l -434.61 152.83 l -434.61 153.31 434.68 153.65 434.82 153.85 c -434.96 154.05 435.19 154.15 435.51 154.15 c -436.21 154.15 436.88 153.69 437.50 152.76 c -437.50 148.64 l -438.66 148.64 l -438.66 155.00 l -h -442.66 155.15 m -442.13 155.15 441.49 155.02 440.74 154.78 c -440.74 153.72 l -441.49 154.09 442.15 154.28 442.71 154.28 c -443.04 154.28 443.31 154.19 443.53 154.01 c -443.75 153.83 443.86 153.61 443.86 153.34 c -443.86 152.94 443.55 152.62 442.94 152.36 c -442.27 152.07 l -441.27 151.66 440.77 151.06 440.77 150.28 c -440.77 149.73 440.97 149.29 441.36 148.97 c -441.75 148.66 442.29 148.50 442.98 148.50 c -443.33 148.50 443.77 148.54 444.29 148.64 c -444.54 148.69 l -444.54 149.65 l -443.89 149.46 443.38 149.36 443.00 149.36 c -442.26 149.36 441.89 149.63 441.89 150.17 c -441.89 150.52 442.17 150.81 442.73 151.05 c -443.29 151.29 l -443.92 151.55 444.36 151.83 444.62 152.13 c -444.88 152.42 445.02 152.79 445.02 153.23 c -445.02 153.79 444.79 154.25 444.35 154.61 c -443.91 154.97 443.35 155.15 442.66 155.15 c -h -451.75 154.79 m -450.98 155.03 450.32 155.15 449.77 155.15 c -448.83 155.15 448.07 154.83 447.47 154.21 c -446.88 153.59 446.59 152.78 446.59 151.79 c -446.59 150.82 446.85 150.03 447.37 149.42 c -447.89 148.80 448.56 148.49 449.37 148.49 c -450.14 148.49 450.73 148.76 451.15 149.31 c -451.57 149.86 451.78 150.63 451.78 151.64 c -451.78 152.00 l -447.76 152.00 l -447.93 153.51 448.67 154.27 449.98 154.27 c -450.46 154.27 451.05 154.14 451.75 153.88 c -h -447.82 151.13 m -450.62 151.13 l -450.62 149.95 450.18 149.36 449.30 149.36 c -448.41 149.36 447.92 149.95 447.82 151.13 c -h -453.78 155.00 m -453.78 148.64 l -454.93 148.64 l -454.93 149.83 l -455.39 148.94 456.05 148.50 456.92 148.50 c -457.04 148.50 457.16 148.51 457.29 148.53 c -457.29 149.60 l -457.09 149.54 456.92 149.50 456.76 149.50 c -456.03 149.50 455.42 149.94 454.93 150.80 c -454.93 155.00 l -h -f -[ 5.0000 5.0000] 0.0000 d -463.50 158.50 m -70.500 158.50 l -S -[] 0.0000 d -70.000 158.00 m -76.000 152.00 l -76.000 164.00 l -h -f* -371.84 117.15 m -371.31 117.15 370.67 117.02 369.92 116.78 c -369.92 115.72 l -370.67 116.09 371.33 116.28 371.89 116.28 c -372.22 116.28 372.50 116.19 372.71 116.01 c -372.93 115.83 373.04 115.61 373.04 115.34 c -373.04 114.94 372.74 114.62 372.12 114.36 c -371.45 114.07 l -370.45 113.66 369.96 113.06 369.96 112.28 c -369.96 111.73 370.15 111.29 370.54 110.97 c -370.94 110.66 371.47 110.50 372.16 110.50 c -372.51 110.50 372.95 110.54 373.48 110.64 c -373.72 110.69 l -373.72 111.65 l -373.07 111.46 372.56 111.36 372.18 111.36 c -371.44 111.36 371.07 111.63 371.07 112.17 c -371.07 112.52 371.35 112.81 371.91 113.05 c -372.47 113.29 l -373.10 113.55 373.54 113.83 373.80 114.13 c -374.07 114.42 374.20 114.79 374.20 115.23 c -374.20 115.79 373.98 116.25 373.54 116.61 c -373.09 116.97 372.53 117.15 371.84 117.15 c -h -380.25 117.00 m -380.25 115.80 l -379.64 116.70 378.89 117.15 378.02 117.15 c -377.46 117.15 377.02 116.97 376.69 116.62 c -376.37 116.27 376.20 115.80 376.20 115.21 c -376.20 110.64 l -377.36 110.64 l -377.36 114.83 l -377.36 115.31 377.42 115.65 377.56 115.85 c -377.70 116.05 377.93 116.15 378.26 116.15 c -378.96 116.15 379.62 115.69 380.25 114.76 c -380.25 110.64 l -381.40 110.64 l -381.40 117.00 l -h -386.20 117.15 m -385.34 117.15 384.63 116.83 384.06 116.19 c -383.50 115.55 383.21 114.75 383.21 113.78 c -383.21 112.75 383.50 111.94 384.06 111.36 c -384.62 110.79 385.40 110.50 386.40 110.50 c -386.90 110.50 387.45 110.56 388.07 110.70 c -388.07 111.67 l -387.41 111.48 386.88 111.38 386.47 111.38 c -385.88 111.38 385.41 111.60 385.05 112.05 c -384.69 112.49 384.52 113.08 384.52 113.82 c -384.52 114.53 384.70 115.11 385.07 115.55 c -385.43 115.99 385.91 116.21 386.51 116.21 c -387.04 116.21 387.58 116.08 388.14 115.81 c -388.14 116.81 l -387.39 117.03 386.75 117.15 386.20 117.15 c -h -392.35 117.15 m -391.49 117.15 390.78 116.83 390.21 116.19 c -389.64 115.55 389.36 114.75 389.36 113.78 c -389.36 112.75 389.64 111.94 390.20 111.36 c -390.76 110.79 391.54 110.50 392.55 110.50 c -393.04 110.50 393.60 110.56 394.21 110.70 c -394.21 111.67 l -393.56 111.48 393.03 111.38 392.62 111.38 c -392.03 111.38 391.56 111.60 391.20 112.05 c -390.84 112.49 390.66 113.08 390.66 113.82 c -390.66 114.53 390.85 115.11 391.21 115.55 c -391.58 115.99 392.06 116.21 392.65 116.21 c -393.18 116.21 393.72 116.08 394.28 115.81 c -394.28 116.81 l -393.54 117.03 392.89 117.15 392.35 117.15 c -h -400.68 116.79 m -399.90 117.03 399.24 117.15 398.69 117.15 c -397.75 117.15 396.99 116.83 396.40 116.21 c -395.80 115.59 395.51 114.78 395.51 113.79 c -395.51 112.82 395.77 112.03 396.29 111.42 c -396.81 110.80 397.48 110.49 398.29 110.49 c -399.06 110.49 399.66 110.76 400.08 111.31 c -400.50 111.86 400.71 112.63 400.71 113.64 c -400.70 114.00 l -396.69 114.00 l -396.85 115.51 397.59 116.27 398.91 116.27 c -399.39 116.27 399.98 116.14 400.68 115.88 c -h -396.74 113.13 m -399.54 113.13 l -399.54 111.95 399.10 111.36 398.22 111.36 c -397.33 111.36 396.84 111.95 396.74 113.13 c -h -404.38 117.15 m -403.86 117.15 403.22 117.02 402.46 116.78 c -402.46 115.72 l -403.22 116.09 403.87 116.28 404.43 116.28 c -404.76 116.28 405.04 116.19 405.26 116.01 c -405.48 115.83 405.59 115.61 405.59 115.34 c -405.59 114.94 405.28 114.62 404.67 114.36 c -403.99 114.07 l -403.00 113.66 402.50 113.06 402.50 112.28 c -402.50 111.73 402.69 111.29 403.09 110.97 c -403.48 110.66 404.02 110.50 404.70 110.50 c -405.06 110.50 405.50 110.54 406.02 110.64 c -406.26 110.69 l -406.26 111.65 l -405.62 111.46 405.10 111.36 404.72 111.36 c -403.98 111.36 403.61 111.63 403.61 112.17 c -403.61 112.52 403.89 112.81 404.46 113.05 c -405.01 113.29 l -405.64 113.55 406.09 113.83 406.35 114.13 c -406.61 114.42 406.74 114.79 406.74 115.23 c -406.74 115.79 406.52 116.25 406.08 116.61 c -405.64 116.97 405.07 117.15 404.38 117.15 c -h -410.50 117.15 m -409.97 117.15 409.33 117.02 408.58 116.78 c -408.58 115.72 l -409.33 116.09 409.99 116.28 410.55 116.28 c -410.88 116.28 411.16 116.19 411.38 116.01 c -411.59 115.83 411.70 115.61 411.70 115.34 c -411.70 114.94 411.40 114.62 410.78 114.36 c -410.11 114.07 l -409.11 113.66 408.62 113.06 408.62 112.28 c -408.62 111.73 408.81 111.29 409.20 110.97 c -409.60 110.66 410.13 110.50 410.82 110.50 c -411.17 110.50 411.61 110.54 412.14 110.64 c -412.38 110.69 l -412.38 111.65 l -411.73 111.46 411.22 111.36 410.84 111.36 c -410.10 111.36 409.73 111.63 409.73 112.17 c -409.73 112.52 410.01 112.81 410.57 113.05 c -411.13 113.29 l -411.76 113.55 412.20 113.83 412.46 114.13 c -412.73 114.42 412.86 114.79 412.86 115.23 c -412.86 115.79 412.64 116.25 412.20 116.61 c -411.75 116.97 411.19 117.15 410.50 117.15 c -h -414.95 118.88 m -414.95 118.45 l -415.32 118.34 415.51 117.90 415.51 117.12 c -415.51 117.00 l -414.95 117.00 l -414.95 115.55 l -416.40 115.55 l -416.40 116.81 l -416.40 118.09 415.91 118.78 414.95 118.88 c -h -424.44 117.15 m -423.85 117.15 423.39 116.98 423.06 116.64 c -422.74 116.31 422.57 115.84 422.57 115.24 c -422.57 111.50 l -421.78 111.50 l -421.78 110.64 l -422.57 110.64 l -422.57 109.48 l -423.73 109.37 l -423.73 110.64 l -425.39 110.64 l -425.39 111.50 l -423.73 111.50 l -423.73 115.03 l -423.73 115.86 424.09 116.28 424.80 116.28 c -424.96 116.28 425.14 116.25 425.36 116.20 c -425.36 117.00 l -425.01 117.10 424.70 117.15 424.44 117.15 c -h -431.68 116.79 m -430.90 117.03 430.24 117.15 429.69 117.15 c -428.75 117.15 427.99 116.83 427.40 116.21 c -426.81 115.59 426.51 114.78 426.51 113.79 c -426.51 112.82 426.77 112.03 427.29 111.42 c -427.81 110.80 428.48 110.49 429.29 110.49 c -430.06 110.49 430.66 110.76 431.08 111.31 c -431.50 111.86 431.71 112.63 431.71 113.64 c -431.70 114.00 l -427.69 114.00 l -427.86 115.51 428.60 116.27 429.91 116.27 c -430.39 116.27 430.98 116.14 431.68 115.88 c -h -427.74 113.13 m -430.55 113.13 l -430.55 111.95 430.11 111.36 429.22 111.36 c -428.34 111.36 427.84 111.95 427.74 113.13 c -h -433.70 117.00 m -433.70 110.64 l -434.85 110.64 l -434.85 111.83 l -435.46 110.94 436.21 110.50 437.09 110.50 c -437.64 110.50 438.08 110.67 438.41 111.02 c -438.74 111.37 438.90 111.84 438.90 112.43 c -438.90 117.00 l -437.75 117.00 l -437.75 112.80 l -437.75 112.33 437.68 112.00 437.54 111.79 c -437.40 111.59 437.17 111.49 436.85 111.49 c -436.14 111.49 435.48 111.96 434.85 112.88 c -434.85 117.00 l -h -444.43 116.19 m -443.74 116.83 443.07 117.15 442.43 117.15 c -441.90 117.15 441.46 116.98 441.12 116.65 c -440.77 116.32 440.60 115.90 440.60 115.40 c -440.60 114.71 440.89 114.17 441.47 113.80 c -442.06 113.42 442.89 113.24 443.98 113.24 c -444.26 113.24 l -444.26 112.47 l -444.26 111.73 443.88 111.36 443.12 111.36 c -442.51 111.36 441.85 111.55 441.15 111.93 c -441.15 110.97 l -441.92 110.65 442.65 110.50 443.33 110.50 c -444.04 110.50 444.57 110.66 444.91 110.98 c -445.24 111.30 445.41 111.79 445.41 112.47 c -445.41 115.35 l -445.41 116.01 445.62 116.34 446.02 116.34 c -446.07 116.34 446.15 116.34 446.24 116.32 c -446.33 116.96 l -446.06 117.08 445.78 117.15 445.46 117.15 c -444.92 117.15 444.58 116.83 444.43 116.19 c -h -444.26 115.56 m -444.26 113.92 l -443.87 113.91 l -443.24 113.91 442.73 114.03 442.34 114.27 c -441.95 114.51 441.75 114.82 441.75 115.21 c -441.75 115.49 441.85 115.72 442.04 115.92 c -442.24 116.11 442.48 116.20 442.76 116.20 c -443.24 116.20 443.74 115.99 444.26 115.56 c -h -447.77 117.00 m -447.77 110.64 l -448.93 110.64 l -448.93 111.83 l -449.54 110.94 450.28 110.50 451.17 110.50 c -451.72 110.50 452.16 110.67 452.48 111.02 c -452.81 111.37 452.98 111.84 452.98 112.43 c -452.98 117.00 l -451.82 117.00 l -451.82 112.80 l -451.82 112.33 451.75 112.00 451.61 111.79 c -451.48 111.59 451.25 111.49 450.93 111.49 c -450.22 111.49 449.55 111.96 448.93 112.88 c -448.93 117.00 l -h -457.13 117.15 m -456.54 117.15 456.09 116.98 455.76 116.64 c -455.43 116.31 455.27 115.84 455.27 115.24 c -455.27 111.50 l -454.47 111.50 l -454.47 110.64 l -455.27 110.64 l -455.27 109.48 l -456.42 109.37 l -456.42 110.64 l -458.09 110.64 l -458.09 111.50 l -456.42 111.50 l -456.42 115.03 l -456.42 115.86 456.78 116.28 457.50 116.28 c -457.65 116.28 457.84 116.25 458.06 116.20 c -458.06 117.00 l -457.70 117.10 457.39 117.15 457.13 117.15 c -h -f -[ 5.0000 5.0000] 0.0000 d -463.50 120.50 m -70.500 120.50 l -S -[] 0.0000 d -70.000 120.00 m -76.000 114.00 l -76.000 126.00 l -h -f* -428.06 239.15 m -427.48 239.15 427.02 238.98 426.69 238.64 c -426.37 238.31 426.20 237.84 426.20 237.24 c -426.20 233.50 l -425.40 233.50 l -425.40 232.64 l -426.20 232.64 l -426.20 231.48 l -427.36 231.37 l -427.36 232.64 l -429.02 232.64 l -429.02 233.50 l -427.36 233.50 l -427.36 237.03 l -427.36 237.86 427.71 238.28 428.43 238.28 c -428.59 238.28 428.77 238.25 428.99 238.20 c -428.99 239.00 l -428.63 239.10 428.33 239.15 428.06 239.15 c -h -433.13 239.15 m -432.22 239.15 431.50 238.84 430.95 238.24 c -430.41 237.64 430.14 236.83 430.14 235.82 c -430.14 234.79 430.41 233.99 430.96 233.39 c -431.50 232.79 432.24 232.50 433.17 232.50 c -434.11 232.50 434.85 232.79 435.39 233.39 c -435.94 233.99 436.21 234.79 436.21 235.81 c -436.21 236.85 435.94 237.66 435.39 238.26 c -434.84 238.85 434.09 239.15 433.13 239.15 c -h -433.15 238.28 m -434.37 238.28 434.98 237.46 434.98 235.81 c -434.98 234.18 434.38 233.36 433.17 233.36 c -431.97 233.36 431.37 234.18 431.37 235.82 c -431.37 237.46 431.96 238.28 433.15 238.28 c -h -438.01 239.00 m -438.01 229.75 l -439.17 229.75 l -439.17 235.72 l -441.86 232.64 l -443.11 232.64 l -440.53 235.61 l -443.64 239.00 l -442.16 239.00 l -439.17 235.74 l -439.17 239.00 l -h -449.69 238.79 m -448.92 239.03 448.26 239.15 447.71 239.15 c -446.77 239.15 446.00 238.83 445.41 238.21 c -444.82 237.59 444.52 236.78 444.52 235.79 c -444.52 234.82 444.78 234.03 445.31 233.42 c -445.83 232.80 446.49 232.49 447.31 232.49 c -448.08 232.49 448.67 232.76 449.09 233.31 c -449.51 233.86 449.72 234.63 449.72 235.64 c -449.71 236.00 l -445.70 236.00 l -445.87 237.51 446.61 238.27 447.92 238.27 c -448.40 238.27 448.99 238.14 449.69 237.88 c -h -445.75 235.13 m -448.56 235.13 l -448.56 233.95 448.12 233.36 447.24 233.36 c -446.35 233.36 445.86 233.95 445.75 235.13 c -h -451.71 239.00 m -451.71 232.64 l -452.87 232.64 l -452.87 233.83 l -453.48 232.94 454.22 232.50 455.11 232.50 c -455.66 232.50 456.10 232.67 456.42 233.02 c -456.75 233.37 456.92 233.84 456.92 234.43 c -456.92 239.00 l -455.76 239.00 l -455.76 234.80 l -455.76 234.33 455.69 234.00 455.55 233.79 c -455.42 233.59 455.19 233.49 454.87 233.49 c -454.16 233.49 453.49 233.96 452.87 234.88 c -452.87 239.00 l -h -f -[ 5.0000 5.0000] 0.0000 d -463.50 242.50 m -70.500 242.50 l -S -[] 0.0000 d -70.000 242.00 m -76.000 236.00 l -76.000 248.00 l -h -f* -486.84 407.15 m -486.31 407.15 485.67 407.02 484.92 406.78 c -484.92 405.72 l -485.67 406.09 486.33 406.28 486.89 406.28 c -487.22 406.28 487.50 406.19 487.71 406.01 c -487.93 405.83 488.04 405.61 488.04 405.34 c -488.04 404.94 487.74 404.62 487.12 404.36 c -486.45 404.07 l -485.45 403.66 484.96 403.06 484.96 402.28 c -484.96 401.73 485.15 401.29 485.54 400.97 c -485.94 400.66 486.47 400.50 487.16 400.50 c -487.51 400.50 487.95 400.54 488.48 400.64 c -488.72 400.69 l -488.72 401.65 l -488.07 401.46 487.56 401.36 487.18 401.36 c -486.44 401.36 486.07 401.63 486.07 402.17 c -486.07 402.52 486.35 402.81 486.91 403.05 c -487.47 403.29 l -488.10 403.55 488.54 403.83 488.80 404.13 c -489.07 404.42 489.20 404.79 489.20 405.23 c -489.20 405.79 488.98 406.25 488.54 406.61 c -488.09 406.97 487.53 407.15 486.84 407.15 c -h -495.25 407.00 m -495.25 405.80 l -494.64 406.70 493.89 407.15 493.02 407.15 c -492.46 407.15 492.02 406.97 491.69 406.62 c -491.37 406.27 491.20 405.80 491.20 405.21 c -491.20 400.64 l -492.36 400.64 l -492.36 404.83 l -492.36 405.31 492.42 405.65 492.56 405.85 c -492.70 406.05 492.93 406.15 493.26 406.15 c -493.96 406.15 494.62 405.69 495.25 404.76 c -495.25 400.64 l -496.40 400.64 l -496.40 407.00 l -h -501.20 407.15 m -500.34 407.15 499.63 406.83 499.06 406.19 c -498.50 405.55 498.21 404.75 498.21 403.78 c -498.21 402.75 498.50 401.94 499.06 401.36 c -499.62 400.79 500.40 400.50 501.40 400.50 c -501.90 400.50 502.45 400.56 503.07 400.70 c -503.07 401.67 l -502.41 401.48 501.88 401.38 501.47 401.38 c -500.88 401.38 500.41 401.60 500.05 402.05 c -499.69 402.49 499.52 403.08 499.52 403.82 c -499.52 404.53 499.70 405.11 500.07 405.55 c -500.43 405.99 500.91 406.21 501.51 406.21 c -502.04 406.21 502.58 406.08 503.14 405.81 c -503.14 406.81 l -502.39 407.03 501.75 407.15 501.20 407.15 c -h -507.35 407.15 m -506.49 407.15 505.78 406.83 505.21 406.19 c -504.64 405.55 504.36 404.75 504.36 403.78 c -504.36 402.75 504.64 401.94 505.20 401.36 c -505.76 400.79 506.54 400.50 507.55 400.50 c -508.04 400.50 508.60 400.56 509.21 400.70 c -509.21 401.67 l -508.56 401.48 508.03 401.38 507.62 401.38 c -507.03 401.38 506.56 401.60 506.20 402.05 c -505.84 402.49 505.66 403.08 505.66 403.82 c -505.66 404.53 505.85 405.11 506.21 405.55 c -506.58 405.99 507.06 406.21 507.65 406.21 c -508.18 406.21 508.72 406.08 509.28 405.81 c -509.28 406.81 l -508.54 407.03 507.89 407.15 507.35 407.15 c -h -515.68 406.79 m -514.90 407.03 514.24 407.15 513.69 407.15 c -512.75 407.15 511.99 406.83 511.40 406.21 c -510.80 405.59 510.51 404.78 510.51 403.79 c -510.51 402.82 510.77 402.03 511.29 401.42 c -511.81 400.80 512.48 400.49 513.29 400.49 c -514.06 400.49 514.66 400.76 515.08 401.31 c -515.50 401.86 515.71 402.63 515.71 403.64 c -515.70 404.00 l -511.69 404.00 l -511.85 405.51 512.59 406.27 513.91 406.27 c -514.39 406.27 514.98 406.14 515.68 405.88 c -h -511.74 403.13 m -514.54 403.13 l -514.54 401.95 514.10 401.36 513.22 401.36 c -512.33 401.36 511.84 401.95 511.74 403.13 c -h -519.38 407.15 m -518.86 407.15 518.22 407.02 517.46 406.78 c -517.46 405.72 l -518.22 406.09 518.87 406.28 519.43 406.28 c -519.76 406.28 520.04 406.19 520.26 406.01 c -520.48 405.83 520.59 405.61 520.59 405.34 c -520.59 404.94 520.28 404.62 519.67 404.36 c -518.99 404.07 l -518.00 403.66 517.50 403.06 517.50 402.28 c -517.50 401.73 517.69 401.29 518.09 400.97 c -518.48 400.66 519.02 400.50 519.70 400.50 c -520.06 400.50 520.50 400.54 521.02 400.64 c -521.26 400.69 l -521.26 401.65 l -520.62 401.46 520.10 401.36 519.72 401.36 c -518.98 401.36 518.61 401.63 518.61 402.17 c -518.61 402.52 518.89 402.81 519.46 403.05 c -520.01 403.29 l -520.64 403.55 521.09 403.83 521.35 404.13 c -521.61 404.42 521.74 404.79 521.74 405.23 c -521.74 405.79 521.52 406.25 521.08 406.61 c -520.64 406.97 520.07 407.15 519.38 407.15 c -h -525.50 407.15 m -524.97 407.15 524.33 407.02 523.58 406.78 c -523.58 405.72 l -524.33 406.09 524.99 406.28 525.55 406.28 c -525.88 406.28 526.16 406.19 526.38 406.01 c -526.59 405.83 526.70 405.61 526.70 405.34 c -526.70 404.94 526.40 404.62 525.78 404.36 c -525.11 404.07 l -524.11 403.66 523.62 403.06 523.62 402.28 c -523.62 401.73 523.81 401.29 524.20 400.97 c -524.60 400.66 525.13 400.50 525.82 400.50 c -526.17 400.50 526.61 400.54 527.14 400.64 c -527.38 400.69 l -527.38 401.65 l -526.73 401.46 526.22 401.36 525.84 401.36 c -525.10 401.36 524.73 401.63 524.73 402.17 c -524.73 402.52 525.01 402.81 525.57 403.05 c -526.13 403.29 l -526.76 403.55 527.20 403.83 527.46 404.13 c -527.73 404.42 527.86 404.79 527.86 405.23 c -527.86 405.79 527.64 406.25 527.20 406.61 c -526.75 406.97 526.19 407.15 525.50 407.15 c -h -529.95 408.88 m -529.95 408.45 l -530.32 408.34 530.51 407.90 530.51 407.12 c -530.51 407.00 l -529.95 407.00 l -529.95 405.55 l -531.40 405.55 l -531.40 406.81 l -531.40 408.09 530.91 408.78 529.95 408.88 c -h -541.61 407.00 m -541.61 405.80 l -541.14 406.70 540.43 407.15 539.49 407.15 c -538.72 407.15 538.12 406.87 537.68 406.31 c -537.24 405.75 537.02 404.99 537.02 404.02 c -537.02 402.96 537.27 402.11 537.77 401.46 c -538.27 400.82 538.92 400.50 539.73 400.50 c -540.49 400.50 541.11 400.79 541.61 401.36 c -541.61 397.75 l -542.77 397.75 l -542.77 407.00 l -h -541.61 402.15 m -541.01 401.63 540.45 401.36 539.91 401.36 c -538.80 401.36 538.25 402.21 538.25 403.90 c -538.25 405.39 538.74 406.13 539.73 406.13 c -540.37 406.13 541.00 405.78 541.61 405.08 c -h -548.36 406.19 m -547.67 406.83 547.00 407.15 546.36 407.15 c -545.83 407.15 545.40 406.98 545.05 406.65 c -544.70 406.32 544.53 405.90 544.53 405.40 c -544.53 404.71 544.82 404.17 545.40 403.80 c -545.99 403.42 546.82 403.24 547.91 403.24 c -548.19 403.24 l -548.19 402.47 l -548.19 401.73 547.81 401.36 547.05 401.36 c -546.44 401.36 545.79 401.55 545.08 401.93 c -545.08 400.97 l -545.86 400.65 546.58 400.50 547.26 400.50 c -547.97 400.50 548.50 400.66 548.84 400.98 c -549.17 401.30 549.34 401.79 549.34 402.47 c -549.34 405.35 l -549.34 406.01 549.55 406.34 549.95 406.34 c -550.00 406.34 550.08 406.34 550.18 406.32 c -550.26 406.96 l -550.00 407.08 549.71 407.15 549.39 407.15 c -548.85 407.15 548.51 406.83 548.36 406.19 c -h -548.19 405.56 m -548.19 403.92 l -547.80 403.91 l -547.17 403.91 546.66 404.03 546.27 404.27 c -545.88 404.51 545.68 404.82 545.68 405.21 c -545.68 405.49 545.78 405.72 545.97 405.92 c -546.17 406.11 546.41 406.20 546.69 406.20 c -547.17 406.20 547.67 405.99 548.19 405.56 c -h -553.62 407.15 m -553.03 407.15 552.57 406.98 552.24 406.64 c -551.92 406.31 551.75 405.84 551.75 405.24 c -551.75 401.50 l -550.96 401.50 l -550.96 400.64 l -551.75 400.64 l -551.75 399.48 l -552.91 399.37 l -552.91 400.64 l -554.57 400.64 l -554.57 401.50 l -552.91 401.50 l -552.91 405.03 l -552.91 405.86 553.27 406.28 553.98 406.28 c -554.14 406.28 554.32 406.25 554.54 406.20 c -554.54 407.00 l -554.19 407.10 553.88 407.15 553.62 407.15 c -h -559.47 406.19 m -558.78 406.83 558.12 407.15 557.48 407.15 c -556.95 407.15 556.51 406.98 556.16 406.65 c -555.82 406.32 555.64 405.90 555.64 405.40 c -555.64 404.71 555.93 404.17 556.52 403.80 c -557.10 403.42 557.94 403.24 559.03 403.24 c -559.30 403.24 l -559.30 402.47 l -559.30 401.73 558.93 401.36 558.17 401.36 c -557.56 401.36 556.90 401.55 556.19 401.93 c -556.19 400.97 l -556.97 400.65 557.70 400.50 558.38 400.50 c -559.09 400.50 559.61 400.66 559.95 400.98 c -560.29 401.30 560.46 401.79 560.46 402.47 c -560.46 405.35 l -560.46 406.01 560.66 406.34 561.07 406.34 c -561.12 406.34 561.19 406.34 561.29 406.32 c -561.37 406.96 l -561.11 407.08 560.82 407.15 560.51 407.15 c -559.97 407.15 559.62 406.83 559.47 406.19 c -h -559.30 405.56 m -559.30 403.92 l -558.92 403.91 l -558.29 403.91 557.77 404.03 557.38 404.27 c -556.99 404.51 556.80 404.82 556.80 405.21 c -556.80 405.49 556.89 405.72 557.09 405.92 c -557.29 406.11 557.52 406.20 557.80 406.20 c -558.29 406.20 558.79 405.99 559.30 405.56 c -h -561.67 409.02 m -561.67 408.15 l -567.67 408.15 l -567.67 409.02 l -h -570.73 407.15 m -570.14 407.15 569.69 406.98 569.36 406.64 c -569.03 406.31 568.87 405.84 568.87 405.24 c -568.87 401.50 l -568.07 401.50 l -568.07 400.64 l -568.87 400.64 l -568.87 399.48 l -570.02 399.37 l -570.02 400.64 l -571.69 400.64 l -571.69 401.50 l -570.02 401.50 l -570.02 405.03 l -570.02 405.86 570.38 406.28 571.10 406.28 c -571.25 406.28 571.44 406.25 571.66 406.20 c -571.66 407.00 l -571.30 407.10 570.99 407.15 570.73 407.15 c -h -575.80 407.15 m -574.89 407.15 574.16 406.84 573.62 406.24 c -573.08 405.64 572.80 404.83 572.80 403.82 c -572.80 402.79 573.08 401.99 573.62 401.39 c -574.17 400.79 574.91 400.50 575.84 400.50 c -576.77 400.50 577.51 400.79 578.06 401.39 c -578.60 401.99 578.88 402.79 578.88 403.81 c -578.88 404.85 578.60 405.66 578.05 406.26 c -577.51 406.85 576.76 407.15 575.80 407.15 c -h -575.82 406.28 m -577.04 406.28 577.65 405.46 577.65 403.81 c -577.65 402.18 577.05 401.36 575.84 401.36 c -574.64 401.36 574.04 402.18 574.04 403.82 c -574.04 405.46 574.63 406.28 575.82 406.28 c -h -579.53 409.02 m -579.53 408.15 l -585.53 408.15 l -585.53 409.02 l -h -591.34 406.79 m -590.57 407.03 589.91 407.15 589.36 407.15 c -588.42 407.15 587.66 406.83 587.06 406.21 c -586.47 405.59 586.18 404.78 586.18 403.79 c -586.18 402.82 586.44 402.03 586.96 401.42 c -587.48 400.80 588.15 400.49 588.96 400.49 c -589.73 400.49 590.32 400.76 590.74 401.31 c -591.16 401.86 591.37 402.63 591.37 403.64 c -591.37 404.00 l -587.35 404.00 l -587.52 405.51 588.26 406.27 589.57 406.27 c -590.05 406.27 590.64 406.14 591.34 405.88 c -h -587.41 403.13 m -590.21 403.13 l -590.21 401.95 589.77 401.36 588.89 401.36 c -588.00 401.36 587.51 401.95 587.41 403.13 c -h -595.85 407.15 m -594.99 407.15 594.28 406.83 593.71 406.19 c -593.14 405.55 592.86 404.75 592.86 403.78 c -592.86 402.75 593.14 401.94 593.70 401.36 c -594.26 400.79 595.04 400.50 596.05 400.50 c -596.54 400.50 597.10 400.56 597.71 400.70 c -597.71 401.67 l -597.06 401.48 596.53 401.38 596.12 401.38 c -595.53 401.38 595.06 401.60 594.70 402.05 c -594.34 402.49 594.16 403.08 594.16 403.82 c -594.16 404.53 594.35 405.11 594.71 405.55 c -595.08 405.99 595.56 406.21 596.15 406.21 c -596.68 406.21 597.22 406.08 597.78 405.81 c -597.78 406.81 l -597.04 407.03 596.39 407.15 595.85 407.15 c -h -599.51 407.00 m -599.51 397.75 l -600.67 397.75 l -600.67 401.83 l -601.28 400.94 602.02 400.50 602.90 400.50 c -603.46 400.50 603.89 400.67 604.22 401.02 c -604.55 401.37 604.71 401.84 604.71 402.43 c -604.71 407.00 l -603.56 407.00 l -603.56 402.80 l -603.56 402.33 603.49 402.00 603.35 401.79 c -603.21 401.59 602.98 401.49 602.66 401.49 c -601.96 401.49 601.29 401.96 600.67 402.88 c -600.67 407.00 l -h -609.45 407.15 m -608.54 407.15 607.81 406.84 607.27 406.24 c -606.73 405.64 606.46 404.83 606.46 403.82 c -606.46 402.79 606.73 401.99 607.27 401.39 c -607.82 400.79 608.56 400.50 609.49 400.50 c -610.42 400.50 611.16 400.79 611.71 401.39 c -612.25 401.99 612.53 402.79 612.53 403.81 c -612.53 404.85 612.25 405.66 611.71 406.26 c -611.16 406.85 610.41 407.15 609.45 407.15 c -h -609.47 406.28 m -610.69 406.28 611.30 405.46 611.30 403.81 c -611.30 402.18 610.70 401.36 609.49 401.36 c -608.29 401.36 607.69 402.18 607.69 403.82 c -607.69 405.46 608.28 406.28 609.47 406.28 c -h -f -[ 5.0000 5.0000] 0.0000 d -618.50 410.50 m -70.500 410.50 l -S -[] 0.0000 d -70.000 410.00 m -76.000 404.00 l -76.000 416.00 l -h -f* -482.13 371.00 m -482.13 369.80 l -481.52 370.70 480.78 371.15 479.90 371.15 c -479.35 371.15 478.90 370.97 478.58 370.62 c -478.25 370.27 478.08 369.80 478.08 369.21 c -478.08 364.64 l -479.24 364.64 l -479.24 368.83 l -479.24 369.31 479.31 369.65 479.45 369.85 c -479.58 370.05 479.82 370.15 480.14 370.15 c -480.84 370.15 481.51 369.69 482.13 368.76 c -482.13 364.64 l -483.29 364.64 l -483.29 371.00 l -h -487.29 371.15 m -486.76 371.15 486.12 371.02 485.37 370.78 c -485.37 369.72 l -486.12 370.09 486.78 370.28 487.34 370.28 c -487.67 370.28 487.94 370.19 488.16 370.01 c -488.38 369.83 488.49 369.61 488.49 369.34 c -488.49 368.94 488.18 368.62 487.57 368.36 c -486.90 368.07 l -485.90 367.66 485.40 367.06 485.40 366.28 c -485.40 365.73 485.60 365.29 485.99 364.97 c -486.38 364.66 486.92 364.50 487.61 364.50 c -487.96 364.50 488.40 364.54 488.92 364.64 c -489.16 364.69 l -489.16 365.65 l -488.52 365.46 488.01 365.36 487.63 365.36 c -486.89 365.36 486.52 365.63 486.52 366.17 c -486.52 366.52 486.80 366.81 487.36 367.05 c -487.92 367.29 l -488.54 367.55 488.99 367.83 489.25 368.13 c -489.51 368.42 489.64 368.79 489.64 369.23 c -489.64 369.79 489.42 370.25 488.98 370.61 c -488.54 370.97 487.98 371.15 487.29 371.15 c -h -496.38 370.79 m -495.61 371.03 494.95 371.15 494.40 371.15 c -493.46 371.15 492.69 370.83 492.10 370.21 c -491.51 369.59 491.21 368.78 491.21 367.79 c -491.21 366.82 491.48 366.03 492.00 365.42 c -492.52 364.80 493.19 364.49 494.00 364.49 c -494.77 364.49 495.36 364.76 495.78 365.31 c -496.20 365.86 496.41 366.63 496.41 367.64 c -496.41 368.00 l -492.39 368.00 l -492.56 369.51 493.30 370.27 494.61 370.27 c -495.09 370.27 495.68 370.14 496.38 369.88 c -h -492.45 367.13 m -495.25 367.13 l -495.25 365.95 494.81 365.36 493.93 365.36 c -493.04 365.36 492.55 365.95 492.45 367.13 c -h -498.40 371.00 m -498.40 364.64 l -499.56 364.64 l -499.56 365.83 l -500.02 364.94 500.68 364.50 501.55 364.50 c -501.67 364.50 501.79 364.51 501.92 364.53 c -501.92 365.60 l -501.72 365.54 501.54 365.50 501.39 365.50 c -500.66 365.50 500.05 365.94 499.56 366.80 c -499.56 371.00 l -h -503.33 372.88 m -503.33 372.45 l -503.71 372.34 503.89 371.90 503.89 371.12 c -503.89 371.00 l -503.33 371.00 l -503.33 369.55 l -504.78 369.55 l -504.78 370.81 l -504.78 372.09 504.30 372.78 503.33 372.88 c -h -512.82 371.15 m -512.23 371.15 511.78 370.98 511.45 370.64 c -511.12 370.31 510.96 369.84 510.96 369.24 c -510.96 365.50 l -510.16 365.50 l -510.16 364.64 l -510.96 364.64 l -510.96 363.48 l -512.11 363.37 l -512.11 364.64 l -513.77 364.64 l -513.77 365.50 l -512.11 365.50 l -512.11 369.03 l -512.11 369.86 512.47 370.28 513.19 370.28 c -513.34 370.28 513.53 370.25 513.74 370.20 c -513.74 371.00 l -513.39 371.10 513.08 371.15 512.82 371.15 c -h -517.89 371.15 m -516.98 371.15 516.25 370.84 515.71 370.24 c -515.16 369.64 514.89 368.83 514.89 367.82 c -514.89 366.79 515.17 365.99 515.71 365.39 c -516.25 364.79 516.99 364.50 517.93 364.50 c -518.86 364.50 519.60 364.79 520.15 365.39 c -520.69 365.99 520.96 366.79 520.96 367.81 c -520.96 368.85 520.69 369.66 520.14 370.26 c -519.60 370.85 518.84 371.15 517.89 371.15 c -h -517.90 370.28 m -519.13 370.28 519.74 369.46 519.74 367.81 c -519.74 366.18 519.13 365.36 517.93 365.36 c -516.72 365.36 516.12 366.18 516.12 367.82 c -516.12 369.46 516.72 370.28 517.90 370.28 c -h -522.77 371.00 m -522.77 361.75 l -523.92 361.75 l -523.92 367.72 l -526.62 364.64 l -527.86 364.64 l -525.29 367.61 l -528.39 371.00 l -526.92 371.00 l -523.92 367.74 l -523.92 371.00 l -h -534.45 370.79 m -533.67 371.03 533.01 371.15 532.46 371.15 c -531.52 371.15 530.76 370.83 530.17 370.21 c -529.57 369.59 529.28 368.78 529.28 367.79 c -529.28 366.82 529.54 366.03 530.06 365.42 c -530.58 364.80 531.25 364.49 532.06 364.49 c -532.83 364.49 533.42 364.76 533.84 365.31 c -534.26 365.86 534.47 366.63 534.47 367.64 c -534.47 368.00 l -530.46 368.00 l -530.62 369.51 531.36 370.27 532.68 370.27 c -533.16 370.27 533.75 370.14 534.45 369.88 c -h -530.51 367.13 m -533.31 367.13 l -533.31 365.95 532.87 365.36 531.99 365.36 c -531.10 365.36 530.61 365.95 530.51 367.13 c -h -536.47 371.00 m -536.47 364.64 l -537.62 364.64 l -537.62 365.83 l -538.23 364.94 538.98 364.50 539.86 364.50 c -540.41 364.50 540.85 364.67 541.18 365.02 c -541.51 365.37 541.67 365.84 541.67 366.43 c -541.67 371.00 l -540.52 371.00 l -540.52 366.80 l -540.52 366.33 540.45 366.00 540.31 365.79 c -540.17 365.59 539.94 365.49 539.62 365.49 c -538.91 365.49 538.25 365.96 537.62 366.88 c -537.62 371.00 l -h -542.76 373.02 m -542.76 372.15 l -548.76 372.15 l -548.76 373.02 l -h -554.00 371.00 m -554.00 369.80 l -553.53 370.70 552.82 371.15 551.88 371.15 c -551.11 371.15 550.51 370.87 550.07 370.31 c -549.63 369.75 549.41 368.99 549.41 368.02 c -549.41 366.96 549.66 366.11 550.16 365.46 c -550.66 364.82 551.31 364.50 552.12 364.50 c -552.88 364.50 553.50 364.79 554.00 365.36 c -554.00 361.75 l -555.16 361.75 l -555.16 371.00 l -h -554.00 366.15 m -553.40 365.63 552.83 365.36 552.30 365.36 c -551.19 365.36 550.64 366.21 550.64 367.90 c -550.64 369.39 551.13 370.13 552.12 370.13 c -552.76 370.13 553.38 369.78 554.00 369.08 c -h -560.75 370.19 m -560.06 370.83 559.39 371.15 558.75 371.15 c -558.22 371.15 557.79 370.98 557.44 370.65 c -557.09 370.32 556.92 369.90 556.92 369.40 c -556.92 368.71 557.21 368.17 557.79 367.80 c -558.38 367.42 559.21 367.24 560.30 367.24 c -560.58 367.24 l -560.58 366.47 l -560.58 365.73 560.20 365.36 559.44 365.36 c -558.83 365.36 558.17 365.55 557.47 365.93 c -557.47 364.97 l -558.24 364.65 558.97 364.50 559.65 364.50 c -560.36 364.50 560.89 364.66 561.23 364.98 c -561.56 365.30 561.73 365.79 561.73 366.47 c -561.73 369.35 l -561.73 370.01 561.94 370.34 562.34 370.34 c -562.39 370.34 562.47 370.34 562.56 370.32 c -562.65 370.96 l -562.38 371.08 562.10 371.15 561.78 371.15 c -561.24 371.15 560.90 370.83 560.75 370.19 c -h -560.58 369.56 m -560.58 367.92 l -560.19 367.91 l -559.56 367.91 559.05 368.03 558.66 368.27 c -558.27 368.51 558.07 368.82 558.07 369.21 c -558.07 369.49 558.17 369.72 558.36 369.92 c -558.56 370.11 558.80 370.20 559.08 370.20 c -559.56 370.20 560.06 369.99 560.58 369.56 c -h -566.00 371.15 m -565.42 371.15 564.96 370.98 564.63 370.64 c -564.30 370.31 564.14 369.84 564.14 369.24 c -564.14 365.50 l -563.34 365.50 l -563.34 364.64 l -564.14 364.64 l -564.14 363.48 l -565.29 363.37 l -565.29 364.64 l -566.96 364.64 l -566.96 365.50 l -565.29 365.50 l -565.29 369.03 l -565.29 369.86 565.65 370.28 566.37 370.28 c -566.53 370.28 566.71 370.25 566.93 370.20 c -566.93 371.00 l -566.57 371.10 566.27 371.15 566.00 371.15 c -h -571.86 370.19 m -571.17 370.83 570.51 371.15 569.87 371.15 c -569.34 371.15 568.90 370.98 568.55 370.65 c -568.21 370.32 568.03 369.90 568.03 369.40 c -568.03 368.71 568.32 368.17 568.91 367.80 c -569.49 367.42 570.33 367.24 571.42 367.24 c -571.69 367.24 l -571.69 366.47 l -571.69 365.73 571.31 365.36 570.56 365.36 c -569.95 365.36 569.29 365.55 568.58 365.93 c -568.58 364.97 l -569.36 364.65 570.09 364.50 570.77 364.50 c -571.48 364.50 572.00 364.66 572.34 364.98 c -572.68 365.30 572.85 365.79 572.85 366.47 c -572.85 369.35 l -572.85 370.01 573.05 370.34 573.46 370.34 c -573.51 370.34 573.58 370.34 573.68 370.32 c -573.76 370.96 l -573.50 371.08 573.21 371.15 572.89 371.15 c -572.36 371.15 572.01 370.83 571.86 370.19 c -h -571.69 369.56 m -571.69 367.92 l -571.31 367.91 l -570.67 367.91 570.16 368.03 569.77 368.27 c -569.38 368.51 569.19 368.82 569.19 369.21 c -569.19 369.49 569.28 369.72 569.48 369.92 c -569.67 370.11 569.91 370.20 570.19 370.20 c -570.67 370.20 571.17 369.99 571.69 369.56 c -h -f -[ 5.0000 5.0000] 0.0000 d -471.50 374.50 m -618.50 374.50 l -S -[] 0.0000 d -618.00 374.00 m -612.00 368.00 l -612.00 380.00 l -h -f* -517.47 353.00 m -515.11 346.64 l -516.26 346.64 l -518.11 351.59 l -520.06 346.64 l -521.14 346.64 l -518.63 353.00 l -h -525.65 352.19 m -524.96 352.83 524.29 353.15 523.65 353.15 c -523.12 353.15 522.68 352.98 522.34 352.65 c -521.99 352.32 521.81 351.90 521.81 351.40 c -521.81 350.71 522.11 350.17 522.69 349.80 c -523.27 349.42 524.11 349.24 525.20 349.24 c -525.48 349.24 l -525.48 348.47 l -525.48 347.73 525.10 347.36 524.34 347.36 c -523.73 347.36 523.07 347.55 522.37 347.93 c -522.37 346.97 l -523.14 346.65 523.87 346.50 524.55 346.50 c -525.26 346.50 525.79 346.66 526.12 346.98 c -526.46 347.30 526.63 347.79 526.63 348.47 c -526.63 351.35 l -526.63 352.01 526.83 352.34 527.24 352.34 c -527.29 352.34 527.37 352.34 527.46 352.32 c -527.54 352.96 l -527.28 353.08 526.99 353.15 526.68 353.15 c -526.14 353.15 525.79 352.83 525.65 352.19 c -h -525.48 351.56 m -525.48 349.92 l -525.09 349.91 l -524.46 349.91 523.95 350.03 523.55 350.27 c -523.16 350.51 522.97 350.82 522.97 351.21 c -522.97 351.49 523.07 351.72 523.26 351.92 c -523.46 352.11 523.70 352.20 523.98 352.20 c -524.46 352.20 524.96 351.99 525.48 351.56 c -h -528.99 353.00 m -528.99 343.75 l -530.15 343.75 l -530.15 353.00 l -h -532.46 353.00 m -532.46 346.64 l -533.62 346.64 l -533.62 353.00 l -h -532.46 345.48 m -532.46 344.33 l -533.62 344.33 l -533.62 345.48 l -h -540.01 353.00 m -540.01 351.80 l -539.54 352.70 538.84 353.15 537.89 353.15 c -537.13 353.15 536.52 352.87 536.08 352.31 c -535.65 351.75 535.43 350.99 535.43 350.02 c -535.43 348.96 535.67 348.11 536.17 347.46 c -536.67 346.82 537.33 346.50 538.14 346.50 c -538.89 346.50 539.52 346.79 540.01 347.36 c -540.01 343.75 l -541.17 343.75 l -541.17 353.00 l -h -540.01 348.15 m -539.42 347.63 538.85 347.36 538.31 347.36 c -537.21 347.36 536.66 348.21 536.66 349.90 c -536.66 351.39 537.15 352.13 538.13 352.13 c -538.77 352.13 539.40 351.78 540.01 351.08 c -h -546.76 352.19 m -546.07 352.83 545.41 353.15 544.77 353.15 c -544.24 353.15 543.80 352.98 543.45 352.65 c -543.11 352.32 542.93 351.90 542.93 351.40 c -542.93 350.71 543.22 350.17 543.81 349.80 c -544.39 349.42 545.23 349.24 546.32 349.24 c -546.59 349.24 l -546.59 348.47 l -546.59 347.73 546.21 347.36 545.46 347.36 c -544.85 347.36 544.19 347.55 543.48 347.93 c -543.48 346.97 l -544.26 346.65 544.99 346.50 545.67 346.50 c -546.38 346.50 546.90 346.66 547.24 346.98 c -547.58 347.30 547.75 347.79 547.75 348.47 c -547.75 351.35 l -547.75 352.01 547.95 352.34 548.36 352.34 c -548.41 352.34 548.48 352.34 548.58 352.32 c -548.66 352.96 l -548.40 353.08 548.11 353.15 547.79 353.15 c -547.26 353.15 546.91 352.83 546.76 352.19 c -h -546.59 351.56 m -546.59 349.92 l -546.21 349.91 l -545.57 349.91 545.06 350.03 544.67 350.27 c -544.28 350.51 544.09 350.82 544.09 351.21 c -544.09 351.49 544.18 351.72 544.38 351.92 c -544.57 352.11 544.81 352.20 545.09 352.20 c -545.57 352.20 546.07 351.99 546.59 351.56 c -h -552.02 353.15 m -551.43 353.15 550.98 352.98 550.65 352.64 c -550.32 352.31 550.16 351.84 550.16 351.24 c -550.16 347.50 l -549.36 347.50 l -549.36 346.64 l -550.16 346.64 l -550.16 345.48 l -551.31 345.37 l -551.31 346.64 l -552.97 346.64 l -552.97 347.50 l -551.31 347.50 l -551.31 351.03 l -551.31 351.86 551.67 352.28 552.39 352.28 c -552.54 352.28 552.73 352.25 552.95 352.20 c -552.95 353.00 l -552.59 353.10 552.28 353.15 552.02 353.15 c -h -559.26 352.79 m -558.49 353.03 557.83 353.15 557.28 353.15 c -556.34 353.15 555.57 352.83 554.98 352.21 c -554.39 351.59 554.09 350.78 554.09 349.79 c -554.09 348.82 554.35 348.03 554.88 347.42 c -555.40 346.80 556.06 346.49 556.88 346.49 c -557.65 346.49 558.24 346.76 558.66 347.31 c -559.08 347.86 559.29 348.63 559.29 349.64 c -559.29 350.00 l -555.27 350.00 l -555.44 351.51 556.18 352.27 557.49 352.27 c -557.97 352.27 558.56 352.14 559.26 351.88 c -h -555.32 349.13 m -558.13 349.13 l -558.13 347.95 557.69 347.36 556.81 347.36 c -555.92 347.36 555.43 347.95 555.32 349.13 c -h -568.37 353.00 m -561.43 349.53 l -568.37 346.06 l -568.37 347.03 l -563.37 349.53 l -568.37 352.03 l -h -572.73 353.15 m -572.15 353.15 571.69 352.98 571.36 352.64 c -571.03 352.31 570.87 351.84 570.87 351.24 c -570.87 347.50 l -570.07 347.50 l -570.07 346.64 l -570.87 346.64 l -570.87 345.48 l -572.02 345.37 l -572.02 346.64 l -573.69 346.64 l -573.69 347.50 l -572.02 347.50 l -572.02 351.03 l -572.02 351.86 572.38 352.28 573.10 352.28 c -573.25 352.28 573.44 352.25 573.66 352.20 c -573.66 353.00 l -573.30 353.10 572.99 353.15 572.73 353.15 c -h -577.80 353.15 m -576.89 353.15 576.16 352.84 575.62 352.24 c -575.08 351.64 574.81 350.83 574.81 349.82 c -574.81 348.79 575.08 347.99 575.62 347.39 c -576.17 346.79 576.91 346.50 577.84 346.50 c -578.78 346.50 579.51 346.79 580.06 347.39 c -580.60 347.99 580.88 348.79 580.88 349.81 c -580.88 350.85 580.60 351.66 580.06 352.26 c -579.51 352.85 578.76 353.15 577.80 353.15 c -h -577.82 352.28 m -579.04 352.28 579.65 351.46 579.65 349.81 c -579.65 348.18 579.05 347.36 577.84 347.36 c -576.64 347.36 576.04 348.18 576.04 349.82 c -576.04 351.46 576.63 352.28 577.82 352.28 c -h -582.68 353.00 m -582.68 343.75 l -583.84 343.75 l -583.84 349.72 l -586.53 346.64 l -587.77 346.64 l -585.20 349.61 l -588.31 353.00 l -586.83 353.00 l -583.84 349.74 l -583.84 353.00 l -h -594.36 352.79 m -593.59 353.03 592.92 353.15 592.37 353.15 c -591.44 353.15 590.67 352.83 590.08 352.21 c -589.49 351.59 589.19 350.78 589.19 349.79 c -589.19 348.82 589.45 348.03 589.97 347.42 c -590.50 346.80 591.16 346.49 591.97 346.49 c -592.74 346.49 593.34 346.76 593.76 347.31 c -594.18 347.86 594.39 348.63 594.39 349.64 c -594.38 350.00 l -590.37 350.00 l -590.54 351.51 591.28 352.27 592.59 352.27 c -593.07 352.27 593.66 352.14 594.36 351.88 c -h -590.42 349.13 m -593.23 349.13 l -593.23 347.95 592.79 347.36 591.90 347.36 c -591.02 347.36 590.52 347.95 590.42 349.13 c -h -596.38 353.00 m -596.38 346.64 l -597.54 346.64 l -597.54 347.83 l -598.14 346.94 598.89 346.50 599.77 346.50 c -600.32 346.50 600.76 346.67 601.09 347.02 c -601.42 347.37 601.58 347.84 601.58 348.43 c -601.58 353.00 l -600.43 353.00 l -600.43 348.80 l -600.43 348.33 600.36 348.00 600.22 347.79 c -600.08 347.59 599.85 347.49 599.53 347.49 c -598.83 347.49 598.16 347.96 597.54 348.88 c -597.54 353.00 l -h -603.97 353.00 m -610.91 349.53 l -603.97 346.06 l -603.97 347.03 l -608.97 349.53 l -603.97 352.03 l -h -f -618.50 356.50 m -471.50 356.50 l -S -471.00 356.00 m -477.00 350.00 l -477.00 362.00 l -h -f* -17.000 7.0000 m -121.00 7.0000 l -121.00 44.000 l -17.000 44.000 l -17.000 7.0000 l -h -f -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -15.000 5.0000 m -117.00 5.0000 l -117.00 40.000 l -15.000 40.000 l -15.000 5.0000 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -15.500 5.5000 m -117.50 5.5000 l -117.50 40.500 l -15.500 40.500 l -15.500 5.5000 l -h -S -29.500 24.500 m -103.50 24.500 l -S -32.639 22.146 m -31.779 22.146 31.066 21.828 30.500 21.191 c -29.934 20.555 29.650 19.752 29.650 18.783 c -29.650 17.748 29.931 16.941 30.491 16.363 c -31.052 15.785 31.834 15.496 32.838 15.496 c -33.334 15.496 33.889 15.564 34.502 15.701 c -34.502 16.668 l -33.850 16.477 33.318 16.381 32.908 16.381 c -32.318 16.381 31.845 16.603 31.487 17.046 c -31.130 17.489 30.951 18.080 30.951 18.818 c -30.951 19.533 31.135 20.111 31.502 20.553 c -31.869 20.994 32.350 21.215 32.943 21.215 c -33.471 21.215 34.014 21.080 34.572 20.811 c -34.572 21.807 l -33.826 22.033 33.182 22.146 32.639 22.146 c -h -36.301 22.000 m -36.301 12.748 l -37.455 12.748 l -37.455 22.000 l -h -39.770 22.000 m -39.770 15.637 l -40.924 15.637 l -40.924 22.000 l -h -39.770 14.482 m -39.770 13.328 l -40.924 13.328 l -40.924 14.482 l -h -47.902 21.795 m -47.129 22.029 46.467 22.146 45.916 22.146 c -44.979 22.146 44.214 21.835 43.622 21.212 c -43.030 20.589 42.734 19.781 42.734 18.789 c -42.734 17.824 42.995 17.033 43.517 16.416 c -44.038 15.799 44.705 15.490 45.518 15.490 c -46.287 15.490 46.882 15.764 47.302 16.311 c -47.722 16.857 47.932 17.635 47.932 18.643 c -47.926 19.000 l -43.912 19.000 l -44.080 20.512 44.820 21.268 46.133 21.268 c -46.613 21.268 47.203 21.139 47.902 20.881 c -h -43.965 18.133 m -46.771 18.133 l -46.771 16.949 46.330 16.357 45.447 16.357 c -44.561 16.357 44.066 16.949 43.965 18.133 c -h -49.924 22.000 m -49.924 15.637 l -51.078 15.637 l -51.078 16.832 l -51.688 15.941 52.434 15.496 53.316 15.496 c -53.867 15.496 54.307 15.671 54.635 16.021 c -54.963 16.370 55.127 16.840 55.127 17.430 c -55.127 22.000 l -53.973 22.000 l -53.973 17.805 l -53.973 17.332 53.903 16.995 53.765 16.794 c -53.626 16.593 53.396 16.492 53.076 16.492 c -52.369 16.492 51.703 16.955 51.078 17.881 c -51.078 22.000 l -h -59.281 22.146 m -58.695 22.146 58.238 21.979 57.910 21.643 c -57.582 21.307 57.418 20.840 57.418 20.242 c -57.418 16.504 l -56.621 16.504 l -56.621 15.637 l -57.418 15.637 l -57.418 14.482 l -58.572 14.371 l -58.572 15.637 l -60.236 15.637 l -60.236 16.504 l -58.572 16.504 l -58.572 20.031 l -58.572 20.863 58.932 21.279 59.650 21.279 c -59.803 21.279 59.988 21.254 60.207 21.203 c -60.207 22.000 l -59.852 22.098 59.543 22.146 59.281 22.146 c -h -62.023 22.000 m -62.023 20.846 l -63.178 20.846 l -63.178 22.000 l -h -62.023 16.797 m -62.023 15.637 l -63.178 15.637 l -63.178 16.797 l -h -65.621 22.000 m -65.621 13.328 l -66.852 13.328 l -66.852 21.080 l -70.754 21.080 l -70.754 22.000 l -h -72.055 22.000 m -72.055 15.637 l -73.209 15.637 l -73.209 22.000 l -h -72.055 14.482 m -72.055 13.328 l -73.209 13.328 l -73.209 14.482 l -h -75.523 22.070 m -75.523 12.748 l -76.678 12.748 l -76.678 16.832 l -77.150 15.941 77.859 15.496 78.805 15.496 c -79.570 15.496 80.173 15.775 80.612 16.334 c -81.052 16.893 81.271 17.656 81.271 18.625 c -81.271 19.680 81.022 20.530 80.524 21.177 c -80.026 21.823 79.371 22.146 78.559 22.146 c -77.805 22.146 77.178 21.857 76.678 21.279 c -76.537 22.070 l -h -76.678 20.482 m -77.271 21.014 77.838 21.279 78.377 21.279 c -79.486 21.279 80.041 20.434 80.041 18.742 c -80.041 17.250 79.549 16.504 78.564 16.504 c -77.920 16.504 77.291 16.854 76.678 17.553 c -h -83.076 22.000 m -83.076 15.637 l -84.230 15.637 l -84.230 16.832 l -84.688 15.941 85.352 15.496 86.223 15.496 c -86.340 15.496 86.463 15.506 86.592 15.525 c -86.592 16.604 l -86.393 16.537 86.217 16.504 86.064 16.504 c -85.334 16.504 84.723 16.938 84.230 17.805 c -84.230 22.000 l -h -91.268 21.191 m -90.576 21.828 89.910 22.146 89.270 22.146 c -88.742 22.146 88.305 21.981 87.957 21.651 c -87.609 21.321 87.436 20.904 87.436 20.400 c -87.436 19.705 87.728 19.171 88.312 18.798 c -88.896 18.425 89.732 18.238 90.822 18.238 c -91.098 18.238 l -91.098 17.471 l -91.098 16.732 90.719 16.363 89.961 16.363 c -89.352 16.363 88.693 16.551 87.986 16.926 c -87.986 15.971 l -88.764 15.654 89.492 15.496 90.172 15.496 c -90.883 15.496 91.407 15.656 91.745 15.977 c -92.083 16.297 92.252 16.795 92.252 17.471 c -92.252 20.354 l -92.252 21.014 92.455 21.344 92.861 21.344 c -92.912 21.344 92.986 21.336 93.084 21.320 c -93.166 21.959 l -92.904 22.084 92.615 22.146 92.299 22.146 c -91.760 22.146 91.416 21.828 91.268 21.191 c -h -91.098 20.564 m -91.098 18.918 l -90.711 18.906 l -90.078 18.906 89.566 19.026 89.176 19.267 c -88.785 19.507 88.590 19.822 88.590 20.213 c -88.590 20.490 88.688 20.725 88.883 20.916 c -89.078 21.107 89.316 21.203 89.598 21.203 c -90.078 21.203 90.578 20.990 91.098 20.564 c -h -94.613 22.000 m -94.613 15.637 l -95.768 15.637 l -95.768 16.832 l -96.225 15.941 96.889 15.496 97.760 15.496 c -97.877 15.496 98.000 15.506 98.129 15.525 c -98.129 16.604 l -97.930 16.537 97.754 16.504 97.602 16.504 c -96.871 16.504 96.260 16.938 95.768 17.805 c -95.768 22.000 l -h -99.893 24.314 m -100.92 22.000 l -98.463 15.637 l -99.711 15.637 l -101.53 20.430 l -103.48 15.637 l -104.57 15.637 l -101.09 24.314 l -h -f -416.00 7.0000 m -525.00 7.0000 l -525.00 44.000 l -416.00 44.000 l -416.00 7.0000 l -h -f -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -414.00 5.0000 m -521.00 5.0000 l -521.00 40.000 l -414.00 40.000 l -414.00 5.0000 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -414.50 5.5000 m -521.50 5.5000 l -521.50 40.500 l -414.50 40.500 l -414.50 5.5000 l -h -S -420.50 24.500 m -515.50 24.500 l -S -421.15 22.000 m -421.15 12.748 l -422.31 12.748 l -422.31 18.725 l -425.00 15.637 l -426.25 15.637 l -423.67 18.607 l -426.78 22.000 l -425.30 22.000 l -422.31 18.736 l -422.31 22.000 l -h -432.83 21.795 m -432.06 22.029 431.40 22.146 430.85 22.146 c -429.91 22.146 429.14 21.835 428.55 21.212 c -427.96 20.589 427.66 19.781 427.66 18.789 c -427.66 17.824 427.92 17.033 428.45 16.416 c -428.97 15.799 429.63 15.490 430.45 15.490 c -431.22 15.490 431.81 15.764 432.23 16.311 c -432.65 16.857 432.86 17.635 432.86 18.643 c -432.86 19.000 l -428.84 19.000 l -429.01 20.512 429.75 21.268 431.06 21.268 c -431.54 21.268 432.13 21.139 432.83 20.881 c -h -428.89 18.133 m -431.70 18.133 l -431.70 16.949 431.26 16.357 430.38 16.357 c -429.49 16.357 429.00 16.949 428.89 18.133 c -h -435.22 24.314 m -436.25 22.000 l -433.79 15.637 l -435.04 15.637 l -436.86 20.430 l -438.81 15.637 l -439.90 15.637 l -436.42 24.314 l -h -442.81 22.146 m -442.28 22.146 441.64 22.023 440.89 21.777 c -440.89 20.717 l -441.64 21.092 442.30 21.279 442.86 21.279 c -443.19 21.279 443.46 21.189 443.68 21.010 c -443.90 20.830 444.01 20.605 444.01 20.336 c -444.01 19.941 443.71 19.615 443.09 19.357 c -442.42 19.070 l -441.42 18.656 440.92 18.061 440.92 17.283 c -440.92 16.729 441.12 16.292 441.51 15.974 c -441.91 15.655 442.44 15.496 443.13 15.496 c -443.48 15.496 443.92 15.545 444.45 15.643 c -444.69 15.689 l -444.69 16.650 l -444.04 16.459 443.53 16.363 443.15 16.363 c -442.41 16.363 442.04 16.633 442.04 17.172 c -442.04 17.520 442.32 17.812 442.88 18.051 c -443.44 18.285 l -444.07 18.551 444.51 18.831 444.77 19.126 c -445.04 19.421 445.17 19.789 445.17 20.230 c -445.17 20.789 444.95 21.248 444.50 21.607 c -444.06 21.967 443.50 22.146 442.81 22.146 c -h -449.15 22.146 m -448.56 22.146 448.11 21.979 447.78 21.643 c -447.45 21.307 447.29 20.840 447.29 20.242 c -447.29 16.504 l -446.49 16.504 l -446.49 15.637 l -447.29 15.637 l -447.29 14.482 l -448.44 14.371 l -448.44 15.637 l -450.11 15.637 l -450.11 16.504 l -448.44 16.504 l -448.44 20.031 l -448.44 20.863 448.80 21.279 449.52 21.279 c -449.67 21.279 449.86 21.254 450.08 21.203 c -450.08 22.000 l -449.72 22.098 449.41 22.146 449.15 22.146 c -h -454.22 22.146 m -453.31 22.146 452.58 21.845 452.04 21.241 c -451.50 20.638 451.22 19.830 451.22 18.818 c -451.22 17.795 451.50 16.985 452.04 16.390 c -452.59 15.794 453.33 15.496 454.26 15.496 c -455.19 15.496 455.93 15.794 456.48 16.390 c -457.02 16.985 457.29 17.791 457.29 18.807 c -457.29 19.846 457.02 20.662 456.47 21.256 c -455.93 21.850 455.18 22.146 454.22 22.146 c -h -454.24 21.279 m -455.46 21.279 456.07 20.455 456.07 18.807 c -456.07 17.178 455.47 16.363 454.26 16.363 c -453.06 16.363 452.46 17.182 452.46 18.818 c -452.46 20.459 453.05 21.279 454.24 21.279 c -h -459.10 22.000 m -459.10 15.637 l -460.25 15.637 l -460.25 16.832 l -460.86 15.941 461.61 15.496 462.49 15.496 c -463.04 15.496 463.48 15.671 463.81 16.021 c -464.14 16.370 464.30 16.840 464.30 17.430 c -464.30 22.000 l -463.15 22.000 l -463.15 17.805 l -463.15 17.332 463.08 16.995 462.94 16.794 c -462.80 16.593 462.57 16.492 462.25 16.492 c -461.54 16.492 460.88 16.955 460.25 17.881 c -460.25 22.000 l -h -471.21 21.795 m -470.44 22.029 469.78 22.146 469.22 22.146 c -468.29 22.146 467.52 21.835 466.93 21.212 c -466.34 20.589 466.04 19.781 466.04 18.789 c -466.04 17.824 466.30 17.033 466.83 16.416 c -467.35 15.799 468.01 15.490 468.83 15.490 c -469.60 15.490 470.19 15.764 470.61 16.311 c -471.03 16.857 471.24 17.635 471.24 18.643 c -471.23 19.000 l -467.22 19.000 l -467.39 20.512 468.13 21.268 469.44 21.268 c -469.92 21.268 470.51 21.139 471.21 20.881 c -h -467.27 18.133 m -470.08 18.133 l -470.08 16.949 469.64 16.357 468.76 16.357 c -467.87 16.357 467.38 16.949 467.27 18.133 c -h -473.40 22.000 m -473.40 20.846 l -474.55 20.846 l -474.55 22.000 l -h -473.40 16.797 m -473.40 15.637 l -474.55 15.637 l -474.55 16.797 l -h -478.69 22.217 m -478.11 22.217 477.37 22.090 476.46 21.836 c -476.46 20.617 l -477.44 21.070 478.24 21.297 478.87 21.297 c -479.35 21.297 479.74 21.170 480.04 20.916 c -480.33 20.662 480.48 20.328 480.48 19.914 c -480.48 19.574 480.38 19.285 480.19 19.047 c -480.00 18.809 479.64 18.543 479.12 18.250 c -478.52 17.904 l -477.79 17.482 477.26 17.085 476.96 16.712 c -476.66 16.339 476.51 15.904 476.51 15.408 c -476.51 14.740 476.75 14.190 477.23 13.759 c -477.72 13.327 478.34 13.111 479.09 13.111 c -479.75 13.111 480.46 13.223 481.20 13.445 c -481.20 14.570 l -480.29 14.211 479.61 14.031 479.16 14.031 c -478.73 14.031 478.38 14.145 478.10 14.371 c -477.82 14.598 477.69 14.883 477.69 15.227 c -477.69 15.516 477.79 15.771 477.99 15.994 c -478.19 16.217 478.56 16.482 479.10 16.791 c -479.72 17.143 l -480.47 17.568 481.00 17.971 481.29 18.350 c -481.59 18.729 481.74 19.184 481.74 19.715 c -481.74 20.469 481.46 21.074 480.91 21.531 c -480.35 21.988 479.61 22.217 478.69 22.217 c -h -488.16 21.795 m -487.38 22.029 486.72 22.146 486.17 22.146 c -485.23 22.146 484.47 21.835 483.88 21.212 c -483.28 20.589 482.99 19.781 482.99 18.789 c -482.99 17.824 483.25 17.033 483.77 16.416 c -484.29 15.799 484.96 15.490 485.77 15.490 c -486.54 15.490 487.14 15.764 487.56 16.311 c -487.98 16.857 488.19 17.635 488.19 18.643 c -488.18 19.000 l -484.17 19.000 l -484.33 20.512 485.07 21.268 486.39 21.268 c -486.87 21.268 487.46 21.139 488.16 20.881 c -h -484.22 18.133 m -487.03 18.133 l -487.03 16.949 486.58 16.357 485.70 16.357 c -484.81 16.357 484.32 16.949 484.22 18.133 c -h -490.18 22.000 m -490.18 15.637 l -491.33 15.637 l -491.33 16.832 l -491.79 15.941 492.45 15.496 493.32 15.496 c -493.44 15.496 493.56 15.506 493.69 15.525 c -493.69 16.604 l -493.49 16.537 493.32 16.504 493.17 16.504 c -492.44 16.504 491.82 16.938 491.33 17.805 c -491.33 22.000 l -h -496.41 22.000 m -494.04 15.637 l -495.19 15.637 l -497.04 20.588 l -499.00 15.637 l -500.07 15.637 l -497.56 22.000 l -h -501.30 22.000 m -501.30 15.637 l -502.45 15.637 l -502.45 22.000 l -h -501.30 14.482 m -501.30 13.328 l -502.45 13.328 l -502.45 14.482 l -h -507.25 22.146 m -506.39 22.146 505.68 21.828 505.11 21.191 c -504.55 20.555 504.26 19.752 504.26 18.783 c -504.26 17.748 504.54 16.941 505.10 16.363 c -505.67 15.785 506.45 15.496 507.45 15.496 c -507.95 15.496 508.50 15.564 509.12 15.701 c -509.12 16.668 l -508.46 16.477 507.93 16.381 507.52 16.381 c -506.93 16.381 506.46 16.603 506.10 17.046 c -505.74 17.489 505.56 18.080 505.56 18.818 c -505.56 19.533 505.75 20.111 506.12 20.553 c -506.48 20.994 506.96 21.215 507.56 21.215 c -508.08 21.215 508.63 21.080 509.19 20.811 c -509.19 21.807 l -508.44 22.033 507.79 22.146 507.25 22.146 c -h -515.58 21.795 m -514.80 22.029 514.14 22.146 513.59 22.146 c -512.65 22.146 511.89 21.835 511.30 21.212 c -510.71 20.589 510.41 19.781 510.41 18.789 c -510.41 17.824 510.67 17.033 511.19 16.416 c -511.71 15.799 512.38 15.490 513.19 15.490 c -513.96 15.490 514.56 15.764 514.98 16.311 c -515.40 16.857 515.61 17.635 515.61 18.643 c -515.60 19.000 l -511.59 19.000 l -511.76 20.512 512.50 21.268 513.81 21.268 c -514.29 21.268 514.88 21.139 515.58 20.881 c -h -511.64 18.133 m -514.45 18.133 l -514.45 16.949 514.01 16.357 513.12 16.357 c -512.24 16.357 511.74 16.949 511.64 18.133 c -h -f -573.00 7.0000 m -677.00 7.0000 l -677.00 44.000 l -573.00 44.000 l -573.00 7.0000 l -h -f -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -571.00 5.0000 m -673.00 5.0000 l -673.00 40.000 l -571.00 40.000 l -571.00 5.0000 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -571.50 5.5000 m -673.50 5.5000 l -673.50 40.500 l -571.50 40.500 l -571.50 5.5000 l -h -S -587.50 24.500 m -658.50 24.500 l -S -592.82 21.795 m -592.04 22.029 591.38 22.146 590.83 22.146 c -589.89 22.146 589.13 21.835 588.54 21.212 c -587.95 20.589 587.65 19.781 587.65 18.789 c -587.65 17.824 587.91 17.033 588.43 16.416 c -588.95 15.799 589.62 15.490 590.43 15.490 c -591.20 15.490 591.80 15.764 592.22 16.311 c -592.64 16.857 592.85 17.635 592.85 18.643 c -592.84 19.000 l -588.83 19.000 l -589.00 20.512 589.74 21.268 591.05 21.268 c -591.53 21.268 592.12 21.139 592.82 20.881 c -h -588.88 18.133 m -591.69 18.133 l -591.69 16.949 591.25 16.357 590.36 16.357 c -589.48 16.357 588.98 16.949 588.88 18.133 c -h -597.32 22.146 m -596.46 22.146 595.75 21.828 595.19 21.191 c -594.62 20.555 594.34 19.752 594.34 18.783 c -594.34 17.748 594.62 16.941 595.18 16.363 c -595.74 15.785 596.52 15.496 597.52 15.496 c -598.02 15.496 598.57 15.564 599.19 15.701 c -599.19 16.668 l -598.54 16.477 598.00 16.381 597.59 16.381 c -597.00 16.381 596.53 16.603 596.17 17.046 c -595.82 17.489 595.64 18.080 595.64 18.818 c -595.64 19.533 595.82 20.111 596.19 20.553 c -596.55 20.994 597.04 21.215 597.63 21.215 c -598.16 21.215 598.70 21.080 599.26 20.811 c -599.26 21.807 l -598.51 22.033 597.87 22.146 597.32 22.146 c -h -600.99 22.000 m -600.99 12.748 l -602.14 12.748 l -602.14 16.832 l -602.75 15.941 603.50 15.496 604.38 15.496 c -604.93 15.496 605.37 15.671 605.70 16.021 c -606.03 16.370 606.19 16.840 606.19 17.430 c -606.19 22.000 l -605.04 22.000 l -605.04 17.805 l -605.04 17.332 604.97 16.995 604.83 16.794 c -604.69 16.593 604.46 16.492 604.14 16.492 c -603.43 16.492 602.77 16.955 602.14 17.881 c -602.14 22.000 l -h -610.92 22.146 m -610.01 22.146 609.29 21.845 608.74 21.241 c -608.20 20.638 607.93 19.830 607.93 18.818 c -607.93 17.795 608.20 16.985 608.75 16.390 c -609.29 15.794 610.03 15.496 610.96 15.496 c -611.90 15.496 612.64 15.794 613.18 16.390 c -613.73 16.985 614.00 17.791 614.00 18.807 c -614.00 19.846 613.73 20.662 613.18 21.256 c -612.63 21.850 611.88 22.146 610.92 22.146 c -h -610.94 21.279 m -612.16 21.279 612.78 20.455 612.78 18.807 c -612.78 17.178 612.17 16.363 610.96 16.363 c -609.76 16.363 609.16 17.182 609.16 18.818 c -609.16 20.459 609.75 21.279 610.94 21.279 c -h -615.97 22.000 m -615.97 20.846 l -617.12 20.846 l -617.12 22.000 l -h -615.97 16.797 m -615.97 15.637 l -617.12 15.637 l -617.12 16.797 l -h -621.27 22.217 m -620.68 22.217 619.94 22.090 619.03 21.836 c -619.03 20.617 l -620.01 21.070 620.81 21.297 621.44 21.297 c -621.93 21.297 622.32 21.170 622.61 20.916 c -622.91 20.662 623.05 20.328 623.05 19.914 c -623.05 19.574 622.96 19.285 622.76 19.047 c -622.57 18.809 622.21 18.543 621.69 18.250 c -621.10 17.904 l -620.36 17.482 619.84 17.085 619.53 16.712 c -619.23 16.339 619.08 15.904 619.08 15.408 c -619.08 14.740 619.32 14.190 619.81 13.759 c -620.29 13.327 620.91 13.111 621.66 13.111 c -622.33 13.111 623.03 13.223 623.77 13.445 c -623.77 14.570 l -622.86 14.211 622.18 14.031 621.73 14.031 c -621.30 14.031 620.95 14.145 620.67 14.371 c -620.40 14.598 620.26 14.883 620.26 15.227 c -620.26 15.516 620.36 15.771 620.56 15.994 c -620.77 16.217 621.14 16.482 621.68 16.791 c -622.30 17.143 l -623.05 17.568 623.57 17.971 623.87 18.350 c -624.16 18.729 624.31 19.184 624.31 19.715 c -624.31 20.469 624.03 21.074 623.48 21.531 c -622.92 21.988 622.18 22.217 621.27 22.217 c -h -630.73 21.795 m -629.96 22.029 629.29 22.146 628.74 22.146 c -627.80 22.146 627.04 21.835 626.45 21.212 c -625.86 20.589 625.56 19.781 625.56 18.789 c -625.56 17.824 625.82 17.033 626.34 16.416 c -626.86 15.799 627.53 15.490 628.34 15.490 c -629.11 15.490 629.71 15.764 630.13 16.311 c -630.55 16.857 630.76 17.635 630.76 18.643 c -630.75 19.000 l -626.74 19.000 l -626.91 20.512 627.65 21.268 628.96 21.268 c -629.44 21.268 630.03 21.139 630.73 20.881 c -h -626.79 18.133 m -629.60 18.133 l -629.60 16.949 629.16 16.357 628.27 16.357 c -627.39 16.357 626.89 16.949 626.79 18.133 c -h -632.75 22.000 m -632.75 15.637 l -633.90 15.637 l -633.90 16.832 l -634.36 15.941 635.03 15.496 635.90 15.496 c -636.01 15.496 636.14 15.506 636.27 15.525 c -636.27 16.604 l -636.07 16.537 635.89 16.504 635.74 16.504 c -635.01 16.504 634.40 16.938 633.90 17.805 c -633.90 22.000 l -h -638.98 22.000 m -636.61 15.637 l -637.77 15.637 l -639.62 20.588 l -641.57 15.637 l -642.65 15.637 l -640.13 22.000 l -h -643.87 22.000 m -643.87 15.637 l -645.03 15.637 l -645.03 22.000 l -h -643.87 14.482 m -643.87 13.328 l -645.03 13.328 l -645.03 14.482 l -h -649.82 22.146 m -648.96 22.146 648.25 21.828 647.69 21.191 c -647.12 20.555 646.84 19.752 646.84 18.783 c -646.84 17.748 647.12 16.941 647.68 16.363 c -648.24 15.785 649.02 15.496 650.02 15.496 c -650.52 15.496 651.07 15.564 651.69 15.701 c -651.69 16.668 l -651.04 16.477 650.50 16.381 650.09 16.381 c -649.50 16.381 649.03 16.603 648.67 17.046 c -648.32 17.489 648.14 18.080 648.14 18.818 c -648.14 19.533 648.32 20.111 648.69 20.553 c -649.05 20.994 649.54 21.215 650.13 21.215 c -650.66 21.215 651.20 21.080 651.76 20.811 c -651.76 21.807 l -651.01 22.033 650.37 22.146 649.82 22.146 c -h -658.15 21.795 m -657.38 22.029 656.71 22.146 656.16 22.146 c -655.23 22.146 654.46 21.835 653.87 21.212 c -653.28 20.589 652.98 19.781 652.98 18.789 c -652.98 17.824 653.24 17.033 653.76 16.416 c -654.29 15.799 654.95 15.490 655.77 15.490 c -656.54 15.490 657.13 15.764 657.55 16.311 c -657.97 16.857 658.18 17.635 658.18 18.643 c -658.17 19.000 l -654.16 19.000 l -654.33 20.512 655.07 21.268 656.38 21.268 c -656.86 21.268 657.45 21.139 658.15 20.881 c -h -654.21 18.133 m -657.02 18.133 l -657.02 16.949 656.58 16.357 655.70 16.357 c -654.81 16.357 654.31 16.949 654.21 18.133 c -h -f -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -60.000 62.000 m -110.00 62.000 l -110.00 79.000 l -60.000 79.000 l -60.000 62.000 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -2.0000 w -60.500 62.500 m -489.50 62.500 l -489.50 168.50 l -60.500 168.50 l -60.500 62.500 l -h -S -60.500 79.500 m -109.50 79.500 l -S -109.50 79.500 m -111.50 75.500 l -S -111.50 75.500 m -111.50 62.500 l -S -63.990 75.778 m -63.990 74.400 l -64.921 74.790 65.717 74.984 66.377 74.984 c -67.147 74.984 67.532 74.722 67.532 74.197 c -67.532 73.859 67.215 73.562 66.580 73.309 c -65.945 73.055 l -65.256 72.775 64.763 72.475 64.466 72.153 c -64.170 71.832 64.022 71.434 64.022 70.960 c -64.022 70.300 64.274 69.787 64.777 69.421 c -65.281 69.055 65.986 68.872 66.891 68.872 c -67.458 68.872 68.133 68.954 68.916 69.119 c -68.916 70.439 l -68.163 70.177 67.539 70.046 67.043 70.046 c -66.265 70.046 65.875 70.287 65.875 70.770 c -65.875 71.087 66.163 71.356 66.739 71.576 c -67.285 71.785 l -68.101 72.094 68.670 72.407 68.989 72.725 c -69.309 73.042 69.468 73.450 69.468 73.950 c -69.468 74.606 69.196 75.138 68.653 75.546 c -68.109 75.955 67.401 76.159 66.529 76.159 c -65.691 76.159 64.845 76.032 63.990 75.778 c -h -71.525 76.000 m -71.525 69.030 l -73.404 69.030 l -73.404 76.000 l -h -71.525 67.856 m -71.525 66.288 l -73.404 66.288 l -73.404 67.856 l -h -75.803 78.165 m -75.956 76.800 l -76.730 77.155 77.468 77.333 78.171 77.333 c -78.865 77.333 79.366 77.185 79.675 76.889 c -79.984 76.592 80.139 76.112 80.139 75.448 c -80.139 74.496 l -79.682 75.499 78.926 76.000 77.873 76.000 c -77.043 76.000 76.385 75.690 75.898 75.070 c -75.412 74.450 75.168 73.611 75.168 72.553 c -75.168 71.440 75.441 70.548 75.987 69.878 c -76.533 69.207 77.259 68.872 78.165 68.872 c -78.875 68.872 79.534 69.159 80.139 69.735 c -80.335 69.030 l -82.024 69.030 l -82.024 74.350 l -82.024 75.412 81.959 76.178 81.830 76.647 c -81.701 77.117 81.451 77.517 81.078 77.847 c -80.452 78.389 79.569 78.660 78.431 78.660 c -77.623 78.660 76.747 78.495 75.803 78.165 c -h -80.139 73.404 m -80.139 70.833 l -79.686 70.321 79.212 70.065 78.717 70.065 c -78.243 70.065 77.866 70.281 77.587 70.712 c -77.308 71.144 77.168 71.724 77.168 72.452 c -77.168 73.814 77.606 74.496 78.482 74.496 c -79.087 74.496 79.639 74.132 80.139 73.404 c -h -84.328 76.000 m -84.328 69.030 l -86.207 69.030 l -86.207 70.344 l -86.821 69.362 87.612 68.872 88.581 68.872 c -89.203 68.872 89.694 69.068 90.054 69.462 c -90.413 69.855 90.593 70.393 90.593 71.074 c -90.593 76.000 l -88.714 76.000 l -88.714 71.538 l -88.714 70.746 88.452 70.351 87.927 70.351 c -87.331 70.351 86.757 70.772 86.207 71.614 c -86.207 76.000 l -h -97.176 76.000 m -97.176 74.686 l -96.566 75.668 95.775 76.159 94.802 76.159 c -94.180 76.159 93.689 75.962 93.329 75.568 c -92.969 75.175 92.790 74.637 92.790 73.956 c -92.790 69.030 l -94.668 69.030 l -94.668 73.493 l -94.668 74.284 94.933 74.680 95.462 74.680 c -96.054 74.680 96.626 74.259 97.176 73.417 c -97.176 69.030 l -99.055 69.030 l -99.055 76.000 l -h -101.40 78.507 m -101.40 69.030 l -103.28 69.030 l -103.28 70.344 l -103.76 69.362 104.52 68.872 105.56 68.872 c -106.40 68.872 107.06 69.178 107.54 69.792 c -108.02 70.406 108.26 71.250 108.26 72.325 c -108.26 73.493 107.99 74.424 107.45 75.118 c -106.90 75.812 106.18 76.159 105.27 76.159 c -104.54 76.159 103.87 75.871 103.28 75.295 c -103.28 78.507 l -h -103.28 74.178 m -103.74 74.686 104.23 74.940 104.74 74.940 c -105.20 74.940 105.57 74.717 105.85 74.270 c -106.12 73.824 106.26 73.228 106.26 72.483 c -106.26 71.057 105.80 70.344 104.88 70.344 c -104.33 70.344 103.79 70.702 103.28 71.417 c -h -f -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -60.000 184.00 m -142.00 184.00 l -142.00 201.00 l -60.000 201.00 l -60.000 184.00 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -60.500 184.50 m -489.50 184.50 l -489.50 252.50 l -60.500 252.50 l -60.500 184.50 l -h -S -60.500 201.50 m -141.50 201.50 l -S -141.50 201.50 m -143.50 197.50 l -S -143.50 197.50 m -143.50 184.50 l -S -67.589 197.25 m -66.963 197.86 66.292 198.16 65.577 198.16 c -64.968 198.16 64.473 197.97 64.092 197.60 c -63.711 197.23 63.521 196.75 63.521 196.15 c -63.521 195.38 63.828 194.79 64.444 194.37 c -65.060 193.96 65.941 193.75 67.088 193.75 c -67.589 193.75 l -67.589 193.11 l -67.589 192.39 67.177 192.03 66.352 192.03 c -65.619 192.03 64.879 192.23 64.130 192.65 c -64.130 191.35 l -64.980 191.03 65.823 190.87 66.656 190.87 c -68.480 190.87 69.392 191.60 69.392 193.05 c -69.392 196.13 l -69.392 196.68 69.568 196.95 69.919 196.95 c -69.982 196.95 70.065 196.94 70.167 196.93 c -70.211 197.98 l -69.813 198.10 69.462 198.16 69.157 198.16 c -68.387 198.16 67.892 197.86 67.672 197.25 c -h -67.589 196.24 m -67.589 194.83 l -67.145 194.83 l -65.931 194.83 65.323 195.21 65.323 195.97 c -65.323 196.23 65.411 196.44 65.587 196.62 c -65.762 196.80 65.979 196.88 66.237 196.88 c -66.677 196.88 67.128 196.67 67.589 196.24 c -h -76.127 198.00 m -76.127 196.69 l -75.518 197.67 74.726 198.16 73.753 198.16 c -73.131 198.16 72.640 197.96 72.280 197.57 c -71.921 197.17 71.741 196.64 71.741 195.96 c -71.741 191.03 l -73.620 191.03 l -73.620 195.49 l -73.620 196.28 73.884 196.68 74.413 196.68 c -75.006 196.68 75.577 196.26 76.127 195.42 c -76.127 191.03 l -78.006 191.03 l -78.006 198.00 l -h -83.947 197.96 m -83.499 198.09 83.145 198.16 82.887 198.16 c -81.258 198.16 80.443 197.40 80.443 195.87 c -80.443 192.20 l -79.663 192.20 l -79.663 191.03 l -80.443 191.03 l -80.443 189.86 l -82.322 189.64 l -82.322 191.03 l -83.814 191.03 l -83.814 192.20 l -82.322 192.20 l -82.322 195.63 l -82.322 196.48 82.671 196.91 83.370 196.91 c -83.530 196.91 83.723 196.88 83.947 196.82 c -h -85.623 198.00 m -85.623 187.98 l -87.502 187.98 l -87.502 192.34 l -88.116 191.36 88.907 190.87 89.876 190.87 c -90.498 190.87 90.989 191.07 91.349 191.46 c -91.708 191.86 91.888 192.39 91.888 193.07 c -91.888 198.00 l -90.009 198.00 l -90.009 193.54 l -90.009 192.75 89.747 192.35 89.222 192.35 c -88.625 192.35 88.052 192.77 87.502 193.61 c -87.502 198.00 l -h -99.835 197.77 m -98.943 198.03 98.096 198.16 97.296 198.16 c -96.133 198.16 95.214 197.83 94.542 197.17 c -93.869 196.51 93.532 195.61 93.532 194.46 c -93.532 193.39 93.840 192.52 94.456 191.86 c -95.072 191.20 95.885 190.87 96.896 190.87 c -97.916 190.87 98.661 191.19 99.131 191.84 c -99.601 192.48 99.835 193.50 99.835 194.89 c -95.513 194.89 l -95.640 196.22 96.370 196.88 97.703 196.88 c -98.333 196.88 99.044 196.74 99.835 196.44 c -h -95.487 193.83 m -97.988 193.83 l -97.988 192.64 97.605 192.05 96.839 192.05 c -96.061 192.05 95.610 192.64 95.487 193.83 c -h -101.78 198.00 m -101.78 191.03 l -103.66 191.03 l -103.66 192.34 l -104.27 191.36 105.06 190.87 106.03 190.87 c -106.65 190.87 107.14 191.07 107.50 191.46 c -107.86 191.86 108.04 192.39 108.04 193.07 c -108.04 198.00 l -106.16 198.00 l -106.16 193.54 l -106.16 192.75 105.90 192.35 105.38 192.35 c -104.78 192.35 104.21 192.77 103.66 193.61 c -103.66 198.00 l -h -113.91 197.96 m -113.46 198.09 113.11 198.16 112.85 198.16 c -111.22 198.16 110.40 197.40 110.40 195.87 c -110.40 192.20 l -109.62 192.20 l -109.62 191.03 l -110.40 191.03 l -110.40 189.86 l -112.28 189.64 l -112.28 191.03 l -113.77 191.03 l -113.77 192.20 l -112.28 192.20 l -112.28 195.63 l -112.28 196.48 112.63 196.91 113.33 196.91 c -113.49 196.91 113.68 196.88 113.91 196.82 c -h -115.58 198.00 m -115.58 191.03 l -117.46 191.03 l -117.46 198.00 l -h -115.58 189.86 m -115.58 188.29 l -117.46 188.29 l -117.46 189.86 l -h -124.95 197.85 m -124.17 198.06 123.45 198.16 122.79 198.16 c -121.68 198.16 120.80 197.83 120.15 197.18 c -119.51 196.52 119.18 195.63 119.18 194.51 c -119.18 193.37 119.52 192.48 120.18 191.84 c -120.84 191.19 121.76 190.87 122.93 190.87 c -123.50 190.87 124.16 190.96 124.90 191.14 c -124.90 192.50 l -124.13 192.25 123.51 192.13 123.05 192.13 c -122.49 192.13 122.03 192.34 121.69 192.78 c -121.35 193.21 121.18 193.78 121.18 194.50 c -121.18 195.23 121.36 195.81 121.73 196.25 c -122.10 196.69 122.60 196.91 123.21 196.91 c -123.78 196.91 124.36 196.79 124.95 196.55 c -h -130.15 197.25 m -129.52 197.86 128.85 198.16 128.13 198.16 c -127.52 198.16 127.03 197.97 126.65 197.60 c -126.27 197.23 126.08 196.75 126.08 196.15 c -126.08 195.38 126.38 194.79 127.00 194.37 c -127.62 193.96 128.50 193.75 129.64 193.75 c -130.15 193.75 l -130.15 193.11 l -130.15 192.39 129.73 192.03 128.91 192.03 c -128.18 192.03 127.44 192.23 126.69 192.65 c -126.69 191.35 l -127.54 191.03 128.38 190.87 129.21 190.87 c -131.04 190.87 131.95 191.60 131.95 193.05 c -131.95 196.13 l -131.95 196.68 132.12 196.95 132.48 196.95 c -132.54 196.95 132.62 196.94 132.72 196.93 c -132.77 197.98 l -132.37 198.10 132.02 198.16 131.71 198.16 c -130.94 198.16 130.45 197.86 130.23 197.25 c -h -130.15 196.24 m -130.15 194.83 l -129.70 194.83 l -128.49 194.83 127.88 195.21 127.88 195.97 c -127.88 196.23 127.97 196.44 128.14 196.62 c -128.32 196.80 128.54 196.88 128.79 196.88 c -129.23 196.88 129.68 196.67 130.15 196.24 c -h -137.97 197.96 m -137.52 198.09 137.16 198.16 136.91 198.16 c -135.28 198.16 134.46 197.40 134.46 195.87 c -134.46 192.20 l -133.68 192.20 l -133.68 191.03 l -134.46 191.03 l -134.46 189.86 l -136.34 189.64 l -136.34 191.03 l -137.83 191.03 l -137.83 192.20 l -136.34 192.20 l -136.34 195.63 l -136.34 196.48 136.69 196.91 137.39 196.91 c -137.55 196.91 137.74 196.88 137.97 196.82 c -h -145.32 197.77 m -144.42 198.03 143.58 198.16 142.78 198.16 c -141.61 198.16 140.70 197.83 140.02 197.17 c -139.35 196.51 139.01 195.61 139.01 194.46 c -139.01 193.39 139.32 192.52 139.94 191.86 c -140.55 191.20 141.37 190.87 142.38 190.87 c -143.40 190.87 144.14 191.19 144.61 191.84 c -145.08 192.48 145.32 193.50 145.32 194.89 c -140.99 194.89 l -141.12 196.22 141.85 196.88 143.18 196.88 c -143.81 196.88 144.53 196.74 145.32 196.44 c -h -140.97 193.83 m -143.47 193.83 l -143.47 192.64 143.09 192.05 142.32 192.05 c -141.54 192.05 141.09 192.64 140.97 193.83 c -h -f -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -50.000 268.00 m -128.00 268.00 l -128.00 285.00 l -50.000 285.00 l -50.000 268.00 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -50.500 268.50 m -654.50 268.50 l -654.50 420.50 l -50.500 420.50 l -50.500 268.50 l -h -S -50.500 285.50 m -127.50 285.50 l -S -127.50 285.50 m -129.50 281.50 l -S -129.50 281.50 m -129.50 268.50 l -S -58.484 282.00 m -58.484 280.69 l -57.875 281.67 57.084 282.16 56.110 282.16 c -55.488 282.16 54.997 281.96 54.638 281.57 c -54.278 281.17 54.098 280.64 54.098 279.96 c -54.098 275.03 l -55.977 275.03 l -55.977 279.49 l -55.977 280.28 56.242 280.68 56.771 280.68 c -57.363 280.68 57.934 280.26 58.484 279.42 c -58.484 275.03 l -60.363 275.03 l -60.363 282.00 l -h -62.528 281.78 m -62.528 280.40 l -63.459 280.79 64.254 280.98 64.915 280.98 c -65.685 280.98 66.070 280.72 66.070 280.20 c -66.070 279.86 65.752 279.56 65.118 279.31 c -64.483 279.05 l -63.793 278.78 63.300 278.47 63.004 278.15 c -62.708 277.83 62.560 277.43 62.560 276.96 c -62.560 276.30 62.811 275.79 63.315 275.42 c -63.819 275.05 64.523 274.87 65.429 274.87 c -65.996 274.87 66.671 274.95 67.454 275.12 c -67.454 276.44 l -66.700 276.18 66.076 276.05 65.581 276.05 c -64.802 276.05 64.413 276.29 64.413 276.77 c -64.413 277.09 64.701 277.36 65.276 277.58 c -65.822 277.79 l -66.639 278.09 67.207 278.41 67.527 278.72 c -67.846 279.04 68.006 279.45 68.006 279.95 c -68.006 280.61 67.734 281.14 67.190 281.55 c -66.646 281.95 65.939 282.16 65.067 282.16 c -64.229 282.16 63.383 282.03 62.528 281.78 c -h -75.737 281.77 m -74.844 282.03 73.998 282.16 73.198 282.16 c -72.035 282.16 71.116 281.83 70.443 281.17 c -69.771 280.51 69.434 279.61 69.434 278.46 c -69.434 277.39 69.742 276.52 70.358 275.86 c -70.973 275.20 71.787 274.87 72.798 274.87 c -73.818 274.87 74.563 275.19 75.033 275.84 c -75.502 276.48 75.737 277.50 75.737 278.89 c -71.415 278.89 l -71.542 280.22 72.271 280.88 73.604 280.88 c -74.235 280.88 74.946 280.74 75.737 280.44 c -h -71.389 277.83 m -73.890 277.83 l -73.890 276.64 73.507 276.05 72.741 276.05 c -71.963 276.05 71.512 276.64 71.389 277.83 c -h -76.505 284.27 m -76.505 283.25 l -83.005 283.25 l -83.005 284.27 l -h -83.996 281.78 m -83.996 280.40 l -84.927 280.79 85.722 280.98 86.382 280.98 c -87.153 280.98 87.538 280.72 87.538 280.20 c -87.538 279.86 87.220 279.56 86.585 279.31 c -85.951 279.05 l -85.261 278.78 84.768 278.47 84.472 278.15 c -84.175 277.83 84.027 277.43 84.027 276.96 c -84.027 276.30 84.279 275.79 84.783 275.42 c -85.286 275.05 85.991 274.87 86.896 274.87 c -87.464 274.87 88.139 274.95 88.921 275.12 c -88.921 276.44 l -88.168 276.18 87.544 276.05 87.049 276.05 c -86.270 276.05 85.881 276.29 85.881 276.77 c -85.881 277.09 86.169 277.36 86.744 277.58 c -87.290 277.79 l -88.107 278.09 88.675 278.41 88.994 278.72 c -89.314 279.04 89.474 279.45 89.474 279.95 c -89.474 280.61 89.202 281.14 88.658 281.55 c -88.114 281.95 87.406 282.16 86.535 282.16 c -85.697 282.16 84.850 282.03 83.996 281.78 c -h -97.205 281.77 m -96.312 282.03 95.466 282.16 94.666 282.16 c -93.502 282.16 92.584 281.83 91.911 281.17 c -91.238 280.51 90.902 279.61 90.902 278.46 c -90.902 277.39 91.210 276.52 91.825 275.86 c -92.441 275.20 93.255 274.87 94.266 274.87 c -95.286 274.87 96.031 275.19 96.500 275.84 c -96.970 276.48 97.205 277.50 97.205 278.89 c -92.882 278.89 l -93.009 280.22 93.739 280.88 95.072 280.88 c -95.703 280.88 96.414 280.74 97.205 280.44 c -h -92.857 277.83 m -95.358 277.83 l -95.358 276.64 94.975 276.05 94.209 276.05 c -93.430 276.05 92.980 276.64 92.857 277.83 c -h -99.147 282.00 m -99.147 275.03 l -101.03 275.03 l -101.03 276.34 l -101.51 275.36 102.25 274.87 103.25 274.87 c -103.37 274.87 103.48 274.88 103.60 274.91 c -103.60 276.59 l -103.33 276.49 103.08 276.44 102.85 276.44 c -102.11 276.44 101.50 276.82 101.03 277.58 c -101.03 282.00 l -h -106.78 282.00 m -104.12 275.03 l -106.10 275.03 l -108.05 280.06 l -109.98 275.03 l -111.34 275.03 l -108.66 282.00 l -h -112.77 282.00 m -112.77 275.03 l -114.65 275.03 l -114.65 282.00 l -h -112.77 273.86 m -112.77 272.29 l -114.65 272.29 l -114.65 273.86 l -h -122.14 281.85 m -121.36 282.06 120.64 282.16 119.97 282.16 c -118.86 282.16 117.98 281.83 117.34 281.18 c -116.69 280.52 116.37 279.63 116.37 278.51 c -116.37 277.37 116.70 276.48 117.37 275.84 c -118.03 275.19 118.95 274.87 120.12 274.87 c -120.69 274.87 121.34 274.96 122.08 275.14 c -122.08 276.50 l -121.31 276.25 120.70 276.13 120.23 276.13 c -119.67 276.13 119.22 276.34 118.88 276.78 c -118.53 277.21 118.36 277.78 118.36 278.50 c -118.36 279.23 118.55 279.81 118.92 280.25 c -119.29 280.69 119.78 280.91 120.40 280.91 c -120.96 280.91 121.54 280.79 122.14 280.55 c -h -129.59 281.77 m -128.70 282.03 127.85 282.16 127.05 282.16 c -125.89 282.16 124.97 281.83 124.30 281.17 c -123.62 280.51 123.29 279.61 123.29 278.46 c -123.29 277.39 123.60 276.52 124.21 275.86 c -124.83 275.20 125.64 274.87 126.65 274.87 c -127.67 274.87 128.42 275.19 128.89 275.84 c -129.36 276.48 129.59 277.50 129.59 278.89 c -125.27 278.89 l -125.40 280.22 126.12 280.88 127.46 280.88 c -128.09 280.88 128.80 280.74 129.59 280.44 c -h -125.24 277.83 m -127.74 277.83 l -127.74 276.64 127.36 276.05 126.59 276.05 c -125.82 276.05 125.37 276.64 125.24 277.83 c -h -f -/Alpha1 gs -1.0000 1.0000 1.0000 rg -1.0000 1.0000 1.0000 RG -461.00 316.00 m -572.00 316.00 l -572.00 333.00 l -461.00 333.00 l -461.00 316.00 l -h -f -/Alpha1 gs -0.0000 0.0000 0.0000 rg -0.0000 0.0000 0.0000 RG -461.50 316.50 m -644.50 316.50 l -644.50 384.50 l -461.50 384.50 l -461.50 316.50 l -h -S -461.50 333.50 m -571.50 333.50 l -S -571.50 333.50 m -573.50 329.50 l -S -573.50 329.50 m -573.50 316.50 l -S -468.59 329.25 m -467.96 329.86 467.29 330.16 466.58 330.16 c -465.97 330.16 465.47 329.97 465.09 329.60 c -464.71 329.23 464.52 328.75 464.52 328.15 c -464.52 327.38 464.83 326.79 465.44 326.37 c -466.06 325.96 466.94 325.75 468.09 325.75 c -468.59 325.75 l -468.59 325.11 l -468.59 324.39 468.18 324.03 467.35 324.03 c -466.62 324.03 465.88 324.23 465.13 324.65 c -465.13 323.35 l -465.98 323.03 466.82 322.87 467.66 322.87 c -469.48 322.87 470.39 323.60 470.39 325.05 c -470.39 328.13 l -470.39 328.68 470.57 328.95 470.92 328.95 c -470.98 328.95 471.06 328.94 471.17 328.93 c -471.21 329.98 l -470.81 330.10 470.46 330.16 470.16 330.16 c -469.39 330.16 468.89 329.86 468.67 329.25 c -h -468.59 328.24 m -468.59 326.83 l -468.15 326.83 l -466.93 326.83 466.32 327.21 466.32 327.97 c -466.32 328.23 466.41 328.44 466.59 328.62 c -466.76 328.80 466.98 328.88 467.24 328.88 c -467.68 328.88 468.13 328.67 468.59 328.24 c -h -477.13 330.00 m -477.13 328.69 l -476.52 329.67 475.73 330.16 474.75 330.16 c -474.13 330.16 473.64 329.96 473.28 329.57 c -472.92 329.17 472.74 328.64 472.74 327.96 c -472.74 323.03 l -474.62 323.03 l -474.62 327.49 l -474.62 328.28 474.88 328.68 475.41 328.68 c -476.01 328.68 476.58 328.26 477.13 327.42 c -477.13 323.03 l -479.01 323.03 l -479.01 330.00 l -h -484.95 329.96 m -484.50 330.09 484.15 330.16 483.89 330.16 c -482.26 330.16 481.44 329.40 481.44 327.87 c -481.44 324.20 l -480.66 324.20 l -480.66 323.03 l -481.44 323.03 l -481.44 321.86 l -483.32 321.64 l -483.32 323.03 l -484.81 323.03 l -484.81 324.20 l -483.32 324.20 l -483.32 327.63 l -483.32 328.48 483.67 328.91 484.37 328.91 c -484.53 328.91 484.72 328.88 484.95 328.82 c -h -486.62 330.00 m -486.62 319.98 l -488.50 319.98 l -488.50 324.34 l -489.12 323.36 489.91 322.87 490.88 322.87 c -491.50 322.87 491.99 323.07 492.35 323.46 c -492.71 323.86 492.89 324.39 492.89 325.07 c -492.89 330.00 l -491.01 330.00 l -491.01 325.54 l -491.01 324.75 490.75 324.35 490.22 324.35 c -489.63 324.35 489.05 324.77 488.50 325.61 c -488.50 330.00 l -h -493.99 332.27 m -493.99 331.25 l -500.49 331.25 l -500.49 332.27 l -h -501.66 330.00 m -501.66 323.03 l -503.46 323.03 l -503.46 324.34 l -504.01 323.36 504.80 322.87 505.81 322.87 c -506.34 322.87 506.77 323.00 507.11 323.26 c -507.45 323.52 507.65 323.88 507.73 324.34 c -508.38 323.36 509.17 322.87 510.09 322.87 c -511.36 322.87 512.00 323.57 512.00 324.98 c -512.00 330.00 l -510.20 330.00 l -510.20 325.59 l -510.20 324.77 509.92 324.36 509.37 324.36 c -508.81 324.36 508.26 324.76 507.73 325.58 c -507.73 330.00 l -505.93 330.00 l -505.93 325.59 l -505.93 324.77 505.65 324.35 505.09 324.35 c -504.54 324.35 504.00 324.76 503.46 325.58 c -503.46 330.00 l -h -514.27 330.00 m -514.27 323.03 l -516.15 323.03 l -516.15 330.00 l -h -514.27 321.86 m -514.27 320.29 l -516.15 320.29 l -516.15 321.86 l -h -522.88 330.00 m -522.88 328.69 l -522.40 329.67 521.64 330.16 520.60 330.16 c -519.76 330.16 519.11 329.85 518.63 329.24 c -518.15 328.62 517.91 327.78 517.91 326.71 c -517.91 325.54 518.18 324.61 518.72 323.91 c -519.26 323.22 519.98 322.87 520.89 322.87 c -521.62 322.87 522.28 323.16 522.88 323.73 c -522.88 319.98 l -524.77 319.98 l -524.77 330.00 l -h -522.88 324.85 m -522.43 324.34 521.94 324.09 521.43 324.09 c -520.97 324.09 520.60 324.31 520.32 324.76 c -520.05 325.20 519.91 325.80 519.91 326.55 c -519.91 327.97 520.37 328.69 521.28 328.69 c -521.84 328.69 522.37 328.33 522.88 327.61 c -h -531.49 330.00 m -531.49 328.69 l -531.01 329.67 530.25 330.16 529.22 330.16 c -528.38 330.16 527.72 329.85 527.24 329.24 c -526.76 328.62 526.52 327.78 526.52 326.71 c -526.52 325.54 526.79 324.61 527.33 323.91 c -527.87 323.22 528.60 322.87 529.51 322.87 c -530.24 322.87 530.90 323.16 531.49 323.73 c -531.49 319.98 l -533.38 319.98 l -533.38 330.00 l -h -531.49 324.85 m -531.04 324.34 530.56 324.09 530.04 324.09 c -529.58 324.09 529.21 324.31 528.94 324.76 c -528.66 325.20 528.52 325.80 528.52 326.55 c -528.52 327.97 528.98 328.69 529.90 328.69 c -530.45 328.69 530.99 328.33 531.49 327.61 c -h -535.72 330.00 m -535.72 319.98 l -537.60 319.98 l -537.60 330.00 l -h -545.62 329.77 m -544.73 330.03 543.89 330.16 543.09 330.16 c -541.92 330.16 541.00 329.83 540.33 329.17 c -539.66 328.51 539.32 327.61 539.32 326.46 c -539.32 325.39 539.63 324.52 540.24 323.86 c -540.86 323.20 541.67 322.87 542.69 322.87 c -543.71 322.87 544.45 323.19 544.92 323.84 c -545.39 324.48 545.62 325.50 545.62 326.89 c -541.30 326.89 l -541.43 328.22 542.16 328.88 543.49 328.88 c -544.12 328.88 544.83 328.74 545.62 328.44 c -h -541.28 325.83 m -543.78 325.83 l -543.78 324.64 543.39 324.05 542.63 324.05 c -541.85 324.05 541.40 324.64 541.28 325.83 c -h -548.80 330.00 m -546.86 323.03 l -548.61 323.03 l -549.98 327.91 l -551.44 323.03 l -553.06 323.03 l -554.38 327.94 l -555.86 323.03 l -557.16 323.03 l -555.10 330.00 l -553.29 330.00 l -552.02 325.22 l -550.60 330.00 l -h -562.20 329.25 m -561.57 329.86 560.90 330.16 560.19 330.16 c -559.58 330.16 559.08 329.97 558.70 329.60 c -558.32 329.23 558.13 328.75 558.13 328.15 c -558.13 327.38 558.44 326.79 559.05 326.37 c -559.67 325.96 560.55 325.75 561.70 325.75 c -562.20 325.75 l -562.20 325.11 l -562.20 324.39 561.79 324.03 560.96 324.03 c -560.23 324.03 559.49 324.23 558.74 324.65 c -558.74 323.35 l -559.59 323.03 560.43 322.87 561.27 322.87 c -563.09 322.87 564.00 323.60 564.00 325.05 c -564.00 328.13 l -564.00 328.68 564.18 328.95 564.53 328.95 c -564.59 328.95 564.67 328.94 564.78 328.93 c -564.82 329.98 l -564.42 330.10 564.07 330.16 563.77 330.16 c -563.00 330.16 562.50 329.86 562.28 329.25 c -h -562.20 328.24 m -562.20 326.83 l -561.75 326.83 l -560.54 326.83 559.93 327.21 559.93 327.97 c -559.93 328.23 560.02 328.44 560.20 328.62 c -560.37 328.80 560.59 328.88 560.85 328.88 c -561.29 328.88 561.74 328.67 562.20 328.24 c -h -566.43 330.00 m -566.43 323.03 l -568.30 323.03 l -568.30 324.34 l -568.79 323.36 569.53 322.87 570.53 322.87 c -570.64 322.87 570.76 322.88 570.88 322.91 c -570.88 324.59 l -570.61 324.49 570.36 324.44 570.13 324.44 c -569.38 324.44 568.77 324.82 568.30 325.58 c -568.30 330.00 l -h -578.01 329.77 m -577.12 330.03 576.27 330.16 575.47 330.16 c -574.31 330.16 573.39 329.83 572.72 329.17 c -572.04 328.51 571.71 327.61 571.71 326.46 c -571.71 325.39 572.01 324.52 572.63 323.86 c -573.25 323.20 574.06 322.87 575.07 322.87 c -576.09 322.87 576.84 323.19 577.31 323.84 c -577.78 324.48 578.01 325.50 578.01 326.89 c -573.69 326.89 l -573.81 328.22 574.54 328.88 575.88 328.88 c -576.51 328.88 577.22 328.74 578.01 328.44 c -h -573.66 325.83 m -576.16 325.83 l -576.16 324.64 575.78 324.05 575.01 324.05 c -574.24 324.05 573.78 324.64 573.66 325.83 c -h -f -Q -Q -Q - -endstream -endobj - -8 0 obj - 140413 -endobj - -3 0 obj - << - /Parent null - /Type /Pages - /MediaBox [0.0000 0.0000 595.00 842.00] - /Resources 9 0 R - /Kids [6 0 R] - /Count 1 - >> -endobj - -10 0 obj - [/PDF /Text /ImageC] -endobj - -11 0 obj - << - /Alpha1 - << - /ca 1.0000 - /CA 1.0000 - /BM /Normal - /AIS false - >> - >> -endobj - -9 0 obj - << - /ProcSet 10 0 R - /ExtGState 11 0 R - >> -endobj - -4 0 obj - << - /Type /Outlines - /First 12 0 R - /Last 12 0 R - >> -endobj - -12 0 obj - << - /Parent 4 0 R - /Title (Page 1 \(untitled\)) - /Prev null - /Next null - /Dest [6 0 R /Fit] - >> -endobj - -xref -0 13 -0000000000 65535 f -0000000016 00000 n -0000000343 00000 n -0000141197 00000 n -0000141628 00000 n -0000000525 00000 n -0000000602 00000 n -0000000691 00000 n -0000141171 00000 n -0000141553 00000 n -0000141368 00000 n -0000141409 00000 n -0000141718 00000 n - -trailer -<< - /Size 12 - /Root 2 0 R - /Info 1 0 R ->> - -startxref -141862 - -%%EOF diff --git a/examples/echo/docs/echo_flow.sdx b/examples/echo/docs/echo_flow.sdx deleted file mode 100644 index 8ba2aa1958..0000000000 --- a/examples/echo/docs/echo_flow.sdx +++ /dev/null @@ -1,75 +0,0 @@ - - - -client:success, user=keystone.add_user -[/c] - -[c:authenticate] -client:token=keystone.auth -[/c] - -[c:use_service] -client:success, data_to_echo=echo.any_call - -[c:auth_middleware] -echo:user, token_data=keystone.validate -[/c] - -[/c]]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/echo/echo/__init__.py b/examples/echo/echo/__init__.py deleted file mode 100644 index 3c39fdbc7d..0000000000 --- a/examples/echo/echo/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 2010-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. - -from server import app_factory diff --git a/examples/echo/echo/echo.ini b/examples/echo/echo/echo.ini deleted file mode 100644 index 3cfffd3a82..0000000000 --- a/examples/echo/echo/echo.ini +++ /dev/null @@ -1,46 +0,0 @@ -[DEFAULT] -;delegated means we still allow unauthenticated requests through so the -;service can make the final decision on authentication -delay_auth_decision = 0 - -;where to find the OpenStack service (if not in local WSGI chain) -service_protocol = http -service_host = 127.0.0.1 -service_port = 35357 -;used to verify this component with the OpenStack service (or PAPIAuth) -service_pass = dTpw - -;where to find x.509 client certificates -certfile = ../../ssl/certs/middleware-key.pem -keyfile = ../../ssl/certs/middleware-key.pem - -[app:echo] -paste.app_factory = echo:app_factory - -[pipeline:main] -pipeline = - tokenauth - echo - -[filter:tokenauth] -paste.filter_factory = keystone.middleware.auth_token:filter_factory -;where to find the token auth service -auth_host = 127.0.0.1 -auth_port = 35357 -auth_protocol = http -auth_uri = http://localhost:5000/ -;Uncomment the following out for SSL connections -;auth_protocol = https -;auth_uri = https://localhost:5000/ -;Uncomment the following out for memcached caching -memcache_hosts = 127.0.0.1:11211 - -;how to authenticate to the auth service for priviledged operations -;like validate token -admin_token = 999888777666 - -[filter:basicauth] -paste.filter_factory = keystone.middleware.auth_basic:filter_factory - -[filter:openidauth] -paste.filter_factory = keystone.middleware.auth_openid:filter_factory diff --git a/examples/echo/echo/echo.wadl b/examples/echo/echo/echo.wadl deleted file mode 100644 index b9572999b4..0000000000 --- a/examples/echo/echo/echo.wadl +++ /dev/null @@ -1,86 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/echo/echo/echo_basic.ini b/examples/echo/echo/echo_basic.ini deleted file mode 100644 index ba590f376b..0000000000 --- a/examples/echo/echo/echo_basic.ini +++ /dev/null @@ -1,38 +0,0 @@ -[DEFAULT] -;delegated means we still allow unauthenticated requests through so the -;service can make the final decision on authentication -delay_auth_decision = 0 - -;where to find the OpenStack service (if not in local WSGI chain) -service_protocol = http -service_host = 127.0.0.1 -service_port = 35357 -;used to verify this component with the OpenStack service (or PAPIAuth) -service_pass = dTpw - - -[app:echo] -paste.app_factory = echo:app_factory - -[pipeline:main] -pipeline = - basicauth - echo - -[filter:tokenauth] -paste.filter_factory = keystone.middleware.auth_token:filter_factory -;where to find the token auth service -auth_host = 127.0.0.1 -auth_port = 35357 -auth_protocol = http -;how to authenticate to the auth service for priviledged operations -;like validate token -admin_token = 999888777666 -;Uncomment the following out for memcached caching -;memcache_hosts = 127.0.0.1:11211 - -[filter:basicauth] -paste.filter_factory = keystone.middleware.auth_basic:filter_factory - -[filter:openidauth] -paste.filter_factory = keystone.middleware.auth_openid:filter_factory diff --git a/examples/echo/echo/echo_remote.ini b/examples/echo/echo/echo_remote.ini deleted file mode 100644 index ba5e46102e..0000000000 --- a/examples/echo/echo/echo_remote.ini +++ /dev/null @@ -1,19 +0,0 @@ -[DEFAULT] - -[app:echo] -paste.app_factory = echo:app_factory - -[pipeline:main] -pipeline = - remoteauth - echo - -[filter:remoteauth] -paste.filter_factory = keystone.middleware.remoteauth:filter_factory -;password which will tell us call is coming from a trusted auth proxy -; (otherwise we redirect call) -remote_auth_pass = dTpw -;where to redirect untrusted calls to -proxy_location = http://127.0.0.1:8090/ - - diff --git a/examples/echo/echo/samples/echo.json b/examples/echo/echo/samples/echo.json deleted file mode 100644 index b20afb606c..0000000000 --- a/examples/echo/echo/samples/echo.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "echo": { - "method": "GET", - "pathInfo": "/bla/bla", - "queryString": "hello", - "content": { - "type": "application/xml", - "value": " This is some content. " - } - } -} diff --git a/examples/echo/echo/samples/echo.xml b/examples/echo/echo/samples/echo.xml deleted file mode 100644 index 8cf4b98499..0000000000 --- a/examples/echo/echo/samples/echo.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - This is some content. - - diff --git a/examples/echo/echo/server.py b/examples/echo/echo/server.py deleted file mode 100644 index 6289aac83a..0000000000 --- a/examples/echo/echo/server.py +++ /dev/null @@ -1,165 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 2010-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. - -import eventlet -from eventlet import wsgi -from lxml import etree -import os -from paste.deploy import loadapp -import sys -from webob.exc import HTTPUnauthorized - - -# If ../echo/__init__.py exists, add ../ to Python search path, so that -# it will override what happens to be installed in /usr/(local/)lib/python... -POSSIBLE_TOPDIR = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), - os.pardir, - os.pardir)) -if os.path.exists(os.path.join(POSSIBLE_TOPDIR, 'echo', '__init__.py')): - # also use the local keystone - KEYSTONE_TOPDIR = os.path.normpath(os.path.join(POSSIBLE_TOPDIR, - os.pardir, - os.pardir)) - if os.path.exists(os.path.join(KEYSTONE_TOPDIR, - 'keystone', - '__init__.py')): - sys.path.insert(0, KEYSTONE_TOPDIR) - sys.path.insert(0, POSSIBLE_TOPDIR) - - -""" -Echo: a dummy service for OpenStack auth testing. It returns request info. -""" - - -class EchoApp(object): - def __init__(self, environ, start_response): - self.envr = environ - self.start = start_response - self.dom = self.toDOM(environ) - echo_xsl = os.path.join(os.path.abspath(\ - os.path.dirname(__file__)), "xsl/echo.xsl") - self.transform = etree.XSLT(etree.parse(echo_xsl)) - - def __iter__(self): - # We expect an X_AUTHORIZATION header to be passed in - # We assume the request is coming from a trusted source. Middleware - # is used to perform that validation. - if 'HTTP_X_AUTHORIZATION' not in self.envr: - self.start('401 Unauthorized', [('Content-Type', - 'application/json')]) - return iter(["401 Unauthorized"]) - - if 'HTTP_X_IDENTITY_STATUS' not in self.envr: - identity_status = "Unknown" - else: - identity_status = self.envr["HTTP_X_IDENTITY_STATUS"] - - print ' Received:' - print ' Auth Status:', identity_status - if 'HTTP_X_AUTHORIZATION' in self.envr: - print ' Identity :', self.envr['HTTP_X_AUTHORIZATION'] - if 'HTTP_X_TENANT' in self.envr: - print ' Tenant :', self.envr['HTTP_X_TENANT'] - if 'HTTP_X_ROLE' in self.envr: - print ' Roles :', self.envr['HTTP_X_ROLE'] - - accept = self.envr.get("HTTP_ACCEPT", "application/json") - if accept == "application/xml": - return self.toXML() - else: - return self.toJSON() - - def toJSON(self): - self.start('200 OK', [('Content-Type', 'application/json')]) - yield str(self.transform(self.dom)) - - def toXML(self): - self.start('200 OK', [('Content-Type', 'application/xml')]) - yield etree.tostring(self.dom) - - def toDOM(self, environ): - echo = etree.Element("{http://docs.openstack.org/echo/api/v1.0}echo", - method=environ["REQUEST_METHOD"], - pathInfo=environ["PATH_INFO"], - queryString=environ.get('QUERY_STRING', "")) - content = etree.Element( - "{http://docs.openstack.org/echo/api/v1.0}content") - content.set("type", environ["CONTENT_TYPE"]) - content.text = "" - inReq = environ["wsgi.input"] - for line in inReq: - content.text = content.text + line - echo.append(content) - return echo - - -def app_factory(global_conf, **local_conf): - return EchoApp - -if __name__ == "__main__": - def usage(): - print "Runs Echo, the canonical OpenStack service, " \ - "with auth middleware" - print "Options:" - print "-h, --help : show this usage information" - print "-b, --basic : run with basic auth (uses echo_basic.ini)" - print "-r, --remote: run with remote auth on port 8100" \ - "(uses echo_remote.ini)" - print "-i, --ini filename: run with specified ini file" - print "-p, --port: specifies port to listen on (default is 8090)" - print "by default will run with local, token auth (uses echo.ini)" - - import getopt - try: - opts, args = getopt.getopt(sys.argv[1:], - "hbrp:i:", - ["help", "basic", "remote", "port", "ini"]) - except getopt.GetoptError: - usage() - sys.exit() - - port = 0 - ini = "echo.ini" - auth_name = "local Token Auth" - - for opt, arg in opts: - if opt in ["-h", "--help"]: - usage() - sys.exit() - elif opt in ["-p", "--port"]: - port = int(arg) - elif opt in ["-i", "--ini"]: - auth_name = "with custom ini: %s" % arg - ini = arg - elif opt in ["-b", "--basic"]: - auth_name = "Basic Auth" - ini = "echo_basic.ini" - elif opt in ["-r", "--remote"]: - auth_name = "remote Token Auth" - ini = "echo_remote.ini" - if not port: - port = 8100 - - if not port: - port = 8090 - print "Running with", auth_name - app = loadapp("config:" + \ - os.path.join(os.path.abspath(os.path.dirname(__file__)), - ini), global_conf={"log_name": "echo.log"}) - listener = eventlet.listen(('', port)) - pool = eventlet.GreenPool(1000) - wsgi.server(listener, app, custom_pool=pool) diff --git a/examples/echo/echo/xsd/echo.xsd b/examples/echo/echo/xsd/echo.xsd deleted file mode 100644 index 3d5c2a515b..0000000000 --- a/examples/echo/echo/xsd/echo.xsd +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/echo/echo/xsl/echo.xsl b/examples/echo/echo/xsl/echo.xsl deleted file mode 100644 index dbe8b5ddc6..0000000000 --- a/examples/echo/echo/xsl/echo.xsl +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - diff --git a/examples/echo/echo_client.py b/examples/echo/echo_client.py deleted file mode 100755 index 8070f0bd0a..0000000000 --- a/examples/echo/echo_client.py +++ /dev/null @@ -1,112 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 2010-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. -""" -Implement a client for Echo service using Identity service -""" - -import httplib -import json -import sys - - -def keystone_conn(): - """ Get a connection. If it is SSL, needs the '-s' option, optionally - followed by the location of the cert_file with private key. """ - if '-s' in sys.argv: - cert_file = None - if len(sys.argv) > sys.argv.index('-s') + 1: - cert_file = sys.argv[sys.argv.index('-s') + 1] - conn = httplib.HTTPSConnection("localhost:5000", cert_file=cert_file) - else: - conn = httplib.HTTPConnection("localhost:5000") - return conn - - -def get_auth_token(username, password, tenant): - headers = {"Content-type": "application/json", - "Accept": "application/json"} - params = {"auth": {"passwordCredentials": - {"username": username, "password": password}, - "tenantName": tenant}} - conn = keystone_conn() - conn.request("POST", "/v2.0/tokens", json.dumps(params), headers=headers) - response = conn.getresponse() - data = response.read() - print data - ret = data - return ret - - -def call_service(token): - headers = {"X-Auth-Token": token, - "Content-type": "application/json", - "Accept": "application/json"} - params = '{"ping": "abcdefg"}' - conn = httplib.HTTPConnection("localhost:8090") - conn.request("POST", "/", params, headers=headers) - response = conn.getresponse() - data = response.read() - ret = data - return ret - - -def hack_attempt(token): - # Injecting headers in the request - headers = {"X-Auth-Token": token, - "Content-type": "application/json", - "Accept": "application/json\nX_AUTHORIZATION: someone else\n" - "X_IDENTITY_STATUS: Confirmed\nINJECTED_HEADER: aha!"} - params = '{"ping": "abcdefg"}' - conn = httplib.HTTPConnection("localhost:8090") - print headers - conn.request("POST", "/", params, headers=headers) - response = conn.getresponse() - data = response.read() - ret = data - return ret - - -if __name__ == '__main__': - # Call the keystone service to get a token - # NOTE: assumes the test_setup.sql script has loaded this user - print "\033[91mTrying with valid test credentials...\033[0m" - auth = get_auth_token("joeuser", "secrete", "customer-x") - obj = json.loads(auth) - token = obj["access"]["token"]["id"] - print "Token obtained:", token - - # Use that token to call an OpenStack service (echo) - data = call_service(token) - print "Response received:", data - print - - # Use the valid token, but inject some headers - print "\033[91mInjecting some headers >:-/ \033[0m" - data = hack_attempt(token) - print "Response received:", data - print - - # Use bad token to call an OpenStack service (echo) - print "\033[91mTrying with bad token...\033[0m" - data = call_service("xxxx_invalid_token_xxxx") - print "Response received:", data - print - - #Supply bad credentials - print "\033[91mTrying with bad credentials...\033[0m" - auth = get_auth_token("joeuser", "wrongpass", "customer-x") - print "Response:", auth diff --git a/examples/echo/setup.py b/examples/echo/setup.py deleted file mode 100644 index 045df550a4..0000000000 --- a/examples/echo/setup.py +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/python -# Copyright (c) 2010-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. - -from setuptools import setup, find_packages - -version = '1.0' - -setup( - name='echo', - version=version, - description="", - license='Apache License (2.0)', - classifiers=["Programming Language :: Python"], - keywords='', - author='OpenStack, LLC.', - include_package_data=True, - packages=find_packages(exclude=['test', 'bin']), - zip_safe=False, - install_requires=['setuptools', 'keystone'], - entry_points={ - 'paste.app_factory': ['main=echo:app_factory'], - 'paste.filter_factory': [ - 'papiauth=keystone:papiauth_factory', - ], - }, - ) diff --git a/examples/paste/auth_basic.ini b/examples/paste/auth_basic.ini deleted file mode 100644 index ae5d75d8e8..0000000000 --- a/examples/paste/auth_basic.ini +++ /dev/null @@ -1,13 +0,0 @@ -[DEFAULT] - -[app:main] -paste.app_factory = auth_basic:app_factory - -delay_auth_decision = 0 - -service_protocol = http -service_host = 127.0.0.1 -service_port = 8100 -service_pass = dTpw - - diff --git a/examples/paste/auth_token.ini b/examples/paste/auth_token.ini deleted file mode 100644 index b1ce7f209e..0000000000 --- a/examples/paste/auth_token.ini +++ /dev/null @@ -1,23 +0,0 @@ -[DEFAULT] - -[app:main] -paste.app_factory = auth_token:app_factory - -auth_protocol = http -auth_host = 127.0.0.1 -auth_port = 35357 -auth_uri = http://127.0.0.1:35357/ -admin_token = 999888777666 -auth_timeout = 10 - -delay_auth_decision = 1 - -service_protocol = http -service_host = 127.0.0.1 -service_port = 8100 -service_pass = dTpw -service_timeout = 120 - -;Uncomment the following out for memcached caching -;memcache_hosts = 127.0.0.1:11211 - diff --git a/examples/paste/glance-api-paste.ini b/examples/paste/glance-api-paste.ini deleted file mode 100644 index 7a27ee9ce2..0000000000 --- a/examples/paste/glance-api-paste.ini +++ /dev/null @@ -1,60 +0,0 @@ -#[pipeline:glance-api] -#pipeline = versionnegotiation authtoken context apiv1app - -# Default minimal pipeline -[pipeline:glance-api] -pipeline = versionnegotiation context apiv1app - -# Use the following pipeline for keystone auth -# i.e. in glance-api.conf: -# [paste_deploy] -# flavor = keystone -# -[pipeline:glance-api-keystone] -pipeline = versionnegotiation authtoken auth-context apiv1app - -#[pipeline:versions] -#pipeline = versionsapp - -#[app:versionsapp] -#paste.app_factory = glance.api.versions:app_factory - -#[app:apiv1app] -#paste.app_factory = glance.api.v1:app_factory - -[app:apiv1app] -paste.app_factory = glance.common.wsgi:app_factory -glance.app_factory = glance.api.v1.router:API - -#[filter:versionnegotiation] -#paste.filter_factory = glance.api.middleware.version_negotiation:filter_factory - -[filter:versionnegotiation] -paste.filter_factory = glance.common.wsgi:filter_factory -glance.filter_factory = glance.api.middleware.version_negotiation:VersionNegotiationFilter - -#[filter:imagecache] -#paste.filter_factory = glance.api.middleware.image_cache:filter_factory - -#[filter:context] -#paste.filter_factory = glance.common.context:filter_factory - -[filter:context] -paste.filter_factory = glance.common.wsgi:filter_factory -glance.filter_factory = glance.common.context:ContextMiddleware - -[filter:authtoken] -paste.filter_factory = keystone.middleware.auth_token:filter_factory -service_protocol = http -service_host = 127.0.0.1 -service_port = 5000 -auth_host = 127.0.0.1 -auth_port = 35357 -auth_protocol = http -auth_uri = http://127.0.0.1:5000/ -admin_token = 999888777666 -auth_timeout = 30 - -[filter:auth-context] -paste.filter_factory = glance.common.wsgi:filter_factory -glance.filter_factory = keystone.middleware.glance_auth_token:KeystoneContextMiddleware diff --git a/examples/paste/glance-api.conf b/examples/paste/glance-api.conf deleted file mode 100644 index 9c0f34e9c6..0000000000 --- a/examples/paste/glance-api.conf +++ /dev/null @@ -1,138 +0,0 @@ -[DEFAULT] -# Show more verbose log output (sets INFO log level output) -verbose = True - -# Show debugging output in logs (sets DEBUG log level output) -debug = False - -# Which backend store should Glance use by default is not specified -# in a request to add a new image to Glance? Default: 'file' -# Available choices are 'file', 'swift', and 's3' -default_store = file - -# Address to bind the API server -bind_host = 0.0.0.0 - -# Port the bind the API server to -bind_port = 9292 - -# Address to find the registry server -registry_host = 0.0.0.0 - -# Port the registry server is listening on -registry_port = 9191 - -# Log to this file. Make sure you do not set the same log -# file for both the API and registry servers! -log_file = /var/log/glance/api.log - -# Send logs to syslog (/dev/log) instead of to file specified by `log_file` -use_syslog = False - -# ============ Notification System Options ===================== - -# Notifications can be sent when images are create, updated or deleted. -# There are three methods of sending notifications, logging (via the -# log_file directive), rabbit (via a rabbitmq queue) or noop (no -# notifications sent, the default) -notifier_strategy = noop - -# Configuration options if sending notifications via rabbitmq (these are -# the defaults) -rabbit_host = localhost -rabbit_port = 5672 -rabbit_use_ssl = false -rabbit_userid = guest -rabbit_password = guest -rabbit_virtual_host = / -rabbit_notification_topic = glance_notifications - -# ============ Filesystem Store Options ======================== - -# Directory that the Filesystem backend store -# writes image data to -filesystem_store_datadir = /var/lib/glance/images/ - -# ============ Swift Store Options ============================= - -# Address where the Swift authentication service lives -swift_store_auth_address = 127.0.0.1:8080/v1.0/ - -# User to authenticate against the Swift authentication service -swift_store_user = jdoe - -# Auth key for the user authenticating against the -# Swift authentication service -swift_store_key = a86850deb2742ec3cb41518e26aa2d89 - -# Container within the account that the account should use -# for storing images in Swift -swift_store_container = glance - -# Do we create the container if it does not exist? -swift_store_create_container_on_put = False - -# What size, in MB, should Glance start chunking image files -# and do a large object manifest in Swift? By default, this is -# the maximum object size in Swift, which is 5GB -swift_store_large_object_size = 5120 - -# When doing a large object manifest, what size, in MB, should -# Glance write chunks to Swift? This amount of data is written -# to a temporary disk buffer during the process of chunking -# the image file, and the default is 200MB -swift_store_large_object_chunk_size = 200 - -# Whether to use ServiceNET to communicate with the Swift storage servers. -# (If you aren't RACKSPACE, leave this False!) -# -# To use ServiceNET for authentication, prefix hostname of -# `swift_store_auth_address` with 'snet-'. -# Ex. https://example.com/v1.0/ -> https://snet-example.com/v1.0/ -swift_enable_snet = False - -# ============ S3 Store Options ============================= - -# Address where the S3 authentication service lives -s3_store_host = 127.0.0.1:8080/v1.0/ - -# User to authenticate against the S3 authentication service -s3_store_access_key = <20-char AWS access key> - -# Auth key for the user authenticating against the -# S3 authentication service -s3_store_secret_key = <40-char AWS secret key> - -# Container within the account that the account should use -# for storing images in S3. Note that S3 has a flat namespace, -# so you need a unique bucket name for your glance images. An -# easy way to do this is append your AWS access key to "glance". -# S3 buckets in AWS *must* be lowercased, so remember to lowercase -# your AWS access key if you use it in your bucket name below! -s3_store_bucket = glance - -# Do we create the bucket if it does not exist? -s3_store_create_bucket_on_put = False - -# ============ Image Cache Options ======================== - -image_cache_enabled = False - -# Directory that the Image Cache writes data to -# Make sure this is also set in glance-pruner.conf -image_cache_datadir = /var/lib/glance/image-cache/ - -# Number of seconds after which we should consider an incomplete image to be -# stalled and eligible for reaping -image_cache_stall_timeout = 86400 - -# ============ Delayed Delete Options ============================= - -# Turn on/off delayed delete -delayed_delete = False - -# ============ Paste Deploy Options =============================== - -# Select the keystone deployment flavor -[paste_deploy] -flavor = keystone diff --git a/examples/paste/glance-registry-paste.ini b/examples/paste/glance-registry-paste.ini deleted file mode 100644 index c943767f24..0000000000 --- a/examples/paste/glance-registry-paste.ini +++ /dev/null @@ -1,37 +0,0 @@ -# Default minimal pipeline -[pipeline:glance-registry] -pipeline = context registryapp - -# Use the following pipeline for keystone auth -# i.e. in glance-registry.conf: -# [paste_deploy] -# flavor = keystone -# -[pipeline:glance-registry-keystone] -pipeline = authtoken auth-context registryapp - -[app:registryapp] -paste.app_factory = glance.common.wsgi:app_factory -glance.app_factory = glance.registry.api.v1:API - -[filter:context] -context_class = glance.registry.context.RequestContext -paste.filter_factory = glance.common.wsgi:filter_factory -glance.filter_factory = glance.common.context:ContextMiddleware - -[filter:authtoken] -paste.filter_factory = keystone.middleware.auth_token:filter_factory -service_protocol = http -service_host = 127.0.0.1 -service_port = 5000 -auth_host = 127.0.0.1 -auth_port = 35357 -auth_protocol = http -auth_uri = http://127.0.0.1:5000/ -admin_token = 999888777666 -auth_timeout = 30 - -[filter:auth-context] -context_class = glance.registry.context.RequestContext -paste.filter_factory = glance.common.wsgi:filter_factory -glance.filter_factory = keystone.middleware.glance_auth_token:KeystoneContextMiddleware diff --git a/examples/paste/glance-registry.conf b/examples/paste/glance-registry.conf deleted file mode 100644 index 89c931b73a..0000000000 --- a/examples/paste/glance-registry.conf +++ /dev/null @@ -1,45 +0,0 @@ -[DEFAULT] -# Show more verbose log output (sets INFO log level output) -verbose = True - -# Show debugging output in logs (sets DEBUG log level output) -debug = False - -# Address to bind the registry server -bind_host = 0.0.0.0 - -# Port the bind the registry server to -bind_port = 9191 - -# Log to this file. Make sure you do not set the same log -# file for both the API and registry servers! -log_file = /var/log/glance/registry.log - -# Send logs to syslog (/dev/log) instead of to file specified by `log_file` -use_syslog = False - -# SQLAlchemy connection string for the reference implementation -# registry server. Any valid SQLAlchemy connection string is fine. -# See: http://www.sqlalchemy.org/docs/05/reference/sqlalchemy/connections.html#sqlalchemy.create_engine -sql_connection = sqlite:///glance.sqlite - -# Period in seconds after which SQLAlchemy should reestablish its connection -# to the database. -# -# MySQL uses a default `wait_timeout` of 8 hours, after which it will drop -# idle connections. This can result in 'MySQL Gone Away' exceptions. If you -# notice this, you can lower this value to ensure that SQLAlchemy reconnects -# before MySQL can drop the connection. -sql_idle_timeout = 3600 - -# Limit the api to return `param_limit_max` items in a call to a container. If -# a larger `limit` query param is provided, it will be reduced to this value. -api_limit_max = 1000 - -# If a `limit` query param is not provided in an api request, it will -# default to `limit_param_default` -limit_param_default = 25 - -# Select the keystone deployment flavor -[paste_deploy] -flavor = keystone diff --git a/examples/paste/swift-proxy-server.conf b/examples/paste/swift-proxy-server.conf deleted file mode 100644 index 38d585ad8d..0000000000 --- a/examples/paste/swift-proxy-server.conf +++ /dev/null @@ -1,41 +0,0 @@ -[DEFAULT] -bind_ip = 0.0.0.0 -bind_port = 3333 -user = root - -[pipeline:main] -#pipeline = healthcheck cache swift3 keystone proxy-server -pipeline = healthcheck cache s3token swift3 keystone proxy-server - -[app:proxy-server] -use = egg:swift#proxy -account_autocreate = true -allow_account_management = true - -[filter:swift3] -use = egg:swift#swift3 - -[filter:s3token] -use = egg:keystone#s3token -auth_protocol = http -auth_host = 127.0.0.1 -auth_port = 5000 -admin_token = 999888777666 - -[filter:keystone] -use = egg:keystone#tokenauth -auth_protocol = http -auth_host = 127.0.0.1 -auth_port = 35357 -admin_token = 999888777666 -delay_auth_decision = 0 -service_protocol = http -service_host = 127.0.0.1 -service_port = 3333 -service_pass = pass - -[filter:healthcheck] -use = egg:swift#healthcheck - -[filter:cache] -use = egg:swift#memcache diff --git a/examples/ssl/certs/ca.pem b/examples/ssl/certs/ca.pem deleted file mode 100644 index 07ae29a0a2..0000000000 --- a/examples/ssl/certs/ca.pem +++ /dev/null @@ -1,22 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDmTCCAwKgAwIBAgIJALMGu1g0q5GjMA0GCSqGSIb3DQEBBQUAMIGQMQswCQYD -VQQGEwJVUzELMAkGA1UECBMCQ0ExEjAQBgNVBAcTCVJvc2V2aWxsZTESMBAGA1UE -ChMJT3BlbnN0YWNrMREwDwYDVQQLEwhLZXlzdG9uZTESMBAGA1UEAxMJbG9jYWxo -b3N0MSUwIwYJKoZIhvcNAQkBFhZrZXlzdG9uZUBvcGVuc3RhY2sub3JnMB4XDTEx -MTAyMDE2MDQ0MloXDTIxMTAxNzE2MDQ0MlowgZAxCzAJBgNVBAYTAlVTMQswCQYD -VQQIEwJDQTESMBAGA1UEBxMJUm9zZXZpbGxlMRIwEAYDVQQKEwlPcGVuc3RhY2sx -ETAPBgNVBAsTCEtleXN0b25lMRIwEAYDVQQDEwlsb2NhbGhvc3QxJTAjBgkqhkiG -9w0BCQEWFmtleXN0b25lQG9wZW5zdGFjay5vcmcwgZ8wDQYJKoZIhvcNAQEBBQAD -gY0AMIGJAoGBAMfYcS0Fs7DRqdGSMVyrLk91vdzs+K6a6NOgppxhETqrOMAjW5yL -ajE2Ly48qfO/BRZR0kgTGSpnv7oiFzWLCvPf63nUnCalkE+uBpksY7BpphnTCJ8F -IsZ6aggAGKto9mmADpiKxt1uSQ6DDpPm8quXbMdSZTFOOVQNPYhwPMYvAgMBAAGj -gfgwgfUwHQYDVR0OBBYEFGA/MhYYUnjIdH9FWFVVo/YODkZBMIHFBgNVHSMEgb0w -gbqAFGA/MhYYUnjIdH9FWFVVo/YODkZBoYGWpIGTMIGQMQswCQYDVQQGEwJVUzEL -MAkGA1UECBMCQ0ExEjAQBgNVBAcTCVJvc2V2aWxsZTESMBAGA1UEChMJT3BlbnN0 -YWNrMREwDwYDVQQLEwhLZXlzdG9uZTESMBAGA1UEAxMJbG9jYWxob3N0MSUwIwYJ -KoZIhvcNAQkBFhZrZXlzdG9uZUBvcGVuc3RhY2sub3JnggkAswa7WDSrkaMwDAYD -VR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQBoeuR/pRznAtStj4Axe8Xq1ivL -jXFt2G9Pj+MwLs2wokcUBYz6/rJdSTjW21s4/FQCHiw9K7HA63c4mbjkRRgtJlXo -F5PiQqv4F1KqZmWeIDGxOGStQbgc77unsYYXILI27pSqQLKc9xlli77LekY+BzTK -tr5JYtKMaby4lJTg3A== ------END CERTIFICATE----- diff --git a/examples/ssl/certs/keystone.pem b/examples/ssl/certs/keystone.pem deleted file mode 100644 index 6460d32a75..0000000000 --- a/examples/ssl/certs/keystone.pem +++ /dev/null @@ -1,62 +0,0 @@ -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 1 (0x1) - Signature Algorithm: sha1WithRSAEncryption - Issuer: C=US, ST=CA, L=Roseville, O=Openstack, OU=Keystone, CN=localhost/emailAddress=keystone@openstack.org - Validity - Not Before: Oct 20 16:34:17 2011 GMT - Not After : Oct 19 16:34:17 2012 GMT - Subject: C=US, ST=CA, L=Roseville, O=Openstack, OU=Keystone, CN=localhost/emailAddress=keystone@openstack.org - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public Key: (1024 bit) - Modulus (1024 bit): - 00:9e:5a:5c:be:dc:20:d4:af:36:5c:33:6d:72:44: - 94:59:c6:a9:24:ed:fa:8b:2c:53:ab:24:7d:79:46: - cc:a6:45:05:b0:57:b4:0d:d6:8f:f4:d9:a5:11:64: - e4:78:b1:26:30:de:fb:4a:72:c8:97:e7:31:4f:55: - bb:5b:16:d7:22:1b:13:ca:fc:6b:04:bd:15:9c:09: - 51:d6:f9:14:51:67:a3:42:4a:81:ce:98:0f:6e:5c: - ac:7f:36:be:0f:79:ad:07:81:75:a2:21:a8:5f:e5: - 9c:22:71:4c:db:63:b6:44:29:65:22:76:6e:07:98: - de:be:58:3f:b2:fe:cd:27:f7 - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Basic Constraints: - CA:FALSE - Netscape Comment: - OpenSSL Generated Certificate - X509v3 Subject Key Identifier: - C1:E3:A1:36:45:3F:B5:3B:11:A1:23:A4:7E:3A:A0:F9:BC:F6:93:A3 - X509v3 Authority Key Identifier: - keyid:60:3F:32:16:18:52:78:C8:74:7F:45:58:55:55:A3:F6:0E:0E:46:41 - - Signature Algorithm: sha1WithRSAEncryption - 06:86:d7:5d:93:11:94:ce:23:ae:74:b2:16:09:99:32:63:3d: - d9:be:8f:99:87:43:7c:0d:27:25:5c:08:c2:d6:18:37:3c:4e: - b9:06:51:53:a9:d7:93:da:14:a1:25:96:2b:eb:8d:81:9d:68: - 8d:ec:b8:1f:9e:09:80:25:fb:be:f8:20:5b:fc:ca:6c:3d:38: - c7:09:36:aa:dd:f8:0c:01:35:3e:c5:c5:3b:60:24:8c:5f:c5: - 44:e7:7f:9b:ce:b6:d5:85:b7:93:e4:8a:a5:a9:90:ff:2d:09: - 56:8c:e6:17:1f:07:33:0a:46:73:b1:65:13:d8:6f:39:76:3a: - 93:87 ------BEGIN CERTIFICATE----- -MIIDEzCCAnygAwIBAgIBATANBgkqhkiG9w0BAQUFADCBkDELMAkGA1UEBhMCVVMx -CzAJBgNVBAgTAkNBMRIwEAYDVQQHEwlSb3NldmlsbGUxEjAQBgNVBAoTCU9wZW5z -dGFjazERMA8GA1UECxMIS2V5c3RvbmUxEjAQBgNVBAMTCWxvY2FsaG9zdDElMCMG -CSqGSIb3DQEJARYWa2V5c3RvbmVAb3BlbnN0YWNrLm9yZzAeFw0xMTEwMjAxNjM0 -MTdaFw0xMjEwMTkxNjM0MTdaMIGQMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0Ex -EjAQBgNVBAcTCVJvc2V2aWxsZTESMBAGA1UEChMJT3BlbnN0YWNrMREwDwYDVQQL -EwhLZXlzdG9uZTESMBAGA1UEAxMJbG9jYWxob3N0MSUwIwYJKoZIhvcNAQkBFhZr -ZXlzdG9uZUBvcGVuc3RhY2sub3JnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB -gQCeWly+3CDUrzZcM21yRJRZxqkk7fqLLFOrJH15RsymRQWwV7QN1o/02aURZOR4 -sSYw3vtKcsiX5zFPVbtbFtciGxPK/GsEvRWcCVHW+RRRZ6NCSoHOmA9uXKx/Nr4P -ea0HgXWiIahf5ZwicUzbY7ZEKWUidm4HmN6+WD+y/s0n9wIDAQABo3sweTAJBgNV -HRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZp -Y2F0ZTAdBgNVHQ4EFgQUweOhNkU/tTsRoSOkfjqg+bz2k6MwHwYDVR0jBBgwFoAU -YD8yFhhSeMh0f0VYVVWj9g4ORkEwDQYJKoZIhvcNAQEFBQADgYEABobXXZMRlM4j -rnSyFgmZMmM92b6PmYdDfA0nJVwIwtYYNzxOuQZRU6nXk9oUoSWWK+uNgZ1ojey4 -H54JgCX7vvggW/zKbD04xwk2qt34DAE1PsXFO2AkjF/FROd/m8621YW3k+SKpamQ -/y0JVozmFx8HMwpGc7FlE9hvOXY6k4c= ------END CERTIFICATE----- diff --git a/examples/ssl/certs/middleware-key.pem b/examples/ssl/certs/middleware-key.pem deleted file mode 100644 index 780de11086..0000000000 --- a/examples/ssl/certs/middleware-key.pem +++ /dev/null @@ -1,77 +0,0 @@ -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 1 (0x1) - Signature Algorithm: sha1WithRSAEncryption - Issuer: C=US, ST=CA, L=Roseville, O=Openstack, OU=Keystone, CN=localhost/emailAddress=keystone@openstack.org - Validity - Not Before: Oct 20 17:22:02 2011 GMT - Not After : Oct 19 17:22:02 2012 GMT - Subject: C=US, ST=CA, O=Openstack, OU=Middleware, CN=localhost/emailAddress=middleware@openstack.org - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public Key: (1024 bit) - Modulus (1024 bit): - 00:cb:8d:ff:0a:f8:1f:da:0b:65:d9:15:86:e7:4a: - 89:07:81:26:7a:2e:ef:67:30:bb:5b:88:3e:73:31: - 0e:c9:d9:eb:84:55:7c:57:1b:07:8a:29:7f:41:ed: - 1a:47:b2:c4:74:3c:dc:52:81:81:ba:6c:43:b8:44: - bd:83:20:28:4a:82:03:34:f2:1e:88:89:1c:f3:d6: - ef:02:27:9f:7b:4b:dc:ed:50:91:7a:13:a0:8f:5f: - 44:10:a6:17:01:6f:7d:7a:3a:a2:1a:28:4e:6e:c5: - b6:06:0b:ba:5c:c9:e9:15:39:95:54:63:bb:40:90: - 5d:5d:76:f6:ae:ed:ee:ed:85 - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Basic Constraints: - CA:FALSE - Netscape Comment: - OpenSSL Generated Certificate - X509v3 Subject Key Identifier: - 5A:34:DE:19:11:FF:77:19:2E:E5:6C:36:FA:42:17:6B:46:AF:6A:61 - X509v3 Authority Key Identifier: - keyid:60:3F:32:16:18:52:78:C8:74:7F:45:58:55:55:A3:F6:0E:0E:46:41 - - Signature Algorithm: sha1WithRSAEncryption - a2:1b:e0:d3:e5:c5:35:ad:18:cb:79:a4:fc:f3:d6:7b:53:1e: - dd:28:95:e0:6c:b0:db:fe:aa:30:04:19:c8:99:7a:eb:cb:ed: - dd:74:29:ad:f8:89:6a:ed:d0:10:35:b3:62:36:a2:b0:cc:9f: - 86:e8:96:fd:d7:1b:5e:2c:64:b5:5d:f3:bf:1a:1a:07:8b:01: - 1f:5f:09:c3:e1:62:cd:30:35:1a:08:e1:cd:71:be:8c:87:de: - f6:7d:40:1b:c6:5f:f0:80:a0:68:55:01:00:74:86:08:52:7e: - c7:fd:62:f9:e3:d0:f8:0b:b0:64:d9:20:70:80:ec:95:11:74: - fb:0b ------BEGIN CERTIFICATE----- -MIIDAzCCAmygAwIBAgIBATANBgkqhkiG9w0BAQUFADCBkDELMAkGA1UEBhMCVVMx -CzAJBgNVBAgTAkNBMRIwEAYDVQQHEwlSb3NldmlsbGUxEjAQBgNVBAoTCU9wZW5z -dGFjazERMA8GA1UECxMIS2V5c3RvbmUxEjAQBgNVBAMTCWxvY2FsaG9zdDElMCMG -CSqGSIb3DQEJARYWa2V5c3RvbmVAb3BlbnN0YWNrLm9yZzAeFw0xMTEwMjAxNzIy -MDJaFw0xMjEwMTkxNzIyMDJaMIGAMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0Ex -EjAQBgNVBAoTCU9wZW5zdGFjazETMBEGA1UECxMKTWlkZGxld2FyZTESMBAGA1UE -AxMJbG9jYWxob3N0MScwJQYJKoZIhvcNAQkBFhhtaWRkbGV3YXJlQG9wZW5zdGFj -ay5vcmcwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMuN/wr4H9oLZdkVhudK -iQeBJnou72cwu1uIPnMxDsnZ64RVfFcbB4opf0HtGkeyxHQ83FKBgbpsQ7hEvYMg -KEqCAzTyHoiJHPPW7wInn3tL3O1QkXoToI9fRBCmFwFvfXo6ohooTm7FtgYLulzJ -6RU5lVRju0CQXV129q7t7u2FAgMBAAGjezB5MAkGA1UdEwQCMAAwLAYJYIZIAYb4 -QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBRa -NN4ZEf93GS7lbDb6QhdrRq9qYTAfBgNVHSMEGDAWgBRgPzIWGFJ4yHR/RVhVVaP2 -Dg5GQTANBgkqhkiG9w0BAQUFAAOBgQCiG+DT5cU1rRjLeaT889Z7Ux7dKJXgbLDb -/qowBBnImXrry+3ddCmt+Ilq7dAQNbNiNqKwzJ+G6Jb91xteLGS1XfO/GhoHiwEf -XwnD4WLNMDUaCOHNcb6Mh972fUAbxl/wgKBoVQEAdIYIUn7H/WL549D4C7Bk2SBw -gOyVEXT7Cw== ------END CERTIFICATE----- ------BEGIN RSA PRIVATE KEY----- -MIICXAIBAAKBgQDLjf8K+B/aC2XZFYbnSokHgSZ6Lu9nMLtbiD5zMQ7J2euEVXxX -GweKKX9B7RpHssR0PNxSgYG6bEO4RL2DIChKggM08h6IiRzz1u8CJ597S9ztUJF6 -E6CPX0QQphcBb316OqIaKE5uxbYGC7pcyekVOZVUY7tAkF1ddvau7e7thQIDAQAB -AoGAITSpzV1KvOQtGiuz1RlIn0vHPhlX/opplfX00g/HrM/65pyXaxJCuZwpYVTP -e7DC8X9YJbFwuzucFHxKOhDN4YbnW145bgfHbI9KLXtZiDvXvHg2MGKjpL/S3Lp3 -zzWBo8gknmFGLK41WbYCCWKcvikEb3/KowcooznY5X5BjWECQQD6NC9Bi2EUUyPR -B2ZT3C3h2Hj53yqLkJzP0PaxTC+j7rsycy5r7UiOK8+8aC1T9EsaJrmEKlYBmlbd -lVdhohpNAkEA0EUphaVGURlNmXZgYdSZ1rrpJTvKbFtXmUCowi7Ml2h/oTuHDFHf -i4P8//79YB1uJ4Ll9edjJsZqtAErUTnMGQJBAJcKp7hutqU5Z3bJe8mGMqCTOLzH -LvzfyPpfkH0Jm/zfolxbUhvPO6yv4BFB5pM295uK4xVZJWCEVoofnIeQ/0UCQHuK -ex3esv5KTyCX+oYtkW+xgbjnZaSu7iBnHXPKROwPPZ4LbIlfS4Y7rejAfdX0vzHK -0NP0BHmsuwC5rNNKwIkCQBZqTnLVcisz1FRM2g/OKfWMx+lhVf5fIng+jUEJCdNE -fGjCUu4BRs+nXq6EzoijLvtrmRmFL7VYAKdabSVeLRc= ------END RSA PRIVATE KEY----- diff --git a/examples/ssl/private/cakey.pem b/examples/ssl/private/cakey.pem deleted file mode 100644 index 36e38e090c..0000000000 --- a/examples/ssl/private/cakey.pem +++ /dev/null @@ -1,18 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -Proc-Type: 4,ENCRYPTED -DEK-Info: DES-EDE3-CBC,116D8984CC1AC50A - -PnDGqu3+5ITsGtOwCucdQBs7UmPpJKk3x+UuBdpJuygMEgGM70P+eN+RLTH/vaAl -GFWV9wvlL7j+azrEYlbiKhHn4+6SmDSWjjSVM5wGclzH/UYhyhVe/GZsJ8axW278 -6EdwzmrbvIjuPTN/dJjyXdeOlFFpoCST8TI03+qYo9T0L86560Y3SjTr/hHhlVyL -PgwfcN3wdarhPloJvFoV10kNH3MBpgGeclNQcNVRH7+Z2DwzHgV3bW28w1h4dOI7 -RrPpa1YaAi0lTltuiZYLUtTBI/+xEDf3kFkeSNSdl3sLp9faHUoosVObdFfLCmV1 -+66MdqgesPFipkfGPlTGuUX9CmYMooCn+hs7+tVZUqCl/fcErFWeW8iS5+nrat7f -HBiAsOTZ96AEvy/FksYPymrdaK085aODgPqSfR2pvMuF66iKS1xRZiTpMnDApTVN -A6BOZdJgTqGX4yny7ORxQ90xkv39oZYS9cc10Hqec1DG1LWy9dfvavEPk7/GejiT -Z5SMbIHHiNe5tNTomGqtgLIhjfoRXH14zbPGbJ5bI0REJ+sdUM3ItH75tTHYQUIb -S8UQBkHzU+ExK4q5E3BvKR7UH0KD5z6B6QhAyCB6mQp+63nsIP5cImXuAY9u0s1a -3tOmvUpXWDpqJLeAShb3DAPz5+FMx4mbT5oZq1Y8q5RDqMSSrB7XilAroCqasUIb -LoLNMri7WKcrCT1dKjN4y17ucwU8wLPo7Lpo+x5/XWqQA5qSB83YG9nh6nuzYNyo -aUsLH4cfAj3vCPU+KQux5jJfpcma9fyxVfCfa55dmakmGM8ww6ZXXQ== ------END RSA PRIVATE KEY----- diff --git a/examples/ssl/private/keystonekey.pem b/examples/ssl/private/keystonekey.pem deleted file mode 100644 index 563c65f02f..0000000000 --- a/examples/ssl/private/keystonekey.pem +++ /dev/null @@ -1,15 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIICXAIBAAKBgQCeWly+3CDUrzZcM21yRJRZxqkk7fqLLFOrJH15RsymRQWwV7QN -1o/02aURZOR4sSYw3vtKcsiX5zFPVbtbFtciGxPK/GsEvRWcCVHW+RRRZ6NCSoHO -mA9uXKx/Nr4Pea0HgXWiIahf5ZwicUzbY7ZEKWUidm4HmN6+WD+y/s0n9wIDAQAB -AoGAf6eY3MPYM5yL1ggfUt62OSlNcdfnAgrZ6D2iaQIKOH+r9ly9aepuYpSR3VPY -WvN0NjGLopil3M8jkTEruGLRSgin8+v+qlcRFsoXamegc3NV4XtxJhSmSIocKIIK -14w5YxcDz1QGqoati4LxQ1D6V5eNhiO65YhdcUDarGnlcAECQQDK4vcBGLY7H91f -lGT/oFJ0crqF4V+bLxMO28NhtS0G+GoM0MKrPfIu+nZDlKQzzHUlEZMNXSLz1T+T -po92UVe3AkEAx87ZKDK4xZZRNz0dAe29a3gQ6PmVkav1+NIxr0MP7Ff4tH6K/uoz -96OZpZg+TxdaoxSeNltuUelt3/xPs9AxwQJBAI01t1FuD7fLD9ssf7djsMAX8jao -jFCITS10S+K/pR1K3RUaX8OsE9oavSGAXWEoFwi72KvefStU6zErJoLlTrUCQCG5 -wmHMne+L/c1rHVhT/qMDMyd/6UUbV3tWT1ib4zYraylcKq34bikgjjCrT+kdsgjQ -1BustyRQWGF0PyfEvoECQGhVOY2byAOEau+GeTC0c3LIDoErx6WaW3d9ty3Tmx3G -Y81XHlbO4Lw2q8fWZ8Ah2ptjv2IpKj0GAGRiJ5NnPTM= ------END RSA PRIVATE KEY----- diff --git a/keystone/__init__.py b/keystone/__init__.py index e3771d3ebb..e69de29bb2 100644 --- a/keystone/__init__.py +++ b/keystone/__init__.py @@ -1,19 +0,0 @@ -# Copyright (C) 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. - -import gettext - -# This installs the _(...) function as a built-in so all other modules -# don't need to. -gettext.install('keystone') diff --git a/keystone/backends/__init__.py b/keystone/backends/__init__.py deleted file mode 100755 index 84d73d04ec..0000000000 --- a/keystone/backends/__init__.py +++ /dev/null @@ -1,56 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 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 logging - -from keystone.cfg import NoSuchOptError -from keystone import config -from keystone import utils - -LOG = logging.getLogger(__name__) - -CONF = config.CONF -DEFAULT_BACKENDS = "keystone.backends.sqlalchemy" - -#Configs applicable to all backends. -SHOULD_HASH_PASSWORD = True - - -class GroupConf(CONF.__class__): - """ Allows direct access to the values in the backend groups.""" - def __init__(self, group, *args, **kwargs): - self.group = group - super(GroupConf, self).__init__(*args, **kwargs) - - def __getattr__(self, att): - try: - # pylint: disable=W0212 - return CONF._get(att, self.group) - except NoSuchOptError: - return None - - -def configure_backends(): - """Load backends given in the 'backends' option.""" - global SHOULD_HASH_PASSWORD # pylint: disable=W0603 - SHOULD_HASH_PASSWORD = CONF.hash_password - - backend_names = CONF.backends or DEFAULT_BACKENDS - for module_name in backend_names.split(","): - backend_module = utils.import_module(module_name) - backend_conf = GroupConf(module_name) - backend_module.configure_backend(backend_conf) diff --git a/keystone/backends/api.py b/keystone/backends/api.py deleted file mode 100755 index c44805b3de..0000000000 --- a/keystone/backends/api.py +++ /dev/null @@ -1,439 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 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. - -# pylint: disable=W0603, R0921 - - -#Base APIs -class BaseUserAPI(object): - def __init__(self, *args, **kw): - pass - - def get_all(self): - """ Get all users """ - raise NotImplementedError - - def create(self, values): - """ Create a user - - The backend will assign an ID if is not passed in - - :param values: dict of user attributes (models.User works) - :returns: models.User - the created user object - - """ - raise NotImplementedError - - def get(self, id): - """ Get a user - - :param id: string - the user ID to get - :returns: models.User - the user object - - """ - raise NotImplementedError - - def get_by_name(self, name): - """ Get a user by username - - :param name: string - the user name - :returns: models.User - - """ - raise NotImplementedError - - def get_by_email(self, email): - """ Get a user by email - - :param name: string - the user email - :returns: models.User - - """ - raise NotImplementedError - - def get_page(self, marker, limit): - raise NotImplementedError - - def get_page_markers(self, marker, limit): - raise NotImplementedError - - def user_roles_by_tenant(self, user_id, tenant_id): - raise NotImplementedError - - def update(self, id, values): - """ Update a user - - :param values: dict of user attributes (models.User works) - :returns: models.User - the updated user object - - """ - raise NotImplementedError - - def delete(self, id): - """ Delete a user - - :param id: string - the user id - - """ - raise NotImplementedError - - def get_by_tenant(self, user_id, tenant_id): - """ Gets a user for a tenant - - Same as get user, but also validates the user is related to that tenant - either through the default tenant (user.tenant_id) or by role - - :param user_id: string - id of user - :param tenant_id: string - id of tenant - :returns: models.User - the user object valid on the tenant, othwerwise - None - - """ - raise NotImplementedError - - def get_by_access(self, access): - raise NotImplementedError - - def users_get_by_tenant(self, user_id, tenant_id): - raise NotImplementedError - - def user_role_add(self, values): - """ Adds a user to a role (optionally for a tenant) - 'grant' - - This creates a new UserRoleAssociation based on the passed in values - - :param values: dict of values containing user_id, role_id, and - optionally a tenant_id - - """ - raise NotImplementedError - - def users_get_page(self, marker, limit): - raise NotImplementedError - - def users_get_page_markers(self, marker, limit): - raise NotImplementedError - - def users_get_by_tenant_get_page(self, tenant_id, role_id, marker, limit): - raise NotImplementedError - - def users_get_by_tenant_get_page_markers(self, tenant_id, - role_id, marker, limit): - raise NotImplementedError - - def check_password(self, user_id, password): - """ Check a user password - - The backend should handle any encryption/decryption - - :param user_id: string - user id - :param password: string - the password to check - :returns: True/False - - """ - raise NotImplementedError - - -class BaseTokenAPI(object): - def __init__(self, *args, **kw): - pass - - def create(self, values): - raise NotImplementedError - - def get(self, id): - raise NotImplementedError - - def delete(self, id): - raise NotImplementedError - - def get_for_user(self, user_id): - raise NotImplementedError - - def get_for_user_by_tenant(self, user_id, tenant_id): - raise NotImplementedError - - def get_all(self): - raise NotImplementedError - - -class BaseTenantAPI(object): - def __init__(self, *args, **kw): - pass - - def create(self, values): - raise NotImplementedError - - def get(self, id): - raise NotImplementedError - - def get_by_name(self, name): - raise NotImplementedError - - def get_all(self): - raise NotImplementedError - - def list_for_user_get_page(self, user, marker, limit): - raise NotImplementedError - - def list_for_user_get_page_markers(self, user, marker, limit): - raise NotImplementedError - - def get_page(self, marker, limit): - raise NotImplementedError - - def get_page_markers(self, marker, limit): - raise NotImplementedError - - def update(self, id, values): - raise NotImplementedError - - def delete(self, id): - raise NotImplementedError - - def get_all_endpoints(self, tenant_id): - raise NotImplementedError - - def get_role_assignments(self, tenant_id): - raise NotImplementedError - - -class BaseRoleAPI(object): - def __init__(self, *args, **kw): - pass - - # - # Role Methods - # - def create(self, values): - raise NotImplementedError - - def delete(self, id): - raise NotImplementedError - - def get(self, id): - raise NotImplementedError - - def get_by_name(self, name): - raise NotImplementedError - - def get_by_service(self, service_id): - raise NotImplementedError - - def get_by_service_get_page(self, service_id, marker, limit): - """ Get one page of roles by service""" - raise NotImplementedError - - def get_by_service_get_page_markers(self, service_id, marker, limit): - """ Calculate pagination markers for roles by service """ - raise NotImplementedError - - def get_all(self): - raise NotImplementedError - - def get_page(self, marker, limit): - raise NotImplementedError - - def get_page_markers(self, marker, limit): - raise NotImplementedError - - # - # Role-Grant Methods - # - def rolegrant_get(self, id): - """ Get a UserRoleAssociation (role grant) by id """ - raise NotImplementedError - - def rolegrant_delete(self, id): - """ Delete a UserRoleAssociation (role grant) by id """ - raise NotImplementedError - - def rolegrant_list_by_role(self, id): - """ Get a list of all (global and tenant) grants for this role """ - raise NotImplementedError - - def rolegrant_get_by_ids(self, user_id, role_id, tenant_id): - raise NotImplementedError - - def list_global_roles_for_user(self, user_id): - """ Get a list of all global roles granted to this user. - - :param user_id: string - id of user - - """ - raise NotImplementedError - - def list_tenant_roles_for_user(self, user_id, tenant_id): - """ Get a list of all tenant roles granted to this user. - - :param user_id: string - id of user - :param tenant_id: string - id of tenant - - """ - raise NotImplementedError - - def rolegrant_get_page(self, marker, limit, user_id, tenant_id): - raise NotImplementedError - - def rolegrant_get_page_markers(self, user_id, tenant_id, marker, limit): - raise NotImplementedError - - -class BaseEndpointTemplateAPI(object): - def __init__(self, *args, **kw): - pass - - def create(self, values): - raise NotImplementedError - - def update(self, id, values): - raise NotImplementedError - - def delete(self, id): - raise NotImplementedError - - def get(self, id): - raise NotImplementedError - - def get_all(self): - raise NotImplementedError - - def get_by_service(self, service_id): - raise NotImplementedError - - def get_page(self, marker, limit): - raise NotImplementedError - - def get_page_markers(self, marker, limit): - raise NotImplementedError - - def get_by_service_get_page(self, service_id, marker, limit): - raise NotImplementedError - - def get_by_service_get_page_markers(self, service_id, marker, limit): - raise NotImplementedError - - def endpoint_get_by_tenant_get_page(self, tenant_id, marker, limit): - raise NotImplementedError - - def endpoint_get_by_tenant_get_page_markers(self, tenant_id, marker, - limit): - raise NotImplementedError - - def endpoint_get_by_endpoint_template(self, endpoint_template_id): - raise NotImplementedError - - def endpoint_add(self, values): - raise NotImplementedError - - def endpoint_get(self, id): - raise NotImplementedError - - def endpoint_get_by_tenant(self, tenant_id): - raise NotImplementedError - - def endpoint_delete(self, id): - raise NotImplementedError - - -class BaseServiceAPI(object): - def __init__(self, *args, **kw): - pass - - def create(self, values): - raise NotImplementedError - - def get(self, id): - raise NotImplementedError - - def get_by_name(self, name): - raise NotImplementedError - - def get_by_name_and_type(self, name, type): - raise NotImplementedError - - def get_all(self): - raise NotImplementedError - - def get_page(self, marker, limit): - raise NotImplementedError - - def get_page_markers(self, marker, limit): - raise NotImplementedError - - def delete(self, id): - raise NotImplementedError - - -class BaseCredentialsAPI(object): - def __init__(self, *args, **kw): - pass - - def create(self, values): - raise NotImplementedError - - def update(self, id, credential): - raise NotImplementedError - - def delete(self, id): - raise NotImplementedError - - def get(self, id): - raise NotImplementedError - - def get_all(self): - raise NotImplementedError - - def get_by_access(self, access): - raise NotImplementedError - - -#API -#TODO(Yogi) Refactor all API to separate classes specific to models. -ENDPOINT_TEMPLATE = BaseEndpointTemplateAPI() -ROLE = BaseRoleAPI() -TENANT = BaseTenantAPI() -TOKEN = BaseTokenAPI() -USER = BaseUserAPI() -SERVICE = BaseServiceAPI() -CREDENTIALS = BaseCredentialsAPI() - - -# Function to dynamically set module references. -def set_value(variable_name, value): - if variable_name == 'endpoint_template': - global ENDPOINT_TEMPLATE - ENDPOINT_TEMPLATE = value - elif variable_name == 'role': - global ROLE - ROLE = value - elif variable_name == 'tenant': - global TENANT - TENANT = value - elif variable_name == 'token': - global TOKEN - TOKEN = value - elif variable_name == 'user': - global USER - USER = value - elif variable_name == 'service': - global SERVICE - SERVICE = value - elif variable_name == 'credentials': - global CREDENTIALS - CREDENTIALS = value diff --git a/keystone/backends/backendutils.py b/keystone/backends/backendutils.py deleted file mode 100644 index 3e16f2144a..0000000000 --- a/keystone/backends/backendutils.py +++ /dev/null @@ -1,60 +0,0 @@ -import logging -logger = logging.getLogger(__name__) # pylint: disable=C0103 - -from keystone.backends import models -import keystone.backends as backends -# pylint: disable=E0611 -try: - from passlib.hash import sha512_crypt as sc -except ImportError as exc: - logger.exception(exc) - raise exc - - -def __get_hashed_password(password): - if password: - return __make_password(password) - else: - return None - - -def set_hashed_password(values): - """ - Sets hashed password for password. - """ - if backends.SHOULD_HASH_PASSWORD: - if isinstance(values, dict) and 'password' in values.keys(): - values['password'] = __get_hashed_password(values['password']) - elif isinstance(values, models.User): - values.password = __get_hashed_password(values.password) - else: - logger.warn("Could not hash password on unsupported type: %s" % - type(values)) - - -def check_password(raw_password, enc_password): - """ - Compares raw password and encoded password. - """ - if not raw_password: - return False - if backends.SHOULD_HASH_PASSWORD: - return sc.verify(raw_password, enc_password) - else: - return enc_password == raw_password - - -def __make_password(raw_password): - """ - Produce a new encoded password. - """ - if raw_password is None: - return None - hsh = __get_hexdigest(raw_password) - return '%s' % (hsh) - - -#Refer http://packages.python.org/passlib/lib/passlib.hash.sha512_crypt.html -#Using the default properties as of now.Salt gets generated automatically. -def __get_hexdigest(raw_password): - return sc.encrypt(raw_password) diff --git a/keystone/backends/ldap/__init__.py b/keystone/backends/ldap/__init__.py deleted file mode 100644 index b3e0b31b31..0000000000 --- a/keystone/backends/ldap/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 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 ldap - -import keystone.backends.api as top_api -import keystone.backends.models as top_models -from keystone import utils - -from . import api -from . import models - - -def configure_backend(conf): - api_obj = api.API(conf) - for name in api_obj.apis: - top_api.set_value(name, getattr(api_obj, name)) - for model_name in models.__all__: - top_models.set_value(model_name, getattr(models, model_name)) diff --git a/keystone/backends/ldap/api/__init__.py b/keystone/backends/ldap/api/__init__.py deleted file mode 100644 index c739b4da48..0000000000 --- a/keystone/backends/ldap/api/__init__.py +++ /dev/null @@ -1,111 +0,0 @@ -import ldap -import logging - -from .. import fakeldap -from .tenant import TenantAPI -from .user import UserAPI -from .role import RoleAPI - -LOG = logging.getLogger('keystone.backends.ldap.api') - - -def py2ldap(val): - if isinstance(val, str): - return val - elif isinstance(val, bool): - return 'TRUE' if val else 'FALSE' - else: - return str(val) - -LDAP_VALUES = { - 'TRUE': True, - 'FALSE': False, -} - - -def ldap2py(val): - try: - return LDAP_VALUES[val] - except KeyError: - pass - try: - return int(val) - except ValueError: - pass - return val - - -def safe_iter(attrs): - if attrs is None: - return - elif isinstance(attrs, list): - for e in attrs: - yield e - else: - yield attrs - - -class LDAPWrapper(object): - def __init__(self, url): - LOG.debug("LDAP init: url=%s", url) - self.conn = ldap.initialize(url) - - def simple_bind_s(self, user, password): - LOG.debug("LDAP bind: dn=%s", user) - return self.conn.simple_bind_s(user, password) - - def add_s(self, dn, attrs): - ldap_attrs = [(typ, map(py2ldap, safe_iter(values))) - for typ, values in attrs] - if LOG.isEnabledFor(logging.DEBUG): - sane_attrs = [(typ, values if typ != 'userPassword' else ['****']) - for typ, values in ldap_attrs] - LOG.debug("LDAP add: dn=%s, attrs=%s", dn, sane_attrs) - return self.conn.add_s(dn, ldap_attrs) - - def search_s(self, dn, scope, query): - if LOG.isEnabledFor(logging.DEBUG): - LOG.debug("LDAP search: dn=%s, scope=%s, query=%s", dn, - fakeldap.scope_names[scope], query) - res = self.conn.search_s(dn, scope, query) - return [(dn, dict([(typ, map(ldap2py, values)) - for typ, values in attrs.iteritems()])) - for dn, attrs in res] - - def modify_s(self, dn, modlist): - ldap_modlist = [(op, typ, None if values is None else - map(py2ldap, safe_iter(values))) - for op, typ, values in modlist] - if LOG.isEnabledFor(logging.DEBUG): - sane_modlist = [(op, typ, values if typ != 'userPassword' - else ['****']) for op, typ, values in ldap_modlist] - LOG.debug("LDAP modify: dn=%s, modlist=%s", dn, sane_modlist) - return self.conn.modify_s(dn, ldap_modlist) - - def delete_s(self, dn): - LOG.debug("LDAP delete: dn=%s", dn) - return self.conn.delete_s(dn) - - -class API(object): - apis = ['tenant', 'user', 'role'] - - def __init__(self, conf): - self.LDAP_URL = conf.ldap_url - self.LDAP_USER = conf.ldap_user - self.LDAP_PASSWORD = conf.ldap_password - self.tenant = TenantAPI(self, conf) - self.user = UserAPI(self, conf) - self.role = RoleAPI(self, conf) - - def get_connection(self, user=None, password=None): - if self.LDAP_URL.startswith('fake://'): - conn = fakeldap.initialize(self.LDAP_URL) - else: - conn = LDAPWrapper(self.LDAP_URL) - if user is None: - user = self.LDAP_USER - if password is None: - password = self.LDAP_PASSWORD - conn.simple_bind_s(user, password) - return conn diff --git a/keystone/backends/ldap/api/base.py b/keystone/backends/ldap/api/base.py deleted file mode 100644 index 1275c08dce..0000000000 --- a/keystone/backends/ldap/api/base.py +++ /dev/null @@ -1,183 +0,0 @@ -import ast -import ldap -from itertools import izip, count - - -def _get_redirect(cls, method): - # pylint: disable=W0613 - def inner(self, *args): - return getattr(cls(), method)(*args) - return inner - - -def add_redirects(loc, cls, methods): - for method in methods: - loc[method] = _get_redirect(cls, method) - - -class BaseLdapAPI(object): - DEFAULT_TREE_DN = None - DEFAULT_STRUCTURAL_CLASSES = None - DEFAULT_ID_ATTR = 'cn' - DUMB_MEMBER_DN = 'cn=dumb,dc=nonexistent' - options_name = None - object_class = 'top' - model = None - attribute_mapping = {} - attribute_ignore = [] - - def __init__(self, api, conf): - self.api = api - if self.options_name is not None: - dn = '%s_tree_dn' % self.options_name - self.tree_dn = conf[dn] or self.DEFAULT_TREE_DN - structs = '%s_structural_classes' % self.options_name - lst = conf[structs] or self.DEFAULT_STRUCTURAL_CLASSES - self.structural_classes = ast.literal_eval(str(lst)) - idatt = '%s_id_attr' % self.options_name - self.id_attr = conf[idatt] or self.DEFAULT_ID_ATTR - self.use_dumb_member = conf.use_dumb_member or True - - def _id_to_dn(self, id): - return '%s=%s,%s' % (self.id_attr, ldap.dn.escape_dn_chars(str(id)), - self.tree_dn) - - @staticmethod - def _dn_to_id(dn): - return ldap.dn.str2dn(dn)[0][0][1] - - # pylint: disable=E1102 - def _ldap_res_to_model(self, res): - obj = self.model(id=self._dn_to_id(res[0])) - obj['name'] = obj['id'] - for k in obj: - if k in self.attribute_ignore: - continue - try: - v = res[1][self.attribute_mapping.get(k, k)] - except KeyError: - pass - else: - try: - obj[k] = v[0] - except IndexError: - obj[k] = None - return obj - - # pylint: disable=E1102 - def create(self, values): - conn = self.api.get_connection() - object_classes = self.structural_classes + [self.object_class] - attrs = [('objectClass', object_classes)] - for k, v in values.iteritems(): - if k == 'id' or k in self.attribute_ignore: - continue - if v is not None: - attr_type = self.attribute_mapping.get(k, k) - attrs.append((attr_type, [v])) - if 'groupOfNames' in object_classes and self.use_dumb_member: - attrs.append(('member', [self.DUMB_MEMBER_DN])) - conn.add_s(self._id_to_dn(values['id']), attrs) - return self.model(**values) - - def _ldap_get(self, id, filter=None): - conn = self.api.get_connection() - query = '(objectClass=%s)' % (self.object_class,) - if filter is not None: - query = '(&%s%s)' % (filter, query) - try: - res = conn.search_s(self._id_to_dn(id), ldap.SCOPE_BASE, query) - except ldap.NO_SUCH_OBJECT: - return None - try: - return res[0] - except IndexError: - return None - - def _ldap_get_all(self, filter=None): - conn = self.api.get_connection() - query = '(objectClass=%s)' % (self.object_class,) - if filter is not None: - query = '(&%s%s)' % (filter, query) - try: - return conn.search_s(self.tree_dn, ldap.SCOPE_ONELEVEL, query) - except ldap.NO_SUCH_OBJECT: - return [] - - def get(self, id, filter=None): - res = self._ldap_get(id, filter) - if res is None: - return None - else: - return self._ldap_res_to_model(res) - - # pylint: disable=W0141 - def get_all(self, filter=None): - return map(self._ldap_res_to_model, self._ldap_get_all(filter)) - - def get_page(self, marker, limit): - return self._get_page(marker, limit, self.get_all()) - - def get_page_markers(self, marker, limit): - return self._get_page_markers(marker, limit, self.get_all()) - - # pylint: disable=W0141 - @staticmethod - def _get_page(marker, limit, lst, key=lambda e: e.id): - lst.sort(key=key) - if not marker: - return lst[:limit] - else: - return filter(lambda e: key(e) > marker, lst)[:limit] - - @staticmethod - def _get_page_markers(marker, limit, lst, key=lambda e: e.id): - if len(lst) < limit: - return (None, None) - lst.sort(key=key) - if marker is None: - if len(lst) <= limit + 1: - nxt = None - else: - nxt = key(lst[limit]) - return (None, nxt) - - for i, item in izip(count(), lst): - k = key(item) - if k >= marker: - break - # pylint: disable=W0631 - if i <= limit: - prv = None - else: - prv = key(lst[i - limit]) - if i + limit >= len(lst) - 1: - nxt = None - else: - nxt = key(lst[i + limit]) - return (prv, nxt) - - def update(self, id, values, old_obj=None): - if old_obj is None: - old_obj = self.get(id) - modlist = [] - for k, v in values.iteritems(): - if k == 'id' or k in self.attribute_ignore: - continue - if v is None: - if old_obj[k] is not None: - modlist.append((ldap.MOD_DELETE, - self.attribute_mapping.get(k, k), None)) - else: - if old_obj[k] != v: - if old_obj[k] is None: - op = ldap.MOD_ADD - else: - op = ldap.MOD_REPLACE - modlist.append((op, self.attribute_mapping.get(k, k), [v])) - conn = self.api.get_connection() - conn.modify_s(self._id_to_dn(id), modlist) - - def delete(self, id): - conn = self.api.get_connection() - conn.delete_s(self._id_to_dn(id)) diff --git a/keystone/backends/ldap/api/role.py b/keystone/backends/ldap/api/role.py deleted file mode 100644 index 5ec8194975..0000000000 --- a/keystone/backends/ldap/api/role.py +++ /dev/null @@ -1,288 +0,0 @@ -import ldap - -from keystone.backends.api import BaseTenantAPI -from keystone.common import exception - -from keystone import models -from .base import BaseLdapAPI - - -# pylint: disable=W0212, W0223 -class RoleAPI(BaseLdapAPI, BaseTenantAPI): - DEFAULT_TREE_DN = 'ou=Groups,dc=example,dc=com' - DEFAULT_STRUCTURAL_CLASSES = ['groupOfNames'] - options_name = 'role' - object_class = 'keystoneRole' - model = models.Role - attribute_mapping = {'description': 'desc', 'serviceId': 'service_id'} - - @staticmethod - def _create_ref(role_id, tenant_id, user_id): - role_id = '' if role_id is None else str(role_id) - tenant_id = '' if tenant_id is None else str(tenant_id) - user_id = '' if user_id is None else str(user_id) - return '%d-%d-%s%s%s' % (len(role_id), len(tenant_id), - role_id, tenant_id, user_id) - - @staticmethod - def _explode_ref(rolegrant): - a = rolegrant.split('-', 2) - len_role = int(a[0]) - len_tenant = int(a[1]) - role_id = a[2][:len_role] - role_id = None if len(role_id) == 0 else str(role_id) - tenant_id = a[2][len_role:len_tenant + len_role] - tenant_id = None if len(tenant_id) == 0 else str(tenant_id) - user_id = a[2][len_tenant + len_role:] - user_id = None if len(user_id) == 0 else str(user_id) - return role_id, tenant_id, user_id - - def _subrole_id_to_dn(self, role_id, tenant_id): - if tenant_id is None: - return self._id_to_dn(role_id) - else: - return "cn=%s,%s" % (ldap.dn.escape_dn_chars(role_id), - self.api.tenant._id_to_dn(tenant_id)) - - def get(self, id, filter=None): - model = super(RoleAPI, self).get(id, filter) - if model: - model['name'] = model['id'] - return model - - def create(self, values): - values['id'] = values['name'] - delattr(values, 'name') - - return super(RoleAPI, self).create(values) - - # pylint: disable=W0221 - def get_by_name(self, name, filter=None): - return self.get(name, filter) - - def add_user(self, role_id, user_id, tenant_id=None): - user = self.api.user.get(user_id) - if user is None: - raise exception.NotFound("User %s not found" % (user_id,)) - role_dn = self._subrole_id_to_dn(role_id, tenant_id) - conn = self.api.get_connection() - user_dn = self.api.user._id_to_dn(user_id) - try: - conn.modify_s(role_dn, [(ldap.MOD_ADD, 'member', user_dn)]) - except ldap.TYPE_OR_VALUE_EXISTS: - raise exception.Duplicate( - "User %s already has role %s in tenant %s" % (user_id, - role_id, tenant_id)) - except ldap.NO_SUCH_OBJECT: - if tenant_id is None or self.get(role_id) is None: - raise exception.NotFound("Role %s not found" % (role_id,)) - attrs = [ - ('objectClass', ['keystoneTenantRole', 'groupOfNames']), - ('member', [user_dn]), - ('keystoneRole', self._id_to_dn(role_id)), - ] - if self.use_dumb_member: - attrs[1][1].append(self.DUMB_MEMBER_DN) - conn.add_s(role_dn, attrs) - return models.UserRoleAssociation( - id=self._create_ref(role_id, tenant_id, user_id), - role_id=role_id, user_id=user_id, tenant_id=tenant_id) - - def get_by_service(self, service_id): - roles = self.get_all('(service_id=%s)' % \ - (ldap.filter.escape_filter_chars(service_id),)) - try: - res = [] - for role in roles: - res.append(role) - return res - except IndexError: - return None - - def get_role_assignments(self, tenant_id): - conn = self.api.get_connection() - query = '(objectClass=keystoneTenantRole)' - tenant_dn = self.api.tenant._id_to_dn(tenant_id) - try: - roles = conn.search_s(tenant_dn, ldap.SCOPE_ONELEVEL, query) - except ldap.NO_SUCH_OBJECT: - return [] - res = [] - for role_dn, attrs in roles: - try: - user_dns = attrs['member'] - except KeyError: - continue - for user_dn in user_dns: - if self.use_dumb_member and user_dn == self.DUMB_MEMBER_DN: - continue - user_id = self.api.user._dn_to_id(user_dn) - role_id = self._dn_to_id(role_dn) - res.append(models.UserRoleAssociation( - id=self._create_ref(role_id, tenant_id, user_id), - user_id=user_id, - role_id=role_id, - tenant_id=tenant_id)) - return res - - def list_global_roles_for_user(self, user_id): - user_dn = self.api.user._id_to_dn(user_id) - roles = self.get_all('(member=%s)' % (user_dn,)) - return [models.UserRoleAssociation( - id=self._create_ref(role.id, None, user_id), - role_id=role.id, - user_id=user_id) for role in roles] - - def list_tenant_roles_for_user(self, user_id, tenant_id=None): - conn = self.api.get_connection() - user_dn = self.api.user._id_to_dn(user_id) - query = '(&(objectClass=keystoneTenantRole)(member=%s))' % (user_dn,) - if tenant_id is not None: - tenant_dn = self.api.tenant._id_to_dn(tenant_id) - try: - roles = conn.search_s(tenant_dn, ldap.SCOPE_ONELEVEL, query) - except ldap.NO_SUCH_OBJECT: - return [] - res = [] - for role_dn, _ in roles: - role_id = self._dn_to_id(role_dn) - res.append(models.UserRoleAssociation( - id=self._create_ref(role_id, tenant_id, user_id), - user_id=user_id, - role_id=role_id, - tenant_id=tenant_id)) - return res - else: - try: - roles = conn.search_s(self.api.tenant.tree_dn, - ldap.SCOPE_SUBTREE, query) - except ldap.NO_SUCH_OBJECT: - return [] - res = [] - for role_dn, _ in roles: - role_id = self._dn_to_id(role_dn) - tenant_id = ldap.dn.str2dn(role_dn)[1][0][1] - res.append(models.UserRoleAssociation( - id=self._create_ref(role_id, tenant_id, user_id), - user_id=user_id, - role_id=role_id, - tenant_id=tenant_id)) - return res - - def rolegrant_get(self, id): - role_id, tenant_id, user_id = self._explode_ref(id) - user_dn = self.api.user._id_to_dn(user_id) - role_dn = self._subrole_id_to_dn(role_id, tenant_id) - query = '(&(objectClass=keystoneTenantRole)(member=%s))' % (user_dn,) - conn = self.api.get_connection() - try: - res = conn.search_s(role_dn, ldap.SCOPE_BASE, query) - except ldap.NO_SUCH_OBJECT: - return None - if len(res) == 0: - return None - return models.UserRoleAssociation(id=id, role_id=role_id, - tenant_id=tenant_id, user_id=user_id) - - def rolegrant_delete(self, id): - role_id, tenant_id, user_id = self._explode_ref(id) - user_dn = self.api.user._id_to_dn(user_id) - role_dn = self._subrole_id_to_dn(role_id, tenant_id) - conn = self.api.get_connection() - try: - conn.modify_s(role_dn, [(ldap.MOD_DELETE, 'member', [user_dn])]) - except ldap.NO_SUCH_ATTRIBUTE: - raise exception.NotFound("No such user in role") - - def rolegrant_get_page(self, marker, limit, user_id, tenant_id): - all_roles = [] - if tenant_id is None: - all_roles += self.list_global_roles_for_user(user_id) - else: - for tenant in self.api.tenant.get_all(): - all_roles += self.list_tenant_roles_for_user(user_id, - tenant.id) - return self._get_page(marker, limit, all_roles) - - def rolegrant_get_page_markers(self, user_id, tenant_id, marker, limit): - all_roles = [] - if tenant_id is None: - all_roles = self.list_global_roles_for_user(user_id) - else: - for tenant in self.api.tenant.get_all(): - all_roles += self.list_tenant_roles_for_user(user_id, - tenant.id) - return self._get_page_markers(marker, limit, all_roles) - - def get_by_service_get_page(self, service_id, marker, limit): - all_roles = self.get_by_service(service_id) - return self._get_page(marker, limit, all_roles) - - def get_by_service_get_page_markers(self, service_id, marker, limit): - all_roles = self.get_by_service(service_id) - return self._get_page_markers(marker, limit, all_roles) - - def rolegrant_list_by_role(self, id): - role_dn = self._id_to_dn(id) - try: - roles = self.get_all('(keystoneRole=%s)' % (role_dn,)) - except ldap.NO_SUCH_OBJECT: - return [] - res = [] - for role_dn, attrs in roles: - try: - user_dns = attrs['member'] - tenant_dns = attrs['tenant'] - except KeyError: - continue - for user_dn in user_dns: - if self.use_dumb_member and user_dn == self.DUMB_MEMBER_DN: - continue - user_id = self.api.user._dn_to_id(user_dn) - tenant_id = None - if tenant_dns is not None: - for tenant_dn in tenant_dns: - tenant_id = self.api.tenant._dn_to_id(tenant_dn) - role_id = self._dn_to_id(role_dn) - res.append(models.UserRoleAssociation( - id=self._create_ref(role_id, tenant_id, user_id), - user_id=user_id, - role_id=role_id, - tenant_id=tenant_id)) - return res - - def rolegrant_get_by_ids(self, user_id, role_id, tenant_id): - conn = self.api.get_connection() - user_dn = self.api.user._id_to_dn(user_id) - query = '(&(objectClass=keystoneTenantRole)(member=%s))' % (user_dn,) - if tenant_id is not None: - tenant_dn = self.api.tenant._id_to_dn(tenant_id) - try: - roles = conn.search_s(tenant_dn, ldap.SCOPE_ONELEVEL, query) - except ldap.NO_SUCH_OBJECT: - return None - if len(roles) == 0: - return None - for role_dn, _ in roles: - ldap_role_id = self._dn_to_id(role_dn) - if role_id == ldap_role_id: - res = models.UserRoleAssociation( - id=self._create_ref(role_id, tenant_id, user_id), - user_id=user_id, - role_id=role_id, - tenant_id=tenant_id) - return res - else: - try: - roles = self.get_all('(member=%s)' % (user_dn,)) - except ldap.NO_SUCH_OBJECT: - return None - if len(roles) == 0: - return None - for role in roles: - if role.id == role_id: - return models.UserRoleAssociation( - id=self._create_ref(role.id, None, user_id), - role_id=role.id, - user_id=user_id) - return None diff --git a/keystone/backends/ldap/api/tenant.py b/keystone/backends/ldap/api/tenant.py deleted file mode 100644 index fbc795ec77..0000000000 --- a/keystone/backends/ldap/api/tenant.py +++ /dev/null @@ -1,109 +0,0 @@ -import ldap -import uuid - -from keystone.backends.api import BaseTenantAPI -from keystone.backends.sqlalchemy.api.tenant import TenantAPI as SQLTenantAPI - -from keystone import models -from .base import BaseLdapAPI, add_redirects - - -class TenantAPI(BaseLdapAPI, BaseTenantAPI): # pylint: disable=W0223 - DEFAULT_TREE_DN = 'ou=Groups,dc=example,dc=com' - DEFAULT_STRUCTURAL_CLASSES = ['groupOfNames'] - options_name = 'tenant' - object_class = 'keystoneTenant' - model = models.Tenant - attribute_mapping = {'description': 'desc', 'enabled': 'keystoneEnabled', - 'name': 'keystoneName'} - - def get_by_name(self, name, filter=None): # pylint: disable=W0221,W0613 - tenants = self.get_all('(keystoneName=%s)' % \ - (ldap.filter.escape_filter_chars(name),)) - try: - return tenants[0] - except IndexError: - return None - - def create(self, values): - data = values.copy() - if 'id' not in data or data['id'] is None: - data['id'] = str(uuid.uuid4()) - return super(TenantAPI, self).create(data) - - def get_user_tenants(self, user_id, include_roles=True): - """Returns list of tenants a user has access to - - Always includes default tenants. - Adds role assignments if 'include_roles' is True. - """ - user_dn = self.api.user._id_to_dn(user_id) # pylint: disable=W0212 - query = '(member=%s)' % (user_dn,) - memberships = self.get_all(query) - if include_roles: - roles = self.api.role.list_tenant_roles_for_user(user_id) - for role in roles: - exists = False - for tenant in memberships: - if tenant['id'] == role.tenant_id: - exists = True - break - if not exists: - memberships.append(self.get(role.tenant_id)) - return memberships - - def list_for_user_get_page(self, user, marker, limit): - return self._get_page(marker, limit, self.get_user_tenants(user.id)) - - def list_for_user_get_page_markers(self, user, marker, limit): - return self._get_page_markers(marker, limit, - self.get_user_tenants(user.id)) - - def is_empty(self, id): - tenant = self._ldap_get(id) - members = tenant[1].get('member', []) - if self.use_dumb_member: - empty = members == [self.DUMB_MEMBER_DN] - else: - empty = len(members) == 0 - return empty and len(self.api.role.get_role_assignments(id)) == 0 - - def get_role_assignments(self, tenant_id): - return self.api.role.get_role_assignments(tenant_id) - - def add_user(self, tenant_id, user_id): - conn = self.api.get_connection() - conn.modify_s(self._id_to_dn(tenant_id), - [(ldap.MOD_ADD, 'member', - self.api.user._id_to_dn(user_id))]) # pylint: disable=W0212 - - def remove_user(self, tenant_id, user_id): - conn = self.api.get_connection() - conn.modify_s(self._id_to_dn(tenant_id), - [(ldap.MOD_DELETE, 'member', - self.api.user._id_to_dn(user_id))]) # pylint: disable=W0212 - - def get_users(self, tenant_id, role_id=None): - tenant = self._ldap_get(tenant_id) - res = [] - if not role_id: - # Get users who have default tenant mapping - for user_dn in tenant[1].get('member', []): - if self.use_dumb_member and user_dn == self.DUMB_MEMBER_DN: - continue - #pylint: disable=W0212 - res.append(self.api.user.get(self.api.user._dn_to_id(user_dn))) - rolegrants = self.api.role.get_role_assignments(tenant_id) - # Get users who are explicitly mapped via a tenant - for rolegrant in rolegrants: - if role_id is None or rolegrant.role_id == role_id: - res.append(self.api.user.get(rolegrant.user_id)) - return res - - add_redirects(locals(), SQLTenantAPI, ['get_all_endpoints']) - - def delete(self, id): - if not self.is_empty(id): - raise fault.ForbiddenFault("You may not delete a tenant that " - "contains users") - super(TenantAPI, self).delete(id) diff --git a/keystone/backends/ldap/api/user.py b/keystone/backends/ldap/api/user.py deleted file mode 100644 index 77bb6e2297..0000000000 --- a/keystone/backends/ldap/api/user.py +++ /dev/null @@ -1,124 +0,0 @@ -import ldap -import ldap.filter -import uuid - -import keystone.backends.backendutils as utils -from keystone.backends.api import BaseUserAPI -from keystone.backends.sqlalchemy.api.user import UserAPI as SQLUserAPI - -from keystone import models -from .base import BaseLdapAPI, add_redirects - - -class UserAPI(BaseLdapAPI, BaseUserAPI): - DEFAULT_TREE_DN = 'ou=Users,dc=example,dc=com' - DEFAULT_STRUCTURAL_CLASSES = ['keystoneUidObject'] - DEFAULT_ID_ATTR = 'uid' - options_name = 'user' - object_class = 'keystoneUser' - model = models.User - attribute_mapping = { - 'password': 'userPassword', - 'email': 'mail', - 'enabled': 'keystoneEnabled', - 'name': 'keystoneName', - } - attribute_ignore = ['tenant_id'] - - def _ldap_res_to_model(self, res): - obj = super(UserAPI, self)._ldap_res_to_model(res) - tenants = self.api.tenant.get_user_tenants(obj.id, False) - if len(tenants) > 0: - obj.tenant_id = tenants[0].id - return obj - - def get_by_name(self, name, filter=None): - users = self.get_all('(keystoneName=%s)' % \ - (ldap.filter.escape_filter_chars(name),)) - try: - return users[0] - except IndexError: - return None - - def create(self, values): - values['id'] = str(uuid.uuid4()) - utils.set_hashed_password(values) - values = super(UserAPI, self).create(values) - if values['tenant_id'] is not None: - self.api.tenant.add_user(values['tenant_id'], values['id']) - return values - - def update(self, id, values): - old_obj = self.get(id) - try: - new_tenant = values['tenant_id'] - except KeyError: - pass - else: - if old_obj.tenant_id != new_tenant: - if old_obj.tenant_id: - self.api.tenant.remove_user(old_obj.tenant_id, id) - if new_tenant: - self.api.tenant.add_user(new_tenant, id) - utils.set_hashed_password(values) - super(UserAPI, self).update(id, values, old_obj) - - def delete(self, id): - user = self.get(id) - if user.tenant_id: - self.api.tenant.remove_user(user.tenant_id, id) - super(UserAPI, self).delete(id) - for ref in self.api.role.list_global_roles_for_user(id): - self.api.role.rolegrant_delete(ref.id) - for ref in self.api.role.list_tenant_roles_for_user(id): - self.api.role.rolegrant_delete(ref.id) - - def get_by_email(self, email): - users = self.get_all('(mail=%s)' % \ - (ldap.filter.escape_filter_chars(email),)) - try: - return users[0] - except IndexError: - return None - - def user_roles_by_tenant(self, user_id, tenant_id): - return self.api.role.list_tenant_roles_for_user(user_id, tenant_id) - - def get_by_tenant(self, user_id, tenant_id): - user_dn = self._id_to_dn(user_id) - user = self.get(user_id) - tenant = self.api.tenant._ldap_get(tenant_id, - '(member=%s)' % (user_dn,)) - if tenant is not None: - return user - else: - if self.api.role.list_tenant_roles_for_user(user_id, tenant_id): - return user - return None - - def user_role_add(self, values): - return self.api.role.add_user(values.role_id, values.user_id, - values.tenant_id) - - def users_get_page(self, marker, limit): - return self.get_page(marker, limit) - - def users_get_page_markers(self, marker, limit): - return self.get_page_markers(marker, limit) - - def users_get_by_tenant_get_page(self, tenant_id, role_id, marker, limit): - return self._get_page(marker, limit, - self.api.tenant.get_users(tenant_id, role_id)) - - def users_get_by_tenant_get_page_markers(self, tenant_id, - role_id, marker, limit): - return self._get_page_markers(marker, limit, - self.api.tenant.get_users(tenant_id, role_id)) - - def check_password(self, user_id, password): - user = self.get(user_id) - return utils.check_password(password, user.password) - - add_redirects(locals(), SQLUserAPI, ['get_by_group', 'tenant_group', - 'tenant_group_delete', 'user_groups_get_all', - 'users_tenant_group_get_page', 'users_tenant_group_get_page_markers']) diff --git a/keystone/backends/ldap/fakeldap.py b/keystone/backends/ldap/fakeldap.py deleted file mode 100644 index ee80d15a9a..0000000000 --- a/keystone/backends/ldap/fakeldap.py +++ /dev/null @@ -1,315 +0,0 @@ -# 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. -"""Fake LDAP server for test harness. - -This class does very little error checking, and knows nothing about ldap -class definitions. It implements the minimum emulation of the python ldap -library to work with nova. - -""" - -import logging -import re -import shelve - -import ldap - - -scope_names = { - ldap.SCOPE_BASE: 'SCOPE_BASE', - ldap.SCOPE_ONELEVEL: 'SCOPE_ONELEVEL', - ldap.SCOPE_SUBTREE: 'SCOPE_SUBTREE', -} - - -LOG = logging.getLogger('keystone.backends.ldap.fakeldap') - - -def initialize(uri): - """Opens a fake connection with an LDAP server.""" - return FakeLDAP(uri) - - -def _match_query(query, attrs): - """Match an ldap query to an attribute dictionary. - - The characters &, |, and ! are supported in the query. No syntax checking - is performed, so malformed querys will not work correctly. - """ - # cut off the parentheses - inner = query[1:-1] - if inner.startswith('&'): - # cut off the & - l, r = _paren_groups(inner[1:]) - return _match_query(l, attrs) and _match_query(r, attrs) - if inner.startswith('|'): - # cut off the | - l, r = _paren_groups(inner[1:]) - return _match_query(l, attrs) or _match_query(r, attrs) - if inner.startswith('!'): - # cut off the ! and the nested parentheses - return not _match_query(query[2:-1], attrs) - - (k, _sep, v) = inner.partition('=') - return _match(k, v, attrs) - - -def _paren_groups(source): - """Split a string into parenthesized groups.""" - count = 0 - start = 0 - result = [] - for pos in xrange(len(source)): - if source[pos] == '(': - if count == 0: - start = pos - count += 1 - if source[pos] == ')': - count -= 1 - if count == 0: - result.append(source[start:pos + 1]) - return result - - -def _match(key, value, attrs): - """Match a given key and value against an attribute list.""" - if key not in attrs: - return False - # This is a wild card search. Implemented as all or nothing for now. - if value == "*": - return True - if key == 'serviceId': - # for serviceId, the backend is returning a list of numbers - # make sure we convert them to strings first before comparing - # them - str_sids = map(lambda x: str(x), attrs[key]) - return str(value) in str_sids - if key != "objectclass": - return value in attrs[key] - # it is an objectclass check, so check subclasses - values = _subs(value) - for v in values: - if v in attrs[key]: - return True - return False - - -def _subs(value): - """Returns a list of subclass strings. - - The strings represent the ldap objectclass plus any subclasses that - inherit from it. Fakeldap doesn't know about the ldap object structure, - so subclasses need to be defined manually in the dictionary below. - - """ - subs = {'groupOfNames': [ - 'keystoneTenant', - 'keystoneRole', - 'keystoneTenantRole']} - if value in subs: - return [value] + subs[value] - return [value] - - -server_fail = False - - -class FakeShelve(dict): - @classmethod - def get_instance(cls): - try: - return cls.__instance - except AttributeError: - cls.__instance = cls() - return cls.__instance - - def sync(self): - pass - - -class FakeLDAP(object): - """Fake LDAP connection.""" - - def __init__(self, url): - LOG.debug("FakeLDAP initialize url=%s" % (url,)) - if url == 'fake://memory': - self.db = FakeShelve.get_instance() - else: - self.db = shelve.open(url[7:]) - - def simple_bind_s(self, dn, password): - """This method is ignored, but provided for compatibility.""" - if server_fail: - raise ldap.SERVER_DOWN - LOG.debug("FakeLDAP bind dn=%s" % (dn,)) - if dn == 'cn=Admin' and password == 'password': - return - try: - attrs = self.db["%s%s" % (self.__prefix, dn)] - except KeyError: - LOG.error("FakeLDAP bind fail: dn=%s not found" % (dn,)) - raise ldap.NO_SUCH_OBJECT - db_passwd = None - try: - db_passwd = attrs['userPassword'][0] - except (KeyError, IndexError): - LOG.error("FakeLDAP bind fail: password for dn=%s not found" % dn) - raise ldap.INAPPROPRIATE_AUTH - if db_passwd != password: - LOG.error("FakeLDAP bind fail: password for dn=%s does not match" % - dn) - raise ldap.INVALID_CREDENTIALS - - def unbind_s(self): - """This method is ignored, but provided for compatibility.""" - if server_fail: - raise ldap.SERVER_DOWN - - def add_s(self, dn, attrs): - """Add an object with the specified attributes at dn.""" - if server_fail: - raise ldap.SERVER_DOWN - - key = "%s%s" % (self.__prefix, dn) - LOG.debug("FakeLDAP add item: dn=%s, attrs=%s" % (dn, attrs)) - if key in self.db: - LOG.error( - "FakeLDAP add item failed: dn '%s' is already in store." % dn) - raise ldap.ALREADY_EXISTS(dn) - self.db[key] = dict([(k, v if isinstance(v, list) else [v]) - for k, v in attrs]) - self.db.sync() - - def delete_s(self, dn): - """Remove the ldap object at specified dn.""" - if server_fail: - raise ldap.SERVER_DOWN - - key = "%s%s" % (self.__prefix, dn) - LOG.debug("FakeLDAP delete item: dn=%s" % (dn,)) - try: - del self.db[key] - except KeyError: - LOG.error("FakeLDAP delete item failed: dn '%s' not found." % dn) - raise ldap.NO_SUCH_OBJECT - self.db.sync() - - def modify_s(self, dn, attrs): - """Modify the object at dn using the attribute list. - - :param dn: an LDAP DN - :param attrs: a list of tuples in the following form: - ([MOD_ADD | MOD_DELETE | MOD_REPACE], attribute, value) - """ - if server_fail: - raise ldap.SERVER_DOWN - - key = "%s%s" % (self.__prefix, dn) - LOG.debug("FakeLDAP modify item: dn=%s attrs=%s" % (dn, attrs)) - try: - entry = self.db[key] - except KeyError: - LOG.error("FakeLDAP modify item failed: dn '%s' not found." % dn) - raise ldap.NO_SUCH_OBJECT - - for cmd, k, v in attrs: - values = entry.setdefault(k, []) - if cmd == ldap.MOD_ADD: - if isinstance(v, list): - values += v - else: - values.append(v) - elif cmd == ldap.MOD_REPLACE: - values[:] = v if isinstance(v, list) else [v] - elif cmd == ldap.MOD_DELETE: - if v is None: - if len(values) == 0: - LOG.error("FakeLDAP modify item failed: " - "item has no attribute '%s' to delete" % k) - raise ldap.NO_SUCH_ATTRIBUTE - values[:] = [] - else: - if not isinstance(v, list): - v = [v] - for val in v: - try: - values.remove(val) - except ValueError: - LOG.error("FakeLDAP modify item failed: " - "item has no attribute '%s' with value '%s'" - " to delete" % (k, val)) - raise ldap.NO_SUCH_ATTRIBUTE - else: - LOG.error("FakeLDAP modify item failed: unknown command %s" - % (cmd,)) - raise NotImplementedError(\ - "modify_s action %s not implemented" % (cmd,)) - self.db[key] = entry - self.db.sync() - - def search_s(self, dn, scope, query=None, fields=None): - """Search for all matching objects under dn using the query. - - Args: - dn -- dn to search under - scope -- only SCOPE_BASE and SCOPE_SUBTREE are supported - query -- query to filter objects by - fields -- fields to return. Returns all fields if not specified - - """ - if server_fail: - raise ldap.SERVER_DOWN - - LOG.debug("FakeLDAP search at dn=%s scope=%s query='%s'" % - (dn, scope_names.get(scope, scope), query)) - if scope == ldap.SCOPE_BASE: - try: - item_dict = self.db["%s%s" % (self.__prefix, dn)] - except KeyError: - LOG.debug("FakeLDAP search fail: dn not found for SCOPE_BASE") - raise ldap.NO_SUCH_OBJECT - results = [(dn, item_dict)] - elif scope == ldap.SCOPE_SUBTREE: - results = [(k[len(self.__prefix):], v) - for k, v in self.db.iteritems() - if re.match("%s.*,%s" % (self.__prefix, dn), k)] - elif scope == ldap.SCOPE_ONELEVEL: - results = [(k[len(self.__prefix):], v) - for k, v in self.db.iteritems() - if re.match("%s\w+=[^,]+,%s" % (self.__prefix, dn), k)] - else: - LOG.error("FakeLDAP search fail: unknown scope %s" % (scope,)) - raise NotImplementedError("Search scope %s not implemented." % - (scope,)) - - objects = [] - for dn, attrs in results: - # filter the objects by query - if not query or _match_query(query, attrs): - # filter the attributes by fields - attrs = dict([(k, v) for k, v in attrs.iteritems() - if not fields or k in fields]) - objects.append((dn, attrs)) - # pylint: enable=E1103 - LOG.debug("FakeLDAP search result: %s" % (objects,)) - return objects - - @property - def __prefix(self): # pylint: disable=R0201 - """Get the prefix to use for all keys.""" - return 'ldap:' diff --git a/keystone/backends/ldap/keystone.ldif b/keystone/backends/ldap/keystone.ldif deleted file mode 100644 index 831b161c36..0000000000 --- a/keystone/backends/ldap/keystone.ldif +++ /dev/null @@ -1,74 +0,0 @@ -dn: cn=keystone,cn=schema,cn=config -objectClass: olcSchemaConfig -cn: keystone -olcAttributeTypes: ( - 1.3.6.1.3.1.666.667.3.1 - NAME 'keystoneEnabled' - EQUALITY booleanMatch - SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 - SINGLE-VALUE - ) -olcAttributeTypes: ( - 1.3.6.1.3.1.666.667.3.2 - NAME 'keystoneTenant' - SUP distinguishedName - SINGLE-VALUE - ) -olcAttributeTypes: ( - 1.3.6.1.3.1.666.667.3.3 - NAME 'keystoneRole' - SUP distinguishedName - SINGLE-VALUE - ) -olcAttributeTypes: ( - 1.3.6.1.3.1.666.667.3.4 - NAME 'serviceId' - EQUALITY caseExactIA5Match - SUBSTR caseExactIA5SubstringsMatch - SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 - SINGLE-VALUE - ) -olcAttributeTypes: ( - 1.3.6.1.3.1.666.667.3.5 - NAME 'keystoneName' - SUP name - SINGLE-VALUE - ) -olcObjectClasses: ( - 1.3.6.1.3.1.666.667.4.1 - NAME 'keystoneUidObject' - SUP top - STRUCTURAL - MUST ( uid ) - ) -olcObjectClasses: ( - 1.3.6.1.3.1.666.667.4.2 - NAME 'keystoneUser' - SUP top - AUXILIARY - MUST ( keystoneName $ keystoneEnabled ) - MAY ( mail $ userPassword ) - ) -olcObjectClasses: ( - 1.3.6.1.3.1.666.667.4.3 - NAME 'keystoneRole' - SUP top - AUXILIARY - MAY ( member $ description $ serviceId ) - ) -olcObjectClasses: ( - 1.3.6.1.3.1.666.667.4.4 - NAME 'keystoneTenant' - SUP top - AUXILIARY - MUST ( keystoneName $ keystoneEnabled ) - MAY ( member $ description ) - ) -olcObjectClasses: ( - 1.3.6.1.3.1.666.667.4.5 - NAME 'keystoneTenantRole' - SUP top - AUXILIARY - MUST ( keystoneRole ) - MAY ( member ) - ) diff --git a/keystone/backends/ldap/keystone.schema b/keystone/backends/ldap/keystone.schema deleted file mode 100644 index 68f0d56210..0000000000 --- a/keystone/backends/ldap/keystone.schema +++ /dev/null @@ -1,83 +0,0 @@ -objectidentifier keystoneSchema 1.3.6.1.3.1.666.667 -objectidentifier keystoneAttrs keystoneSchema:3 -objectidentifier keystoneOCs keystoneSchema:4 - -attributetype ( - keystoneAttrs:1 - NAME 'keystoneEnabled' - EQUALITY booleanMatch - SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 - SINGLE-VALUE - ) - -attributetype ( - keystoneAttrs:2 - NAME 'keystoneTenant' - SUP distinguishedName - SINGLE-VALUE - ) - -attributetype ( - keystoneAttrs:3 - NAME 'keystoneRole' - SUP distinguishedName - SINGLE-VALUE - ) - -attributetype ( - keystoneAttrs:4 - NAME 'serviceId' - EQUALITY caseExactIA5Match - SUBSTR caseExactIA5SubstringsMatch - SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 - SINGLE-VALUE - ) -attributetype: ( - keystoneAttrs:5 - NAME 'keystoneName' - SUP name - SINGLE-VALUE - ) -objectClass ( - keystoneOCs:1 - NAME 'keystoneUidObject' - SUP top - STRUCTURAL - MUST ( uid ) - ) - -objectClass ( - keystoneOCs:2 - NAME 'keystoneUser' - SUP top - AUXILIARY - MUST ( keystoneName $ keystoneEnabled ) - MAY ( mail $ userPassword ) - ) - -objectClass ( - keystoneOCs:3 - NAME 'keystoneRole' - SUP top - AUXILIARY - MUST ( cn ) - MAY ( member $ description $ serviceId ) - ) - -objectClass ( - keystoneOCs:4 - NAME 'keystoneTenant' - SUP top - AUXILIARY - MUST ( keystoneName $ keystoneEnabled ) - MAY ( member $ description ) - ) - -objectClass ( - keystoneOCs:5 - NAME 'keystoneTenantRole' - SUP top - AUXILIARY - MUST ( keystoneRole ) - MAY ( member ) - ) diff --git a/keystone/backends/ldap/models.py b/keystone/backends/ldap/models.py deleted file mode 100644 index d0c9ffcaaa..0000000000 --- a/keystone/backends/ldap/models.py +++ /dev/null @@ -1,52 +0,0 @@ -from collections import Mapping - -__all__ = ['UserRoleAssociation', 'Role', 'Tenant', 'User'] - - -def create_model(name, attrs): - class Cmapper(Mapping): - __slots__ = attrs - - def __init__(self, arg=None, **kwargs): - if arg is None: - arg = kwargs - if isinstance(arg, dict): - missed_attrs = set(attrs) - for k, v in kwargs.iteritems(): - setattr(self, k, v) - missed_attrs.remove(k) - for name in missed_attrs: - setattr(self, name, None) - elif isinstance(arg, C): - for name in attrs: - setattr(self, name, getattr(arg, name)) - else: - raise ValueError - - def __getitem__(self, name): - return getattr(self, name) - - def __setitem__(self, name, value): - return setattr(self, name, value) - - def __iter__(self): - return iter(attrs) - - def __len__(self): - return len(attrs) - Cmapper.__name__ = name - return Cmapper - - -UserRoleAssociation = create_model( - 'UserRoleAssociation', ['id', 'user_id', 'role_id', 'tenant_id']) -Role = create_model( - 'Role', ['id', 'desc', 'service_id']) -Tenant = create_model( - 'Tenant', ['id', 'name', 'desc', 'enabled']) -User = create_model( - 'User', ['id', 'name', 'password', 'email', 'enabled', 'tenant_id']) -#Endpoints = create_model( -# 'Endpoints', ['id', 'tenant_id', 'endpoint_template_id']) -#Credentials = create_model( -# 'Credentials', ['id', 'user_id', 'type', 'key', 'secret']) diff --git a/keystone/backends/memcache/__init__.py b/keystone/backends/memcache/__init__.py deleted file mode 100755 index 084c36d6cd..0000000000 --- a/keystone/backends/memcache/__init__.py +++ /dev/null @@ -1,81 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 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 ast -import logging - -from keystone.common import config -from keystone.backends.memcache import models -import keystone.utils as utils -import keystone.backends.api as top_api -import keystone.backends.models as top_models -import memcache - -MODEL_PREFIX = 'keystone.backends.memcache.models.' -API_PREFIX = 'keystone.backends.memcache.api.' -MEMCACHE_SERVER = None -CACHE_TIME = 86400 - - -def configure_backend(options): - hosts = options['memcache_hosts'] - global MEMCACHE_SERVER - if not MEMCACHE_SERVER: - MEMCACHE_SERVER = Memcache_Server(hosts) - register_models(options) - global CACHE_TIME - CACHE_TIME = config.get_option( - options, 'cache_time', type='int', default=86400) - - -class Memcache_Server(): - def __init__(self, hosts): - self.hosts = hosts - self.server = memcache.Client([self.hosts]) - - def set(self, key, value, expiry=CACHE_TIME): - """ - This method is used to set a new value - in the memcache server. - """ - self.server.set(key.encode('utf-8'), value, expiry) - - def get(self, key): - """ - This method is used to retrieve a value - from the memcache server - """ - return self.server.get(key.encode('utf-8')) - - def delete(self, key): - """ - This method is used to delete a value from the - memcached server. Lazy delete - """ - self.server.delete(key.encode('utf-8')) - - -def register_models(options): - """Register Models and create properties""" - supported_memcache_models = ast.literal_eval( - options["backend_entities"]) - for supported_memcache_model in supported_memcache_models: - model = utils.import_module(MODEL_PREFIX + supported_memcache_model) - top_models.set_value(supported_memcache_model, model) - if model.__api__ is not None: - model_api = utils.import_module(API_PREFIX + model.__api__) - top_api.set_value(model.__api__, model_api.get()) diff --git a/keystone/backends/memcache/api/__init__.py b/keystone/backends/memcache/api/__init__.py deleted file mode 100644 index 86d2910ec4..0000000000 --- a/keystone/backends/memcache/api/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 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 . import token diff --git a/keystone/backends/memcache/api/token.py b/keystone/backends/memcache/api/token.py deleted file mode 100755 index 4bbb5fa36a..0000000000 --- a/keystone/backends/memcache/api/token.py +++ /dev/null @@ -1,74 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 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 keystone.backends.memcache import MEMCACHE_SERVER -from keystone.backends.api import BaseTokenAPI - - -# pylint: disable=W0223 -class TokenAPI(BaseTokenAPI): - def __init__(self, *args, **kw): - super(TokenAPI, self).__init__(*args, **kw) - - def create(self, token): - if not hasattr(token, 'tenant_id'): - token.tenant_id = None - if token.tenant_id is not None: - tenant_user_key = "%s::%s" % (token.tenant_id, token.user_id) - else: - tenant_user_key = "U%s" % token.user_id - - MEMCACHE_SERVER.set(token.id, token) - MEMCACHE_SERVER.set(tenant_user_key, token) - - def get(self, id): - token = MEMCACHE_SERVER.get(id) - if token is not None and not hasattr(token, 'tenant_id'): - token.tenant_id = None - return token - - # pylint: disable=E1103 - def delete(self, id): - token = self.get(id) - if token is not None: - MEMCACHE_SERVER.delete(id) - if token is not None and not hasattr(token, 'tenant_id'): - token.tenant_id = None - if token.tenant_id is not None: - MEMCACHE_SERVER.delete("%s::%s" % (token.tenant_id, - token.user_id)) - else: - MEMCACHE_SERVER.delete(token.id) - MEMCACHE_SERVER.delete("U%s" % token.user_id) - - def get_for_user(self, user_id): - token = MEMCACHE_SERVER.get("U%s" % user_id) - if token is not None and not hasattr(token, 'tenant_id'): - token.tenant_id = None - return token - - def get_for_user_by_tenant(self, user_id, tenant_id): - if tenant_id is not None: - token = MEMCACHE_SERVER.get("%s::%s" % (tenant_id, user_id)) - else: - token = MEMCACHE_SERVER.get("U%s" % user_id) - if token is not None and not hasattr(token, 'tenant_id'): - token.tenant_id = None - return token - - -def get(): - return TokenAPI() diff --git a/keystone/backends/memcache/models.py b/keystone/backends/memcache/models.py deleted file mode 100755 index 58dc3f0384..0000000000 --- a/keystone/backends/memcache/models.py +++ /dev/null @@ -1,19 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 2010-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. - - -class Token(): - __api__ = 'token' diff --git a/keystone/backends/models.py b/keystone/backends/models.py deleted file mode 100755 index 10ac62673d..0000000000 --- a/keystone/backends/models.py +++ /dev/null @@ -1,60 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 2010-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. - -# pylint: disable=C0103,W0603 -#Current Models -UserRoleAssociation = None -Endpoints = None -Role = None -Tenant = None -User = None -Credentials = None -Token = None -EndpointTemplates = None -Service = None - - -# Function to dynamically set model references. -def set_value(variable_name, value): - if variable_name == 'UserRoleAssociation': - global UserRoleAssociation - UserRoleAssociation = value - elif variable_name == 'Endpoints': - global Endpoints - Endpoints = value - elif variable_name == 'Role': - global Role - Role = value - elif variable_name == 'Tenant': - global Tenant - Tenant = value - elif variable_name == 'User': - global User - User = value - elif variable_name == 'Credentials': - global Credentials - Credentials = value - elif variable_name == 'Token': - global Token - Token = value - elif variable_name == 'EndpointTemplates': - global EndpointTemplates - EndpointTemplates = value - elif variable_name == 'Service': - global Service - Service = value - else: - raise IndexError("Unrecognized model type: %s" % variable_name) diff --git a/keystone/backends/sqlalchemy/__init__.py b/keystone/backends/sqlalchemy/__init__.py deleted file mode 100755 index cea111c1ff..0000000000 --- a/keystone/backends/sqlalchemy/__init__.py +++ /dev/null @@ -1,169 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 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. - -# pylint: disable=W0602,W0603 - -from sqlalchemy.orm import joinedload, aliased, sessionmaker - -import ast -import logging -import os -import sys - -from sqlalchemy import create_engine -from sqlalchemy.pool import StaticPool - -try: - # pylint: disable=E0611 - from migrate.versioning import exceptions as versioning_exceptions -except ImportError: - from migrate import exceptions as versioning_exceptions - -from keystone import utils -from keystone.backends.sqlalchemy import models -from keystone.backends.sqlalchemy import migration -import keystone.backends.api as top_api -import keystone.backends.models as top_models - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - -_DRIVER = None - - -class Driver(): - def __init__(self, conf): - self.session = None - self._engine = None - self.connection_str = conf.sql_connection - model_list = ast.literal_eval(conf.backend_entities) - self._init_engine(model_list) - self._init_models(model_list) - self._init_session_maker() - - def _init_engine(self, model_list): - logger.info("Initializing sqlalchemy backend: %s" % \ - self.connection_str) - if self.connection_str == "sqlite://": - # initialize in-memory sqlite (i.e. for testing) - self._engine = create_engine( - self.connection_str, - connect_args={'check_same_thread': False}, - poolclass=StaticPool) - - # TODO(dolph): we should be using version control, but - # we don't have a way to pass our in-memory instance to - # the versioning api - self._init_tables(model_list) - else: - # initialize a "real" database - self._engine = create_engine( - self.connection_str, - pool_recycle=3600) - self._init_version_control() - self._init_tables(model_list) - - def _init_version_control(self): - """Verify the state of the database""" - repo_path = migration.get_migrate_repo_path() - - try: - repo_version = migration.get_repo_version(repo_path) - db_version = migration.get_db_version(self._engine, repo_path) - - if repo_version != db_version: - msg = ('Database (%s) is not up to date (current=%s, ' - 'latest=%s); run `keystone-manage sync_database` or ' - 'override your migrate version manually (see docs)' % - (self.connection_str, db_version, repo_version)) - logging.warning(msg) - raise Exception(msg) - except versioning_exceptions.DatabaseNotControlledError: - msg = ('Database (%s) is not version controlled; ' - 'run `keystone-manage sync_database` or ' - 'override your migrate version manually (see docs)' % - (self.connection_str)) - logging.warning(msg) - - @staticmethod - def _init_models(model_list): - for model in model_list: - model_class = getattr(models, model) - top_models.set_value(model, model_class) - - if model_class.__api__ is not None: - api_path = '.'.join([__package__, 'api', model_class.__api__]) - api_module = sys.modules.get(api_path) - if api_module is None: - api_module = utils.import_module(api_path) - top_api.set_value(model_class.__api__, api_module.get()) - - def _init_tables(self, model_list): - tables = [] - - for model in model_list: - model_class = getattr(models, model) - tables.append(model_class.__table__) - - tables_to_create = [] - for table in reversed(models.Base.metadata.sorted_tables): - if table in tables: - tables_to_create.append(table) - - logger.debug('Creating tables: %s' % \ - ','.join([table.name for table in tables_to_create])) - models.Base.metadata.create_all(self._engine, tables=tables_to_create, - checkfirst=True) - - def _init_session_maker(self): - self.session = sessionmaker( - bind=self._engine, - autocommit=True, - expire_on_commit=False) - - def get_session(self): - """Creates a pre-configured database session""" - return self.session() - - def reset(self): - """Unregister models and reset DB engine. - - Useful clearing out data before testing - - TODO(dolph):: - - ... but what does this *do*? Issue DROP TABLE statements? - TRUNCATE TABLE? Or is the scope of impact limited to python? - """ - if self._engine is not None: - models.Base.metadata.drop_all(self._engine) - self._engine = None - - -def configure_backend(conf): - global _DRIVER - _DRIVER = Driver(conf) - - -def get_session(): - global _DRIVER - return _DRIVER.get_session() - - -def unregister_models(): - global _DRIVER - if _DRIVER: - return _DRIVER.reset() diff --git a/keystone/backends/sqlalchemy/api/credentials.py b/keystone/backends/sqlalchemy/api/credentials.py deleted file mode 100755 index 2210569446..0000000000 --- a/keystone/backends/sqlalchemy/api/credentials.py +++ /dev/null @@ -1,137 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# 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 keystone.backends.sqlalchemy import get_session, models -from keystone.backends import api -from keystone.models import Credentials -from keystone.logic.types import fault - - -# pylint: disable=E1103,W0221 -class CredentialsAPI(api.BaseCredentialsAPI): - def __init__(self, *args, **kw): - super(CredentialsAPI, self).__init__(*args, **kw) - - @staticmethod - def transpose(ref): - """ Transposes field names from domain to sql model""" - if hasattr(api.TENANT, 'uid_to_id'): - if 'tenant_id' in ref: - ref['tenant_id'] = api.TENANT.uid_to_id(ref['tenant_id']) - elif hasattr(ref, 'tenant_id'): - ref.tenant_id = api.TENANT.uid_to_id(ref.tenant_id) - - if hasattr(api.USER, 'uid_to_id'): - if 'user_id' in ref: - ref['user_id'] = api.USER.uid_to_id(ref['user_id']) - elif hasattr(ref, 'tenant_id'): - ref.user_id = api.USER.uid_to_id(ref.user_id) - - @staticmethod - def to_model(ref): - """ Returns Keystone model object based on SQLAlchemy model""" - if ref: - if hasattr(api.TENANT, 'uid_to_id'): - if 'tenant_id' in ref: - ref['tenant_id'] = api.TENANT.id_to_uid(ref['tenant_id']) - elif hasattr(ref, 'tenant_id'): - ref.tenant_id = api.TENANT.id_to_uid(ref.tenant_id) - - if hasattr(api.USER, 'uid_to_id'): - if 'user_id' in ref: - ref['user_id'] = api.USER.id_to_uid(ref['user_id']) - elif hasattr(ref, 'user_id'): - ref.user_id = api.USER.id_to_uid(ref.user_id) - - return Credentials(id=ref.id, user_id=ref.user_id, - tenant_id=ref.tenant_id, type=ref.type, key=ref.key, - secret=ref.secret) - - @staticmethod - def to_model_list(refs): - return [CredentialsAPI.to_model(ref) for ref in refs] - - def create(self, values): - data = values.copy() - CredentialsAPI.transpose(data) - - if 'tenant_id' in values: - if data['tenant_id'] is None and values['tenant_id'] is not None: - raise fault.ItemNotFoundFault('Invalid tenant id: %s' % \ - values['tenant_id']) - - credentials_ref = models.Credentials() - credentials_ref.update(data) - credentials_ref.save() - - return CredentialsAPI.to_model(credentials_ref) - - @staticmethod - def update(id, values, session=None): - if not session: - session = get_session() - - CredentialsAPI.transpose(values) - - with session.begin(): - ref = session.query(models.Credentials).filter_by(id=id).first() - ref.update(values) - ref.save(session=session) - - def get(self, id, session=None): - result = self._get(id, session) - - return CredentialsAPI.to_model(result) - - @staticmethod - def _get(id, session=None): - if id is None: - return None - - session = session or get_session() - - return session.query(models.Credentials).filter_by(id=id).first() - - @staticmethod - def get_all(session=None): - if not session: - session = get_session() - - results = session.query(models.Credentials).all() - - return CredentialsAPI.to_model_list(results) - - def get_by_access(self, access, session=None): - if not session: - session = get_session() - - result = session.query(models.Credentials).\ - filter_by(type="EC2", key=access).first() - - return CredentialsAPI.to_model(result) - - def delete(self, id, session=None): - if not session: - session = get_session() - - with session.begin(): - group_ref = self._get(id, session) - session.delete(group_ref) - - -def get(): - return CredentialsAPI() diff --git a/keystone/backends/sqlalchemy/api/endpoint_template.py b/keystone/backends/sqlalchemy/api/endpoint_template.py deleted file mode 100755 index 84fce4549e..0000000000 --- a/keystone/backends/sqlalchemy/api/endpoint_template.py +++ /dev/null @@ -1,381 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 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 keystone.backends.sqlalchemy import get_session, models, aliased -from keystone.backends import api - - -# pylint: disable=E1103,W0221 -class EndpointTemplateAPI(api.BaseEndpointTemplateAPI): - def __init__(self, *args, **kw): - super(EndpointTemplateAPI, self).__init__(*args, **kw) - - @staticmethod - def transpose(values): - """ Transposes field names from domain to sql model""" - pass - - @staticmethod - def to_model(ref): - """ Returns Keystone model object based on SQLAlchemy model""" - pass - - @staticmethod - def to_model_list(refs): - return [EndpointTemplateAPI.to_model(ref) for ref in refs] - - def create(self, values): - endpoint_template = models.EndpointTemplates() - endpoint_template.update(values) - endpoint_template.save() - return endpoint_template - - def update(self, id, values, session=None): - if not session: - session = get_session() - with session.begin(): - ref = self.get(id, session) - ref.update(values) - ref.save(session=session) - return ref - - def delete(self, id, session=None): - if not session: - session = get_session() - with session.begin(): - endpoint_template = self.get(id, session) - session.delete(endpoint_template) - - def get(self, id, session=None): - if id is None: - return None - - session = session or get_session() - - return session.query(models.EndpointTemplates).\ - filter_by(id=id).first() - - def get_all(self, session=None): - if not session: - session = get_session() - - return session.query(models.EndpointTemplates).all() - - def get_by_service(self, service_id, session=None): - if not session: - session = get_session() - return session.query(models.EndpointTemplates).\ - filter_by(service_id=service_id).all() - - def get_by_service_get_page(self, service_id, marker, limit, session=None): - if not session: - session = get_session() - - if marker: - return session.query(models.EndpointTemplates).\ - filter("id>:marker").params(\ - marker='%s' % marker).filter_by(\ - service_id=service_id).order_by(\ - models.EndpointTemplates.id.desc()).limit(int(limit)).all() - else: - return session.query(models.EndpointTemplates).filter_by(\ - service_id=service_id).order_by(\ - models.EndpointTemplates.id.desc()).\ - limit(int(limit)).all() - - # pylint: disable=R0912 - def get_by_service_get_page_markers(self, service_id, marker, \ - limit, session=None): - if not session: - session = get_session() - first = session.query(models.EndpointTemplates).filter_by(\ - service_id=service_id).order_by(\ - models.EndpointTemplates.id).first() - last = session.query(models.EndpointTemplates).filter_by(\ - service_id=service_id).order_by(\ - models.EndpointTemplates.id.desc()).first() - if first is None: - return (None, None) - if marker is None: - marker = first.id - next_page = session.query(models.EndpointTemplates).\ - filter("id > :marker").\ - filter_by(service_id=service_id).\ - params(marker='%s' % marker).\ - order_by(models.EndpointTemplates.id).\ - limit(int(limit)).\ - all() - prev_page = session.query(models.EndpointTemplates).\ - filter("id < :marker").\ - filter_by(service_id=service_id).\ - params(marker='%s' % marker).\ - order_by(models.EndpointTemplates.id.desc()).\ - limit(int(limit)).\ - all() - if len(next_page) == 0: - next_page = last - else: - for t in next_page: - next_page = t - if len(prev_page) == 0: - prev_page = first - else: - for t in prev_page: - prev_page = t - if prev_page.id == marker: - prev_page = None - else: - prev_page = prev_page.id - if next_page.id == last.id: - next_page = None - else: - next_page = next_page.id - return (prev_page, next_page) - - def get_page(self, marker, limit, session=None): - if not session: - session = get_session() - - if marker: - return session.query(models.EndpointTemplates).\ - filter("id>:marker").params(\ - marker='%s' % marker).order_by(\ - models.EndpointTemplates.id.desc()).limit(int(limit)).all() - else: - return session.query(models.EndpointTemplates).order_by(\ - models.EndpointTemplates.id.desc()).\ - limit(int(limit)).all() - - # pylint: disable=R0912 - def get_page_markers(self, marker, limit, session=None): - if not session: - session = get_session() - first = session.query(models.EndpointTemplates).order_by(\ - models.EndpointTemplates.id).first() - last = session.query(models.EndpointTemplates).order_by(\ - models.EndpointTemplates.id.desc()).first() - if first is None: - return (None, None) - if marker is None: - marker = first.id - next_page = session.query(models.EndpointTemplates).\ - filter("id > :marker").\ - params(marker='%s' % marker).\ - order_by(models.EndpointTemplates.id).\ - limit(int(limit)).\ - all() - prev_page = session.query(models.EndpointTemplates).\ - filter("id < :marker").\ - params(marker='%s' % marker).\ - order_by(models.EndpointTemplates.id.desc()).\ - limit(int(limit)).\ - all() - if len(next_page) == 0: - next_page = last - else: - for t in next_page: - next_page = t - if len(prev_page) == 0: - prev_page = first - else: - for t in prev_page: - prev_page = t - if prev_page.id == marker: - prev_page = None - else: - prev_page = prev_page.id - if next_page.id == last.id: - next_page = None - else: - next_page = next_page.id - return (prev_page, next_page) - - def endpoint_get_by_tenant_get_page(self, tenant_id, marker, limit, - session=None): - if not session: - session = get_session() - - if hasattr(api.TENANT, 'uid_to_id'): - tenant_id = api.TENANT.uid_to_id(tenant_id) - - if marker: - results = session.query(models.Endpoints).\ - filter(models.Endpoints.tenant_id == tenant_id).\ - filter("id >= :marker").params( - marker='%s' % marker).order_by( - models.Endpoints.id).limit(int(limit)).all() - else: - results = session.query(models.Endpoints).\ - filter(models.Endpoints.tenant_id == tenant_id).\ - order_by(models.Endpoints.id).limit(int(limit)).all() - - if hasattr(api.TENANT, 'id_to_uid'): - for result in results: - result.tenant_id = api.TENANT.id_to_uid(result.tenant_id) - - return results - - # pylint: disable=R0912 - def endpoint_get_by_tenant_get_page_markers(self, tenant_id, marker, limit, - session=None): - if not session: - session = get_session() - - if hasattr(api.TENANT, 'uid_to_id'): - tenant_id = api.TENANT.uid_to_id(tenant_id) - - tba = aliased(models.Endpoints) - first = session.query(tba).\ - filter(tba.tenant_id == tenant_id).\ - order_by(tba.id).first() - last = session.query(tba).\ - filter(tba.tenant_id == tenant_id).\ - order_by(tba.id.desc()).first() - if first is None: - return (None, None) - if marker is None: - marker = first.id - next_page = session.query(tba).\ - filter(tba.tenant_id == tenant_id).\ - filter("id>=:marker").params( - marker='%s' % marker).order_by( - tba.id).limit(int(limit)).all() - - prev_page = session.query(tba).\ - filter(tba.tenant_id == tenant_id).\ - filter("id < :marker").params( - marker='%s' % marker).order_by( - tba.id).limit(int(limit) + 1).all() - next_len = len(next_page) - prev_len = len(prev_page) - - if next_len == 0: - next_page = last - else: - for t in next_page: - next_page = t - if prev_len == 0: - prev_page = first - else: - for t in prev_page: - prev_page = t - if first.id == marker: - prev_page = None - else: - prev_page = prev_page.id - if marker == last.id: - next_page = None - else: - next_page = next_page.id - return (prev_page, next_page) - - def endpoint_add(self, values): - if hasattr(api.TENANT, 'uid_to_id'): - values.tenant_id = api.TENANT.uid_to_id(values.tenant_id) - - endpoints = models.Endpoints() - endpoints.update(values) - endpoints.save() - - if hasattr(api.TENANT, 'id_to_uid'): - endpoints.tenant_id = api.TENANT.id_to_uid(endpoints.tenant_id) - - return endpoints - - def endpoint_get(self, id, session=None): - if not session: - session = get_session() - - result = session.query(models.Endpoints).\ - filter_by(id=id).first() - - if hasattr(api.TENANT, 'id_to_uid'): - if result: - result.tenant_id = api.TENANT.id_to_uid(result.tenant_id) - - return result - - @staticmethod - def endpoint_get_by_ids(endpoint_template_id, tenant_id, - session=None): - if not session: - session = get_session() - - if hasattr(api.TENANT, 'uid_to_id'): - tenant_id = api.TENANT.uid_to_id(tenant_id) - - result = session.query(models.Endpoints).\ - filter_by(endpoint_template_id=endpoint_template_id).\ - filter_by(tenant_id=tenant_id).first() - - if hasattr(api.TENANT, 'id_to_uid'): - if result: - result.tenant_id = api.TENANT.id_to_uid(result.tenant_id) - - return result - - @staticmethod - def endpoint_get_all(session=None): - if not session: - session = get_session() - - results = session.query(models.Endpoints).all() - - for result in results: - if hasattr(api.TENANT, 'id_to_uid'): - result.tenant_id = api.TENANT.id_to_uid(result.tenant_id) - - return results - - def endpoint_get_by_tenant(self, tenant_id, session=None): - if not session: - session = get_session() - - if hasattr(api.TENANT, 'uid_to_id'): - tenant_id = api.TENANT.uid_to_id(tenant_id) - - result = session.query(models.Endpoints).\ - filter_by(tenant_id=tenant_id).first() - - if hasattr(api.TENANT, 'id_to_uid'): - if result: - result.tenant_id = api.TENANT.id_to_uid(result.tenant_id) - - return result - - def endpoint_get_by_endpoint_template( - self, endpoint_template_id, session=None): - if not session: - session = get_session() - - result = session.query(models.Endpoints).\ - filter_by(endpoint_template_id=endpoint_template_id).all() - - return result - - def endpoint_delete(self, id, session=None): - if not session: - session = get_session() - - with session.begin(): - endpoints = self.endpoint_get(id, session) - if endpoints: - session.delete(endpoints) - - -def get(): - return EndpointTemplateAPI() diff --git a/keystone/backends/sqlalchemy/api/role.py b/keystone/backends/sqlalchemy/api/role.py deleted file mode 100755 index 5419924fd3..0000000000 --- a/keystone/backends/sqlalchemy/api/role.py +++ /dev/null @@ -1,463 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 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 keystone.backends.sqlalchemy import get_session, models -from keystone.backends import api -from keystone.models import Role, UserRoleAssociation - - -# pylint: disable=E1103,W0221 -class RoleAPI(api.BaseRoleAPI): - def __init__(self, *args, **kw): - super(RoleAPI, self).__init__(*args, **kw) - - @staticmethod - def transpose(values): - """ Handles transposing field names from Keystone model to - sqlalchemy mode - - Differences: - desc <-> description - """ - if 'description' in values: - values['desc'] = values.pop('description') - - @staticmethod - def to_model(ref): - """ Returns Keystone model object based on SQLAlchemy model""" - if ref: - return Role(id=str(ref.id), name=ref.name, description=ref.desc, - service_id=ref.service_id) - - @staticmethod - def to_model_list(refs): - return [RoleAPI.to_model(ref) for ref in refs] - - @staticmethod - def to_ura_model(ref): - """ Returns Keystone model object based on SQLAlchemy model""" - if ref: - return UserRoleAssociation(id=ref.id, - role_id=ref.role_id, - user_id=ref.user_id, - tenant_id=ref.tenant_id) - - @staticmethod - def to_ura_model_list(refs): - return [RoleAPI.to_ura_model(ref) for ref in refs] - - # pylint: disable=W0221 - def create(self, values): - data = values.copy() - RoleAPI.transpose(data) - role = models.Role() - role.update(data) - role.save() - return RoleAPI.to_model(role) - - def delete(self, id, session=None): - if not session: - session = get_session() - with session.begin(): - role = session.query(models.Role).filter_by(id=id).first() - session.delete(role) - - @staticmethod - def update(id, values, session=None): - if not session: - session = get_session() - - RoleAPI.transpose(values) - - with session.begin(): - ref = session.query(models.Role).filter_by(id=id).first() - ref.update(values) - ref.save(session=session) - - def get(self, id, session=None): - if id is None: - return None - - session = session or get_session() - return RoleAPI.to_model( - session.query(models.Role).filter_by(id=id).first()) - - def get_by_name(self, name, session=None): - if not session: - session = get_session() - return RoleAPI.to_model( - session.query(models.Role).filter_by(name=name).first()) - - def get_by_service(self, service_id, session=None): - if not session: - session = get_session() - results = session.query(models.Role).\ - filter_by(service_id=service_id).all() - return RoleAPI.to_model_list(results) - - def get_all(self, session=None): - if not session: - session = get_session() - return RoleAPI.to_model_list(session.query(models.Role).all()) - - def get_page(self, marker, limit, session=None): - if not session: - session = get_session() - - if marker: - results = session.query(models.Role).filter("id>:marker").params( - marker='%s' % marker).order_by( - models.Role.id.desc()).limit(int(limit)).all() - else: - results = session.query(models.Role).order_by( - models.Role.id.desc()).limit(int(limit)).all() - return RoleAPI.to_model_list(results) - - # pylint: disable=R0912 - def get_page_markers(self, marker, limit, session=None): - if not session: - session = get_session() - first = session.query(models.Role).order_by( - models.Role.id).first() - last = session.query(models.Role).order_by( - models.Role.id.desc()).first() - if first is None: - return (None, None) - if marker is None: - marker = first.id - next_page = session.query(models.Role).filter("id > :marker").params( - marker='%s' % marker).order_by( - models.Role.id).limit(int(limit)).all() - prev_page = session.query(models.Role).filter("id < :marker").params( - marker='%s' % marker).order_by( - models.Role.id.desc()).limit(int(limit)).all() - if not next_page: - next_page = last - else: - next_page = next_page[-1] - if not prev_page: - prev_page = first - else: - prev_page = prev_page[-1] - if prev_page.id == marker: - prev_page = None - else: - prev_page = prev_page.id - if next_page.id == last.id: - next_page = None - else: - next_page = next_page.id - return (prev_page, next_page) - - def get_by_service_get_page(self, service_id, marker, limit, session=None): - if not session: - session = get_session() - - if marker: - results = session.query(models.Role).filter("id>:marker").params( - marker='%s' % marker).filter_by( - service_id=service_id).order_by( - models.Role.id.desc()).limit(int(limit)).all() - else: - results = session.query(models.Role).filter_by( - service_id=service_id).order_by( - models.Role.id.desc()).limit(int(limit)).all() - return RoleAPI.to_model_list(results) - - # pylint: disable=R0912 - def get_by_service_get_page_markers(self, - service_id, marker, limit, session=None): - if not session: - session = get_session() - first = session.query(models.Role).filter_by( - service_id=service_id).order_by( - models.Role.id).first() - last = session.query(models.Role).filter_by( - service_id=service_id).order_by( - models.Role.id.desc()).first() - if first is None: - return (None, None) - if marker is None: - marker = first.id - next_page = session.query(models.Role).filter("id > :marker").params( - marker='%s' % marker).filter_by( - service_id=service_id).order_by( - models.Role.id).limit(int(limit)).all() - prev_page = session.query(models.Role).filter("id < :marker").params( - marker='%s' % marker).filter_by( - service_id=service_id).order_by( - models.Role.id.desc()).limit(int(limit)).all() - if not next_page: - next_page = last - else: - next_page = next_page[-1] - if not prev_page: - prev_page = first - else: - prev_page = prev_page[-1] - if prev_page.id == marker: - prev_page = None - else: - prev_page = prev_page.id - if next_page.id == last.id: - next_page = None - else: - next_page = next_page.id - return (prev_page, next_page) - - # - # Role Grants start here - # - def rolegrant_get(self, id, session=None): - if not session: - session = get_session() - - result = session.query(models.UserRoleAssociation).filter_by(id=id).\ - first() - - if result: - if hasattr(api.USER, 'uid_to_id'): - result.user_id = api.USER.id_to_uid(result.user_id) - if hasattr(api.TENANT, 'uid_to_id'): - result.tenant_id = api.TENANT.id_to_uid(result.tenant_id) - - return RoleAPI.to_ura_model(result) - - def list_role_grants(self, role_id=None, user_id=None, tenant_id=None, - session=None): - """Lists all role grants; optionally specify a role ID, user ID, or - tenant ID. - - If tenant ID is provided as False (tenant_id=False), global grants will - be returned.""" - - session = session or get_session() - - is_global = tenant_id == False - - if hasattr(api.USER, 'uid_to_id'): - user_id = api.USER.uid_to_id(user_id) - if hasattr(api.TENANT, 'uid_to_id'): - tenant_id = api.TENANT.uid_to_id(tenant_id) - - query = session.query(models.UserRoleAssociation) - - if role_id is not None: - query = query.filter_by(role_id=role_id) - - if user_id is not None: - query = query.filter_by(user_id=user_id) - - if is_global: - query = query.filter(models.UserRoleAssociation.tenant_id == None) - elif tenant_id is not None: - query = query.filter_by(tenant_id=tenant_id) - - results = query.all() - - for result in results: - if hasattr(api.USER, 'uid_to_id'): - result.user_id = api.USER.id_to_uid(result.user_id) - if hasattr(api.TENANT, 'uid_to_id'): - result.tenant_id = api.TENANT.id_to_uid(result.tenant_id) - - return RoleAPI.to_ura_model_list(results) - - def rolegrant_delete(self, id, session=None): - if not session: - session = get_session() - - with session.begin(): - rolegrant = session.query(models.UserRoleAssociation).\ - filter_by(id=id).first() - session.delete(rolegrant) - - # pylint: disable=R0912 - def rolegrant_get_page_markers(self, user_id, tenant_id, marker, - limit, session=None): - if not session: - session = get_session() - - if hasattr(api.USER, 'uid_to_id'): - user_id = api.USER.uid_to_id(user_id) - if hasattr(api.TENANT, 'uid_to_id'): - tenant_id = api.TENANT.uid_to_id(tenant_id) - - query = session.query(models.UserRoleAssociation).filter_by( - user_id=user_id) - if tenant_id: - query = query.filter_by(tenant_id=tenant_id) - else: - query = query.filter("tenant_id is null") - first = query.order_by(models.UserRoleAssociation.id).first() - last = query.order_by(models.UserRoleAssociation.id.desc()).first() - if first is None: - return (None, None) - if marker is None: - marker = first.id - next_page = query.\ - filter("id > :marker").\ - params(marker='%s' % marker).\ - order_by(models.UserRoleAssociation.id).\ - limit(int(limit)).\ - all() - prev_page = query.\ - filter("id < :marker").\ - params(marker='%s' % marker).\ - order_by(models.UserRoleAssociation.id.desc()).\ - limit(int(limit)).\ - all() - - if not next_page: - next_page = last - else: - next_page = next_page[-1] - if not prev_page: - prev_page = first - else: - prev_page = prev_page[-1] - if prev_page.id == marker: - prev_page = None - else: - prev_page = prev_page.id - if next_page.id == last.id: - next_page = None - else: - next_page = next_page.id - return (prev_page, next_page) - - def rolegrant_get_page(self, marker, limit, user_id, tenant_id, - session=None): - if not session: - session = get_session() - - if hasattr(api.USER, 'uid_to_id'): - user_id = api.USER.uid_to_id(user_id) - if hasattr(api.TENANT, 'uid_to_id'): - tenant_id = api.TENANT.uid_to_id(tenant_id) - - query = session.query(models.UserRoleAssociation).\ - filter_by(user_id=user_id) - if tenant_id: - query = query.filter_by(tenant_id=tenant_id) - else: - query = query.filter("tenant_id is null") - if marker: - results = query.filter("id>:marker").params( - marker='%s' % marker).order_by( - models.UserRoleAssociation.id.desc()).limit( - int(limit)).all() - else: - results = query.order_by( - models.UserRoleAssociation.id.desc()).limit( - int(limit)).all() - - for result in results: - if hasattr(api.USER, 'uid_to_id'): - result.user_id = api.USER.id_to_uid(result.user_id) - if hasattr(api.TENANT, 'uid_to_id'): - result.tenant_id = api.TENANT.id_to_uid(result.tenant_id) - - return RoleAPI.to_ura_model_list(results) - - def list_global_roles_for_user(self, user_id, session=None): - if not session: - session = get_session() - - if hasattr(api.USER, 'uid_to_id'): - user_id = api.USER.uid_to_id(user_id) - - results = session.query(models.UserRoleAssociation).\ - filter_by(user_id=user_id).filter("tenant_id is null").all() - - for result in results: - result['role_id'] = str(result['role_id']) - if hasattr(api.USER, 'uid_to_id'): - result.user_id = api.USER.id_to_uid(result.user_id) - if hasattr(api.TENANT, 'uid_to_id'): - result.tenant_id = api.TENANT.id_to_uid(result.tenant_id) - - return RoleAPI.to_ura_model_list(results) - - def list_tenant_roles_for_user(self, user_id, tenant_id, session=None): - if not session: - session = get_session() - - if hasattr(api.USER, 'uid_to_id'): - user_id = api.USER.uid_to_id(user_id) - if hasattr(api.TENANT, 'uid_to_id'): - tenant_id = api.TENANT.uid_to_id(tenant_id) - - results = session.query(models.UserRoleAssociation).\ - filter_by(user_id=user_id).filter_by(tenant_id=tenant_id).all() - - for result in results: - result['role_id'] = str(result['role_id']) - if hasattr(api.USER, 'uid_to_id'): - result.user_id = api.USER.id_to_uid(result.user_id) - if hasattr(api.TENANT, 'uid_to_id'): - result.tenant_id = api.TENANT.id_to_uid(result.tenant_id) - - return RoleAPI.to_ura_model_list(results) - - def rolegrant_list_by_role(self, role_id, session=None): - """ Get a list of all (global and tenant) grants for this role """ - if not session: - session = get_session() - - results = session.query(models.UserRoleAssociation).\ - filter_by(role_id=role_id).all() - - for result in results: - result['role_id'] = str(result['role_id']) - if hasattr(api.USER, 'uid_to_id'): - result.user_id = api.USER.id_to_uid(result.user_id) - if hasattr(api.TENANT, 'uid_to_id'): - result.tenant_id = api.TENANT.id_to_uid(result.tenant_id) - - return RoleAPI.to_ura_model_list(results) - - def rolegrant_get_by_ids(self, user_id, role_id, tenant_id, session=None): - if not session: - session = get_session() - - if hasattr(api.USER, 'uid_to_id'): - user_id = api.USER.uid_to_id(user_id) - if hasattr(api.TENANT, 'uid_to_id'): - tenant_id = api.TENANT.uid_to_id(tenant_id) - - if tenant_id is None: - result = session.query(models.UserRoleAssociation).\ - filter_by(user_id=user_id).filter("tenant_id is null").\ - filter_by(role_id=role_id).first() - else: - result = session.query(models.UserRoleAssociation).\ - filter_by(user_id=user_id).filter_by(tenant_id=tenant_id).\ - filter_by(role_id=role_id).first() - - if result: - result['role_id'] = str(result['role_id']) - if hasattr(api.USER, 'uid_to_id'): - result.user_id = api.USER.id_to_uid(result.user_id) - if hasattr(api.TENANT, 'uid_to_id'): - result.tenant_id = api.TENANT.id_to_uid(result.tenant_id) - - return RoleAPI.to_ura_model(result) - - -def get(): - return RoleAPI() diff --git a/keystone/backends/sqlalchemy/api/service.py b/keystone/backends/sqlalchemy/api/service.py deleted file mode 100644 index 3e54610675..0000000000 --- a/keystone/backends/sqlalchemy/api/service.py +++ /dev/null @@ -1,194 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 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 keystone.backends.sqlalchemy import get_session, models -from keystone.backends import api -from keystone.models import Service - - -# pylint: disable=E1103,W0221 -class ServiceAPI(api.BaseServiceAPI): - def __init__(self, *args, **kw): - super(ServiceAPI, self).__init__(*args, **kw) - - # pylint: disable=W0221 - @staticmethod - def transpose(values): - """ Handles transposing field names from Keystone model to - sqlalchemy model - - Differences: - desc <-> description - id <-> uid (coming soon) - """ - if 'description' in values: - values['desc'] = values.pop('description') - - if hasattr(api.USER, 'uid_to_id'): - if 'owner_id' in values: - values['owner_id'] = api.USER.uid_to_id(values['owner_id']) - elif hasattr(values, 'owner_id'): - values.owner_id = api.USER.uid_to_id(values.owner_id) - - @staticmethod - def from_model(ref): - """ Returns SQLAlchemy model object based on Keystone model""" - if ref: - if hasattr(api.USER, 'uid_to_id'): - if 'owner_id' in ref: - ref['owner_id'] = api.USER.uid_to_id(ref['owner_id']) - elif hasattr(ref, 'owner_id'): - ref.owner_id = api.USER.uid_to_id(ref.owner_id) - - result = models.Service() - try: - result.id = int(ref.id) - except (AttributeError, TypeError): - # Unspecified or invalid ID -- ignore it. - pass - result.update(ref) - if hasattr(ref, 'description'): - result.desc = ref.description - return result - - @staticmethod - def to_model(ref): - """ Returns Keystone model object based on SQLAlchemy model""" - if ref: - if hasattr(api.USER, 'id_to_uid'): - if 'owner_id' in ref: - ref['owner_id'] = api.USER.id_to_uid(ref['owner_id']) - elif hasattr(ref, 'owner_id'): - ref.owner_id = api.USER.id_to_uid(ref.owner_id) - - return Service(id=str(ref.id), name=ref.name, description=ref.desc, - type=ref.type, owner_id=ref.owner_id) - - @staticmethod - def to_model_list(refs): - return [ServiceAPI.to_model(ref) for ref in refs] - - # pylint: disable=W0221 - def create(self, values): - service_ref = ServiceAPI.from_model(values) - service_ref.save() - return ServiceAPI.to_model(service_ref) - - @staticmethod - def update(id, values, session=None): - if not session: - session = get_session() - - ServiceAPI.transpose(values) - - with session.begin(): - service_ref = session.query(models.Service).filter_by(id=id).\ - first() - service_ref.update(values) - service_ref.save(session=session) - - def get(self, id, session=None): - if id is None: - return None - - session = session or get_session() - return ServiceAPI.to_model(session.query(models.Service). - filter_by(id=id).first()) - - def get_by_name(self, name, session=None): - if not session: - session = get_session() - return ServiceAPI.to_model(session.query(models.Service). - filter_by(name=name).first()) - - def get_by_name_and_type(self, name, type, session=None): - if not session: - session = get_session() - result = session.query(models.Service).\ - filter_by(name=name).\ - filter_by(type=type).\ - first() - return ServiceAPI.to_model(result) - - def get_all(self, session=None): - if not session: - session = get_session() - return ServiceAPI.to_model_list(session.query(models.Service).all()) - - def get_page(self, marker, limit, session=None): - if not session: - session = get_session() - if marker: - return session.query(models.Service).filter("id>:marker").params( - marker='%s' % marker).order_by( - models.Service.id.desc()).limit(int(limit)).all() - else: - return session.query(models.Service).order_by( - models.Service.id.desc()).limit( - int(limit)).all() - - @staticmethod - def get_page_markers(marker, limit, session=None): - if not session: - session = get_session() - first = session.query(models.Service).order_by( - models.Service.id).first() - last = session.query(models.Service).order_by( - models.Service.id.desc()).first() - if first is None: - return (None, None) - if marker is None: - marker = first.id - next_page = session.query(models.Service).\ - filter("id > :marker").params( - marker='%s' % marker).order_by( - models.Service.id).limit(int(limit)).all() - prev_page = session.query(models.Service).\ - filter("id < :marker").params( - marker='%s' % marker).order_by( - models.Service.id.desc()).limit(int(limit)).all() - if len(next_page) == 0: - next_page = last - else: - for t in next_page: - next_page = t - if len(prev_page) == 0: - prev_page = first - else: - for t in prev_page: - prev_page = t - if prev_page.id == marker: - prev_page = None - else: - prev_page = prev_page.id - if next_page.id == last.id: - next_page = None - else: - next_page = next_page.id - return (prev_page, next_page) - - def delete(self, id, session=None): - if not session: - session = get_session() - with session.begin(): - service_ref = session.query(models.Service).\ - filter_by(id=id).first() - session.delete(service_ref) - - -def get(): - return ServiceAPI() diff --git a/keystone/backends/sqlalchemy/api/tenant.py b/keystone/backends/sqlalchemy/api/tenant.py deleted file mode 100755 index db516b155e..0000000000 --- a/keystone/backends/sqlalchemy/api/tenant.py +++ /dev/null @@ -1,377 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 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 uuid - -from keystone.backends.sqlalchemy import get_session, models, aliased -from keystone.backends import api -from keystone.models import Tenant - - -# pylint: disable=E1103,W0221 -class TenantAPI(api.BaseTenantAPI): - def __init__(self, *args, **kw): - super(TenantAPI, self).__init__(*args, **kw) - - # pylint: disable=W0221 - @staticmethod - def transpose(values): - """ Handles transposing field names from Keystone model to - sqlalchemy mode - - Differences: - desc <-> description - id <-> uid (coming soon) - """ - if 'id' in values: - values['uid'] = values['id'] - del values['id'] - if 'description' in values: - values['desc'] = values['description'] - del values['description'] - if 'enabled' in values: - if values['enabled'] in [1, 'true', 'True', True]: - values['enabled'] = True - else: - values['enabled'] = False - - @staticmethod - def to_model(ref): - """ Returns Keystone model object based on SQLAlchemy model""" - if ref: - return Tenant(id=ref.uid, name=ref.name, description=ref.desc, - enabled=bool(ref.enabled)) - - @staticmethod - def to_model_list(refs): - return [TenantAPI.to_model(ref) for ref in refs] - - def create(self, values): - data = values.copy() - TenantAPI.transpose(data) - tenant_ref = models.Tenant() - tenant_ref.update(data) - if tenant_ref.uid is None: - tenant_ref.uid = uuid.uuid4().hex - tenant_ref.save() - return TenantAPI.to_model(tenant_ref) - - def get(self, id, session=None): - """Returns a tenant by ID. - - .warning:: - - Internally, the provided ID is matched against the ``tenants.UID``, - not the PK (``tenants.id``) column. - - For PK lookups from within the sqlalchemy backend, - use ``_get_by_id()`` instead. - """ - if id is None: - return None - - session = session or get_session() - - result = session.query(models.Tenant).filter_by(uid=id).first() - - return TenantAPI.to_model(result) - - @staticmethod - def _get_by_id(id, session=None): - """Returns a tenant by ID (PK). - - .warning:: - - The provided ID is matched against the PK (``tenants.ID``). - - This is **only** for use within the sqlalchemy backend. - """ - if id is None: - return None - - session = session or get_session() - - return session.query(models.Tenant).filter_by(id=id).first() - - @staticmethod - def id_to_uid(id, session=None): - if id is None: - return None - - session = session or get_session() - tenant = session.query(models.Tenant).filter_by(id=id).first() - return tenant.uid if tenant else None - - @staticmethod - def uid_to_id(uid, session=None): - if uid is None: - return None - - session = session or get_session() - tenant = session.query(models.Tenant).filter_by(uid=uid).first() - return tenant.id if tenant else None - - def get_by_name(self, name, session=None): - session = session or get_session() - - result = session.query(models.Tenant).filter_by(name=name).first() - - return TenantAPI.to_model(result) - - def get_all(self, session=None): - if not session: - session = get_session() - - results = session.query(models.Tenant).all() - - return TenantAPI.to_model_list(results) - - def list_for_user_get_page(self, user_id, marker, limit, session=None): - if not session: - session = get_session() - - user = api.USER.get(user_id) - if hasattr(api.USER, 'uid_to_id'): - backend_user_id = api.USER.uid_to_id(user_id) - else: - backend_user_id = user_id - - ura = aliased(models.UserRoleAssociation) - tenant = aliased(models.Tenant) - q1 = session.query(tenant).join((ura, ura.tenant_id == tenant.id)).\ - filter(ura.user_id == backend_user_id) - if 'tenant_id' in user: - if hasattr(api.TENANT, 'uid_to_id'): - backend_tenant_id = api.TENANT.uid_to_id(user.tenant_id) - else: - backend_tenant_id = user.tenant_id - q2 = session.query(tenant).filter(tenant.id == backend_tenant_id) - q3 = q1.union(q2) - else: - q3 = q1 - if marker: - results = q3.filter("tenant.id>:marker").params( - marker='%s' % marker).order_by( - tenant.id.desc()).limit(int(limit)).all() - else: - results = q3.order_by(tenant.id.desc()).limit(int(limit)).all() - - return TenantAPI.to_model_list(results) - - # pylint: disable=R0912 - def list_for_user_get_page_markers(self, user_id, marker, limit, - session=None): - if not session: - session = get_session() - - user = api.USER.get(user_id) - if hasattr(api.USER, 'uid_to_id'): - backend_user_id = api.USER.uid_to_id(user_id) - else: - backend_user_id = user_id - - ura = aliased(models.UserRoleAssociation) - tenant = aliased(models.Tenant) - q1 = session.query(tenant).join((ura, ura.tenant_id == tenant.id)).\ - filter(ura.user_id == backend_user_id) - if 'tenant_id' in user: - if hasattr(api.TENANT, 'uid_to_id'): - backend_tenant_id = api.TENANT.uid_to_id(user.tenant_id) - else: - backend_tenant_id = user.tenant_id - q2 = session.query(tenant).filter(tenant.id == backend_tenant_id) - q3 = q1.union(q2) - else: - q3 = q1 - - first = q3.order_by(tenant.id).first() - last = q3.order_by(tenant.id.desc()).first() - if first is None: - return (None, None) - if marker is None: - marker = first.id - next_page = q3.filter(tenant.id > marker).order_by( - tenant.id).limit(int(limit)).all() - prev_page = q3.filter(tenant.id > marker).order_by( - tenant.id.desc()).limit(int(limit)).all() - if len(next_page) == 0: - next_page = last - else: - for t in next_page: - next_page = t - if len(prev_page) == 0: - prev_page = first - else: - for t in prev_page: - prev_page = t - if prev_page.id == marker: - prev_page = None - else: - prev_page = prev_page.id - if next_page.id == last.id: - next_page = None - else: - next_page = next_page.id - return (prev_page, next_page) - - def get_page(self, marker, limit, session=None): - if not session: - session = get_session() - - if marker: - tenants = session.query(models.Tenant).\ - filter("id>:marker").params( - marker='%s' % marker).order_by( - models.Tenant.id.desc()).limit(int(limit)).all() - else: - tenants = session.query(models.Tenant).order_by( - models.Tenant.id.desc()).limit(int(limit)).all() - - return self.to_model_list(tenants) - - # pylint: disable=R0912 - def get_page_markers(self, marker, limit, session=None): - if not session: - session = get_session() - first = session.query(models.Tenant).order_by( - models.Tenant.id).first() - last = session.query(models.Tenant).order_by( - models.Tenant.id.desc()).first() - if first is None: - return (None, None) - if marker is None: - marker = first.id - next_page = session.query(models.Tenant).\ - filter("id > :marker").\ - params(marker='%s' % marker).\ - order_by(models.Tenant.id).\ - limit(int(limit)).\ - all() - prev_page = session.query(models.Tenant).\ - filter("id < :marker").\ - params(marker='%s' % marker).\ - order_by(models.Tenant.id.desc()).\ - limit(int(limit)).\ - all() - if len(next_page) == 0: - next_page = last - else: - for t in next_page: - next_page = t - if len(prev_page) == 0: - prev_page = first - else: - for t in prev_page: - prev_page = t - if prev_page.id == marker: - prev_page = None - else: - prev_page = prev_page.id - if next_page.id == last.id: - next_page = None - else: - next_page = next_page.id - return (prev_page, next_page) - - def is_empty(self, id, session=None): - if not session: - session = get_session() - - if hasattr(api.TENANT, 'uid_to_id'): - id = self.uid_to_id(id) - - a_user = session.query(models.UserRoleAssociation).filter_by( - tenant_id=id).first() - if a_user is not None: - return False - a_user = session.query(models.User).filter_by(tenant_id=id).first() - if a_user is not None: - return False - return True - - def update(self, id, values, session=None): - if not session: - session = get_session() - - if hasattr(api.TENANT, 'uid_to_id'): - pkid = self.uid_to_id(id) - else: - pkid = id - - data = values.copy() - TenantAPI.transpose(data) - - with session.begin(): - tenant_ref = self._get_by_id(pkid, session) - tenant_ref.update(data) - tenant_ref.save(session=session) - return self.get(id, session) - - def delete(self, id, session=None): - if not session: - session = get_session() - - if not self.is_empty(id): - raise fault.ForbiddenFault("You may not delete a tenant that " - "contains users") - - if hasattr(api.TENANT, 'uid_to_id'): - id = self.uid_to_id(id) - - with session.begin(): - tenant_ref = self._get_by_id(id, session) - session.delete(tenant_ref) - - def get_all_endpoints(self, tenant_id, session=None): - if not session: - session = get_session() - - if hasattr(api.TENANT, 'uid_to_id'): - tenant_id = self.uid_to_id(tenant_id) - - endpoint_templates = aliased(models.EndpointTemplates) - q = session.query(endpoint_templates).\ - filter(endpoint_templates.is_global == True) - if tenant_id: - ep = aliased(models.Endpoints) - q1 = session.query(endpoint_templates).join((ep, - ep.endpoint_template_id == endpoint_templates.id)).\ - filter(ep.tenant_id == tenant_id) - q = q.union(q1) - return q.all() - - def get_role_assignments(self, tenant_id, session=None): - if not session: - session = get_session() - - if hasattr(api.TENANT, 'uid_to_id'): - tenant_id = TenantAPI.uid_to_id(tenant_id) - - results = session.query(models.UserRoleAssociation).\ - filter_by(tenant_id=tenant_id) - - for result in results: - if hasattr(api.USER, 'uid_to_id'): - result.user_id = api.USER.id_to_uid(result.user_id) - if hasattr(api.TENANT, 'uid_to_id'): - result.tenant_id = api.TENANT.id_to_uid(result.tenant_id) - - return results - - -def get(): - return TenantAPI() diff --git a/keystone/backends/sqlalchemy/api/token.py b/keystone/backends/sqlalchemy/api/token.py deleted file mode 100755 index 869822bbaf..0000000000 --- a/keystone/backends/sqlalchemy/api/token.py +++ /dev/null @@ -1,149 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 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 keystone.backends.sqlalchemy import get_session, models -from keystone.backends import api -from keystone.models import Token - - -# pylint: disable=E1103,W0221 -class TokenAPI(api.BaseTokenAPI): - def __init__(self, *args, **kw): - super(TokenAPI, self).__init__(*args, **kw) - - @staticmethod - def transpose(ref): - """ Transposes field names from domain to sql model""" - if hasattr(api.TENANT, 'uid_to_id'): - if 'tenant_id' in ref: - ref['tenant_id'] = api.TENANT.uid_to_id(ref['tenant_id']) - elif hasattr(ref, 'tenant_id'): - ref.tenant_id = api.TENANT.uid_to_id(ref.tenant_id) - - if hasattr(api.USER, 'uid_to_id'): - if 'user_id' in ref: - ref['user_id'] = api.USER.uid_to_id(ref['user_id']) - elif hasattr(ref, 'tenant_id'): - ref.user_id = api.USER.uid_to_id(ref.user_id) - - @staticmethod - def to_model(ref): - """ Returns Keystone model object based on SQLAlchemy model""" - if ref: - if hasattr(api.TENANT, 'uid_to_id'): - if 'tenant_id' in ref: - ref['tenant_id'] = api.TENANT.id_to_uid(ref['tenant_id']) - elif hasattr(ref, 'tenant_id'): - ref.tenant_id = api.TENANT.id_to_uid(ref.tenant_id) - - if hasattr(api.USER, 'uid_to_id'): - if 'user_id' in ref: - ref['user_id'] = api.USER.id_to_uid(ref['user_id']) - elif hasattr(ref, 'user_id'): - ref.user_id = api.USER.id_to_uid(ref.user_id) - - return Token(id=ref.id, user_id=ref.user_id, expires=ref.expires, - tenant_id=ref.tenant_id) - - @staticmethod - def to_model_list(refs): - return [TokenAPI.to_model(ref) for ref in refs] - - def create(self, values): - data = values.copy() - TokenAPI.transpose(data) - token_ref = models.Token() - token_ref.update(data) - token_ref.save() - return TokenAPI.to_model(token_ref) - - def get(self, id, session=None): - result = self._get(id, session) - - return TokenAPI.to_model(result) - - @staticmethod - def _get(id, session=None): - if id is None: - return None - - session = session or get_session() - - result = session.query(models.Token).filter_by(id=id).first() - - return result - - @staticmethod - def update(id, values, session=None): - if not session: - session = get_session() - - TokenAPI.transpose(values) - - with session.begin(): - ref = session.query(models.Token).filter_by(id=id).first() - ref.update(values) - ref.save(session=session) - - def delete(self, id, session=None): - if not session: - session = get_session() - - with session.begin(): - token_ref = self._get(id, session) - session.delete(token_ref) - - def get_for_user(self, user_id, session=None): - if not session: - session = get_session() - - if hasattr(api.USER, 'uid_to_id'): - user_id = api.USER.uid_to_id(user_id) - - result = session.query(models.Token).filter_by( - user_id=user_id, tenant_id=None).order_by("expires desc").first() - - return TokenAPI.to_model(result) - - def get_for_user_by_tenant(self, user_id, tenant_id, session=None): - if not session: - session = get_session() - - if hasattr(api.USER, 'uid_to_id'): - user_id = api.USER.uid_to_id(user_id) - - if hasattr(api.TENANT, 'uid_to_id'): - tenant_id = api.TENANT.uid_to_id(tenant_id) - - result = session.query(models.Token).\ - filter_by(user_id=user_id, tenant_id=tenant_id).\ - order_by("expires desc").\ - first() - - return TokenAPI.to_model(result) - - def get_all(self, session=None): - if not session: - session = get_session() - - results = session.query(models.Token).all() - - return TokenAPI.to_model_list(results) - - -def get(): - return TokenAPI() diff --git a/keystone/backends/sqlalchemy/api/user.py b/keystone/backends/sqlalchemy/api/user.py deleted file mode 100755 index f5a64b1823..0000000000 --- a/keystone/backends/sqlalchemy/api/user.py +++ /dev/null @@ -1,458 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 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 uuid - -import keystone.backends.backendutils as utils -from keystone.backends.sqlalchemy import get_session, models, aliased, \ - joinedload -from keystone.backends import api -from keystone.models import User - - -# pylint: disable=E1103,W0221,W0223 -class UserAPI(api.BaseUserAPI): - def __init__(self, *args, **kw): - super(UserAPI, self).__init__(*args, **kw) - - @staticmethod - def transpose(ref): - """ Transposes field names from domain to sql model""" - if 'id' in ref: - ref['uid'] = ref.pop('id') - - if hasattr(api.TENANT, 'uid_to_id'): - if 'tenant_id' in ref: - ref['tenant_id'] = api.TENANT.uid_to_id(ref['tenant_id']) - elif hasattr(ref, 'tenant_id'): - ref.tenant_id = api.TENANT.uid_to_id(ref.tenant_id) - - @staticmethod - def to_model(ref): - """ Returns Keystone model object based on SQLAlchemy model""" - if ref: - if hasattr(api.TENANT, 'uid_to_id'): - if 'tenant_id' in ref: - ref['tenant_id'] = api.TENANT.id_to_uid(ref['tenant_id']) - elif hasattr(ref, 'tenant_id'): - ref.tenant_id = api.TENANT.id_to_uid(ref.tenant_id) - - return User(id=ref.uid, password=ref.password, name=ref.name, - tenant_id=ref.tenant_id, email=ref.email, - enabled=bool(ref.enabled)) - - @staticmethod - def to_model_list(refs): - return [UserAPI.to_model(ref) for ref in refs] - - # pylint: disable=W0221 - def get_all(self, session=None): - if not session: - session = get_session() - - results = session.query(models.User) - - return UserAPI.to_model_list(results) - - def create(self, values): - data = values.copy() - UserAPI.transpose(data) - utils.set_hashed_password(data) - if 'uid' not in data or data['uid'] is None: - data['uid'] = uuid.uuid4().hex - user_ref = models.User() - user_ref.update(data) - user_ref.save() - return UserAPI.to_model(user_ref) - - def get(self, id, session=None): - if id is None: - return None - - session = session or get_session() - - result = session.query(models.User).filter_by(uid=str(id)).first() - - return UserAPI.to_model(result) - - @staticmethod - def _get_by_id(id, session=None): - """Only for use by the sql backends - - - Queries by PK ID - - Doesn't wrap result with domain layer models - """ - if id is None: - return None - - session = session or get_session() - - # TODO(dolph): user ID's are NOT strings... why is this is being cast? - return session.query(models.User).filter_by(id=str(id)).first() - - @staticmethod - def id_to_uid(id, session=None): - if id is None: - return None - - session = session or get_session() - user = session.query(models.User).filter_by(id=str(id)).first() - return user.uid if user else None - - @staticmethod - def uid_to_id(uid, session=None): - if uid is None: - return None - - session = session or get_session() - user = session.query(models.User).filter_by(uid=str(uid)).first() - return user.id if user else None - - def get_by_name(self, name, session=None): - if not session: - session = get_session() - - result = session.query(models.User).filter_by(name=name).first() - - return UserAPI.to_model(result) - - def get_by_email(self, email, session=None): - if not session: - session = get_session() - - result = session.query(models.User).filter_by(email=email).first() - - return UserAPI.to_model(result) - - def get_page(self, marker, limit, session=None): - if not session: - session = get_session() - - if marker: - results = session.query(models.User).filter("id>:marker").params( - marker='%s' % marker).order_by( - models.User.id.desc()).limit(int(limit)).all() - else: - results = session.query(models.User).order_by( - models.User.id.desc()).limit(int(limit)).all() - - return UserAPI.to_model_list(results) - - # pylint: disable=R0912 - def get_page_markers(self, marker, limit, session=None): - if not session: - session = get_session() - - first = session.query(models.User).order_by( - models.User.id).first() - last = session.query(models.User).order_by( - models.User.id.desc()).first() - if first is None: - return (None, None) - if marker is None: - marker = first.id - next_page = session.query(models.User).filter("id > :marker").params( - marker='%s' % marker).order_by( - models.User.id).limit(int(limit)).all() - prev_page = session.query(models.User).filter("id < :marker").params( - marker='%s' % marker).order_by( - models.User.id.desc()).limit(int(limit)).all() - if len(next_page) == 0: - next_page = last - else: - for t in next_page: - next_page = t - if len(prev_page) == 0: - prev_page = first - else: - for t in prev_page: - prev_page = t - if prev_page.id == marker: - prev_page = None - else: - prev_page = prev_page.id - if next_page.id == last.id: - next_page = None - else: - next_page = next_page.id - return (prev_page, next_page) - - def user_roles_by_tenant(self, user_id, tenant_id, session=None): - if not session: - session = get_session() - - if hasattr(api.USER, 'uid_to_id'): - user_id = api.USER.uid_to_id(user_id) - if hasattr(api.TENANT, 'uid_to_id'): - tenant_id = api.TENANT.uid_to_id(tenant_id) - - results = session.query(models.UserRoleAssociation).\ - filter_by(user_id=user_id, tenant_id=tenant_id).\ - options(joinedload('roles')) - - for result in results: - if hasattr(api.USER, 'id_to_uid'): - result.user_id = api.USER.id_to_uid(result.user_id) - if hasattr(api.TENANT, 'id_to_uid'): - result.tenant_id = api.TENANT.id_to_uid(result.tenant_id) - - return results - - def update(self, id, values, session=None): - if not session: - session = get_session() - - UserAPI.transpose(values) - - with session.begin(): - user_ref = session.query(models.User).filter_by(uid=id).first() - utils.set_hashed_password(values) - user_ref.update(values) - user_ref.save(session=session) - - def delete(self, id, session=None): - if not session: - session = get_session() - - with session.begin(): - user_ref = session.query(models.User).filter_by(uid=id).first() - session.delete(user_ref) - - def get_by_tenant(self, id, tenant_id, session=None): - if not session: - session = get_session() - - uid = id - - if hasattr(api.USER, 'uid_to_id'): - id = api.USER.uid_to_id(uid) - if hasattr(api.TENANT, 'uid_to_id'): - tenant_id = api.TENANT.uid_to_id(tenant_id) - - # Most common use case: user lives in tenant - user = session.query(models.User).\ - filter_by(id=id, tenant_id=tenant_id).first() - if user: - return UserAPI.to_model(user) - - # Find user through grants to this tenant - result = session.query(models.UserRoleAssociation).\ - filter_by(tenant_id=tenant_id, user_id=id).first() - if result: - return self.get(uid, session) - else: - return None - - def users_get_by_tenant(self, user_id, tenant_id, session=None): - if not session: - session = get_session() - - if hasattr(api.USER, 'uid_to_id'): - user_id = api.USER.uid_to_id(user_id) - if hasattr(api.TENANT, 'uid_to_id'): - tenant_id = api.TENANT.uid_to_id(tenant_id) - - results = session.query(models.User).filter_by(id=user_id, - tenant_id=tenant_id) - return UserAPI.to_model_list(results) - - def user_role_add(self, values): - if hasattr(api.USER, 'uid_to_id'): - values['user_id'] = api.USER.uid_to_id(values['user_id']) - if hasattr(api.TENANT, 'uid_to_id'): - values['tenant_id'] = api.TENANT.uid_to_id(values['tenant_id']) - - user_rolegrant = models.UserRoleAssociation() - user_rolegrant.update(values) - user_rolegrant.save() - - if hasattr(api.USER, 'id_to_uid'): - user_rolegrant.user_id = api.USER.id_to_uid(user_rolegrant.user_id) - if hasattr(api.TENANT, 'id_to_uid'): - user_rolegrant.tenant_id = api.TENANT.id_to_uid( - user_rolegrant.tenant_id) - - return user_rolegrant - - def users_get_page(self, marker, limit, session=None): - if not session: - session = get_session() - - user = aliased(models.User) - if marker: - results = session.query(user).\ - filter("id>=:marker").params( - marker='%s' % marker).order_by( - "id").limit(int(limit)).all() - else: - results = session.query(user).\ - order_by("id").limit(int(limit)).all() - - return UserAPI.to_model_list(results) - - # pylint: disable=R0912 - def users_get_page_markers(self, marker, limit, session=None): - if not session: - session = get_session() - - user = aliased(models.User) - first = session.query(user).\ - order_by(user.id).first() - last = session.query(user).\ - order_by(user.id.desc()).first() - if first is None: - return (None, None) - if marker is None: - marker = first.id - next_page = session.query(user).\ - filter("id > :marker").params( - marker='%s' % marker).order_by(user.id).\ - limit(int(limit)).all() - prev_page = session.query(user).\ - filter("id < :marker").params( - marker='%s' % marker).order_by( - user.id.desc()).limit(int(limit)).all() - next_len = len(next_page) - prev_len = len(prev_page) - - if next_len == 0: - next_page = last - else: - for t in next_page: - next_page = t - if prev_len == 0: - prev_page = first - else: - for t in prev_page: - prev_page = t - if first.id == marker: - prev_page = None - else: - prev_page = prev_page.id - if marker == last.id: - next_page = None - else: - next_page = next_page.id - return (prev_page, next_page) - - def users_get_by_tenant_get_page(self, tenant_id, role_id, marker, limit, - session=None): - # This is broken. If a user has more than one role per project - # shit hits the fan because we're limiting the wrong model. - # Also the user lookup is nasty and potentially injectiable. - if not session: - session = get_session() - - if hasattr(api.TENANT, 'uid_to_id'): - tenant_id = api.TENANT.uid_to_id(tenant_id) - - user = aliased(models.UserRoleAssociation) - query = session.query(user).\ - filter("tenant_id = :tenant_id").\ - params(tenant_id='%s' % tenant_id) - - if role_id: - query = query.filter( - user.role_id == role_id) - - if marker: - rv = query.filter("id>=:marker").\ - params(marker='%s' % marker).\ - order_by("id").\ - limit(int(limit)).\ - all() - else: - rv = query.\ - order_by("id").\ - limit(int(limit)).\ - all() - - user_ids = set([str(assoc.user_id) for assoc in rv]) - users = session.query(models.User).\ - filter("id in ('%s')" % "','".join(user_ids)).\ - all() - - for usr in users: - usr.tenant_roles = set() - for role in usr.roles: - if role.tenant_id == tenant_id: - usr.tenant_roles.add(role.role_id) - - return UserAPI.to_model_list(users) - - # pylint: disable=R0912 - def users_get_by_tenant_get_page_markers(self, tenant_id, \ - role_id, marker, limit, session=None): - if not session: - session = get_session() - - if hasattr(api.TENANT, 'uid_to_id'): - tenant_id = api.TENANT.uid_to_id(tenant_id) - - user = aliased(models.UserRoleAssociation) - query = session.query(user).\ - filter(user.tenant_id == tenant_id) - if role_id: - query = query.filter( - user.role_id == role_id) - first = query.\ - order_by(user.id).first() - last = query.\ - order_by(user.id.desc()).first() - if first is None: - return (None, None) - if marker is None: - marker = first.id - next_page = query.\ - filter("id > :marker").params( - marker='%s' % marker).order_by(user.id).\ - limit(int(limit)).all() - prev_page = query.\ - filter("id < :marker").params( - marker='%s' % marker).order_by( - user.id.desc()).limit(int(limit)).all() - next_len = len(next_page) - prev_len = len(prev_page) - - if next_len == 0: - next_page = last - else: - for t in next_page: - next_page = t - if prev_len == 0: - prev_page = first - else: - for t in prev_page: - prev_page = t - if first.id == marker: - prev_page = None - else: - prev_page = prev_page.id - if marker == last.id: - next_page = None - else: - next_page = next_page.id - return (prev_page, next_page) - - def check_password(self, user_id, password): - user = self.get(user_id) - return utils.check_password(password, user.password) - # pylint: enable=W0221 - - -def get(): - return UserAPI() diff --git a/keystone/backends/sqlalchemy/migrate_repo/versions/001_initial_migration.py b/keystone/backends/sqlalchemy/migrate_repo/versions/001_initial_migration.py deleted file mode 100644 index 45b696851f..0000000000 --- a/keystone/backends/sqlalchemy/migrate_repo/versions/001_initial_migration.py +++ /dev/null @@ -1,196 +0,0 @@ -# pylint: disable=C0103,R0801 - - -import sqlalchemy - - -meta = sqlalchemy.MetaData() - - -# services - -service = {} -service['id'] = sqlalchemy.Column('id', sqlalchemy.Integer, - primary_key=True, autoincrement=True) -service['name'] = sqlalchemy.Column('name', sqlalchemy.String(255), - unique=True) -service['type'] = sqlalchemy.Column('type', sqlalchemy.String(255)) -service['desc'] = sqlalchemy.Column('desc', sqlalchemy.String(255)) -services = sqlalchemy.Table('services', meta, *service.values()) - -sqlalchemy.UniqueConstraint(service['name']) - - -# roles - -role = {} -role['id'] = sqlalchemy.Column('id', sqlalchemy.Integer, - primary_key=True, autoincrement=True) -role['name'] = sqlalchemy.Column('name', sqlalchemy.String(255)) -role['desc'] = sqlalchemy.Column('desc', sqlalchemy.String(255)) -role['service_id'] = sqlalchemy.Column('service_id', sqlalchemy.Integer) -roles = sqlalchemy.Table('roles', meta, *role.values()) - -sqlalchemy.UniqueConstraint(role['name'], role['service_id']) - -sqlalchemy.ForeignKeyConstraint( - [role['service_id']], - [service['id']]) - - -# tenants - -tenant = {} -tenant['id'] = sqlalchemy.Column('id', sqlalchemy.Integer, primary_key=True, - autoincrement=True) -tenant['name'] = sqlalchemy.Column('name', sqlalchemy.String(255), unique=True) -tenant['desc'] = sqlalchemy.Column('desc', sqlalchemy.String(255)) -tenant['enabled'] = sqlalchemy.Column('enabled', sqlalchemy.Integer) -tenants = sqlalchemy.Table('tenants', meta, *tenant.values()) - -sqlalchemy.UniqueConstraint(tenant['name']) - - -# users - -user = {} -user['id'] = sqlalchemy.Column('id', sqlalchemy.Integer, primary_key=True, - autoincrement=True) -user['name'] = sqlalchemy.Column('name', sqlalchemy.String(255), unique=True) -user['password'] = sqlalchemy.Column('password', sqlalchemy.String(255)) -user['email'] = sqlalchemy.Column('email', sqlalchemy.String(255)) -user['enabled'] = sqlalchemy.Column('enabled', sqlalchemy.Integer) -user['tenant_id'] = sqlalchemy.Column('tenant_id', sqlalchemy.Integer) -users = sqlalchemy.Table('users', meta, *user.values()) - -sqlalchemy.UniqueConstraint(user['name']) - -sqlalchemy.ForeignKeyConstraint( - [user['tenant_id']], - [tenant['id']]) - - -# credentials - -credential = {} -credential['id'] = sqlalchemy.Column('id', sqlalchemy.Integer, - primary_key=True, autoincrement=True) -credential['user_id'] = sqlalchemy.Column('user_id', sqlalchemy.Integer) -credential['tenant_id'] = sqlalchemy.Column('tenant_id', sqlalchemy.Integer, - nullable=True) -credential['type'] = sqlalchemy.Column('type', sqlalchemy.String(20)) -credential['key'] = sqlalchemy.Column('key', sqlalchemy.String(255)) -credential['secret'] = sqlalchemy.Column('secret', sqlalchemy.String(255)) -credentials = sqlalchemy.Table('credentials', meta, *credential.values()) - -sqlalchemy.ForeignKeyConstraint( - [credential['user_id']], - [user['id']]) -sqlalchemy.ForeignKeyConstraint( - [credential['tenant_id']], - [tenant['id']]) - - -# tokens - -token = {} -token['id'] = sqlalchemy.Column('id', sqlalchemy.String(255), primary_key=True, - unique=True) -token['user_id'] = sqlalchemy.Column('user_id', sqlalchemy.Integer) -token['tenant_id'] = sqlalchemy.Column('tenant_id', sqlalchemy.Integer) -token['expires'] = sqlalchemy.Column('expires', sqlalchemy.DateTime) -tokens = sqlalchemy.Table('token', meta, *token.values()) - - -# endpoint_templates - -endpoint_template = {} -endpoint_template['id'] = sqlalchemy.Column('id', sqlalchemy.Integer, - primary_key=True) -endpoint_template['region'] = sqlalchemy.Column('region', - sqlalchemy.String(255)) -endpoint_template['service_id'] = sqlalchemy.Column('service_id', - sqlalchemy.Integer) -endpoint_template['public_url'] = sqlalchemy.Column('public_url', - sqlalchemy.String(2000)) -endpoint_template['admin_url'] = sqlalchemy.Column('admin_url', - sqlalchemy.String(2000)) -endpoint_template['internal_url'] = sqlalchemy.Column('internal_url', - sqlalchemy.String(2000)) -endpoint_template['enabled'] = sqlalchemy.Column('enabled', - sqlalchemy.Boolean) -endpoint_template['is_global'] = sqlalchemy.Column('is_global', - sqlalchemy.Boolean) -endpoint_templates = sqlalchemy.Table('endpoint_templates', meta, - *endpoint_template.values()) - -sqlalchemy.ForeignKeyConstraint( - [endpoint_template['service_id']], [service['id']]) - - -# endpoints - -endpoint = {} -endpoint['id'] = sqlalchemy.Column('id', sqlalchemy.Integer, primary_key=True) -endpoint['tenant_id'] = sqlalchemy.Column('tenant_id', sqlalchemy.Integer) -endpoint['endpoint_template_id'] = sqlalchemy.Column('endpoint_template_id', - sqlalchemy.Integer) -endpoints = sqlalchemy.Table('endpoints', meta, *endpoint.values()) - -sqlalchemy.UniqueConstraint( - endpoint['endpoint_template_id'], endpoint['tenant_id']) - -sqlalchemy.ForeignKeyConstraint( - [endpoint['endpoint_template_id']], - [endpoint_template['id']]) - - -# user_roles - -user_role = {} -user_role['id'] = sqlalchemy.Column('id', sqlalchemy.Integer, primary_key=True) -user_role['user_id'] = sqlalchemy.Column('user_id', sqlalchemy.Integer) -user_role['role_id'] = sqlalchemy.Column('role_id', sqlalchemy.Integer) -user_role['tenant_id'] = sqlalchemy.Column('tenant_id', sqlalchemy.Integer) -user_roles = sqlalchemy.Table('user_roles', meta, *user_role.values()) - -sqlalchemy.UniqueConstraint( - user_role['user_id'], user_role['role_id'], user_role['tenant_id']) - -sqlalchemy.ForeignKeyConstraint( - [user_role['user_id']], - [user['id']]) -sqlalchemy.ForeignKeyConstraint( - [user_role['role_id']], - [role['id']]) -sqlalchemy.ForeignKeyConstraint( - [user_role['tenant_id']], - [tenant['id']]) - - -def upgrade(migrate_engine): - meta.bind = migrate_engine - - user_roles.create() - endpoints.create() - roles.create() - services.create() - tenants.create() - users.create() - credentials.create() - tokens.create() - endpoint_templates.create() - - -def downgrade(migrate_engine): - meta.bind = migrate_engine - - user_roles.drop() - endpoints.drop() - roles.drop() - services.drop() - tenants.drop() - users.drop() - credentials.drop() - tokens.drop() - endpoint_templates.drop() diff --git a/keystone/backends/sqlalchemy/migrate_repo/versions/002_rename_token_table.py b/keystone/backends/sqlalchemy/migrate_repo/versions/002_rename_token_table.py deleted file mode 100644 index e350b2a418..0000000000 --- a/keystone/backends/sqlalchemy/migrate_repo/versions/002_rename_token_table.py +++ /dev/null @@ -1,26 +0,0 @@ -""" -Addresses bug 854425 - -Renames the 'token' table to 'tokens', -in order to appear more consistent with -other table names. -""" -# pylint: disable=C0103,R0801 - - -import sqlalchemy - - -meta = sqlalchemy.MetaData() - - -def upgrade(migrate_engine): - meta.bind = migrate_engine - # pylint: disable=E1101 - sqlalchemy.Table('token', meta).rename('tokens') - - -def downgrade(migrate_engine): - meta.bind = migrate_engine - # pylint: disable=E1101 - sqlalchemy.Table('tokens', meta).rename('token') diff --git a/keystone/backends/sqlalchemy/migrate_repo/versions/003_add_endpoint_template_versions.py b/keystone/backends/sqlalchemy/migrate_repo/versions/003_add_endpoint_template_versions.py deleted file mode 100644 index be5bde241a..0000000000 --- a/keystone/backends/sqlalchemy/migrate_repo/versions/003_add_endpoint_template_versions.py +++ /dev/null @@ -1,64 +0,0 @@ -""" -Adds support for versioning endpoint templates -""" -# pylint: disable=C0103,R0801 - - -import sqlalchemy -import migrate - - -meta = sqlalchemy.MetaData() - -endpoint_template = {} -endpoint_template['id'] = sqlalchemy.Column('id', sqlalchemy.Integer, - primary_key=True) -endpoint_template['region'] = sqlalchemy.Column('region', - sqlalchemy.String(255)) -endpoint_template['service_id'] = sqlalchemy.Column('service_id', - sqlalchemy.Integer) -endpoint_template['public_url'] = sqlalchemy.Column('public_url', - sqlalchemy.String(2000)) -endpoint_template['admin_url'] = sqlalchemy.Column('admin_url', - sqlalchemy.String(2000)) -endpoint_template['internal_url'] = sqlalchemy.Column('internal_url', - sqlalchemy.String(2000)) -endpoint_template['enabled'] = sqlalchemy.Column('enabled', - sqlalchemy.Boolean) -endpoint_template['is_global'] = sqlalchemy.Column('is_global', - sqlalchemy.Boolean) -endpoint_templates = sqlalchemy.Table('endpoint_templates', meta, - *endpoint_template.values()) - -version_id = sqlalchemy.Column('version_id', sqlalchemy.String(20), - nullable=True) -version_list = sqlalchemy.Column('version_list', sqlalchemy.String(2000), - nullable=True) -version_info = sqlalchemy.Column('version_info', sqlalchemy.String(500), - nullable=True) - - -def upgrade(migrate_engine): - meta.bind = migrate_engine - - migrate.create_column(version_id, endpoint_templates) - assert endpoint_templates.c.version_id is version_id - - migrate.create_column(version_list, endpoint_templates) - assert endpoint_templates.c.version_list is version_list - - migrate.create_column(version_info, endpoint_templates) - assert endpoint_templates.c.version_info is version_info - - -def downgrade(migrate_engine): - meta.bind = migrate_engine - - migrate.drop_column(version_id, endpoint_templates) - assert not hasattr(endpoint_templates.c, 'version_id') - - migrate.drop_column(version_list, endpoint_templates) - assert not hasattr(endpoint_templates.c, 'version_list') - - migrate.drop_column(version_info, endpoint_templates) - assert not hasattr(endpoint_templates.c, 'version_info') diff --git a/keystone/backends/sqlalchemy/migrate_repo/versions/004_add_service_owner.py b/keystone/backends/sqlalchemy/migrate_repo/versions/004_add_service_owner.py deleted file mode 100644 index 4bf02c8ed4..0000000000 --- a/keystone/backends/sqlalchemy/migrate_repo/versions/004_add_service_owner.py +++ /dev/null @@ -1,39 +0,0 @@ -""" -Adds support for owner id in services -""" -# pylint: disable=C0103,R0801 - - -import sqlalchemy -import migrate - - -meta = sqlalchemy.MetaData() - -service = {} -service['id'] = sqlalchemy.Column('id', sqlalchemy.Integer, - primary_key=True, autoincrement=True) -service['name'] = sqlalchemy.Column('name', sqlalchemy.String(255), - unique=True) -service['type'] = sqlalchemy.Column('type', sqlalchemy.String(255)) -service['desc'] = sqlalchemy.Column('desc', sqlalchemy.String(255)) -services = sqlalchemy.Table('services', meta, *service.values()) - -owner_id = sqlalchemy.Column('owner_id', sqlalchemy.Integer, - nullable=True) - -sqlalchemy.UniqueConstraint(service['name']) - - -def upgrade(migrate_engine): - meta.bind = migrate_engine - - migrate.create_column(owner_id, services) - assert services.c.owner_id is owner_id - - -def downgrade(migrate_engine): - meta.bind = migrate_engine - - migrate.drop_column(owner_id, services) - assert not hasattr(services.c, 'owner_id') diff --git a/keystone/backends/sqlalchemy/migrate_repo/versions/005_add_tenants_uid.py b/keystone/backends/sqlalchemy/migrate_repo/versions/005_add_tenants_uid.py deleted file mode 100644 index ab8e73059b..0000000000 --- a/keystone/backends/sqlalchemy/migrate_repo/versions/005_add_tenants_uid.py +++ /dev/null @@ -1,38 +0,0 @@ -# pylint: disable=C0103,R0801 - - -import sqlalchemy -import migrate - - -meta = sqlalchemy.MetaData() - - -# define the previous state of tenants - -tenant = {} -tenant['id'] = sqlalchemy.Column('id', sqlalchemy.Integer, primary_key=True, - autoincrement=True) -tenant['name'] = sqlalchemy.Column('name', sqlalchemy.String(255), unique=True) -tenant['desc'] = sqlalchemy.Column('desc', sqlalchemy.String(255)) -tenant['enabled'] = sqlalchemy.Column('enabled', sqlalchemy.Integer) -tenants = sqlalchemy.Table('tenants', meta, *tenant.values()) - - -# this column will become unique/non-nullable after populating it -tenant_uid = sqlalchemy.Column('uid', sqlalchemy.String(255), - unique=False, nullable=True) - - -def upgrade(migrate_engine): - meta.bind = migrate_engine - - migrate.create_column(tenant_uid, tenants) - assert tenants.c.uid is tenant_uid - - -def downgrade(migrate_engine): - meta.bind = migrate_engine - - migrate.drop_column(tenant_uid, tenants) - assert not hasattr(tenants.c, 'uid') diff --git a/keystone/backends/sqlalchemy/migrate_repo/versions/006_populate_tenants_uid.py b/keystone/backends/sqlalchemy/migrate_repo/versions/006_populate_tenants_uid.py deleted file mode 100644 index 8e5ea2d4fe..0000000000 --- a/keystone/backends/sqlalchemy/migrate_repo/versions/006_populate_tenants_uid.py +++ /dev/null @@ -1,40 +0,0 @@ -""" -Data migration to populate tenants.uid with existing tenants.id values. -""" -# pylint: disable=C0103,R0801 - - -import sqlalchemy - - -meta = sqlalchemy.MetaData() - - -# define the previous state of tenants - -tenant = {} -tenant['id'] = sqlalchemy.Column('id', sqlalchemy.Integer, primary_key=True, - autoincrement=True) -tenant['uid'] = sqlalchemy.Column('uid', sqlalchemy.String(255), unique=False, - nullable=True) -tenant['name'] = sqlalchemy.Column('name', sqlalchemy.String(255), unique=True) -tenant['desc'] = sqlalchemy.Column('desc', sqlalchemy.String(255)) -tenant['enabled'] = sqlalchemy.Column('enabled', sqlalchemy.Integer) -tenants = sqlalchemy.Table('tenants', meta, *tenant.values()) - - -def upgrade(migrate_engine): - meta.bind = migrate_engine - - dtenants = tenants.select().execute() - for dtenant in dtenants: - whereclause = "`id`='%s'" % (dtenant.id) - values = {'uid': str(dtenant.id)} - - tenants.update(whereclause=whereclause, values=values).execute() - - -def downgrade(migrate_engine): - meta.bind = migrate_engine - - tenants.update(values={'uid': None}).execute() diff --git a/keystone/backends/sqlalchemy/migrate_repo/versions/007_make_tenants_uid_unique.py b/keystone/backends/sqlalchemy/migrate_repo/versions/007_make_tenants_uid_unique.py deleted file mode 100644 index 555d3c7f26..0000000000 --- a/keystone/backends/sqlalchemy/migrate_repo/versions/007_make_tenants_uid_unique.py +++ /dev/null @@ -1,59 +0,0 @@ -""" -Schema migration to enforce uniqueness on tenants.uid -""" -# pylint: disable=C0103,R0801 - - -import sqlalchemy -import migrate -from migrate.changeset import constraint - - -meta = sqlalchemy.MetaData() - - -# define the previous state of tenants - -tenant = {} -tenant['id'] = sqlalchemy.Column('id', sqlalchemy.Integer, primary_key=True, - autoincrement=True) -tenant['uid'] = sqlalchemy.Column('uid', sqlalchemy.String(255), unique=False, - nullable=True) -tenant['name'] = sqlalchemy.Column('name', sqlalchemy.String(255), unique=True) -tenant['desc'] = sqlalchemy.Column('desc', sqlalchemy.String(255)) -tenant['enabled'] = sqlalchemy.Column('enabled', sqlalchemy.Integer) -tenants = sqlalchemy.Table('tenants', meta, *tenant.values()) - - -unique_constraint = constraint.UniqueConstraint(tenant['uid']) - - -def upgrade(migrate_engine): - meta.bind = migrate_engine - - tenant['uid'].alter(nullable=False) - assert not tenants.c.uid.nullable - - unique_constraint.create() - - -def downgrade(migrate_engine): - meta.bind = migrate_engine - - try: - # this is NOT supported in sqlite! - # but let's try anyway, in case it is - unique_constraint.drop() - except migrate.exceptions.NotSupportedError, e: - if migrate_engine.name == 'sqlite': - # skipping the constraint drop doesn't seem to cause any issues - # *in sqlite* - # as constraints are only checked on row insert/update, - # and don't apply to nulls. - print 'WARNING: Skipping dropping unique constraint ' \ - 'from `tenants`, UNIQUE (uid)' - else: - raise e - - tenant['uid'].alter(nullable=True) - assert tenants.c.uid.nullable diff --git a/keystone/backends/sqlalchemy/migrate_repo/versions/008_add_users_uid.py b/keystone/backends/sqlalchemy/migrate_repo/versions/008_add_users_uid.py deleted file mode 100644 index c634cae910..0000000000 --- a/keystone/backends/sqlalchemy/migrate_repo/versions/008_add_users_uid.py +++ /dev/null @@ -1,40 +0,0 @@ -# pylint: disable=C0103,R0801 - - -import sqlalchemy -import migrate - - -meta = sqlalchemy.MetaData() - - -# define the previous state of users - -user = {} -user['id'] = sqlalchemy.Column('id', sqlalchemy.Integer, primary_key=True, - autoincrement=True) -user['name'] = sqlalchemy.Column('name', sqlalchemy.String(255), unique=True) -user['password'] = sqlalchemy.Column('password', sqlalchemy.String(255)) -user['email'] = sqlalchemy.Column('email', sqlalchemy.String(255)) -user['enabled'] = sqlalchemy.Column('enabled', sqlalchemy.Integer) -user['tenant_id'] = sqlalchemy.Column('tenant_id', sqlalchemy.Integer) -users = sqlalchemy.Table('users', meta, *user.values()) - - -# this column will become unique/non-nullable after populating it -user_uid = sqlalchemy.Column('uid', sqlalchemy.String(255), - unique=False, nullable=True) - - -def upgrade(migrate_engine): - meta.bind = migrate_engine - - migrate.create_column(user_uid, users) - assert users.c.uid is user_uid - - -def downgrade(migrate_engine): - meta.bind = migrate_engine - - migrate.drop_column(user_uid, users) - assert not hasattr(users.c, 'uid') diff --git a/keystone/backends/sqlalchemy/migrate_repo/versions/009_populate_users_uid.py b/keystone/backends/sqlalchemy/migrate_repo/versions/009_populate_users_uid.py deleted file mode 100644 index c769eaa5e3..0000000000 --- a/keystone/backends/sqlalchemy/migrate_repo/versions/009_populate_users_uid.py +++ /dev/null @@ -1,42 +0,0 @@ -""" -Data migration to populate users.uid with existing users.id values. -""" -# pylint: disable=C0103,R0801 - - -import sqlalchemy - - -meta = sqlalchemy.MetaData() - - -# define the previous state of users - -user = {} -user['id'] = sqlalchemy.Column('id', sqlalchemy.Integer, primary_key=True, - autoincrement=True) -user['uid'] = sqlalchemy.Column('uid', sqlalchemy.String(255), unique=False, - nullable=True) -user['name'] = sqlalchemy.Column('name', sqlalchemy.String(255), unique=True) -user['password'] = sqlalchemy.Column('password', sqlalchemy.String(255)) -user['email'] = sqlalchemy.Column('email', sqlalchemy.String(255)) -user['enabled'] = sqlalchemy.Column('enabled', sqlalchemy.Integer) -user['tenant_id'] = sqlalchemy.Column('tenant_id', sqlalchemy.Integer) -users = sqlalchemy.Table('users', meta, *user.values()) - - -def upgrade(migrate_engine): - meta.bind = migrate_engine - - dusers = users.select().execute() - for duser in dusers: - whereclause = "`id`='%s'" % (duser.id) - values = {'uid': str(duser.id)} - - users.update(whereclause=whereclause, values=values).execute() - - -def downgrade(migrate_engine): - meta.bind = migrate_engine - - users.update(values={'uid': None}).execute() diff --git a/keystone/backends/sqlalchemy/migrate_repo/versions/010_make_users_uid_unique.py b/keystone/backends/sqlalchemy/migrate_repo/versions/010_make_users_uid_unique.py deleted file mode 100644 index 164be7d4ba..0000000000 --- a/keystone/backends/sqlalchemy/migrate_repo/versions/010_make_users_uid_unique.py +++ /dev/null @@ -1,60 +0,0 @@ -""" -Schema migration to enforce uniqueness on users.uid -""" -# pylint: disable=C0103,R0801 - - -import sqlalchemy -import migrate -from migrate.changeset import constraint - - -meta = sqlalchemy.MetaData() - - -# define the previous state of users - -user = {} -user['id'] = sqlalchemy.Column('id', sqlalchemy.Integer, primary_key=True, - autoincrement=True) -user['uid'] = sqlalchemy.Column('uid', sqlalchemy.String(255), unique=False, - nullable=True) -user['name'] = sqlalchemy.Column('name', sqlalchemy.String(255), unique=True) -user['password'] = sqlalchemy.Column('password', sqlalchemy.String(255)) -user['email'] = sqlalchemy.Column('email', sqlalchemy.String(255)) -user['enabled'] = sqlalchemy.Column('enabled', sqlalchemy.Integer) -user['tenant_id'] = sqlalchemy.Column('tenant_id', sqlalchemy.Integer) -users = sqlalchemy.Table('users', meta, *user.values()) - -unique_constraint = constraint.UniqueConstraint(user['uid']) - - -def upgrade(migrate_engine): - meta.bind = migrate_engine - - user['uid'].alter(nullable=False) - assert not users.c.uid.nullable - - unique_constraint.create() - - -def downgrade(migrate_engine): - meta.bind = migrate_engine - - try: - # this is NOT supported in sqlite! - # but let's try anyway, in case it is - unique_constraint.drop() - except migrate.exceptions.NotSupportedError, e: - if migrate_engine.name == 'sqlite': - # skipping the constraint drop doesn't seem to cause any issues - # *in sqlite* - # as constraints are only checked on row insert/update, - # and don't apply to nulls. - print 'WARNING: Skipping dropping unique constraint ' \ - 'from `users`, UNIQUE (uid)' - else: - raise e - - user['uid'].alter(nullable=True) - assert users.c.uid.nullable diff --git a/keystone/backends/sqlalchemy/migrate_repo/versions/011_is_enabled_boolean.py b/keystone/backends/sqlalchemy/migrate_repo/versions/011_is_enabled_boolean.py deleted file mode 100644 index 47b4a0bf76..0000000000 --- a/keystone/backends/sqlalchemy/migrate_repo/versions/011_is_enabled_boolean.py +++ /dev/null @@ -1,58 +0,0 @@ -""" -Change 'enabled' columns to boolean types -""" -# pylint: disable=C0103,R0801 - - -import sqlalchemy -from migrate.changeset import constraint - - -meta = sqlalchemy.MetaData() - - -# define the previous state of users - -user = {} -user['id'] = sqlalchemy.Column('id', sqlalchemy.Integer, primary_key=True, - autoincrement=True) -user['uid'] = sqlalchemy.Column('uid', sqlalchemy.String(255), unique=False, - nullable=False) -user['name'] = sqlalchemy.Column('name', sqlalchemy.String(255), unique=True) -user['password'] = sqlalchemy.Column('password', sqlalchemy.String(255)) -user['email'] = sqlalchemy.Column('email', sqlalchemy.String(255)) -user['enabled'] = sqlalchemy.Column('enabled', sqlalchemy.Integer) -user['tenant_id'] = sqlalchemy.Column('tenant_id', sqlalchemy.Integer) -users = sqlalchemy.Table('users', meta, *user.values()) -constraint.UniqueConstraint(user['uid']) - -tenant = {} -tenant['id'] = sqlalchemy.Column('id', sqlalchemy.Integer, primary_key=True, - autoincrement=True) -tenant['uid'] = sqlalchemy.Column('uid', sqlalchemy.String(255), unique=False, - nullable=False) -tenant['name'] = sqlalchemy.Column('name', sqlalchemy.String(255), unique=True) -tenant['desc'] = sqlalchemy.Column('desc', sqlalchemy.String(255)) -tenant['enabled'] = sqlalchemy.Column('enabled', sqlalchemy.Integer) -tenants = sqlalchemy.Table('tenants', meta, *tenant.values()) -constraint.UniqueConstraint(tenant['uid']) - - -def upgrade(migrate_engine): - meta.bind = migrate_engine - - user['enabled'].alter(type=sqlalchemy.Boolean) - assert users.c.enabled is user['enabled'] - - tenant['enabled'].alter(type=sqlalchemy.Boolean) - assert tenants.c.enabled is tenant['enabled'] - - -def downgrade(migrate_engine): - meta.bind = migrate_engine - - user['enabled'].alter(type=sqlalchemy.Integer) - assert users.c.enabled is user['enabled'] - - tenant['enabled'].alter(type=sqlalchemy.Integer) - assert tenants.c.enabled is tenant['enabled'] diff --git a/keystone/backends/sqlalchemy/migrate_repo/versions/011_postgresql_downgrade.sql b/keystone/backends/sqlalchemy/migrate_repo/versions/011_postgresql_downgrade.sql deleted file mode 100644 index 0d0bf99a09..0000000000 --- a/keystone/backends/sqlalchemy/migrate_repo/versions/011_postgresql_downgrade.sql +++ /dev/null @@ -1,5 +0,0 @@ -ALTER TABLE users ALTER COLUMN enabled DROP DEFAULT; -ALTER TABLE users ALTER COLUMN enabled TYPE integer USING CASE enabled WHEN true THEN 1 ELSE 0 END; - -ALTER TABLE tenants ALTER COLUMN enabled DROP DEFAULT; -ALTER TABLE tenants ALTER COLUMN enabled TYPE integer USING CASE enabled WHEN true THEN 1 ELSE 0 END; diff --git a/keystone/backends/sqlalchemy/migrate_repo/versions/011_postgresql_upgrade.sql b/keystone/backends/sqlalchemy/migrate_repo/versions/011_postgresql_upgrade.sql deleted file mode 100644 index f0eafb7e90..0000000000 --- a/keystone/backends/sqlalchemy/migrate_repo/versions/011_postgresql_upgrade.sql +++ /dev/null @@ -1,5 +0,0 @@ -ALTER TABLE users ALTER COLUMN enabled DROP DEFAULT; -ALTER TABLE users ALTER COLUMN enabled TYPE boolean USING CASE enabled WHEN '1' THEN true ELSE '0' END; - -ALTER TABLE tenants ALTER COLUMN enabled DROP DEFAULT; -ALTER TABLE tenants ALTER COLUMN enabled TYPE boolean USING CASE enabled WHEN '1' THEN true ELSE '0' END; diff --git a/keystone/backends/sqlalchemy/migration.py b/keystone/backends/sqlalchemy/migration.py deleted file mode 100644 index 62219272d4..0000000000 --- a/keystone/backends/sqlalchemy/migration.py +++ /dev/null @@ -1,172 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# 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. - -import logging -import os - -from migrate.versioning import api as versioning_api -# See LP bug #719834. sqlalchemy-migrate changed location of -# exceptions.py after 0.6.0. -try: - # pylint: disable=E0611 - from migrate.versioning import exceptions as versioning_exceptions -except ImportError: - from migrate import exceptions as versioning_exceptions - -from keystone.logic.types import fault - - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -def get_migrate_repo_path(): - """Get the path for the migrate repository.""" - path = os.path.join(os.path.abspath(os.path.dirname(__file__)), - 'migrate_repo') - assert os.path.exists(path) - return path - - -def get_migrate_repo(repo_path): - return versioning_api.repository.Repository(repo_path) - - -def get_schema(engine, repo_path): - return versioning_api.schema.ControlledSchema(engine, repo_path) - - -def get_repo_version(repo_path): - return get_migrate_repo(repo_path).latest - - -def get_db_version(engine, repo_path): - return get_schema(engine, repo_path).version - - -def db_goto_version(sql_connection, version): - """ - Jump to a specific database version without performing migrations. - - :param sql_connection: sqlalchemy connection string - :param version: version to jump to - """ - - # pylint: disable=W0613 - @versioning_api.with_engine - def set_db_version(url, repository, old_v, new_v, **opts): - engine = opts.pop('engine') - schema = get_schema(engine, repo_path) - schema.update_repository_table(old_v, new_v) - return True - - repo_path = get_migrate_repo_path() - new_version = int(version) - try: - old_version = versioning_api.db_version(sql_connection, repo_path) - if new_version != old_version: - return set_db_version(sql_connection, repo_path, old_version, - new_version) - except versioning_exceptions.DatabaseNotControlledError: - msg = (_("database '%(sql_connection)s' is not under " - "migration control") % locals()) - raise fault.DatabaseMigrationError(msg) - - -def db_version(sql_connection): - """ - Return the database's current migration number - - :param sql_connection: sqlalchemy connection string - :retval version number - """ - repo_path = get_migrate_repo_path() - try: - return versioning_api.db_version(sql_connection, repo_path) - except versioning_exceptions.DatabaseNotControlledError: - msg = (_("database '%(sql_connection)s' is not under " - "migration control") % locals()) - raise fault.DatabaseMigrationError(msg) - - -def upgrade(sql_connection, version=None): - """ - Upgrade the database's current migration level - - :param sql_connection: sqlalchemy connection string - :param version: version to upgrade (defaults to latest) - :retval version number - """ - db_version(sql_connection) # Ensure db is under migration control - repo_path = get_migrate_repo_path() - version_str = version or 'latest' # pylint: disable=W0612 - logger.info(_("Upgrading %(sql_connection)s to version %(version_str)s") % - locals()) - return versioning_api.upgrade(sql_connection, repo_path, version) - - -def downgrade(sql_connection, version): - """ - Downgrade the database's current migration level - - :param sql_connection: sqlalchemy connection string - :param version: version to downgrade to - :retval version number - """ - db_version(sql_connection) # Ensure db is under migration control - repo_path = get_migrate_repo_path() - logger.info(_("Downgrading %(sql_connection)s to version %(version)s") % - locals()) - return versioning_api.downgrade(sql_connection, repo_path, version) - - -def version_control(sql_connection): - """ - Place a database under migration control - - :param sql_connection: sqlalchemy connection string - """ - try: - _version_control(sql_connection) - except versioning_exceptions.DatabaseAlreadyControlledError: - msg = (_("database '%(sql_connection)s' is already under migration " - "control") % locals()) - raise fault.DatabaseMigrationError(msg) - - -def _version_control(sql_connection): - """ - Place a database under migration control - - :param sql_connection: sqlalchemy connection string - """ - repo_path = get_migrate_repo_path() - return versioning_api.version_control(sql_connection, repo_path) - - -def db_sync(sql_connection, version=None): - """ - Place a database under migration control and perform an upgrade - - :param sql_connection: sqlalchemy connection string - :retval version number - """ - try: - _version_control(sql_connection) - except versioning_exceptions.DatabaseAlreadyControlledError: - pass - - upgrade(sql_connection, version=version) diff --git a/keystone/backends/sqlalchemy/models.py b/keystone/backends/sqlalchemy/models.py deleted file mode 100755 index 7efe059c37..0000000000 --- a/keystone/backends/sqlalchemy/models.py +++ /dev/null @@ -1,187 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 2010-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. - -from sqlalchemy import Column, String, Integer, ForeignKey, \ - UniqueConstraint, Boolean, DateTime -from sqlalchemy.exc import IntegrityError -from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy.orm import relationship, object_mapper - -# pylint: disable=C0103 -Base = declarative_base() - - -class KeystoneBase(object): - """Base class for Keystone Models.""" - __api__ = None - # pylint: disable=C0103 - _i = None - - def save(self, session=None): - """Save this object.""" - - if not session: - from keystone.backends.sqlalchemy import get_session - session = get_session() - session.add(self) - try: - session.flush() - except IntegrityError: - raise - - def delete(self, session=None): - """Delete this object.""" - self.save(session=session) - - def __setitem__(self, key, value): - setattr(self, key, value) - - def __getitem__(self, key): - return getattr(self, key) - - def get(self, key, default=None): - return getattr(self, key, default) - - def __iter__(self): - self._i = iter(object_mapper(self).columns) - return self - - def next(self): - n = self._i.next().name - return n, getattr(self, n) - - def update(self, values): - """Make the model object behave like a dict""" - for k, v in values.iteritems(): - setattr(self, k, v) - - def iteritems(self): - """Make the model object behave like a dict. - - Includes attributes from joins.""" - local = dict(self) - joined = dict([(k, v) for k, v in self.__dict__.iteritems() - if not k[0] == '_']) - local.update(joined) - return local.iteritems() - - def copy(self): - """Make the model object behave like a dict.""" - return dict(self).copy() - - -# Define associations first -class UserRoleAssociation(Base, KeystoneBase): - __tablename__ = 'user_roles' - id = Column(Integer, primary_key=True) - user_id = Column(Integer, ForeignKey('users.id')) - role_id = Column(Integer, ForeignKey('roles.id')) - tenant_id = Column(Integer, ForeignKey('tenants.id')) - __table_args__ = (UniqueConstraint("user_id", "role_id", "tenant_id"), {}) - - user = relationship('User') - role = relationship('Role') - - -class Endpoints(Base, KeystoneBase): - __tablename__ = 'endpoints' - id = Column(Integer, primary_key=True) - tenant_id = Column(Integer) - endpoint_template_id = Column(Integer, ForeignKey('endpoint_templates.id')) - __table_args__ = ( - UniqueConstraint("endpoint_template_id", "tenant_id"), {}) - - -# Define objects -class Role(Base, KeystoneBase): - __tablename__ = 'roles' - __api__ = 'role' - id = Column(Integer, primary_key=True, autoincrement=True) - name = Column(String(255)) - desc = Column(String(255)) - service_id = Column(Integer, ForeignKey('services.id')) - __table_args__ = ( - UniqueConstraint("name", "service_id"), {}) - - -class Service(Base, KeystoneBase): - __tablename__ = 'services' - __api__ = 'service' - id = Column(Integer, primary_key=True, autoincrement=True) - name = Column(String(255), unique=True) - type = Column(String(255)) - desc = Column(String(255)) - owner_id = Column(Integer, ForeignKey('users.id')) - - -class Tenant(Base, KeystoneBase): - __tablename__ = 'tenants' - __api__ = 'tenant' - id = Column(Integer, primary_key=True, autoincrement=True) - uid = Column(String(255), unique=True, nullable=False) - name = Column(String(255), unique=True) - desc = Column(String(255)) - enabled = Column(Boolean) - - -class User(Base, KeystoneBase): - __tablename__ = 'users' - __api__ = 'user' - id = Column(Integer, primary_key=True, autoincrement=True) - uid = Column(String(255), unique=True, nullable=False) - name = Column(String(255), unique=True) - password = Column(String(255)) - email = Column(String(255)) - enabled = Column(Boolean) - tenant_id = Column(Integer, ForeignKey('tenants.id')) - roles = relationship(UserRoleAssociation, cascade="all") - credentials = relationship('Credentials', backref='user', cascade="all") - - -class Credentials(Base, KeystoneBase): - __tablename__ = 'credentials' - __api__ = 'credentials' - id = Column(Integer, primary_key=True, autoincrement=True) - user_id = Column(Integer, ForeignKey('users.id')) - tenant_id = Column(Integer, ForeignKey('tenants.id'), nullable=True) - type = Column(String(20)) # ('Password','APIKey','EC2') - key = Column(String(255)) - secret = Column(String(255)) - - -class Token(Base, KeystoneBase): - __tablename__ = 'tokens' - __api__ = 'token' - id = Column(String(255), primary_key=True, unique=True) - user_id = Column(Integer) - tenant_id = Column(Integer) - expires = Column(DateTime) - - -class EndpointTemplates(Base, KeystoneBase): - __tablename__ = 'endpoint_templates' - __api__ = 'endpoint_template' - id = Column(Integer, primary_key=True) - region = Column(String(255)) - service_id = Column(Integer, ForeignKey('services.id')) - public_url = Column(String(2000)) - admin_url = Column(String(2000)) - internal_url = Column(String(2000)) - enabled = Column(Boolean) - is_global = Column(Boolean) - version_id = Column(String(20)) - version_list = Column(String(2000)) - version_info = Column(String(500)) diff --git a/keystone/catalog/__init__.py b/keystone/catalog/__init__.py new file mode 100644 index 0000000000..95af1256fd --- /dev/null +++ b/keystone/catalog/__init__.py @@ -0,0 +1 @@ +from keystone.catalog.core import * diff --git a/doc/source/__init__.py b/keystone/catalog/backends/__init__.py similarity index 100% rename from doc/source/__init__.py rename to keystone/catalog/backends/__init__.py diff --git a/keystone/catalog/backends/kvs.py b/keystone/catalog/backends/kvs.py new file mode 100644 index 0000000000..8ee781ba94 --- /dev/null +++ b/keystone/catalog/backends/kvs.py @@ -0,0 +1,39 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + + +from keystone.common import kvs + + +class Catalog(kvs.Base): + # Public interface + def get_catalog(self, user_id, tenant_id, metadata=None): + return self.db.get('catalog-%s-%s' % (tenant_id, user_id)) + + def get_service(self, service_id): + return self.db.get('service-%s' % service_id) + + def list_services(self): + return self.db.get('service_list', []) + + def create_service(self, service_id, service): + self.db.set('service-%s' % service_id, service) + service_list = set(self.db.get('service_list', [])) + service_list.add(service_id) + self.db.set('service_list', list(service_list)) + return service + + def update_service(self, service_id, service): + self.db.set('service-%s' % service_id, service) + return service + + def delete_service(self, service_id): + self.db.delete('service-%s' % service_id) + service_list = set(self.db.get('service_list', [])) + service_list.remove(service_id) + self.db.set('service_list', list(service_list)) + return None + + # Private interface + def _create_catalog(self, user_id, tenant_id, data): + self.db.set('catalog-%s-%s' % (tenant_id, user_id), data) + return data diff --git a/keystone/catalog/backends/templated.py b/keystone/catalog/backends/templated.py new file mode 100644 index 0000000000..b6e7036fdd --- /dev/null +++ b/keystone/catalog/backends/templated.py @@ -0,0 +1,95 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +from keystone import config +from keystone.common import logging +from keystone.catalog.backends import kvs + + +CONF = config.CONF +config.register_str('template_file', group='catalog') + + +def parse_templates(template_lines): + o = {} + for line in template_lines: + if ' = ' not in line: + continue + + k, v = line.strip().split(' = ') + if not k.startswith('catalog.'): + continue + + parts = k.split('.') + + region = parts[1] + # NOTE(termie): object-store insists on having a dash + service = parts[2].replace('_', '-') + key = parts[3] + + region_ref = o.get(region, {}) + service_ref = region_ref.get(service, {}) + service_ref[key] = v + + region_ref[service] = service_ref + o[region] = region_ref + + return o + + +class TemplatedCatalog(kvs.Catalog): + """A backend that generates endpoints for the Catalog based on templates. + + It is usually configured via config entries that look like: + + catalog.$REGION.$SERVICE.$key = $value + + and is stored in a similar looking hierarchy. Where a value can contain + values to be interpolated by standard python string interpolation that look + like (the % is replaced by a $ due to paste attmepting to interpolate on + its own: + + http://localhost:$(public_port)s/ + + When expanding the template it will pass in a dict made up of the conf + instance plus a few additional key-values, notably tenant_id and user_id. + + It does not care what the keys and values are but it is worth noting that + keystone_compat will expect certain keys to be there so that it can munge + them into the output format keystone expects. These keys are: + + name - the name of the service, most likely repeated for all services of + the same type, across regions. + + adminURL - the url of the admin endpoint + + publicURL - the url of the public endpoint + + internalURL - the url of the internal endpoint + + """ + + def __init__(self, templates=None): + if templates: + self.templates = templates + else: + self._load_templates(CONF.catalog.template_file) + super(TemplatedCatalog, self).__init__() + + def _load_templates(self, template_file): + self.templates = parse_templates(open(template_file)) + + def get_catalog(self, user_id, tenant_id, metadata=None): + d = dict(CONF.iteritems()) + d.update({'tenant_id': tenant_id, + 'user_id': user_id}) + + o = {} + for region, region_ref in self.templates.iteritems(): + o[region] = {} + for service, service_ref in region_ref.iteritems(): + o[region][service] = {} + for k, v in service_ref.iteritems(): + v = v.replace('$(', '%(') + o[region][service][k] = v % d + + return o diff --git a/keystone/catalog/core.py b/keystone/catalog/core.py new file mode 100644 index 0000000000..93c0de2077 --- /dev/null +++ b/keystone/catalog/core.py @@ -0,0 +1,57 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +"""Main entry point into the Catalog service.""" + +import uuid + +import webob.exc + +from keystone import config +from keystone.common import manager +from keystone.common import wsgi + + +CONF = config.CONF + + +class Manager(manager.Manager): + """Default pivot point for the Catalog backend. + + See :mod:`keystone.common.manager.Manager` for more details on how this + dynamically calls the backend. + + """ + + def __init__(self): + super(Manager, self).__init__(CONF.catalog.driver) + + +class ServiceController(wsgi.Application): + def __init__(self): + self.catalog_api = Manager() + super(ServiceController, self).__init__() + + # CRUD extensions + # NOTE(termie): this OS-KSADM stuff is not very consistent + def get_services(self, context): + service_list = self.catalog_api.list_services(context) + service_refs = [self.catalog_api.get_service(context, x) + for x in service_list] + return {'OS-KSADM:services': service_refs} + + def get_service(self, context, service_id): + service_ref = self.catalog_api.get_service(context, service_id) + if not service_ref: + raise webob.exc.HTTPNotFound() + return {'OS-KSADM:service': service_ref} + + def delete_service(self, context, service_id): + service_ref = self.catalog_api.delete_service(context, service_id) + + def create_service(self, context, OS_KSADM_service): + service_id = uuid.uuid4().hex + service_ref = OS_KSADM_service.copy() + service_ref['id'] = service_id + new_service_ref = self.catalog_api.create_service( + context, service_id, service_ref) + return {'OS-KSADM:service': new_service_ref} diff --git a/keystone/cli.py b/keystone/cli.py new file mode 100644 index 0000000000..b6b5abb8f3 --- /dev/null +++ b/keystone/cli.py @@ -0,0 +1,129 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +from __future__ import absolute_import + +import json +import logging +import sys +import StringIO +import textwrap + +import cli.app +import cli.log + +from keystone import config +from keystone.common import utils + + +CONF = config.CONF +CONF.set_usage('%prog COMMAND [key1=value1 key2=value2 ...]') + + +class BaseApp(cli.log.LoggingApp): + def __init__(self, *args, **kw): + kw.setdefault('name', self.__class__.__name__.lower()) + super(BaseApp, self).__init__(*args, **kw) + + def add_default_params(self): + for args, kw in DEFAULT_PARAMS: + self.add_param(*args, **kw) + + def _parse_keyvalues(self, args): + kv = {} + for x in args: + key, value = x.split('=', 1) + # make lists if there are multiple values + if key.endswith('[]'): + key = key[:-2] + existing = kv.get(key, []) + existing.append(value) + kv[key] = existing + else: + kv[key] = value + return kv + + +class DbSync(BaseApp): + """Sync the database.""" + + name = 'db_sync' + + def __init__(self, *args, **kw): + super(DbSync, self).__init__(*args, **kw) + + def main(self): + for k in ['identity', 'catalog', 'policy', 'token']: + driver = utils.import_object(getattr(CONF, k).driver) + if hasattr(driver, 'db_sync'): + driver.db_sync() + + +class ImportLegacy(BaseApp): + """Import a legacy database.""" + + name = 'import_legacy' + + def __init__(self, *args, **kw): + super(ImportLegacy, self).__init__(*args, **kw) + self.add_param('old_db', nargs=1) + + def main(self): + from keystone.common.sql import legacy + old_db = self.params.old_db[0] + migration = legacy.LegacyMigration(old_db) + migration.migrate_all() + + +class ExportLegacyCatalog(BaseApp): + """Export the service catalog from a legacy database.""" + + name = 'export_legacy_catalog' + + def __init__(self, *args, **kw): + super(ExportLegacyCatalog, self).__init__(*args, **kw) + self.add_param('old_db', nargs=1) + + def main(self): + from keystone.common.sql import legacy + old_db = self.params.old_db[0] + migration = legacy.LegacyMigration(old_db) + print '\n'.join(migration.dump_catalog()) + + +CMDS = {'db_sync': DbSync, + 'import_legacy': ImportLegacy, + 'export_legacy_catalog': ExportLegacyCatalog, + } + + +def print_commands(cmds): + print + print 'Available commands:' + o = [] + max_length = max([len(k) for k in cmds]) + 2 + for k, cmd in sorted(cmds.iteritems()): + initial_indent = '%s%s: ' % (' ' * (max_length - len(k)), k) + tw = textwrap.TextWrapper(initial_indent=initial_indent, + subsequent_indent=' ' * (max_length + 2), + width=80) + o.extend(tw.wrap( + (cmd.__doc__ and cmd.__doc__ or 'no docs').strip().split('\n')[0])) + print '\n'.join(o) + + +def run(cmd, args): + return CMDS[cmd](argv=args).run() + + +def main(argv=None, config_files=None): + CONF.reset() + args = CONF(config_files=config_files, args=argv) + + if len(args) < 2: + CONF.print_help() + print_commands(CMDS) + sys.exit(1) + + cmd = args[1] + if cmd in CMDS: + return run(cmd, (args[:1] + args[2:])) diff --git a/keystone/client.py b/keystone/client.py deleted file mode 100644 index 2e4a075d09..0000000000 --- a/keystone/client.py +++ /dev/null @@ -1,193 +0,0 @@ -# Copyright (C) 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. - -"""Python HTTP clients for accessing Keystone's Service and Admin APIs.""" - -import httplib -import json -import logging - -import keystone.common.exception - -LOG = logging.getLogger(__name__) - - -class ServiceClient(object): - """Keystone v2.0 HTTP API client for normal service function. - - Provides functionality for retrieving new tokens and for retrieving - a list of tenants which the supplied token has access to. - - """ - - _default_port = 5000 - - def __init__(self, host, port=None, is_ssl=False, cert_file=None): - """Initialize client. - - :param host: The hostname or IP of the Keystone service to use - :param port: The port of the Keystone service to use - - """ - self.host = host - self.port = port or self._default_port - self.is_ssl = is_ssl - self.cert_file = cert_file - - def _http_request(self, verb, path, body=None, headers=None): - """Perform an HTTP request and return the HTTP response. - - :param verb: HTTP verb (e.g. GET, POST, etc.) - :param path: HTTP path (e.g. /v2.0/tokens) - :param body: HTTP Body content - :param headers: Dictionary of HTTP headers - :returns: httplib.HTTPResponse object - - """ - LOG.debug("Connecting to %s" % self.auth_address) - if (self.is_ssl): - connection = httplib.HTTPSConnection(self.auth_address, - cert_file=self.cert_file) - else: - connection = httplib.HTTPConnection(self.auth_address) - connection.request(verb, path, body=body, headers=headers) - response = connection.getresponse() - response.body = response.read() - status_int = int(response.status) - connection.close() - - if status_int < 200 or status_int >= 300: - msg = "Client received HTTP %d" % status_int - raise keystone.common.exception.ClientError(msg) - - return response - - @property - def auth_address(self): - """Return a host:port combination string.""" - return "%s:%d" % (self.host, self.port) - - def get_token(self, username, password): - """Retrieve a token from Keystone for a given user/password. - - :param username: The user name to authenticate with - :param password: The password to authenticate with - :returns: A string token - - """ - body = json.dumps({ - "auth": { - "passwordCredentials": { - "username": username, - "password": password, - }, - }, - }) - - headers = { - "Accept": "application/json", - "Content-Type": "application/json", - } - - response = self._http_request("POST", "/v2.0/tokens", body, headers) - token_id = json.loads(response.body)["access"]["token"]["id"] - - return token_id - - -class AdminClient(ServiceClient): - """Keystone v2.0 HTTP API client for administrative functions. - - Provides functionality for retrieving new tokens, validating existing - tokens, and retrieving user information from valid tokens. - - """ - - _default_port = 35357 - _default_admin_name = "admin" - _default_admin_pass = "password" - - # pylint: disable=R0913 - def __init__(self, host, port=None, is_ssl=False, cert_file=None, - admin_name=None, admin_pass=None): - """Initialize client. - - :param host: The hostname or IP of the Keystone service to use - :param port: The port of the Keystone service to use - :param admin_name: The username to use for admin purposes - :param admin_pass: The password to use for the admin account - - """ - super(AdminClient, self).__init__(host, port=port, is_ssl=is_ssl, - cert_file=cert_file) - self.admin_name = admin_name or self._default_admin_name - self.admin_pass = admin_pass or self._default_admin_pass - self._admin_token = None - - @property - def admin_token(self): - """Retrieve a valid admin token. - - If a token has already been retrieved, ensure that it is still valid - and then return it. If it has not already been retrieved or the token - is found to be invalid, retrieve a new token and return it. - - """ - token = self._admin_token - - if token is None or not self.check_token(token, token): - token = self.get_token(self.admin_name, self.admin_pass) - - self._admin_token = token - return self._admin_token - - def validate_token(self, token): - """Validate a token, returning details about the user. - - :param token: A token string - :returns: Object representing the user the token belongs to, or None - if the token is not valid. - - """ - url = "/v2.0/tokens/%s" % token - - headers = { - "Accept": "application/json", - "X-Auth-Token": self.admin_token, - } - - try: - response = self._http_request("GET", url, headers=headers) - except keystone.common.exception.ClientError: - return None - - return json.loads(response.body) - - def check_token(self, token, admin_token=None): - """Check to see if given token is valid. - - :param token: A token string - :param admin_token: The administrative token to use - :returns: True if token is valid, otherwise False - - """ - url = "/v2.0/tokens/%s" % token - headers = {"X-Auth-Token": admin_token or self.admin_token} - - try: - self._http_request("HEAD", url, headers=headers) - except keystone.common.exception.ClientError: - return False - - return True diff --git a/keystone/common/bufferedhttp.py b/keystone/common/bufferedhttp.py index db64175d1b..95caa4f400 100644 --- a/keystone/common/bufferedhttp.py +++ b/keystone/common/bufferedhttp.py @@ -1,3 +1,5 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + # Copyright (c) 2010-2011 OpenStack, LLC. # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -30,20 +32,13 @@ from urllib import quote import logging import time -# pylint: disable=E0611 from eventlet.green.httplib import CONTINUE, HTTPConnection, HTTPMessage, \ HTTPResponse, HTTPSConnection, _UNKNOWN -DEFAULT_TIMEOUT = 30 -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -# pylint: disable=R0902 class BufferedHTTPResponse(HTTPResponse): """HTTPResponse class that buffers reading of headers""" - # pylint: disable=C0103 def __init__(self, sock, debuglevel=0, strict=0, method=None): # pragma: no cover self.sock = sock @@ -64,7 +59,6 @@ class BufferedHTTPResponse(HTTPResponse): self.length = _UNKNOWN # number of bytes left in response self.will_close = _UNKNOWN # conn will close at end of response - # pylint: disable=E1101,E0203,W0201 def expect_response(self): self.fp = self.sock.makefile('rb', 0) version, status, reason = self._read_status() @@ -79,24 +73,20 @@ class BufferedHTTPResponse(HTTPResponse): self.msg.fp = None -# pylint: disable=W0232 class BufferedHTTPConnection(HTTPConnection): """HTTPConnection class that uses BufferedHTTPResponse""" response_class = BufferedHTTPResponse - # pylint: disable=W0201 def connect(self): self._connected_time = time.time() return HTTPConnection.connect(self) - # pylint: disable=W0201 def putrequest(self, method, url, skip_host=0, skip_accept_encoding=0): self._method = method self._path = url return HTTPConnection.putrequest(self, method, url, skip_host, skip_accept_encoding) - # pylint: disable=E1101 def getexpect(self): response = BufferedHTTPResponse(self.sock, strict=self.strict, method=self._method) @@ -105,17 +95,15 @@ class BufferedHTTPConnection(HTTPConnection): def getresponse(self): response = HTTPConnection.getresponse(self) - logger.debug(("HTTP PERF: %(time).5f seconds to %(method)s " - "%(host)s:%(port)s %(path)s)"), + logging.debug(('HTTP PERF: %(time).5f seconds to %(method)s ' + '%(host)s:%(port)s %(path)s)'), {'time': time.time() - self._connected_time, 'method': self._method, 'host': self.host, 'port': self.port, 'path': self._path}) return response -# pylint: disable=R0913 def http_connect(ipaddr, port, device, partition, method, path, - headers=None, query_string=None, ssl=False, key_file=None, - cert_file=None, timeout=None): + headers=None, query_string=None, ssl=False): """ Helper function to create an HTTPConnection object. If ssl is set True, HTTPSConnection will be used. However, if ssl=False, BufferedHTTPConnection @@ -130,21 +118,26 @@ def http_connect(ipaddr, port, device, partition, method, path, :param headers: dictionary of headers :param query_string: request query string :param ssl: set True if SSL should be used (default: False) - :param key_file: Private key file (not needed if cert_file has private key) - :param cert_file: Certificate file (Keystore) :returns: HTTPConnection object """ + if ssl: + conn = HTTPSConnection('%s:%s' % (ipaddr, port)) + else: + conn = BufferedHTTPConnection('%s:%s' % (ipaddr, port)) path = quote('/' + device + '/' + str(partition) + path) - # pylint: disable=E1121, E1124 - return http_connect_raw(ipaddr, port, device, partition, method, path, - headers, query_string, ssl, key_file, cert_file, - timeout=timeout) + if query_string: + path += '?' + query_string + conn.path = path + conn.putrequest(method, path) + if headers: + for header, value in headers.iteritems(): + conn.putheader(header, value) + conn.endheaders() + return conn -# pylint: disable=W0201 def http_connect_raw(ipaddr, port, method, path, headers=None, - query_string=None, ssl=False, key_file=None, - cert_file=None, timeout=None): + query_string=None, ssl=False): """ Helper function to create an HTTPConnection object. If ssl is set True, HTTPSConnection will be used. However, if ssl=False, BufferedHTTPConnection @@ -157,26 +150,18 @@ def http_connect_raw(ipaddr, port, method, path, headers=None, :param headers: dictionary of headers :param query_string: request query string :param ssl: set True if SSL should be used (default: False) - :param key_file: Private key file (not needed if cert_file has private key) - :param cert_file: Certificate file (Keystore) :returns: HTTPConnection object """ - if timeout is None: - timeout = DEFAULT_TIMEOUT if ssl: - conn = HTTPSConnection('%s:%s' % (ipaddr, port), key_file=key_file, - cert_file=cert_file, timeout=timeout) + conn = HTTPSConnection('%s:%s' % (ipaddr, port)) else: - conn = BufferedHTTPConnection('%s:%s' % (ipaddr, port), - timeout=timeout) + conn = BufferedHTTPConnection('%s:%s' % (ipaddr, port)) if query_string: path += '?' + query_string conn.path = path conn.putrequest(method, path) if headers: - # pylint: disable=E1103 for header, value in headers.iteritems(): conn.putheader(header, value) - # pylint: disable=E1103 conn.endheaders() return conn diff --git a/keystone/cfg.py b/keystone/common/cfg.py similarity index 91% rename from keystone/cfg.py rename to keystone/common/cfg.py index da91356fab..91c9546c10 100644 --- a/keystone/cfg.py +++ b/keystone/common/cfg.py @@ -17,7 +17,7 @@ r""" Configuration options which may be set on the command line or in config files. -The schema for each option is defined using the Opt sub-classes e.g. +The schema for each option is defined using the Opt sub-classes e.g.:: common_opts = [ cfg.StrOpt('bind_host', @@ -28,7 +28,7 @@ The schema for each option is defined using the Opt sub-classes e.g. help='Port number to listen on') ] -Options can be strings, integers, floats, booleans, lists or 'multi strings': +Options can be strings, integers, floats, booleans, lists or 'multi strings':: enabled_apis_opt = \ cfg.ListOpt('enabled_apis', @@ -43,7 +43,7 @@ Options can be strings, integers, floats, booleans, lists or 'multi strings': default=DEFAULT_EXTENSIONS) Option schemas are registered with with the config manager at runtime, but -before the option is referenced: +before the option is referenced:: class ExtensionManager(object): @@ -59,7 +59,7 @@ before the option is referenced: .... A common usage pattern is for each option schema to be defined in the module or -class which uses the option: +class which uses the option:: opts = ... @@ -74,7 +74,7 @@ class which uses the option: An option may optionally be made available via the command line. Such options must registered with the config manager before the command line is parsed (for -the purposes of --help and CLI arg validation): +the purposes of --help and CLI arg validation):: cli_opts = [ cfg.BoolOpt('verbose', @@ -90,7 +90,7 @@ the purposes of --help and CLI arg validation): def add_common_opts(conf): conf.register_cli_opts(cli_opts) -The config manager has a single CLI option defined by default, --config-file: +The config manager has a single CLI option defined by default, --config-file:: class ConfigOpts(object): @@ -104,7 +104,7 @@ The config manager has a single CLI option defined by default, --config-file: Option values are parsed from any supplied config files using SafeConfigParser. If none are specified, a default set is used e.g. glance-api.conf and -glance-common.conf: +glance-common.conf:: glance-api.conf: [DEFAULT] @@ -119,7 +119,7 @@ are parsed in order, with values in later files overriding those in earlier files. The parsing of CLI args and config files is initiated by invoking the config -manager e.g. +manager e.g.:: conf = ConfigOpts() conf.register_opt(BoolOpt('verbose', ...)) @@ -127,7 +127,7 @@ manager e.g. if conf.verbose: ... -Options can be registered as belonging to a group: +Options can be registered as belonging to a group:: rabbit_group = cfg.OptionGroup(name='rabbit', title='RabbitMQ options') @@ -154,7 +154,7 @@ Options can be registered as belonging to a group: conf.register_opt(rabbit_ssl_opt, group=rabbit_group) If no group is specified, options belong to the 'DEFAULT' section of config -files: +files:: glance-api.conf: [DEFAULT] @@ -175,7 +175,7 @@ Command-line options in a group are automatically prefixed with the group name: Option values in the default group are referenced as attributes/properties on the config manager; groups are also attributes on the config manager, with -attributes for each of the options associated with the group: +attributes for each of the options associated with the group:: server.start(app, conf.bind_port, conf.bind_host, conf) @@ -184,7 +184,7 @@ attributes for each of the options associated with the group: port=conf.rabbit.port, ...) -Option values may reference other values using PEP 292 string substitution: +Option values may reference other values using PEP 292 string substitution:: opts = [ cfg.StrOpt('state_path', @@ -200,17 +200,13 @@ Option values may reference other values using PEP 292 string substitution: Note that interpolation can be avoided by using '$$'. """ -# pylint: disable=W0231,W0212,W0141,C0302,R0913,W0402 + +import sys import ConfigParser import copy -import logging import optparse import os -import re import string -import sys - -LOG = logging.getLogger(__name__) class Error(Exception): @@ -227,9 +223,9 @@ class ArgsAlreadyParsedError(Error): """Raised if a CLI opt is registered after parsing.""" def __str__(self): - ret = "arguments already parsed" + ret = 'arguments already parsed' if self.msg: - ret += ": " + self.msg + ret += ': ' + self.msg return ret @@ -242,9 +238,9 @@ class NoSuchOptError(Error): def __str__(self): if self.group is None: - return "no such option: %s" % self.opt_name + return 'no such option: %s' % self.opt_name else: - return "no such option in group %s: %s" % (self.group.name, + return 'no such option in group %s: %s' % (self.group.name, self.opt_name) @@ -255,7 +251,7 @@ class NoSuchGroupError(Error): self.group_name = group_name def __str__(self): - return "no such group: %s" % self.group_name + return 'no such group: %s' % self.group_name class DuplicateOptError(Error): @@ -265,14 +261,14 @@ class DuplicateOptError(Error): self.opt_name = opt_name def __str__(self): - return "duplicate option: %s" % self.opt_name + return 'duplicate option: %s' % self.opt_name class TemplateSubstitutionError(Error): """Raised if an error occurs substituting a variable in an opt value.""" def __str__(self): - return "template substitution error: %s" % self.msg + return 'template substitution error: %s' % self.msg class ConfigFilesNotFoundError(Error): @@ -445,8 +441,7 @@ class Opt(object): prefix = self._get_optparse_prefix('', group) self._add_to_optparse(container, self.name, self.short, kwargs, prefix) - @staticmethod - def _add_to_optparse(container, name, short, kwargs, prefix=''): + def _add_to_optparse(self, container, name, short, kwargs, prefix=''): """Add an option to an optparse parser or group. :param container: an optparse.OptionContainer object @@ -464,8 +459,7 @@ class Opt(object): raise DuplicateOptError(a) container.add_option(*args, **kwargs) - @staticmethod - def _get_optparse_container(parser, group): + def _get_optparse_container(self, parser, group): """Returns an optparse.OptionContainer. :param parser: an optparse.OptionParser @@ -497,8 +491,7 @@ class Opt(object): }) return kwargs - @staticmethod - def _get_optparse_prefix(prefix, group): + def _get_optparse_prefix(self, prefix, group): """Build a prefix for the CLI option name, if required. CLI options in a group are prefixed with the group's name in order @@ -603,7 +596,6 @@ class ListOpt(Opt): callback=self._parse_list, **kwargs) - # pylint: disable=W0613 def _parse_list(self, option, opt, value, parser): """An optparse callback for parsing an option value into a list.""" setattr(parser.values, self.dest, value.split(',')) @@ -689,7 +681,6 @@ class OptGroup(object): return self._optparse_group -# pylint: disable=R0902 class ConfigOpts(object): """ @@ -764,11 +755,16 @@ class ConfigOpts(object): :raises: SystemExit, ConfigFilesNotFoundError, ConfigFileParseError """ self.reset() + self._args = args + (values, args) = self._oparser.parse_args(self._args) + self._cli_values = vars(values) + if self.config_file: - self._parse_config_files() + self._parse_config_files(self.config_file) + return args def __getattr__(self, name): @@ -934,16 +930,9 @@ class ConfigOpts(object): info = self._get_opt_info(name, group) default, opt, override = map(lambda k: info[k], sorted(info.keys())) - # Explicit code overrides always trump others. if override is not None: return override - # CLI values override configuration settings. - name = name if group is None else "%s_%s" % (group.name, name) - value = self._cli_values.get(name, None) - if value is not None: - return value - if self._cparser is not None: section = group.name if group is not None else 'DEFAULT' try: @@ -954,6 +943,11 @@ class ConfigOpts(object): except ValueError, ve: raise ConfigFileValueError(str(ve)) + name = name if group is None else group.name + '_' + name + value = self._cli_values.get(name, None) + if value is not None: + return value + if default is not None: return default @@ -1017,60 +1011,11 @@ class ConfigOpts(object): return opts[opt_name] - # pylint: disable=R0914,R0201 - def _update_config_format(self, config_files): - """ - Update the config files in case they contain keys with dashes instead - of underscores. If a file needs updating, write it to a temp files and - replace the original name with the temp file. - NOTE: warnings are issued if any old formats are found. - """ - ret = config_files[:] - bad_settings = [] - for pos, cf in enumerate(config_files): - warn = False - out = [] - with file(cf) as config: - for ln in config: - try: - kk, vv = ln.split("=") - except ValueError: - out.append(ln) - continue - # Replace dashes with underscores - newk = kk.replace("-", "_") - # Replace periods with colons *only* if not part of a - # numeric value. - newk = re.sub(r"([^\d])\.([^\d])", r"\1:\2", newk) - if newk != kk and not kk.startswith("paste."): - warn = True - bad_settings.append("%s -> %s" % (kk, newk)) - out.append("=".join((newk, vv))) - out_text = "\n".join(out) - if warn: - # Need to write out a new tmp file and print a warning - changes = "\n\t".join(bad_settings) - msg = "\n\nWarning: your configuration may be using an old"\ - " format. Please update the following settings by"\ - " replacing hyphens with underscores, and periods"\ - " with colons:\n\t%s\n\n" % changes - # TODO(ziad): verify if that is what other projects are moving - # to? If so, make this a warning - LOG.debug(msg) - # Importing here to avoid circular imports. - from keystone import utils - new_name = utils.write_temp_file(out_text) - ret[pos] = new_name - return ret - - def _parse_config_files(self, config_files=None): + def _parse_config_files(self, config_files): """Parse the supplied configuration files. :raises: ConfigFilesNotFoundError, ConfigFileParseError """ - if config_files is None: - config_files = self.config_file - config_files = self._update_config_format(config_files) self._cparser = ConfigParser.SafeConfigParser() try: diff --git a/keystone/common/config.py b/keystone/common/config.py deleted file mode 100755 index dbfad235a5..0000000000 --- a/keystone/common/config.py +++ /dev/null @@ -1,386 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# 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. - -""" -Routines for configuring OpenStack Service -""" - -import logging.config -from logging import FileHandler -import optparse -import os -from paste import deploy -import sys -import ConfigParser -from keystone.common.wsgi import add_console_handler - -DEFAULT_LOG_FORMAT = "%(asctime)s %(levelname)8s [%(name)s] %(message)s" -DEFAULT_LOG_DATE_FORMAT = "%Y-%m-%d %H:%M:%S" -DEFAULT_LOG_DIR = "/var/log/keystone" -DEFAULT_LOG_FILE = "keystone.log" - - -def parse_options(parser, cli_args=None): - """ - Returns the parsed CLI options, command to run and its arguments, merged - with any same-named options found in a configuration file. - - The function returns a tuple of (options, args), where options is a - mapping of option key/str(value) pairs, and args is the set of arguments - (not options) supplied on the command-line. - - The reason that the option values are returned as strings only is that - ConfigParser and paste.deploy only accept string values... - - :param parser: The option parser - :param cli_args: (Optional) Set of arguments to process. If not present, - sys.argv[1:] is used. - :returns: tuple of (options, args) - - """ - - (options, args) = parser.parse_args(cli_args) - - return (vars(options), args) - - -def add_common_options(parser): - """ - Given a supplied optparse.OptionParser, adds an OptionGroup that - represents all common configuration options. - - :param parser: optparse.OptionParser - """ - help_text = "The following configuration options are common to "\ - "all keystone programs." - - group = optparse.OptionGroup(parser, "Common Options", help_text) - group.add_option('-v', '--verbose', default=False, dest="verbose", - action="store_true", - help="Print more verbose output") - group.add_option('-d', '--debug', default=False, dest="debug", - action="store_true", - help="Print debugging output to console") - group.add_option('-c', '--config-file', default=None, metavar="PATH", - help="""Path to the config file to use. When not \ -specified (the default), we generally look at the first argument specified to \ -be a config file, and if that is also missing, we search standard directories \ -for a config file.""") - group.add_option('-p', '--port', '--bind-port', - dest="bind_port", - help="specifies port to listen on") - group.add_option('--host', '--bind-host', - default=None, dest="bind_host", - help="specifies host address to listen on "\ - "(default is all or 0.0.0.0)") - # This one is handled by keystone/tools/tracer.py (if loaded) - group.add_option('-t', '--trace-calls', default=False, - dest="trace_calls", - action="store_true", - help="Turns on call tracing for troubleshooting") - - parser.add_option_group(group) - return group - - -def add_log_options(parser): - """ - Given a supplied optparse.OptionParser, adds an OptionGroup that - represents all the configuration options around logging. - - :param parser: optparse.OptionParser - """ - help_text = "The following configuration options are specific to logging "\ - "functionality for this program." - - group = optparse.OptionGroup(parser, "Logging Options", help_text) - group.add_option('--log-config', default=None, metavar="PATH", - help="""If this option is specified, the logging \ -configuration file specified is used and overrides any other logging options \ -specified. Please see the Python logging module documentation for details on \ -logging configuration files.""") - group.add_option('--log-date-format', metavar="FORMAT", - default=DEFAULT_LOG_DATE_FORMAT, - help="Format string for %(asctime)s in log records. "\ - "Default: %default") - group.add_option('--log-file', default=None, metavar="PATH", - help="(Optional) Name of log file to output to. "\ - "If not set, logging will go to stdout.") - group.add_option("--log-dir", default=None, - help="(Optional) The directory to keep log files in "\ - "(will be prepended to --logfile)") - - parser.add_option_group(group) - return group - - -def setup_logging(options, conf): - """ - Sets up the logging options for a log with supplied name - - :param options: Mapping of typed option key/values - :param conf: Mapping of untyped key/values from config file - """ - if options.get('log_config', None): - # Use a logging configuration file for all settings... - if os.path.exists(options['log_config']): - logging.config.fileConfig(options['log_config']) - return - else: - raise RuntimeError("Unable to locate specified logging "\ - "config file: %s" % options['log_config']) - - # If either the CLI option or the conf value - # is True, we set to True - debug = options.get('debug') or conf.get('debug', False) - debug = debug in [True, "True", "1"] - verbose = options.get('verbose') or conf.get('verbose', False) - verbose = verbose in [True, "True", "1"] - root_logger = logging.root - root_logger.setLevel( - logging.DEBUG if debug else - logging.INFO if verbose else - logging.WARNING) - - # Set log configuration from options... - # Note that we use a hard-coded log format in the options - # because of Paste.Deploy bug #379 - # http://trac.pythonpaste.org/pythonpaste/ticket/379 - log_format = options.get('log_format', DEFAULT_LOG_FORMAT) - log_date_format = options.get('log_date_format', DEFAULT_LOG_DATE_FORMAT) - formatter = logging.Formatter(log_format, log_date_format) - - # grab log_file and log_dir from config; set to defaults of not already - # defined - logfile = options.get('log_file') or conf.get('log_file', DEFAULT_LOG_FILE) - logdir = options.get('log_dir') or conf.get('log_dir', DEFAULT_LOG_DIR) - - # Add FileHandler if it doesn't exist - logfile = os.path.abspath(os.path.join(logdir, logfile)) - handlers = [handler for handler in root_logger.handlers - if isinstance(handler, FileHandler) - and handler.baseFilename == logfile] - - if not handlers: - logfile = logging.FileHandler(logfile) - logfile.setFormatter(formatter) - root_logger.addHandler(logfile) - - # Mirror to console if verbose or debug - if debug or verbose: - add_console_handler(root_logger, logging.DEBUG) - else: - add_console_handler(root_logger, logging.INFO) - - -def find_config_file(options, args): - """ - Return the first config file found. - - We search for the paste config file in the following order: - * If --config-file option is used, use that - * If args[0] is a file, use that - * Search for keystone.conf in standard directories: - - * . - * ~.keystone/ - * ~ - * /etc/keystone - * /etc - - If no config file is given get from possible_topdir/etc/keystone.conf - - :returns: Full path to config file, or None if no config file found - """ - POSSIBLE_TOPDIR = os.path.normpath(os.path.join(\ - os.path.abspath(sys.argv[0]), - os.pardir, - os.pardir)) - fix_path = lambda p: os.path.abspath(os.path.expanduser(p)) - cfg_file = options.get('config_file') - if cfg_file: - if isinstance(cfg_file, list): - cfg_file = cfg_file[0] - if os.path.exists(cfg_file): - return fix_path(cfg_file) - elif args: - if os.path.exists(args[0]): - return fix_path(args[0]) - - # Handle standard directory search for keystone.conf - config_file_dirs = [fix_path(os.getcwd()), - fix_path(os.path.join('~', '.keystone')), - fix_path('~'), - '/etc/keystone/', - '/etc'] - - for cfg_dir in config_file_dirs: - cfg_file = os.path.join(cfg_dir, 'keystone.conf') - if os.path.exists(cfg_file): - return cfg_file - else: - if os.path.exists(os.path.join(POSSIBLE_TOPDIR, 'etc', \ - 'keystone.conf')): - # For debug only - config_file = os.path.join(POSSIBLE_TOPDIR, 'etc', \ - 'keystone.conf') - return config_file - - -def load_paste_config(app_name, options, args): - """ - Looks for a config file to use for an app and returns the - config file path and a configuration mapping from a paste config file. - - We search for the paste config file in the following order: - - * If --config-file option is used, use that - * If args[0] is a file, use that - * Search for keystone.conf in standard directories: - - * . - * ~.keystone/ - * ~ - * /etc/keystone - * /etc - - :param app_name: Name of the application to load config for, or None. - None signifies to only load the [DEFAULT] section of - the config file. - :param options: Set of typed options returned from parse_options() - :param args: Command line arguments from argv[1:] - :returns: Tuple of (conf_file, conf) - :raises: RuntimeError when config file cannot be located or there was a - problem loading the configuration file. - """ - conf_file = find_config_file(options, args) - if not conf_file: - raise RuntimeError("Unable to locate any configuration file. "\ - "Cannot load application %s" % app_name) - try: - conf = deploy.appconfig("config:%s" % conf_file, name=app_name) - conf.global_conf.update(get_non_paste_configs(conf_file)) - return conf_file, conf - except Exception, e: - raise RuntimeError("Error loading config %s: %s" % (conf_file, e)) - - -def get_non_paste_configs(conf_file): - load_config_files(conf_file) - complete_conf = load_config_files(conf_file) - #Add Non Paste global sections.Need to find a better way. - global_conf = {} - if complete_conf is not None: - for section in complete_conf.sections(): - if not (section.startswith('filter:') or \ - section.startswith('app:') or \ - section.startswith('pipeline:')): - section_items = complete_conf.items(section) - section_items_dict = {} - for section_item in section_items: - section_items_dict[section_item[0]] = section_item[1] - global_conf[section] = section_items_dict - return global_conf - - -def load_config_files(config_files): - '''Load the config files.''' - config = ConfigParser.ConfigParser() - if config_files is not None: - config.read(config_files) - return config - - -def load_paste_app(app_name, options, args): - """ - Builds and returns a WSGI app from a paste config file. - - We search for the paste config file in the following order: - * If --config-file option is used, use that - * If args[0] is a file, use that - * Search for keystone.conf in standard directories: - - * . - * ~.keystone/ - * ~ - * /etc/keystone - * /etc - - :param app_name: Name of the application to load (server, admin, proxy, ..) - :param options: Set of typed options returned from parse_options() - :param args: Command line arguments from argv[1:] - :raises: RuntimeError when config file cannot be located or application - cannot be loaded from config file - """ - conf_file, conf = load_paste_config(app_name, options, args) - - try: - # Setup logging early, supplying both the CLI options and the - # configuration mapping from the config file - if not conf.get('log_file'): - options['log_file'] = "%s.log" % app_name - setup_logging(options, conf) - - # We only update the conf dict for the verbose and debug - # flags. Everything else must be set up in the conf file... - debug = options.get('debug') or conf.get('debug', False) - debug = debug in [True, "True", "1"] - verbose = options.get('verbose') or conf.get('verbose', False) - verbose = verbose in [True, "True", "1"] - conf['debug'] = debug - conf['verbose'] = verbose - # Log the options used when starting if we're in debug mode... - if debug: - logger = logging.getLogger(app_name) - logger.info("*" * 50) - logger.info("Configuration options gathered from config file:") - logger.info(conf_file) - logger.info("================================================") - items = dict([(k, v) for k, v in conf.items() - if k not in ('__file__', 'here')]) - for key, value in sorted(items.items()): - logger.info("%(key)-20s %(value)s" % locals()) - logger.info("*" * 50) - app = deploy.loadapp("config:%s" % conf_file, name=app_name, - global_conf=conf.global_conf) - except (LookupError, ImportError) as e: - raise RuntimeError("Unable to load %(app_name)s from " - "configuration file %(conf_file)s." - "\nGot: %(e)r" % locals()) - return conf, app - - -def get_option(options, option, **kwargs): - if option in options: - value = options[option] - type_ = kwargs.get('type', 'str') - if type_ == 'bool': - if hasattr(value, 'lower'): - return value.lower() == 'true' - else: - return value - elif type_ == 'int': - return int(value) - elif type_ == 'float': - return float(value) - else: - return value - elif 'default' in kwargs: - return kwargs['default'] - else: - raise KeyError("option '%s' not found" % option) diff --git a/keystone/common/crypt.py b/keystone/common/crypt.py deleted file mode 100644 index bb25620dab..0000000000 --- a/keystone/common/crypt.py +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# 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. - -""" -Routines for URL-safe encrypting/decrypting - -Keep this file in sync with all copies: -- glance/common/crypt.py -- keystone/middleware/crypt.py -- keystone/common/crypt.py - -""" - -import base64 - -from Crypto.Cipher import AES -from Crypto import Random -from Crypto.Random import random - - -def urlsafe_encrypt(key, plaintext, blocksize=16): - """ - Encrypts plaintext. Resulting ciphertext will contain URL-safe characters - :param key: AES secret key - :param plaintext: Input text to be encrypted - :param blocksize: Non-zero integer multiple of AES blocksize in bytes (16) - - :returns : Resulting ciphertext - """ - def pad(text): - """ - Pads text to be encrypted - """ - pad_length = (blocksize - len(text) % blocksize) - sr = random.StrongRandom() - pad = ''.join(chr(sr.randint(1, 0xFF)) for i in range(pad_length - 1)) - # We use chr(0) as a delimiter between text and padding - return text + chr(0) + pad - - # random initial 16 bytes for CBC - init_vector = Random.get_random_bytes(16) - cypher = AES.new(key, AES.MODE_CBC, init_vector) - padded = cypher.encrypt(pad(str(plaintext))) - return base64.urlsafe_b64encode(init_vector + padded) - - -def urlsafe_decrypt(key, ciphertext): - """ - Decrypts URL-safe base64 encoded ciphertext - :param key: AES secret key - :param ciphertext: The encrypted text to decrypt - - :returns : Resulting plaintext - """ - # Cast from unicode - ciphertext = base64.urlsafe_b64decode(str(ciphertext)) - cypher = AES.new(key, AES.MODE_CBC, ciphertext[:16]) - padded = cypher.decrypt(ciphertext[16:]) - return padded[:padded.rfind(chr(0))] diff --git a/keystone/common/exception.py b/keystone/common/exception.py deleted file mode 100755 index e734e94f60..0000000000 --- a/keystone/common/exception.py +++ /dev/null @@ -1,100 +0,0 @@ -# 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. - -""" -OpenStack base exception handling, including decorator for re-raising -OpenStack-type exceptions. SHOULD include dedicated exception logging. -""" - -import logging - - -class ProcessExecutionError(IOError): - def __init__(self, stdout=None, stderr=None, exit_code=None, cmd=None, - description=None): - if description is None: - description = "Unexpected error while running command." - if exit_code is None: - exit_code = '-' - message = "%s\nCommand: %s\nExit code: %s\nStdout: %r\nStderr: %r" % ( - description, cmd, exit_code, stdout, stderr) - IOError.__init__(self, message) - - -class Error(Exception): - def __init__(self, message=None): - super(Error, self).__init__(message) - - -class ApiError(Error): - def __init__(self, message='Unknown', code='Unknown'): - self.message = message - self.code = code - super(ApiError, self).__init__('%s: %s' % (code, message)) - - -class NotFound(Error): - pass - - -class Duplicate(Error): - pass - - -class NotAuthorized(Error): - pass - - -class NotEmpty(Error): - pass - - -class Invalid(Error): - pass - - -class BadInputError(Exception): - """Error resulting from a client sending bad input to a server""" - pass - - -class MissingArgumentError(Error): - pass - - -class DatabaseMigrationError(Error): - pass - - -class ClientError(Error): - pass - - -def wrap_exception(f): - def _wrap(*args, **kw): - try: - return f(*args, **kw) - except Exception, e: - if not isinstance(e, Error): - #exc_type, exc_value, exc_traceback = sys.exc_info() - logging.exception('Uncaught exception') - #logging.error(traceback.extract_stack(exc_traceback)) - raise Error(str(e)) - raise - _wrap.func_name = f.func_name - return _wrap diff --git a/keystone/common/kvs.py b/keystone/common/kvs.py new file mode 100644 index 0000000000..795c2f70bd --- /dev/null +++ b/keystone/common/kvs.py @@ -0,0 +1,24 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + + +class DictKvs(dict): + def set(self, key, value): + if type(value) is type({}): + self[key] = value.copy() + else: + self[key] = value[:] + + def delete(self, key): + del self[key] + + +INMEMDB = DictKvs() + + +class Base(object): + def __init__(self, db=None): + if db is None: + db = INMEMDB + elif type(db) is type({}): + db = DictKvs(db) + self.db = db diff --git a/keystone/common/logging.py b/keystone/common/logging.py new file mode 100644 index 0000000000..a9aaccfced --- /dev/null +++ b/keystone/common/logging.py @@ -0,0 +1,55 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +"""Wrapper for built-in logging module.""" + +from __future__ import absolute_import + +import functools +import logging +import pprint + +from logging.handlers import SysLogHandler +from logging.handlers import WatchedFileHandler + +# A list of things we want to replicate from logging. +# levels +CRITICAL = logging.CRITICAL +FATAL = logging.FATAL +ERROR = logging.ERROR +WARNING = logging.WARNING +WARN = logging.WARN +INFO = logging.INFO +DEBUG = logging.DEBUG +NOTSET = logging.NOTSET + + +# methods +getLogger = logging.getLogger +debug = logging.debug +info = logging.info +warning = logging.warning +warn = logging.warn +error = logging.error +exception = logging.exception +critical = logging.critical +log = logging.log + +# classes +root = logging.root +Formatter = logging.Formatter + +# handlers +StreamHandler = logging.StreamHandler +WatchedFileHandler = WatchedFileHandler +SysLogHandler = SysLogHandler + + +def log_debug(f): + @functools.wraps(f) + def wrapper(*args, **kw): + logging.debug('%s(%s, %s) ->', f.func_name, str(args), str(kw)) + rv = f(*args, **kw) + logging.debug(pprint.pformat(rv, indent=2)) + logging.debug('') + return rv + return wrapper diff --git a/keystone/common/manager.py b/keystone/common/manager.py new file mode 100644 index 0000000000..17c7d5e53b --- /dev/null +++ b/keystone/common/manager.py @@ -0,0 +1,36 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +import functools + +from keystone import config +from keystone.common import utils + + +class Manager(object): + """Base class for intermediary request layer. + + The Manager layer exists to support additional logic that applies to all + or some of the methods exposed by a service that are not specific to the + HTTP interface. + + It also provides a stable entry point to dynamic backends. + + An example of a probable use case is logging all the calls. + + """ + + def __init__(self, driver_name): + self.driver = utils.import_object(driver_name) + + def __getattr__(self, name): + """Forward calls to the underlying driver.""" + # NOTE(termie): context is the first argument, we're going to strip + # that for now, in the future we'll probably do some + # logging and whatnot in this class + f = getattr(self.driver, name) + + @functools.wraps(f) + def _wrapper(context, *args, **kw): + return f(*args, **kw) + setattr(self, name, _wrapper) + return _wrapper diff --git a/keystone/common/sql/__init__.py b/keystone/common/sql/__init__.py new file mode 100644 index 0000000000..ae31c7029e --- /dev/null +++ b/keystone/common/sql/__init__.py @@ -0,0 +1 @@ +from keystone.common.sql.core import * diff --git a/keystone/common/sql/core.py b/keystone/common/sql/core.py new file mode 100644 index 0000000000..cb62186598 --- /dev/null +++ b/keystone/common/sql/core.py @@ -0,0 +1,119 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +"""SQL backends for the various services.""" + + +import json + +import eventlet.db_pool +import sqlalchemy as sql +from sqlalchemy import types as sql_types +from sqlalchemy.ext import declarative +import sqlalchemy.orm +import sqlalchemy.pool +import sqlalchemy.engine.url + +from keystone import config + + +CONF = config.CONF + + +ModelBase = declarative.declarative_base() + + +# For exporting to other modules +Column = sql.Column +String = sql.String +ForeignKey = sql.ForeignKey +DateTime = sql.DateTime + + +# Special Fields +class JsonBlob(sql_types.TypeDecorator): + impl = sql.Text + + def process_bind_param(self, value, dialect): + return json.dumps(value) + + def process_result_value(self, value, dialect): + return json.loads(value) + + +class DictBase(object): + def to_dict(self): + return dict(self.iteritems()) + + def __setitem__(self, key, value): + setattr(self, key, value) + + def __getitem__(self, key): + return getattr(self, key) + + def get(self, key, default=None): + return getattr(self, key, default) + + def __iter__(self): + self._i = iter(sqlalchemy.orm.object_mapper(self).columns) + return self + + def next(self): + n = self._i.next().name + return n + + def update(self, values): + """Make the model object behave like a dict.""" + for k, v in values.iteritems(): + setattr(self, k, v) + + def iteritems(self): + """Make the model object behave like a dict. + + Includes attributes from joins. + + """ + return dict([(k, getattr(self, k)) for k in self]) + #local = dict(self) + #joined = dict([(k, v) for k, v in self.__dict__.iteritems() + # if not k[0] == '_']) + #local.update(joined) + #return local.iteritems() + + +# Backends +class Base(object): + _MAKER = None + _ENGINE = None + + def get_session(self, autocommit=True, expire_on_commit=False): + """Return a SQLAlchemy session.""" + if self._MAKER is None or self._ENGINE is None: + self._ENGINE = self.get_engine() + self._MAKER = self.get_maker(self._ENGINE, + autocommit, + expire_on_commit) + + session = self._MAKER() + # TODO(termie): we may want to do something similar + #session.query = nova.exception.wrap_db_error(session.query) + #session.flush = nova.exception.wrap_db_error(session.flush) + return session + + def get_engine(self): + """Return a SQLAlchemy engine.""" + connection_dict = sqlalchemy.engine.url.make_url(CONF.sql.connection) + + engine_args = {'pool_recycle': CONF.sql.idle_timeout, + 'echo': False, + } + + if 'sqlite' in connection_dict.drivername: + engine_args['poolclass'] = sqlalchemy.pool.NullPool + + return sql.create_engine(CONF.sql.connection, **engine_args) + + def get_maker(self, engine, autocommit=True, expire_on_commit=False): + """Return a SQLAlchemy sessionmaker using the given engine.""" + return sqlalchemy.orm.sessionmaker(bind=engine, + autocommit=autocommit, + expire_on_commit=expire_on_commit) diff --git a/keystone/common/sql/legacy.py b/keystone/common/sql/legacy.py new file mode 100644 index 0000000000..d8bbb2c5f8 --- /dev/null +++ b/keystone/common/sql/legacy.py @@ -0,0 +1,141 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +import re + +import sqlalchemy + +from keystone.identity.backends import sql as identity_sql + + +def export_db(db): + table_names = db.table_names() + + migration_data = {} + for table_name in table_names: + query = db.execute("select * from %s" % table_name) + table_data = [] + for row in query.fetchall(): + entry = {} + for k in row.keys(): + entry[k] = row[k] + table_data.append(entry) + + migration_data[table_name] = table_data + + return migration_data + + +def _translate_replacements(s): + if '%' not in str(s): + return s + return re.sub(r'%([\w_]+)%', r'$(\1)s', s) + + +class LegacyMigration(object): + def __init__(self, db_string): + self.db = sqlalchemy.create_engine(db_string) + self.identity_driver = identity_sql.Identity() + self.identity_driver.db_sync() + self._data = {} + self._user_map = {} + self._tenant_map = {} + self._role_map = {} + + def migrate_all(self): + self._export_legacy_db() + self._migrate_tenants() + self._migrate_users() + self._migrate_roles() + self._migrate_user_roles() + self._migrate_tokens() + + def dump_catalog(self): + """Generate the contents of a catalog templates file.""" + self._export_legacy_db() + + services_by_id = dict((x['id'], x) for x in self._data['services']) + template = 'catalog.%(region)s.%(service_type)s.%(key)s = %(value)s' + + o = [] + for row in self._data['endpoint_templates']: + service = services_by_id[row['service_id']] + d = {'service_type': service['type'], + 'region': row['region']} + + for x in ['internal_url', 'public_url', 'admin_url', 'enabled']: + d['key'] = x.replace('_u', 'U') + d['value'] = _translate_replacements(row[x]) + o.append(template % d) + + d['key'] = 'name' + d['value'] = service['desc'] + o.append(template % d) + + return o + + def _export_legacy_db(self): + self._data = export_db(self.db) + + def _migrate_tenants(self): + for x in self._data['tenants']: + # map + new_dict = {'description': x.get('desc', ''), + 'id': x.get('uid', x.get('id')), + 'enabled': x.get('enabled', True)} + new_dict['name'] = x.get('name', new_dict.get('id')) + # track internal ids + self._tenant_map[x.get('id')] = new_dict['id'] + # create + #print 'create_tenant(%s, %s)' % (new_dict['id'], new_dict) + self.identity_driver.create_tenant(new_dict['id'], new_dict) + + def _migrate_users(self): + for x in self._data['users']: + # map + new_dict = {'email': x.get('email', ''), + 'password': x.get('password', None), + 'id': x.get('uid', x.get('id')), + 'enabled': x.get('enabled', True)} + if x.get('tenant_id'): + new_dict['tenant_id'] = self._tenant_map.get(x['tenant_id']) + new_dict['name'] = x.get('name', new_dict.get('id')) + # track internal ids + self._user_map[x.get('id')] = new_dict['id'] + # create + #print 'create_user(%s, %s)' % (new_dict['id'], new_dict) + self.identity_driver.create_user(new_dict['id'], new_dict) + if new_dict.get('tenant_id'): + self.identity_driver.add_user_to_tenant(new_dict['tenant_id'], + new_dict['id']) + + def _migrate_roles(self): + for x in self._data['roles']: + # map + new_dict = {'id': x['id'], + 'name': x.get('name', x['id'])} + # track internal ids + self._role_map[x.get('id')] = new_dict['id'] + # create + self.identity_driver.create_role(new_dict['id'], new_dict) + + def _migrate_user_roles(self): + for x in self._data['user_roles']: + # map + if (not x.get('user_id') + or not x.get('tenant_id') + or not x.get('role_id')): + continue + user_id = self._user_map[x['user_id']] + tenant_id = self._tenant_map[x['tenant_id']] + role_id = self._role_map[x['role_id']] + + try: + self.identity_driver.add_user_to_tenant(tenant_id, user_id) + except Exception: + pass + + self.identity_driver.add_role_to_user_and_tenant( + user_id, tenant_id, role_id) + + def _migrate_tokens(self): + pass diff --git a/keystone/backends/sqlalchemy/migrate_repo/README b/keystone/common/sql/migrate_repo/README similarity index 100% rename from keystone/backends/sqlalchemy/migrate_repo/README rename to keystone/common/sql/migrate_repo/README diff --git a/examples/echo/__init__.py b/keystone/common/sql/migrate_repo/__init__.py similarity index 100% rename from examples/echo/__init__.py rename to keystone/common/sql/migrate_repo/__init__.py diff --git a/keystone/backends/sqlalchemy/migrate_repo/manage.py b/keystone/common/sql/migrate_repo/manage.py old mode 100755 new mode 100644 similarity index 55% rename from keystone/backends/sqlalchemy/migrate_repo/manage.py rename to keystone/common/sql/migrate_repo/manage.py index 2a928c84c9..39fa3892e5 --- a/keystone/backends/sqlalchemy/migrate_repo/manage.py +++ b/keystone/common/sql/migrate_repo/manage.py @@ -1,3 +1,5 @@ #!/usr/bin/env python from migrate.versioning.shell import main -main(debug='False') + +if __name__ == '__main__': + main(debug='False') diff --git a/keystone/backends/sqlalchemy/migrate_repo/migrate.cfg b/keystone/common/sql/migrate_repo/migrate.cfg similarity index 78% rename from keystone/backends/sqlalchemy/migrate_repo/migrate.cfg rename to keystone/common/sql/migrate_repo/migrate.cfg index 42986cf790..a8be608953 100644 --- a/keystone/backends/sqlalchemy/migrate_repo/migrate.cfg +++ b/keystone/common/sql/migrate_repo/migrate.cfg @@ -1,7 +1,7 @@ [db_settings] # Used to identify which repository this database is versioned under. # You can use the name of your project. -repository_id=Keystone +repository_id=keystone # The name of the database table used to track the schema version. # This name shouldn't already be used by your project. @@ -18,3 +18,8 @@ version_table=migrate_version # be using to ensure your updates to that database work properly. # This must be a list; example: ['postgres','sqlite'] required_dbs=[] + +# When creating new change scripts, Migrate will stamp the new script with +# a version number. By default this is latest_version + 1. You can set this +# to 'true' to tell Migrate to use the UTC timestamp instead. +use_timestamp_numbering=False diff --git a/keystone/common/sql/migrate_repo/versions/001_add_initial_tables.py b/keystone/common/sql/migrate_repo/versions/001_add_initial_tables.py new file mode 100644 index 0000000000..ae54b476dc --- /dev/null +++ b/keystone/common/sql/migrate_repo/versions/001_add_initial_tables.py @@ -0,0 +1,20 @@ +from sqlalchemy import * +from migrate import * + +from keystone.common import sql + +# these are to make sure all the models we care about are defined +import keystone.identity.backends.sql +import keystone.token.backends.sql +import keystone.contrib.ec2.backends.sql + + +def upgrade(migrate_engine): + # Upgrade operations go here. Don't create your own engine; bind + # migrate_engine to your metadata + sql.ModelBase.metadata.create_all(migrate_engine) + + +def downgrade(migrate_engine): + # Operations to reverse the above upgrade go here. + pass diff --git a/keystone/backends/sqlalchemy/api/__init__.py b/keystone/common/sql/migrate_repo/versions/__init__.py old mode 100755 new mode 100644 similarity index 100% rename from keystone/backends/sqlalchemy/api/__init__.py rename to keystone/common/sql/migrate_repo/versions/__init__.py diff --git a/keystone/common/sql/migration.py b/keystone/common/sql/migration.py new file mode 100644 index 0000000000..0ea9cd12d3 --- /dev/null +++ b/keystone/common/sql/migration.py @@ -0,0 +1,80 @@ +# 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. + +import os +import sys + +import sqlalchemy +from migrate.versioning import api as versioning_api + +from keystone import config + + +CONF = config.CONF + + +try: + from migrate.versioning import exceptions as versioning_exceptions +except ImportError: + try: + # python-migration changed location of exceptions after 1.6.3 + # See LP Bug #717467 + from migrate import exceptions as versioning_exceptions + except ImportError: + sys.exit('python-migrate is not installed. Exiting.') + + +def db_sync(version=None): + if version is not None: + try: + version = int(version) + except ValueError: + raise Exception('version should be an integer') + + current_version = db_version() + repo_path = _find_migrate_repo() + if version is None or version > current_version: + return versioning_api.upgrade( + CONF.sql.connection, repo_path, version) + else: + return versioning_api.downgrade( + CONF.sql.connection, repo_path, version) + + +def db_version(): + repo_path = _find_migrate_repo() + try: + return versioning_api.db_version( + CONF.sql.connection, repo_path) + except versioning_exceptions.DatabaseNotControlledError: + return db_version_control(0) + + +def db_version_control(version=None): + repo_path = _find_migrate_repo() + versioning_api.version_control( + CONF.sql.connection, repo_path, version) + return version + + +def _find_migrate_repo(): + """Get the path for the migrate repository.""" + path = os.path.join(os.path.abspath(os.path.dirname(__file__)), + 'migrate_repo') + assert os.path.exists(path) + return path diff --git a/keystone/common/sql/util.py b/keystone/common/sql/util.py new file mode 100644 index 0000000000..a181c28498 --- /dev/null +++ b/keystone/common/sql/util.py @@ -0,0 +1,18 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +import os + +from keystone import config +from keystone.common.sql import migration + + +CONF = config.CONF + + +def setup_test_database(): + # TODO(termie): be smart about this + try: + os.unlink('bla.db') + except Exception: + pass + migration.db_sync() diff --git a/keystone/common/template.py b/keystone/common/template.py deleted file mode 100644 index 27073e7031..0000000000 --- a/keystone/common/template.py +++ /dev/null @@ -1,373 +0,0 @@ -# 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. -# -# Template library copied from bottle: http://bottlepy.org/ -# -# Copyright (c) 2011, Marcel Hellkamp. -# -# 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. -# - - -import cgi -import re -import os -import functools -import time -import tokenize -import mimetypes -from webob import Response -from paste.util.template import TemplateError -# from paste.util.datetimeutil import parse_date -import datetime - -import keystone.logic.types.fault as fault -from keystone.logic.types.fault import ForbiddenFault - -TEMPLATES = {} -DEBUG = False -TEMPLATE_PATH = ['./', './views/'] - - -class BaseTemplate(object): - """ Base class and minimal API for template adapters """ - extentions = ['tpl', 'html', 'thtml', 'stpl'] - settings = {} # used in prepare() - defaults = {} # used in render() - - def __init__(self, source=None, name=None, lookup=None, encoding='utf8', - **settings): - """ Create a new template. - If the source parameter (str or buffer) is missing, the name argument - is used to guess a template filename. Subclasses can assume that - self.source and/or self.filename are set. Both are strings. - The lookup, encoding and settings parameters are stored as instance - variables. - The lookup parameter stores a list containing directory paths. - The encoding parameter should be used to decode byte strings or files. - The settings parameter contains a dict for engine-specific settings. - """ - lookup = lookup or [] - - self.name = name - self.source = source.read() if hasattr(source, 'read') else source - self.filename = source.filename \ - if hasattr(source, 'filename') \ - else None - self.lookup = [os.path.abspath(path) for path in lookup] - self.encoding = encoding - self.settings = self.settings.copy() # Copy from class variable - self.settings.update(settings) # Apply - if not self.source and self.name: - self.filename = self.search(self.name, self.lookup) - if not self.filename: - raise TemplateError('Template %s not found' % repr(name), - (0, 0), None) - if not self.source and not self.filename: - raise TemplateError('No template specified', (0, 0), None) - self.prepare(**self.settings) - - @classmethod - def search(cls, name, lookup=None): - """ Search name in all directories specified in lookup. - First without, then with common extensions. Return first hit. """ - lookup = lookup or [] - - if os.path.isfile(name): - return name - for spath in lookup: - fname = os.path.join(spath, name) - if os.path.isfile(fname): - return fname - for ext in cls.extentions: - if os.path.isfile('%s.%s' % (fname, ext)): - return '%s.%s' % (fname, ext) - - @classmethod - def global_config(cls, key, *args): - '''This reads or sets the global settings stored in class.settings.''' - if args: - cls.settings[key] = args[0] - else: - return cls.settings[key] - - def prepare(self, **options): - """Run preparations (parsing, caching, ...). - It should be possible to call this again to refresh a template or to - update settings. - """ - raise NotImplementedError - - def render(self, **args): - """Render the template with the specified local variables and return - a single byte or unicode string. If it is a byte string, the encoding - must match self.encoding. This method must be thread-safe! - """ - raise NotImplementedError - - -class SimpleTemplate(BaseTemplate): - blocks = ('if', 'elif', 'else', 'try', 'except', 'finally', 'for', 'while', - 'with', 'def', 'class') - dedent_blocks = ('elif', 'else', 'except', 'finally') - cache = None - code = None - compiled = None - _str = None - _escape = None - - def prepare(self, escape_func=cgi.escape, noescape=False): - self.cache = {} - if self.source: - self.code = self.translate(self.source) - self.compiled = compile(self.code, '', 'exec') - else: - self.code = self.translate(open(self.filename).read()) - self.compiled = compile(self.code, self.filename, 'exec') - enc = self.encoding - touni = functools.partial(unicode, encoding=self.encoding) - self._str = lambda x: touni(x, enc) - self._escape = lambda x: escape_func(touni(x)) - if noescape: - self._str, self._escape = self._escape, self._str - - def translate(self, template): - stack = [] # Current Code indentation - lineno = 0 # Current line of code - ptrbuffer = [] # Buffer for printable strings and token tuples - codebuffer = [] # Buffer for generated python code - functools.partial(unicode, encoding=self.encoding) - multiline = dedent = False - - def yield_tokens(line): - for i, part in enumerate(re.split(r'\{\{(.*?)\}\}', line)): - if i % 2: - if part.startswith('!'): - yield 'RAW', part[1:] - else: - yield 'CMD', part - else: - yield 'TXT', part - - def split_comment(codeline): - """ Removes comments from a line of code. """ - line = codeline.splitlines()[0] - try: - tokens = list(tokenize.generate_tokens(iter(line).next)) - except tokenize.TokenError: - return line.rsplit('#', 1) if '#' in line else (line, '') - for token in tokens: - if token[0] == tokenize.COMMENT: - start, end = token[2][1], token[3][1] - return ( - codeline[:start] + codeline[end:], - codeline[start:end]) - return line, '' - - def flush(): - """Flush the ptrbuffer""" - if not ptrbuffer: - return - cline = '' - for line in ptrbuffer: - for token, value in line: - if token == 'TXT': - cline += repr(value) - elif token == 'RAW': - cline += '_str(%s)' % value - elif token == 'CMD': - cline += '_escape(%s)' % value - cline += ', ' - cline = cline[:-2] + '\\\n' - cline = cline[:-2] - if cline[:-1].endswith('\\\\\\\\\\n'): - cline = cline[:-7] + cline[-1] # 'nobr\\\\\n' --> 'nobr' - cline = '_printlist([' + cline + '])' - del ptrbuffer[:] # Do this before calling code() again - code(cline) - - def code(stmt): - for line in stmt.splitlines(): - codebuffer.append(' ' * len(stack) + line.strip()) - - for line in template.splitlines(True): - lineno += 1 - line = line if isinstance(line, unicode)\ - else unicode(line, encoding=self.encoding) - if lineno <= 2: - m = re.search(r"%.*coding[:=]\s*([-\w\.]+)", line) - if m: - self.encoding = m.group(1) - if m: - line = line.replace('coding', 'coding (removed)') - if line.strip()[:2].count('%') == 1: - line = line.split('%', 1)[1].lstrip() # Rest of line after % - cline = split_comment(line)[0].strip() - cmd = re.split(r'[^a-zA-Z0-9_]', cline)[0] - flush() # encodig (TODO: why?) - if cmd in self.blocks or multiline: - cmd = multiline or cmd - dedent = cmd in self.dedent_blocks # "else:" - if dedent and not multiline: - cmd = stack.pop() - code(line) - oneline = not cline.endswith(':') # "if 1: pass" - multiline = cmd if cline.endswith('\\') else False - if not oneline and not multiline: - stack.append(cmd) - elif cmd == 'end' and stack: - code('#end(%s) %s' % (stack.pop(), line.strip()[3:])) - elif cmd == 'include': - p = cline.split(None, 2)[1:] - if len(p) == 2: - code("_=_include(%s, _stdout, %s)" % - (repr(p[0]), p[1])) - elif p: - code("_=_include(%s, _stdout)" % repr(p[0])) - else: # Empty %include -> reverse of %rebase - code("_printlist(_base)") - elif cmd == 'rebase': - p = cline.split(None, 2)[1:] - if len(p) == 2: - code("globals()['_rebase']=(%s, dict(%s))" % ( - repr(p[0]), p[1])) - elif p: - code("globals()['_rebase']=(%s, {})" % repr(p[0])) - else: - code(line) - else: # Line starting with text (not '%') or '%%' (escaped) - if line.strip().startswith('%%'): - line = line.replace('%%', '%', 1) - ptrbuffer.append(yield_tokens(line)) - flush() - return '\n'.join(codebuffer) + '\n' - - def subtemplate(self, _name, _stdout, **args): - if _name not in self.cache: - self.cache[_name] = self.__class__(name=_name, lookup=self.lookup) - return self.cache[_name].execute(_stdout, **args) - - def execute(self, _stdout, **args): - env = self.defaults.copy() - env.update({'_stdout': _stdout, '_printlist': _stdout.extend, - '_include': self.subtemplate, '_str': self._str, - '_escape': self._escape}) - env.update(args) - eval(self.compiled, env) - if '_rebase' in env: - subtpl, rargs = env['_rebase'] - subtpl = self.__class__(name=subtpl, lookup=self.lookup) - rargs['_base'] = _stdout[:] # copy stdout - del _stdout[:] # clear stdout - return subtpl.execute(_stdout, **rargs) - return env - - def render(self, **args): - """ Render the template using keyword arguments as local variables. """ - stdout = [] - self.execute(stdout, **args) - return ''.join(stdout) - - -def static_file(resp, req, filename, root, guessmime=True, mimetype=None, - download=False): - """ Opens a file in a safe way and returns a HTTPError object with status - code 200, 305, 401 or 404. Sets Content-Type, Content-Length and - Last-Modified header. Obeys If-Modified-Since header and HEAD requests. - """ - root = os.path.abspath(root) + os.sep - filename = os.path.abspath(os.path.join(root, filename.strip('/\\'))) - if not filename.startswith(root): - return ForbiddenFault("Access denied.") - if not os.path.exists(filename) or not os.path.isfile(filename): - return fault.ItemNotFoundFault("File does not exist.") - if not os.access(filename, os.R_OK): - return ForbiddenFault( - "You do not have permission to access this file.") - - if not mimetype and guessmime: - resp.content_type = mimetypes.guess_type(filename)[0] - else: - resp.content_type = mimetype or 'text/plain' - - if download: - download = os.path.basename(filename) - resp.content_disposition = 'attachment; filename="%s"' % download - - stats = os.stat(filename) - lm = time.strftime("%a, %d %b %Y %H:%M:%S GMT", - time.gmtime(stats.st_mtime)) - resp.last_modified = lm - ims = req.environ.get('HTTP_IF_MODIFIED_SINCE') - if ims: - ims = ims.split(";")[0].strip() # IE sends "; length=146" - try: - ims = datetime.datetime.fromtimestamp(stats.st_mtime) - ims = datetime.datetime.ctime(ims) - filetime = datetime.datetime.fromtimestamp(stats.st_mtime) - if ims is not None and ims >= filetime: - resp.date = time.strftime( - "%a, %d %b %Y %H:%M:%S GMT", time.gmtime()) - return Response(body=None, status=304, - headerlist=resp.headerlist) - except: - # TODO(Ziad): handle this better - pass - resp.content_length = stats.st_size - if req.method == 'HEAD': - return Response(body=None, status=200, headerlist=resp.headerlist) - else: - return Response(body=open(filename).read(), status=200, - headerlist=resp.headerlist) - - -def template(tpl, template_adapter=SimpleTemplate, **kwargs): - ''' - Get a rendered template as a string iterator. - You can use a name, a filename or a template string as first parameter. - ''' - if tpl not in TEMPLATES or DEBUG: - settings = kwargs.get('template_settings', {}) - lookup = kwargs.get('template_lookup', TEMPLATE_PATH) - if isinstance(tpl, template_adapter): - TEMPLATES[tpl] = tpl - if settings: - TEMPLATES[tpl].prepare(**settings) - elif "\n" in tpl or "{" in tpl or "%" in tpl or '$' in tpl: - TEMPLATES[tpl] = template_adapter(source=tpl, lookup=lookup, - **settings) - else: - TEMPLATES[tpl] = template_adapter(name=tpl, lookup=lookup, - **settings) - return TEMPLATES[tpl].render(**kwargs) diff --git a/keystone/common/utils.py b/keystone/common/utils.py new file mode 100644 index 0000000000..96e595bb0a --- /dev/null +++ b/keystone/common/utils.py @@ -0,0 +1,227 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# Copyright 2011 - 2012 Justin Santa Barbara +# 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 base64 +import hashlib +import hmac +import json +import subprocess +import sys +import time +import urllib + +import passlib.hash + +from keystone import config +from keystone.common import logging + + +CONF = config.CONF +config.register_int('crypt_strength', default=40000) + + +ISO_TIME_FORMAT = '%Y-%m-%dT%H:%M:%SZ' + + +def import_class(import_str): + """Returns a class from a string including module and class.""" + mod_str, _sep, class_str = import_str.rpartition('.') + try: + __import__(mod_str) + return getattr(sys.modules[mod_str], class_str) + except (ImportError, ValueError, AttributeError), exc: + logging.debug('Inner Exception: %s', exc) + raise + + +def import_object(import_str, *args, **kw): + """Returns an object including a module or module and class.""" + try: + __import__(import_str) + return sys.modules[import_str] + except ImportError: + cls = import_class(import_str) + return cls(*args, **kw) + + +class SmarterEncoder(json.JSONEncoder): + """Help for JSON encoding dict-like objects.""" + def default(self, obj): + if not isinstance(obj, dict) and hasattr(obj, 'iteritems'): + return dict(obj.iteritems()) + return super(SmarterEncoder, self).default(obj) + + +class Ec2Signer(object): + """Hacked up code from boto/connection.py""" + + def __init__(self, secret_key): + secret_key = secret_key.encode() + self.hmac = hmac.new(secret_key, digestmod=hashlib.sha1) + if hashlib.sha256: + self.hmac_256 = hmac.new(secret_key, digestmod=hashlib.sha256) + + def generate(self, credentials): + """Generate auth string according to what SignatureVersion is given.""" + if credentials['params']['SignatureVersion'] == '0': + return self._calc_signature_0(credentials['params']) + if credentials['params']['SignatureVersion'] == '1': + return self._calc_signature_1(credentials['params']) + if credentials['params']['SignatureVersion'] == '2': + return self._calc_signature_2(credentials['params'], + credentials['verb'], + credentials['host'], + credentials['path']) + raise Exception('Unknown Signature Version: %s' % + credentials['params']['SignatureVersion']) + + @staticmethod + def _get_utf8_value(value): + """Get the UTF8-encoded version of a value.""" + if not isinstance(value, str) and not isinstance(value, unicode): + value = str(value) + if isinstance(value, unicode): + return value.encode('utf-8') + else: + return value + + def _calc_signature_0(self, params): + """Generate AWS signature version 0 string.""" + s = params['Action'] + params['Timestamp'] + self.hmac.update(s) + return base64.b64encode(self.hmac.digest()) + + def _calc_signature_1(self, params): + """Generate AWS signature version 1 string.""" + keys = params.keys() + keys.sort(cmp=lambda x, y: cmp(x.lower(), y.lower())) + for key in keys: + self.hmac.update(key) + val = self._get_utf8_value(params[key]) + self.hmac.update(val) + return base64.b64encode(self.hmac.digest()) + + def _calc_signature_2(self, params, verb, server_string, path): + """Generate AWS signature version 2 string.""" + logging.debug('using _calc_signature_2') + string_to_sign = '%s\n%s\n%s\n' % (verb, server_string, path) + if self.hmac_256: + current_hmac = self.hmac_256 + params['SignatureMethod'] = 'HmacSHA256' + else: + current_hmac = self.hmac + params['SignatureMethod'] = 'HmacSHA1' + keys = params.keys() + keys.sort() + pairs = [] + for key in keys: + val = self._get_utf8_value(params[key]) + val = urllib.quote(val, safe='-_~') + pairs.append(urllib.quote(key, safe='') + '=' + val) + qs = '&'.join(pairs) + logging.debug('query string: %s', qs) + string_to_sign += qs + logging.debug('string_to_sign: %s', string_to_sign) + current_hmac.update(string_to_sign) + b64 = base64.b64encode(current_hmac.digest()) + logging.debug('len(b64)=%d', len(b64)) + logging.debug('base64 encoded digest: %s', b64) + return b64 + + +def hash_password(password): + """Hash a password. Hard.""" + password_utf8 = password.encode('utf-8') + if passlib.hash.sha512_crypt.identify(password_utf8): + return password_utf8 + h = passlib.hash.sha512_crypt.encrypt(password_utf8, + rounds=CONF.crypt_strength) + return h + + +def check_password(password, hashed): + """Check that a plaintext password matches hashed. + + hashpw returns the salt value concatenated with the actual hash value. + It extracts the actual salt if this value is then passed as the salt. + + """ + if password is None: + return False + password_utf8 = password.encode('utf-8') + return passlib.hash.sha512_crypt.verify(password_utf8, hashed) + + +# From python 2.7 +def check_output(*popenargs, **kwargs): + r"""Run command with arguments and return its output as a byte string. + + If the exit code was non-zero it raises a CalledProcessError. The + CalledProcessError object will have the return code in the returncode + attribute and output in the output attribute. + + The arguments are the same as for the Popen constructor. Example: + + >>> check_output(['ls', '-l', '/dev/null']) + 'crw-rw-rw- 1 root root 1, 3 Oct 18 2007 /dev/null\n' + + The stdout argument is not allowed as it is used internally. + To capture standard error in the result, use stderr=STDOUT. + + >>> check_output(['/bin/sh', '-c', + ... 'ls -l non_existent_file ; exit 0'], + ... stderr=STDOUT) + 'ls: non_existent_file: No such file or directory\n' + """ + if 'stdout' in kwargs: + raise ValueError('stdout argument not allowed, it will be overridden.') + logging.debug(' '.join(popenargs[0])) + process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs) + output, unused_err = process.communicate() + retcode = process.poll() + if retcode: + cmd = kwargs.get('args') + if cmd is None: + cmd = popenargs[0] + raise subprocess.CalledProcessError(retcode, cmd) + return output + + +def git(*args): + return check_output(['git'] + list(args)) + + +def isotime(dt_obj): + """Format datetime object as ISO compliant string. + + :param dt_obj: datetime.datetime object + :returns: string representation of datetime object + + """ + return dt_obj.strftime(ISO_TIME_FORMAT) + + +def unixtime(dt_obj): + """Format datetime object as unix timestamp + + :param dt_obj: datetime.datetime object + :returns: float + + """ + return time.mktime(dt_obj.utctimetuple()) diff --git a/keystone/common/wsgi.py b/keystone/common/wsgi.py old mode 100755 new mode 100644 index c3ce797518..7b6926d0f7 --- a/keystone/common/wsgi.py +++ b/keystone/common/wsgi.py @@ -17,102 +17,60 @@ # License for the specific language governing permissions and limitations # under the License. -# pylint: disable=W0613 -""" -Utility methods for working with WSGI servers -""" +"""Utility methods for working with WSGI servers.""" import json import logging import sys -import datetime -import ssl +import eventlet import eventlet.wsgi -eventlet.patcher.monkey_patch(all=False, socket=True) +eventlet.patcher.monkey_patch(all=False, socket=True, time=True) +import routes import routes.middleware -from webob import Response +import webob import webob.dec +import webob.exc -LOG = logging.getLogger(__name__) - - -def find_console_handler(logger): - """Returns a stream handler, if any""" - for handler in logger.handlers: - if isinstance(handler, logging.StreamHandler) and \ - handler.stream == sys.stderr: - return handler - - -def add_console_handler(logger, level=logging.INFO): - """ - Add a Handler which writes log messages to sys.stderr (usually the console) - """ - console = find_console_handler(logger) - - if not console: - console = logging.StreamHandler() - console.setLevel(level) - # set a format which is simpler for console use - formatter = logging.Formatter( - "%(name)-12s: %(levelname)-8s %(message)s") - # tell the handler to use this format - console.setFormatter(formatter) - # add the handler to the root logger - LOG.debug("Adding console handler at level %s" % level) - logger.addHandler(console) - elif console.level != level: - LOG.debug("Setting console handler level to %s from %s" % (level, - console.level)) - console.setLevel(level) - return console +from keystone import exception +from keystone.common import utils class WritableLogger(object): """A thin wrapper that responds to `write` and logs.""" - def __init__(self, logger, level=logging.INFO): + def __init__(self, logger, level=logging.DEBUG): self.logger = logger self.level = level - if level == logging.DEBUG: - add_console_handler(logger, level) def write(self, msg): - self.logger.log(self.level, msg.strip("\n")) - - -def run_server(application, port): - """Run a WSGI server with the given application.""" - LOG.debug("Running WSGI server on 0.0.0.0:%s" % port) - sock = eventlet.listen(('0.0.0.0', port)) - eventlet.wsgi.server(sock, application) + self.logger.log(self.level, msg) class Server(object): """Server class to manage multiple WSGI sockets and applications.""" - started = False - def __init__(self, application=None, port=None, threads=1000): + def __init__(self, application, port, threads=1000): self.application = application self.port = port self.pool = eventlet.GreenPool(threads) self.socket_info = {} - self.threads = {} + self.greenthread = None - def start(self, application=None, port=None, host='0.0.0.0', key=None, - backlog=128): + def start(self, host='0.0.0.0', key=None, backlog=128): """Run a WSGI server with the given application.""" - if application is not None: - self.application = application - if port is not None: - self.port = port - LOG.debug("start server '%s' on %s:%s" % (key, host, self.port)) + logging.debug('Starting %(arg0)s on %(host)s:%(port)s' % \ + {'arg0': sys.argv[0], + 'host': host, + 'port': self.port}) socket = eventlet.listen((host, self.port), backlog=backlog) - thread = self.pool.spawn(self._run, self.application, socket) + self.greenthread = self.pool.spawn(self._run, self.application, socket) if key: - self.socket_info[key] = socket - self.threads[key] = thread + self.socket_info[key] = socket.getsockname() + + def kill(self): + if self.greenthread: + self.greenthread.kill() def wait(self): """Wait until all servers have completed running.""" @@ -123,50 +81,189 @@ class Server(object): def _run(self, application, socket): """Start a WSGI server in a new green thread.""" - LOG.debug("_run called") - eventlet_logger = logging.getLogger('eventlet.wsgi.server') + logger = logging.getLogger('eventlet.wsgi.server') eventlet.wsgi.server(socket, application, custom_pool=self.pool, - log=WritableLogger(eventlet_logger, logging.root.level)) + log=WritableLogger(logger)) -class SslServer(Server): - """SSL Server class to manage multiple WSGI sockets and applications.""" - # pylint: disable=W0221,R0913 - def start(self, application, port, host='0.0.0.0', backlog=128, - certfile=None, keyfile=None, ca_certs=None, - cert_required='True', key=None): - """Run a 2-way SSL WSGI server with the given application.""" - LOG.debug("start SSL server '%s' on %s:%s" % (key, host, port)) - socket = eventlet.listen((host, port), backlog=backlog) - if cert_required == 'True': - cert_reqs = ssl.CERT_REQUIRED - else: - cert_reqs = ssl.CERT_NONE - sslsocket = eventlet.wrap_ssl(socket, certfile=certfile, - keyfile=keyfile, - server_side=True, cert_reqs=cert_reqs, - ca_certs=ca_certs) - thread = self.pool.spawn(self._run, application, sslsocket) - if key: - self.socket_info[key] = sslsocket - self.threads[key] = thread +class Request(webob.Request): + pass -class Middleware(object): - """ - Base WSGI middleware wrapper. These classes require an application to be +class BaseApplication(object): + """Base WSGI application wrapper. Subclasses need to implement __call__.""" + + @classmethod + def factory(cls, global_config, **local_config): + """Used for paste app factories in paste.deploy config files. + + Any local configuration (that is, values under the [app:APPNAME] + section of the paste config) will be passed into the `__init__` method + as kwargs. + + A hypothetical configuration would look like: + + [app:wadl] + latest_version = 1.3 + paste.app_factory = nova.api.fancy_api:Wadl.factory + + which would result in a call to the `Wadl` class as + + import nova.api.fancy_api + fancy_api.Wadl(latest_version='1.3') + + You could of course re-implement the `factory` method in subclasses, + but using the kwarg passing it shouldn't be necessary. + + """ + return cls() + + def __call__(self, environ, start_response): + r"""Subclasses will probably want to implement __call__ like this: + + @webob.dec.wsgify(RequestClass=Request) + def __call__(self, req): + # Any of the following objects work as responses: + + # Option 1: simple string + res = 'message\n' + + # Option 2: a nicely formatted HTTP exception page + res = exc.HTTPForbidden(detail='Nice try') + + # Option 3: a webob Response object (in case you need to play with + # headers, or you want to be treated like an iterable, or or or) + res = Response(); + res.app_iter = open('somefile') + + # Option 4: any wsgi app to be run next + res = self.application + + # Option 5: you can get a Response object for a wsgi app, too, to + # play with headers etc + res = req.get_response(self.application) + + # You can then just return your response... + return res + # ... or set req.response and return None. + req.response = res + + See the end of http://pythonpaste.org/webob/modules/dec.html + for more info. + + """ + raise NotImplementedError('You must implement __call__') + + +class Application(BaseApplication): + @webob.dec.wsgify + def __call__(self, req): + arg_dict = req.environ['wsgiorg.routing_args'][1] + action = arg_dict.pop('action') + del arg_dict['controller'] + logging.debug('arg_dict: %s', arg_dict) + + # allow middleware up the stack to provide context & params + context = req.environ.get('openstack.context', {}) + context['query_string'] = dict(req.params.iteritems()) + params = req.environ.get('openstack.params', {}) + params.update(arg_dict) + + # TODO(termie): do some basic normalization on methods + method = getattr(self, action) + + # NOTE(vish): make sure we have no unicode keys for py2.6. + params = self._normalize_dict(params) + + try: + result = method(context, **params) + except exception.Error as e: + logging.warning(e) + return render_exception(e) + + if result is None or type(result) is str or type(result) is unicode: + return result + elif isinstance(result, webob.exc.WSGIHTTPException): + return result + + response = webob.Response() + self._serialize(response, result) + return response + + def _serialize(self, response, result): + response.content_type = 'application/json' + response.body = json.dumps(result, cls=utils.SmarterEncoder) + + def _normalize_arg(self, arg): + return str(arg).replace(':', '_').replace('-', '_') + + def _normalize_dict(self, d): + return dict([(self._normalize_arg(k), v) + for (k, v) in d.iteritems()]) + + def assert_admin(self, context): + if not context['is_admin']: + try: + user_token_ref = self.token_api.get_token( + context=context, token_id=context['token_id']) + except exception.TokenNotFound: + raise exception.Unauthorized() + creds = user_token_ref['metadata'].copy() + creds['user_id'] = user_token_ref['user'].get('id') + creds['tenant_id'] = user_token_ref['tenant'].get('id') + # NOTE(vish): this is pretty inefficient + creds['roles'] = [self.identity_api.get_role(context, role)['name'] + for role in creds.get('roles', [])] + # Accept either is_admin or the admin role + assert self.policy_api.can_haz(context, + ('is_admin:1', 'roles:admin'), + creds) + + +class Middleware(Application): + """Base WSGI middleware. + + These classes require an application to be initialized that will be called next. By default the middleware will simply call its wrapped app, or you can override __call__ to customize its behavior. + """ + @classmethod + def factory(cls, global_config, **local_config): + """Used for paste app factories in paste.deploy config files. + + Any local configuration (that is, values under the [filter:APPNAME] + section of the paste config) will be passed into the `__init__` method + as kwargs. + + A hypothetical configuration would look like: + + [filter:analytics] + redis_host = 127.0.0.1 + paste.filter_factory = nova.api.analytics:Analytics.factory + + which would result in a call to the `Analytics` class as + + import nova.api.analytics + analytics.Analytics(app_from_paste, redis_host='127.0.0.1') + + You could of course re-implement the `factory` method in subclasses, + but using the kwarg passing it shouldn't be necessary. + + """ + def _factory(app): + conf = global_config.copy() + conf.update(local_config) + return cls(app) + return _factory + def __init__(self, application): self.application = application - @staticmethod - def process_request(req): - """ - Called on each request. + def process_request(self, req): + """Called on each request. If this returns None, the next application down the stack will be executed. If it returns a response then that response will be returned @@ -175,13 +272,11 @@ class Middleware(object): """ return None - @staticmethod - def process_response(response): + def process_response(self, response): """Do whatever you'd like to the response.""" return response - # pylint: disable=W1111 - @webob.dec.wsgify + @webob.dec.wsgify(RequestClass=Request) def __call__(self, req): response = self.process_request(req) if response: @@ -191,23 +286,30 @@ class Middleware(object): class Debug(Middleware): - """ - Helper class that can be inserted into any WSGI application chain - to get information about the request and response. + """Helper class for debugging a WSGI application. + + Can be inserted into any WSGI application chain to get information + about the request and response. + """ - @webob.dec.wsgify + @webob.dec.wsgify(RequestClass=Request) def __call__(self, req): - print ("*" * 40) + " REQUEST ENVIRON" + logging.debug('%s %s %s', ('*' * 20), 'REQUEST ENVIRON', ('*' * 20)) for key, value in req.environ.items(): - print key, "=", value - print + logging.debug('%s = %s', key, value) + logging.debug('') + logging.debug('%s %s %s', ('*' * 20), 'REQUEST BODY', ('*' * 20)) + for line in req.body_file: + logging.debug(line) + logging.debug('') + resp = req.get_response(self.application) - print ("*" * 40) + " RESPONSE HEADERS" + logging.debug('%s %s %s', ('*' * 20), 'RESPONSE HEADERS', ('*' * 20)) for (key, value) in resp.headers.iteritems(): - print key, "=", value - print + logging.debug('%s = %s', key, value) + logging.debug('') resp.app_iter = self.print_generator(resp.app_iter) @@ -215,283 +317,158 @@ class Debug(Middleware): @staticmethod def print_generator(app_iter): - """ - Iterator that prints the contents of a wrapper string iterator - when iterated. - """ - print ("*" * 40) + " BODY" + """Iterator that prints the contents of a wrapper string.""" + logging.debug('%s %s %s', ('*' * 20), 'RESPONSE BODY', ('*' * 20)) for part in app_iter: - sys.stdout.write(part) - sys.stdout.flush() + #sys.stdout.write(part) + logging.debug(part) + #sys.stdout.flush() yield part print -def debug_filter_factory(global_conf): - """Filter factor to easily insert a debugging middleware into the - paste.deploy pipeline""" - def filter(app): - return Debug(app) - return filter - - class Router(object): - """ - WSGI middleware that maps incoming requests to WSGI apps. - """ + """WSGI middleware that maps incoming requests to WSGI apps.""" def __init__(self, mapper): - """ - Create a router for the given routes.Mapper. + """Create a router for the given routes.Mapper. Each route in `mapper` must specify a 'controller', which is a WSGI app to call. You'll probably want to specify an 'action' as - well and have your controller be a wsgi.Controller, who will route - the request to the action method. + well and have your controller be an object that can route + the request to the action-specific method. Examples: mapper = routes.Mapper() sc = ServerController() # Explicit mapping of one route to a controller+action - mapper.connect(None, "/svrlist", controller=sc, action="list") + mapper.connect(None, '/svrlist', controller=sc, action='list') # Actions are all implicitly defined - mapper.resource("server", "servers", controller=sc) + mapper.resource('server', 'servers', controller=sc) # Pointing to an arbitrary WSGI app. You can specify the # {path_info:.*} parameter so the target app can be handed just that # section of the URL. - mapper.connect(None, "/v2.0/{path_info:.*}", controller=TheApp()) + mapper.connect(None, '/v1.0/{path_info:.*}', controller=BlogApp()) + """ self.map = mapper self._router = routes.middleware.RoutesMiddleware(self._dispatch, self.map) - @webob.dec.wsgify + @webob.dec.wsgify(RequestClass=Request) def __call__(self, req): - """ - Route the incoming request to a controller based on self.map. + """Route the incoming request to a controller based on self.map. + If no match, return a 404. + """ return self._router @staticmethod - @webob.dec.wsgify + @webob.dec.wsgify(RequestClass=Request) def _dispatch(req): - """ + """Dispatch the request to the appropriate controller. + Called by self._router after matching the incoming request to a route - and putting the information into req.environ. Returns the routed - WSGI app's response or an Accept-appropriate 404. + and putting the information into req.environ. Either returns 404 + or the routed WSGI app's response. + """ - return req.environ['wsgiorg.routing_args'][1].get('controller') \ - or HTTPNotFound() + match = req.environ['wsgiorg.routing_args'][1] + if not match: + return webob.exc.HTTPNotFound() + app = match['controller'] + return app -class Controller(object): - """ - WSGI app that reads routing information supplied by RoutesMiddleware - and calls the requested action method upon itself. All action methods - must, in addition to their normal parameters, accept a 'req' argument - which is the incoming webob.Request. They raise a webob.exc exception, - or return a dict which will be serialized by requested content type. +class ComposingRouter(Router): + def __init__(self, mapper=None, routers=None): + if mapper is None: + mapper = routes.Mapper() + if routers is None: + routers = [] + for router in routers: + router.add_routes(mapper) + super(ComposingRouter, self).__init__(mapper) + + +class ComposableRouter(Router): + """Router that supports use by ComposingRouter.""" + + def __init__(self, mapper=None): + if mapper is None: + mapper = routes.Mapper() + self.add_routes(mapper) + super(ComposableRouter, self).__init__(mapper) + + def add_routes(self, mapper): + """Add routes to given mapper.""" + pass + + +class ExtensionRouter(Router): + """A router that allows extensions to supplement or overwrite routes. + + Expects to be subclassed. """ + def __init__(self, application, mapper=None): + if mapper is None: + mapper = routes.Mapper() + self.application = application + self.add_routes(mapper) + mapper.connect('{path_info:.*}', controller=self.application) + super(ExtensionRouter, self).__init__(mapper) + + def add_routes(self, mapper): + pass + + @classmethod + def factory(cls, global_config, **local_config): + """Used for paste app factories in paste.deploy config files. + + Any local configuration (that is, values under the [filter:APPNAME] + section of the paste config) will be passed into the `__init__` method + as kwargs. + + A hypothetical configuration would look like: + + [filter:analytics] + redis_host = 127.0.0.1 + paste.filter_factory = nova.api.analytics:Analytics.factory + + which would result in a call to the `Analytics` class as + + import nova.api.analytics + analytics.Analytics(app_from_paste, redis_host='127.0.0.1') + + You could of course re-implement the `factory` method in subclasses, + but using the kwarg passing it shouldn't be necessary. - @webob.dec.wsgify - def __call__(self, req): """ - Call the method specified in req.environ by RoutesMiddleware. - """ - arg_dict = req.environ['wsgiorg.routing_args'][1] - action = arg_dict['action'] - method = getattr(self, action) - del arg_dict['controller'] - del arg_dict['action'] - arg_dict['req'] = req - result = method(**arg_dict) - if isinstance(result, dict): - return self._serialize(result, req) - else: - return result - - def _serialize(self, data, request): - """ - Serialize the given dict to the response type requested in request. - Uses self._serialization_metadata if it exists, which is a dict mapping - MIME types to information needed to serialize to that type. - """ - _metadata = getattr(type(self), "_serialization_metadata", {}) - serializer = Serializer(request.environ, _metadata) - return serializer.to_content_type(data) + def _factory(app): + conf = global_config.copy() + conf.update(local_config) + return cls(app) + return _factory -class Serializer(object): - """ - Serializes a dictionary to a Content Type specified by a WSGI environment. - """ +def render_exception(error): + """Forms a WSGI response based on the current error.""" + resp = webob.Response() + resp.status = '%s %s' % (error.code, error.title) + resp.headerlist = [('Content-Type', 'application/json')] - def __init__(self, environ, metadata=None): - """ - Create a serializer based on the given WSGI environment. - 'metadata' is an optional dict mapping MIME types to information - needed to serialize a dictionary to that type. - """ - if metadata is None: - metadata = {} - self.environ = environ - self.metadata = metadata - self._methods = { - 'application/json': self._to_json, - 'application/xml': self._to_xml} + body = { + 'error': { + 'code': error.code, + 'title': error.title, + 'message': str(error), + } + } - def to_content_type(self, data): - """ - Serialize a dictionary into a string. The format of the string - will be decided based on the Content Type requested in self.environ: - by Accept: header, or by URL suffix. - """ - # FIXME(sirp): for now, supporting json only - #mimetype = 'application/xml' - mimetype = 'application/json' - LOG.debug("serializing: mimetype=%s" % mimetype) - # TODO(gundlach): determine mimetype from request - return self._methods.get(mimetype, repr)(data) + resp.body = json.dumps(body) - @staticmethod - def _to_json(data): - def sanitizer(obj): - if isinstance(obj, datetime.datetime): - return obj.isoformat() - return obj - - return json.dumps(data, default=sanitizer) - - def _to_xml(self, data): - metadata = self.metadata.get('application/xml', {}) - # We expect data to contain a single key which is the XML root. - root_key = data.keys()[0] - from xml.dom import minidom - doc = minidom.Document() - node = self._to_xml_node(doc, metadata, root_key, data[root_key]) - return node.toprettyxml(indent=' ') - - def _to_xml_node(self, doc, metadata, nodename, data): - """Recursive method to convert data members to XML nodes.""" - result = doc.createElement(nodename) - if isinstance(data, list): - singular = metadata.get('plurals', {}).get(nodename, None) - if singular is None: - if nodename.endswith('s'): - singular = nodename[:-1] - else: - singular = 'item' - for item in data: - node = self._to_xml_node(doc, metadata, singular, item) - result.appendChild(node) - elif isinstance(data, dict): - attrs = metadata.get('attributes', {}).get(nodename, {}) - for k, v in data.items(): - if k in attrs: - result.setAttribute(k, str(v)) - else: - node = self._to_xml_node(doc, metadata, k, v) - result.appendChild(node) - else: # atom - node = doc.createTextNode(str(data)) - result.appendChild(node) - return result - - -class WSGIHTTPException(Response, webob.exc.HTTPException): - """Returned when no matching route can be identified""" - - code = None - label = None - title = None - explanation = None - - xml_template = """\ - -<%s xmlns="http://docs.openstack.org/identity/api/v2.0" code="%s"> - %s -
%s
-""" - - def __init__(self, code, label, title, explanation, **kw): - self.code = code - self.label = label - self.title = title - self.explanation = explanation - - Response.__init__(self, status='%s %s' % (self.code, self.title), **kw) - webob.exc.HTTPException.__init__(self, self.explanation, self) - - def xml_body(self): - """Generate a XML body string using the available data""" - return self.xml_template % ( - self.label, self.code, self.title, self.explanation, self.label) - - def json_body(self): - """Generate a JSON body string using the available data""" - json_dict = {self.label: {}} - json_dict[self.label]['message'] = self.title - json_dict[self.label]['details'] = self.explanation - json_dict[self.label]['code'] = self.code - - return json.dumps(json_dict) - - def generate_response(self, environ, start_response): - """Returns a response to the given environment""" - if self.content_length is not None: - del self.content_length - - headerlist = list(self.headerlist) - - accept = environ.get('HTTP_ACCEPT', '') - - # Return JSON by default - if accept and 'xml' in accept: - content_type = 'application/xml' - body = self.xml_body() - else: - content_type = 'application/json' - body = self.json_body() - - extra_kw = {} - - if isinstance(body, unicode): - extra_kw.update(charset='utf-8') - - resp = Response(body, - status=self.status, - headerlist=headerlist, - content_type=content_type, - **extra_kw) - - # Why is this repeated? - resp.content_type = content_type - - return resp(environ, start_response) - - def __call__(self, environ, start_response): - if environ['REQUEST_METHOD'] == 'HEAD': - start_response(self.status, self.headerlist) - return [] - if not self.body: - return self.generate_response(environ, start_response) - return webob.Response.__call__(self, environ, start_response) - - def exception(self): - """Returns self as an exception response""" - return webob.exc.HTTPException(self.explanation, self) - - exception = property(exception) - - -class HTTPNotFound(WSGIHTTPException): - """Represents a 404 Not Found webob response exception""" - def __init__(self, code=404, label='itemNotFound', title='Item not found.', - explanation='Error Details...', **kw): - """Build a 404 WSGI response""" - super(HTTPNotFound, self).__init__(code, label, title, explanation, - **kw) + return resp diff --git a/keystone/config.py b/keystone/config.py index 977c3df3be..e95efe425b 100644 --- a/keystone/config.py +++ b/keystone/config.py @@ -1,41 +1,22 @@ -#!/usr/bin/env python +# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# 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. - -""" -Routines for configuring OpenStack Service -""" import gettext -import logging -import logging.config -import logging.handlers import sys import os -from keystone import cfg +from keystone.common import cfg +from keystone.common import logging -gettext.install("keystone", unicode=1) +gettext.install('keystone', unicode=1) -class Config(cfg.CommonConfigOpts): - def __call__(self, config_files=None): +class ConfigMixin(object): + def __call__(self, config_files=None, *args, **kw): if config_files is not None: - self._opts["config_file"]["opt"].default = config_files - return super(Config, self).__call__() + self._opts['config_file']['opt'].default = config_files + kw.setdefault('args', []) + return super(ConfigMixin, self).__call__(*args, **kw) def __getitem__(self, key, default=None): return getattr(self, key, default) @@ -44,21 +25,23 @@ class Config(cfg.CommonConfigOpts): return setattr(self, key, value) def iteritems(self): - for key in self._opts: - yield (key, getattr(self, key)) + for k in self._opts: + yield (k, getattr(self, k)) - def to_dict(self): - """ Returns a representation of the CONF settings as a dict.""" - ret = {} - for key, val in self.iteritems(): - if val is not None: - ret[key] = val - for grp_name in self._groups: - ret[grp_name] = grp_dict = {} - grp = self._get_group(grp_name) - for opt in grp._opts: # pylint: disable=W0212 - grp_dict[opt] = self._get(opt, grp_name) - return ret + def print_help(self): + self._oparser.print_help() + + def set_usage(self, usage): + self.usage = usage + self._oparser.usage = usage + + +class Config(ConfigMixin, cfg.ConfigOpts): + pass + + +class CommonConfig(ConfigMixin, cfg.CommonConfigOpts): + pass def setup_logging(conf): @@ -67,15 +50,16 @@ def setup_logging(conf): :param conf: a cfg.ConfOpts object """ + if conf.log_config: # Use a logging configuration file for all settings... - for location in (sys.argv[0], "."): - pth = os.path.join(location, "etc", conf.log_config) - if os.path.exists(pth): - logging.config.fileConfig(pth) - return - raise RuntimeError("Unable to locate specified logging " - "config file: %s" % conf.log_config) + if os.path.exists(conf.log_config): + logging.config.fileConfig(conf.log_config) + return + else: + raise RuntimeError('Unable to locate specified logging ' + 'config file: %s' % conf.log_config) + root_logger = logging.root if conf.debug: root_logger.setLevel(logging.DEBUG) @@ -83,94 +67,92 @@ def setup_logging(conf): root_logger.setLevel(logging.INFO) else: root_logger.setLevel(logging.WARNING) + formatter = logging.Formatter(conf.log_format, conf.log_date_format) if conf.use_syslog: try: - facility = getattr(logging.handlers.SysLogHandler, + facility = getattr(logging.SysLogHandler, conf.syslog_log_facility) except AttributeError: - raise ValueError(_("Invalid syslog facility")) + raise ValueError(_('Invalid syslog facility')) - handler = logging.handlers.SysLogHandler(address="/dev/log", + handler = logging.SysLogHandler(address='/dev/log', facility=facility) elif conf.log_file: logfile = conf.log_file if conf.log_dir: logfile = os.path.join(conf.log_dir, logfile) - handler = logging.handlers.WatchedFileHandler(logfile) + handler = logging.WatchedFileHandler(logfile) else: handler = logging.StreamHandler(sys.stdout) + handler.setFormatter(formatter) root_logger.addHandler(handler) def register_str(*args, **kw): - group = _ensure_group(kw) - return CONF.register_opt(cfg.StrOpt(*args, **kw), group=group) + conf = kw.pop('conf', CONF) + group = _ensure_group(kw, conf) + return conf.register_opt(cfg.StrOpt(*args, **kw), group=group) def register_cli_str(*args, **kw): - group = _ensure_group(kw) - return CONF.register_opt(cfg.StrOpt(*args, **kw), group=group) + conf = kw.pop('conf', CONF) + group = _ensure_group(kw, conf) + return conf.register_cli_opt(cfg.StrOpt(*args, **kw), group=group) def register_bool(*args, **kw): - group = _ensure_group(kw) - return CONF.register_opt(cfg.BoolOpt(*args, **kw), group=group) + conf = kw.pop('conf', CONF) + group = _ensure_group(kw, conf) + return conf.register_opt(cfg.BoolOpt(*args, **kw), group=group) def register_cli_bool(*args, **kw): - group = _ensure_group(kw) - return CONF.register_cli_opt(cfg.BoolOpt(*args, **kw), group=group) + conf = kw.pop('conf', CONF) + group = _ensure_group(kw, conf) + return conf.register_cli_opt(cfg.BoolOpt(*args, **kw), group=group) -def register_list(*args, **kw): - group = _ensure_group(kw) - return CONF.register_opt(cfg.ListOpt(*args, **kw), group=group) +def register_int(*args, **kw): + conf = kw.pop('conf', CONF) + group = _ensure_group(kw, conf) + return conf.register_opt(cfg.IntOpt(*args, **kw), group=group) -def register_multi_string(*args, **kw): - group = _ensure_group(kw) - return CONF.register_opt(cfg.MultiStrOpt(*args, **kw), group=group) +def register_cli_int(*args, **kw): + conf = kw.pop('conf', CONF) + group = _ensure_group(kw, conf) + return conf.register_cli_opt(cfg.IntOpt(*args, **kw), group=group) -def _ensure_group(kw): - group = kw.pop("group", None) +def _ensure_group(kw, conf): + group = kw.pop('group', None) if group: - CONF.register_group(cfg.OptGroup(name=group)) + conf.register_group(cfg.OptGroup(name=group)) return group -CONF = Config(project="keystone") +CONF = CommonConfig(project='keystone') -register_str("default_store") -register_str("service_header_mappings") -register_list("extensions") -register_str("service_host") -register_str("service_port") -register_bool("service_ssl") -register_str("admin_host") -register_str("admin_port") -register_bool("admin_ssl") -register_str("bind_host") -register_str("bind_port") -register_str("certfile") -register_str("keyfile") -register_str("ca_certs") -register_bool("cert_required") -register_str("keystone_admin_role") -register_str("keystone_service_admin_role") -register_bool("hash_password") -register_str("backends") -register_str("global_service_id") -register_bool("disable_tokens_in_url") -register_str("sql_connection", group="keystone.backends.sqlalchemy") -register_str("backend_entities", group="keystone.backends.sqlalchemy") -register_str("sql_idle_timeout", group="keystone.backends.sqlalchemy") -# May need to initialize other backends, too. -register_str("ldap_url", group="keystone.backends.ldap") -register_str("ldap_user", group="keystone.backends.ldap") -register_str("ldap_password", group="keystone.backends.ldap") -register_list("backend_entities", group="kkeystone.backends.ldap") +register_str('admin_token', default='ADMIN') +register_str('compute_port') +register_str('admin_port') +register_str('public_port') + + +# sql options +register_str('connection', group='sql') +register_str('idle_timeout', group='sql') +register_str('min_pool_size', group='sql') +register_str('maz_pool_size', group='sql') +register_str('pool_timeout', group='sql') + + +register_str('driver', group='catalog') +register_str('driver', group='identity') +register_str('driver', group='policy') +register_str('driver', group='token') +register_str('driver', group='ec2') diff --git a/keystone/content/admin/HP-IDM-admin-devguide.pdf b/keystone/content/admin/HP-IDM-admin-devguide.pdf deleted file mode 100644 index 7ab314ace2..0000000000 Binary files a/keystone/content/admin/HP-IDM-admin-devguide.pdf and /dev/null differ diff --git a/keystone/content/admin/HP-IDM-admin.wadl b/keystone/content/admin/HP-IDM-admin.wadl deleted file mode 100644 index 4fcb25d495..0000000000 --- a/keystone/content/admin/HP-IDM-admin.wadl +++ /dev/null @@ -1,150 +0,0 @@ - - - - - - - - - - %common; -]> - - - - - - - - - - - - - - - - - - - You need a valid admin token for access. - - - - - - - - - - - - - - - - - - - -

- Check that a token is valid and that it belongs to a supplied tenant - and services and return the permissions relevant to a particular client. -

-

- Valid tokens will exist in the - /tokens/{tokenId} path and invalid - tokens will not. In other words, a user should expect an - itemNotFound (404) fault for an - invalid token. -

-

- If 'HP-IDM-serviceId' is provided, it must be a comma-separated string of - service IDs. If any of the service IDs is invalid or if there are no - roles associated with the service IDs, a user should expect a 401. -

-
- - - -

- Validates a token has the supplied tenant in scope. -

-
- - - -

- If provided, filter the roles to be returned by the given service IDs. -

-
- -
- - - - - - - - - - - - - &commonFaults; - &getFaults; -
- - -

- Check that a token is valid and that it belongs to a particular tenant and services - (For performance). -

-

- Valid tokens will exist in the - /tokens/{tokenId} path and invalid - tokens will not. In other words, a user should expect an - itemNotFound (404) fault for an - invalid token. -

-

- If `belongsTo` is provided, validates that a token has a specific tenant in scope. -

-

- If 'HP-IDM-serviceId' is provided, it must be a comma-separated string of - service IDs. If any of the service ID is invalid or if there are no - roles associated with the service IDs, a user should expect a 401. -

-

- No response body is returned for this method. -

-
- - - -

- Validates a token has the supplied tenant in scope. (for performance). -

-
- - - -

- Check the roles against the given service IDs. -

-
- -
- - &commonFaults; - &getFaults; -
-
diff --git a/keystone/content/admin/OS-KSADM-admin-devguide.pdf b/keystone/content/admin/OS-KSADM-admin-devguide.pdf deleted file mode 100644 index 7a433dc73e..0000000000 Binary files a/keystone/content/admin/OS-KSADM-admin-devguide.pdf and /dev/null differ diff --git a/keystone/content/admin/OS-KSADM-admin.wadl b/keystone/content/admin/OS-KSADM-admin.wadl deleted file mode 100644 index 3f03442715..0000000000 --- a/keystone/content/admin/OS-KSADM-admin.wadl +++ /dev/null @@ -1,793 +0,0 @@ - - - - - - - - - - -%common; -]> - - - - - - - - - - - - - - - - - You need a valid admin token for access. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- Creates a tenant. -

-

This call creates a tenant.

-
- - - - - - - - - - - - - - - - - - - - - - - - - &commonFaults; - &postPutFaults; -
- - - -

- Updates a tenant. -

-

This call updates a tenant.

-
- - - - - - - - - - - - - - - - - - - - - - - - - &commonFaults; - &getFaults; - &postPutFaults; -
- - - -

- Deletes a tenant. -

-

This call deletes a tenant.

-
- - &commonFaults; - &getFaults; -
- - - -

Lists all the users for a tenant.

-

Lists all the users for a tenant.

- -
- - - - - - - - - - - - - - - - - &commonFaults; - &getFaults; -
- - - -

Adds a specific role to a user for a tenant.

-
- - &commonFaults; - &postPutFaults; - &getFaults; -
- - - -

Deletes a specific role from a user for a tenant.

-
- - &commonFaults; - &getFaults; -
- - - - -

List users.

- -
- - - - - - - - - - - - - &commonFaults; - &getFaults; -
- - - - -

Adds a user.

-
- - - - - - - - - - - - - - - - - - - - - - - - - &commonFaults; - &getFaults; - &postPutFaults; -
- - - -

Update a user.

-
- - - - - - - - - - - - - - - - - - - - - - - - - &commonFaults; - &postPutFaults; - &getFaults; -
- - - -

Delete a user.

-
- - &commonFaults; - &getFaults; -
- - - -

Enable user.

- - -
- - - - - - - - - - - - - - - - - - - - - - - - - &commonFaults; - &postPutFaults; - &getFaults; -
- - - - -

Adds a specific global role to a user.

-
- - &commonFaults; - &postPutFaults; - &getFaults; -
- - - -

Deletes a specific global role from a user.

-
- - &commonFaults; - &getFaults; -
- - - - - -

Adds a credential to a user.

-
- - - - - - - - - - - - - - - - - - - - - - - - - &commonFaults; - &postPutFaults; - &getFaults; -
- - - -

List credentials.

- -
- - - - - - - - - - - - - - - - - &commonFaults; - &getFaults; -
- - - -

List credentials by type.

- -
- - - - - - - - - - - - - - - - - &commonFaults; - &getFaults; -
- - - -

Update credentials.

- -
- - - - - - - - - - - - - - - - - - - - - - - - - &commonFaults; - &postPutFaults; - &getFaults; -
- - - -

Delete User credentials.

-
- - &commonFaults; - &postPutFaults; - &getFaults; -
- - - -

Get user credentials.

- -
- - - - - - - - - - - - - &commonFaults; - &getFaults; -
- - - - -

List roles.

- -
- - - - - - - - - - - - - - - - - - &commonFaults; - &getFaults; -
- - - -

Add a Role.

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - &commonFaults; - &postPutFaults; - &getFaults; -
- - - -

Get a role by Name.

-
- - - - - - - - - - - - - - - - - - &commonFaults; - &postPutFaults; - &getFaults; -
- - - - -

Get a role.

-
- - - - - - - - - - - - - - - &commonFaults; - &postPutFaults; - &getFaults; -
- - - -

Delete a role.

-
- - &commonFaults; - &getFaults; -
- - - - -

List services.

-
- - - - - - - - - - - - - - - - - &commonFaults; - &getFaults; -
- - -

Get a service by name.

-
- - - - - - - - - - - - - - - - &commonFaults; - &getFaults; -
- - -

Get a service.

-
- - - - - - - - - - - - - &commonFaults; - &getFaults; -
- - -

Add a service.

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - &commonFaults; - &postPutFaults; - &getFaults; -
- - -

Delete a service.

-
- - &commonFaults; - &getFaults; -
-
diff --git a/keystone/content/admin/OS-KSCATALOG-admin-devguide.pdf b/keystone/content/admin/OS-KSCATALOG-admin-devguide.pdf deleted file mode 100644 index fef083bb63..0000000000 Binary files a/keystone/content/admin/OS-KSCATALOG-admin-devguide.pdf and /dev/null differ diff --git a/keystone/content/admin/OS-KSCATALOG-admin.wadl b/keystone/content/admin/OS-KSCATALOG-admin.wadl deleted file mode 100644 index 2ed3558cc8..0000000000 --- a/keystone/content/admin/OS-KSCATALOG-admin.wadl +++ /dev/null @@ -1,295 +0,0 @@ - - - - - - - - - - -%common; -]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

List Endpoint Templates.

- -
- - - - - - - - - - - - - - - - &commonFaults; - &getFaults; -
- - - -

Get Endpoint Template.

- -
- - - - - - - - - - - - - &commonFaults; - &getFaults; -
- - - -

Add Endpoint Template.

- - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - &commonFaults; - &getFaults; - &postPutFaults; -
- - - -

Update Endpoint Template.

-
- - - - - - - - - - - - - - - - - - - - - - - - - &commonFaults; - &getFaults; - &postPutFaults; -
- - - -

Delete a Endpoint Template.

-
- - &commonFaults; - &getFaults; -
- - - -

Add Endpoint to a tenant.

- - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - &commonFaults; - &getFaults; - &postPutFaults; -
- - - -

List Endpoints of a Tenant.

- -
- - - - - - - - - - - - - &commonFaults; - &getFaults; -
- - - -

Get Endpoint of a Tenant.

- -
- - - - - - - - - - - - - &commonFaults; - &getFaults; -
- - - -

Delete a Endpoint from a Tenant.

-
- - &commonFaults; - &getFaults; -
-
diff --git a/keystone/content/admin/OS-KSEC2-admin-devguide.pdf b/keystone/content/admin/OS-KSEC2-admin-devguide.pdf deleted file mode 100644 index 084a26c4cd..0000000000 Binary files a/keystone/content/admin/OS-KSEC2-admin-devguide.pdf and /dev/null differ diff --git a/keystone/content/admin/OS-KSEC2-admin.wadl b/keystone/content/admin/OS-KSEC2-admin.wadl deleted file mode 100644 index 8c2f62bdad..0000000000 --- a/keystone/content/admin/OS-KSEC2-admin.wadl +++ /dev/null @@ -1,212 +0,0 @@ - - - - - - - - - - -%common; -]> - - - - - - - - - - - - - - - - - You need a valid admin token for access. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Adds a credential to a user.

-
- - - - - - - - - - - - - - - - - - - - - - - - - &commonFaults; - &postPutFaults; - &getFaults; -
- - - -

List credentials.

-
- - - - - - - - - - - - - - - - - &commonFaults; - &getFaults; -
- - - -

List credentials by type.

-
- - - - - - - - - - - - - - - - - &commonFaults; - &getFaults; -
- - - -

Update credentials.

-
- - - - - - - - - - - - - - - - - - - - - - - - - &commonFaults; - &postPutFaults; - &getFaults; -
- - - -

Delete User credentials.

-
- - &commonFaults; - &postPutFaults; - &getFaults; -
- - - -

Get user credentials.

-
- - - - - - - - - - - - - &commonFaults; - &getFaults; -
-
diff --git a/keystone/content/admin/OS-KSS3-admin.wadl b/keystone/content/admin/OS-KSS3-admin.wadl deleted file mode 100644 index fcffb6ebc3..0000000000 --- a/keystone/content/admin/OS-KSS3-admin.wadl +++ /dev/null @@ -1,212 +0,0 @@ - - - - - - - - - - -%common; -]> - - - - - - - - - - - - - - - - - You need a valid admin token for access. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Adds a credential to a user.

-
- - - - - - - - - - - - - - - - - - - - - - - - - &commonFaults; - &postPutFaults; - &getFaults; -
- - - -

List credentials.

-
- - - - - - - - - - - - - - - - - &commonFaults; - &getFaults; -
- - - -

List credentials by type.

-
- - - - - - - - - - - - - - - - - &commonFaults; - &getFaults; -
- - - -

Update credentials.

-
- - - - - - - - - - - - - - - - - - - - - - - - - &commonFaults; - &postPutFaults; - &getFaults; -
- - - -

Delete User credentials.

-
- - &commonFaults; - &postPutFaults; - &getFaults; -
- - - -

Get user credentials.

-
- - - - - - - - - - - - - &commonFaults; - &getFaults; -
-
diff --git a/keystone/content/admin/OS-KSVALIDATE-admin.wadl b/keystone/content/admin/OS-KSVALIDATE-admin.wadl deleted file mode 100644 index c477131d68..0000000000 --- a/keystone/content/admin/OS-KSVALIDATE-admin.wadl +++ /dev/null @@ -1,192 +0,0 @@ - - - - - - - - - - %common; -]> - - - - - - - - - - - - - - - - - - - - You need a valid admin token for access. - - - You need to supply a token to validate. - - - - - - - - - - You need a valid admin token for access. - - - You need to supply a token to validate. - - - - - - - - - - - - - - - - - -

- Check that a token is valid and that it belongs to a supplied tenant - and services and return the permissions relevant to a particular client. -

-

- Behaviour is similar to /tokens/{tokenId}. In - other words, a user should expect an - itemNotFound (404) fault for an - invalid token. -

-

- 'X-Subject-Token' is encrypted, but can still be used for - caching. This extension will basically decrypt this header and - internally call Keystone's normal validation, passing along all - headers and query parameters. It should therefore support - all exsting calls on /tokens/{tokenId}, including - extensions such as HP-IDM. -

-
- - - -

- Validates a token has the supplied tenant in scope. -

-
- - - -

- If provided, filter the roles to be returned by the given service IDs. -

-
- -
- - - - - - - - - - - - - &commonFaults; - &getFaults; -
- - -

- Check that a token is valid and that it belongs to a particular - tenant and services (For performance). -

-

- Behaviour is similar to /tokens/{tokenId}. In - other words, a user should expect an - itemNotFound (404) fault for an - invalid token. -

-

- 'X-Subject-Token' is encrypted, but can still be used for - caching. This extension will basically decrypt this header and - internally call Keystone's normal validation, passing along all - headers and query parameters. It should therefore support - all exsting calls on /tokens/{tokenId}, including - extensions such as HP-IDM. -

-

- No response body is returned for this method. -

-
- - - -

- Validates a token has the supplied tenant in scope. (for performance). -

-
- - - -

- Check the roles against the given service IDs. -

-
- -
- - &commonFaults; - &getFaults; -
- - -

- Returns a list of endpoints associated with a specific token. -

-
- - - - - - - - - - - - - &commonFaults; - &getFaults; -
- -
diff --git a/keystone/content/admin/RAX-KSGRP-admin.wadl b/keystone/content/admin/RAX-KSGRP-admin.wadl deleted file mode 100644 index 0154802956..0000000000 --- a/keystone/content/admin/RAX-KSGRP-admin.wadl +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - - - - - -%common; -]> - - - - - - - - - - - - - - - - You need a valid admin token for access. - - - - - - - - - - - - - - -

List all the groups for a user.

- -
- - - - - - - - - - - - - &commonFaults; - &getFaults; -
-
- diff --git a/keystone/content/admin/RAX-KSKEY-admin-devguide.pdf b/keystone/content/admin/RAX-KSKEY-admin-devguide.pdf deleted file mode 100644 index 224cd99996..0000000000 Binary files a/keystone/content/admin/RAX-KSKEY-admin-devguide.pdf and /dev/null differ diff --git a/keystone/content/admin/RAX-KSKEY-admin.wadl b/keystone/content/admin/RAX-KSKEY-admin.wadl deleted file mode 100644 index 3bebb1b333..0000000000 --- a/keystone/content/admin/RAX-KSKEY-admin.wadl +++ /dev/null @@ -1,212 +0,0 @@ - - - - - - - - - - -%common; -]> - - - - - - - - - - - - - - - - - You need a valid admin token for access. - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Adds a credential to a user.

-
- - - - - - - - - - - - - - - - - - - - - - - - - &commonFaults; - &postPutFaults; - &getFaults; -
- - - -

List credentials.

-
- - - - - - - - - - - - - - - - - &commonFaults; - &getFaults; -
- - - -

List credentials by type.

- -
- - - - - - - - - - - - - - - - - &commonFaults; - &getFaults; -
- - - -

Update credentials.

-
- - - - - - - - - - - - - - - - - - - - - - - - - &commonFaults; - &postPutFaults; - &getFaults; -
- - - -

Delete User credentials.

-
- - &commonFaults; - &postPutFaults; - &getFaults; -
- - - -

Get user credentials.

-
- - - - - - - - - - - - - &commonFaults; - &getFaults; -
-
- diff --git a/keystone/content/admin/RAX-KSQA-admin.wadl b/keystone/content/admin/RAX-KSQA-admin.wadl deleted file mode 100644 index 23d201fd66..0000000000 --- a/keystone/content/admin/RAX-KSQA-admin.wadl +++ /dev/null @@ -1,106 +0,0 @@ - - - - - - - - - - -%common; -]> - - - - - - - - - - - - - - - - You need a valid admin token for access. - - - - - - - - - - - - - - - - - -

Gets a User secret Question and Answer.

-
- - - - - - - - - - - - - &commonFaults; - &getFaults; -
- - -

Updates a User secret Question and Answer.

-
- - - - - - - - - - - - - - - - - - - - - - - - - &commonFaults; - &postPutFaults; -
-
diff --git a/keystone/content/admin/extensions.json b/keystone/content/admin/extensions.json deleted file mode 100644 index 7a514fd749..0000000000 --- a/keystone/content/admin/extensions.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extensions": { - "values": [] - } -} diff --git a/keystone/content/admin/extensions.xml b/keystone/content/admin/extensions.xml deleted file mode 100644 index ed5ee9c6e2..0000000000 --- a/keystone/content/admin/extensions.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - diff --git a/keystone/content/admin/identity-admin.wadl b/keystone/content/admin/identity-admin.wadl deleted file mode 100644 index a6ded2ac1e..0000000000 --- a/keystone/content/admin/identity-admin.wadl +++ /dev/null @@ -1,508 +0,0 @@ - - - - - - - - - - %common; -]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - You need a valid admin token for access. - - - - - - - - - - - - - - You need a valid admin token for access. - - - - - - - - - - - - - - - -

- You need a valid admin token for access. -

-
- - - - - - - - - - - - - - - - - -
-
-
- - - - - - - - - - - -

- A list of supported extensions. -

-
- -
- - - - - - - - - -

- Returns detailed information about this specific version of the API. -

-
- - - - - - - - - &commonFaults; - &getFaults; -
- - - - - -

- Lists supported extensions. -

-
- - - - - - - - - - - - &commonFaults; -
- - -

- Gets details about a specific extension. -

-
- - - - - &commonFaults; - &getFaults; -
- - - - - -

- Authenticate to generate a token. -

-

- This call will return a token if successful. Each ReST request against other services (or other - calls on Keystone such as the GET /tenants call) - requires the inclusion of a specific authorization token HTTP x-header, defined as X-Auth-Token. - Clients obtain - this token, along with the URL to other service APIs, by first authenticating against the - Keystone Service and supplying valid credentials. -

-

- Client authentication is provided via a ReST interface using the POST method, - with v2.0/tokens supplied as the path. A payload of credentials must be included - in the body. -

-

- The Keystone Service is a ReSTful web service. It is the entry point to all service APIs. - To access the Keystone Service, you must know URL of the Keystone service. -

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - &commonFaults; - &getFaults; -
- - -

- Check that a token is valid and that it belongs to a supplied tenant - and return the permissions relevant to a particular client. -

-

- Valid tokens will exist in the - /tokens/{tokenId} path and invalid - tokens will not. In other words, a user should expect an - itemNotFound (404) fault for an - invalid token. -

-
- - - -

- Validates a token has the supplied tenant in scope. -

-
- -
- - - - - - - - - - - - - &commonFaults; - &getFaults; -
- - -

- Check that a token is valid and that it belongs to a particular tenant - (For performance). -

-
- - - -

- Validates a token has the supplied tenant in scope. (for performance). -

-

- Valid tokens will exist in the - /tokens/{tokenId} path and invalid - tokens will not. In other words, a user should expect an - itemNotFound (404) fault for an - invalid token. -

-

- If `belongsTo` is provided, validates that a token has a specific tenant in scope. -

-

- No response body is returned for this method. -

-
- -
- - &commonFaults; - &getFaults; -
- - - - -

- Returns detailed information about a specific user, by user name. -

-
- - - - - - - - - - - - - - - - &commonFaults; - &getFaults; -
- - -

- Returns detailed information about a specific user, by user id. -

-
- - - - - - - - - - - - - &commonFaults; - &getFaults; -
- - -

- Returns global roles for a specific user (excludes tenant roles). -

-

Returns a list of global roles associated with a specific - user (excludes tenant roles).

-
- - - - - - - - - - - - - &commonFaults; - &getFaults; -
- - - - - -

- Get a list of tenants. -

-

- The operation returns a list of tenants which the supplied token provides - access to. This call must be authenticated, so a valid token must - be passed in as a header. -

- - - - - -
- - - - - - - - - - - - - - - - - &commonFaults; - &getFaults; -
- - -

- Returns detailed information about a tenant, by name. -

-
- - - - - - - - - - - - - - - - &commonFaults; - &getFaults; -
- - -

- Returns detailed information about a tenant, by id. -

-
- - - - - - - - - - - - - &commonFaults; - &getFaults; -
- - -

- Returns a list of endpoints associated with a specific token. -

-
- - - - - - - - - - - - - &commonFaults; - &getFaults; -
- - - -

- Returns roles for a specific user on a specific tenant (excludes global roles). -

-
- - - - - - - - - - - - - &commonFaults; - &getFaults; -
-
diff --git a/keystone/content/admin/identityadminguide.pdf b/keystone/content/admin/identityadminguide.pdf deleted file mode 100644 index 5229e5d43c..0000000000 Binary files a/keystone/content/admin/identityadminguide.pdf and /dev/null differ diff --git a/keystone/content/admin/version.atom.tpl b/keystone/content/admin/version.atom.tpl deleted file mode 100644 index e39250d508..0000000000 --- a/keystone/content/admin/version.atom.tpl +++ /dev/null @@ -1,29 +0,0 @@ - - - Available API Versions - 2010-12-22T00:00:00.00Z - http://identity.api.openstack.org/ - OpenStackhttp://www.openstack.org/ - - - http://identity.api.openstack.org/v2.0/ - Version v2.0 - 2011-09-30T00:00:00.00Z - - Version v2.0 BETA (2011-09-30T00:00:00.00Z) - - - http://identity.api.openstack.org/v1.1/ - Version v1.1 - 2011-01-21T11:33:21-06:00 - - Version v1.1 CURRENT (2011-01-21T11:33:21-06:00) - - - http://identity.api.openstack.org/v1.0/ - Version v1.0 - 2009-10-09T11:30:00Z - - Version v1.0 DEPRECATED (2009-10-09T11:30:00Z) - - diff --git a/keystone/content/admin/version.json.tpl b/keystone/content/admin/version.json.tpl deleted file mode 100644 index 112ba1b8ff..0000000000 --- a/keystone/content/admin/version.json.tpl +++ /dev/null @@ -1,32 +0,0 @@ -{ - "versions": { - "values": [{ - "id": "v{{API_VERSION}}", - "status": "{{API_VERSION_STATUS}}", - "updated": "{{API_VERSION_DATE}}", - "links": [{ - "rel": "self", - "href": "http://{{HOST}}:{{PORT}}/v{{API_VERSION}}/" - }, { - "rel": "describedby", - "type": "text/html", - "href": "http://docs.openstack.org/api/openstack-identity-service/{{API_VERSION}}/content/" - }, { - "rel": "describedby", - "type": "application/pdf", - "href": "http://docs.openstack.org/api/openstack-identity-service/{{API_VERSION}}/identity-dev-guide-{{API_VERSION}}.pdf" - }, { - "rel": "describedby", - "type": "application/vnd.sun.wadl+xml", - "href": "http://{{HOST}}:{{PORT}}/v2.0/identity-admin.wadl" - }], - "media-types": [{ - "base": "application/xml", - "type": "application/vnd.openstack.identity-v{{API_VERSION}}+xml" - }, { - "base": "application/json", - "type": "application/vnd.openstack.identity-v{{API_VERSION}}+json" - }] - }] - } -} \ No newline at end of file diff --git a/keystone/content/admin/version.xml.tpl b/keystone/content/admin/version.xml.tpl deleted file mode 100644 index b62c9b6ae7..0000000000 --- a/keystone/content/admin/version.xml.tpl +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/keystone/content/common/common.ent b/keystone/content/common/common.ent deleted file mode 100644 index b492c5d2b7..0000000000 --- a/keystone/content/common/common.ent +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - '> - - - - - - '> - - - - - - '> diff --git a/keystone/content/common/js/shjs/sh_java.js b/keystone/content/common/js/shjs/sh_java.js deleted file mode 100644 index 731fc9f340..0000000000 --- a/keystone/content/common/js/shjs/sh_java.js +++ /dev/null @@ -1,337 +0,0 @@ -if (! this.sh_languages) { - this.sh_languages = {}; -} -sh_languages['java'] = [ - [ - [ - /\b(?:import|package)\b/g, - 'sh_preproc', - -1 - ], - [ - /\/\/\//g, - 'sh_comment', - 1 - ], - [ - /\/\//g, - 'sh_comment', - 7 - ], - [ - /\/\*\*/g, - 'sh_comment', - 8 - ], - [ - /\/\*/g, - 'sh_comment', - 9 - ], - [ - /\b[+-]?(?:(?:0x[A-Fa-f0-9]+)|(?:(?:[\d]*\.)?[\d]+(?:[eE][+-]?[\d]+)?))u?(?:(?:int(?:8|16|32|64))|L)?\b/g, - 'sh_number', - -1 - ], - [ - /"/g, - 'sh_string', - 10 - ], - [ - /'/g, - 'sh_string', - 11 - ], - [ - /(\b(?:class|interface))([ \t]+)([$A-Za-z0-9_]+)/g, - ['sh_keyword', 'sh_normal', 'sh_classname'], - -1 - ], - [ - /\b(?:abstract|assert|break|case|catch|class|const|continue|default|do|else|extends|false|final|finally|for|goto|if|implements|instanceof|interface|native|new|null|private|protected|public|return|static|strictfp|super|switch|synchronized|throw|throws|true|this|transient|try|volatile|while)\b/g, - 'sh_keyword', - -1 - ], - [ - /\b(?:int|byte|boolean|char|long|float|double|short|void)\b/g, - 'sh_type', - -1 - ], - [ - /~|!|%|\^|\*|\(|\)|-|\+|=|\[|\]|\\|:|;|,|\.|\/|\?|&|<|>|\|/g, - 'sh_symbol', - -1 - ], - [ - /\{|\}/g, - 'sh_cbracket', - -1 - ], - [ - /(?:[A-Za-z]|_)[A-Za-z0-9_]*(?=[ \t]*\()/g, - 'sh_function', - -1 - ], - [ - /([A-Za-z](?:[^`~!@#$%&*()_=+{}|;:",<.>\/?'\\[\]\^\-\s]|[_])*)((?:<.*>)?)(\s+(?=[*&]*[A-Za-z][^`~!@#$%&*()_=+{}|;:",<.>\/?'\\[\]\^\-\s]*\s*[`~!@#$%&*()_=+{}|;:",<.>\/?'\\[\]\^\-\[\]]+))/g, - ['sh_usertype', 'sh_usertype', 'sh_normal'], - -1 - ] - ], - [ - [ - /$/g, - null, - -2 - ], - [ - /(?:?)|(?:?)/g, - 'sh_url', - -1 - ], - [ - /<\?xml/g, - 'sh_preproc', - 2, - 1 - ], - [ - //g, - 'sh_keyword', - -1 - ], - [ - /<(?:\/)?[A-Za-z](?:[A-Za-z0-9_:.-]*)/g, - 'sh_keyword', - 6, - 1 - ], - [ - /&(?:[A-Za-z0-9]+);/g, - 'sh_preproc', - -1 - ], - [ - /<(?:\/)?[A-Za-z][A-Za-z0-9]*(?:\/)?>/g, - 'sh_keyword', - -1 - ], - [ - /<(?:\/)?[A-Za-z][A-Za-z0-9]*/g, - 'sh_keyword', - 6, - 1 - ], - [ - /@[A-Za-z]+/g, - 'sh_type', - -1 - ], - [ - /(?:TODO|FIXME|BUG)(?:[:]?)/g, - 'sh_todo', - -1 - ] - ], - [ - [ - /\?>/g, - 'sh_preproc', - -2 - ], - [ - /([^=" \t>]+)([ \t]*)(=?)/g, - ['sh_type', 'sh_normal', 'sh_symbol'], - -1 - ], - [ - /"/g, - 'sh_string', - 3 - ] - ], - [ - [ - /\\(?:\\|")/g, - null, - -1 - ], - [ - /"/g, - 'sh_string', - -2 - ] - ], - [ - [ - />/g, - 'sh_preproc', - -2 - ], - [ - /([^=" \t>]+)([ \t]*)(=?)/g, - ['sh_type', 'sh_normal', 'sh_symbol'], - -1 - ], - [ - /"/g, - 'sh_string', - 3 - ] - ], - [ - [ - /-->/g, - 'sh_comment', - -2 - ], - [ - //g, - 'sh_comment', - -2 - ], - [ - //g, - 'sh_comment', - -2 - ], - [ - / array of links - // - // possible types include: import, include, element, - // attribute, complextype, simpleType - // - // each link contains the following properties: - // name : the name of the link - // href : the link itself - // title : a description of the link - links : new Object(), - - // - // A single link that points to the schema index document. - // - index : null, - - // - // Our initialization function - // - _init : function() { - // - // Load the menu... - // - var controllerDiv = document.getElementById("Controller"); - var mainMenu = this._menuMarkup("mainmenu"); - - for (var linkType in this.links) - { - var subItem = this._menuItemMarkup(mainMenu, linkType, "#", null); - var subMenu = this._menuMarkup (linkType+"_subMenu"); - - var items = this.links[linkType]; - for (var i=0;i Array of sample ids. - // - samples : new Object(), - - // - // An array of code data.. - // - // Code data is defined as an object with the following - // properties: - // - // type: The mimetype of the code...href: The location of the code - // or null if it's inline - // - // id: The id of the pre that contains the code. - // - // The initial object is the source code for the current document. - // - codes : new Array({ - id : "SrcContentCode", - type : "application/xml", - href : (function() { - var ret = location.href; - if (location.hash && (location.hash.length != 0)) - { - ret = ret.replace (location.hash, ""); - } - return ret; - })() - }), - - // - // Sets up the manager, begins the loading process... - // - _init : function() { - // - // Setup an array to hold data items to load, this is used by - // the loadSample method. - // - this._toLoad = new Array(); - - for (var i=0;i -1) && - (ieVersion < 8)) - { - code = trc.util.text.unix2dos (code); - } - - var pre = document.getElementById(codeData.id); - var preNodes = pre.childNodes; - // - // Remove placeholder data... - // - while (preNodes.length != 0) - { - pre.removeChild (preNodes[0]); - } - - // - // Set the correct class type... - // - switch (codeData.type) - { - /* - Javascript mimetypes - */ - case 'application/json': - case 'application/javascript': - case 'application/x-javascript': - case 'application/ecmascript': - case 'text/ecmascript': - case 'text/javascript': - trc.util.dom.setClassName (pre, "sh_javascript"); - break; - /* - Not real mimetypes but this is what we'll use for Java. - */ - case 'application/java': - case 'text/java': - trc.util.dom.setClassName (pre, "sh_java"); - break; - default: - trc.util.dom.setClassName (pre, "sh_xml"); - break; - } - - // - // Add new code... - // - pre.appendChild (document.createTextNode (code)); - }, - - // - // Retrives source code text - // - _getCodeText : function (codeData /* Info for the code to get*/) - { - var pre = document.getElementById(codeData.id); - pre.normalize(); - // - // Should be a single text node after pre... - // - return pre.firstChild.nodeValue; - }, - - - // - // Normalizes text by ensuring that top, bottom, right indent - // levels are equal for all samples. - // - _normalizeCodeText : function (top, /* integer, top indent in lines */ - bottom, /* integer, bottom indent in lines */ - right /* integer, right indent in spaces */ - ) - { - for (var i=0;i -1) && - (ieVersion < 7)) - { - element.className = name; - } - else - { - element.setAttribute ("class",name); - } - } -}; - -trc.util.text = { - // - // Useful RegExps - // - blank : new RegExp ("^\\s*$"), /* A blank string */ - indent : new RegExp ("^\\s+"), /* Line indent */ - lines : new RegExp ("$","m"), /* All lines */ - linechars : new RegExp ("(\n|\r)"), /* EOL line characters */ - tabs : new RegExp ("\t","g"), /* All tabs */ - - // - // We need this because microsoft browsers before IE 7, connot - // display pre-formatted text correctly win unix style line - // endings. - // - unix2dos : function(txt /* String */) { - //if already DOS... - if (txt.search(/\r\n/) != -1) - { - return txt; - } - return txt.replace (/\n/g, "\r\n"); - }, - - // - // Useful to normalize text. - // - dos2unix : function(txt /* String */) { - //if already unix... - if (txt.search(/\r\n/) == -1) - { - return txt; - } - - return txt.replace(/\r/g, ""); - }, - - // - // Create a string with a character repeated x times. - // - repString : function (length, /* integer, size of the string to create */ - ch /* string, The character to set the string to */ - ) - { - var ret = new String(); - for (var i=0;idep. - // - _deps : new Object(), - - // - // An array of callback functions, these should be called when all - // dependecies are loaded. - // - _callbacks : new Array(), - - // - // The init function simply calls the YUI loader... - // - _init : function() { - var yuiUtil = this; - - // - // It takes safari a while to load the YUI Loader if it hasn't - // loaded yet keep trying at 1/4 second intervals - // - if (!window.YAHOO) - { - window.setTimeout (function() { - yuiUtil._init(); - }, 250); - return; - } - - // - // Collect requirements... - // - var required = new Array(); - for (var req in this._deps) - { - required.push (req); - } - - // - // Load YUI dependecies... - // - var loader = new YAHOO.util.YUILoader({ - require: required, - loadOptional: true, - filter: "RAW", - onSuccess: function() { - yuiUtil._depsLoaded(); - }, - timeout: 10000, - combine: true - }); - loader.insert(); - }, - - // - // Called after all dependecies have been loaded - // - _depsLoaded : function() { - // - // Dependecies are loaded let everyone know. - // - for (var i=0;i - \ No newline at end of file diff --git a/keystone/content/common/samples/RAX-KSGRP-groups.json b/keystone/content/common/samples/RAX-KSGRP-groups.json deleted file mode 100644 index 7e5ac29d79..0000000000 --- a/keystone/content/common/samples/RAX-KSGRP-groups.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "RAX-KSGRP:groups": [ - { - "id": "test_global_group_add", - "description": "A description ..." - } - ], - "RAX-KSGRP:groups_links": [] -} diff --git a/keystone/content/common/samples/RAX-KSGRP-groups.xml b/keystone/content/common/samples/RAX-KSGRP-groups.xml deleted file mode 100644 index 3a6a43ba55..0000000000 --- a/keystone/content/common/samples/RAX-KSGRP-groups.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - A Description of the group - - diff --git a/keystone/content/common/samples/RAX-KSQA-secretQA.json b/keystone/content/common/samples/RAX-KSQA-secretQA.json deleted file mode 100644 index 6baf6e32dc..0000000000 --- a/keystone/content/common/samples/RAX-KSQA-secretQA.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "RAX-KSQA:secretQA": { - "question": "What is the color of my eyes?", - "answer": "Leonardo Da Vinci" - } -} diff --git a/keystone/content/common/samples/RAX-KSQA-secretQA.xml b/keystone/content/common/samples/RAX-KSQA-secretQA.xml deleted file mode 100644 index c6570af6e5..0000000000 --- a/keystone/content/common/samples/RAX-KSQA-secretQA.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/keystone/content/common/samples/apiKeyCredentials.json b/keystone/content/common/samples/apiKeyCredentials.json deleted file mode 100644 index 4b59ac3be3..0000000000 --- a/keystone/content/common/samples/apiKeyCredentials.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "RAX-KSKEY:apiKeyCredentials": { - "username": "test_user", - "apiKey": "aaaaa-bbbbb-ccccc-12345678" - } -} diff --git a/keystone/content/common/samples/apiKeyCredentials.xml b/keystone/content/common/samples/apiKeyCredentials.xml deleted file mode 100644 index 7a4b0fdb54..0000000000 --- a/keystone/content/common/samples/apiKeyCredentials.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - diff --git a/keystone/content/common/samples/auth.json b/keystone/content/common/samples/auth.json deleted file mode 100644 index 6730360f3a..0000000000 --- a/keystone/content/common/samples/auth.json +++ /dev/null @@ -1,98 +0,0 @@ -{ - "access": { - "token": { - "id": "ab48a9efdfedb23ty3494", - "expires": "2010-11-01T03:32:15-05:00", - "tenant": { - "id": "t1000", - "name": "My Project" - }, - "tenants": [{ - "id": "t1000", - "name": "My Project" - }] - }, - "user": { - "id": "u123", - "name": "jqsmith", - "roles": [ - { - "id": "100", - "name": "compute:admin" - }, - { - "id": "101", - "name": "object-store:admin", - "tenantId": "t1000" - } - ], - "roles_links": [] - }, - "serviceCatalog": [ - { - "name": "Cloud Servers", - "type": "compute", - "endpoints": [ - { - "id": "1", - "tenantId": "t1000", - "publicURL": "https://compute.north.host.com/v1/t1000", - "internalURL": "https://compute.north.internal/v1/t1000", - "region": "North", - "versionId": "1", - "versionInfo": "https://compute.north.host.com/v1/", - "versionList": "https://compute.north.host.com/" - }, - { - "id": "2", - "tenantId": "t1000", - "publicURL": "https://compute.north.host.com/v1.1/t1000", - "internalURL": "https://compute.north.internal/v1.1/t1000", - "region": "North", - "versionId": "1.1", - "versionInfo": "https://compute.north.host.com/v1.1/", - "versionList": "https://compute.north.host.com/" - } - ], - "endpoints_links": [] - }, - { - "name": "Cloud Files", - "type": "object-store", - "endpoints": [ - { - "tenantId": "t1000", - "publicURL": "https://storage.north.host.com/v1/t1000", - "internalURL": "https://storage.north.internal/v1/t1000", - "region": "North", - "versionId": "1", - "versionInfo": "https://storage.north.host.com/v1/", - "versionList": "https://storage.north.host.com/" - }, - { - "tenantId": "t1000", - "publicURL": "https://storage.south.host.com/v1/t1000", - "internalURL": "https://storage.south.internal/v1/t1000", - "region": "South", - "versionId": "1", - "versionInfo": "https://storage.south.host.com/v1/", - "versionList": "https://storage.south.host.com/" - } - ] - }, - { - "name": "DNS-as-a-Service", - "type": "dnsextension:dns", - "endpoints": [ - { - "tenantId": "t1000", - "publicURL": "https://dns.host.com/v2.0/t1000", - "versionId": "2.0", - "versionInfo": "https://dns.host.com/v2.0/", - "versionList": "https://dns.host.com/" - } - ] - } - ] - } -} diff --git a/keystone/content/common/samples/auth.xml b/keystone/content/common/samples/auth.xml deleted file mode 100644 index 21efa79d4c..0000000000 --- a/keystone/content/common/samples/auth.xml +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/keystone/content/common/samples/auth_credentials-OS-KSEC2.json b/keystone/content/common/samples/auth_credentials-OS-KSEC2.json deleted file mode 100644 index 45c987e661..0000000000 --- a/keystone/content/common/samples/auth_credentials-OS-KSEC2.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "auth": { - "OS-KSEC2:ec2Credentials": { - "username": "test_user", - "secret": "aaaaa", - "signature": "bbb" - }, - "tenantId": "77654" - } -} diff --git a/keystone/content/common/samples/auth_credentials-OS-KSEC2.xml b/keystone/content/common/samples/auth_credentials-OS-KSEC2.xml deleted file mode 100644 index ec4f331407..0000000000 --- a/keystone/content/common/samples/auth_credentials-OS-KSEC2.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - diff --git a/keystone/content/common/samples/auth_credentials-RAX-KSKEY.json b/keystone/content/common/samples/auth_credentials-RAX-KSKEY.json deleted file mode 100644 index 29f78dcd1e..0000000000 --- a/keystone/content/common/samples/auth_credentials-RAX-KSKEY.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "auth": { - "RAX-KSKEY:apiKeyCredentials": { - "username": "test_user", - "apiKey": "aaaaa-bbbbb-ccccc-12345678" - }, - "tenantId": "1234" - } -} diff --git a/keystone/content/common/samples/auth_credentials-RAX-KSKEY.xml b/keystone/content/common/samples/auth_credentials-RAX-KSKEY.xml deleted file mode 100644 index a4284f9527..0000000000 --- a/keystone/content/common/samples/auth_credentials-RAX-KSKEY.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - diff --git a/keystone/content/common/samples/auth_credentials.json b/keystone/content/common/samples/auth_credentials.json deleted file mode 100644 index 00fb7826a0..0000000000 --- a/keystone/content/common/samples/auth_credentials.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "auth": { - "passwordCredentials": { - "username": "test_user", - "password": "mypass" - }, - "tenantName": "customer-x" - } -} diff --git a/keystone/content/common/samples/auth_credentials.xml b/keystone/content/common/samples/auth_credentials.xml deleted file mode 100644 index 6621b83a93..0000000000 --- a/keystone/content/common/samples/auth_credentials.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - diff --git a/keystone/content/common/samples/auth_with_token.json b/keystone/content/common/samples/auth_with_token.json deleted file mode 100644 index 7a765eba23..0000000000 --- a/keystone/content/common/samples/auth_with_token.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "auth": { - "tenantName": "customer-x", - "token": { - "id": "abcdefghijk" - } - } -} diff --git a/keystone/content/common/samples/auth_with_token.xml b/keystone/content/common/samples/auth_with_token.xml deleted file mode 100644 index 5190f62abf..0000000000 --- a/keystone/content/common/samples/auth_with_token.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/keystone/content/common/samples/authwithgroups.json b/keystone/content/common/samples/authwithgroups.json deleted file mode 100644 index d364ebb4c9..0000000000 --- a/keystone/content/common/samples/authwithgroups.json +++ /dev/null @@ -1,94 +0,0 @@ -{ - "access": { - "token": { - "id": "asdasdasd-adsasdads-asdasdasd-adsadsasd", - "expires": "2010-11-01T03:32:15-05:00" - }, - "user": { - "id": "123", - "name": "testName", - "roles": [ - { - "id": "234", - "name": "compute:admin" - }, - { - "id": "235", - "name": "object-store:admin", - "tenantId": "1" - } - ], - "roles_links": [], - "RAX-KSGRP:groups": [ - { - "id": "test_global_group_add", - "description": "A description ..." - } - ], - "RAX-KSGRP:groups_links": [] - }, - "serviceCatalog": [ - { - "name": "Cloud Servers", - "type": "compute", - "endpoints": [ - { - "publicURL": "https://compute.north.host/v1/1234", - "internalURL": "https://compute.north.host/v1/1234", - "region": "North", - "tenantId": "1234", - "versionId": "1.0", - "versionInfo": "https://compute.north.host/v1.0/", - "versionList": "https://compute.north.host/" - }, - { - "publicURL": "https://compute.north.host/v1.1/3456", - "internalURL": "https://compute.north.host/v1.1/3456", - "region": "North", - "tenantId": "3456", - "versionId": "1.1", - "versionInfo": "https://compute.north.host/v1.1/", - "versionList": "https://compute.north.host/" - } - ], - "endpoints_links": [] - }, - { - "name": "Cloud Files", - "type": "object-store", - "endpoints": [ - { - "publicURL": "https://compute.north.host/v1/blah-blah", - "internalURL": "https://compute.north.host/v1/blah-blah", - "region": "South", - "tenantId": "1234", - "versionId": "1.0", - "versionInfo": "uri", - "versionList": "uri" - }, - { - "publicURL": "https://compute.north.host/v1.1/blah-blah", - "internalURL": "https://compute.north.host/v1.1/blah-blah", - "region": "South", - "tenantId": "1234", - "versionId": "1.1", - "versionInfo": "https://compute.north.host/v1.1/", - "versionList": "https://compute.north.host/" - } - ], - "endpoints_links": [ - { - "rel": "next", - "href": "https://identity.north.host/v2.0/endpoints?marker=2" - } - ] - } - ], - "serviceCatalog_links": [ - { - "rel": "next", - "href": "https://identity.host/v2.0/endpoints?session=2hfh8Ar&marker=2" - } - ] - } -} diff --git a/keystone/content/common/samples/authwithgroups.xml b/keystone/content/common/samples/authwithgroups.xml deleted file mode 100644 index f8c6981b4a..0000000000 --- a/keystone/content/common/samples/authwithgroups.xml +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - A Description of the group - - - - diff --git a/keystone/content/common/samples/choices.json b/keystone/content/common/samples/choices.json deleted file mode 100644 index 259b8c4228..0000000000 --- a/keystone/content/common/samples/choices.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "choices": [ - { - "id": "v1.0", - "status": "DEPRECATED", - "links": [ - { - "rel": "self", - "href": "http://identity.api.openstack.org/v1.0" - } - ], - "media-types": { - "values": [ - { - "base": "application/xml", - "type": "application/vnd.openstack.identity+xml;version=1.0" - }, - { - "base": "application/json", - "type": "application/vnd.openstack.identity+json;version=1.0" - } - ] - } - }, - { - "id": "v1.1", - "status": "CURRENT", - "links": [ - { - "rel": "self", - "href": "http://identity.api.openstack.org/v1.1" - } - ], - "media-types": { - "values": [ - { - "base": "application/xml", - "type": "application/vnd.openstack.identity+xml;version=1.1" - }, - { - "base": "application/json", - "type": "application/vnd.openstack.identity+json;version=1.1" - } - ] - } - }, - { - "id": "v2.0", - "status": "BETA", - "links": [ - { - "rel": "self", - "href": "http://identity.api.openstack.org/v2.0" - } - ], - "media-types": { - "values": [ - { - "base": "application/xml", - "type": "application/vnd.openstack.identity+xml;version=2.0" - }, - { - "base": "application/json", - "type": "application/vnd.openstack.identity+json;version=2.0" - } - ] - } - } - ], - "choices_links": "" -} diff --git a/keystone/content/common/samples/choices.xml b/keystone/content/common/samples/choices.xml deleted file mode 100644 index 834a1987a5..0000000000 --- a/keystone/content/common/samples/choices.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/keystone/content/common/samples/credentials.json b/keystone/content/common/samples/credentials.json deleted file mode 100644 index 584fc3a1da..0000000000 --- a/keystone/content/common/samples/credentials.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "credentials": [ - { - "passwordCredentials": { - "username": "test_user", - "password": "mypass" - } - } - ], - "credentials_links": [] -} diff --git a/keystone/content/common/samples/credentials.xml b/keystone/content/common/samples/credentials.xml deleted file mode 100644 index 8efa9f2e43..0000000000 --- a/keystone/content/common/samples/credentials.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - diff --git a/keystone/content/common/samples/credentialswithapikey.json b/keystone/content/common/samples/credentialswithapikey.json deleted file mode 100644 index 8ab8f2cdef..0000000000 --- a/keystone/content/common/samples/credentialswithapikey.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "credentials": [ - { - "passwordCredentials": { - "username": "test_user", - "password": "mypass" - } - }, - { - "RAX-KSKEY:apiKeyCredentials": { - "username": "test_user", - "apiKey": "aaaaa-bbbbb-ccccc-12345678" - } - } - ], - "credentials_links": [] -} diff --git a/keystone/content/common/samples/credentialswithapikey.xml b/keystone/content/common/samples/credentialswithapikey.xml deleted file mode 100644 index 762016e877..0000000000 --- a/keystone/content/common/samples/credentialswithapikey.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - diff --git a/keystone/content/common/samples/credentialswithec2.json b/keystone/content/common/samples/credentialswithec2.json deleted file mode 100644 index 8da6844e5d..0000000000 --- a/keystone/content/common/samples/credentialswithec2.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "credentials": [ - { - "passwordCredentials": { - "username": "test_user", - "password": "mypass" - } - }, - { - "OS-KSEC2:ec2Credentials": { - "username": "test_user", - "secret": "aaaaa", - "signature": "bbb" - } - } - ], - "credentials_links": [] -} diff --git a/keystone/content/common/samples/credentialswithec2.xml b/keystone/content/common/samples/credentialswithec2.xml deleted file mode 100644 index b6ded2a329..0000000000 --- a/keystone/content/common/samples/credentialswithec2.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/keystone/content/common/samples/credentialswiths3.json b/keystone/content/common/samples/credentialswiths3.json deleted file mode 100644 index fb286205cb..0000000000 --- a/keystone/content/common/samples/credentialswiths3.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "credentials":[{ - "passwordCredentials":{ - "username":"test_user", - "password":"mypass" - } - }, - { - "OS-KSS3:s3Credentials":{ - "username":"test_user", - "secret":"aaaaa", - "signature":"bbb" - } - } - ], - "credentials_links":[] -} diff --git a/keystone/content/common/samples/credentialswiths3.xml b/keystone/content/common/samples/credentialswiths3.xml deleted file mode 100644 index 4c482d8a9e..0000000000 --- a/keystone/content/common/samples/credentialswiths3.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/keystone/content/common/samples/ec2Credentials.json b/keystone/content/common/samples/ec2Credentials.json deleted file mode 100644 index e72ee28c77..0000000000 --- a/keystone/content/common/samples/ec2Credentials.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "OS-KSEC2:ec2Credentials": { - "username": "test_user", - "secret": "aaaaa", - "signature": "bbb" - } -} diff --git a/keystone/content/common/samples/ec2Credentials.xml b/keystone/content/common/samples/ec2Credentials.xml deleted file mode 100644 index e36f231c8d..0000000000 --- a/keystone/content/common/samples/ec2Credentials.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - diff --git a/keystone/content/common/samples/endpoint.json b/keystone/content/common/samples/endpoint.json deleted file mode 100644 index aad05b6249..0000000000 --- a/keystone/content/common/samples/endpoint.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "endpoint": { - "id": 1, - "tenantId": 1, - "region": "North", - "type": "compute", - "publicURL": "https://compute.north.public.com/v1", - "internalURL": "https://compute.north.internal.com/v1", - "adminURL": "https://compute.north.internal.com/v1", - "versionId": "1", - "versionInfo": "https://compute.north.public.com/v1/", - "versionList": "https://compute.north.public.com/" - } -} diff --git a/keystone/content/common/samples/endpoint.xml b/keystone/content/common/samples/endpoint.xml deleted file mode 100644 index cd09b24a7f..0000000000 --- a/keystone/content/common/samples/endpoint.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - \ No newline at end of file diff --git a/keystone/content/common/samples/endpointTemplate.json b/keystone/content/common/samples/endpointTemplate.json deleted file mode 100644 index 4cf6c4d12b..0000000000 --- a/keystone/content/common/samples/endpointTemplate.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "OS-KSCATALOG:endpointTemplate": { - "id": 1, - "region": "North", - "global": true, - "name": "nova", - "type": "compute", - "publicURL": "https://compute.north.public.com/v1", - "internalURL": "https://compute.north.internal.com/v1", - "versionId": "1", - "versionInfo": "https://compute.north.public.com/v1/", - "versionList": "https://compute.north.public.com/", - "enabled": true - } -} diff --git a/keystone/content/common/samples/endpointTemplate.xml b/keystone/content/common/samples/endpointTemplate.xml deleted file mode 100644 index 37f98091b0..0000000000 --- a/keystone/content/common/samples/endpointTemplate.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - \ No newline at end of file diff --git a/keystone/content/common/samples/endpointTemplateWithOnlyId.json b/keystone/content/common/samples/endpointTemplateWithOnlyId.json deleted file mode 100644 index e2fa47f75b..0000000000 --- a/keystone/content/common/samples/endpointTemplateWithOnlyId.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "OS-KSCATALOG:endpointTemplate": { - "id": 1 - } -} diff --git a/keystone/content/common/samples/endpointTemplateWithOnlyId.xml b/keystone/content/common/samples/endpointTemplateWithOnlyId.xml deleted file mode 100644 index 6379b3ae32..0000000000 --- a/keystone/content/common/samples/endpointTemplateWithOnlyId.xml +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/keystone/content/common/samples/endpointTemplates.json b/keystone/content/common/samples/endpointTemplates.json deleted file mode 100644 index ab683ae8ae..0000000000 --- a/keystone/content/common/samples/endpointTemplates.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "OS-KSCATALOG:endpointsTemplates": [ - { - "id": 1, - "region": "North", - "global": true, - "type": "compute", - "publicURL": "https://compute.north.public.com/v1", - "internalURL": "https://compute.north.internal.com/v1", - "versionId": "1", - "versionInfo": "https://compute.north.public.com/v1/", - "versionList": "https://compute.north.public.com/", - "enabled": true - }, - { - "id": 2, - "region": "South", - "type": "compute", - "publicURL": "https://compute.south.public.com/v1", - "internalURL": "https://compute.south.internal.com/v1", - "versionId": "1", - "versionInfo": "https://compute.south.public.com/v1/", - "versionList": "https://compute.south.public.com/", - "enabled": false - }, - { - "id": 3, - "region": "North", - "global": true, - "type": "object-store", - "publicURL": "https://object-store.north.public.com/v1.0", - "versionId": "1.0", - "versionInfo": "https://object-store.north.public.com/v1.0/", - "versionList": "https://object-store.north.public.com/", - "enabled": true - }, - { - "id": 4, - "region": "South", - "type": "object-store", - "publicURL": "https://object-store.south.public.com/v2", - "versionId": "2", - "versionInfo": "https://object-store.south.public.com/v2/", - "versionList": "https://object-store.south.public.com/", - "enabled": true - }, - { - "id": 5, - "global": true, - "type": "OS-DNS:DNS", - "publicURL": "https://dns.public.com/v3.2", - "versionId": "1.0", - "versionInfo": "https://dns.public.com/v1.0/", - "versionList": "https://dns.public.com/", - "enabled": true - } - ], - "OS-KSCATALOG:endpointsTemplates_links": [] -} diff --git a/keystone/content/common/samples/endpointTemplates.xml b/keystone/content/common/samples/endpointTemplates.xml deleted file mode 100644 index 965be9be36..0000000000 --- a/keystone/content/common/samples/endpointTemplates.xml +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/keystone/content/common/samples/endpoints.json b/keystone/content/common/samples/endpoints.json deleted file mode 100644 index e10f05e3a7..0000000000 --- a/keystone/content/common/samples/endpoints.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "endpoints": [ - { - "id": 1, - "tenantId": "1", - "region": "North", - "type": "compute", - "publicURL": "https://compute.north.public.com/v1", - "internalURL": "https://compute.north.internal.com/v1", - "adminURL": "https://compute.north.internal.com/v1", - "versionId": "1", - "versionInfo": "https://compute.north.public.com/v1/", - "versionList": "https://compute.north.public.com/" - }, - { - "id": 2, - "tenantId": "1", - "region": "South", - "type": "compute", - "publicURL": "https://compute.north.public.com/v1", - "internalURL": "https://compute.north.internal.com/v1", - "adminURL": "https://compute.north.internal.com/v1", - "versionId": "1", - "versionInfo": "https://compute.north.public.com/v1/", - "versionList": "https://compute.north.public.com/" - }, - { - "id": 3, - "tenantId": "1", - "region": "East", - "type": "compute", - "publicURL": "https://compute.north.public.com/v1", - "internalURL": "https://compute.north.internal.com/v1", - "adminURL": "https://compute.north.internal.com/v1", - "versionId": "1", - "versionInfo": "https://compute.north.public.com/v1/", - "versionList": "https://compute.north.public.com/" - }, - { - "id": 4, - "tenantId": "1", - "region": "West", - "type": "compute", - "publicURL": "https://compute.north.public.com/v1", - "internalURL": "https://compute.north.internal.com/v1", - "adminURL": "https://compute.north.internal.com/v1", - "versionId": "1", - "versionInfo": "https://compute.north.public.com/v1/", - "versionList": "https://compute.north.public.com/" - }, - { - "id": 5, - "tenantId": "1", - "region": "Global", - "type": "compute", - "publicURL": "https://compute.north.public.com/v1", - "internalURL": "https://compute.north.internal.com/v1", - "adminURL": "https://compute.north.internal.com/v1", - "versionId": "1", - "versionInfo": "https://compute.north.public.com/v1/", - "versionList": "https://compute.north.public.com/" - } - ], - "endpoints_links": [] -} diff --git a/keystone/content/common/samples/endpoints.xml b/keystone/content/common/samples/endpoints.xml deleted file mode 100644 index b09819a16b..0000000000 --- a/keystone/content/common/samples/endpoints.xml +++ /dev/null @@ -1,76 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/keystone/content/common/samples/ext-getuser.json b/keystone/content/common/samples/ext-getuser.json deleted file mode 100644 index f4071ccfc6..0000000000 --- a/keystone/content/common/samples/ext-getuser.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "user": { - "roles": [ - { - "tenantId": "1234", - "id": "Admin" - } - ], - "roles_links": [], - "id": "1000", - "username": "jqsmith", - "email": "john.smith@example.org", - "enabled": true, - "RS-META:metadata": { - "values": { - "MetaKey1": "MetaValue1", - "MetaKey2": "MetaValue2" - } - } - } -} diff --git a/keystone/content/common/samples/ext-getuser.xml b/keystone/content/common/samples/ext-getuser.xml deleted file mode 100644 index b155904a55..0000000000 --- a/keystone/content/common/samples/ext-getuser.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - MetaValue1 - MetaValue2 - - diff --git a/keystone/content/common/samples/extension.json b/keystone/content/common/samples/extension.json deleted file mode 100644 index bb66a50c73..0000000000 --- a/keystone/content/common/samples/extension.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "extension": { - "name": "User Metadata Extension", - "namespace": "http://docs.rackspacecloud.com/identity/api/ext/meta/v2.0", - "alias": "RS-META", - "updated": "2011-01-12T11:22:33-06:00", - "description": "Allows associating arbritrary metadata with a user.", - "links": [ - { - "rel": "describedby", - "type": "application/pdf", - "href": "http://docs.rackspacecloud.com/identity/api/ext/identity-meta-20111201.pdf" - }, - { - "rel": "describedby", - "type": "application/vnd.sun.wadl+xml", - "href": "http://docs.rackspacecloud.com/identity/api/ext/identity-cbs.wadl" - } - ] - } -} diff --git a/keystone/content/common/samples/extension.xml b/keystone/content/common/samples/extension.xml deleted file mode 100644 index 056d7e9607..0000000000 --- a/keystone/content/common/samples/extension.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - Allows associating arbritrary metadata with a user. - - - - - - - - diff --git a/keystone/content/common/samples/extensions.json b/keystone/content/common/samples/extensions.json deleted file mode 100644 index ca46f94101..0000000000 --- a/keystone/content/common/samples/extensions.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "extensions": { - "values": [ - { - "name": "Reset Password Extension", - "namespace": "http://docs.rackspacecloud.com/identity/api/ext/rpe/v2.0", - "alias": "RS-RPE", - "updated": "2011-01-22T13:25:27-06:00", - "description": "Adds the capability to reset a user's password. The user is emailed when the password has been reset.", - "links": [ - { - "rel": "describedby", - "type": "application/pdf", - "href": "http://docs.rackspacecloud.com/identity/api/ext/identity-rpe-20111111.pdf" - }, - { - "rel": "describedby", - "type": "application/vnd.sun.wadl+xml", - "href": "http://docs.rackspacecloud.com/identity/api/ext/identity-rpe.wadl" - } - ] - }, - { - "name": "User Metadata Extension", - "namespace": "http://docs.rackspacecloud.com/identity/api/ext/meta/v2.0", - "alias": "RS-META", - "updated": "2011-01-12T11:22:33-06:00", - "description": "Allows associating arbritrary metadata with a user.", - "links": [ - { - "rel": "describedby", - "type": "application/pdf", - "href": "http://docs.rackspacecloud.com/identity/api/ext/identity-meta-20111201.pdf" - }, - { - "rel": "describedby", - "type": "application/vnd.sun.wadl+xml", - "href": "http://docs.rackspacecloud.com/identity/api/ext/identity-meta.wadl" - } - ] - } - ]}, - "extensions_links": [] -} diff --git a/keystone/content/common/samples/extensions.xml b/keystone/content/common/samples/extensions.xml deleted file mode 100644 index c11b06d74a..0000000000 --- a/keystone/content/common/samples/extensions.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - Adds the capability to reset a user's password. The user is - emailed when the password has been reset. - - - - - - - - Allows associating arbritrary metadata with a user. - - - - - - diff --git a/keystone/content/common/samples/getuser-1.json b/keystone/content/common/samples/getuser-1.json deleted file mode 100644 index 741312cc87..0000000000 --- a/keystone/content/common/samples/getuser-1.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "user": { - "roles": [ - { - "tenantId": "1234", - "id": "Admin" - }, - { - "tenantId": "1234", - "id": "DBUser" - } - ], - "roles_links": [ - { - "rel": "next", - "href": "http://identity.api.openstack.org/v2.0/tenants/1234/users/u1000/roles?marker=Super" - } - ], - "id": "u1000", - "username": "jqsmith", - "email": "john.smith@example.org", - "enabled": true - } -} diff --git a/keystone/content/common/samples/getuser-1.xml b/keystone/content/common/samples/getuser-1.xml deleted file mode 100644 index 531a229fa7..0000000000 --- a/keystone/content/common/samples/getuser-1.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - diff --git a/keystone/content/common/samples/identity_fault.json b/keystone/content/common/samples/identity_fault.json deleted file mode 100644 index 9968eec2a5..0000000000 --- a/keystone/content/common/samples/identity_fault.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "identityFault": { - "message": "Fault", - "details": "Error Details...", - "code": 500 - } -} diff --git a/keystone/content/common/samples/identity_fault.xml b/keystone/content/common/samples/identity_fault.xml deleted file mode 100644 index 6787af21c6..0000000000 --- a/keystone/content/common/samples/identity_fault.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - Fault -
Error Details...
-
diff --git a/keystone/content/common/samples/item_not_found.json b/keystone/content/common/samples/item_not_found.json deleted file mode 100644 index 8ba8c207fb..0000000000 --- a/keystone/content/common/samples/item_not_found.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "itemNotFound": { - "message": "Item not found.", - "details": "Error Details...", - "code": 404 - } -} diff --git a/keystone/content/common/samples/item_not_found.xml b/keystone/content/common/samples/item_not_found.xml deleted file mode 100644 index 3f78b498f0..0000000000 --- a/keystone/content/common/samples/item_not_found.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - Item not found. -
Error Details...
-
diff --git a/keystone/content/common/samples/norequestbody.txt b/keystone/content/common/samples/norequestbody.txt deleted file mode 100644 index c6a777d517..0000000000 --- a/keystone/content/common/samples/norequestbody.txt +++ /dev/null @@ -1 +0,0 @@ -This operation does not require a request body. diff --git a/keystone/content/common/samples/noresponsebody.txt b/keystone/content/common/samples/noresponsebody.txt deleted file mode 100644 index 96e583f9b7..0000000000 --- a/keystone/content/common/samples/noresponsebody.txt +++ /dev/null @@ -1 +0,0 @@ -This operation does not return a response body. diff --git a/keystone/content/common/samples/passwordcredentials.json b/keystone/content/common/samples/passwordcredentials.json deleted file mode 100644 index c57309ba31..0000000000 --- a/keystone/content/common/samples/passwordcredentials.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "passwordCredentials": { - "username": "test_user", - "password": "mypass" - } -} diff --git a/keystone/content/common/samples/passwordcredentials.xml b/keystone/content/common/samples/passwordcredentials.xml deleted file mode 100644 index 86e4d9fe37..0000000000 --- a/keystone/content/common/samples/passwordcredentials.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - diff --git a/keystone/content/common/samples/role.json b/keystone/content/common/samples/role.json deleted file mode 100644 index a1b8109d8f..0000000000 --- a/keystone/content/common/samples/role.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "role": { - "id": "123", - "name": "Guest", - "description": "Guest Access" - } -} diff --git a/keystone/content/common/samples/role.xml b/keystone/content/common/samples/role.xml deleted file mode 100644 index dd25cfff8f..0000000000 --- a/keystone/content/common/samples/role.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - diff --git a/keystone/content/common/samples/roles.json b/keystone/content/common/samples/roles.json deleted file mode 100644 index 2504a2b1b5..0000000000 --- a/keystone/content/common/samples/roles.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "roles": [ - { - "id": "123", - "name": "compute:admin", - "description": "Nova Administrator" - } - ], - "roles_links": [] -} diff --git a/keystone/content/common/samples/roles.xml b/keystone/content/common/samples/roles.xml deleted file mode 100644 index 30596f92c2..0000000000 --- a/keystone/content/common/samples/roles.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/keystone/content/common/samples/s3Credentials.json b/keystone/content/common/samples/s3Credentials.json deleted file mode 100644 index f4b59aa519..0000000000 --- a/keystone/content/common/samples/s3Credentials.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "OS-KSS3:s3Credentials":{ - "username":"test_user", - "secret":"aaaaa", - "signature":"bbb" - } -} diff --git a/keystone/content/common/samples/s3Credentials.xml b/keystone/content/common/samples/s3Credentials.xml deleted file mode 100644 index a69413923f..0000000000 --- a/keystone/content/common/samples/s3Credentials.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - diff --git a/keystone/content/common/samples/samplerequestheader.txt b/keystone/content/common/samples/samplerequestheader.txt deleted file mode 100644 index 5641d8744c..0000000000 --- a/keystone/content/common/samples/samplerequestheader.txt +++ /dev/null @@ -1,4 +0,0 @@ -POST /v2.0/tokens HTTP/1.1 -Host: identity.api.openstack.org -Content-Type: application/json -Accept: application/xml \ No newline at end of file diff --git a/keystone/content/common/samples/sampleresponseheader.txt b/keystone/content/common/samples/sampleresponseheader.txt deleted file mode 100644 index aee1205a34..0000000000 --- a/keystone/content/common/samples/sampleresponseheader.txt +++ /dev/null @@ -1,4 +0,0 @@ -HTTP/1.1 200 OKAY -Date: Mon, 12 Nov 2010 15:55:01 GMT -Content-Length: -Content-Type: application/xml; charset=UTF-8 \ No newline at end of file diff --git a/keystone/content/common/samples/service.json b/keystone/content/common/samples/service.json deleted file mode 100644 index f2a783f2f0..0000000000 --- a/keystone/content/common/samples/service.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "OS-KSADM:service": { - "id": "123", - "name": "nova", - "type": "compute", - "description": "OpenStack Compute Service" - } -} diff --git a/keystone/content/common/samples/service.xml b/keystone/content/common/samples/service.xml deleted file mode 100644 index d1c98cecc1..0000000000 --- a/keystone/content/common/samples/service.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - diff --git a/keystone/content/common/samples/services.json b/keystone/content/common/samples/services.json deleted file mode 100644 index dd8dea4f40..0000000000 --- a/keystone/content/common/samples/services.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "OS-KSADM:services": [ - { - "id": "123", - "name": "nova", - "type": "compute", - "description": "OpenStack Compute Service" - }, - { - "id": "234", - "name": "glance", - "type": "image", - "description": "OpenStack Image Service" - } - ], - "OS-KSADM:services_links": [] -} diff --git a/keystone/content/common/samples/services.xml b/keystone/content/common/samples/services.xml deleted file mode 100644 index 12947c3a4e..0000000000 --- a/keystone/content/common/samples/services.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/keystone/content/common/samples/tenant.json b/keystone/content/common/samples/tenant.json deleted file mode 100644 index 794a61ce46..0000000000 --- a/keystone/content/common/samples/tenant.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "tenant": { - "id": "1234", - "name": "ACME corp", - "description": "A description ...", - "enabled": true - } -} diff --git a/keystone/content/common/samples/tenant.xml b/keystone/content/common/samples/tenant.xml deleted file mode 100644 index ce0137bebe..0000000000 --- a/keystone/content/common/samples/tenant.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - A description... - diff --git a/keystone/content/common/samples/tenantlock.json b/keystone/content/common/samples/tenantlock.json deleted file mode 100644 index 611e4adb72..0000000000 --- a/keystone/content/common/samples/tenantlock.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "tenant": { - "description": "A NEW description..." - } -} diff --git a/keystone/content/common/samples/tenantlock.xml b/keystone/content/common/samples/tenantlock.xml deleted file mode 100644 index 06a68a839f..0000000000 --- a/keystone/content/common/samples/tenantlock.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - A NEW description... - diff --git a/keystone/content/common/samples/tenants-1.json b/keystone/content/common/samples/tenants-1.json deleted file mode 100644 index b3f423490a..0000000000 --- a/keystone/content/common/samples/tenants-1.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "tenants": [ - { - "id": "1234", - "name": "ACME corp", - "description": "A description ...", - "enabled": true - } - ], - "tenants_links": [ - { - "rel": "next", - "href": "http://identity.api.openstack.org/v2.0/tenants?limit=1&marker=1234" - } - ] -} diff --git a/keystone/content/common/samples/tenants-1.xml b/keystone/content/common/samples/tenants-1.xml deleted file mode 100644 index e486649ebe..0000000000 --- a/keystone/content/common/samples/tenants-1.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - A description... - - diff --git a/keystone/content/common/samples/tenants-2.json b/keystone/content/common/samples/tenants-2.json deleted file mode 100644 index c464a44637..0000000000 --- a/keystone/content/common/samples/tenants-2.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "tenants": [ - { - "id": "3645", - "name": "Iron Works", - "description": "A description ...", - "enabled": true - } - ], - "tenants_links": [ - { - "rel": "next", - "href": "http://identity.api.openstack.org/v2.0/tenants?limit=1&marker=3645" - }, - { - "rel": "previous", - "href": "http://identity.api.openstack.org/v2.0/tenants?limit=1" - } - ] -} diff --git a/keystone/content/common/samples/tenants-2.xml b/keystone/content/common/samples/tenants-2.xml deleted file mode 100644 index 7b049c402f..0000000000 --- a/keystone/content/common/samples/tenants-2.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - A description... - - - diff --git a/keystone/content/common/samples/tenants-3.json b/keystone/content/common/samples/tenants-3.json deleted file mode 100644 index 5b62bd7ef3..0000000000 --- a/keystone/content/common/samples/tenants-3.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "tenants": [ - { - "id": "9999", - "name": "Bigz", - "description": "A description ...", - "enabled": true - } - ], - "tenants_links": [ - { - "rel": "previous", - "href": "http://identity.api.openstack.org/v2.0/tenants?limit=1&marker=1234" - } - ] -} diff --git a/keystone/content/common/samples/tenants-3.xml b/keystone/content/common/samples/tenants-3.xml deleted file mode 100644 index a0edcadb3d..0000000000 --- a/keystone/content/common/samples/tenants-3.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - A description... - - - diff --git a/keystone/content/common/samples/tenants-request.txt b/keystone/content/common/samples/tenants-request.txt deleted file mode 100644 index 9dbf85e5d0..0000000000 --- a/keystone/content/common/samples/tenants-request.txt +++ /dev/null @@ -1,5 +0,0 @@ -GET /v2.0/tenants HTTP/1.1 -Host: identity.api.openstack.org -Content-Type: application/json -X-Auth-Token: fa8426a0-8eaf-4d22-8e13-7c1b16a9370c -Accept: application/json \ No newline at end of file diff --git a/keystone/content/common/samples/tenants.json b/keystone/content/common/samples/tenants.json deleted file mode 100644 index a249472fb6..0000000000 --- a/keystone/content/common/samples/tenants.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "tenants": [ - { - "id": "1234", - "name": "ACME Corp", - "description": "A description ...", - "enabled": true - }, - { - "id": "3456", - "name": "Iron Works", - "description": "A description ...", - "enabled": true - } - ], - "tenants_links": [] -} diff --git a/keystone/content/common/samples/tenants.xml b/keystone/content/common/samples/tenants.xml deleted file mode 100644 index ac5fa2d982..0000000000 --- a/keystone/content/common/samples/tenants.xml +++ /dev/null @@ -1,14 +0,0 @@ -HTTP/1.1 200 OK -Content-Type: application/xml; charset=UTF-8 -Content-Length: 200 -Date: Sun, 1 Jan 2011 9:00:00 GMT - - - - - A description... - - - A description... - - diff --git a/keystone/content/common/samples/tenantwithoutid.json b/keystone/content/common/samples/tenantwithoutid.json deleted file mode 100644 index 63faba9c8f..0000000000 --- a/keystone/content/common/samples/tenantwithoutid.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "tenant": { - "name": "ACME corp", - "description": "A description ...", - "enabled": true - } -} diff --git a/keystone/content/common/samples/tenantwithoutid.xml b/keystone/content/common/samples/tenantwithoutid.xml deleted file mode 100644 index 3983684bb4..0000000000 --- a/keystone/content/common/samples/tenantwithoutid.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - A description... - diff --git a/keystone/content/common/samples/updatedtenant.json b/keystone/content/common/samples/updatedtenant.json deleted file mode 100644 index 2acf53d13d..0000000000 --- a/keystone/content/common/samples/updatedtenant.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "tenant": { - "id": "1234", - "name": "ACME Corp", - "description": "A NEW description...", - "enabled": true - } -} diff --git a/keystone/content/common/samples/updatedtenant.xml b/keystone/content/common/samples/updatedtenant.xml deleted file mode 100644 index 5b36701aed..0000000000 --- a/keystone/content/common/samples/updatedtenant.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - A NEW description... - diff --git a/keystone/content/common/samples/user.json b/keystone/content/common/samples/user.json deleted file mode 100644 index 75d1508b93..0000000000 --- a/keystone/content/common/samples/user.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "user": { - "id": "u1000", - "username": "jqsmith", - "email": "john.smith@example.org", - "enabled": true - } -} diff --git a/keystone/content/common/samples/user.xml b/keystone/content/common/samples/user.xml deleted file mode 100644 index ccaf7ec4f9..0000000000 --- a/keystone/content/common/samples/user.xml +++ /dev/null @@ -1,4 +0,0 @@ - - diff --git a/keystone/content/common/samples/users.json b/keystone/content/common/samples/users.json deleted file mode 100644 index 3fc104d7c2..0000000000 --- a/keystone/content/common/samples/users.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "users": [ - { - "id": "u1000", - "username": "jqsmith", - "email": "john.smith@example.org", - "enabled": true - }, - { - "id": "u1001", - "username": "jqsmith", - "email": "john.smith@example.org", - "enabled": true - } - ], - "users_links": [] -} diff --git a/keystone/content/common/samples/users.xml b/keystone/content/common/samples/users.xml deleted file mode 100644 index 01b321da70..0000000000 --- a/keystone/content/common/samples/users.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - diff --git a/keystone/content/common/samples/userwithenabledonly.json b/keystone/content/common/samples/userwithenabledonly.json deleted file mode 100644 index fdd7456216..0000000000 --- a/keystone/content/common/samples/userwithenabledonly.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "user": { - "enabled": true - } -} diff --git a/keystone/content/common/samples/userwithenabledonly.xml b/keystone/content/common/samples/userwithenabledonly.xml deleted file mode 100644 index 0176b9eb35..0000000000 --- a/keystone/content/common/samples/userwithenabledonly.xml +++ /dev/null @@ -1,5 +0,0 @@ - - diff --git a/keystone/content/common/samples/userwithoutid.json b/keystone/content/common/samples/userwithoutid.json deleted file mode 100644 index 8d15f3af3a..0000000000 --- a/keystone/content/common/samples/userwithoutid.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "user": { - "username": "jqsmith", - "email": "john.smith@example.org", - "enabled": true - } -} diff --git a/keystone/content/common/samples/userwithoutid.xml b/keystone/content/common/samples/userwithoutid.xml deleted file mode 100644 index 3e875beb82..0000000000 --- a/keystone/content/common/samples/userwithoutid.xml +++ /dev/null @@ -1,4 +0,0 @@ - - diff --git a/keystone/content/common/samples/validatetoken.json b/keystone/content/common/samples/validatetoken.json deleted file mode 100644 index 4a8db8819c..0000000000 --- a/keystone/content/common/samples/validatetoken.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "access": { - "token": { - "id": "ab48a9efdfedb23ty3494", - "expires": "2010-11-01T03:32:15-05:00", - "tenant": { - "id": "345", - "name": "My Project" - } - }, - "user": { - "id": "123", - "name": "jqsmith", - "roles": [ - { - "id": "234", - "name": "compute:admin" - }, - { - "id": "234", - "name": "object-store:admin", - "tenantId": "1" - } - ], - "roles_links": [] - } - } -} diff --git a/keystone/content/common/samples/validatetoken.xml b/keystone/content/common/samples/validatetoken.xml deleted file mode 100644 index 12da9ebbd5..0000000000 --- a/keystone/content/common/samples/validatetoken.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/keystone/content/common/samples/version-atom.xml b/keystone/content/common/samples/version-atom.xml deleted file mode 100644 index 3d78a4a399..0000000000 --- a/keystone/content/common/samples/version-atom.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - About This Version - 2011-01-21T11:33:21-06:00 - http://identity.api.openstack.org/v2.0/ - OpenStackhttp://www.openstack.org/ - - - http://identity.api.openstack.org/v2.0/ - Version v2.0 - 2011-01-21T11:33:21-06:00 - - - - Version v2.0 CURRENT (2011-01-21T11:33:21-06:00) - - diff --git a/keystone/content/common/samples/version.json b/keystone/content/common/samples/version.json deleted file mode 100644 index 4410f93f25..0000000000 --- a/keystone/content/common/samples/version.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "version": { - "id": "v2.0", - "status": "CURRENT", - "updated": "2011-01-21T11:33:21-06:00", - "links": [ - { - "rel": "self", - "href": "http://identity.api.openstack.org/v2.0/" - }, - { - "rel": "describedby", - "type": "application/pdf", - "href": "http://docs.openstack.org/identity/api/v2.0/identity-latest.pdf" - }, - { - "rel": "describedby", - "type": "application/vnd.sun.wadl+xml", - "href": "http://docs.openstack.org/identity/api/v2.0/identity.wadl" - } - ], - "media-types": [ - { - "base": "application/xml", - "type": "application/vnd.openstack.identity+xml;version=2.0" - }, - { - "base": "application/json", - "type": "application/vnd.openstack.identity+json;version=2.0" - } - ] - } -} diff --git a/keystone/content/common/samples/version.xml b/keystone/content/common/samples/version.xml deleted file mode 100644 index d0f77e4250..0000000000 --- a/keystone/content/common/samples/version.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - diff --git a/keystone/content/common/samples/versions-atom.xml b/keystone/content/common/samples/versions-atom.xml deleted file mode 100644 index 5c864fcee7..0000000000 --- a/keystone/content/common/samples/versions-atom.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - Available API Versions - 2010-12-12T18:30:02.25Z - http://identity.api.openstack.org/ - OpenStackhttp://www.openstack.org/ - - - http://identity.api.openstack.org/v2.0/ - Version v2.0 - 2011-05-27T20:22:02.25Z - - Version v2.1 CURRENT (2011-05-27T20:22:02.25Z) - - - http://identity.api.openstack.org/v1.1/ - Version v1.1 - 2010-12-12T18:30:02.25Z - - Version v1.1 CURRENT (2010-12-12T18:30:02.25Z) - - - http://identity.api.openstack.org/v1.0/ - Version v1.0 - 2009-10-09T11:30:00Z - - Version v1.0 DEPRECATED (2009-10-09T11:30:00Z) - - diff --git a/keystone/content/common/samples/versions.json b/keystone/content/common/samples/versions.json deleted file mode 100644 index e1e590f47e..0000000000 --- a/keystone/content/common/samples/versions.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "versions": [ - { - "id": "v1.0", - "status": "DEPRECATED", - "updated": "2009-10-09T11:30:00Z", - "links": [ - { - "rel": "self", - "href": "http://identity.api.openstack.org/v1.0/" - } - ] - }, - { - "id": "v1.1", - "status": "CURRENT", - "updated": "2010-12-12T18:30:02.25Z", - "links": [ - { - "rel": "self", - "href": "http://identity.api.openstack.org/v1.1/" - } - ] - }, - { - "id": "v2.0", - "status": "BETA", - "updated": "2011-05-27T20:22:02.25Z", - "links": [ - { - "rel": "self", - "href": "http://identity.api.openstack.org/v2.0/" - } - ] - } - ], - "versions_links": [] -} diff --git a/keystone/content/common/samples/versions.xml b/keystone/content/common/samples/versions.xml deleted file mode 100644 index caa9801b38..0000000000 --- a/keystone/content/common/samples/versions.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/keystone/content/common/style/schema.css b/keystone/content/common/style/schema.css deleted file mode 100644 index f174ca5200..0000000000 --- a/keystone/content/common/style/schema.css +++ /dev/null @@ -1,82 +0,0 @@ -/* - * (C) 2009 Rackspace Hosting, All Rights Reserved. - */ -body, div, dl, dt, dd, ul, ol, li, h2, h3, -h4, h5, h6, pre, code, form, fieldset, legend, -input, button, textarea, p, blockquote, th, td { - text-align: left; -} - -h1 { - font-size: 350%; - margin-bottom: 10px; -} - -#Content { - border: 1px solid; - padding: 0px 40px 40px; - margin-left: 155px; -} - -#SrcContent { - padding: 0px 40px 40px; - display: none; - margin-left: 155px; -} - -#Controller { - position: fixed; - width: 145px; - left: 10px; - top: 10px; -} - -.Sample { - display: none; -} - -.EnumValue{ - padding: 10px 0px; -} - -.EnumDoc{ - padding: 10px 10px 10px 0px; -} - -.ExternHref{ - padding-top: 5px; -} - -.ExternDoc{ - padding-right: 10px; -} - -pre { - overflow: auto; -} - -td { - padding: 0px 0px 0px 10px; - width: 50%; - font-size: 90%; -} - -table { - width: 100%; -} - -a { - text-decoration: none; -} - -a:hover { - text-decoration: underline; -} - -a:link { - color: #000090; -} - -a:visited { - color: #000090; -} diff --git a/keystone/content/common/style/shjs/sh_acid.css b/keystone/content/common/style/shjs/sh_acid.css deleted file mode 100644 index a34b786f30..0000000000 --- a/keystone/content/common/style/shjs/sh_acid.css +++ /dev/null @@ -1,151 +0,0 @@ -pre.sh_sourceCode { - background-color: #eeeeee; - color: #000000; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_keyword { - color: #bb7977; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_type { - color: #8080c0; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_string { - color: #a68500; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_regexp { - color: #a68500; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_specialchar { - color: #ff00ff; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_comment { - color: #ff8000; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_number { - color: #800080; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_preproc { - color: #0080c0; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_symbol { - color: #ff0080; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_function { - color: #004466; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_cbracket { - color: #ff0080; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_url { - color: #a68500; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_date { - color: #bb7977; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_time { - color: #bb7977; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_file { - color: #bb7977; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_ip { - color: #a68500; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_name { - color: #a68500; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_variable { - color: #0080c0; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_oldfile { - color: #ff00ff; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_newfile { - color: #a68500; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_difflines { - color: #bb7977; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_selector { - color: #0080c0; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_property { - color: #bb7977; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_value { - color: #a68500; - font-weight: normal; - font-style: normal; -} - diff --git a/keystone/content/common/style/shjs/sh_darkblue.css b/keystone/content/common/style/shjs/sh_darkblue.css deleted file mode 100644 index 23fd6dab21..0000000000 --- a/keystone/content/common/style/shjs/sh_darkblue.css +++ /dev/null @@ -1,151 +0,0 @@ -pre.sh_sourceCode { - background-color: #000040; - color: #C7C7C7; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_keyword { - color: #ffff60; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_type { - color: #60ff60; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_string { - color: #ffa0a0; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_regexp { - color: #ffa0a0; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_specialchar { - color: #ffa500; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_comment { - color: #80a0ff; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_number { - color: #42cad9; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_preproc { - color: #ff80ff; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_symbol { - color: #d8e91b; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_function { - color: #ffffff; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_cbracket { - color: #d8e91b; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_url { - color: #ffa0a0; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_date { - color: #ffff60; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_time { - color: #ffff60; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_file { - color: #ffff60; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_ip { - color: #ffa0a0; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_name { - color: #ffa0a0; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_variable { - color: #26e0e7; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_oldfile { - color: #ffa500; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_newfile { - color: #ffa0a0; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_difflines { - color: #ffff60; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_selector { - color: #26e0e7; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_property { - color: #ffff60; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_value { - color: #ffa0a0; - font-weight: normal; - font-style: normal; -} - diff --git a/keystone/content/common/style/shjs/sh_emacs.css b/keystone/content/common/style/shjs/sh_emacs.css deleted file mode 100644 index 6e019cbeaa..0000000000 --- a/keystone/content/common/style/shjs/sh_emacs.css +++ /dev/null @@ -1,139 +0,0 @@ -pre.sh_sourceCode { - background-color: #ffffff; - color: #000000; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_keyword { - color: #9c20ee; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_type { - color: #208920; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_string { - color: #bd8d8b; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_regexp { - color: #bd8d8b; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_specialchar { - color: #bd8d8b; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_comment { - color: #ac2020; - font-weight: normal; - font-style: italic; -} - -pre.sh_sourceCode .sh_number { - color: #000000; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_preproc { - color: #000000; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_function { - color: #000000; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_url { - color: #bd8d8b; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_date { - color: #9c20ee; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_time { - color: #9c20ee; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_file { - color: #9c20ee; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_ip { - color: #bd8d8b; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_name { - color: #bd8d8b; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_variable { - color: #0000ff; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_oldfile { - color: #bd8d8b; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_newfile { - color: #bd8d8b; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_difflines { - color: #9c20ee; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_selector { - color: #0000ff; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_property { - color: #9c20ee; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_value { - color: #bd8d8b; - font-weight: normal; - font-style: normal; -} - diff --git a/keystone/content/common/style/shjs/sh_night.css b/keystone/content/common/style/shjs/sh_night.css deleted file mode 100644 index d8d371b47b..0000000000 --- a/keystone/content/common/style/shjs/sh_night.css +++ /dev/null @@ -1,151 +0,0 @@ -pre.sh_sourceCode { - background-color: #000044; - color: #dd00ff; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_keyword { - color: #ffffff; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_type { - color: #f1157c; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_string { - color: #ffffff; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_regexp { - color: #ffffff; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_specialchar { - color: #82d66d; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_comment { - color: #bfbfbf; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_number { - color: #8ee119; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_preproc { - color: #00bb00; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_symbol { - color: #e7ee5c; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_function { - color: #ff06cd; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_cbracket { - color: #e7ee5c; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_url { - color: #ffffff; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_date { - color: #ffffff; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_time { - color: #ffffff; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_file { - color: #ffffff; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_ip { - color: #ffffff; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_name { - color: #ffffff; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_variable { - color: #7aec27; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_oldfile { - color: #82d66d; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_newfile { - color: #ffffff; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_difflines { - color: #ffffff; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_selector { - color: #7aec27; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_property { - color: #ffffff; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_value { - color: #ffffff; - font-weight: normal; - font-style: normal; -} - diff --git a/keystone/content/common/style/shjs/sh_pablo.css b/keystone/content/common/style/shjs/sh_pablo.css deleted file mode 100644 index 173cd7bf03..0000000000 --- a/keystone/content/common/style/shjs/sh_pablo.css +++ /dev/null @@ -1,151 +0,0 @@ -pre.sh_sourceCode { - background-color: #000000; - color: #ffffff; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_keyword { - color: #c0c000; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_type { - color: #00c000; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_string { - color: #00ffff; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_regexp { - color: #00ffff; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_specialchar { - color: #0000ff; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_comment { - color: #808080; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_number { - color: #00ffff; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_preproc { - color: #00ff00; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_symbol { - color: #ff0000; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_function { - color: #ff22b9; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_cbracket { - color: #ff0000; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_url { - color: #00ffff; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_date { - color: #c0c000; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_time { - color: #c0c000; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_file { - color: #c0c000; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_ip { - color: #00ffff; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_name { - color: #00ffff; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_variable { - color: #0000c0; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_oldfile { - color: #0000ff; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_newfile { - color: #00ffff; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_difflines { - color: #c0c000; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_selector { - color: #0000c0; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_property { - color: #c0c000; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_value { - color: #00ffff; - font-weight: normal; - font-style: normal; -} - diff --git a/keystone/content/common/style/shjs/sh_print.css b/keystone/content/common/style/shjs/sh_print.css deleted file mode 100644 index 1e8c116896..0000000000 --- a/keystone/content/common/style/shjs/sh_print.css +++ /dev/null @@ -1,145 +0,0 @@ -pre.sh_sourceCode { - background-color: #ffffff; - color: #000000; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_keyword { - color: #000000; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_type { - color: #000000; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_string { - color: #000000; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_regexp { - color: #000000; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_specialchar { - color: #000000; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_comment { - color: #666666; - font-weight: normal; - font-style: italic; -} - -pre.sh_sourceCode .sh_number { - color: #000000; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_preproc { - color: #000000; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_symbol { - color: #000000; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_cbracket { - color: #000000; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_url { - color: #000000; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_date { - color: #000000; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_time { - color: #000000; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_file { - color: #000000; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_ip { - color: #000000; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_name { - color: #000000; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_variable { - color: #000000; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_oldfile { - color: #000000; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_newfile { - color: #000000; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_difflines { - color: #000000; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_selector { - color: #000000; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_property { - color: #000000; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_value { - color: #000000; - font-weight: normal; - font-style: normal; -} - diff --git a/keystone/content/common/style/shjs/sh_style.css b/keystone/content/common/style/shjs/sh_style.css deleted file mode 100644 index 6cd20b47c4..0000000000 --- a/keystone/content/common/style/shjs/sh_style.css +++ /dev/null @@ -1,66 +0,0 @@ -pre.sh_sourceCode { - background-color: white; - color: black; - font-style: normal; - font-weight: normal; -} - -pre.sh_sourceCode .sh_keyword { color: blue; font-weight: bold; } /* language keywords */ -pre.sh_sourceCode .sh_type { color: darkgreen; } /* basic types */ -pre.sh_sourceCode .sh_usertype { color: teal; } /* user defined types */ -pre.sh_sourceCode .sh_string { color: red; font-family: monospace; } /* strings and chars */ -pre.sh_sourceCode .sh_regexp { color: orange; font-family: monospace; } /* regular expressions */ -pre.sh_sourceCode .sh_specialchar { color: pink; font-family: monospace; } /* e.g., \n, \t, \\ */ -pre.sh_sourceCode .sh_comment { color: brown; font-style: italic; } /* comments */ -pre.sh_sourceCode .sh_number { color: purple; } /* literal numbers */ -pre.sh_sourceCode .sh_preproc { color: darkblue; font-weight: bold; } /* e.g., #include, import */ -pre.sh_sourceCode .sh_symbol { color: darkred; } /* e.g., <, >, + */ -pre.sh_sourceCode .sh_function { color: black; font-weight: bold; } /* function calls and declarations */ -pre.sh_sourceCode .sh_cbracket { color: red; } /* block brackets (e.g., {, }) */ -pre.sh_sourceCode .sh_todo { font-weight: bold; background-color: cyan; } /* TODO and FIXME */ - -/* Predefined variables and functions (for instance glsl) */ -pre.sh_sourceCode .sh_predef_var { color: darkblue; } -pre.sh_sourceCode .sh_predef_func { color: darkblue; font-weight: bold; } - -/* for OOP */ -pre.sh_sourceCode .sh_classname { color: teal; } - -/* line numbers (not yet implemented) */ -pre.sh_sourceCode .sh_linenum { color: black; font-family: monospace; } - -/* Internet related */ -pre.sh_sourceCode .sh_url { color: blue; text-decoration: underline; font-family: monospace; } - -/* for ChangeLog and Log files */ -pre.sh_sourceCode .sh_date { color: blue; font-weight: bold; } -pre.sh_sourceCode .sh_time, pre.sh_sourceCode .sh_file { color: darkblue; font-weight: bold; } -pre.sh_sourceCode .sh_ip, pre.sh_sourceCode .sh_name { color: darkgreen; } - -/* for Prolog, Perl... */ -pre.sh_sourceCode .sh_variable { color: darkgreen; } - -/* for LaTeX */ -pre.sh_sourceCode .sh_italics { color: darkgreen; font-style: italic; } -pre.sh_sourceCode .sh_bold { color: darkgreen; font-weight: bold; } -pre.sh_sourceCode .sh_underline { color: darkgreen; text-decoration: underline; } -pre.sh_sourceCode .sh_fixed { color: green; font-family: monospace; } -pre.sh_sourceCode .sh_argument { color: darkgreen; } -pre.sh_sourceCode .sh_optionalargument { color: purple; } -pre.sh_sourceCode .sh_math { color: orange; } -pre.sh_sourceCode .sh_bibtex { color: blue; } - -/* for diffs */ -pre.sh_sourceCode .sh_oldfile { color: orange; } -pre.sh_sourceCode .sh_newfile { color: darkgreen; } -pre.sh_sourceCode .sh_difflines { color: blue; } - -/* for css */ -pre.sh_sourceCode .sh_selector { color: purple; } -pre.sh_sourceCode .sh_property { color: blue; } -pre.sh_sourceCode .sh_value { color: darkgreen; font-style: italic; } - -/* other */ -pre.sh_sourceCode .sh_section { color: black; font-weight: bold; } -pre.sh_sourceCode .sh_paren { color: red; } -pre.sh_sourceCode .sh_attribute { color: darkgreen; } diff --git a/keystone/content/common/style/shjs/sh_whitengrey.css b/keystone/content/common/style/shjs/sh_whitengrey.css deleted file mode 100644 index 41df0e2c6d..0000000000 --- a/keystone/content/common/style/shjs/sh_whitengrey.css +++ /dev/null @@ -1,139 +0,0 @@ -pre.sh_sourceCode { - background-color: #ffffff; - color: #696969; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_keyword { - color: #696969; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_type { - color: #696969; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_string { - color: #008800; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_regexp { - color: #008800; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_specialchar { - color: #008800; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_comment { - color: #1326a2; - font-weight: normal; - font-style: italic; -} - -pre.sh_sourceCode .sh_number { - color: #bb00ff; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_preproc { - color: #470000; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_function { - color: #000000; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_url { - color: #008800; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_date { - color: #696969; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_time { - color: #696969; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_file { - color: #696969; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_ip { - color: #008800; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_name { - color: #008800; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_variable { - color: #696969; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_oldfile { - color: #008800; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_newfile { - color: #008800; - font-weight: normal; - font-style: normal; -} - -pre.sh_sourceCode .sh_difflines { - color: #696969; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_selector { - color: #696969; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_property { - color: #696969; - font-weight: bold; - font-style: normal; -} - -pre.sh_sourceCode .sh_value { - color: #008800; - font-weight: normal; - font-style: normal; -} - diff --git a/keystone/content/common/xsd/OS-KSADM.xsd b/keystone/content/common/xsd/OS-KSADM.xsd deleted file mode 100644 index 5dfa08dd3e..0000000000 --- a/keystone/content/common/xsd/OS-KSADM.xsd +++ /dev/null @@ -1,108 +0,0 @@ - - - - - - - - - - - - - - - - -

- A list of services. -

-
-
-
- - - - -

- A service. -

-
-
-
- - - - -

- An extensible credentials type. -

-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
\ No newline at end of file diff --git a/keystone/content/common/xsd/OS-KSCATALOG.xsd b/keystone/content/common/xsd/OS-KSCATALOG.xsd deleted file mode 100644 index 86de52d127..0000000000 --- a/keystone/content/common/xsd/OS-KSCATALOG.xsd +++ /dev/null @@ -1,193 +0,0 @@ - - - - - - - - - - - - - - -

- A list of Endpoint Templates. -

-
-
-
- - - - -

- An Endpoint Template. -

-
-
-
- - - - - - - -

- Version details. -

-
-
-
- - -
- - - -

- An ID uniquely identifying the Endpoint Template. -

-
-
-
- - - -

- The OpenStack-registered type (e.g. 'compute', 'object-store', etc). -

-
-
-
- - - -

- The commercial service name (e.g. 'My Nova Cloud Servers'). -

-
-
-
- - - -

- The region of Endpoint Template. -

-
-
-
- - - -

- The public URL to access represented service. -

-
-
-
- - - -

- The internal version of the public URL. -

-
-
-
- - - -

- The admin URL. -

-
-
-
- - - -

- If true the Endpoint Template is automatically part of every account. -

-
-
-
- - - -

- True if the Endpoint Template is enabled (active). - A Endpoint Template cannot be added if it's disabled or inactive (false). -

-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/keystone/content/common/xsd/OS-KSEC2-credentials.xsd b/keystone/content/common/xsd/OS-KSEC2-credentials.xsd deleted file mode 100644 index 4b7da23816..0000000000 --- a/keystone/content/common/xsd/OS-KSEC2-credentials.xsd +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/keystone/content/common/xsd/OS-KSS3-credentials.xsd b/keystone/content/common/xsd/OS-KSS3-credentials.xsd deleted file mode 100644 index 8de65bf923..0000000000 --- a/keystone/content/common/xsd/OS-KSS3-credentials.xsd +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/keystone/content/common/xsd/RAX-KSADM-credentials.xsd b/keystone/content/common/xsd/RAX-KSADM-credentials.xsd deleted file mode 100644 index c1430ac3d7..0000000000 --- a/keystone/content/common/xsd/RAX-KSADM-credentials.xsd +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/keystone/content/common/xsd/RAX-KSADM-users.xsd b/keystone/content/common/xsd/RAX-KSADM-users.xsd deleted file mode 100644 index d6e8026bb5..0000000000 --- a/keystone/content/common/xsd/RAX-KSADM-users.xsd +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/keystone/content/common/xsd/RAX-KSGRP-groups.xsd b/keystone/content/common/xsd/RAX-KSGRP-groups.xsd deleted file mode 100644 index 267bacc398..0000000000 --- a/keystone/content/common/xsd/RAX-KSGRP-groups.xsd +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/keystone/content/common/xsd/RAX-KSKEY-credentials.xsd b/keystone/content/common/xsd/RAX-KSKEY-credentials.xsd deleted file mode 100644 index 69f5ce102d..0000000000 --- a/keystone/content/common/xsd/RAX-KSKEY-credentials.xsd +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- The user's API Key. -

-
-
-
-
-
-
- -
diff --git a/keystone/content/common/xsd/RAX-KSQA-secretQA.xsd b/keystone/content/common/xsd/RAX-KSQA-secretQA.xsd deleted file mode 100644 index cc13a492ec..0000000000 --- a/keystone/content/common/xsd/RAX-KSQA-secretQA.xsd +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - - - - - - - -

- A Secret Question and Answer. The answer shall serve to prove - the user's identity as it should only be able to be answered - by the user who proposed the question. -

-
- - - - - - - - - - -
- - - - - - -
-
- -
diff --git a/keystone/content/common/xsd/api-common.xsd b/keystone/content/common/xsd/api-common.xsd deleted file mode 100644 index 0625e5beac..0000000000 --- a/keystone/content/common/xsd/api-common.xsd +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - - Open Stack Common API Schema Types 1.0 - - - - - -

- This is the main index XML Schema document - for Common API Schema Types Version 1.0. -

-
-
- - - -

- Types related to extensions. -

-
-
-
- - - -

- Types related to API version details. -

-
-
-
-
diff --git a/keystone/content/common/xsd/api.xsd b/keystone/content/common/xsd/api.xsd deleted file mode 100644 index 5b6140cc8b..0000000000 --- a/keystone/content/common/xsd/api.xsd +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/keystone/content/common/xsd/atom/atom.xsd b/keystone/content/common/xsd/atom/atom.xsd deleted file mode 100644 index c515c497da..0000000000 --- a/keystone/content/common/xsd/atom/atom.xsd +++ /dev/null @@ -1,115 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - See section 3.4 of the ATOM RFC RFC4287 - - - - - - - TODO(Jorge) - - - - - - - - TODO(Jorge) - - - - - - - - TODO(Jorge) - - - - - - - - TODO(Jorge) - - - - - - - - TODO(Jorge) - - - - - - - - diff --git a/keystone/content/common/xsd/atom/xml.xsd b/keystone/content/common/xsd/atom/xml.xsd deleted file mode 100644 index aea7d0db0a..0000000000 --- a/keystone/content/common/xsd/atom/xml.xsd +++ /dev/null @@ -1,287 +0,0 @@ - - - - - - -
-

About the XML namespace

- -
-

- This schema document describes the XML namespace, in a form - suitable for import by other schema documents. -

-

- See - http://www.w3.org/XML/1998/namespace.html and - - http://www.w3.org/TR/REC-xml for information - about this namespace. -

-

- Note that local names in this namespace are intended to be - defined only by the World Wide Web Consortium or its subgroups. - The names currently defined in this namespace are listed below. - They should not be used with conflicting semantics by any Working - Group, specification, or document instance. -

-

- See further below in this document for more information about how to refer to this schema document from your own - XSD schema documents and about the - namespace-versioning policy governing this schema document. -

-
-
-
-
- - - - -
- -

lang (as an attribute name)

-

- denotes an attribute whose value - is a language code for the natural language of the content of - any element; its value is inherited. This name is reserved - by virtue of its definition in the XML specification.

- -
-
-

Notes

-

- Attempting to install the relevant ISO 2- and 3-letter - codes as the enumerated possible values is probably never - going to be a realistic possibility. -

-

- See BCP 47 at - http://www.rfc-editor.org/rfc/bcp/bcp47.txt - and the IANA language subtag registry at - - http://www.iana.org/assignments/language-subtag-registry - for further information. -

-

- The union allows for the 'un-declaration' of xml:lang with - the empty string. -

-
-
-
- - - - - - - - - -
- - - - -
- -

space (as an attribute name)

-

- denotes an attribute whose - value is a keyword indicating what whitespace processing - discipline is intended for the content of the element; its - value is inherited. This name is reserved by virtue of its - definition in the XML specification.

- -
-
-
- - - - - - -
- - - -
- -

base (as an attribute name)

-

- denotes an attribute whose value - provides a URI to be used as the base for interpreting any - relative URIs in the scope of the element on which it - appears; its value is inherited. This name is reserved - by virtue of its definition in the XML Base specification.

- -

- See http://www.w3.org/TR/xmlbase/ - for information about this attribute. -

-
-
-
-
- - - - -
- -

id (as an attribute name)

-

- denotes an attribute whose value - should be interpreted as if declared to be of type ID. - This name is reserved by virtue of its definition in the - xml:id specification.

- -

- See http://www.w3.org/TR/xml-id/ - for information about this attribute. -

-
-
-
-
- - - - - - - - - - -
- -

Father (in any context at all)

- -
-

- denotes Jon Bosak, the chair of - the original XML Working Group. This name is reserved by - the following decision of the W3C XML Plenary and - XML Coordination groups: -

-
-

- In appreciation for his vision, leadership and - dedication the W3C XML Plenary on this 10th day of - February, 2000, reserves for Jon Bosak in perpetuity - the XML name "xml:Father". -

-
-
-
-
-
- - - -
-

About this schema document

- -
-

- This schema defines attributes and an attribute group suitable - for use by schemas wishing to allow xml:base, - xml:lang, xml:space or - xml:id attributes on elements they define. -

-

- To enable this, such a schema must import this schema for - the XML namespace, e.g. as follows: -

-
-          <schema . . .>
-           . . .
-           <import namespace="http://www.w3.org/XML/1998/namespace"
-                      schemaLocation="http://www.w3.org/2001/xml.xsd"/>
-     
-

- or -

-
-           <import namespace="http://www.w3.org/XML/1998/namespace"
-                      schemaLocation="http://www.w3.org/2009/01/xml.xsd"/>
-     
-

- Subsequently, qualified reference to any of the attributes or the - group defined below will have the desired effect, e.g. -

-
-          <type . . .>
-           . . .
-           <attributeGroup ref="xml:specialAttrs"/>
-     
-

- will define a type which will schema-validate an instance element - with any of those attributes. -

-
-
-
-
- - - -
-

Versioning policy for this schema document

-
-

- In keeping with the XML Schema WG's standard versioning - policy, this schema document will persist at - - http://www.w3.org/2009/01/xml.xsd. -

-

- At the date of issue it can also be found at - - http://www.w3.org/2001/xml.xsd. -

-

- The schema document at that URI may however change in the future, - in order to remain compatible with the latest version of XML - Schema itself, or with the XML namespace itself. In other words, - if the XML Schema or XML namespaces change, the version of this - document at - http://www.w3.org/2001/xml.xsd - - will change accordingly; the version at - - http://www.w3.org/2009/01/xml.xsd - - will not change. -

-

- Previous dated (and unchanging) versions of this schema - document are at: -

- -
-
-
-
- -
- diff --git a/keystone/content/common/xsd/credentials.xsd b/keystone/content/common/xsd/credentials.xsd deleted file mode 100644 index 36e59942f9..0000000000 --- a/keystone/content/common/xsd/credentials.xsd +++ /dev/null @@ -1,105 +0,0 @@ - - - - - - - - - - - - - - - - - - - - -

- Base type for credential in Keystone. -

-
-
- - - - -
- - - - - -

- Both the tenantId and tenantName are optional, but should not be specified together. If both attributes are specified, the server SHOULD respond with a 400 Bad Request. -

-
-
- - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/keystone/content/common/xsd/endpoints.xsd b/keystone/content/common/xsd/endpoints.xsd deleted file mode 100644 index dbb075166f..0000000000 --- a/keystone/content/common/xsd/endpoints.xsd +++ /dev/null @@ -1,161 +0,0 @@ - - - - - - - - - - - - - - -

- An Endpoint. -

-
-
-
- - - -

- A list of Endpoints. -

-
-
-
- - - - - - - -

- Version details. -

-
-
-
- - -
- - - -

- An ID uniquely identifying the Endpoint. -

-
-
-
- - - -

- The OpenStack-registered type (e.g. 'compute', 'object-store', etc). -

-
-
-
- - - -

- The commercial service name (e.g. 'My Nova Cloud Servers'). -

-
-
-
- - - -

- The region of Endpoint Template. -

-
-
-
- - - -

- The public URL to access represented service. -

-
-
-
- - - -

- The internal version of the public URL. -

-
-
-
- - - -

- The admin URL. -

-
-
-
- - - -

- Tenant id to which the endpoints belong. -

-
-
-
- -
- - - - - - - - -
diff --git a/keystone/content/common/xsd/extensions.xsd b/keystone/content/common/xsd/extensions.xsd deleted file mode 100644 index 26694c079f..0000000000 --- a/keystone/content/common/xsd/extensions.xsd +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/keystone/content/common/xsd/fault.xsd b/keystone/content/common/xsd/fault.xsd deleted file mode 100644 index 4776ddaf09..0000000000 --- a/keystone/content/common/xsd/fault.xsd +++ /dev/null @@ -1,138 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - -

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

-
-
-
- - - -

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

-
-
-
- -
- - - -

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

-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

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

-
-
-
-
-
-
- -
diff --git a/keystone/content/common/xsd/roles.xsd b/keystone/content/common/xsd/roles.xsd deleted file mode 100644 index 0ed30143ea..0000000000 --- a/keystone/content/common/xsd/roles.xsd +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - - - - - - - - -

- A list of roles. -

-
-
-
- - - - -

- A role. -

-
-
-
- - - - - - - - - - - - - - - - - - - - -
\ No newline at end of file diff --git a/keystone/content/common/xsd/services.xsd b/keystone/content/common/xsd/services.xsd deleted file mode 100644 index 248e1e9392..0000000000 --- a/keystone/content/common/xsd/services.xsd +++ /dev/null @@ -1,103 +0,0 @@ - - - - - - - - - - - - - - -

- An extensible service type allows all of the - strings defined in ServiceType or an - alias prefixed status. -

-
-
- -
- - - - - - -

- The type for an OpenStack Compute API 1.1 compatible service. -

-
-
-
- - - -

- The type for a Swift-compatible service. -

-
-
-
- - - -

- The type for a Glance-compatible service -

-
-
-
- - - -

- The type for a Keystone-compatible service. -

-
-
-
-
-
- - - - -

- A non-core service type which must contain an extension prefix. -

-
-
- - - -
-
\ No newline at end of file diff --git a/keystone/content/common/xsd/tenant.xsd b/keystone/content/common/xsd/tenant.xsd deleted file mode 100644 index 8e64bd145a..0000000000 --- a/keystone/content/common/xsd/tenant.xsd +++ /dev/null @@ -1,151 +0,0 @@ - - - - - - - - - - - - - -

- A container used to group or isolate resources and/or identity - objects. Depending on the service operator, a tenant may map to a customer, - account, organization, or project. -

-
- - -           -             -           -           -             -           -         - -
-
- - - -

- A list of tenants. -

-
-
-
- - - - - - - - - - - - - - - - -

- An free text description of the tenant. -

-
-
-
- -
- - - -

- An ID uniquely identifying the tenant. This usually comes from the back-end store. - This value is guaranteed to be unique and immutable (it will never change). -

-
-
-
- - - -

- The name of the tenant. This is guaranteed to be unique, but may change. -

-
-
-
- - - -

- An boolean signifying if a tenant is enabled or not. A disabled tenant - cannot be authenticated against. -

-
-
-
- - - -

- A human-readable, friendly name for use in user interfaces. -

-
-
-
- - - -

- A time-stamp identifying the modification time of the - tenant. -

-
-
-
- - - -

- A creation time-stamp for the tenant. -

-
-
-
- -
-
diff --git a/keystone/content/common/xsd/token.xsd b/keystone/content/common/xsd/token.xsd deleted file mode 100644 index 5e410716cf..0000000000 --- a/keystone/content/common/xsd/token.xsd +++ /dev/null @@ -1,298 +0,0 @@ - - - - - - - - - - - - - - - - - - - - -

- A token is an arbitrary bit of text that is used to access - resources. Each token has a scope which describes which - resources are accessible with it. A token may be - revoked at anytime and is valid for a finite duration. -

-

- While Keystone supports token-based authentication in this release, - the intention is for it to support additional protocols in the - future. The desire is for it to be an integration service, and not - a full-fledged identity store and management solution. -

-
- - - - - - - - - - -
- - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- The service catalog lists the services you have access to -

-
-

- We optimized for future flexibility around the hierarchy. So we - left the design as a flat list of endpoints with attributes and the - consumer can categorize as they need. - This results in potential duplication (such as with the version/@list) - but we acceopt that normalization cost in order to not force an - artificial hierarchy (suchas on region, which can be optional). -

-
-
- - -           -             -           -           -             -           -         - -
- - - - -

- A list of services. -

-
-
-
- - -
- -
- - - - - - -

- A list of endpoints. -

-
-
-
- - -
- - - -

- The OpenStack-registered type (e.g. 'compute', 'object-store', etc). -

-
-
-
- - - -

- The commercial service name (e.g. 'My Nova Cloud Servers'). -

-
-
-
- -
- - - - - - - -

- Version details. -

-
-
-
- - -
- - - -

- The name of the region where the endpoint - lives. Example: airport codes; LHR (UK), - STL (Saint Louis) -

-
-
-
- - - -

- Tenant id to which the endpoints belong. -

-
-
-
- - - -

- Public accessible service URL. -

-
-
-
- - - -

- A service URL, accessible only locally within that - cloud (generally over a high bandwidth, low latency, - free of charge link). -

-
-
-
- - - -

- An Admin URL (used for administration using privileged - calls). This may expose - additional functionality not found in the public and - internal URL. -

-
-
-
- -
- - - - - - -

- Id of the version. -

-
-
-
- - - -

- URI to get the information specific to this version. -

-
-
-
- - - -

- URI to get the information about all versions. -

-
-
-
- -
-
- diff --git a/keystone/content/common/xsd/user.xsd b/keystone/content/common/xsd/user.xsd deleted file mode 100644 index 526b0f84d4..0000000000 --- a/keystone/content/common/xsd/user.xsd +++ /dev/null @@ -1,131 +0,0 @@ - - - - - - - - - - - - - - -

- A list of Users. -

-
-
-
- - - - -

- A Keystone User. -

-
-
-
- - - - - - -

- An automatically generated, unique, immutable (it will never change) identifier - for the user. This is generated by the backend this user is stored in. -

-
-
-
- - - -

- A unique, mutable (it can change) user name that may be used by the user - an identifier when presenting credentials. -

-
-
-
- - - - -

- A true/false value that determines if the user may authenticate or not. - If enabled is false, the user will not be able to authenticate. - How this value is stored or generated is dependent on the backend in use. -

-
-
-
- - - -

- A human-readable, friendly name for use in user interfaces. -

-
-
-
- - - -

- A time-stamp identifying the modification time of the - user. -

-
-
-
- - - -

- A creation time-stamp for the user. -

-
-
-
- -
- - - - - - - - - -
diff --git a/keystone/content/common/xsd/version.xsd b/keystone/content/common/xsd/version.xsd deleted file mode 100644 index a65d1ff2e4..0000000000 --- a/keystone/content/common/xsd/version.xsd +++ /dev/null @@ -1,357 +0,0 @@ - - - - - - - - - - Version Types - - - -

- This schema file defines all types related to versioning. -

-
-
- - - - - - - - -

- This element is returned when the version of the - resource cannot be determined. The element - provides a list of choices for the resource. -

-
- - - - - - - - - - -
-
- - - - - -

- Provides a list of supported versions. -

-
- - - - - - - - - - - - - -
-
- - - -

- This element provides detailed meta information - regarding the status of the current API version. - This is the XSD 1.0 compatible element definition. -

-
-
-
- - - - -

- This element provides detailed meta information - regarding the status of the current API - version. The description should include a pointer - to both a human readable and a machine processable - description of the API service. -

-
- - - - - - - - - - - - - -
-
- - - - - - - The VersionStatus type describes a service's operational status. - - - - - - - - - - This is a new service the API. Thi API - contract may be set, but the implementaiton - may not be 100% complient with it. Developers - are encouraged to begin testing aganst an - ALPHA version to provide feedback. - - - - - - - - - A status of BETA indicates that this - version is a candidate for the next major - release and may feature functionality not - available in the current - version. Developers are encouraged to test - and begin the migration processes to a - BETA version. Note that a BETA version is - undergoing testing, it has not been - officially released, and my not be stable. - - - - - - - - - The API version is stable and has been - tested. Developers are encouraged to - develop against this API version. The - current released version of the API will - always be marked as CURRENT. - - - - - - - - - A status of DEPRECATED indicates that a - newer version of the API is - available. Application developers are - discouraged from using this version and - should instead develop against the latest - current version of the API. - - - - - - - - - - - - A version choice list outlines a collection of - resources at various versions. - - - - - - - - - - - - - - - - - - - - - - - - A version choice contains relevant information - about an available service that a user can then - use to target a specific version of the service. - - - - - - - - - - - - - - - The ID of a version choice represents the service version's unique - identifier. This ID is guaranteed to be unique only among the - service version choices outlined in the VersionChoiceList. - - - - - - - - - - A version choice's status describes the current operational state of - the given service version. The operational status is captured in a - simple type enumeration called VersionStatus. - - - - - - - - - - A version choice's updated attribute describes - the time when the version was updated. The - time should be updated anytime - anything in the - version has changed: documentation, - extensions, bug fixes. - - - - - - - - - - - - A MediaTypeList outlines a collection of valid media types for a given - service version. - - - - - - - - - - - - - - - - A MediaType describes what content types the service version understands. - - - - - - - - - - - The base of a given media type describes the - simple MIME type that then a more complicated - media type can be derived from. These types - are basic and provide no namespace or version - specific data are are only provided as a - convenience. - - - - - - - - - - The type attribute of a MediaType describes - the MIME specific identifier of the media type - in question. - - - - - - -
diff --git a/keystone/content/common/xslt/schema.xslt b/keystone/content/common/xslt/schema.xslt deleted file mode 100644 index 6d602cc73a..0000000000 --- a/keystone/content/common/xslt/schema.xslt +++ /dev/null @@ -1,1342 +0,0 @@ - - - - - - - - - - - - - - - - - .. - - - - - - XML Schema Documentation - application/xhtml+xml - http://www.w3.org/2001/XMLSchema - http://web4.w3.org/TR/2001/REC-xmlschema-2-20010502/# - - " - ' - - - - - - - - - - - - - - - - - - - - element_ - attrib_ - attgrp_ - grp_ - type_ - - - - http://yui.yahooapis.com/2.7.0/build/ - - - - - - - - - - - - - - - - - stylesheet - text/css - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <xslt:value-of select="xsd:annotation/xsd:appinfo/xsdxt:title"/> - - - <xslt:value-of select="$defaultTitle"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
-
-
-
Loading...
-
-
-
- - - -

-
- -

-
-
- - - - - - - - - - - -
- - - - - - - - - -

Namespaces

- - - -
-

- Your browser does not seem to have support for - namespace nodes in XPath. If you're a Firefox - user, please consider voting to get this issue - resolved: - - https://bugzilla.mozilla.org/show_bug.cgi?id=94270 - -

-
-
- - - - - - - - - - - -
-
-
- - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
- - - - - trc.schema.controller.links[' - - ']=[ - - - - - - - - - - - - - - - - - - - - - - - - - - , - - - ]; - - - - - - trc.schema.controller.index = - - - - - - index - - - Index Schema Document - - - ; - - - - - - - trc.schema.controller.links[' - - ']=[ - - - - # - - - - - - - - See definition of - - - - - , - - - ]; - - - - - - - - { href : - - - - - - , name : - - - - - - , title : - - - - - - } - - - - - - - - -

Imports

- - - - - - - - - -
- - -
-
- - - Visit - - -
-
- -
-
-
-
- - -

Includes

- - - - - - - - -
-
-
- - - Visit - - -
-
- -
-
-
-
- - -

Elements

- - - - - - - - -
- - - - - - -
-
- - - trc.schema.sampleManager.showSample( - - - - ); - - - - - - - - - - - - - - - - - - - - -
-
- - - - Sample -
- -
- -
-
-
- - - -
- - - - - - - Loading... - - - - - - -
-
- - - - - - -

Complex Types

- - - - - - -
- - -

Simple Types

- - - - - - -
- - - - - - # - - - - - - - - - - - - - - - - - - -

- -

- - - - - -
- extends: - - - - , - - -
-
- -
- restricts: - - - - , - - -
-
-
-
- - - -
- - - - - - - - - - SubAttributes - - - Attributes - - - - - - - - - - - - - - - -
-
-
-
- - - - - - - - - - SubDocumentation - - - Documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Sequence - - -
-
- - - -
-
-
- -
- - - -
- - - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - <?> (Any Element) - - - - - - - - @? (Any Attribute) - - - - - -
- restriction -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
enum values
- - - - - - - - -
- - - - - -
-
- - - (id = - - ) - -
- -
- -
-
-
- -
- - - - - - - - (id = - - ) - - - (fixed) - - - - - - - - - - - - -
- -
- -
- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - < - - > - - - - - - @ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/keystone/content/multiple_choice.json.tpl b/keystone/content/multiple_choice.json.tpl deleted file mode 100644 index 578502ac55..0000000000 --- a/keystone/content/multiple_choice.json.tpl +++ /dev/null @@ -1,26 +0,0 @@ -{ - "choices": [ - { - "id": "v{{API_VERSION}}", - "status": "{{API_VERSION_STATUS}}", - "links": [ - { - "rel": "self", - "href": "{{PROTOCOL}}://{{HOST}}:{{PORT}}/v{{API_VERSION}}{{RESOURCE_PATH}}" - } - ], - "media-types": { - "values": [ - { - "base": "application/xml", - "type": "application/vnd.openstack.identity+xml;version={{API_VERSION}}" - }, - { - "base": "application/json", - "type": "application/vnd.openstack.identity+json;version={{API_VERSION}}" - } - ] - } - } - ] -} \ No newline at end of file diff --git a/keystone/content/multiple_choice.xml.tpl b/keystone/content/multiple_choice.xml.tpl deleted file mode 100644 index 9f3b88a449..0000000000 --- a/keystone/content/multiple_choice.xml.tpl +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/keystone/content/service/OS-KSEC2-service-devguide.pdf b/keystone/content/service/OS-KSEC2-service-devguide.pdf deleted file mode 100644 index 7ebf1078ee..0000000000 Binary files a/keystone/content/service/OS-KSEC2-service-devguide.pdf and /dev/null differ diff --git a/keystone/content/service/RAX-KSGRP-service-devguide.pdf b/keystone/content/service/RAX-KSGRP-service-devguide.pdf deleted file mode 100644 index add0cb286d..0000000000 Binary files a/keystone/content/service/RAX-KSGRP-service-devguide.pdf and /dev/null differ diff --git a/keystone/content/service/RAX-KSKEY-service-devguide.pdf b/keystone/content/service/RAX-KSKEY-service-devguide.pdf deleted file mode 100644 index 289ceb481c..0000000000 Binary files a/keystone/content/service/RAX-KSKEY-service-devguide.pdf and /dev/null differ diff --git a/keystone/content/service/extensions.json b/keystone/content/service/extensions.json deleted file mode 100644 index 7a514fd749..0000000000 --- a/keystone/content/service/extensions.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extensions": { - "values": [] - } -} diff --git a/keystone/content/service/extensions.xml b/keystone/content/service/extensions.xml deleted file mode 100644 index d63c1b9670..0000000000 --- a/keystone/content/service/extensions.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - diff --git a/keystone/content/service/identity.wadl b/keystone/content/service/identity.wadl deleted file mode 100644 index d91a7030db..0000000000 --- a/keystone/content/service/identity.wadl +++ /dev/null @@ -1,182 +0,0 @@ - - - - - - - - - - - %common; -]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- A list of supported extensions. -

-
- -
- - - - - - - - - -

- Returns detailed information about this specific version of the API. -

-
- - - - - - - - - &commonFaults; - &getFaults; -
- - - - - -

List all available extensions.

-
- - - - - - - - - - - - &commonFaults; - &getFaults; -
- - -

Get details about a specific extension.

-
- - - - - &commonFaults; - &getFaults; -
- - - - -

- Client authentication is provided via a ReST interface using the POST method, - with v2.0/tokens supplied as the path. A payload of credentials must be included - in the body. See supported credentials -

-

- Each ReST request against the Keystone system requires the inclusion of a - specific authorization token HTTP x-header, defined as X-Auth-Token. Clients obtain - this token, along with the URL to other service APIs, by first authenticating against the - Keystone Service and supplying valid credentials. -

-

- The Keystone Service is a ReSTful web service. It is the entry point to all service APIs. - To access the Keystone Service, you must know URL of the Keystone service. -

-
- - - - - - - - - - - - - &commonFaults; - &getFaults; -
- - - - - -

- Returns a list of tenants. -

-
- - - - - - - - - &commonFaults; - &getFaults; -
-
diff --git a/keystone/content/service/identitydevguide.pdf b/keystone/content/service/identitydevguide.pdf deleted file mode 100644 index 5229e5d43c..0000000000 Binary files a/keystone/content/service/identitydevguide.pdf and /dev/null differ diff --git a/keystone/content/service/version.atom.tpl b/keystone/content/service/version.atom.tpl deleted file mode 100644 index e39250d508..0000000000 --- a/keystone/content/service/version.atom.tpl +++ /dev/null @@ -1,29 +0,0 @@ - - - Available API Versions - 2010-12-22T00:00:00.00Z - http://identity.api.openstack.org/ - OpenStackhttp://www.openstack.org/ - - - http://identity.api.openstack.org/v2.0/ - Version v2.0 - 2011-09-30T00:00:00.00Z - - Version v2.0 BETA (2011-09-30T00:00:00.00Z) - - - http://identity.api.openstack.org/v1.1/ - Version v1.1 - 2011-01-21T11:33:21-06:00 - - Version v1.1 CURRENT (2011-01-21T11:33:21-06:00) - - - http://identity.api.openstack.org/v1.0/ - Version v1.0 - 2009-10-09T11:30:00Z - - Version v1.0 DEPRECATED (2009-10-09T11:30:00Z) - - diff --git a/keystone/content/service/version.json.tpl b/keystone/content/service/version.json.tpl deleted file mode 100644 index 0eb824bf05..0000000000 --- a/keystone/content/service/version.json.tpl +++ /dev/null @@ -1,32 +0,0 @@ -{ - "versions": { - "values": [{ - "id": "v{{API_VERSION}}", - "status": "{{API_VERSION_STATUS}}", - "updated": "{{API_VERSION_DATE}}", - "links": [{ - "rel": "self", - "href": "http://{{HOST}}:{{PORT}}/v2.0/" - }, { - "rel": "describedby", - "type": "text/html", - "href": "http://docs.openstack.org/api/openstack-identity-service/{{API_VERSION}}/content/" - }, { - "rel": "describedby", - "type": "application/pdf", - "href": "http://docs.openstack.org/api/openstack-identity-service/{{API_VERSION}}/identity-dev-guide-{{API_VERSION}}.pdf" - }, { - "rel": "describedby", - "type": "application/vnd.sun.wadl+xml", - "href": "http://{{HOST}}:{{PORT}}/v2.0/identity.wadl" - }], - "media-types": [{ - "base": "application/xml", - "type": "application/vnd.openstack.identity-v2.0+xml" - }, { - "base": "application/json", - "type": "application/vnd.openstack.identity-v2.0+json" - }] - }] - } -} \ No newline at end of file diff --git a/keystone/content/service/version.xml.tpl b/keystone/content/service/version.xml.tpl deleted file mode 100644 index 30a46489bf..0000000000 --- a/keystone/content/service/version.xml.tpl +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/keystone/contrib/admin_crud/__init__.py b/keystone/contrib/admin_crud/__init__.py new file mode 100644 index 0000000000..eb8c4f17e6 --- /dev/null +++ b/keystone/contrib/admin_crud/__init__.py @@ -0,0 +1 @@ +from keystone.contrib.admin_crud.core import * diff --git a/keystone/contrib/admin_crud/core.py b/keystone/contrib/admin_crud/core.py new file mode 100644 index 0000000000..15a597ed18 --- /dev/null +++ b/keystone/contrib/admin_crud/core.py @@ -0,0 +1,150 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +from keystone import catalog +from keystone import identity +from keystone.common import wsgi + + +class CrudExtension(wsgi.ExtensionRouter): + """Previously known as the OS-KSADM extension. + + Provides a bunch of CRUD operations for internal data types. + + """ + + def add_routes(self, mapper): + tenant_controller = identity.TenantController() + user_controller = identity.UserController() + role_controller = identity.RoleController() + service_controller = catalog.ServiceController() + + # Tenant Operations + mapper.connect('/tenants', controller=tenant_controller, + action='create_tenant', + conditions=dict(method=['POST'])) + mapper.connect('/tenants/{tenant_id}', + controller=tenant_controller, + action='update_tenant', + conditions=dict(method=['PUT', 'POST'])) + mapper.connect('/tenants/{tenant_id}', + controller=tenant_controller, + action='delete_tenant', + conditions=dict(method=['DELETE'])) + mapper.connect('/tenants/{tenant_id}/users', + controller=user_controller, + action='get_tenant_users', + conditions=dict(method=['GET'])) + + # User Operations + mapper.connect('/users', + controller=user_controller, + action='get_users', + conditions=dict(method=['GET'])) + mapper.connect('/users', + controller=user_controller, + action='create_user', + conditions=dict(method=['POST'])) + # NOTE(termie): not in diablo + mapper.connect('/users/{user_id}', + controller=user_controller, + action='update_user', + conditions=dict(method=['PUT'])) + mapper.connect('/users/{user_id}', + controller=user_controller, + action='delete_user', + conditions=dict(method=['DELETE'])) + + # COMPAT(diablo): the copy with no OS-KSADM is from diablo + mapper.connect('/users/{user_id}/password', + controller=user_controller, + action='set_user_password', + conditions=dict(method=['PUT'])) + mapper.connect('/users/{user_id}/OS-KSADM/password', + controller=user_controller, + action='set_user_password', + conditions=dict(method=['PUT'])) + + # COMPAT(diablo): the copy with no OS-KSADM is from diablo + mapper.connect('/users/{user_id}/tenant', + controller=user_controller, + action='update_user_tenant', + conditions=dict(method=['PUT'])) + mapper.connect('/users/{user_id}/OS-KSADM/tenant', + controller=user_controller, + action='update_user_tenant', + conditions=dict(method=['PUT'])) + + # COMPAT(diablo): the copy with no OS-KSADM is from diablo + mapper.connect('/users/{user_id}/enabled', + controller=user_controller, + action='set_user_enabled', + conditions=dict(method=['PUT'])) + mapper.connect('/users/{user_id}/OS-KSADM/enabled', + controller=user_controller, + action='set_user_enabled', + conditions=dict(method=['PUT'])) + + # User Roles + mapper.connect('/users/{user_id}/roles/OS-KSADM/{role_id}', + controller=role_controller, action='add_role_to_user', + conditions=dict(method=['PUT'])) + mapper.connect('/users/{user_id}/roles/OS-KSADM/{role_id}', + controller=role_controller, action='delete_role_from_user', + conditions=dict(method=['DELETE'])) + + # COMPAT(diablo): User Roles + mapper.connect('/users/{user_id}/roleRefs', + controller=role_controller, action='get_role_refs', + conditions=dict(method=['GET'])) + mapper.connect('/users/{user_id}/roleRefs', + controller=role_controller, action='create_role_ref', + conditions=dict(method=['POST'])) + mapper.connect('/users/{user_id}/roleRefs/{role_ref_id}', + controller=role_controller, action='delete_role_ref', + conditions=dict(method=['DELETE'])) + + # User-Tenant Roles + mapper.connect( + '/tenants/{tenant_id}/users/{user_id}/roles/OS-KSADM/{role_id}', + controller=role_controller, action='add_role_to_user', + conditions=dict(method=['PUT'])) + mapper.connect( + '/tenants/{tenant_id}/users/{user_id}/roles/OS-KSADM/{role_id}', + controller=role_controller, action='remove_role_from_user', + conditions=dict(method=['DELETE'])) + + # Service Operations + mapper.connect('/OS-KSADM/services', + controller=service_controller, + action='get_services', + conditions=dict(method=['GET'])) + mapper.connect('/OS-KSADM/services', + controller=service_controller, + action='create_service', + conditions=dict(method=['POST'])) + mapper.connect('/OS-KSADM/services/{service_id}', + controller=service_controller, + action='delete_service', + conditions=dict(method=['DELETE'])) + mapper.connect('/OS-KSADM/services/{service_id}', + controller=service_controller, + action='get_service', + conditions=dict(method=['GET'])) + + # Role Operations + mapper.connect('/OS-KSADM/roles', + controller=role_controller, + action='create_role', + conditions=dict(method=['POST'])) + mapper.connect('/OS-KSADM/roles', + controller=role_controller, + action='get_roles', + conditions=dict(method=['GET'])) + mapper.connect('/OS-KSADM/roles/{role_id}', + controller=role_controller, + action='get_role', + conditions=dict(method=['GET'])) + mapper.connect('/OS-KSADM/roles/{role_id}', + controller=role_controller, + action='delete_role', + conditions=dict(method=['DELETE'])) diff --git a/keystone/contrib/ec2/__init__.py b/keystone/contrib/ec2/__init__.py new file mode 100644 index 0000000000..6c85c66df7 --- /dev/null +++ b/keystone/contrib/ec2/__init__.py @@ -0,0 +1 @@ +from keystone.contrib.ec2.core import * diff --git a/keystone/backends/sqlalchemy/migrate_repo/__init__.py b/keystone/contrib/ec2/backends/__init__.py similarity index 100% rename from keystone/backends/sqlalchemy/migrate_repo/__init__.py rename to keystone/contrib/ec2/backends/__init__.py diff --git a/keystone/contrib/ec2/backends/kvs.py b/keystone/contrib/ec2/backends/kvs.py new file mode 100644 index 0000000000..6aa2fe81d7 --- /dev/null +++ b/keystone/contrib/ec2/backends/kvs.py @@ -0,0 +1,31 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +from keystone.common import kvs + + +class Ec2(kvs.Base): + # Public interface + def get_credential(self, credential_id): + credential_ref = self.db.get('credential-%s' % credential_id) + return credential_ref + + def list_credentials(self, user_id): + credential_ids = self.db.get('credential_list', []) + rv = [self.get_credential(x) for x in credential_ids] + return [x for x in rv if x['user_id'] == user_id] + + # CRUD + def create_credential(self, credential_id, credential): + self.db.set('credential-%s' % credential_id, credential) + credential_list = set(self.db.get('credential_list', [])) + credential_list.add(credential_id) + self.db.set('credential_list', list(credential_list)) + return credential + + def delete_credential(self, credential_id): + old_credential = self.db.get('credential-%s' % credential_id) + self.db.delete('credential-%s' % credential_id) + credential_list = set(self.db.get('credential_list', [])) + credential_list.remove(credential_id) + self.db.set('credential_list', list(credential_list)) + return None diff --git a/keystone/contrib/ec2/backends/sql.py b/keystone/contrib/ec2/backends/sql.py new file mode 100644 index 0000000000..3105660e43 --- /dev/null +++ b/keystone/contrib/ec2/backends/sql.py @@ -0,0 +1,52 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +from keystone.common import sql +from keystone.common.sql import migration + + +class Ec2Credential(sql.ModelBase, sql.DictBase): + __tablename__ = 'ec2_credential' + access = sql.Column(sql.String(64), primary_key=True) + secret = sql.Column(sql.String(64)) + user_id = sql.Column(sql.String(64)) + tenant_id = sql.Column(sql.String(64)) + + @classmethod + def from_dict(cls, user_dict): + return cls(**user_dict) + + def to_dict(self): + return dict(self.iteritems()) + + +class Ec2(sql.Base): + def get_credential(self, credential_id): + session = self.get_session() + credential_ref = session.query(Ec2Credential)\ + .filter_by(access=credential_id).first() + if not credential_ref: + return + return credential_ref.to_dict() + + def list_credentials(self, user_id): + session = self.get_session() + credential_refs = session.query(Ec2Credential)\ + .filter_by(user_id=user_id) + return [x.to_dict() for x in credential_refs] + + # CRUD + def create_credential(self, credential_id, credential): + session = self.get_session() + with session.begin(): + credential_ref = Ec2Credential.from_dict(credential) + session.add(credential_ref) + session.flush() + return credential_ref.to_dict() + + def delete_credential(self, credential_id): + session = self.get_session() + credential_ref = session.query(Ec2Credential)\ + .filter_by(access=credential_id).first() + with session.begin(): + session.delete(credential_ref) + session.flush() diff --git a/keystone/contrib/ec2/core.py b/keystone/contrib/ec2/core.py new file mode 100644 index 0000000000..08a286cc60 --- /dev/null +++ b/keystone/contrib/ec2/core.py @@ -0,0 +1,288 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +"""Main entry point into the EC2 Credentials service. + +This service allows the creation of access/secret credentials used for +the ec2 interop layer of OpenStack. + +A user can create as many access/secret pairs, each of which map to a +specific tenant. This is required because OpenStack supports a user +belonging to multiple tenants, whereas the signatures created on ec2-style +requests don't allow specification of which tenant the user wishs to act +upon. + +To complete the cycle, we provide a method that OpenStack services can +use to validate a signature and get a corresponding openstack token. This +token allows method calls to other services within the context the +access/secret was created. As an example, nova requests keystone to validate +the signature of a request, receives a token, and then makes a request to +glance to list images needed to perform the requested task. + +""" + +import uuid + +import webob.exc + +from keystone import catalog +from keystone import config +from keystone import exception +from keystone import identity +from keystone import policy +from keystone import service +from keystone import token +from keystone.common import manager +from keystone.common import utils +from keystone.common import wsgi + + +CONF = config.CONF + + +class Manager(manager.Manager): + """Default pivot point for the EC2 Credentials backend. + + See :mod:`keystone.common.manager.Manager` for more details on how this + dynamically calls the backend. + + """ + + def __init__(self): + super(Manager, self).__init__(CONF.ec2.driver) + + +class Ec2Extension(wsgi.ExtensionRouter): + def add_routes(self, mapper): + ec2_controller = Ec2Controller() + # validation + mapper.connect('/ec2tokens', + controller=ec2_controller, + action='authenticate', + conditions=dict(method=['POST'])) + + # crud + mapper.connect('/users/{user_id}/credentials/OS-EC2', + controller=ec2_controller, + action='create_credential', + conditions=dict(method=['POST'])) + mapper.connect('/users/{user_id}/credentials/OS-EC2', + controller=ec2_controller, + action='get_credentials', + conditions=dict(method=['GET'])) + mapper.connect('/users/{user_id}/credentials/OS-EC2/{credential_id}', + controller=ec2_controller, + action='get_credential', + conditions=dict(method=['GET'])) + mapper.connect('/users/{user_id}/credentials/OS-EC2/{credential_id}', + controller=ec2_controller, + action='delete_credential', + conditions=dict(method=['DELETE'])) + + +class Ec2Controller(wsgi.Application): + def __init__(self): + self.catalog_api = catalog.Manager() + self.identity_api = identity.Manager() + self.token_api = token.Manager() + self.policy_api = policy.Manager() + self.ec2_api = Manager() + super(Ec2Controller, self).__init__() + + def check_signature(self, creds_ref, credentials): + signer = utils.Ec2Signer(creds_ref['secret']) + signature = signer.generate(credentials) + if signature == credentials['signature']: + return + # NOTE(vish): Some libraries don't use the port when signing + # requests, so try again without port. + elif ':' in credentials['signature']: + hostname, _port = credentials['host'].split(':') + credentials['host'] = hostname + signature = signer.generate(credentials) + if signature != credentials.signature: + # TODO(termie): proper exception + msg = 'Invalid signature' + raise webob.exc.HTTPUnauthorized(explanation=msg) + else: + msg = 'Signature not supplied' + raise webob.exc.HTTPUnauthorized(explanation=msg) + + def authenticate(self, context, credentials=None, + ec2Credentials=None): + """Validate a signed EC2 request and provide a token. + + Other services (such as Nova) use this **admin** call to determine + if a request they signed received is from a valid user. + + If it is a valid signature, an openstack token that maps + to the user/tenant is returned to the caller, along with + all the other details returned from a normal token validation + call. + + The returned token is useful for making calls to other + OpenStack services within the context of the request. + + :param context: standard context + :param credentials: dict of ec2 signature + :param ec2Credentials: DEPRECATED dict of ec2 signature + :returns: token: openstack token equivalent to access key along + with the corresponding service catalog and roles + """ + + # FIXME(ja): validate that a service token was used! + + # NOTE(termie): backwards compat hack + if not credentials and ec2Credentials: + credentials = ec2Credentials + + creds_ref = self.ec2_api.get_credential(context, + credentials['access']) + if not creds_ref: + msg = 'Access key not found' + raise webob.exc.HTTPUnauthorized(explanation=msg) + + self.check_signature(creds_ref, credentials) + + # TODO(termie): don't create new tokens every time + # TODO(termie): this is copied from TokenController.authenticate + token_id = uuid.uuid4().hex + tenant_ref = self.identity_api.get_tenant( + context=context, + tenant_id=creds_ref['tenant_id']) + user_ref = self.identity_api.get_user( + context=context, + user_id=creds_ref['user_id']) + metadata_ref = self.identity_api.get_metadata( + context=context, + user_id=user_ref['id'], + tenant_id=tenant_ref['id']) + catalog_ref = self.catalog_api.get_catalog( + context=context, + user_id=user_ref['id'], + tenant_id=tenant_ref['id'], + metadata=metadata_ref) + + token_ref = self.token_api.create_token( + context, token_id, dict(id=token_id, + user=user_ref, + tenant=tenant_ref, + metadata=metadata_ref)) + + # TODO(termie): optimize this call at some point and put it into the + # the return for metadata + # fill out the roles in the metadata + roles_ref = [] + for role_id in metadata_ref.get('roles', []): + roles_ref.append(self.identity_api.get_role(context, role_id)) + + # TODO(termie): make this a util function or something + # TODO(termie): i don't think the ec2 middleware currently expects a + # full return, but it contains a note saying that it + # would be better to expect a full return + token_controller = service.TokenController() + return token_controller._format_authenticate( + token_ref, roles_ref, catalog_ref) + + def create_credential(self, context, user_id, tenant_id): + """Create a secret/access pair for use with ec2 style auth. + + Generates a new set of credentials that map the the user/tenant + pair. + + :param context: standard context + :param user_id: id of user + :param tenant_id: id of tenant + :returns: credential: dict of ec2 credential + """ + if not self._is_admin(context): + self._assert_identity(context, user_id) + cred_ref = {'user_id': user_id, + 'tenant_id': tenant_id, + 'access': uuid.uuid4().hex, + 'secret': uuid.uuid4().hex} + self.ec2_api.create_credential(context, cred_ref['access'], cred_ref) + return {'credential': cred_ref} + + def get_credentials(self, context, user_id): + """List all credentials for a user. + + :param context: standard context + :param user_id: id of user + :returns: credentials: list of ec2 credential dicts + """ + if not self._is_admin(context): + self._assert_identity(context, user_id) + return {'credentials': self.ec2_api.list_credentials(context, user_id)} + + def get_credential(self, context, user_id, credential_id): + """Retreive a user's access/secret pair by the access key. + + Grab the full access/secret pair for a given access key. + + :param context: standard context + :param user_id: id of user + :param credential_id: access key for credentials + :returns: credential: dict of ec2 credential + """ + if not self._is_admin(context): + self._assert_identity(context, user_id) + return {'credential': self.ec2_api.get_credential(context, + credential_id)} + + def delete_credential(self, context, user_id, credential_id): + """Delete a user's access/secret pair. + + Used to revoke a user's access/secret pair + + :param context: standard context + :param user_id: id of user + :param credential_id: access key for credentials + :returns: bool: success + """ + if not self._is_admin(context): + self._assert_identity(context, user_id) + self._assert_owner(context, user_id, credential_id) + return self.ec2_api.delete_credential(context, credential_id) + + def _assert_identity(self, context, user_id): + """Check that the provided token belongs to the user. + + :param context: standard context + :param user_id: id of user + :raises webob.exc.HTTPForbidden: when token is invalid + + """ + try: + token_ref = self.token_api.get_token(context=context, + token_id=context['token_id']) + except exception.TokenNotFound: + raise exception.Unauthorized() + token_user_id = token_ref['user'].get('id') + if not token_user_id == user_id: + raise webob.exc.HTTPForbidden() + + def _is_admin(self, context): + """Wrap admin assertion error return statement. + + :param context: standard context + :returns: bool: success + + """ + try: + self.assert_admin(context) + return True + except AssertionError: + return False + + def _assert_owner(self, context, user_id, credential_id): + """Ensure the provided user owns the credential. + + :param context: standard context + :param user_id: expected credential owner + :param credential_id: id of credential object + :raises webob.exc.HTTPForbidden: on failure + + """ + cred_ref = self.ec2_api.get_credential(context, credential_id) + if not user_id == cred_ref['user_id']: + raise webob.exc.HTTPForbidden() diff --git a/keystone/contrib/extensions/__init__.py b/keystone/contrib/extensions/__init__.py deleted file mode 100644 index 6d4247ba5d..0000000000 --- a/keystone/contrib/extensions/__init__.py +++ /dev/null @@ -1,52 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 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 logging - -from keystone import config -from keystone import utils - -CONF = config.CONF -EXTENSION_PREFIX = 'keystone.contrib.extensions.' -DEFAULT_EXTENSIONS = ['osksadm', 'oskscatalog'] -CONFIG_EXTENSION_PROPERTY = 'extensions' - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class BaseExtensionConfigurer(object): - def configure_extensions(self, extension_type, mapper): - extensions = CONF[CONFIG_EXTENSION_PROPERTY] or \ - DEFAULT_EXTENSIONS - extensions = [extension.strip() for extension in extensions] - for supported_extension in extensions: - self.extension_handlers = [] - supported_extension = "%s%s.%s" % ( - EXTENSION_PREFIX, extension_type, supported_extension.strip()) - try: - extension_module = utils.import_module(supported_extension) - if hasattr(extension_module, 'ExtensionHandler'): - extension_class = extension_module.ExtensionHandler() - extension_class.map_extension_methods(mapper) - self.extension_handlers.append(extension_class) - except Exception as err: - logger.exception("Could not load extension for %s:%s %s" % - (extension_type, supported_extension, err)) - - def get_extension_handlers(self): - return self.extension_handlers diff --git a/keystone/contrib/extensions/admin/__init__.py b/keystone/contrib/extensions/admin/__init__.py deleted file mode 100644 index 7db667ef45..0000000000 --- a/keystone/contrib/extensions/admin/__init__.py +++ /dev/null @@ -1,36 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 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 ast -import logging -from keystone import utils -from keystone.contrib.extensions import BaseExtensionConfigurer -ADMIN_CONFIGURER = None -EXTENSION_ADMIN_PREFIX = 'admin' - - -class AdminExtensionConfigurer(BaseExtensionConfigurer): - def configure(self, mapper): - self.configure_extensions(EXTENSION_ADMIN_PREFIX, mapper) - - -def get_extension_configurer(): - global ADMIN_CONFIGURER - if not ADMIN_CONFIGURER: - ADMIN_CONFIGURER = AdminExtensionConfigurer() - return ADMIN_CONFIGURER diff --git a/keystone/contrib/extensions/admin/extension.py b/keystone/contrib/extensions/admin/extension.py deleted file mode 100644 index d2ae15258c..0000000000 --- a/keystone/contrib/extensions/admin/extension.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright 2010 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 - - -class BaseExtensionHandler(object): - def map_extension_methods(self, mapper): - raise NotImplementedError diff --git a/keystone/contrib/extensions/admin/hpidm/__init__.py b/keystone/contrib/extensions/admin/hpidm/__init__.py deleted file mode 100644 index 451a662a28..0000000000 --- a/keystone/contrib/extensions/admin/hpidm/__init__.py +++ /dev/null @@ -1,25 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 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 keystone.contrib.extensions.admin.extension import BaseExtensionHandler -from keystone.controllers.token import TokenController - - -class ExtensionHandler(BaseExtensionHandler): - def map_extension_methods(self, mapper): - pass diff --git a/keystone/contrib/extensions/admin/hpidm/extension.json b/keystone/contrib/extensions/admin/hpidm/extension.json deleted file mode 100644 index 5804de07e4..0000000000 --- a/keystone/contrib/extensions/admin/hpidm/extension.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "extension": { - "name": "HP Token Validation Extension", - "namespace": "http://docs.openstack.org/identity/api/ext/HP-IDM/v1.0", - "alias": "HP-IDM", - "updated": "2011-12-06T19:00:00-00:00", - "description": "Validate token with the optional serviceId parameter so that only the roles associated with the given service IDs are returned. See https://bugs.launchpad.net/keystone/+bug/890411 for more details.", - "links": [ - { - "rel": "describedby", - "type": "application/pdf", - "href": "https://github.com/openstack/keystone/raw/master/keystone/content/admin/HP-IDM-admin-devguide.pdf" - }, - { - "rel": "describedby", - "type": "application/vnd.sun.wadl+xml", - "href": "https://raw.github.com/openstack/keystone/master/keystone/content/admin/HP-IDM-admin.wadl" - } - ] - } -} diff --git a/keystone/contrib/extensions/admin/hpidm/extension.xml b/keystone/contrib/extensions/admin/hpidm/extension.xml deleted file mode 100644 index 0482faace6..0000000000 --- a/keystone/contrib/extensions/admin/hpidm/extension.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - Validate token with the optional serviceId parameter so that only the roles associated with the given service IDs are returned. See https://bugs.launchpad.net/keystone/+bug/890411 for more details. - - - - - - diff --git a/keystone/contrib/extensions/admin/osec2/extension.json b/keystone/contrib/extensions/admin/osec2/extension.json deleted file mode 100644 index 195091b50a..0000000000 --- a/keystone/contrib/extensions/admin/osec2/extension.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "extension": { - "name": "OpenStack EC2 authentication Extension", - "namespace": "http://docs.openstack.org/identity/api/ext/OS-KSEC2/v1.0", - "alias": "OS-KSEC2", - "updated": "2011-08-25T09:50:00-00:00", - "description": "Adds the capability to support EC2 style authentication.", - "links": [ - { - "rel": "describedby", - "type": "application/pdf", - "href": "https://github.com/openstack/keystone/raw/master/keystone/content/admin/OS-KSEC2-admin-devguide.pdf" - }, - { - "rel": "describedby", - "type": "application/vnd.sun.wadl+xml", - "href": "https://raw.github.com/openstack/keystone/master/keystone/content/admin/OS-KSEC2-admin.wadl" - } - ] - } -} diff --git a/keystone/contrib/extensions/admin/osec2/extension.xml b/keystone/contrib/extensions/admin/osec2/extension.xml deleted file mode 100644 index ea24379227..0000000000 --- a/keystone/contrib/extensions/admin/osec2/extension.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - Adds the capability to support EC2 style authentication. - - - - - - diff --git a/keystone/contrib/extensions/admin/osksadm/__init__.py b/keystone/contrib/extensions/admin/osksadm/__init__.py deleted file mode 100644 index c660a7aa9f..0000000000 --- a/keystone/contrib/extensions/admin/osksadm/__init__.py +++ /dev/null @@ -1,149 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 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 keystone.contrib.extensions.admin.extension import BaseExtensionHandler -from keystone.controllers.services import ServicesController -from keystone.controllers.roles import RolesController -from keystone.controllers.user import UserController -from keystone.controllers.tenant import TenantController -from keystone.controllers.credentials import CredentialsController - - -class ExtensionHandler(BaseExtensionHandler): - def map_extension_methods(self, mapper): - tenant_controller = TenantController() - roles_controller = RolesController() - user_controller = UserController() - credentials_controller = CredentialsController() - - # Tenant Operations - mapper.connect("/tenants", controller=tenant_controller, - action="create_tenant", - conditions=dict(method=["POST"])) - mapper.connect("/tenants/{tenant_id}", - controller=tenant_controller, - action="update_tenant", conditions=dict(method=["POST"])) - mapper.connect("/tenants/{tenant_id}", - controller=tenant_controller, - action="delete_tenant", conditions=dict(method=["DELETE"])) - mapper.connect("/tenants/{tenant_id}/users", - controller=user_controller, - action="get_tenant_users", - conditions=dict(method=["GET"])) - - #Add/Delete Tenant specific role. - mapper.connect( - "/tenants/{tenant_id}/users/{user_id}/roles/OS-KSADM/{role_id}", - controller=roles_controller, action="add_role_to_user", - conditions=dict(method=["PUT"])) - mapper.connect( - "/tenants/{tenant_id}/users/{user_id}/roles/OS-KSADM/{role_id}", - controller=roles_controller, action="delete_role_from_user", - conditions=dict(method=["DELETE"])) - # User Operations - mapper.connect("/users", - controller=user_controller, - action="get_users", - conditions=dict(method=["GET"])) - mapper.connect("/users", - controller=user_controller, - action="create_user", - conditions=dict(method=["POST"])) - mapper.connect("/users/{user_id}", - controller=user_controller, - action="update_user", - conditions=dict(method=["POST"])) - mapper.connect("/users/{user_id}", - controller=user_controller, - action="delete_user", - conditions=dict(method=["DELETE"])) - #API doesn't have any of the shorthand updates as of now. - mapper.connect("/users/{user_id}/OS-KSADM/password", - controller=user_controller, - action="set_user_password", - conditions=dict(method=["PUT"])) - mapper.connect("/users/{user_id}/OS-KSADM/tenant", - controller=user_controller, - action="update_user_tenant", - conditions=dict(method=["PUT"])) - # Test this, test failed - mapper.connect("/users/{user_id}/OS-KSADM/enabled", - controller=user_controller, - action="set_user_enabled", - conditions=dict(method=["PUT"])) - #User Roles - #Add/Delete Global role. - mapper.connect("/users/{user_id}/roles/OS-KSADM/{role_id}", - controller=roles_controller, action="add_role_to_user", - conditions=dict(method=["PUT"])) - mapper.connect("/users/{user_id}/roles/OS-KSADM/{role_id}", - controller=roles_controller, action="delete_role_from_user", - conditions=dict(method=["DELETE"])) - - # Services Operations - services_controller = ServicesController() - mapper.connect("/OS-KSADM/services", - controller=services_controller, - action="get_services", - conditions=dict(method=["GET"])) - mapper.connect("/OS-KSADM/services", - controller=services_controller, - action="create_service", - conditions=dict(method=["POST"])) - mapper.connect("/OS-KSADM/services/{service_id}", - controller=services_controller, - action="delete_service", - conditions=dict(method=["DELETE"])) - mapper.connect("/OS-KSADM/services/{service_id}", - controller=services_controller, - action="get_service", - conditions=dict(method=["GET"])) - #Roles Operations - mapper.connect("/OS-KSADM/roles", controller=roles_controller, - action="create_role", conditions=dict(method=["POST"])) - mapper.connect("/OS-KSADM/roles", controller=roles_controller, - action="get_roles", conditions=dict(method=["GET"])) - mapper.connect("/OS-KSADM/roles/{role_id}", - controller=roles_controller, action="get_role", - conditions=dict(method=["GET"])) - mapper.connect("/OS-KSADM/roles/{role_id}", - controller=roles_controller, action="delete_role", - conditions=dict(method=["DELETE"])) - - #Credentials Operations - mapper.connect("/users/{user_id}/OS-KSADM/credentials", - controller=credentials_controller, action="get_credentials", - conditions=dict(method=["GET"])) - mapper.connect("/users/{user_id}/OS-KSADM/credentials", - controller=credentials_controller, action="add_credential", - conditions=dict(method=["POST"])) - mapper.connect("/users/{user_id}/OS-KSADM/"\ - "credentials/passwordCredentials", - controller=credentials_controller, - action="get_password_credential", - conditions=dict(method=["GET"])) - mapper.connect("/users/{user_id}/OS-KSADM/credentials"\ - "/passwordCredentials", - controller=credentials_controller, - action="update_password_credential", - conditions=dict(method=["POST"])) - mapper.connect("/users/{user_id}/"\ - "OS-KSADM/credentials/passwordCredentials", - controller=credentials_controller, - action="delete_password_credential", - conditions=dict(method=["DELETE"])) diff --git a/keystone/contrib/extensions/admin/osksadm/extension.json b/keystone/contrib/extensions/admin/osksadm/extension.json deleted file mode 100644 index 2c644f977c..0000000000 --- a/keystone/contrib/extensions/admin/osksadm/extension.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "extension": { - "name": "Openstack Keystone Admin", - "namespace": "http://docs.openstack.org/identity/api/ext/OS-KSADM/v1.0", - "alias": "OS-KSADM", - "updated": "2011-08-19T13:25:27-06:00", - "description": "Openstack extensions to Keystone v2.0 API enabling Admin Operations.", - "links": [ - { - "rel": "describedby", - "type": "application/pdf", - "href": "https://github.com/openstack/keystone/raw/master/keystone/content/admin/OS-KSADM-admin-devguide.pdf" - }, - { - "rel": "describedby", - "type": "application/vnd.sun.wadl+xml", - "href": "https://github.com/openstack/keystone/raw/master/keystone/content/admin/OS-KSADM-admin.wadl" - } - ] - } -} diff --git a/keystone/contrib/extensions/admin/osksadm/extension.xml b/keystone/contrib/extensions/admin/osksadm/extension.xml deleted file mode 100644 index b3c0a7a673..0000000000 --- a/keystone/contrib/extensions/admin/osksadm/extension.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - Openstack extensions to Keystone v2.0 - API enabling Admin Operations. - - - - diff --git a/keystone/contrib/extensions/admin/oskscatalog/__init__.py b/keystone/contrib/extensions/admin/oskscatalog/__init__.py deleted file mode 100644 index 38f5cf6572..0000000000 --- a/keystone/contrib/extensions/admin/oskscatalog/__init__.py +++ /dev/null @@ -1,63 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 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 keystone.contrib.extensions.admin.extension import BaseExtensionHandler -from keystone.controllers.endpointtemplates import EndpointTemplatesController - - -class ExtensionHandler(BaseExtensionHandler): - def map_extension_methods(self, mapper): - #EndpointTemplates Calls - endpoint_templates_controller = EndpointTemplatesController() - mapper.connect("/OS-KSCATALOG/endpointTemplates", - controller=endpoint_templates_controller, - action="get_endpoint_templates", - conditions=dict(method=["GET"])) - mapper.connect("/OS-KSCATALOG/endpointTemplates", - controller=endpoint_templates_controller, - action="add_endpoint_template", - conditions=dict(method=["POST"])) - mapper.connect( - "/OS-KSCATALOG/endpointTemplates/{endpoint_template_id}", - controller=endpoint_templates_controller, - action="get_endpoint_template", - conditions=dict(method=["GET"])) - mapper.connect( - "/OS-KSCATALOG/endpointTemplates/{endpoint_template_id}", - controller=endpoint_templates_controller, - action="modify_endpoint_template", - conditions=dict(method=["PUT"])) - mapper.connect( - "/OS-KSCATALOG/endpointTemplates/{endpoint_template_id}", - controller=endpoint_templates_controller, - action="delete_endpoint_template", - conditions=dict(method=["DELETE"])) - #Endpoint Calls - mapper.connect("/tenants/{tenant_id}/OS-KSCATALOG/endpoints", - controller=endpoint_templates_controller, - action="get_endpoints_for_tenant", - conditions=dict(method=["GET"])) - mapper.connect("/tenants/{tenant_id}/OS-KSCATALOG/endpoints", - controller=endpoint_templates_controller, - action="add_endpoint_to_tenant", - conditions=dict(method=["POST"])) - mapper.connect( - "/tenants/{tenant_id}/OS-KSCATALOG/endpoints/{endpoint_id}", - controller=endpoint_templates_controller, - action="remove_endpoint_from_tenant", - conditions=dict(method=["DELETE"])) diff --git a/keystone/contrib/extensions/admin/oskscatalog/extension.json b/keystone/contrib/extensions/admin/oskscatalog/extension.json deleted file mode 100644 index c84c0e5993..0000000000 --- a/keystone/contrib/extensions/admin/oskscatalog/extension.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "extension": { - "name": "Openstack Keystone Catalog", - "namespace": "http://docs.openstack.org/identity/api/ext/OS-KSCATALOG/v1.0", - "alias": "OS-KSCATALOG", - "updated": "2011-07-13T13:25:27-06:00", - "description": "Openstack extensions to Keystone v2.0 API enabling Admin Operations to support Catalog.", - "links": [ - { - "rel": "describedby", - "type": "application/pdf", - "href": "https://github.com/openstack/keystone/raw/master/keystone/content/admin/OS-KSCATALOG-admin-devguide.pdf" - }, - { - "rel": "describedby", - "type": "application/vnd.sun.wadl+xml", - "href": "https://github.com/openstack/keystone/raw/master/keystone/content/admin/OS-KSCATALOG-admin.wadl" - } - ] - } -} diff --git a/keystone/contrib/extensions/admin/oskscatalog/extension.xml b/keystone/contrib/extensions/admin/oskscatalog/extension.xml deleted file mode 100644 index 8664c77fa1..0000000000 --- a/keystone/contrib/extensions/admin/oskscatalog/extension.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - Openstack extensions to Keystone v2.0 API enabling Admin Operations - to support Catalog. - - - - diff --git a/keystone/contrib/extensions/admin/osksvalidate/__init__.py b/keystone/contrib/extensions/admin/osksvalidate/__init__.py deleted file mode 100644 index 36a50bd78d..0000000000 --- a/keystone/contrib/extensions/admin/osksvalidate/__init__.py +++ /dev/null @@ -1,37 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 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 keystone.contrib.extensions.admin.extension import BaseExtensionHandler -from keystone.contrib.extensions.admin.osksvalidate import handler - - -class ExtensionHandler(BaseExtensionHandler): - def map_extension_methods(self, mapper): - extension_controller = handler.SecureValidationController() - - # Token Operations - mapper.connect("/OS-KSVALIDATE/token/validate", - controller=extension_controller, - action="handle_validate_request", - conditions=dict(method=["GET"])) - - mapper.connect("/OS-KSVALIDATE/token/endpoints", - controller=extension_controller, - action="handle_endpoints_request", - conditions=dict(method=["GET"])) - # TODO(zns): make this handle all routes by using the mapper diff --git a/keystone/contrib/extensions/admin/osksvalidate/extension.json b/keystone/contrib/extensions/admin/osksvalidate/extension.json deleted file mode 100644 index b1f0c65bc1..0000000000 --- a/keystone/contrib/extensions/admin/osksvalidate/extension.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "extension": { - "name": "Openstack Keystone Admin", - "namespace": "http://docs.openstack.org/identity/api/ext/OS-KSVALIDATE/v1.0", - "alias": "OS-KSVALIDATE", - "updated": "2012-01-12T12:17:00-06:00", - "description": "Openstack extensions to Keystone v2.0 API for Secure Token Validation.", - "links": [ - { - "rel": "describedby", - "type": "application/pdf", - "href": "https://github.com/openstack/keystone/raw/master/keystone/content/admin/OS-KSVALIDATE-admin-devguide.pdf" - }, - { - "rel": "describedby", - "type": "application/vnd.sun.wadl+xml", - "href": "https://github.com/openstack/keystone/raw/master/keystone/content/admin/OS-KSVALIDATE-admin.wadl" - } - ] - } -} diff --git a/keystone/contrib/extensions/admin/osksvalidate/extension.xml b/keystone/contrib/extensions/admin/osksvalidate/extension.xml deleted file mode 100644 index 6643484a15..0000000000 --- a/keystone/contrib/extensions/admin/osksvalidate/extension.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - Openstack extensions to Keystone v2.0 - API for Secure Token Validation. - - - - diff --git a/keystone/contrib/extensions/admin/osksvalidate/handler.py b/keystone/contrib/extensions/admin/osksvalidate/handler.py deleted file mode 100644 index 4ce7e7c525..0000000000 --- a/keystone/contrib/extensions/admin/osksvalidate/handler.py +++ /dev/null @@ -1,46 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010-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. - - -""" -Router & Controller for handling Secure Token Validation - -""" -import logging - -from keystone.common import wsgi -from keystone.controllers.token import TokenController - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class SecureValidationController(wsgi.Controller): - """Controller for Tenant related operations""" - - # pylint: disable=W0231 - def __init__(self): - self.token_controller = TokenController() - - logger.info("Initializing Secure Token Validation extension") - - def handle_validate_request(self, req): - token_id = req.headers.get("X-Subject-Token") - return self.token_controller.validate_token(req=req, token_id=token_id) - - def handle_endpoints_request(self, req): - token_id = req.headers.get("X-Subject-Token") - return self.token_controller.endpoints(req=req, token_id=token_id) diff --git a/keystone/contrib/extensions/admin/raxgrp/extension.json b/keystone/contrib/extensions/admin/raxgrp/extension.json deleted file mode 100644 index 31744b3d33..0000000000 --- a/keystone/contrib/extensions/admin/raxgrp/extension.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "extension": { - "name": "Rackspace Keystone Group Extensions", - "namespace": "http://docs.rackspace.com/identity/api/ext/RAX-KSGROUP/v1.0", - "alias": "RAX-KSGRP", - "updated": "2011-08-14T13:25:27-06:00", - "description": "Rackspace extensions to Keystone v2.0 API enabling groups.", - "links": [ - { - "rel": "describedby", - "type": "application/pdf", - "href": "https://github.com/openstack/keystone/raw/master/keystone/content/admin/RAX-KSGRP-admin-devguide.pdf" - }, - { - "rel": "describedby", - "type": "application/vnd.sun.wadl+xml", - "href": "https://github.com/openstack/keystone/raw/master/keystone/content/admin/RAX-KSGRP-admin.wadl" - } - ] - } -} diff --git a/keystone/contrib/extensions/admin/raxgrp/extension.xml b/keystone/contrib/extensions/admin/raxgrp/extension.xml deleted file mode 100644 index 22f77faadb..0000000000 --- a/keystone/contrib/extensions/admin/raxgrp/extension.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - Rackspace extensions to Keystone v2.0 API - enabling groups. - - - - diff --git a/keystone/contrib/extensions/admin/raxkey/extension.json b/keystone/contrib/extensions/admin/raxkey/extension.json deleted file mode 100644 index 301ad6a2c7..0000000000 --- a/keystone/contrib/extensions/admin/raxkey/extension.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "extension": { - "name": "Rackspace API Key Authentication", - "namespace": "http://docs.rackspace.com/identity/api/ext/RAX-KSKEY/v1.0", - "alias": "RAX-KSKEY", - "updated": "2011-07-13T13:25:27-06:00", - "description": "Rackspace extensions to Keystone v2.0 API enabling API Key authentication.", - "links": [ - { - "rel": "describedby", - "type": "application/pdf", - "href": "https://github.com/openstack/keystone/raw/master/keystone/content/admin/RAX-KSKEY-admin-devguide.pdf" - }, - { - "rel": "describedby", - "type": "application/vnd.sun.wadl+xml", - "href": "https://github.com/openstack/keystone/raw/master/keystone/content/admin/RAX-KSKEY-admin.wadl" - } - ] - } -} diff --git a/keystone/contrib/extensions/admin/raxkey/extension.xml b/keystone/contrib/extensions/admin/raxkey/extension.xml deleted file mode 100644 index 175d7302ac..0000000000 --- a/keystone/contrib/extensions/admin/raxkey/extension.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - Rackspace extensions to Keystone v2.0 API - enabling API Key authentication. - - - - diff --git a/keystone/contrib/extensions/admin/raxkey/frontend.py b/keystone/contrib/extensions/admin/raxkey/frontend.py deleted file mode 100644 index 4f3f0796d6..0000000000 --- a/keystone/contrib/extensions/admin/raxkey/frontend.py +++ /dev/null @@ -1,105 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010-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. - -""" -RACKSPACE API KEY EXTENSION - -This WSGI component -- detects calls with extensions in them. -- processes the necessary components -""" - -import json -import logging -from lxml import etree -import os -from webob.exc import Request, Response - -from keystone import utils - -EXTENSION_ALIAS = "RAX-KSKEY-admin" - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class FrontEndFilter(object): - """API Key Middleware that handles authentication with API Key""" - - def __init__(self, app, conf): - """ Common initialization code """ - logger.info(_("Starting the %s extension" % - EXTENSION_ALIAS)) - self.conf = conf - self.app = app - - def __call__(self, env, start_response): - """ Handle incoming request. Transform. And send downstream. """ - request = Request(env) - if request.path == "/extensions": - if env['KEYSTONE_API_VERSION'] == '2.0': - request = Request(env) - response = request.get_response(self.app) - if response.status_int == 200: - if response.content_type == 'application/json': - #load json for this extension from file - thisextension = open(os.path.join( - os.path.dirname(__file__), - "extension.json")).read() - thisextensionjson = json.loads(thisextension) - - #load json in response - body = json.loads(response.body) - extensionsarray = body["extensions"]["values"] - - #add this extension and return the response - extensionsarray.append(thisextensionjson) - newresp = Response( - content_type='application/json', - body=json.dumps(body)) - return newresp(env, start_response) - elif response.content_type == 'application/xml': - #load xml for this extension from file - thisextensionxml = etree.parse(os.path.join( - os.path.dirname(__file__), - "extension.xml")).getroot() - #load xml being returned in response - body = etree.fromstring(response.body) - - #add this extension and return the response - body.append(thisextensionxml) - newresp = Response( - content_type='application/xml', - body=etree.tostring(body)) - return newresp(env, start_response) - - # return the response - return response(env, start_response) - - #default action, bypass - return self.app(env, start_response) - - -def filter_factory(global_conf, **local_conf): - """Returns a WSGI filter app for use with paste.deploy.""" - conf = global_conf.copy() - conf.update(local_conf) - - def ext_filter(app): - """Closure to return""" - return FrontEndFilter(app, conf) - return ext_filter diff --git a/keystone/contrib/extensions/extensions.json b/keystone/contrib/extensions/extensions.json deleted file mode 100644 index 7a514fd749..0000000000 --- a/keystone/contrib/extensions/extensions.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extensions": { - "values": [] - } -} diff --git a/keystone/contrib/extensions/extensions.xml b/keystone/contrib/extensions/extensions.xml deleted file mode 100644 index ed5ee9c6e2..0000000000 --- a/keystone/contrib/extensions/extensions.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - diff --git a/keystone/contrib/extensions/service/__init__.py b/keystone/contrib/extensions/service/__init__.py deleted file mode 100644 index e0eeb867cb..0000000000 --- a/keystone/contrib/extensions/service/__init__.py +++ /dev/null @@ -1 +0,0 @@ -EXTENSION_SERVICE_PREFIX = 'service' diff --git a/keystone/contrib/extensions/service/osec2/__init__.py b/keystone/contrib/extensions/service/osec2/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/keystone/contrib/extensions/service/osec2/extension.json b/keystone/contrib/extensions/service/osec2/extension.json deleted file mode 100644 index a2d287178f..0000000000 --- a/keystone/contrib/extensions/service/osec2/extension.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "extension": { - "name": "OpenStack EC2 authentication Extension", - "namespace": "http://docs.openstack.org/identity/api/ext/OS-KSEC2/v1.0", - "alias": "OS-KSEC2", - "updated": "2011-08-25T09:50:00-00:00", - "description": "Adds the capability to support EC2 style authentication.", - "links": [ - { - "rel": "describedby", - "type": "application/pdf", - "href": "https://github.com/openstack/keystone/raw/master/keystone/content/service/OS-KSEC2-service-devguide.pdf" - } - ] - } -} diff --git a/keystone/contrib/extensions/service/osec2/extension.xml b/keystone/contrib/extensions/service/osec2/extension.xml deleted file mode 100644 index 0f8307f2ca..0000000000 --- a/keystone/contrib/extensions/service/osec2/extension.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - Adds the capability to support EC2 style authentication. - - - - - diff --git a/keystone/contrib/extensions/service/osec2/frontend.py b/keystone/contrib/extensions/service/osec2/frontend.py deleted file mode 100644 index b2741b8982..0000000000 --- a/keystone/contrib/extensions/service/osec2/frontend.py +++ /dev/null @@ -1,105 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010-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. - -""" -RACKSPACE API KEY EXTENSION - -This WSGI component -- detects calls with extensions in them. -- processes the necessary components -""" - -import json -import os -import logging -from lxml import etree -from webob.exc import Request, Response - -from keystone import utils - -EXTENSION_ALIAS = "OS-EC2" - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class FrontEndFilter(object): - """API Key Middleware that handles authentication with API Key""" - - def __init__(self, app, conf): - """ Common initialization code """ - logger.info(_("Starting the %s extension" % - EXTENSION_ALIAS)) - self.conf = conf - self.app = app - - def __call__(self, env, start_response): - """ Handle incoming request. Transform. And send downstream. """ - request = Request(env) - if request.path == "/extensions": - if env['KEYSTONE_API_VERSION'] == '2.0': - request = Request(env) - response = request.get_response(self.app) - if response.status_int == 200: - if response.content_type == 'application/json': - #load json for this extension from file - thisextension = open(os.path.join( - os.path.dirname(__file__), - "extension.json")).read() - thisextensionjson = json.loads(thisextension) - - #load json in response - body = json.loads(response.body) - extensionsarray = body["extensions"]["values"] - - #add this extension and return the response - extensionsarray.append(thisextensionjson) - newresp = Response( - content_type='application/json', - body=json.dumps(body)) - return newresp(env, start_response) - elif response.content_type == 'application/xml': - #load xml for this extension from file - thisextensionxml = etree.parse(os.path.join( - os.path.dirname(__file__), - "extension.xml")).getroot() - #load xml being returned in response - body = etree.fromstring(response.body) - - #add this extension and return the response - body.append(thisextensionxml) - newresp = Response( - content_type='application/xml', - body=etree.tostring(body)) - return newresp(env, start_response) - - # return the response - return response(env, start_response) - - #default action, bypass - return self.app(env, start_response) - - -def filter_factory(global_conf, **local_conf): - """Returns a WSGI filter app for use with paste.deploy.""" - conf = global_conf.copy() - conf.update(local_conf) - - def ext_filter(app): - """Closure to return""" - return FrontEndFilter(app, conf) - return ext_filter diff --git a/keystone/contrib/extensions/service/raxgrp/__init__.py b/keystone/contrib/extensions/service/raxgrp/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/keystone/contrib/extensions/service/raxgrp/api.py b/keystone/contrib/extensions/service/raxgrp/api.py deleted file mode 100755 index 1882a13fec..0000000000 --- a/keystone/contrib/extensions/service/raxgrp/api.py +++ /dev/null @@ -1,141 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 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 keystone.backends.api import BaseUserAPI - - -#Base APIs -class RAXEXTBaseUserAPI(BaseUserAPI): - - def get_by_group(self, user_id, group_id): - raise NotImplementedError - - def tenant_group(self, values): - raise NotImplementedError - - def tenant_group_delete(self, id, group_id): - raise NotImplementedError - - def get_groups(self, id): - raise NotImplementedError - - def users_tenant_group_get_page(self, group_id, marker, limit): - raise NotImplementedError - - def users_tenant_group_get_page_markers(self, group_id, marker, limit): - raise NotImplementedError - - def get_group_by_tenant(self, id): - raise NotImplementedError - - def delete_tenant_user(self, id, tenant_id): - raise NotImplementedError - - def users_get_by_tenant(self, user_id, tenant_id): - raise NotImplementedError - - def user_role_add(self, values): - raise NotImplementedError - - def user_get_update(self, id): - raise NotImplementedError - - def users_get_page(self, marker, limit): - raise NotImplementedError - - def users_get_page_markers(self, marker, limit): - raise NotImplementedError - - def users_get_by_tenant_get_page(self, tenant_id, role_id, marker, limit): - raise NotImplementedError - - def users_get_by_tenant_get_page_markers(self, tenant_id, - role_id, marker, limit): - raise NotImplementedError - - def check_password(self, user, password): - raise NotImplementedError - - -class RAXEXTBaseTenantGroupAPI(object): - def create(self, values): - raise NotImplementedError - - def is_empty(self, id): - raise NotImplementedError - - def get(self, id, tenant): - raise NotImplementedError - - def get_page(self, tenant_id, marker, limit): - raise NotImplementedError - - def get_page_markers(self, tenant_id, marker, limit): - raise NotImplementedError - - def update(self, id, tenant_id, values): - raise NotImplementedError - - def delete(self, id, tenant_id): - raise NotImplementedError - - -class RAXEXTBaseGroupAPI(object): - def get(self, id): - raise NotImplementedError - - def get_users(self, id): - raise NotImplementedError - - def get_all(self): - raise NotImplementedError - - def get_page(self, marker, limit): - raise NotImplementedError - - def get_page_markers(self, marker, limit): - raise NotImplementedError - - def delete(self, id): - raise NotImplementedError - - def get_by_user_get_page(self, user_id, marker, limit): - raise NotImplementedError - - def get_by_user_get_page_markers(self, user_id, marker, limit): - raise NotImplementedError - - -#API -#TODO(Yogi) Refactor all API to separate classes specific to models. -GROUP = RAXEXTBaseGroupAPI() -TENANT_GROUP = RAXEXTBaseTenantGroupAPI() -USER = RAXEXTBaseUserAPI() - - -# Function to dynamically set module references. -def set_value(variable_name, value): - if variable_name == 'group': - global GROUP - GROUP = value - elif variable_name == 'tenant_group': - global TENANT_GROUP - TENANT_GROUP = value - elif variable_name == 'user': - global USER - USER = value diff --git a/keystone/contrib/extensions/service/raxgrp/extension.json b/keystone/contrib/extensions/service/raxgrp/extension.json deleted file mode 100644 index e2c889dd99..0000000000 --- a/keystone/contrib/extensions/service/raxgrp/extension.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "extension": { - "name": "Rackspace Keystone Group Extensions", - "namespace": "http://docs.rackspace.com/identity/api/ext/RAX-KSGROUP/v1.0", - "alias": "RAX-KSGRP", - "updated": "2011-08-14T13:25:27-06:00", - "description": "Rackspace extensions to Keystone v2.0 API enabling groups.", - "links": [ - { - "rel": "describedby", - "type": "application/pdf", - "href": "https://github.com/openstack/keystone/raw/master/keystone/content/service/RAX-KSGRP-service-devguide.pdf" - } - ] - } -} diff --git a/keystone/contrib/extensions/service/raxgrp/extension.xml b/keystone/contrib/extensions/service/raxgrp/extension.xml deleted file mode 100644 index f05855acd4..0000000000 --- a/keystone/contrib/extensions/service/raxgrp/extension.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - Rackspace extensions to Keystone v2.0 API - enabling groups. - - - diff --git a/keystone/contrib/extensions/service/raxkey/__init__.py b/keystone/contrib/extensions/service/raxkey/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/keystone/contrib/extensions/service/raxkey/extension.json b/keystone/contrib/extensions/service/raxkey/extension.json deleted file mode 100644 index ad72420e5e..0000000000 --- a/keystone/contrib/extensions/service/raxkey/extension.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "extension": { - "name": "Rackspace API Key Authentication", - "namespace": "http://docs.rackspace.com/identity/api/ext/RAX-KSKEY/v1.0", - "alias": "RAX-KSKEY", - "updated": "2011-08-14T13:25:27-06:00", - "description": "Rackspace extensions to Keystone v2.0 API enabling API Key authentication.", - "links": [ - { - "rel": "describedby", - "type": "application/pdf", - "href": "https://github.com/openstack/keystone/raw/master/keystone/content/service/RAX-KSKEY-service-devguide.pdf" - } - ] - } -} diff --git a/keystone/contrib/extensions/service/raxkey/extension.xml b/keystone/contrib/extensions/service/raxkey/extension.xml deleted file mode 100644 index 981cb2bc60..0000000000 --- a/keystone/contrib/extensions/service/raxkey/extension.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - Rackspace extensions to Keystone v2.0 API - enabling API Key authentication. - - - diff --git a/keystone/contrib/extensions/service/raxkey/frontend.py b/keystone/contrib/extensions/service/raxkey/frontend.py deleted file mode 100644 index 1a05001310..0000000000 --- a/keystone/contrib/extensions/service/raxkey/frontend.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010-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. - -""" -RACKSPACE API KEY EXTENSION - -Deprecated middleware. We still have it here to not break compatiblity with -configuration files that add it to the pipeline. -""" -import logging - -from keystone import utils - -EXTENSION_ALIAS = "RAX-KEY" - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class FrontEndFilter(object): - """API Key Middleware that handles authentication with API Key""" - - def __init__(self, app, conf): - """ Common initialization code """ - logger.warn(_("WARNING: Starting the %s extension which " - "is deprecated" % - EXTENSION_ALIAS)) - self.conf = conf - self.app = app - - def __call__(self, env, start_response): - logger.warn("%s middleware is deprecated and will be removed in " - "Essex+1 (Fall fo 2012). Remove it from your " - "configuration files." % EXTENSION_ALIAS) - #Kept for backward compatibility with existing configuration files. - #Does nothing now. - return self.app(env, start_response) - - -def filter_factory(global_conf, **local_conf): - """Returns a WSGI filter app for use with paste.deploy.""" - conf = global_conf.copy() - conf.update(local_conf) - - def ext_filter(app): - """Closure to return""" - return FrontEndFilter(app, conf) - return ext_filter diff --git a/keystone/contrib/s3/__init__.py b/keystone/contrib/s3/__init__.py new file mode 100644 index 0000000000..2214959601 --- /dev/null +++ b/keystone/contrib/s3/__init__.py @@ -0,0 +1 @@ +from keystone.contrib.s3.core import * diff --git a/keystone/contrib/s3/core.py b/keystone/contrib/s3/core.py new file mode 100644 index 0000000000..d69fade4f1 --- /dev/null +++ b/keystone/contrib/s3/core.py @@ -0,0 +1,37 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +"""Main entry point into the S3 Credentials service. + +TODO-DOCS +""" + +import base64 +import hmac + +from hashlib import sha1 + +from keystone import config +from keystone.common import wsgi +from keystone.contrib import ec2 + +CONF = config.CONF + + +class S3Extension(wsgi.ExtensionRouter): + def add_routes(self, mapper): + controller = S3Controller() + # validation + mapper.connect('/s3tokens', + controller=controller, + action='authenticate', + conditions=dict(method=['POST'])) + + +class S3Controller(ec2.Ec2Controller): + def check_signature(self, creds_ref, credentials): + msg = base64.urlsafe_b64decode(str(credentials['token'])) + key = str(creds_ref['secret']) + signed = base64.encodestring(hmac.new(key, msg, sha1).digest()).strip() + + if credentials['signature'] != signed: + raise Exception('Not Authorized') diff --git a/keystone/controllers/__init__.py b/keystone/controllers/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/keystone/controllers/base_controller.py b/keystone/controllers/base_controller.py deleted file mode 100644 index 5b87e86051..0000000000 --- a/keystone/controllers/base_controller.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) 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. - -""" -Base Class Controller - -""" -import functools -import logging - -from keystone import utils -from keystone.common import wsgi - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class BaseController(wsgi.Controller): - """Base Controller class for Keystone""" - - @staticmethod - def get_url(req): - return '%s://%s:%s%s' % ( - req.environ['wsgi.url_scheme'], - req.environ.get("SERVER_NAME"), - req.environ.get("SERVER_PORT"), - req.environ['PATH_INFO']) - - def get_marker_limit_and_url(self, req): - marker = req.GET["marker"] if "marker" in req.GET else None - limit = req.GET["limit"] if "limit" in req.GET else 10 - url = self.get_url(req) - return (marker, limit, url) diff --git a/keystone/controllers/credentials.py b/keystone/controllers/credentials.py deleted file mode 100644 index b7d4022b65..0000000000 --- a/keystone/controllers/credentials.py +++ /dev/null @@ -1,71 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010-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. - - -""" -Credentials Controller - -""" -import logging - -from keystone import utils -from keystone.controllers.base_controller import BaseController -from keystone.logic import service -from keystone.logic.types.credential import PasswordCredentials - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class CredentialsController(BaseController): - """Controller for Credentials related operations""" - def __init__(self): - self.identity_service = service.IdentityService() - - @utils.wrap_error - def get_credentials(self, req, user_id): - marker, limit, url = self.get_marker_limit_and_url(req) - credentials = self.identity_service.get_credentials( - utils.get_auth_token(req), user_id, marker, limit, url) - return utils.send_result(200, req, credentials) - - @utils.wrap_error - def get_password_credential(self, req, user_id): - credentials = self.identity_service.get_password_credentials( - utils.get_auth_token(req), user_id) - return utils.send_result(200, req, credentials) - - @utils.wrap_error - def delete_password_credential(self, req, user_id): - self.identity_service.delete_password_credentials( - utils.get_auth_token(req), user_id) - return utils.send_result(204, None) - - @utils.wrap_error - def add_credential(self, req, user_id): - credential = utils.get_normalized_request_content( - PasswordCredentials, req) - credential = self.identity_service.create_password_credentials( - utils.get_auth_token(req), user_id, credential) - return utils.send_result(201, req, credential) - - @utils.wrap_error - def update_password_credential(self, req, user_id): - credential = utils.get_normalized_request_content( - PasswordCredentials, req) - credential = self.identity_service.update_password_credentials( - utils.get_auth_token(req), user_id, credential) - return utils.send_result(200, req, credential) diff --git a/keystone/controllers/endpointtemplates.py b/keystone/controllers/endpointtemplates.py deleted file mode 100644 index 4113d4d865..0000000000 --- a/keystone/controllers/endpointtemplates.py +++ /dev/null @@ -1,99 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010-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. - - -""" -EndpointTemplates Controller - -""" -import logging - -from keystone import utils -from keystone.controllers.base_controller import BaseController -from keystone.logic import service -from keystone.logic.types.endpoint import EndpointTemplate - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class EndpointTemplatesController(BaseController): - """Controller for EndpointTemplates related operations""" - - def __init__(self): - self.identity_service = service.IdentityService() - - @utils.wrap_error - def get_endpoint_templates(self, req): - marker, limit, url = self.get_marker_limit_and_url(req) - service_id = req.GET["serviceId"] if "serviceId" in req.GET else None - if service_id: - endpoint_templates = self.identity_service.\ - get_endpoint_templates_by_service( - utils.get_auth_token(req), service_id, marker, limit, url) - else: - endpoint_templates = self.identity_service.get_endpoint_templates( - utils.get_auth_token(req), marker, limit, url) - return utils.send_result(200, req, endpoint_templates) - - @utils.wrap_error - def add_endpoint_template(self, req): - endpoint_template = utils.get_normalized_request_content( - EndpointTemplate, req) - return utils.send_result(201, req, - self.identity_service.add_endpoint_template( - utils.get_auth_token(req), endpoint_template)) - - @utils.wrap_error - def modify_endpoint_template(self, req, endpoint_template_id): - endpoint_template = utils.\ - get_normalized_request_content(EndpointTemplate, req) - return utils.send_result(201, req, - self.identity_service.modify_endpoint_template(\ - utils.get_auth_token(req), - endpoint_template_id, endpoint_template)) - - @utils.wrap_error - def delete_endpoint_template(self, req, endpoint_template_id): - rval = self.identity_service.delete_endpoint_template( - utils.get_auth_token(req), endpoint_template_id) - return utils.send_result(204, req, rval) - - @utils.wrap_error - def get_endpoint_template(self, req, endpoint_template_id): - endpoint_template = self.identity_service.get_endpoint_template( - utils.get_auth_token(req), endpoint_template_id) - return utils.send_result(200, req, endpoint_template) - - @utils.wrap_error - def get_endpoints_for_tenant(self, req, tenant_id): - marker, limit, url = self.get_marker_limit_and_url(req) - endpoints = self.identity_service.get_tenant_endpoints( - utils.get_auth_token(req), marker, limit, url, tenant_id) - return utils.send_result(200, req, endpoints) - - @utils.wrap_error - def add_endpoint_to_tenant(self, req, tenant_id): - endpoint = utils.get_normalized_request_content(EndpointTemplate, req) - return utils.send_result(201, req, - self.identity_service.create_endpoint_for_tenant( - utils.get_auth_token(req), tenant_id, endpoint)) - - @utils.wrap_error - def remove_endpoint_from_tenant(self, req, tenant_id, endpoint_id): - rval = self.identity_service.delete_endpoint(utils.get_auth_token(req), - endpoint_id) - return utils.send_result(204, req, rval) diff --git a/keystone/controllers/extensions.py b/keystone/controllers/extensions.py deleted file mode 100644 index 171710f674..0000000000 --- a/keystone/controllers/extensions.py +++ /dev/null @@ -1,48 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010-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. - - -""" -Extensions Controller - -""" -import logging - -from keystone import utils -from keystone.controllers.base_controller import BaseController -from keystone.logic.extension_reader import ExtensionsReader -from keystone.contrib.extensions.admin import EXTENSION_ADMIN_PREFIX -from keystone.contrib.extensions.service import EXTENSION_SERVICE_PREFIX - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class ExtensionsController(BaseController): - """Controller for extensions related methods""" - - def __init__(self, is_service_operation=None): - super(ExtensionsController, self).__init__() - if is_service_operation: - self.extension_prefix = EXTENSION_SERVICE_PREFIX - else: - self.extension_prefix = EXTENSION_ADMIN_PREFIX - self.extension_reader = ExtensionsReader(self.extension_prefix) - - @utils.wrap_error - def get_extensions_info(self, req): - return utils.send_result(200, req, - self.extension_reader.get_extensions()) diff --git a/keystone/controllers/roles.py b/keystone/controllers/roles.py deleted file mode 100644 index 0cc516f79c..0000000000 --- a/keystone/controllers/roles.py +++ /dev/null @@ -1,101 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010-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. - - -""" -Roles Controller - -""" -import logging - -from keystone import utils -from keystone.controllers.base_controller import BaseController -from keystone.models import Role -from keystone.logic import service - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class RolesController(BaseController): - """Controller for Role related operations""" - - def __init__(self): - self.identity_service = service.IdentityService() - - # Not exposed yet. - @utils.wrap_error - def create_role(self, req): - role = utils.get_normalized_request_content(Role, req) - return utils.send_result(201, req, - self.identity_service.create_role(utils.get_auth_token(req), role)) - - @utils.wrap_error - def delete_role(self, req, role_id): - rval = self.identity_service.delete_role( - utils.get_auth_token(req), role_id) - return utils.send_result(204, req, rval) - - @utils.wrap_error - def get_roles(self, req): - role_name = req.GET["name"] if "name" in req.GET else None - if role_name: - return self.__get_roles_by_name(req, role_name) - else: - return self.__get_all_roles(req) - - def __get_roles_by_name(self, req, role_name): - tenant = self.identity_service.get_role_by_name( - utils.get_auth_token(req), role_name) - return utils.send_result(200, req, tenant) - - def __get_all_roles(self, req): - service_id = req.GET["serviceId"] if "serviceId" in req.GET else None - marker, limit, url = self.get_marker_limit_and_url(req) - if service_id: - roles = self.identity_service.get_roles_by_service( - utils.get_auth_token(req), marker, limit, url, - service_id) - return utils.send_result(200, req, roles) - else: - roles = self.identity_service.get_roles( - utils.get_auth_token(req), marker, limit, url) - return utils.send_result(200, req, roles) - - @utils.wrap_error - def get_role(self, req, role_id): - role = self.identity_service.get_role(utils.get_auth_token(req), - role_id) - return utils.send_result(200, req, role) - - @utils.wrap_error - def add_role_to_user(self, req, user_id, role_id, tenant_id=None): - self.identity_service.add_role_to_user(utils.get_auth_token(req), - user_id, role_id, tenant_id) - return utils.send_result(201, None) - - @utils.wrap_error - def delete_role_from_user(self, req, user_id, role_id, tenant_id=None): - self.identity_service.remove_role_from_user(utils.get_auth_token(req), - user_id, role_id, tenant_id) - return utils.send_result(204, req, None) - - @utils.wrap_error - def get_user_roles(self, req, user_id, tenant_id=None): - marker, limit, url = self.get_marker_limit_and_url(req) - roles = self.identity_service.get_user_roles( - utils.get_auth_token(req), marker, limit, url, user_id, tenant_id) - return utils.send_result(200, req, roles) diff --git a/keystone/controllers/services.py b/keystone/controllers/services.py deleted file mode 100755 index 9d1c0f4f9d..0000000000 --- a/keystone/controllers/services.py +++ /dev/null @@ -1,69 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010-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. - - -""" -Services Controller - -""" -import logging - -from keystone import utils -from keystone.controllers.base_controller import BaseController -from keystone.models import Service -from keystone.logic import service - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class ServicesController(BaseController): - """Controller for Service related operations""" - - def __init__(self): - self.identity_service = service.IdentityService() - - @utils.wrap_error - def create_service(self, req): - service = utils.get_normalized_request_content(Service, req) - return utils.send_result(201, req, - self.identity_service.create_service(utils.get_auth_token(req), - service)) - - @utils.wrap_error - def get_services(self, req): - service_name = req.GET["name"] if "name" in req.GET else None - if service_name: - tenant = self.identity_service.get_service_by_name( - utils.get_auth_token(req), service_name) - return utils.send_result(200, req, tenant) - else: - marker, limit, url = self.get_marker_limit_and_url(req) - services = self.identity_service.get_services( - utils.get_auth_token(req), marker, limit, url) - return utils.send_result(200, req, services) - - @utils.wrap_error - def get_service(self, req, service_id): - service = self.identity_service.get_service( - utils.get_auth_token(req), service_id) - return utils.send_result(200, req, service) - - @utils.wrap_error - def delete_service(self, req, service_id): - rval = self.identity_service.delete_service(utils.get_auth_token(req), - service_id) - return utils.send_result(204, req, rval) diff --git a/keystone/controllers/staticfiles.py b/keystone/controllers/staticfiles.py deleted file mode 100644 index f6c7918632..0000000000 --- a/keystone/controllers/staticfiles.py +++ /dev/null @@ -1,89 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010-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. - - -""" -Static Files Controller - -Serves static files like PDF, WADL, etc... -""" -import logging -import os -from webob import Response - -from keystone import utils -from keystone.common import template -from keystone.controllers.base_controller import BaseController - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class StaticFilesController(BaseController): - """Controller for contract documents""" - @staticmethod - @utils.wrap_error - def get_pdf_contract(req, pdf, root="content/"): - resp = Response() - filepath = root + pdf - return template.static_file(resp, req, filepath, - root=utils.get_app_root(), mimetype="application/pdf") - - @staticmethod - @utils.wrap_error - def get_wadl_contract(req, wadl, root): - resp = Response() - return template.static_file(resp, req, root + wadl, - root=utils.get_app_root(), mimetype="application/vnd.sun.wadl+xml") - - @staticmethod - @utils.wrap_error - def get_xsd_contract(req, xsd, root="content/"): - resp = Response() - return template.static_file(resp, req, root + "xsd/" + xsd, - root=utils.get_app_root(), mimetype="application/xml") - - @staticmethod - @utils.wrap_error - def get_xsd_atom_contract(req, xsd, root="content/"): - resp = Response() - return template.static_file(resp, req, root + "xsd/atom/" + xsd, - root=utils.get_app_root(), mimetype="application/xml") - - @staticmethod - @utils.wrap_error - def get_static_file(req, path, file, mimetype=None, root="content/"): - resp = Response() - - if mimetype is None: - if utils.is_xml_response(req): - mimetype = "application/xml" - elif utils.is_json_response(req): - mimetype = "application/json" - else: - logger.debug("Unhandled mime type: %s" % req.content_type) - - basename, extension = os.path.splitext(file) - resp_file = "%s%s%s" % (root, path, file) - if extension is None or extension == '': - if mimetype == "application/xml": - resp_file = "%s.xml" % resp_file - elif mimetype == "application/json": - resp_file = "%s.json" % resp_file - - logger.debug("Returning contents from file '%s'" % resp_file) - return template.static_file(resp, req, resp_file, - root=utils.get_app_root(), mimetype=mimetype) diff --git a/keystone/controllers/tenant.py b/keystone/controllers/tenant.py deleted file mode 100644 index 562998c7c1..0000000000 --- a/keystone/controllers/tenant.py +++ /dev/null @@ -1,81 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010-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. - - -""" -Tenant Controller - -""" -import logging - -from keystone import utils -from keystone.controllers.base_controller import BaseController -from keystone.logic import service -from keystone.models import Tenant - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class TenantController(BaseController): - """Controller for Tenant related operations""" - - def __init__(self, is_service_operation=None): - self.identity_service = service.IdentityService() - self.is_service_operation = is_service_operation - logger.debug("Initializing: 'Service API' mode=%s" % - self.is_service_operation) - - @utils.wrap_error - def create_tenant(self, req): - tenant = utils.get_normalized_request_content(Tenant, req) - return utils.send_result(201, req, - self.identity_service.create_tenant(utils.get_auth_token(req), - tenant)) - - @utils.wrap_error - def get_tenants(self, req): - tenant_name = req.GET["name"] if "name" in req.GET else None - if tenant_name: - tenant = self.identity_service.get_tenant_by_name( - utils.get_auth_token(req), - tenant_name) - return utils.send_result(200, req, tenant) - else: - marker, limit, url = self.get_marker_limit_and_url(req) - tenants = self.identity_service.get_tenants( - utils.get_auth_token(req), marker, limit, url, - self.is_service_operation) - return utils.send_result(200, req, tenants) - - @utils.wrap_error - def get_tenant(self, req, tenant_id): - tenant = self.identity_service.get_tenant(utils.get_auth_token(req), - tenant_id) - return utils.send_result(200, req, tenant) - - @utils.wrap_error - def update_tenant(self, req, tenant_id): - tenant = utils.get_normalized_request_content(Tenant, req) - rval = self.identity_service.update_tenant(utils.get_auth_token(req), - tenant_id, tenant) - return utils.send_result(200, req, rval) - - @utils.wrap_error - def delete_tenant(self, req, tenant_id): - rval = self.identity_service.delete_tenant(utils.get_auth_token(req), - tenant_id) - return utils.send_result(204, req, rval) diff --git a/keystone/controllers/token.py b/keystone/controllers/token.py deleted file mode 100644 index 1f2e36c89f..0000000000 --- a/keystone/controllers/token.py +++ /dev/null @@ -1,141 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010-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. - - -""" -Token Controller - -This module contains the TokenController class which receives token-related -calls from the request routers. - -""" -import logging - -from keystone import config -from keystone import utils -from keystone.controllers.base_controller import BaseController -from keystone.logic import extension_reader -from keystone.logic.types import auth -from keystone.logic.types import fault -from keystone.logic import service - -CONF = config.CONF -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class TokenController(BaseController): - """Controller for token related operations""" - - def __init__(self): - self.identity_service = service.IdentityService() - logger.debug("Token controller init with HP-IDM extension: %s" % \ - extension_reader.is_extension_supported('hpidm')) - - @utils.wrap_error - def authenticate(self, req): - credential_type = utils.detect_credential_type(req) - if credential_type == "passwordCredentials": - auth_with_credentials = utils.get_normalized_request_content( - auth.AuthWithPasswordCredentials, req) - result = self.identity_service.authenticate( - auth_with_credentials) - return utils.send_result(200, req, result) - elif credential_type == "token": - unscoped = utils.get_normalized_request_content( - auth.AuthWithUnscopedToken, req) - result = self.identity_service.\ - authenticate_with_unscoped_token(unscoped) - return utils.send_result(200, req, result) - elif credential_type == "OS-KSEC2:ec2Credentials": - return self._authenticate_ec2(req) - elif credential_type == "OS-KSS3:s3Credentials": - return self._authenticate_s3(req) - elif credential_type in ["ec2Credentials", "OS-KSEC2-ec2Credentials"]: - logger.warning('Received EC2 credentials in %s format. Processing ' - 'may fail. Update the client code sending this ' - 'format' % credential_type) - return self._authenticate_ec2(req) - else: - raise fault.BadRequestFault("Invalid credentials %s" % - credential_type) - - @utils.wrap_error - def authenticate_ec2(self, req): - return self._authenticate_ec2(req) - - def _authenticate_ec2(self, req): - """Undecorated EC2 handler""" - creds = utils.get_normalized_request_content(auth.Ec2Credentials, req) - return utils.send_result(200, req, - self.identity_service.authenticate_ec2(creds)) - - @utils.wrap_error - def authenticate_s3(self, req): - return self._authenticate_s3(req) - - def _authenticate_s3(self, req): - """Undecorated S3 handler""" - creds = utils.get_normalized_request_content(auth.S3Credentials, req) - return utils.send_result(200, req, - self.identity_service.authenticate_s3(creds)) - - def _validate_token(self, req, token_id): - """Validates the token, and that it belongs to the specified tenant""" - belongs_to = req.GET.get('belongsTo') - service_ids = None - if extension_reader.is_extension_supported('hpidm'): - # service IDs are only relevant if hpidm extension is enabled - service_ids = req.GET.get('HP-IDM-serviceId') - return self.identity_service.validate_token( - utils.get_auth_token(req), token_id, belongs_to, service_ids) - - @utils.wrap_error - def validate_token(self, req, token_id): - if CONF.disable_tokens_in_url: - fault.ServiceUnavailableFault() - else: - result = self._validate_token(req, token_id) - return utils.send_result(200, req, result) - - @utils.wrap_error - def check_token(self, req, token_id): - """Validates the token, but only returns a status code (HEAD)""" - if CONF.disable_tokens_in_url: - fault.ServiceUnavailableFault() - else: - self._validate_token(req, token_id) - return utils.send_result(200, req) - - @utils.wrap_error - def delete_token(self, req, token_id): - if CONF.disable_tokens_in_url: - fault.ServiceUnavailableFault() - else: - return utils.send_result(204, req, - self.identity_service.revoke_token( - utils.get_auth_token(req), token_id)) - - @utils.wrap_error - def endpoints(self, req, token_id): - if CONF.disable_tokens_in_url: - fault.ServiceUnavailableFault() - else: - marker, limit, url = self.get_marker_limit_and_url(req) - return utils.send_result(200, req, - self.identity_service.get_endpoints_for_token( - utils.get_auth_token(req), - token_id, marker, limit, url)) diff --git a/keystone/controllers/user.py b/keystone/controllers/user.py deleted file mode 100644 index c7f37f646e..0000000000 --- a/keystone/controllers/user.py +++ /dev/null @@ -1,105 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010-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. - - -""" -User Controller - -""" -import logging - -from keystone import utils -from keystone.controllers.base_controller import BaseController -from keystone.logic import service -from keystone.logic.types.user import User, User_Update - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class UserController(BaseController): - """Controller for User related operations""" - - def __init__(self): - self.identity_service = service.IdentityService() - - @utils.wrap_error - def create_user(self, req): - u = utils.get_normalized_request_content(User, req) - return utils.send_result(201, req, self.identity_service.create_user( - utils.get_auth_token(req), u)) - - @utils.wrap_error - def get_users(self, req): - user_name = req.GET["name"] if "name" in req.GET else None - if user_name: - tenant = self.identity_service.get_user_by_name( - utils.get_auth_token(req), - user_name) - return utils.send_result(200, req, tenant) - else: - marker, limit, url = self.get_marker_limit_and_url(req) - users = self.identity_service.get_users(utils.get_auth_token(req), - marker, limit, url) - return utils.send_result(200, req, users) - - @utils.wrap_error - def get_user(self, req, user_id): - user = self.identity_service.get_user(utils.get_auth_token(req), - user_id) - return utils.send_result(200, req, user) - - @utils.wrap_error - def update_user(self, req, user_id): - user = utils.get_normalized_request_content(User_Update, req) - rval = self.identity_service.update_user(utils.get_auth_token(req), - user_id, user) - return utils.send_result(200, req, rval) - - @utils.wrap_error - def delete_user(self, req, user_id): - rval = self.identity_service.delete_user(utils.get_auth_token(req), - user_id) - return utils.send_result(204, req, rval) - - @utils.wrap_error - def set_user_password(self, req, user_id): - user = utils.get_normalized_request_content(User_Update, req) - rval = self.identity_service.set_user_password( - utils.get_auth_token(req), user_id, user) - return utils.send_result(200, req, rval) - - @utils.wrap_error - def set_user_enabled(self, req, user_id): - user = utils.get_normalized_request_content(User_Update, req) - rval = self.identity_service.enable_disable_user( - utils.get_auth_token(req), user_id, user) - return utils.send_result(200, req, rval) - - @utils.wrap_error - def update_user_tenant(self, req, user_id): - user = utils.get_normalized_request_content(User_Update, req) - rval = self.identity_service.set_user_tenant(utils.get_auth_token(req), - user_id, user) - return utils.send_result(200, req, rval) - - @utils.wrap_error - def get_tenant_users(self, req, tenant_id): - marker, limit, url = self.get_marker_limit_and_url(req) - role_id = req.GET["roleId"] if "roleId" in req.GET else None - users = self.identity_service.get_tenant_users( - utils.get_auth_token(req), tenant_id, role_id, marker, limit, url) - return utils.send_result(200, req, users) diff --git a/keystone/controllers/version.py b/keystone/controllers/version.py deleted file mode 100644 index b494c5f5e9..0000000000 --- a/keystone/controllers/version.py +++ /dev/null @@ -1,116 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010-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. - - -""" -Version Controller - -""" -import logging -import os -from webob import Response - -from keystone import utils -from keystone import version -from keystone.common import template -from keystone.controllers.base_controller import BaseController - -# Calculate root path (to get to static files) -POSSIBLE_TOPDIR = os.path.normpath(os.path.join(os.path.dirname(__file__), - os.pardir, - os.pardir)) - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class VersionController(BaseController): - """Controller for version related methods""" - @utils.wrap_error - def get_version_info(self, req, file="version"): - resp = Response() - resp.charset = 'UTF-8' - if utils.is_xml_response(req): - resp_file = os.path.join(POSSIBLE_TOPDIR, - "keystone/content/%s.xml.tpl" % file) - resp.content_type = "application/xml" - elif utils.is_atom_response(req): - resp_file = os.path.join(POSSIBLE_TOPDIR, - "keystone/content/%s.atom.tpl" % file) - resp.content_type = "application/atom+xml" - else: - resp_file = os.path.join(POSSIBLE_TOPDIR, - "keystone/content/%s.json.tpl" % file) - resp.content_type = "application/json" - - hostname = req.environ.get("SERVER_NAME") - port = req.environ.get("SERVER_PORT") - if 'HTTPS' in req.environ: - protocol = 'https' - else: - protocol = 'http' - - resp.unicode_body = template.template(resp_file, - PROTOCOL=protocol, - HOST=hostname, - PORT=port, - API_VERSION=version.API_VERSION, - API_VERSION_STATUS=version.API_VERSION_STATUS, - API_VERSION_DATE=version.API_VERSION_DATE) - - return resp - - @utils.wrap_error - def get_multiple_choice(self, req, file="multiple_choice", path=None): - """ Returns a multiple-choices response based on API spec - - Response will include in it only one choice, which is for the - current API version. The response is a 300 Multiple Choice - response with either an XML or JSON body. - - """ - if path is None: - path = '' - logger.debug("300 Multiple Choices response: %s" % path) - resp = Response(status="300 Multiple Choices") - resp.charset = 'UTF-8' - if utils.is_xml_response(req): - resp_file = os.path.join(POSSIBLE_TOPDIR, - "keystone/content/%s.xml.tpl" % file) - resp.content_type = "application/xml" - else: - resp_file = os.path.join(POSSIBLE_TOPDIR, - "keystone/content/%s.json.tpl" % file) - resp.content_type = "application/json" - - hostname = req.environ.get("SERVER_NAME") - port = req.environ.get("SERVER_PORT") - if 'HTTPS' in req.environ: - protocol = 'https' - else: - protocol = 'http' - - resp.unicode_body = template.template(resp_file, - PROTOCOL=protocol, - HOST=hostname, - PORT=port, - API_VERSION=version.API_VERSION, - API_VERSION_STATUS=version.API_VERSION_STATUS, - API_VERSION_DATE=version.API_VERSION_DATE, - RESOURCE_PATH=path - ) - - return resp diff --git a/keystone/exception.py b/keystone/exception.py new file mode 100644 index 0000000000..c2f32afa30 --- /dev/null +++ b/keystone/exception.py @@ -0,0 +1,58 @@ +import re + + +class Error(StandardError): + """Base error class. + + Child classes should define an HTTP status code, title, and a doc string. + + """ + code = None + title = None + + def __init__(self, message=None, **kwargs): + """Use the doc string as the error message by default.""" + message = message or self.__doc__ % kwargs + super(Error, self).__init__(message) + + def __str__(self): + """Cleans up line breaks and indentation from doc strings.""" + string = super(Error, self).__str__() + string = re.sub('[ \n]+', ' ', string) + string = string.strip() + return string + + +class ValidationError(Error): + """Expecting to find %(attribute)s in %(target)s. + + The server could not comply with the request since it is either malformed + or otherwise incorrect. + + The client is assumed to be in error. + + """ + code = 400 + title = 'Bad Request' + + +class Unauthorized(Error): + """The request you have made requires authentication.""" + code = 401 + title = 'Not Authorized' + + +class Forbidden(Error): + """You are not authorized to perform the requested action: %(action)s""" + code = 403 + title = 'Not Authorized' + + +class NotFound(Error): + """Could not find: %(target)s""" + code = 404 + title = 'Not Found' + + +class TokenNotFound(NotFound): + """Could not find token: %(token_id)s""" diff --git a/keystone/frontends/__init__.py b/keystone/frontends/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/keystone/frontends/d5_compat.py b/keystone/frontends/d5_compat.py deleted file mode 100644 index 46395db792..0000000000 --- a/keystone/frontends/d5_compat.py +++ /dev/null @@ -1,455 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010-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. - -""" -D5 API Compatibility Module - -This WSGI component adds support for the D5 API contract. That contract was -an unofficial contract that made it into live deployments in the wild, so -this middleware is an attempt to support production deployemnts of that -code and allow them to interoperate with Keystone trunk while gradually moving -to updated Keystone code. - -The middleware transforms responses in this way: -- POST /tokens that come in D5 format (not wrapped in "auth":{}) will receive - a D5 formatted response (wrapped in "auth":{} instead of "access":{}) -- GET /tokens/{id} will respond with both an "auth" and an "access" wrapper - (since we can't tell if the caller is expecting a D5 or Diablo final - response) - -Notes: -- GET /tokens will not repond in D5 syntax in XML (because only one root - can exist in XML and I chose not to break Diablo) -- This relies on the URL normalizer (middlewre/url.py) to set - KEYSTONE_API_VERSION. Without that set to '2.0', this middleware does - nothing -""" - -import copy -import json -import logging -logger = logging.getLogger(__name__) # pylint: disable=C0103 -try: - from lxml import etree -except ImportError as exc: - logging.exception(exc) - raise exc -from webob.exc import Request - -from keystone.logic.types import fault -import keystone.utils as utils - -PROTOCOL_NAME = "D5 API Compatibility" - - -class D5AuthBase(object): - """ Handles validating json and XML syntax of auth requests """ - - def __init__(self, tenant_id=None, tenant_name=None): - self.tenant_id = tenant_id - self.tenant_name = tenant_name - - @staticmethod - def _validate_auth(obj, *valid_keys): - root = obj.keys()[0] - - for key in root: - if not key in valid_keys: - logger.debug("Invalid attribute: " % key) - raise fault.BadRequestFault('Invalid attribute(s): %s' % key) - - if root.get('tenantId') and root.get('tenantName'): - msg = _('Expecting either Tenant ID or Tenant Name, but not both') - logger.debug(msg) - raise fault.BadRequestFault(msg) - - return root - - @staticmethod - def _validate_key(obj, key, required_keys, optional_keys): - if not key in obj: - raise fault.BadRequestFault('Expecting %s' % key) - - ret = obj[key] - - for skey in ret: - if not skey in required_keys and not skey in optional_keys: - msg = _('Invalid attribute(s): %s' % skey) - logger.debug(msg) - raise fault.BadRequestFault(msg) - - for required_key in required_keys: - if not ret.get(required_key): - msg = _('Expecting %s:%s' % (key, required_key)) - logger.debug(msg) - raise fault.BadRequestFault(msg) - return ret - - -class D5AuthWithPasswordCredentials(D5AuthBase): - def __init__(self, username, password, tenant_id=None, tenant_name=None): - super(D5AuthWithPasswordCredentials, self).__init__(tenant_id, - tenant_name) - self.username = username - self.password = password - - @staticmethod - def from_xml(xml_str): - try: - dom = etree.Element("root") - dom.append(etree.fromstring(xml_str)) - password_credentials = \ - dom.find("{http://docs.openstack.org/identity/api/v2.0}" - "passwordCredentials") - if password_credentials is None: - raise fault.BadRequestFault("Expecting passwordCredentials") - tenant_id = password_credentials.get("tenantId") - tenant_name = password_credentials.get("tenantName") - username = password_credentials.get("username") - utils.check_empty_string(username, "Expecting a username") - password = password_credentials.get("password") - utils.check_empty_string(password, "Expecting a password") - - if tenant_id and tenant_name: - msg = _("Expecting either Tenant ID or Tenant Name, but not " - "both") - logger.debug(msg) - raise fault.BadRequestFault(msg) - - return D5AuthWithPasswordCredentials(username, password, - tenant_id, tenant_name) - except etree.LxmlError as e: - msg = _("Cannot parse passwordCredentials") - logger.debug(msg) - raise fault.BadRequestFault(msg, str(e)) - - @staticmethod - def from_json(json_str): - try: - obj = json.loads(json_str) - - cred = D5AuthBase._validate_key(obj, 'passwordCredentials', - required_keys=['username', 'password'], - optional_keys=['tenantId', 'tenantName']) - - return D5AuthWithPasswordCredentials(cred['username'], - cred['password'], - cred.get('tenantId'), - cred.get('tenantName')) - except (ValueError, TypeError) as e: - msg = _("Cannot parse passwordCredentials") - logger.debug(msg) - raise fault.BadRequestFault(msg, str(e)) - - def to_json(self): - """ Format the response in Diablo/Stable contract format """ - data = {"auth": {"passwordCredentials": { - "username": self.username, - "password": self.password}}} - if self.tenant_id: - data["auth"]["tenantId"] = self.tenant_id - else: - if self.tenant_name: - data["auth"]["tenant_name"] = self.tenant_name - return json.dumps(data) - - def to_xml(self): - """ Format the response in Diablo/Stable contract format """ - dom = etree.Element("auth", - xmlns="http://docs.openstack.org/identity/api/v2.0") - - password_credentials = etree.Element("passwordCredentials", - username=self.username, - password=self.password) - - if self.tenant_id: - dom.set("tenantId", self.tenant_id) - else: - if self.tenant_name: - dom.set("tenant_name", self.tenant_name) - - dom.append(password_credentials) - - return etree.tostring(dom) - - -class D5toDiabloAuthData(object): - """Authentation Information returned upon successful login. - - This class handles rendering to JSON and XML. It renders - the token, the user data, the roles, and the service catalog. - """ - xml = None - json = None - - def __init__(self, init_json=None, init_xml=None): - if init_json: - logger.debug("init D5toDiabloAuthData with json") - self.json = init_json - if init_xml is not None: - logger.debug("init D5toDiabloAuthData with xml") - self.xml = init_xml - - @staticmethod - def from_xml(xml_str): - """ Verify Diablo syntax and return class initialized with data""" - try: - dom = etree.Element("root") - dom.append(etree.fromstring(xml_str)) - root = \ - dom.find("{http://docs.openstack.org/identity/api/v2.0}" - "access") - if root is None: - logger.debug("Expecting access") - raise fault.BadRequestFault("Expecting access") - return D5toDiabloAuthData(init_xml=root) - except etree.LxmlError as e: - msg = _("Cannot parse Diablo response") - logger.debug(msg) - raise fault.BadRequestFault(msg, str(e)) - - @staticmethod - def from_json(json_str): - """ Verify Diablo syntax and return class initialized with data""" - try: - obj = json.loads(json_str) - auth = obj["access"] - return D5toDiabloAuthData(init_json=auth) - except (ValueError, TypeError) as e: - msg = _("Cannot parse auth response") - logger.debug(msg) - raise fault.BadRequestFault(msg, str(e)) - - def to_xml(self): - """ Convert to D5 syntax from Diablo""" - if self.xml is None: - if self.json is None: - logger.debug("Cannot deserialize response since no json or xml" - "data seems to have been passed in") - raise NotImplementedError - else: - msg = _("%s initialized with json, but xml requested" % - self.__class__.__str__) - logger.debug(msg) - raise fault.IdentityFault(msg) - dom = etree.Element("auth", - xmlns="http://docs.openstack.org/identity/api/v2.0") - for element in self.xml: - dom.append(element) - return etree.tostring(dom) - - def to_json(self): - """ Convert to D5 syntax from Diablo""" - if self.json is None: - if self.xml is None: - logger.debug("Cannot deserialize response since no json or xml" - "data seems to have been passed in") - raise NotImplementedError - else: - msg = _("%s initialized with xml, but json requested" % - self.__class__.__str__) - logger.debug(msg) - raise fault.IdentityFault(msg) - d5_data = {"auth": self.json.copy()} - if 'auth' in d5_data and 'serviceCatalog' in d5_data['auth']: - d5_data['auth']['serviceCatalog'] = \ - dict([(s['type'], s['endpoints']) - for s in d5_data['auth']['serviceCatalog']]) - - return json.dumps(d5_data) - - -class D5ValidateData(object): - """Authentation Information returned upon successful token validation.""" - xml = None - json = None - - def __init__(self, init_json=None, init_xml=None): - if init_json: - self.json = init_json - if init_xml is not None: - self.xml = init_xml - - @staticmethod - def from_xml(xml_str): - """ Verify Diablo syntax and return class initialized with data""" - try: - dom = etree.Element("root") - dom.append(etree.fromstring(xml_str)) - root = \ - dom.find("{http://docs.openstack.org/identity/api/v2.0}" - "access") - if root is None: - logger.debug("Expecting 'access' element") - raise fault.BadRequestFault("Expecting access") - return D5ValidateData(init_xml=root) - except etree.LxmlError as e: - msg = _("Cannot parse Diablo response") - logger.debug(msg) - raise fault.BadRequestFault(msg, str(e)) - - @staticmethod - def from_json(json_str): - """ Verify Diablo syntax and return class initialized with data""" - try: - obj = json.loads(json_str) - return D5ValidateData(init_json=obj) - except (ValueError, TypeError) as e: - msg = _("Cannot parse Diablo response") - logger.debug(msg) - raise fault.BadRequestFault(msg, str(e)) - - def to_xml(self): - """ Returns only Diablo syntax (can only have one root in XML) - - This middleware is designed to provide D5 compatibility but NOT - at the expense of breaking the Diablo contract.""" - if self.xml is None: - if self.json is None: - logger.debug("Cannot deserialize response since no json or xml" - "data seems to have been passed in") - raise NotImplementedError - else: - msg = _("%s initialized with json, but xml requested" % - self.__class__.__str__) - logger.debug(msg) - raise fault.IdentityFault(msg) - return etree.tostring(self.xml) - - def to_json(self): - """ Returns both Diablo and D5 syntax ("access" and "auth")""" - if self.json is None: - if self.xml is None: - logger.debug("Cannot deserialize response since no json or xml" - "data seems to have been passed in") - raise NotImplementedError - else: - msg = _("%s initialized with xml, but json requested" % - self.__class__.__str__) - logger.debug(msg) - raise fault.IdentityFault(msg) - d5_data = self.json.copy() - auth = {} - for key, value in self.json["access"].iteritems(): - auth[key] = copy.copy(value) - if "user" in auth: - # D5 returns 'username' only - user = auth["user"] - user["username"] = user["name"] - del user["name"] - del user["id"] - - # D5 has 'tenantId' under token - token = auth["token"] - if 'tenant' in token: - tenant = token["tenant"] - token["tenantId"] = tenant["id"] - - if "roles" in auth["user"]: - auth["user"]["roleRefs"] = [] - rolerefs = auth["user"]["roleRefs"] - for role in auth["user"]["roles"]: - ref = {} - ref["id"] = role["id"] - ref["roleId"] = role["name"] - if "tenantId" in role: - ref["tenantId"] = role["tenantId"] - rolerefs.append(ref) - del auth["user"]["roles"] - d5_data["auth"] = auth - - return json.dumps(d5_data) - - -class D5AuthProtocol(object): - """D5 Cmpatibility Middleware that transforms client calls and responses""" - - def __init__(self, app, conf): - """ Common initialization code """ - msg = _("Starting the %s component" % PROTOCOL_NAME) - logger.info(msg) - self.conf = conf - self.app = app - - def __call__(self, env, start_response): - """ Handle incoming request. Transform. And send downstream. """ - logger.debug("Entering D5AuthProtocol.__call__") - request = Request(env) - if 'KEYSTONE_API_VERSION' in env and \ - env['KEYSTONE_API_VERSION'] == '2.0': - if request.path.startswith("/tokens"): - is_d5_request = False - if request.method == "POST": - try: - auth_with_credentials = \ - utils.get_normalized_request_content( - D5AuthWithPasswordCredentials, request) - # Convert request body to Diablo syntax - if request.content_type == "application/xml": - request.body = auth_with_credentials.to_xml() - else: - request.body = auth_with_credentials.to_json() - is_d5_request = True - logger.warn("Detected a D5-formatted call") - except: - pass - - if is_d5_request: - response = request.get_response(self.app) - #Handle failures. - if not str(response.status).startswith('20'): - return response(env, start_response) - logger.warn("Responding in D5-format") - auth_data = utils.get_normalized_request_content( - D5toDiabloAuthData, response) - resp = utils.send_result(response.status_int, request, - auth_data) - return resp(env, start_response) - else: - # Pass through - return self.app(env, start_response) - - elif request.method == "GET": - if request.path.endswith("/endpoints"): - # Pass through - return self.app(env, start_response) - else: - response = request.get_response(self.app) - #Handle failures. - if not str(response.status).startswith('20'): - return response(env, start_response) - logger.warn("Adding D5-format to call validate call") - validate_data = utils.get_normalized_request_content( - D5ValidateData, response) - resp = utils.send_result(response.status_int, request, - validate_data) - return resp(env, start_response) - - # All other calls pass to downstream WSGI component - return self.app(env, start_response) - - -def filter_factory(global_conf, **local_conf): - """Returns a WSGI filter app for use with paste.deploy.""" - conf = global_conf.copy() - conf.update(local_conf) - - def auth_filter(wsgiapp): - """Closure to return""" - return D5AuthProtocol(wsgiapp, conf) - return auth_filter diff --git a/keystone/frontends/legacy_token_auth.py b/keystone/frontends/legacy_token_auth.py deleted file mode 100644 index a63395d56c..0000000000 --- a/keystone/frontends/legacy_token_auth.py +++ /dev/null @@ -1,126 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010-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. - -""" -RACKSPACE LEGACY AUTH - STUB - -This WSGI component -- transforms rackspace auth header credentials to keystone credentials -and makes an authentication call on keystone.- transforms response it -receives into custom headers defined in properties and returns -the response. -""" - -import ast -import json -import logging -from webob.exc import Request - -import keystone.utils as utils - -PROTOCOL_NAME = "Legacy Authentication" - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class AuthProtocol(object): - """Legacy Auth Middleware that handles authenticating client calls""" - - def __init__(self, app, conf): - """ Common initialization code """ - msg = _("Starting the %s component" % PROTOCOL_NAME) - logger.info(msg) - self.conf = conf - self.app = app - - # Handle 1.0 and 1.1 calls via middleware. - # Right now I am treating every call of 1.0 and 1.1 as call - # to authenticate - def __call__(self, env, start_response): - """ Handle incoming request. Transform. And send downstream. """ - logger.debug("Entering AuthProtocol.__call__") - request = Request(env) - if env.get('KEYSTONE_API_VERSION') in ['1.0', '1.1']: - logger.debug("This is a v%s call, so taking over" % - env.get('KEYSTONE_API_VERSION')) - params = {"auth": {"passwordCredentials": - {"username": utils.get_auth_user(request), - "password": utils.get_auth_key(request)}}} - #Make request to keystone - new_request = Request.blank('/tokens') - new_request.method = 'POST' - new_request.headers['Content-type'] = 'application/json' - new_request.accept = 'application/json' - new_request.body = json.dumps(params) - logger.debug("Sending v2.0-formatted request downstream") - response = new_request.get_response(self.app) - logger.debug("Got back %s" % response.status) - #Handle failures. - if not str(response.status).startswith('20'): - return response(env, start_response) - headers = self.__transform_headers( - json.loads(response.body)) - logger.debug("Transformed the response. Responding to v1.x client") - resp = utils.send_legacy_result(204, headers) - return resp(env, start_response) - else: - logger.debug("Not a v1.0/v1.1 call, so passing downstream") - return self.app(env, start_response) - - def __transform_headers(self, content): - """Transform Keystone auth to legacy headers""" - headers = {} - if "access" in content: - auth = content["access"] - if "token" in auth: - headers["X-Auth-Token"] = auth["token"]["id"] - if "serviceCatalog" in auth: - services = auth["serviceCatalog"] - service_mappings = ast.literal_eval( - self.conf.get("service_header_mappings", - self.conf["service-header-mappings"])) - for service in services: - service_name = service["name"] - service_urls = '' - for endpoint in service["endpoints"]: - if len(service_urls) > 0: - service_urls += ',' - service_urls += endpoint["publicURL"] - if len(service_urls) > 0: - if service_mappings.get(service_name): - headers[service_mappings.get( - service_name)] = service_urls - else: - #For Services that are not mapped, - #use X- prefix followed by service name. - header = 'X-%s' % service_name.upper() - logger.debug("Adding header to response: %s=%s" % - (header, service_urls)) - headers[header] = service_urls - return headers - - -def filter_factory(global_conf, **local_conf): - """Returns a WSGI filter app for use with paste.deploy.""" - conf = global_conf.copy() - conf.update(local_conf) - - def auth_filter(app): - """Closure to return""" - return AuthProtocol(app, conf) - return auth_filter diff --git a/keystone/frontends/normalizer.py b/keystone/frontends/normalizer.py deleted file mode 100644 index 6c2246b57d..0000000000 --- a/keystone/frontends/normalizer.py +++ /dev/null @@ -1,212 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010 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. - - -""" -Auth Middleware that accepts URL query extension. - -This module can be installed as a filter in front of your service to -detect extension in the resource URI (e.g., foo/resource.xml) to -specify HTTP response body type. If an extension is specified, it -overwrites the Accept header in the request, if present. - -""" - -import logging -import webob.acceptparse -from webob import Request - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - -# Maps supported URL prefixes to API_VERSION -PATH_PREFIXES = { - '/v2.0': '2.0', - '/v1.1': '1.1', - '/v1.0': '1.0'} - -# Maps supported URL extensions to RESPONSE_ENCODING -PATH_SUFFIXES = { - '.json': 'json', - '.xml': 'xml', - '.atom': 'atom+xml'} - -# Maps supported Accept headers to RESPONSE_ENCODING and API_VERSION -ACCEPT_HEADERS = { - 'application/vnd.openstack.identity+json;version=2.0': ('json', '2.0'), - 'application/vnd.openstack.identity+xml;version=2.0': ('xml', '2.0'), - 'application/vnd.openstack.identity-v2.0+json': ('json', '2.0'), - 'application/vnd.openstack.identity-v2.0+xml': ('xml', '2.0'), - 'application/vnd.openstack.identity+json;version=1.1': ('json', '1.1'), - 'application/vnd.openstack.identity+xml;version=1.1': ('xml', '1.1'), - 'application/vnd.openstack.identity-v1.1+json': ('json', '1.1'), - 'application/vnd.openstack.identity-v1.1+xml': ('xml', '1.1'), - 'application/vnd.openstack.identity-v1.0+json': ('json', '1.0'), - 'application/vnd.openstack.identity-v1.0+xml': ('xml', '1.0'), - 'application/vnd.openstack.identity+json;version=1.0': ('json', '1.0'), - 'application/vnd.openstack.identity+xml;version=1.0': ('xml', '1.0'), - 'application/json': ('json', None), - 'application/xml': ('xml', None), - 'application/atom+xml': ('atom+xml', None)} - -DEFAULT_RESPONSE_ENCODING = 'json' -PROTOCOL_NAME = "URL Normalizer" - - -class NormalizingFilter(object): - """Middleware filter to handle URL and Accept header normalization""" - - def __init__(self, app, conf): - msg = "Starting the %s component" % PROTOCOL_NAME - logger.info(msg) - # app is the next app in WSGI chain - eventually the OpenStack service - self.app = app - self.conf = conf - - def __call__(self, env, start_response): - # Inspect the request for mime type and API version - env = normalize_accept_header(env) - env = normalize_path_prefix(env) - env = normalize_path_suffix(env) - env['PATH_INFO'] = normalize_starting_slash(env.get('PATH_INFO')) - env['PATH_INFO'] = normalize_trailing_slash(env['PATH_INFO']) - - # Fall back on defaults, if necessary - env['KEYSTONE_RESPONSE_ENCODING'] = env.get( - 'KEYSTONE_RESPONSE_ENCODING') or DEFAULT_RESPONSE_ENCODING - env['HTTP_ACCEPT'] = 'application/' + (env.get( - 'KEYSTONE_RESPONSE_ENCODING') or DEFAULT_RESPONSE_ENCODING) - - if 'KEYSTONE_API_VERSION' not in env: - # Version was not specified in path or headers - # return multiple choice unless the version controller can handle - # this request - if env['PATH_INFO'] not in ['/', '']: - logger.debug("Call without a version - returning 300. Path=%s" - % env.get('PATH_INFO', '')) - from keystone.controllers.version import VersionController - controller = VersionController() - response = controller.get_multiple_choice(req=Request(env), - file='multiple_choice') - return response(env, start_response) - - return self.app(env, start_response) - - -def normalize_accept_header(env): - """Matches the preferred Accept encoding to supported encodings. - - Sets KEYSTONE_RESPONSE_ENCODING and KEYSTONE_API_VERSION, if appropriate. - - Note:: webob.acceptparse ignores ';version=' values - """ - accept_value = env.get('HTTP_ACCEPT') - - if accept_value: - if accept_value in ACCEPT_HEADERS.keys(): - logger.debug("Found direct match for mimetype %s" % accept_value) - best_accept = accept_value - else: - try: - accept = webob.acceptparse.Accept(accept_value) - except TypeError: - logger.warn("Falling back to `webob` v1.1 and older support. " - "Check your version of webob") - accept = webob.acceptparse.Accept('Accept', accept_value) - - best_accept = accept.best_match(ACCEPT_HEADERS.keys()) - - if best_accept: - response_encoding, api_version = ACCEPT_HEADERS[best_accept] - logger.debug('%s header matched with %s (API=%s, TYPE=%s)', - accept_value, best_accept, api_version, - response_encoding) - - if response_encoding: - env['KEYSTONE_RESPONSE_ENCODING'] = response_encoding - - if api_version: - env['KEYSTONE_API_VERSION'] = api_version - else: - logger.debug('%s header could not be matched', accept_value) - - return env - - -def normalize_path_prefix(env): - """Handles recognized PATH_INFO prefixes. - - Looks for a version prefix on the PATH_INFO, sets KEYSTONE_API_VERSION - accordingly, and removes the prefix to normalize the request.""" - for prefix in PATH_PREFIXES.keys(): - if env['PATH_INFO'].startswith(prefix): - env['KEYSTONE_API_VERSION'] = PATH_PREFIXES[prefix] - env['PATH_INFO'] = env['PATH_INFO'][len(prefix):] - break - - return env - - -def normalize_path_suffix(env): - """Hnadles recognized PATH_INFO suffixes. - - Looks for a recognized suffix on the PATH_INFO, sets the - KEYSTONE_RESPONSE_ENCODING accordingly, and removes the suffix to normalize - the request.""" - for suffix in PATH_SUFFIXES.keys(): - if env['PATH_INFO'].endswith(suffix): - env['KEYSTONE_RESPONSE_ENCODING'] = PATH_SUFFIXES[suffix] - env['PATH_INFO'] = env['PATH_INFO'][:-len(suffix)] - break - - return env - - -def normalize_starting_slash(path_info): - """Removes a trailing slash from the given path, if any.""" - # Ensure the path at least contains a slash - if not path_info: - return '/' - - # Ensure the path starts with a slash - elif path_info[0] != '/': - return '/' + path_info - - # No need to change anything - else: - return path_info - - -def normalize_trailing_slash(path_info): - """Removes a trailing slash from the given path, if any.""" - # Remove trailing slash, unless it's the only char - if len(path_info) > 1 and path_info[-1] == '/': - return path_info[:-1] - - # No need to change anything - else: - return path_info - - -def filter_factory(global_conf, **local_conf): - """Returns a WSGI filter app for use with paste.deploy.""" - conf = global_conf.copy() - conf.update(local_conf) - - def ext_filter(app): - return NormalizingFilter(app, conf) - return ext_filter diff --git a/keystone/identity/__init__.py b/keystone/identity/__init__.py new file mode 100644 index 0000000000..3a86d5a512 --- /dev/null +++ b/keystone/identity/__init__.py @@ -0,0 +1 @@ +from keystone.identity.core import * diff --git a/keystone/backends/sqlalchemy/migrate_repo/versions/__init__.py b/keystone/identity/backends/__init__.py similarity index 100% rename from keystone/backends/sqlalchemy/migrate_repo/versions/__init__.py rename to keystone/identity/backends/__init__.py diff --git a/keystone/identity/backends/kvs.py b/keystone/identity/backends/kvs.py new file mode 100644 index 0000000000..7dfc863370 --- /dev/null +++ b/keystone/identity/backends/kvs.py @@ -0,0 +1,222 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +from keystone import identity +from keystone.common import kvs +from keystone.common import utils + + +def _filter_user(user_ref): + if user_ref: + user_ref = user_ref.copy() + user_ref.pop('password', None) + user_ref.pop('tenants', None) + return user_ref + + +def _ensure_hashed_password(user_ref): + pw = user_ref.get('password', None) + if pw is not None: + user_ref['password'] = utils.hash_password(pw) + return user_ref + + +class Identity(kvs.Base, identity.Driver): + # Public interface + def authenticate(self, user_id=None, tenant_id=None, password=None): + """Authenticate based on a user, tenant and password. + + Expects the user object to have a password field and the tenant to be + in the list of tenants on the user. + + """ + user_ref = self._get_user(user_id) + tenant_ref = None + metadata_ref = None + if (not user_ref + or not utils.check_password(password, user_ref.get('password'))): + raise AssertionError('Invalid user / password') + if tenant_id and tenant_id not in user_ref['tenants']: + raise AssertionError('Invalid tenant') + + tenant_ref = self.get_tenant(tenant_id) + if tenant_ref: + metadata_ref = self.get_metadata(user_id, tenant_id) + else: + metadata_ref = {} + return (_filter_user(user_ref), tenant_ref, metadata_ref) + + def get_tenant(self, tenant_id): + tenant_ref = self.db.get('tenant-%s' % tenant_id) + return tenant_ref + + def get_tenant_by_name(self, tenant_name): + tenant_ref = self.db.get('tenant_name-%s' % tenant_name) + return tenant_ref + + def _get_user(self, user_id): + user_ref = self.db.get('user-%s' % user_id) + return user_ref + + def _get_user_by_name(self, user_name): + user_ref = self.db.get('user_name-%s' % user_name) + return user_ref + + def get_user(self, user_id): + return _filter_user(self._get_user(user_id)) + + def get_user_by_name(self, user_name): + return _filter_user(self._get_user_by_name(user_name)) + + def get_metadata(self, user_id, tenant_id): + return self.db.get('metadata-%s-%s' % (tenant_id, user_id)) + + def get_role(self, role_id): + role_ref = self.db.get('role-%s' % role_id) + return role_ref + + def list_users(self): + user_ids = self.db.get('user_list', []) + return [self.get_user(x) for x in user_ids] + + def list_roles(self): + role_ids = self.db.get('role_list', []) + return [self.get_role(x) for x in role_ids] + + # These should probably be part of the high-level API + def add_user_to_tenant(self, tenant_id, user_id): + user_ref = self._get_user(user_id) + tenants = set(user_ref.get('tenants', [])) + tenants.add(tenant_id) + self.update_user(user_id, {'tenants': list(tenants)}) + + def remove_user_from_tenant(self, tenant_id, user_id): + user_ref = self._get_user(user_id) + tenants = set(user_ref.get('tenants', [])) + tenants.remove(tenant_id) + self.update_user(user_id, {'tenants': list(tenants)}) + + def get_tenants_for_user(self, user_id): + user_ref = self._get_user(user_id) + return user_ref.get('tenants', []) + + def get_roles_for_user_and_tenant(self, user_id, tenant_id): + metadata_ref = self.get_metadata(user_id, tenant_id) + if not metadata_ref: + metadata_ref = {} + return metadata_ref.get('roles', []) + + def add_role_to_user_and_tenant(self, user_id, tenant_id, role_id): + metadata_ref = self.get_metadata(user_id, tenant_id) + if not metadata_ref: + metadata_ref = {} + roles = set(metadata_ref.get('roles', [])) + roles.add(role_id) + metadata_ref['roles'] = list(roles) + self.update_metadata(user_id, tenant_id, metadata_ref) + + def remove_role_from_user_and_tenant(self, user_id, tenant_id, role_id): + metadata_ref = self.get_metadata(user_id, tenant_id) + if not metadata_ref: + metadata_ref = {} + roles = set(metadata_ref.get('roles', [])) + roles.remove(role_id) + metadata_ref['roles'] = list(roles) + self.update_metadata(user_id, tenant_id, metadata_ref) + + # CRUD + def create_user(self, user_id, user): + if self.get_user(user_id): + raise Exception('Duplicate id') + if self.get_user_by_name(user['name']): + raise Exception('Duplicate name') + user = _ensure_hashed_password(user) + self.db.set('user-%s' % user_id, user) + self.db.set('user_name-%s' % user['name'], user) + user_list = set(self.db.get('user_list', [])) + user_list.add(user_id) + self.db.set('user_list', list(user_list)) + return user + + def update_user(self, user_id, user): + if 'name' in user: + existing = self.db.get('user_name-%s' % user['name']) + if existing and user_id != existing['id']: + raise Exception('Duplicate name') + # get the old name and delete it too + old_user = self.db.get('user-%s' % user_id) + new_user = old_user.copy() + user = _ensure_hashed_password(user) + new_user.update(user) + new_user['id'] = user_id + self.db.delete('user_name-%s' % old_user['name']) + self.db.set('user-%s' % user_id, new_user) + self.db.set('user_name-%s' % new_user['name'], new_user) + return new_user + + def delete_user(self, user_id): + old_user = self.db.get('user-%s' % user_id) + self.db.delete('user_name-%s' % old_user['name']) + self.db.delete('user-%s' % user_id) + user_list = set(self.db.get('user_list', [])) + user_list.remove(user_id) + self.db.set('user_list', list(user_list)) + return None + + def create_tenant(self, tenant_id, tenant): + if self.get_tenant(tenant_id): + raise Exception('Duplicate id') + if self.get_tenant_by_name(tenant['name']): + raise Exception('Duplicate name') + self.db.set('tenant-%s' % tenant_id, tenant) + self.db.set('tenant_name-%s' % tenant['name'], tenant) + return tenant + + def update_tenant(self, tenant_id, tenant): + if 'name' in tenant: + existing = self.db.get('tenant_name-%s' % tenant['name']) + if existing and tenant_id != existing['id']: + raise Exception('Duplicate name') + # get the old name and delete it too + old_tenant = self.db.get('tenant-%s' % tenant_id) + new_tenant = old_tenant.copy() + new_tenant['id'] = tenant_id + self.db.delete('tenant_name-%s' % old_tenant['name']) + self.db.set('tenant-%s' % tenant_id, new_tenant) + self.db.set('tenant_name-%s' % new_tenant['name'], new_tenant) + return tenant + + def delete_tenant(self, tenant_id): + old_tenant = self.db.get('tenant-%s' % tenant_id) + self.db.delete('tenant_name-%s' % old_tenant['name']) + self.db.delete('tenant-%s' % tenant_id) + return None + + def create_metadata(self, user_id, tenant_id, metadata): + self.db.set('metadata-%s-%s' % (tenant_id, user_id), metadata) + return metadata + + def update_metadata(self, user_id, tenant_id, metadata): + self.db.set('metadata-%s-%s' % (tenant_id, user_id), metadata) + return metadata + + def delete_metadata(self, user_id, tenant_id): + self.db.delete('metadata-%s-%s' % (tenant_id, user_id)) + return None + + def create_role(self, role_id, role): + self.db.set('role-%s' % role_id, role) + role_list = set(self.db.get('role_list', [])) + role_list.add(role_id) + self.db.set('role_list', list(role_list)) + return role + + def update_role(self, role_id, role): + self.db.set('role-%s' % role_id, role) + return role + + def delete_role(self, role_id): + self.db.delete('role-%s' % role_id) + role_list = set(self.db.get('role_list', [])) + role_list.remove(role_id) + self.db.set('role_list', list(role_list)) + return None diff --git a/keystone/identity/backends/pam.py b/keystone/identity/backends/pam.py new file mode 100644 index 0000000000..d960660138 --- /dev/null +++ b/keystone/identity/backends/pam.py @@ -0,0 +1,29 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +from __future__ import absolute_import + +import pam + + +class PamIdentity(object): + """Very basic identity based on PAM. + + Tenant is always the same as User, root user has admin role. + """ + + def authenticate(self, username, password, **kwargs): + if pam.authenticate(username, password): + metadata = {} + if username == 'root': + metadata['is_admin'] == True + + tenant = {'id': username, + 'name': username} + user = {'id': username, + 'name': username} + + return (tenant, user, metadata) + + def get_tenants(self, username): + return [{'id': username, + 'name': username}] diff --git a/keystone/identity/backends/sql.py b/keystone/identity/backends/sql.py new file mode 100644 index 0000000000..4918a94241 --- /dev/null +++ b/keystone/identity/backends/sql.py @@ -0,0 +1,366 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +import copy + +from keystone import identity +from keystone.common import sql +from keystone.common import utils +from keystone.common.sql import migration + + +def _filter_user(user_ref): + if user_ref: + user_ref.pop('password', None) + return user_ref + + +def _ensure_hashed_password(user_ref): + pw = user_ref.get('password', None) + if pw is not None: + user_ref['password'] = utils.hash_password(pw) + return user_ref + + +class User(sql.ModelBase, sql.DictBase): + __tablename__ = 'user' + id = sql.Column(sql.String(64), primary_key=True) + name = sql.Column(sql.String(64), unique=True) + #password = sql.Column(sql.String(64)) + extra = sql.Column(sql.JsonBlob()) + + @classmethod + def from_dict(cls, user_dict): + # shove any non-indexed properties into extra + extra = {} + for k, v in user_dict.copy().iteritems(): + # TODO(termie): infer this somehow + if k not in ['id', 'name']: + extra[k] = user_dict.pop(k) + + user_dict['extra'] = extra + return cls(**user_dict) + + def to_dict(self): + extra_copy = self.extra.copy() + extra_copy['id'] = self.id + extra_copy['name'] = self.name + return extra_copy + + +class Tenant(sql.ModelBase, sql.DictBase): + __tablename__ = 'tenant' + id = sql.Column(sql.String(64), primary_key=True) + name = sql.Column(sql.String(64), unique=True) + extra = sql.Column(sql.JsonBlob()) + + @classmethod + def from_dict(cls, tenant_dict): + # shove any non-indexed properties into extra + extra = {} + for k, v in tenant_dict.copy().iteritems(): + # TODO(termie): infer this somehow + if k not in ['id', 'name']: + extra[k] = tenant_dict.pop(k) + + tenant_dict['extra'] = extra + return cls(**tenant_dict) + + def to_dict(self): + extra_copy = copy.deepcopy(self.extra) + extra_copy['id'] = self.id + extra_copy['name'] = self.name + return extra_copy + + +class Role(sql.ModelBase, sql.DictBase): + __tablename__ = 'role' + id = sql.Column(sql.String(64), primary_key=True) + name = sql.Column(sql.String(64)) + + +class Metadata(sql.ModelBase, sql.DictBase): + __tablename__ = 'metadata' + #__table_args__ = ( + # sql.Index('idx_metadata_usertenant', 'user', 'tenant'), + # ) + + user_id = sql.Column(sql.String(64), primary_key=True) + tenant_id = sql.Column(sql.String(64), primary_key=True) + data = sql.Column(sql.JsonBlob()) + + +class UserTenantMembership(sql.ModelBase, sql.DictBase): + """Tenant membership join table.""" + __tablename__ = 'user_tenant_membership' + user_id = sql.Column(sql.String(64), + sql.ForeignKey('user.id'), + primary_key=True) + tenant_id = sql.Column(sql.String(64), + sql.ForeignKey('tenant.id'), + primary_key=True) + + +class Identity(sql.Base, identity.Driver): + # Internal interface to manage the database + def db_sync(self): + migration.db_sync() + + # Identity interface + def authenticate(self, user_id=None, tenant_id=None, password=None): + """Authenticate based on a user, tenant and password. + + Expects the user object to have a password field and the tenant to be + in the list of tenants on the user. + + """ + user_ref = self._get_user(user_id) + tenant_ref = None + metadata_ref = None + if (not user_ref + or not utils.check_password(password, user_ref.get('password'))): + raise AssertionError('Invalid user / password') + + tenants = self.get_tenants_for_user(user_id) + if tenant_id and tenant_id not in tenants: + raise AssertionError('Invalid tenant') + + tenant_ref = self.get_tenant(tenant_id) + if tenant_ref: + metadata_ref = self.get_metadata(user_id, tenant_id) + else: + metadata_ref = {} + return (_filter_user(user_ref), tenant_ref, metadata_ref) + + def get_tenant(self, tenant_id): + session = self.get_session() + tenant_ref = session.query(Tenant).filter_by(id=tenant_id).first() + if not tenant_ref: + return + return tenant_ref.to_dict() + + def get_tenant_by_name(self, tenant_name): + session = self.get_session() + tenant_ref = session.query(Tenant).filter_by(name=tenant_name).first() + if not tenant_ref: + return + return tenant_ref.to_dict() + + def _get_user(self, user_id): + session = self.get_session() + user_ref = session.query(User).filter_by(id=user_id).first() + if not user_ref: + return + return user_ref.to_dict() + + def _get_user_by_name(self, user_name): + session = self.get_session() + user_ref = session.query(User).filter_by(name=user_name).first() + if not user_ref: + return + return user_ref.to_dict() + + def get_user(self, user_id): + return _filter_user(self._get_user(user_id)) + + def get_user_by_name(self, user_name): + return _filter_user(self._get_user_by_name(user_name)) + + def get_metadata(self, user_id, tenant_id): + session = self.get_session() + metadata_ref = session.query(Metadata)\ + .filter_by(user_id=user_id)\ + .filter_by(tenant_id=tenant_id)\ + .first() + return getattr(metadata_ref, 'data', None) + + def get_role(self, role_id): + session = self.get_session() + role_ref = session.query(Role).filter_by(id=role_id).first() + return role_ref + + def list_users(self): + session = self.get_session() + user_refs = session.query(User) + return [_filter_user(x.to_dict()) for x in user_refs] + + def list_roles(self): + session = self.get_session() + role_refs = session.query(Role) + return list(role_refs) + + # These should probably be part of the high-level API + def add_user_to_tenant(self, tenant_id, user_id): + session = self.get_session() + q = session.query(UserTenantMembership)\ + .filter_by(user_id=user_id)\ + .filter_by(tenant_id=tenant_id) + rv = q.first() + if rv: + return + + with session.begin(): + session.add(UserTenantMembership(user_id=user_id, + tenant_id=tenant_id)) + session.flush() + + def remove_user_from_tenant(self, tenant_id, user_id): + session = self.get_session() + membership_ref = session.query(UserTenantMembership)\ + .filter_by(user_id=user_id)\ + .filter_by(tenant_id=tenant_id)\ + .first() + with session.begin(): + session.delete(membership_ref) + session.flush() + + def get_tenants_for_user(self, user_id): + session = self.get_session() + membership_refs = session.query(UserTenantMembership)\ + .filter_by(user_id=user_id)\ + .all() + + return [x.tenant_id for x in membership_refs] + + def get_roles_for_user_and_tenant(self, user_id, tenant_id): + metadata_ref = self.get_metadata(user_id, tenant_id) + if not metadata_ref: + metadata_ref = {} + return metadata_ref.get('roles', []) + + def add_role_to_user_and_tenant(self, user_id, tenant_id, role_id): + metadata_ref = self.get_metadata(user_id, tenant_id) + is_new = False + if not metadata_ref: + is_new = True + metadata_ref = {} + roles = set(metadata_ref.get('roles', [])) + roles.add(role_id) + metadata_ref['roles'] = list(roles) + if not is_new: + self.update_metadata(user_id, tenant_id, metadata_ref) + else: + self.create_metadata(user_id, tenant_id, metadata_ref) + + def remove_role_from_user_and_tenant(self, user_id, tenant_id, role_id): + metadata_ref = self.get_metadata(user_id, tenant_id) + is_new = False + if not metadata_ref: + is_new = True + metadata_ref = {} + roles = set(metadata_ref.get('roles', [])) + roles.remove(role_id) + metadata_ref['roles'] = list(roles) + if not is_new: + self.update_metadata(user_id, tenant_id, metadata_ref) + else: + self.create_metadata(user_id, tenant_id, metadata_ref) + + # CRUD + def create_user(self, user_id, user): + user = _ensure_hashed_password(user) + session = self.get_session() + with session.begin(): + user_ref = User.from_dict(user) + session.add(user_ref) + session.flush() + return user_ref.to_dict() + + def update_user(self, user_id, user): + session = self.get_session() + with session.begin(): + user_ref = session.query(User).filter_by(id=user_id).first() + old_user_dict = user_ref.to_dict() + user = _ensure_hashed_password(user) + for k in user: + old_user_dict[k] = user[k] + new_user = User.from_dict(old_user_dict) + + user_ref.name = new_user.name + user_ref.extra = new_user.extra + session.flush() + return user_ref + + def delete_user(self, user_id): + session = self.get_session() + user_ref = session.query(User).filter_by(id=user_id).first() + with session.begin(): + session.delete(user_ref) + session.flush() + + def create_tenant(self, tenant_id, tenant): + session = self.get_session() + with session.begin(): + tenant_ref = Tenant.from_dict(tenant) + session.add(tenant_ref) + session.flush() + return tenant_ref.to_dict() + + def update_tenant(self, tenant_id, tenant): + session = self.get_session() + with session.begin(): + tenant_ref = session.query(Tenant).filter_by(id=tenant_id).first() + old_tenant_dict = tenant_ref.to_dict() + for k in tenant: + old_tenant_dict[k] = tenant[k] + new_tenant = Tenant.from_dict(old_tenant_dict) + + tenant_ref.name = new_tenant.name + tenant_ref.extra = new_tenant.extra + session.flush() + return tenant_ref + + def delete_tenant(self, tenant_id): + session = self.get_session() + tenant_ref = session.query(Tenant).filter_by(id=tenant_id).first() + with session.begin(): + session.delete(tenant_ref) + session.flush() + + def create_metadata(self, user_id, tenant_id, metadata): + session = self.get_session() + with session.begin(): + session.add(Metadata(user_id=user_id, + tenant_id=tenant_id, + data=metadata)) + session.flush() + return metadata + + def update_metadata(self, user_id, tenant_id, metadata): + session = self.get_session() + with session.begin(): + metadata_ref = session.query(Metadata)\ + .filter_by(user_id=user_id)\ + .filter_by(tenant_id=tenant_id)\ + .first() + data = metadata_ref.data.copy() + for k in metadata: + data[k] = metadata[k] + metadata_ref.data = data + session.flush() + return metadata_ref + + def delete_metadata(self, user_id, tenant_id): + self.db.delete('metadata-%s-%s' % (tenant_id, user_id)) + return None + + def create_role(self, role_id, role): + session = self.get_session() + with session.begin(): + session.add(Role(**role)) + session.flush() + return role + + def update_role(self, role_id, role): + session = self.get_session() + with session.begin(): + role_ref = session.query(Role).filter_by(id=role_id).first() + for k in role: + role_ref[k] = role[k] + session.flush() + return role_ref + + def delete_role(self, role_id): + session = self.get_session() + role_ref = session.query(Role).filter_by(id=role_id).first() + with session.begin(): + session.delete(role_ref) diff --git a/keystone/identity/core.py b/keystone/identity/core.py new file mode 100644 index 0000000000..66c2cdce90 --- /dev/null +++ b/keystone/identity/core.py @@ -0,0 +1,537 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +"""Main entry point into the Identity service.""" + +import uuid +import urllib +import urlparse + +import webob.exc + +from keystone import catalog +from keystone import config +from keystone import exception +from keystone import policy +from keystone import token +from keystone.common import manager +from keystone.common import wsgi + + +CONF = config.CONF + + +class Manager(manager.Manager): + """Default pivot point for the Identity backend. + + See :mod:`keystone.common.manager.Manager` for more details on how this + dynamically calls the backend. + + """ + + def __init__(self): + super(Manager, self).__init__(CONF.identity.driver) + + +class Driver(object): + """Interface description for an Identity driver.""" + + def authenticate(self, user_id=None, tenant_id=None, password=None): + """Authenticate a given user, tenant and password. + + Returns: (user, tenant, metadata). + + """ + raise NotImplementedError() + + def get_tenant(self, tenant_id): + """Get a tenant by id. + + Returns: tenant_ref or None. + + """ + raise NotImplementedError() + + def get_tenant_by_name(self, tenant_name): + """Get a tenant by name. + + Returns: tenant_ref or None. + + """ + raise NotImplementedError() + + def get_user(self, user_id): + """Get a user by id. + + Returns: user_ref or None. + + """ + raise NotImplementedError() + + def get_user_by_name(self, user_name): + """Get a user by name. + + Returns: user_ref or None. + + """ + raise NotImplementedError() + + def get_role(self, role_id): + """Get a role by id. + + Returns: role_ref or None. + + """ + raise NotImplementedError() + + def list_users(self): + """List all users in the system. + + NOTE(termie): I'd prefer if this listed only the users for a given + tenant. + + Returns: a list of user_refs or an empty list. + + """ + raise NotImplementedError() + + def list_roles(self): + """List all roles in the system. + + Returns: a list of role_refs or an empty list. + + """ + raise NotImplementedError() + + # NOTE(termie): six calls below should probably be exposed by the api + # more clearly when the api redesign happens + def add_user_to_tenant(self, tenant_id, user_id): + raise NotImplementedError() + + def remove_user_from_tenant(self, tenant_id, user_id): + raise NotImplementedError() + + def get_tenants_for_user(self, user_id): + """Get the tenants associated with a given user. + + Returns: a list of tenant ids. + + """ + raise NotImplementedError() + + def get_roles_for_user_and_tenant(self, user_id, tenant_id): + """Get the roles associated with a user within given tenant. + + Returns: a list of role ids. + + """ + raise NotImplementedError() + + def add_role_for_user_and_tenant(self, user_id, tenant_id, role_id): + """Add a role to a user within given tenant.""" + raise NotImplementedError() + + def remove_role_from_user_and_tenant(self, user_id, tenant_id, role_id): + """Remove a role from a user within given tenant.""" + raise NotImplementedError() + + # user crud + def create_user(self, user_id, user): + raise NotImplementedError() + + def update_user(self, user_id, user): + raise NotImplementedError() + + def delete_user(self, user_id): + raise NotImplementedError() + + # tenant crud + def create_tenant(self, tenant_id, tenant): + raise NotImplementedError() + + def update_tenant(self, tenant_id, tenant): + raise NotImplementedError() + + def delete_tenant(self, tenant_id, tenant): + raise NotImplementedError() + + # metadata crud + def create_metadata(self, user_id, tenant_id, metadata): + raise NotImplementedError() + + def update_metadata(self, user_id, tenant_id, metadata): + raise NotImplementedError() + + def delete_metadata(self, user_id, tenant_id, metadata): + raise NotImplementedError() + + # role crud + def create_role(self, role_id, role): + raise NotImplementedError() + + def update_role(self, role_id, role): + raise NotImplementedError() + + def delete_role(self, role_id): + raise NotImplementedError() + + +class PublicRouter(wsgi.ComposableRouter): + def add_routes(self, mapper): + tenant_controller = TenantController() + mapper.connect('/tenants', + controller=tenant_controller, + action='get_tenants_for_token', + conditions=dict(methods=['GET'])) + + +class AdminRouter(wsgi.ComposableRouter): + def add_routes(self, mapper): + # Tenant Operations + tenant_controller = TenantController() + mapper.connect('/tenants', + controller=tenant_controller, + action='get_tenants_for_token', + conditions=dict(method=['GET'])) + mapper.connect('/tenants/{tenant_id}', + controller=tenant_controller, + action='get_tenant', + conditions=dict(method=['GET'])) + + # User Operations + user_controller = UserController() + mapper.connect('/users/{user_id}', + controller=user_controller, + action='get_user', + conditions=dict(method=['GET'])) + + # Role Operations + roles_controller = RoleController() + mapper.connect('/tenants/{tenant_id}/users/{user_id}/roles', + controller=roles_controller, + action='get_user_roles', + conditions=dict(method=['GET'])) + mapper.connect('/users/{user_id}/roles', + controller=user_controller, + action='get_user_roles', + conditions=dict(method=['GET'])) + + +class TenantController(wsgi.Application): + def __init__(self): + self.identity_api = Manager() + self.policy_api = policy.Manager() + self.token_api = token.Manager() + super(TenantController, self).__init__() + + def get_tenants_for_token(self, context, **kw): + """Get valid tenants for token based on token used to authenticate. + + Pulls the token from the context, validates it and gets the valid + tenants for the user in the token. + + Doesn't care about token scopedness. + + """ + try: + token_ref = self.token_api.get_token(context=context, + token_id=context['token_id']) + except exception.NotFound: + raise exception.Unauthorized() + + user_ref = token_ref['user'] + tenant_ids = self.identity_api.get_tenants_for_user( + context, user_ref['id']) + tenant_refs = [] + for tenant_id in tenant_ids: + tenant_refs.append(self.identity_api.get_tenant( + context=context, + tenant_id=tenant_id)) + params = { + 'limit': context['query_string'].get('limit'), + 'marker': context['query_string'].get('marker'), + } + return self._format_tenant_list(tenant_refs, **params) + + def get_tenant(self, context, tenant_id): + # TODO(termie): this stuff should probably be moved to middleware + self.assert_admin(context) + tenant = self.identity_api.get_tenant(context, tenant_id) + if not tenant: + return webob.exc.HTTPNotFound() + return {'tenant': tenant} + + # CRUD Extension + def create_tenant(self, context, tenant): + tenant_ref = self._normalize_dict(tenant) + self.assert_admin(context) + tenant_id = (tenant_ref.get('id') + and tenant_ref.get('id') + or uuid.uuid4().hex) + tenant_ref['id'] = tenant_id + + tenant = self.identity_api.create_tenant( + context, tenant_id, tenant_ref) + return {'tenant': tenant} + + def update_tenant(self, context, tenant_id, tenant): + self.assert_admin(context) + tenant_ref = self.identity_api.update_tenant( + context, tenant_id, tenant) + return {'tenant': tenant_ref} + + def delete_tenant(self, context, tenant_id, **kw): + self.assert_admin(context) + self.identity_api.delete_tenant(context, tenant_id) + + def get_tenant_users(self, context, **kw): + self.assert_admin(context) + raise NotImplementedError() + + def _format_tenant_list(self, tenant_refs, **kwargs): + marker = kwargs.get('marker') + page_idx = 0 + if marker is not None: + for (marker_idx, tenant) in enumerate(tenant_refs): + if tenant['id'] == marker: + # we start pagination after the marker + page_idx = marker_idx + 1 + break + else: + msg = 'Marker could not be found' + raise webob.exc.HTTPBadRequest(explanation=msg) + + limit = kwargs.get('limit') + if limit is not None: + try: + limit = int(limit) + assert limit >= 0 + except (ValueError, AssertionError): + msg = 'Invalid limit value' + raise webob.exc.HTTPBadRequest(explanation=msg) + + tenant_refs = tenant_refs[page_idx:limit] + + for x in tenant_refs: + x['enabled'] = True + o = {'tenants': tenant_refs, + 'tenants_links': []} + return o + + +class UserController(wsgi.Application): + def __init__(self): + self.catalog_api = catalog.Manager() + self.identity_api = Manager() + self.policy_api = policy.Manager() + self.token_api = token.Manager() + super(UserController, self).__init__() + + def get_user(self, context, user_id): + self.assert_admin(context) + user_ref = self.identity_api.get_user(context, user_id) + if not user_ref: + raise webob.exc.HTTPNotFound() + return {'user': user_ref} + + def get_users(self, context): + # NOTE(termie): i can't imagine that this really wants all the data + # about every single user in the system... + self.assert_admin(context) + user_refs = self.identity_api.list_users(context) + return {'users': user_refs} + + # CRUD extension + def create_user(self, context, user): + user = self._normalize_dict(user) + self.assert_admin(context) + tenant_id = user.get('tenantId', None) + user_id = uuid.uuid4().hex + user_ref = user.copy() + user_ref['id'] = user_id + new_user_ref = self.identity_api.create_user( + context, user_id, user_ref) + if tenant_id: + self.identity_api.add_user_to_tenant(tenant_id, user_id) + return {'user': new_user_ref} + + def update_user(self, context, user_id, user): + # NOTE(termie): this is really more of a patch than a put + self.assert_admin(context) + user_ref = self.identity_api.update_user(context, user_id, user) + return {'user': user_ref} + + def delete_user(self, context, user_id): + self.assert_admin(context) + self.identity_api.delete_user(context, user_id) + + def set_user_enabled(self, context, user_id, user): + return self.update_user(context, user_id, user) + + def set_user_password(self, context, user_id, user): + return self.update_user(context, user_id, user) + + def update_user_tenant(self, context, user_id, user): + """Update the default tenant.""" + # ensure that we're a member of that tenant + tenant_id = user.get('tenantId') + self.identity_api.add_user_to_tenant(context, tenant_id, user_id) + return self.update_user(context, user_id, user) + + +class RoleController(wsgi.Application): + def __init__(self): + self.catalog_api = catalog.Manager() + self.identity_api = Manager() + self.token_api = token.Manager() + self.policy_api = policy.Manager() + super(RoleController, self).__init__() + + # COMPAT(essex-3) + def get_user_roles(self, context, user_id, tenant_id=None): + """Get the roles for a user and tenant pair. + + Since we're trying to ignore the idea of user-only roles we're + not implementing them in hopes that the idea will die off. + + """ + if tenant_id is None: + raise Exception('User roles not supported: tenant_id required') + roles = self.identity_api.get_roles_for_user_and_tenant( + context, user_id, tenant_id) + return {'roles': [self.identity_api.get_role(context, x) + for x in roles]} + + # CRUD extension + def get_role(self, context, role_id): + self.assert_admin(context) + role_ref = self.identity_api.get_role(context, role_id) + if not role_ref: + raise webob.exc.HTTPNotFound() + return {'role': role_ref} + + def create_role(self, context, role): + role = self._normalize_dict(role) + self.assert_admin(context) + role_id = uuid.uuid4().hex + role['id'] = role_id + role_ref = self.identity_api.create_role(context, role_id, role) + return {'role': role_ref} + + def delete_role(self, context, role_id): + self.assert_admin(context) + role_ref = self.identity_api.delete_role(context, role_id) + + def get_roles(self, context): + self.assert_admin(context) + roles = self.identity_api.list_roles(context) + # TODO(termie): probably inefficient at some point + return {'roles': roles} + + def add_role_to_user(self, context, user_id, role_id, tenant_id=None): + """Add a role to a user and tenant pair. + + Since we're trying to ignore the idea of user-only roles we're + not implementing them in hopes that the idea will die off. + + """ + self.assert_admin(context) + if tenant_id is None: + raise Exception('User roles not supported: tenant_id required') + + # This still has the weird legacy semantics that adding a role to + # a user also adds them to a tenant + self.identity_api.add_user_to_tenant(context, tenant_id, user_id) + self.identity_api.add_role_to_user_and_tenant( + context, user_id, tenant_id, role_id) + role_ref = self.identity_api.get_role(context, role_id) + return {'role': role_ref} + + def remove_role_from_user(self, context, user_id, role_id, tenant_id=None): + """Remove a role from a user and tenant pair. + + Since we're trying to ignore the idea of user-only roles we're + not implementing them in hopes that the idea will die off. + + """ + self.assert_admin(context) + if tenant_id is None: + raise Exception('User roles not supported: tenant_id required') + + # This still has the weird legacy semantics that adding a role to + # a user also adds them to a tenant + self.identity_api.remove_role_from_user_and_tenant( + context, user_id, tenant_id, role_id) + roles = self.identity_api.get_roles_for_user_and_tenant( + context, user_id, tenant_id) + if not roles: + self.identity_api.remove_user_from_tenant( + context, tenant_id, user_id) + return + + # COMPAT(diablo): CRUD extension + def get_role_refs(self, context, user_id): + """Ultimate hack to get around having to make role_refs first-class. + + This will basically iterate over the various roles the user has in + all tenants the user is a member of and create fake role_refs where + the id encodes the user-tenant-role information so we can look + up the appropriate data when we need to delete them. + + """ + self.assert_admin(context) + user_ref = self.identity_api.get_user(context, user_id) + tenant_ids = self.identity_api.get_tenants_for_user(context, user_id) + o = [] + for tenant_id in tenant_ids: + role_ids = self.identity_api.get_roles_for_user_and_tenant( + context, user_id, tenant_id) + for role_id in role_ids: + ref = {'roleId': role_id, + 'tenantId': tenant_id, + 'userId': user_id} + ref['id'] = urllib.urlencode(ref) + o.append(ref) + return {'roles': o} + + # COMPAT(diablo): CRUD extension + def create_role_ref(self, context, user_id, role): + """This is actually used for adding a user to a tenant. + + In the legacy data model adding a user to a tenant required setting + a role. + + """ + self.assert_admin(context) + # TODO(termie): for now we're ignoring the actual role + tenant_id = role.get('tenantId') + role_id = role.get('roleId') + self.identity_api.add_user_to_tenant(context, tenant_id, user_id) + self.identity_api.add_role_to_user_and_tenant( + context, user_id, tenant_id, role_id) + role_ref = self.identity_api.get_role(context, role_id) + return {'role': role_ref} + + # COMPAT(diablo): CRUD extension + def delete_role_ref(self, context, user_id, role_ref_id): + """This is actually used for deleting a user from a tenant. + + In the legacy data model removing a user from a tenant required + deleting a role. + + To emulate this, we encode the tenant and role in the role_ref_id, + and if this happens to be the last role for the user-tenant pair, + we remove the user from the tenant. + + """ + self.assert_admin(context) + # TODO(termie): for now we're ignoring the actual role + role_ref_ref = urlparse.parse_qs(role_ref_id) + tenant_id = role_ref_ref.get('tenantId')[0] + role_id = role_ref_ref.get('roleId')[0] + self.identity_api.remove_role_from_user_and_tenant( + context, user_id, tenant_id, role_id) + roles = self.identity_api.get_roles_for_user_and_tenant( + context, user_id, tenant_id) + if not roles: + self.identity_api.remove_user_from_tenant( + context, tenant_id, user_id) diff --git a/keystone/logic/__init__.py b/keystone/logic/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/keystone/logic/extension_reader.py b/keystone/logic/extension_reader.py deleted file mode 100644 index 79a7fd4921..0000000000 --- a/keystone/logic/extension_reader.py +++ /dev/null @@ -1,142 +0,0 @@ -# Copyright (c) 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. - -import os -import json -from lxml import etree - -from keystone import config -from keystone import utils -from keystone.contrib.extensions import CONFIG_EXTENSION_PROPERTY -from keystone.contrib.extensions import DEFAULT_EXTENSIONS -from keystone.logic.types.extension import Extensions - -EXTENSIONS_PATH = 'contrib/extensions' -CONF = config.CONF - - -def get_supported_extensions(): - """ - Returns list of supported extensions. - """ - extensions = CONF[CONFIG_EXTENSION_PROPERTY] or DEFAULT_EXTENSIONS - return [extension.strip() for extension in extensions] - - -def is_extension_supported(extension_name): - """ - Return True if the extension is enabled, False otherwise. - extension_name - extension name - extension_name is case-sensitive. - """ - if extension_name is not None: - return extension_name in get_supported_extensions() - return False - - -class ExtensionsReader(object): - """Reader to read static extensions content""" - def __init__(self, extension_prefix): - self.extensions = None - self.extension_prefix = extension_prefix - self.root = None - self.supported_extensions = None - self.__init_extensions() - - def __init_extensions(self): - self.extensions = Extensions(self.__get_json_extensions(), - self.__get_xml_extensions()) - - def __get_json_extensions(self): - """ Initializes and returns all json static extension content.""" - body = self.__get_all_json_extensions() - extensionsarray = body["extensions"]["values"] - for supported_extension in self.__get_supported_extensions(): - thisextensionjson = self.__get_extension_json( - supported_extension) - if thisextensionjson is not None: - extensionsarray.append(thisextensionjson) - return json.dumps(body) - - def __get_xml_extensions(self): - """ Initializes and returns all xml static extension content.""" - body = self.__get_all_xml_extensions() - for supported_extension in self.__get_supported_extensions(): - thisextensionxml = self.__get_extension_xml(supported_extension) - if thisextensionxml is not None: - body.append(thisextensionxml) - return etree.tostring(body) - - def __get_root(self): - """ Returns application root.Has a local reference for reuse.""" - if self.root is None: - self.root = utils.get_app_root() - self.root = os.path.abspath(self.root) + os.sep - return self.root - - def __get_file(self, resp_file): - """ Helper get file method.""" - root = self.__get_root() - filename = os.path.abspath(os.path.join(root, resp_file.strip('/\\'))) - return open(filename).read() - - def __get_all_json_extensions(self): - """ Gets empty json extensions content to which specific - extensions are added.""" - resp_file = "%s/%s.json" % (EXTENSIONS_PATH, 'extensions') - allextensions = self.__get_file(resp_file) - return json.loads(allextensions) - - def __get_all_xml_extensions(self): - """ Gets empty xml extensions content - to which specific extensions are added.""" - resp_file = "%s/%s.xml" % (EXTENSIONS_PATH, 'extensions') - allextensions = self.__get_file(resp_file) - return etree.fromstring(allextensions) - - def __get_supported_extensions(self): - """ Returns list of supported extensions.""" - if self.supported_extensions is None: - self.supported_extensions = get_supported_extensions() - return self.supported_extensions - - def __get_extension_json(self, extension_name): - """Returns specific extension's json content.""" - thisextension = self.__get_extension_file(extension_name, 'json') - return thisextension if not thisextension\ - else json.loads(thisextension.read()) - - def __get_extension_xml(self, extension_name): - """Returns specific extension's xml content.""" - thisextension = self.__get_extension_file(extension_name, 'xml') - return thisextension if not thisextension\ - else etree.parse(thisextension).getroot() - - def __get_extension_file(self, extension_name, request_type): - """Returns specific static extension file.""" - try: - extension_dir = "%s/%s/%s" % (EXTENSIONS_PATH, - self.extension_prefix, extension_name) - extension_dir = os.path.abspath(os.path.join(self.__get_root(), - extension_dir.strip('/\\'))) - extension_file = open(os.path.join(extension_dir, - "extension." + request_type)) - return extension_file - except IOError: - return None - - def get_extensions(self): - """Return Extensions result.""" - return self.extensions diff --git a/keystone/logic/service.py b/keystone/logic/service.py deleted file mode 100755 index 9387464d50..0000000000 --- a/keystone/logic/service.py +++ /dev/null @@ -1,1558 +0,0 @@ -# Copyright (c) 2010-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. -# -# pylint: disable=C0302,W0603,W0602 - -from datetime import datetime, timedelta -import functools -import logging -import uuid - -from keystone import config -from keystone.logic.types import auth, atom -from keystone.logic.signer import Signer -import keystone.backends as backends -import keystone.backends.models as models -from keystone.logic.types import fault -from keystone.logic.types.tenant import Tenants -from keystone.logic.types.user import User, User_Update, Users -from keystone.logic.types.endpoint import Endpoint, Endpoints, \ - EndpointTemplate, EndpointTemplates -from keystone.logic.types.credential import Credentials, PasswordCredentials -from keystone import utils -# New imports as we refactor old backend design and models -from keystone.models import Tenant, Token -from keystone.models import Role, Roles -from keystone.models import Service, Services -from keystone.managers.token import Manager as TokenManager -from keystone.managers.tenant import Manager as TenantManager -from keystone.managers.user import Manager as UserManager -from keystone.managers.role import Manager as RoleManager -from keystone.managers.grant import Manager as GrantManager -from keystone.managers.service import Manager as ServiceManager -from keystone.managers.endpoint_template import Manager \ - as EndpointTemplateManager -from keystone.managers.endpoint import Manager as EndpointManager -from keystone.managers.credential import Manager as CredentialManager - -CONF = config.CONF - -#Reference to Admin Role. -ADMIN_ROLE_ID = None -ADMIN_ROLE_NAME = None -SERVICE_ADMIN_ROLE_ID = None -SERVICE_ADMIN_ROLE_NAME = None -GLOBAL_SERVICE_ID = None # to facilitate global roles for validate tokens - -LOG = logging.getLogger(__name__) - - -def admin_token_validator(fnc): - """Decorator that applies the validate_admin_token() method.""" - @functools.wraps(fnc) - def _wrapper(self, token_id, *args, **kwargs): - self.validate_admin_token(token_id) - return fnc(self, token_id, *args, **kwargs) - return _wrapper - - -def service_admin_token_validator(fnc): - """Decorator that applies the validate_service_admin_token() method.""" - @functools.wraps(fnc) - def _wrapper(self, token_id, *args, **kwargs): - self.validate_service_admin_token(token_id) - return fnc(self, token_id, *args, **kwargs) - return _wrapper - - -# pylint: disable=R0902 -class IdentityService(object): - """Implements the Identity service - - This class handles all logic of routing requests to the correct - backend as well as validating incoming/outgoing data - """ - - def __init__(self): - """ Initialize - - Loads all necessary backends to handle incoming requests. - """ - backends.configure_backends() - self.token_manager = TokenManager() - self.tenant_manager = TenantManager() - self.user_manager = UserManager() - self.role_manager = RoleManager() - self.grant_manager = GrantManager() - self.service_manager = ServiceManager() - self.endpoint_template_manager = EndpointTemplateManager() - self.endpoint_manager = EndpointManager() - self.credential_manager = CredentialManager() - - # pylint: disable=W0603 - global ADMIN_ROLE_NAME - ADMIN_ROLE_NAME = CONF.keystone_admin_role - - global SERVICE_ADMIN_ROLE_NAME - SERVICE_ADMIN_ROLE_NAME = CONF.keystone_service_admin_role - - global GLOBAL_SERVICE_ID - GLOBAL_SERVICE_ID = CONF.global_service_id or "global" - - LOG.debug("init with ADMIN_ROLE_NAME=%s, SERVICE_ADMIN_ROLE_NAME=%s, " - "GLOBAL_SERVICE_ID=%s" % (ADMIN_ROLE_NAME, - SERVICE_ADMIN_ROLE_NAME, - GLOBAL_SERVICE_ID)) - - # - # Token Operations - # - def authenticate(self, auth_request): - LOG.debug("Authenticating with passwordCredentials") - if not isinstance(auth_request, auth.AuthWithPasswordCredentials): - raise fault.BadRequestFault( - "Expecting auth_with_password_credentials!") - - def validate(duser): - return self.user_manager.check_password(duser.id, - auth_request.password) - - if auth_request.tenant_name: - dtenant = self.validate_tenant_by_name(auth_request.tenant_name) - auth_request.tenant_id = dtenant.id - elif auth_request.tenant_id: - dtenant = self.validate_tenant_by_id(auth_request.tenant_id) - - user = self.user_manager.get_by_name(auth_request.username) - if not user: - LOG.debug("Did not find user with name=%s" % auth_request.username) - raise fault.UnauthorizedFault("Unauthorized") - - return self._authenticate(validate, user.id, auth_request.tenant_id) - - def authenticate_with_unscoped_token(self, auth_request): - LOG.debug("Authenticating with token (unscoped)") - if not isinstance(auth_request, auth.AuthWithUnscopedToken): - raise fault.BadRequestFault("Expecting auth_with_unscoped_token!") - - # We *should* check for an unscoped token here, but as long as - # POST /tokens w/ credentials auto-scopes to User.tenantId, users can't - # reach this flow. - # _token, user = validate_unscoped_token(auth_request.token_id) - _token, user = self._validate_token(auth_request.token_id) - - if auth_request.tenant_name: - dtenant = self.validate_tenant_by_name(auth_request.tenant_name) - auth_request.tenant_id = dtenant.id - elif auth_request.tenant_id: - dtenant = self.validate_tenant_by_id(auth_request.tenant_id) - - # pylint: disable=W0613 - def validate(duser): - # The user is already authenticated - return True - return self._authenticate(validate, user.id, auth_request.tenant_id) - - def authenticate_ec2(self, credentials): - LOG.debug("Authenticating with EC2 credentials") - if not isinstance(credentials, auth.Ec2Credentials): - raise fault.BadRequestFault("Expecting Ec2 Credentials!") - - creds = self.credential_manager.get_by_access(credentials.access) - if not creds: - raise fault.UnauthorizedFault("No credentials found for %s" - % credentials.access) - - # pylint: disable=W0613 - def validate(duser): - signer = Signer(creds.secret) - signature = signer.generate(credentials) - if signature == credentials.signature: - return True - # NOTE(vish): Some libraries don't use the port when signing - # requests, so try again without port. - if ':' in credentials.host: - hostname, _port = credentials.host.split(":") - credentials.host = hostname - signature = signer.generate(credentials) - return signature == credentials.signature - return False - return self._authenticate(validate, creds.user_id, - creds.tenant_id) - - def authenticate_s3(self, credentials): - # Check credentials - if not isinstance(credentials, auth.S3Credentials): - raise fault.BadRequestFault("Expecting S3 Credentials!") - - creds = self.credential_manager.get_by_access(credentials.access) - if not creds: - raise fault.UnauthorizedFault("No credentials found for %s" - % credentials.access) - - def validate(duser): # pylint: disable=W0613 - signer = Signer(creds.secret) - signature = signer.generate(credentials, s3=True) - if signature == credentials.signature: - return True - return False - - return self._authenticate(validate, creds.user_id, creds.tenant_id) - - def _authenticate(self, validate, user_id, tenant_id=None): - LOG.debug("Authenticating user %s (tenant: %s)" % (user_id, tenant_id)) - if tenant_id: - duser = self.user_manager.get_by_tenant(user_id, tenant_id) - if duser is None: - LOG.debug("User %s is not authorized on tenant %s" % ( - user_id, tenant_id)) - raise fault.UnauthorizedFault("Unauthorized on this tenant") - else: - duser = self.user_manager.get(user_id) - if duser is None: - LOG.debug("User with id %s not found" % user_id) - raise fault.UnauthorizedFault("Unauthorized") - - if not duser.enabled: - LOG.debug("User %s is not enabled" % user_id) - raise fault.UserDisabledFault("Your account has been disabled") - - if not validate(duser): - LOG.debug("validate() returned false") - raise fault.UnauthorizedFault("Unauthorized") - - # use user's default tenant_id if one is not specified - tenant_id = tenant_id or duser.tenant_id - - # check for an existing token - dtoken = self.token_manager.find(duser.id, tenant_id) - - if not dtoken or dtoken.expires < datetime.now(): - LOG.debug("Token was not found or expired. Creating a new token " - "for the user") - # Create new token - dtoken = Token() - dtoken.id = str(uuid.uuid4()) - dtoken.user_id = duser.id - dtoken.tenant_id = tenant_id - dtoken.expires = datetime.now() + timedelta(days=1) - dtoken = self.token_manager.create(dtoken) - return self.get_auth_data(dtoken) - - # pylint: disable=W0613 - @service_admin_token_validator - def validate_token(self, admin_token, token_id, belongs_to=None, - service_ids=None): - (token, user) = self._validate_token(token_id, belongs_to, True) - if service_ids and (token.tenant_id or belongs_to): - # scope token, validate the service IDs if present - service_ids = self.parse_service_ids(service_ids) - self.validate_service_ids(service_ids) - auth_data = self.get_validate_data(token, user, service_ids) - if service_ids and (token.tenant_id or belongs_to): - # we have service Ids and scope token, make sure we have some roles - if not auth_data.user.rolegrants.values: - raise fault.UnauthorizedFault("No roles found for scope token") - return auth_data - - @admin_token_validator - def revoke_token(self, admin_token, token_id): - dtoken = self.token_manager.get(token_id) - if not dtoken: - raise fault.ItemNotFoundFault("Token not found") - - self.token_manager.delete(token_id) - - @staticmethod - def parse_service_ids(service_ids): - """ - Method to parse the service IDs string. - service_ids -- comma-separated service IDs - parse and return a list of service IDs. - """ - if service_ids: - return [x.strip() for x in service_ids.split(',')] - return [] - - def validate_service_ids(self, service_ids): - """ - Method to validate the service IDs. - service_ids -- list of service IDs - If not service IDs or encounter an invalid service ID, - fault.UnauthorizedFault will be raised. - """ - if not service_ids: - raise fault.UnauthorizedFault("Missing service IDs") - - services = [self.service_manager.get(service_id) for service_id in - service_ids if not service_id == GLOBAL_SERVICE_ID] - if not all(services): - raise fault.UnauthorizedFault( - "Invalid service ID: %s" % (service_ids)) - - def get_roles_names_by_service_ids(self, service_ids): - """ - Method to find all the roles for the given service IDs. - service_ids -- list of service IDs - """ - roles = [] - for service_id in service_ids: - if service_id != GLOBAL_SERVICE_ID: - sroles = self.role_manager.get_by_service( - service_id=service_id) - if sroles: - roles = roles + sroles - return [role.name for role in roles] - - def get_global_roles_for_user(self, user_id): - """ - Method to return all the global roles for the given user. - user_id -- user ID - """ - ts = [] - drolegrants = self.grant_manager.list_global_roles_for_user(user_id) - for drolegrant in drolegrants: - drole = self.role_manager.get(drolegrant.role_id) - ts.append(Role(drolegrant.role_id, drole.name, - None, drolegrant.tenant_id)) - return ts - - def get_tenant_roles_for_user_and_services(self, user_id, tenant_id, - service_ids): - """ - Method to return all the tenant roles for the given user, - filtered by service ID. - user_id -- user ID - tenant_id -- tenant ID - service_ids -- service IDs - If service_ids are specified, will return the roles filtered by - service IDs. - """ - ts = [] - if tenant_id and user_id: - drolegrants = self.grant_manager.list_tenant_roles_for_user( - user_id, tenant_id) - for drolegrant in drolegrants: - drole = self.role_manager.get(drolegrant.role_id) - ts.append(Role(drolegrant.role_id, drole.name, - None, drolegrant.tenant_id)) - - if service_ids: - # if service IDs are specified, filter roles by service IDs - sroles_names = self.get_roles_names_by_service_ids(service_ids) - return [role for role in ts - if role.name in sroles_names] - else: - return ts - - @service_admin_token_validator - def get_endpoints_for_token(self, admin_token, - token_id, marker, limit, url,): - dtoken = self.token_manager.get(token_id) - if not dtoken: - raise fault.ItemNotFoundFault("Token not found") - if not dtoken.tenant_id: - raise fault.ItemNotFoundFault("Token not mapped to any tenant.") - return self.fetch_tenant_endpoints( - marker, limit, url, dtoken.tenant_id) - - def get_token_info(self, token_id): - """returns token and user object for a token_id""" - - token = None - user = None - if token_id: - token = self.token_manager.get(token_id) - if token: - user = self.user_manager.get(token.user_id) - return (token, user) - - def _validate_token(self, token_id, belongs_to=None, is_check_token=None): - """ - Method to validate a token. - token_id -- id of the token that needs to be validated. - belongs_to -- optional tenant_id to check whether the token is - mapped to a specific tenant. - is_check_token -- optional argument that tells whether - we check the existence of a Token using another Token - to authenticate. This value decides the faults that are to be thrown. - """ - if not token_id: - raise fault.UnauthorizedFault("Missing token") - - (token, user) = self.get_token_info(token_id) - - if not token: - if is_check_token: - raise fault.ItemNotFoundFault("Token does not exist.") - else: - raise fault.UnauthorizedFault( - "Bad token, please reauthenticate") - - if token.expires < datetime.now(): - if is_check_token: - raise fault.ItemNotFoundFault("Token expired, please renew.") - else: - raise fault.ForbiddenFault("Token expired, please renew.") - - if not user.enabled: - raise fault.UserDisabledFault("User %s has been disabled!" - % user.id) - - if user.tenant_id: - self.validate_tenant_by_id(user.tenant_id) - - if token.tenant_id: - self.validate_tenant_by_id(token.tenant_id) - - if belongs_to and unicode(token.tenant_id) != unicode(belongs_to): - raise fault.UnauthorizedFault("Unauthorized on this tenant") - - return (token, user) - - def has_admin_role(self, token_id): - """ Checks if the token belongs to a user who has Keystone admin - rights. - - Returns (token, user) if true. False otherwise. - - This is currently assigned using a global role assignment - (i.e. role assigned without a tenant id). The actual name of the - role is defined in the config file using the keystone-admin-role - setting - """ - (token, user) = self._validate_token(token_id) - self.init_admin_role_identifiers() - if self.has_role(None, user, ADMIN_ROLE_ID): - return (token, user) - else: - return False - - def has_service_admin_role(self, token_id): - """ Checks if the token belongs to a user who has Keystone Service - Admin rights. (Note: Keystone Admin rights include Keystone Service - Admin). - - Returns (token, user) if true. False otherwise. - - This is currently assigned using a global role assignment - (i.e. role assigned without a tenant id). The actual name of the role - is defined in the config file using the keystone-admin-role setting - """ - (token, user) = self._validate_token(token_id) - self.init_admin_role_identifiers() - if self.has_role(None, user, SERVICE_ADMIN_ROLE_ID): - return (token, user) - else: - return self.has_admin_role(token_id) - - def validate_admin_token(self, token_id): - """ Validates that the token belongs to a user who has Keystone admin - rights. Raises an Unauthorized exception if not. - - This is currently assigned using a global role assignment - (i.e. role assigned without a tenant id). The actual name of the role - is defined in the config file using the keystone-admin-role setting - """ - result = self.has_admin_role(token_id) - if result: - return result - else: - raise fault.UnauthorizedFault( - "You are not authorized to make this call") - - def validate_service_admin_token(self, token_id): - """ Validates that the token belongs to a user who has Keystone admin - or Keystone Service Admin rights. Raises an Unaithorized exception if - not. - - These are currently assigned using a global role assignments - (i.e. roles assigned without a tenant id). The actual name of the roles - is defined in the config file using the keystone-admin-role and - keystone-service-admin-role settings - """ - # Does the user have the Service Admin role - result = self.has_service_admin_role(token_id) - if result: - LOG.debug("token is associated with service admin role") - return result - # Does the user have the Admin role (which includes Service Admin - # rights) - result = self.has_admin_role(token_id) - if result: - LOG.debug("token is associated with admin role, so responding" - "positively from validate_service_admin_token") - return result - - LOG.debug("token is not associated with admin or service admin role") - raise fault.UnauthorizedFault( - "You are not authorized to make this call") - - def init_admin_role_identifiers(self): - global ADMIN_ROLE_ID, SERVICE_ADMIN_ROLE_ID - if SERVICE_ADMIN_ROLE_ID is None: - role = self.role_manager.get_by_name(SERVICE_ADMIN_ROLE_NAME) - if role: - SERVICE_ADMIN_ROLE_ID = role.id - else: - LOG.warn('No service admin role found (searching for name=%s.' - % SERVICE_ADMIN_ROLE_NAME) - if ADMIN_ROLE_ID is None: - role = self.role_manager.get_by_name(ADMIN_ROLE_NAME) - if role: - ADMIN_ROLE_ID = role.id - else: - LOG.warn('No admin role found (searching for name=%s.' - % ADMIN_ROLE_NAME) - - def has_role(self, env, user, role): - """Checks if a user has a specific role. - - env: provides the context - user: the user to be checked - role: the role to check that the user has - """ - for rolegrant in\ - self.grant_manager.list_global_roles_for_user(user.id): - if ((rolegrant.role_id == role) - and rolegrant.tenant_id is None): - return True - LOG.debug("User %s failed check - did not have role %s" % - (user.id, role)) - return False - - # pylint: disable=W0613 - @staticmethod - def is_owner(env, user, object): - """Checks if a user is the owner of an object. - - This is done by checking if the user id matches the 'owner_id' - field of the object - - env: provides the context - user: the user to be checked - role: the role to check that the user has - """ - if hasattr(object, 'owner_id'): - if object.owner_id == user.id: - return True - return False - - def validate_unscoped_token(self, token_id, belongs_to=None): - (token, user) = self._validate_token(token_id, belongs_to) - - if token.tenant_id: - raise fault.ForbiddenFault("Expecting unscoped token") - return (token, user) - - def validate_tenant_by_id(self, tenant_id): - if not tenant_id: - raise fault.UnauthorizedFault("Missing tenant id") - - dtenant = self.tenant_manager.get(tenant_id) - return self.validate_tenant(dtenant) - - def validate_tenant_by_name(self, tenant_name): - if not tenant_name: - raise fault.UnauthorizedFault("Missing tenant name") - - dtenant = self.tenant_manager.get_by_name(name=tenant_name) - return self.validate_tenant(dtenant) - - def get_auth_data(self, dtoken): - """returns AuthData object for a token - - AuthData is used for rendering authentication responses - """ - tenant = None - endpoints = None - - if dtoken.tenant_id: - dtenant = self.tenant_manager.get(dtoken.tenant_id) - tenant = auth.Tenant(id=dtenant.id, name=dtenant.name) - endpoints = self.tenant_manager.get_all_endpoints(dtoken.tenant_id) - else: - endpoints = self.tenant_manager.get_all_endpoints(None) - - token = auth.Token(dtoken.expires, dtoken.id, tenant) - duser = self.user_manager.get(dtoken.user_id) - - ts = [] - if dtoken.tenant_id: - drolegrants = self.grant_manager.list_tenant_roles_for_user( - duser.id, dtoken.tenant_id) - for drolegrant in drolegrants: - drole = self.role_manager.get(drolegrant.role_id) - ts.append(Role(drolegrant.role_id, drole.name, - description=drole.desc, tenant_id=drolegrant.tenant_id)) - drolegrants = self.grant_manager.list_global_roles_for_user(duser.id) - for drolegrant in drolegrants: - drole = self.role_manager.get(drolegrant.role_id) - ts.append(Role(drolegrant.role_id, drole.name, - description=drole.desc, tenant_id=drolegrant.tenant_id)) - user = auth.User(duser.id, duser.name, None, None, Roles(ts, [])) - if self.has_service_admin_role(token.id): - # Privileged users see the adminURL as well - url_types = ['admin', 'internal', 'public'] - else: - url_types = ['internal', 'public'] - return auth.AuthData(token, user, endpoints, url_types=url_types) - - def get_validate_data(self, dtoken, duser, service_ids=None): - """return ValidateData object for a token/user pair""" - global GLOBAL_SERVICE_ID - tenant = None - if dtoken.tenant_id: - dtenant = self.tenant_manager.get(dtoken.tenant_id) - tenant = auth.Tenant(id=dtenant.id, name=dtenant.name) - - token = auth.Token(dtoken.expires, dtoken.id, tenant) - - ts = self.get_tenant_roles_for_user_and_services(duser.id, - dtoken.tenant_id, - service_ids) - if (not dtoken.tenant_id or not service_ids or - (GLOBAL_SERVICE_ID in service_ids)): - # return the global roles for unscoped tokens or - # its ID is in the service IDs - ts = ts + self.get_global_roles_for_user(duser.id) - - # Also get the user's tenant's name - tenant_name = None - if duser.tenant_id: - utenant = self.tenant_manager.get(duser.tenant_id) - tenant_name = utenant.name - - user = auth.User(duser.id, duser.name, duser.tenant_id, - tenant_name, Roles(ts, [])) - return auth.ValidateData(token, user) - - @staticmethod - def validate_tenant(dtenant): - if not dtenant: - raise fault.UnauthorizedFault("Tenant not found") - - if dtenant.enabled is None or \ - str(dtenant.enabled).lower() not in ['1', 'true']: - raise fault.TenantDisabledFault("Tenant %s has been disabled!" - % dtenant.id) - return dtenant - - # - # Tenant Operations - # - @admin_token_validator - def create_tenant(self, admin_token, tenant): - if not isinstance(tenant, Tenant): - raise fault.BadRequestFault("Expecting a Tenant") - - utils.check_empty_string(tenant.name, "Expecting a unique Tenant Name") - if self.tenant_manager.get_by_name(tenant.name) is not None: - raise fault.TenantConflictFault( - "A tenant with that name already exists") - dtenant = Tenant() - dtenant.name = tenant.name - dtenant.description = tenant.description - dtenant.enabled = tenant.enabled - return self.tenant_manager.create(dtenant) - - # pylint: disable=R0914 - def get_tenants(self, admin_token, marker, limit, url, - is_service_operation=False): - """Fetch tenants for either an admin or service operation.""" - ts = [] - - if is_service_operation: - # Check regular token validity. - (_token, user) = self._validate_token(admin_token, belongs_to=None, - is_check_token=False) - scope = _token.tenant_id - default_tenant = user.tenant_id - - if (scope is None or - ((scope and default_tenant) and (scope == default_tenant))): - # Return all tenants specific to user if token has no scope - # or if token is scoped to a default tenant - dtenants = self.tenant_manager.list_for_user_get_page( - user.id, marker, limit) - prev_page, next_page = self.tenant_manager.\ - list_for_user_get_page_markers(user.id, marker, limit) - else: - # Return scoped tenant only - dtenants = [self.tenant_manager.get(scope or default_tenant)] - prev_page = 2 - next_page = None - limit = 10 - else: - #Check Admin Token - (_token, user) = self.validate_admin_token(admin_token) - # Return all tenants - dtenants = self.tenant_manager.get_page(marker, limit) - prev_page, next_page = self.tenant_manager.get_page_markers(marker, - limit) - - for dtenant in dtenants: - t = Tenant(id=dtenant.id, name=dtenant.name, - description=dtenant.desc, enabled=dtenant.enabled) - ts.append(t) - - links = self.get_links(url, prev_page, next_page, limit) - return Tenants(ts, links) - - @admin_token_validator - def get_tenant(self, admin_token, tenant_id): - dtenant = self.tenant_manager.get(tenant_id) - if not dtenant: - raise fault.ItemNotFoundFault("The tenant could not be found") - return Tenant(dtenant.id, dtenant.name, dtenant.desc, dtenant.enabled) - - @admin_token_validator - def get_tenant_by_name(self, admin_token, tenant_name): - dtenant = self.tenant_manager.get_by_name(tenant_name) - if not dtenant: - raise fault.ItemNotFoundFault("The tenant could not be found") - return dtenant - - @admin_token_validator - def update_tenant(self, admin_token, tenant_id, tenant): - if not isinstance(tenant, Tenant): - raise fault.BadRequestFault("Expecting a Tenant") - - dtenant = self.tenant_manager.get(tenant_id) - if dtenant is None: - raise fault.ItemNotFoundFault("The tenant could not be found") - - utils.check_empty_string(tenant.name, "Expecting a unique Tenant Name") - - if tenant.name != dtenant.name and \ - self.tenant_manager.get_by_name(tenant.name): - raise fault.TenantConflictFault( - "A tenant with that name already exists") - values = {'id': tenant_id, 'desc': tenant.description, - 'enabled': tenant.enabled, 'name': tenant.name} - self.tenant_manager.update(values) - dtenant = self.tenant_manager.get(tenant_id) - return dtenant - - @admin_token_validator - def delete_tenant(self, admin_token, tenant_id): - dtenant = self.tenant_manager.get(tenant_id) - if dtenant is None: - raise fault.ItemNotFoundFault("The tenant could not be found") - - self.tenant_manager.delete(dtenant.id) - return None - - # - # User Operations - # - @admin_token_validator - def create_user(self, admin_token, user): - self.validate_and_fetch_user_tenant(user.tenant_id) - - if not isinstance(user, User): - raise fault.BadRequestFault("Expecting a User") - - utils.check_empty_string(user.name, - "Expecting a unique user Name") - - if self.user_manager.get_by_name(user.name): - raise fault.UserConflictFault( - "A user with that name already exists") - - if self.user_manager.get_by_email(user.email): - raise fault.EmailConflictFault( - "A user with that email already exists") - - duser = models.User() - duser.name = user.name - duser.password = user.password - duser.email = user.email - duser.enabled = user.enabled - duser.tenant_id = user.tenant_id - duser = self.user_manager.create(duser) - user.id = duser.id - return user - - def validate_and_fetch_user_tenant(self, tenant_id): - if tenant_id: - dtenant = self.tenant_manager.get(tenant_id) - if dtenant is None: - raise fault.ItemNotFoundFault("The tenant is not found") - elif not dtenant.enabled: - raise fault.TenantDisabledFault( - "Your account has been disabled") - return dtenant - - # pylint: disable=R0913 - @admin_token_validator - def get_tenant_users(self, admin_token, tenant_id, - role_id, marker, limit, url): - if tenant_id is None: - raise fault.BadRequestFault("Expecting a Tenant Id") - dtenant = self.tenant_manager.get(tenant_id) - if dtenant is None: - raise fault.ItemNotFoundFault("The tenant not found") - if not dtenant.enabled: - raise fault.TenantDisabledFault("Your account has been disabled") - if role_id: - if not self.role_manager.get(role_id): - raise fault.ItemNotFoundFault("The role not found") - ts = [] - dtenantusers = self.user_manager.users_get_by_tenant_get_page( - tenant_id, role_id, marker, limit) - for dtenantuser in dtenantusers: - try: - troles = dtenantuser.tenant_roles - except AttributeError: - troles = None - ts.append(User(None, dtenantuser.id, dtenantuser.name, tenant_id, - dtenantuser.email, dtenantuser.enabled, troles)) - links = [] - if ts.__len__(): - prev, next = self.\ - user_manager.users_get_by_tenant_get_page_markers( - tenant_id, role_id, marker, limit) - links = self.get_links(url, prev, next, limit) - return Users(ts, links) - - @admin_token_validator - def get_users(self, admin_token, marker, limit, url): - ts = [] - dusers = self.user_manager.users_get_page(marker, limit) - for duser in dusers: - ts.append(User(None, duser.id, duser.name, duser.tenant_id, - duser.email, duser.enabled)) - links = [] - if ts.__len__(): - prev, next = self.user_manager.users_get_page_markers(marker, - limit) - links = self.get_links(url, prev, next, limit) - return Users(ts, links) - - @admin_token_validator - def get_user(self, admin_token, user_id): - duser = self.user_manager.get(user_id) - if not duser: - raise fault.ItemNotFoundFault("The user could not be found") - return User_Update(id=duser.id, tenant_id=duser.tenant_id, - email=duser.email, enabled=duser.enabled, name=duser.name) - - @admin_token_validator - def get_user_by_name(self, admin_token, user_name): - duser = self.user_manager.get_by_name(user_name) - if not duser: - raise fault.ItemNotFoundFault("The user could not be found") - return User_Update(id=duser.id, tenant_id=duser.tenant_id, - email=duser.email, enabled=duser.enabled, name=duser.name) - - @admin_token_validator - def update_user(self, admin_token, user_id, user): - duser = self.user_manager.get(user_id) - if not duser: - raise fault.ItemNotFoundFault("The user could not be found") - - if not isinstance(user, User): - raise fault.BadRequestFault("Expecting a User") - - utils.check_empty_string(user.name, - "Expecting a unique username") - - if user.name != duser.name and \ - self.user_manager.get_by_name(user.name): - raise fault.UserConflictFault( - "A user with that name already exists") - - if user.email != duser.email and \ - self.user_manager.get_by_email(user.email) is not None: - raise fault.EmailConflictFault("Email already exists") - - values = {'id': user_id, 'email': user.email, 'name': user.name} - self.user_manager.update(values) - duser = self.user_manager.get(user_id) - return User(duser.password, duser.id, duser.name, duser.tenant_id, - duser.email, duser.enabled) - - @admin_token_validator - def set_user_password(self, admin_token, user_id, user): - duser = self.user_manager.get(user_id) - if not duser: - raise fault.ItemNotFoundFault("The user could not be found") - - if not isinstance(user, User): - raise fault.BadRequestFault("Expecting a User") - - duser = self.user_manager.get(user_id) - if duser is None: - raise fault.ItemNotFoundFault("The user could not be found") - - values = {'id': user_id, 'password': user.password} - - self.user_manager.update(values) - - return User_Update(password=user.password) - - @admin_token_validator - def enable_disable_user(self, admin_token, user_id, user): - duser = self.user_manager.get(user_id) - if not duser: - raise fault.ItemNotFoundFault("The user could not be found") - if not isinstance(user, User): - raise fault.BadRequestFault("Expecting a User") - - values = {'id': user_id, 'enabled': user.enabled} - - self.user_manager.update(values) - - duser = self.user_manager.get(user_id) - - return User_Update(enabled=user.enabled) - - @admin_token_validator - def set_user_tenant(self, admin_token, user_id, user): - duser = self.user_manager.get(user_id) - if not duser: - raise fault.ItemNotFoundFault("The user could not be found") - if not isinstance(user, User): - raise fault.BadRequestFault("Expecting a User") - - self.validate_and_fetch_user_tenant(user.tenant_id) - values = {'id': user_id, 'tenant_id': user.tenant_id} - self.user_manager.update(values) - return User_Update(tenant_id=user.tenant_id) - - @admin_token_validator - def delete_user(self, admin_token, user_id): - duser = self.user_manager.get(user_id) - if not duser: - raise fault.ItemNotFoundFault("The user could not be found") - - self.user_manager.delete(user_id) - return None - - def create_role(self, admin_token, role): - user = self.validate_service_admin_token(admin_token)[1] - - if not isinstance(role, Role): - raise fault.BadRequestFault("Expecting a Role") - - utils.check_empty_string(role.name, "Expecting a Role name") - - if self.role_manager.get_by_name(role.name) is not None: - raise fault.RoleConflictFault( - "A role with that name '%s' already exists" % role.name) - - #Check if the role name includes an embedded service: in it - #if so, verify the service exists - if role.service_id is None: - split = role.name.split(":") - if isinstance(split, list) and len(split) > 1: - service_name = split[0] - service = self.service_manager.get_by_name(service_name) - if service is None: - raise fault.BadRequestFault( - "A service with the name %s doesn't exist." - % service_name) - role.service_id = service.id - - # Check ownership of the service (or overriding admin rights) - if role.service_id: - service = self.service_manager.get(role.service_id) - if service is None: - raise fault.BadRequestFault( - "A service with that id doesn't exist.") - if not role.name.startswith(service.name + ":"): - raise fault.BadRequestFault( - "Role should begin with service name '%s:'" % service.name) - if not self.is_owner(None, user, service): - if not self.has_admin_role(admin_token): - raise fault.UnauthorizedFault( - "You do not have ownership of the '%s' service" \ - % service.name) - - drole = models.Role() - drole.name = role.name - drole.desc = role.description - drole.service_id = role.service_id - drole = self.role_manager.create(drole) - role.id = drole.id - return role - - @service_admin_token_validator - def get_roles(self, admin_token, marker, limit, url): - droles = self.role_manager.get_page(marker, limit) - prev, next = self.role_manager.get_page_markers(marker, limit) - links = self.get_links(url, prev, next, limit) - ts = self.transform_roles(droles) - return Roles(ts, links) - - @service_admin_token_validator - def get_roles_by_service(self, admin_token, marker, limit, url, - service_id): - droles = self.role_manager.get_by_service_get_page(service_id, marker, - limit) - prev, next = self.role_manager.get_by_service_get_page_markers( - service_id, marker, limit) - links = self.get_links(url, prev, next, limit) - ts = self.transform_roles(droles) - return Roles(ts, links) - - @staticmethod - def transform_roles(droles): - return [Role(drole.id, drole.name, drole.desc, drole.service_id) - for drole in droles] - - @service_admin_token_validator - def get_role(self, admin_token, role_id): - drole = self.role_manager.get(role_id) - if not drole: - raise fault.ItemNotFoundFault("The role could not be found") - return Role(drole.id, drole.name, drole.desc, drole.service_id) - - @service_admin_token_validator - def get_role_by_name(self, admin_token, role_name): - drole = self.role_manager.get_by_name(role_name) - if not drole: - raise fault.ItemNotFoundFault("The role could not be found") - return Role(drole.id, drole.name, - drole.desc, drole.service_id) - - def delete_role(self, admin_token, role_id): - user = self.validate_service_admin_token(admin_token)[1] - - drole = self.role_manager.get(role_id) - if not drole: - raise fault.ItemNotFoundFault("The role could not be found") - - # Check ownership of the service (or overriding admin rights) - if drole.service_id: - service = self.service_manager.get(drole.service_id) - if service: - if not self.is_owner(None, user, service): - if not self.has_admin_role(admin_token): - raise fault.UnauthorizedFault( - "You do not have ownership of the '%s' service" - % service.name) - - rolegrants = self.grant_manager.rolegrant_list_by_role(role_id) - if rolegrants is not None: - for rolegrant in rolegrants: - self.grant_manager.rolegrant_delete(rolegrant.id) - self.role_manager.delete(role_id) - - @service_admin_token_validator - def add_role_to_user(self, admin_token, user_id, role_id, tenant_id=None): - duser = self.user_manager.get(user_id) - if not duser: - raise fault.ItemNotFoundFault("The user could not be found") - - drole = self.role_manager.get(role_id) - if drole is None: - raise fault.ItemNotFoundFault("The role not found") - if tenant_id is not None: - dtenant = self.tenant_manager.get(tenant_id) - if dtenant is None: - raise fault.ItemNotFoundFault("The tenant not found") - - drolegrant = self.grant_manager.rolegrant_get_by_ids(user_id, role_id, - tenant_id) - if drolegrant is not None: - raise fault.RoleConflictFault( - "This role is already mapped to the user.") - - drolegrant = models.UserRoleAssociation() - drolegrant.user_id = duser.id - drolegrant.role_id = drole.id - if tenant_id is not None: - drolegrant.tenant_id = dtenant.id - self.user_manager.user_role_add(drolegrant) - - @service_admin_token_validator - def remove_role_from_user(self, admin_token, user_id, role_id, - tenant_id=None): - drolegrant = self.grant_manager.rolegrant_get_by_ids(user_id, role_id, - tenant_id) - if drolegrant is None: - raise fault.ItemNotFoundFault( - "This role is not mapped to the user.") - self.grant_manager.rolegrant_delete(drolegrant.id) - - # pylint: disable=R0913, R0914 - @service_admin_token_validator - def get_user_roles(self, admin_token, marker, - limit, url, user_id, tenant_id): - duser = self.user_manager.get(user_id) - - if not duser: - raise fault.ItemNotFoundFault("The user could not be found") - - if tenant_id is not None: - dtenant = self.tenant_manager.get(tenant_id) - if not dtenant: - raise fault.ItemNotFoundFault("The tenant could not be found.") - ts = [] - drolegrants = self.grant_manager.rolegrant_get_page(marker, limit, - user_id, tenant_id) - for drolegrant in drolegrants: - drole = self.role_manager.get(drolegrant.role_id) - ts.append(Role(drole.id, drole.name, - drole.desc, drole.service_id)) - prev, next = self.grant_manager.rolegrant_get_page_markers( - user_id, tenant_id, marker, limit) - links = self.get_links(url, prev, next, limit) - return Roles(ts, links) - - def add_endpoint_template(self, admin_token, endpoint_template): - user = self.validate_service_admin_token(admin_token)[1] - - if not isinstance(endpoint_template, EndpointTemplate): - raise fault.BadRequestFault("Expecting a EndpointTemplate") - - utils.check_empty_string(endpoint_template.name, - "Expecting Endpoint Template name.") - utils.check_empty_string(endpoint_template.type, - "Expecting Endpoint Template type.") - - dservice = self.service_manager.get_by_name_and_type( - endpoint_template.name, - endpoint_template.type) - if dservice is None: - raise fault.BadRequestFault( - "A service with that name and type doesn't exist.") - - # Check ownership of the service (or overriding admin rights) - if not self.is_owner(None, user, dservice): - if not self.has_admin_role(admin_token): - raise fault.UnauthorizedFault( - "You do not have ownership of the '%s' service" \ - % dservice.name) - - dendpoint_template = models.EndpointTemplates() - dendpoint_template.region = endpoint_template.region - dendpoint_template.service_id = dservice.id - dendpoint_template.public_url = endpoint_template.public_url - dendpoint_template.admin_url = endpoint_template.admin_url - dendpoint_template.internal_url = endpoint_template.internal_url - dendpoint_template.enabled = endpoint_template.enabled - dendpoint_template.is_global = endpoint_template.is_global - dendpoint_template.version_id = endpoint_template.version_id - dendpoint_template.version_list = endpoint_template.version_list - dendpoint_template.version_info = endpoint_template.version_info - dendpoint_template = self.endpoint_template_manager.create( - dendpoint_template) - endpoint_template.id = dendpoint_template.id - return endpoint_template - - def modify_endpoint_template(self, admin_token, endpoint_template_id, - endpoint_template): - user = self.validate_service_admin_token(admin_token)[1] - - if not isinstance(endpoint_template, EndpointTemplate): - raise fault.BadRequestFault("Expecting a EndpointTemplate") - dendpoint_template = self.endpoint_template_manager.get( - endpoint_template_id) - if not dendpoint_template: - raise fault.ItemNotFoundFault( - "The endpoint template could not be found") - - #Check if the passed service exist. - utils.check_empty_string(endpoint_template.name, - "Expecting Endpoint Template name.") - utils.check_empty_string(endpoint_template.type, - "Expecting Endpoint Template type.") - - dservice = self.service_manager.get(dendpoint_template.service_id) - if not dservice: - raise fault.BadRequestFault( - "A service with that name and type doesn't exist.") - - # Check ownership of the service (or overriding admin rights) - if not self.is_owner(None, user, dservice): - if not self.has_admin_role(admin_token): - raise fault.UnauthorizedFault( - "You do not have ownership of the '%s' service" \ - % dservice.name) - - dendpoint_template.region = endpoint_template.region - dendpoint_template.service_id = dservice.id - dendpoint_template.public_url = endpoint_template.public_url - dendpoint_template.admin_url = endpoint_template.admin_url - dendpoint_template.internal_url = endpoint_template.internal_url - dendpoint_template.enabled = endpoint_template.enabled - dendpoint_template.is_global = endpoint_template.is_global - dendpoint_template.version_id = endpoint_template.version_id - dendpoint_template.version_list = endpoint_template.version_list - dendpoint_template.version_info = endpoint_template.version_info - dendpoint_template = self.endpoint_template_manager.update( - dendpoint_template) - return EndpointTemplate( - dendpoint_template.id, - dendpoint_template.region, - dservice.name, - dservice.type, - dendpoint_template.public_url, - dendpoint_template.admin_url, - dendpoint_template.internal_url, - dendpoint_template.enabled, - dendpoint_template.is_global, - dendpoint_template.version_id, - dendpoint_template.version_list, - dendpoint_template.version_info - ) - - def delete_endpoint_template(self, admin_token, endpoint_template_id): - user = self.validate_service_admin_token(admin_token)[1] - dendpoint_template = self.endpoint_template_manager.get( - endpoint_template_id) - if not dendpoint_template: - raise fault.ItemNotFoundFault( - "The endpoint template could not be found") - - dservice = self.service_manager.get(dendpoint_template.service_id) - if dservice: - # Check ownership of the service (or overriding admin rights) - if not self.is_owner(None, user, dservice): - if not self.has_admin_role(admin_token): - raise fault.UnauthorizedFault( - "You do not have ownership of the '%s' service" \ - % dservice.name) - else: - # Cannot verify service ownership, so verify full admin rights - if not self.has_admin_role(admin_token): - raise fault.UnauthorizedFault( - "You do not have ownership of the '%s' service" \ - % dservice.name) - - #Delete Related endpoints - endpoints = self.endpoint_manager.\ - endpoint_get_by_endpoint_template(endpoint_template_id) - if endpoints is not None: - for endpoint in endpoints: - self.endpoint_manager.delete(endpoint.id) - self.endpoint_template_manager.delete(endpoint_template_id) - - @service_admin_token_validator - def get_endpoint_templates(self, admin_token, marker, limit, url): - dendpoint_templates = self.endpoint_template_manager.get_page(marker, - limit) - ts = self.transform_endpoint_templates(dendpoint_templates) - prev, next = self.endpoint_template_manager.get_page_markers(marker, - limit) - links = self.get_links(url, prev, next, limit) - return EndpointTemplates(ts, links) - - @service_admin_token_validator - def get_endpoint_templates_by_service(self, admin_token, - service_id, marker, limit, url): - dservice = self.service_manager.get(service_id) - if dservice is None: - raise fault.ItemNotFoundFault( - "No service with the id %s found." % service_id) - dendpoint_templates = self.endpoint_template_manager.\ - get_by_service_get_page(service_id, marker, limit) - ts = self.transform_endpoint_templates(dendpoint_templates) - prev, next = self.endpoint_template_manager.\ - get_by_service_get_page_markers(service_id, marker, limit) - links = self.get_links(url, prev, next, limit) - return EndpointTemplates(ts, links) - - def transform_endpoint_templates(self, dendpoint_templates): - ts = [] - for dendpoint_template in dendpoint_templates: - dservice = self.service_manager.get(dendpoint_template.service_id) - ts.append(EndpointTemplate( - dendpoint_template.id, - dendpoint_template.region, - dservice.name, - dservice.type, - dendpoint_template.public_url, - dendpoint_template.admin_url, - dendpoint_template.internal_url, - dendpoint_template.enabled, - dendpoint_template.is_global, - dendpoint_template.version_id, - dendpoint_template.version_list, - dendpoint_template.version_info - )) - return ts - - @service_admin_token_validator - def get_endpoint_template(self, admin_token, endpoint_template_id): - dendpoint_template = self.endpoint_template_manager.get( - endpoint_template_id) - if not dendpoint_template: - raise fault.ItemNotFoundFault( - "The endpoint template could not be found") - dservice = self.service_manager.get(dendpoint_template.service_id) - return EndpointTemplate( - dendpoint_template.id, - dendpoint_template.region, - dservice.name, - dservice.type, - dendpoint_template.public_url, - dendpoint_template.admin_url, - dendpoint_template.internal_url, - dendpoint_template.enabled, - dendpoint_template.is_global, - dendpoint_template.version_id, - dendpoint_template.version_list, - dendpoint_template.version_info - ) - - @service_admin_token_validator - def get_tenant_endpoints(self, admin_token, marker, limit, url, tenant_id): - return self.fetch_tenant_endpoints(marker, limit, - url, tenant_id) - - def fetch_tenant_endpoints(self, marker, limit, url, tenant_id): - if tenant_id is None: - raise fault.BadRequestFault("Expecting a Tenant Id") - - if self.tenant_manager.get(tenant_id) is None: - raise fault.ItemNotFoundFault("The tenant not found") - - ts = [] - - dtenant_endpoints = \ - self.endpoint_manager.endpoint_get_by_tenant_get_page(tenant_id, - marker, limit) - for dtenant_endpoint in dtenant_endpoints: - dendpoint_template = self.endpoint_template_manager.get( - dtenant_endpoint.endpoint_template_id) - dservice = self.service_manager.get(dendpoint_template.service_id) - ts.append(Endpoint( - dtenant_endpoint.id, - dtenant_endpoint.tenant_id, - dendpoint_template.region, - dservice.name, - dservice.type, - dendpoint_template.public_url, - dendpoint_template.admin_url, - dendpoint_template.internal_url, - dendpoint_template.version_id, - dendpoint_template.version_list, - dendpoint_template.version_info - )) - links = [] - if ts.__len__(): - prev, next = \ - self.endpoint_manager.endpoint_get_by_tenant_get_page_markers( - tenant_id, marker, limit) - links = self.get_links(url, prev, next, limit) - return Endpoints(ts, links) - - @service_admin_token_validator - def create_endpoint_for_tenant(self, admin_token, tenant_id, - endpoint_template): - utils.check_empty_string(tenant_id, "Expecting a Tenant Id.") - if self.tenant_manager.get(tenant_id) is None: - raise fault.ItemNotFoundFault("The tenant not found") - - dendpoint_template = self.endpoint_template_manager.get( - endpoint_template.id) - if not dendpoint_template: - raise fault.ItemNotFoundFault( - "The endpoint template could not be found") - dendpoint = models.Endpoints() - dendpoint.tenant_id = tenant_id - dendpoint.endpoint_template_id = endpoint_template.id - dendpoint = self.endpoint_manager.create(dendpoint) - dservice = self.service_manager.get(dendpoint_template.service_id) - dendpoint = Endpoint( - dendpoint.id, - dendpoint.tenant_id, - dendpoint_template.region, - dservice.name, - dservice.type, - dendpoint_template.public_url, - dendpoint_template.admin_url, - dendpoint_template.internal_url, - dendpoint_template.version_id, - dendpoint_template.version_list, - dendpoint_template.version_info - ) - return dendpoint - - @service_admin_token_validator - def delete_endpoint(self, admin_token, endpoint_id): - if self.endpoint_manager.get(endpoint_id) is None: - raise fault.ItemNotFoundFault("The Endpoint is not found.") - self.endpoint_manager.delete(endpoint_id) - return None - - #Service Operations - @service_admin_token_validator - def create_service(self, admin_token, service): - if not isinstance(service, Service): - raise fault.BadRequestFault("Expecting a Service") - - if self.service_manager.get_by_name(service.name) is not None: - raise fault.ServiceConflictFault( - "A service with that name already exists") - - user = self._validate_token(admin_token)[1] - - dservice = models.Service() - dservice.name = service.name - dservice.type = service.type - dservice.desc = service.description - dservice.owner_id = user.id - dservice = self.service_manager.create(dservice) - service.id = dservice.id - - return service - - @service_admin_token_validator - def get_services(self, admin_token, marker, limit, url): - ts = [] - dservices = self.service_manager.get_page(marker, limit) - for dservice in dservices: - ts.append(Service(dservice.id, dservice.name, dservice.type, - dservice.desc)) - prev, next = self.service_manager.get_page_markers(marker, limit) - links = self.get_links(url, prev, next, limit) - return Services(ts, links) - - @service_admin_token_validator - def get_service(self, admin_token, service_id): - dservice = self.service_manager.get(service_id) - if not dservice: - raise fault.ItemNotFoundFault("The service could not be found") - return Service(dservice.id, dservice.name, dservice.type, - dservice.desc) - - @service_admin_token_validator - def get_service_by_name(self, admin_token, service_name): - dservice = self.service_manager.get_by_name(service_name) - if not dservice: - raise fault.ItemNotFoundFault("The service could not be found") - return Service(dservice.id, dservice.name, dservice.type, - dservice.desc) - - @service_admin_token_validator - def delete_service(self, admin_token, service_id): - dservice = self.service_manager.get(service_id) - - if not dservice: - raise fault.ItemNotFoundFault("The service could not be found") - - #Delete Related Endpointtemplates and Endpoints. - endpoint_templates = self.endpoint_template_manager.get_by_service( - service_id) - if endpoint_templates is not None: - for endpoint_template in endpoint_templates: - endpoints = self.endpoint_manager.\ - endpoint_get_by_endpoint_template(endpoint_template.id) - if endpoints is not None: - for endpoint in endpoints: - self.endpoint_manager.delete(endpoint.id) - self.endpoint_template_manager.delete(endpoint_template.id) - #Delete Related Role and RoleRefs - roles = self.role_manager.get_by_service(service_id) - if roles is not None: - for role in roles: - rolegrants = self.grant_manager.rolegrant_list_by_role(role.id) - if rolegrants is not None: - for rolegrant in rolegrants: - self.grant_manager.rolegrant_delete(rolegrant.id) - self.role_manager.delete(role.id) - self.service_manager.delete(service_id) - - @admin_token_validator - def get_credentials(self, admin_token, user_id, marker, limit, url): - ts = [] - duser = self.user_manager.get(user_id) - if not duser: - raise fault.ItemNotFoundFault("The user could not be found") - ts.append(PasswordCredentials(duser.name, None)) - links = [] - return Credentials(ts, links) - - @admin_token_validator - def get_password_credentials(self, admin_token, user_id): - duser = self.user_manager.get(user_id) - if not duser: - raise fault.ItemNotFoundFault("The user could not be found") - if not duser.password: - raise fault.ItemNotFoundFault( - "Password credentials could not be found") - return PasswordCredentials(duser.name, None) - - @admin_token_validator - def delete_password_credentials(self, admin_token, user_id): - duser = self.user_manager.get(user_id) - if not duser: - raise fault.ItemNotFoundFault("The user could not be found") - values = {'id': user_id, 'password': None} - self.user_manager.update(values) - - @admin_token_validator - def update_password_credentials(self, admin_token, user_id, - password_credentials): - duser = self.user_manager.get(user_id) - if not duser: - raise fault.ItemNotFoundFault("The user could not be found") - - if (password_credentials.user_name is None - or not password_credentials.user_name.strip()): - raise fault.BadRequestFault("Expecting a username.") - duser_name = self.user_manager.get_by_name( - password_credentials.user_name) - if duser_name.id != duser.id: - raise fault.UserConflictFault( - "A user with that name already exists") - values = {'id': user_id, 'password': password_credentials.password, - 'name': password_credentials.user_name} - self.user_manager.update(values) - duser = self.user_manager.get(user_id) - return PasswordCredentials(duser.name, duser.password) - - @admin_token_validator - def create_password_credentials(self, admin_token, user_id, - password_credentials): - duser = self.user_manager.get(user_id) - if not duser: - raise fault.ItemNotFoundFault("The user could not be found") - - if password_credentials.user_name is None or\ - not password_credentials.user_name.strip(): - raise fault.BadRequestFault("Expecting a username.") - - if password_credentials.user_name != duser.name: - duser_name = self.user_manager.get_by_name( - password_credentials.user_name) - if duser_name: - raise fault.UserConflictFault( - "A user with that name already exists") - if duser.password: - raise fault.BadRequestFault( - "Password credentials already available.") - values = {'id': user_id, 'password': password_credentials.password, - 'name': password_credentials.user_name} - self.user_manager.update(values) - duser = self.user_manager.get(user_id) - return PasswordCredentials(duser.name, duser.password) - - @staticmethod - def get_links(url, prev, next, limit): - """Method to form and return pagination links.""" - links = [] - if prev: - links.append(atom.Link('prev', "%s?marker=%s&limit=%s" \ - % (url, prev, limit))) - if next: - links.append(atom.Link('next', "%s?marker=%s&limit=%s" \ - % (url, next, limit))) - return links diff --git a/keystone/logic/signer.py b/keystone/logic/signer.py deleted file mode 100644 index 4ca171be77..0000000000 --- a/keystone/logic/signer.py +++ /dev/null @@ -1,166 +0,0 @@ -# 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. -# -# PORTIONS OF THIS FILE ARE FROM: -# http://code.google.com/p/boto -# Copyright (c) 2006-2009 Mitch Garnaat http://garnaat.org/ -# -# Permission is hereby granted, free of charge, to any person obtaining a -# copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, dis- -# tribute, sublicense, and/or sell copies of the Software, and to permit -# persons to whom the Software is furnished to do so, subject to the fol- -# lowing 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 MERCHANTABIL- -# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -# SHALL THE AUTHOR 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. - -""" -Utility class for parsing signed AMI manifests. -""" - -import base64 -import hashlib -import hmac -import logging -import urllib - - -LOG = logging.getLogger('keystone.signer') - - -class Signer(object): - """Hacked up code from boto/connection.py""" - - # pylint: disable=E1101 - def __init__(self, secret_key): - secret_key = secret_key.encode() - self.hmac = hmac.new(secret_key, digestmod=hashlib.sha1) - if hashlib.sha256: - self.hmac_256 = hmac.new(secret_key, digestmod=hashlib.sha256) - - def generate(self, credentials, s3=False): - """Generate auth string according to what SignatureVersion is given.""" - if s3: - return self._calc_signature_s3(credentials.verb, - credentials.expire, - credentials.path, - credentials.content_type, - credentials.content_md5, - credentials.xheaders) - if credentials.params['SignatureVersion'] == '0': - return self._calc_signature_0(credentials.params) - if credentials.params['SignatureVersion'] == '1': - return self._calc_signature_1(credentials.params) - if credentials.params['SignatureVersion'] == '2': - return self._calc_signature_2(credentials.params, - credentials.verb, - credentials.host, - credentials.path) - raise Exception('Unknown Signature Version: %s' % - credentials.params['SignatureVersion']) - - @staticmethod - def _get_utf8_value(value): - """Get the UTF8-encoded version of a value.""" - if not isinstance(value, str) and not isinstance(value, unicode): - value = str(value) - if isinstance(value, unicode): - return value.encode('utf-8') - else: - return value - - def _calc_signature_0(self, params): - """Generate AWS signature version 0 string.""" - s = params['Action'] + params['Timestamp'] - self.hmac.update(s) - return base64.b64encode(self.hmac.digest()) - - def _calc_signature_1(self, params): - """Generate AWS signature version 1 string.""" - keys = params.keys() - keys.sort(cmp=lambda x, y: cmp(x.lower(), y.lower())) - for key in keys: - self.hmac.update(key) - val = self._get_utf8_value(params[key]) - self.hmac.update(val) - return base64.b64encode(self.hmac.digest()) - - def _calc_signature_2(self, params, verb, server_string, path): - """Generate AWS signature version 2 string.""" - LOG.debug('using _calc_signature_2') - string_to_sign = '%s\n%s\n%s\n' % (verb, server_string, path) - if self.hmac_256: - current_hmac = self.hmac_256 - params['SignatureMethod'] = 'HmacSHA256' - else: - current_hmac = self.hmac - params['SignatureMethod'] = 'HmacSHA1' - keys = params.keys() - keys.sort() - pairs = [] - for key in keys: - val = self._get_utf8_value(params[key]) - val = urllib.quote(val, safe='-_~') - pairs.append(urllib.quote(key, safe='') + '=' + val) - qs = '&'.join(pairs) - LOG.debug('query string: %s', qs) - string_to_sign += qs - LOG.debug('string_to_sign: %s', string_to_sign) - current_hmac.update(string_to_sign) - b64 = base64.b64encode(current_hmac.digest()) - LOG.debug('len(b64)=%d', len(b64)) - LOG.debug('base64 encoded digest: %s', b64) - return b64 - - # pylint: disable=R0913 - def _calc_signature_s3(self, verb, expire, path, content_type, content_md5, - xheaders): - """Generate AWS signature for S3 string.""" - LOG.debug('using _calc_signature_s3') - string_to_sign = '%s\n%s\n%s\n%s\n' % ( - verb, content_md5, content_type, expire) - if xheaders: - xheader_keys = xheaders.keys() - xheader_keys.sort() - for key in xheader_keys: - string_to_sign += '%s:%s\n' % (key, xheaders[key]) - string_to_sign += path - current_hmac = self.hmac - LOG.debug('string_to_sign: %s', string_to_sign) - current_hmac.update(string_to_sign) - b64 = base64.b64encode(current_hmac.digest()) - LOG.debug('len(b64)=%d', len(b64)) - LOG.debug('base64 encoded digest: %s', b64) - return b64 - - -if __name__ == '__main__': - # pylint: disable=E1121 - print Signer('foo').generate({'SignatureMethod': 'HmacSHA256', - 'SignatureVersion': '2'}, - 'get', 'server', '/foo') diff --git a/keystone/logic/types/__init__.py b/keystone/logic/types/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/keystone/logic/types/atom.py b/keystone/logic/types/atom.py deleted file mode 100644 index a4e03a9fd5..0000000000 --- a/keystone/logic/types/atom.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright (c) 2010-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. - -# pylint: disable=C0103 - -from lxml import etree - - -class Link(object): - """An atom link""" - - def __init__(self, rel, href, link_type=None, hreflang=None, title=None): - self.rel = rel - self.href = href - self.link_type = link_type - self.hreflang = hreflang - self.title = title - - def to_dict(self): - links = {} - if self.link_type: - links["link_type"] = self.link_type - if self.hreflang: - links["hreflang"] = self.hreflang - if self.title: - links["title"] = self.title - - links["rel"] = self.rel - links["href"] = self.href - return {'links': links} - - def to_dom(self): - ATOM_NAMESPACE = "http://www.w3.org/2005/Atom" - ATOM = "{%s}" % ATOM_NAMESPACE - NSMAP = {'atom': ATOM_NAMESPACE} - dom = etree.Element(ATOM + "link", nsmap=NSMAP) - if self.link_type: - dom.set("link_type", self.link_type) - if self.link_type: - dom.set("hreflang", self.hreflang) - if self.title: - dom.set("title", self.title) - dom.set("rel", self.rel) - dom.set("href", self.href) - return dom diff --git a/keystone/logic/types/auth.py b/keystone/logic/types/auth.py deleted file mode 100755 index ace6aa6b21..0000000000 --- a/keystone/logic/types/auth.py +++ /dev/null @@ -1,673 +0,0 @@ -# Copyright (c) 2010-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. - -# pylint: disable=C0103,R0912,R0913,R0914 - -import json -from lxml import etree -from keystone.logic.types import fault -import keystone.backends.api as db_api -from keystone import utils - - -class AuthBase(object): - def __init__(self, tenant_id=None, tenant_name=None): - self.tenant_id = tenant_id - self.tenant_name = tenant_name - - @staticmethod - def _validate_auth(obj, *valid_keys): - if not 'auth' in obj: - raise fault.BadRequestFault('Expecting auth') - - auth = obj.get('auth') - - for key in auth: - if not key in valid_keys: - raise fault.BadRequestFault('Invalid attribute(s): %s' % key) - - if auth.get('tenantId') and auth.get('tenantName'): - raise fault.BadRequestFault( - 'Expecting either Tenant ID or Tenant Name, but not both') - - return auth - - @staticmethod - def _validate_key(obj, key, *required_keys): - if not key in obj: - raise fault.BadRequestFault('Expecting %s' % key) - - ret = obj[key] - - for skey in ret: - if not skey in required_keys: - raise fault.BadRequestFault('Invalid attribute(s): %s' % skey) - - for required_key in required_keys: - if not ret.get(required_key): - raise fault.BadRequestFault('Expecting %s:%s' % - (key, required_key)) - return ret - - -class AuthWithUnscopedToken(AuthBase): - def __init__(self, token_id, tenant_id=None, tenant_name=None): - super(AuthWithUnscopedToken, self).__init__(tenant_id, tenant_name) - self.token_id = token_id - - @staticmethod - def from_xml(xml_str): - try: - dom = etree.Element("root") - dom.append(etree.fromstring(xml_str)) - root = dom.find("{http://docs.openstack.org/identity/api/v2.0}" - "auth") - if root is None: - raise fault.BadRequestFault("Expecting auth") - token = root.find("{http://docs.openstack.org/identity/api/v2.0}" - "token") - if token is None: - raise fault.BadRequestFault("Expecting token") - - token_id = token.get("id") - tenant_id = root.get("tenantId") - tenant_name = root.get("tenantName") - utils.check_empty_string(token_id, "Expecting a token id.") - if tenant_id and tenant_name: - raise fault.BadRequestFault( - "Expecting either Tenant ID or Tenant Name, but not both") - - return AuthWithUnscopedToken(token_id, tenant_id, tenant_name) - except etree.LxmlError as e: - raise fault.BadRequestFault("Cannot parse password access", str(e)) - - @staticmethod - def from_json(json_str): - try: - obj = json.loads(json_str) - - auth = AuthBase._validate_auth(obj, 'tenantId', 'tenantName', - 'token') - token = AuthBase._validate_key(auth, 'token', 'id') - - return AuthWithUnscopedToken(token['id'], - auth.get('tenantId'), - auth.get('tenantName')) - except (ValueError, TypeError) as e: - raise fault.BadRequestFault("Cannot parse auth", str(e)) - - -class AuthWithPasswordCredentials(AuthBase): - def __init__(self, username, password, tenant_id=None, tenant_name=None): - super(AuthWithPasswordCredentials, self).__init__(tenant_id, - tenant_name) - self.username = username - self.password = password - - @staticmethod - def from_xml(xml_str): - try: - dom = etree.Element("root") - dom.append(etree.fromstring(xml_str)) - root = dom.find("{http://docs.openstack.org/identity/api/v2.0}" - "auth") - if root is None: - raise fault.BadRequestFault("Expecting auth") - tenant_id = root.get("tenantId") - tenant_name = root.get("tenantName") - password_credentials = \ - root.find("{http://docs.openstack.org/identity/api/v2.0}" - "passwordCredentials") - if password_credentials is None: - raise fault.BadRequestFault("Expecting passwordCredentials") - username = password_credentials.get("username") - utils.check_empty_string(username, "Expecting a username") - password = password_credentials.get("password") - utils.check_empty_string(password, "Expecting a password") - - if tenant_id and tenant_name: - raise fault.BadRequestFault( - "Expecting either Tenant ID or Tenant Name, but not both") - - return AuthWithPasswordCredentials(username, password, tenant_id, - tenant_name) - except etree.LxmlError as e: - raise fault.BadRequestFault("Cannot parse password access", str(e)) - - @staticmethod - def from_json(json_str): - try: - obj = json.loads(json_str) - - auth = AuthBase._validate_auth(obj, 'tenantId', 'tenantName', - 'passwordCredentials', 'token') - cred = AuthBase._validate_key(auth, 'passwordCredentials', - 'username', 'password') - - return AuthWithPasswordCredentials(cred['username'], - cred['password'], - auth.get('tenantId'), - auth.get('tenantName')) - except (ValueError, TypeError) as e: - raise fault.BadRequestFault("Cannot parse auth", str(e)) - - -class Ec2Credentials(object): - """Credentials based on username, access_key, signature and data. - - @type access: str - @param access: Access key for user in the form of access:project. - - @type signature: str - @param signature: Signature of the request. - - @type params: dictionary of str - @param params: Web paramaters used for the signature. - - @type verb: str - @param verb: Web request verb ('GET' or 'POST'). - - @type host: str - @param host: Web request host string (including port). - - @type path: str - @param path: Web request path. - - """ - - def __init__(self, access, signature, verb, - host, path, params): - self.access = access - self.signature = signature - self.verb = verb - self.host = host - self.path = path - self.params = params - - @staticmethod - def from_xml(xml_str): - try: - dom = etree.Element("root") - dom.append(etree.fromstring(xml_str)) - root = dom.find("{http://docs.openstack.org/identity/api/v2.0}" - "auth") - xmlns = "http://docs.openstack.org/identity/api/ext/OS-KSEC2/v1.0" - if root is None: - root = dom.find("{%s}ec2Credentials" % xmlns) - else: - root = root.find("{%s}ec2Credentials" % xmlns) - if root is None: - raise fault.BadRequestFault("Expecting ec2Credentials") - access = root.get("key") - utils.check_empty_string(access, "Expecting an access key.") - signature = root.get("signature") - utils.check_empty_string(signature, "Expecting a signature.") - verb = root.get("verb") - utils.check_empty_string(verb, "Expecting a verb.") - host = root.get("host") - utils.check_empty_string(signature, "Expecting a host.") - path = root.get("path") - utils.check_empty_string(signature, "Expecting a path.") - # TODO(vish): parse xml params - params = {} - return Ec2Credentials(access, signature, verb, host, path, params) - except etree.LxmlError as e: - raise fault.BadRequestFault("Cannot parse password credentials", - str(e)) - - @staticmethod - def from_json(json_str): - try: - root = json.loads(json_str) - if "auth" in root: - obj = root['auth'] - else: - obj = root - - if "OS-KSEC2:ec2Credentials" in obj: - cred = obj["OS-KSEC2:ec2Credentials"] - elif "ec2Credentials" in obj: - cred = obj["ec2Credentials"] - else: - raise fault.BadRequestFault("Expecting ec2Credentials") - # Check that fields are valid - invalid = [key for key in cred if key not in\ - ['username', 'access', 'signature', 'params', - 'verb', 'host', 'path']] - if invalid != []: - raise fault.BadRequestFault("Invalid attribute(s): %s" - % invalid) - if not "access" in cred: - raise fault.BadRequestFault("Expecting an access key") - access = cred["access"] - if not "signature" in cred: - raise fault.BadRequestFault("Expecting a signature") - signature = cred["signature"] - if not "verb" in cred: - raise fault.BadRequestFault("Expecting a verb") - verb = cred["verb"] - if not "host" in cred: - raise fault.BadRequestFault("Expecting a host") - host = cred["host"] - if not "path" in cred: - raise fault.BadRequestFault("Expecting a path") - path = cred["path"] - if not "params" in cred: - raise fault.BadRequestFault("Expecting params") - params = cred["params"] - return Ec2Credentials(access, signature, verb, host, path, params) - except (ValueError, TypeError) as e: - raise fault.BadRequestFault("Cannot parse password credentials", - str(e)) - - -# pylint: disable=R0902 -class S3Credentials(object): - """Credentials based on username, access_key, signature and data. - - @type access: str - @param access: Access key for user in the form of access:project. - - @type signature: str - @param signature: Signature of the request. - - @type verb: str - @param verb: Web request verb ('GET' or 'POST'). - - @type host: expire - @param host: Web request expire time. - - @type path: str - @param path: Web request path. - - @type expire: str - @param expire: Web request expire. - - @type content_type: str - @param content_type: Web request content contenttype. - - @type content_md5: str - @param content_md5: Web request content contentmd5. - - @type xheaders: str - @param xheaders: Web request content extended headers. - - """ - - def __init__(self, access, signature, verb, path, expire, content_type, - content_md5, xheaders): - self.access = access - self.signature = signature - self.verb = verb - self.path = path - self.expire = expire - self.content_type = content_type - self.content_md5 = content_md5 - self.xheaders = xheaders - - @staticmethod - def from_xml(xml_str): - try: - dom = etree.Element("root") - dom.append(etree.fromstring(xml_str)) - root = dom.find("{http://docs.openstack.org/identity/api/v2.0}" - "auth") - xmlns = "http://docs.openstack.org/identity/api/ext/OS-KSS3/v1.0" - if root is None: - root = dom.find("{%s}s3Credentials" % xmlns) - else: - root = root.find("{%s}s3Credentials" % xmlns) - - if root is None: - raise fault.BadRequestFault("Expecting s3Credentials") - access = root.get("access") - if access == None: - raise fault.BadRequestFault("Expecting an access key") - signature = root.get("signature") - if signature == None: - raise fault.BadRequestFault("Expecting a signature") - verb = root.get("verb") - if verb == None: - raise fault.BadRequestFault("Expecting a verb") - path = root.get("path") - if path == None: - raise fault.BadRequestFault("Expecting a path") - expire = root.get("expire") - if expire == None: - raise fault.BadRequestFault("Expecting a expire") - content_type = root.get("content_type", '') - content_md5 = root.get("content_md5", '') - xheaders = root.get("xheaders", None) - return S3Credentials(access, signature, verb, path, expire, - content_type, content_md5, xheaders) - except etree.LxmlError as e: - raise fault.BadRequestFault("Cannot parse password credentials", - str(e)) - - @staticmethod - def from_json(json_str): - try: - root = json.loads(json_str) - if "auth" in root: - obj = root['auth'] - else: - obj = root - - if "OS-KSS3:s3Credentials" in obj: - cred = obj["OS-KSS3:s3Credentials"] - elif "s3Credentials" in obj: - cred = obj["s3Credentials"] - else: - raise fault.BadRequestFault("Expecting s3Credentials") - - # Check that fields are valid - invalid = [key for key in cred if key not in\ - ['username', 'access', 'signature', 'verb', 'expire', - 'path', 'content_type', 'content_md5', 'xheaders']] - if invalid != []: - raise fault.BadRequestFault("Invalid attribute(s): %s" - % invalid) - if not "access" in cred: - raise fault.BadRequestFault("Expecting an access key") - access = cred["access"] - if not "signature" in cred: - raise fault.BadRequestFault("Expecting a signature") - signature = cred["signature"] - if not "verb" in cred: - raise fault.BadRequestFault("Expecting a verb") - verb = cred["verb"] - if not "path" in cred: - raise fault.BadRequestFault("Expecting a path") - path = cred["path"] - if not "expire" in cred: - raise fault.BadRequestFault("Expecting a expire") - expire = cred["expire"] - content_type = cred.get("content_type", '') - content_md5 = cred.get("content_md5", '') - xheaders = cred.get("xheaders", None) - return S3Credentials(access, signature, verb, path, expire, - content_type, content_md5, xheaders) - except (ValueError, TypeError) as e: - raise fault.BadRequestFault("Cannot parse password credentials", - str(e)) - - -class Tenant(object): - """Provides the scope of a token""" - - def __init__(self, id, name): - self.id = id - self.name = name - - -class Token(object): - """An auth token.""" - - def __init__(self, expires, token_id, tenant=None): - assert tenant is None or isinstance(tenant, Tenant) - - self.expires = expires - self.id = token_id - self.tenant = tenant - - -class User(object): - """A user.""" - - id = None - username = None - tenant_id = None - tenant_name = None - rolegrants = None - - def __init__(self, id, username, tenant_id, tenant_name, rolegrants=None): - self.id = id - self.username = username - self.tenant_id = tenant_id - self.tenant_name = tenant_name - self.rolegrants = rolegrants - - -class AuthData(object): - """Authentation Information returned upon successful login. - - This class handles rendering to JSON and XML. It renders - the token, the user data, the roles, and the service catalog. - - The list of endpoint URLs in the service catalog can be filtered by - URL type. For example, when we respond to a public call from a user - without elevated privileges, the "adminURL" is not returned. The - url_types paramater in the initializer lists the types to return. - The actual authorization is done in logic/service.py - """ - - def __init__(self, token, user, base_urls=None, url_types=None): - self.token = token - self.user = user - self.base_urls = base_urls - if url_types is None: - self.url_types = ["internal", "public", "admin"] - else: - self.url_types = url_types - self.d = {} - if self.base_urls is not None: - self.__convert_baseurls_to_dict() - - def to_xml(self): - dom = etree.Element("access", - xmlns="http://docs.openstack.org/identity/api/v2.0") - token = etree.Element("token", - expires=self.token.expires.isoformat()) - token.set("id", self.token.id) - if self.token.tenant: - tenant = etree.Element("tenant", - id=unicode(self.token.tenant.id), - name=unicode(self.token.tenant.name)) - token.append(tenant) - dom.append(token) - - user = etree.Element("user", - id=unicode(self.user.id), - name=unicode(self.user.username)) - dom.append(user) - - if self.user.rolegrants is not None: - user.append(self.user.rolegrants.to_dom()) - - if self.base_urls is not None and len(self.base_urls) > 0: - service_catalog = etree.Element("serviceCatalog") - for key, key_base_urls in self.d.items(): - dservice = db_api.SERVICE.get(key) - if not dservice: - raise fault.ItemNotFoundFault( - "The service could not be found") - service = etree.Element("service", - name=dservice.name, type=dservice.type) - for base_url in key_base_urls: - include_this_endpoint = False - endpoint = etree.Element("endpoint") - if base_url.region: - endpoint.set("region", base_url.region) - for url_kind in self.url_types: - base_url_item = getattr(base_url, url_kind + "_url") - if base_url_item: - if '%tenant_id%' in base_url_item: - if self.token.tenant: - # Don't return tenant endpoints if token - # not scoped to a tenant - endpoint.set(url_kind + "URL", - base_url_item.replace('%tenant_id%', - str(self.token.tenant.id))) - endpoint.set('tenantId', - str(self.token.tenant.id)) - include_this_endpoint = True - else: - endpoint.set(url_kind + "URL", base_url_item) - include_this_endpoint = True - if include_this_endpoint: - endpoint.set("id", str(base_url.id)) - if hasattr(base_url, "version_id"): - if base_url.version_id: - endpoint.set("versionId", - str(base_url.version_id)) - service.append(endpoint) - if service.find("endpoint") is not None: - service_catalog.append(service) - dom.append(service_catalog) - return etree.tostring(dom) - - def __convert_baseurls_to_dict(self): - for base_url in self.base_urls: - if base_url.service_id not in self.d: - self.d[base_url.service_id] = list() - self.d[base_url.service_id].append(base_url) - - def to_json(self): - token = {} - token["id"] = self.token.id - token["expires"] = self.token.expires.isoformat() - if self.token.tenant: - tenant = { - 'id': unicode(self.token.tenant.id), - 'name': unicode(self.token.tenant.name)} - token['tenant'] = tenant # v2.0/Diablo contract - token['tenants'] = [tenant] # missed use case in v2.0 - auth = {} - auth["token"] = token - auth['user'] = { - 'id': unicode(self.user.id), - 'name': unicode(self.user.username)} - - if self.user.rolegrants is not None: - auth['user']["roles"] = self.user.rolegrants.to_json_values() - - if self.base_urls is not None and len(self.base_urls) > 0: - service_catalog = [] - for key, key_base_urls in self.d.items(): - service = {} - endpoints = [] - for base_url in key_base_urls: - include_this_endpoint = False - endpoint = {} - if base_url.region: - endpoint["region"] = base_url.region - for url_kind in self.url_types: - base_url_item = getattr(base_url, url_kind + "_url") - if base_url_item: - if '%tenant_id%' in base_url_item: - if self.token.tenant: - # Don't return tenant endpoints if token - # not scoped to a tenant - endpoint[url_kind + "URL"] = \ - base_url_item.replace('%tenant_id%', - str(self.token.tenant.id)) - endpoint['tenantId'] = \ - str(self.token.tenant.id) - include_this_endpoint = True - else: - endpoint[url_kind + "URL"] = base_url_item - include_this_endpoint = True - if include_this_endpoint: - endpoint['id'] = str(base_url.id) - if hasattr(base_url, 'version_id'): - if base_url.version_id: - endpoint['versionId'] = \ - str(base_url.version_id) - endpoints.append(endpoint) - dservice = db_api.SERVICE.get(key) - if not dservice: - raise fault.ItemNotFoundFault( - "The service could not be found for" + str(key)) - if len(endpoints): - service["name"] = dservice.name - service["type"] = dservice.type - service["endpoints"] = endpoints - service_catalog.append(service) - auth["serviceCatalog"] = service_catalog - ret = {} - ret["access"] = auth - return json.dumps(ret) - - -class ValidateData(object): - """Authentation Information returned upon successful token validation.""" - - token = None - user = None - - def __init__(self, token, user): - self.token = token - self.user = user - - def to_xml(self): - dom = etree.Element("access", - xmlns="http://docs.openstack.org/identity/api/v2.0") - - token = etree.Element("token", - id=unicode(self.token.id), - expires=self.token.expires.isoformat()) - - if self.token.tenant: - tenant = etree.Element("tenant", - id=unicode(self.token.tenant.id), - name=unicode(self.token.tenant.name)) - token.append(tenant) - - user = etree.Element("user", - id=unicode(self.user.id), - name=unicode(self.user.username)) - - if self.user.tenant_id is not None: - user.set('tenantId', unicode(self.user.tenant_id)) - if self.user.tenant_name is not None: - user.set('tenantName', unicode(self.user.tenant_name)) - - if self.user.rolegrants is not None: - user.append(self.user.rolegrants.to_dom()) - - dom.append(token) - dom.append(user) - return etree.tostring(dom) - - def to_json(self): - token = { - "id": unicode(self.token.id), - "expires": self.token.expires.isoformat()} - - if self.token.tenant: - tenant = { - 'id': unicode(self.token.tenant.id), - 'name': unicode(self.token.tenant.name)} - token['tenant'] = tenant # v2.0/Diablo contract - token['tenants'] = [tenant] # missed use case in v2.0 - - user = { - "id": unicode(self.user.id), - "name": unicode(self.user.username), - # TODO(ziad) temporary until we are comfortable clients are updated - "username": unicode(self.user.username)} - - if self.user.tenant_id is not None: - user['tenantId'] = unicode(self.user.tenant_id) - if self.user.tenant_name is not None: - user['tenantName'] = unicode(self.user.tenant_name) - - if self.user.rolegrants is not None: - user["roles"] = self.user.rolegrants.to_json_values() - - return json.dumps({ - "access": { - "token": token, - "user": user}}) diff --git a/keystone/logic/types/credential.py b/keystone/logic/types/credential.py deleted file mode 100644 index 40e99f3cce..0000000000 --- a/keystone/logic/types/credential.py +++ /dev/null @@ -1,125 +0,0 @@ -# Copyright (c) 2010-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. -import json -from lxml import etree - -from keystone.logic.types import fault -from keystone import utils - - -class PasswordCredentials(object): - def __init__(self, user_name, password): - self.user_name = user_name - self.password = password - - @staticmethod - def from_xml(xml_str): - try: - dom = etree.Element("root") - dom.append(etree.fromstring(xml_str)) - root = dom.find("{http://docs.openstack.org/identity/api/v2.0}" \ - "passwordCredentials") - if root is None: - raise fault.BadRequestFault("Expecting passwordCredentials") - user_name = root.get("username") - password = root.get("password") - utils.check_empty_string(password, "Expecting a password.") - return PasswordCredentials(user_name, password) - except etree.LxmlError as e: - raise fault.BadRequestFault( - "Cannot parse passwordCredentials", str(e)) - - @staticmethod - def from_json(json_str): - try: - obj = json.loads(json_str) - if not "passwordCredentials" in obj: - raise fault.BadRequestFault("Expecting passwordCredentials") - password_credentials = obj["passwordCredentials"] - - # Check that fields are valid - invalid = [key for key in password_credentials if key not in\ - ['username', 'password']] - if invalid != []: - raise fault.BadRequestFault("Invalid attribute(s): %s" - % invalid) - - user_name = password_credentials.get('username') - password = password_credentials.get('password') - utils.check_empty_string(password, "Expecting a password.") - return PasswordCredentials(user_name, password) - except (ValueError, TypeError) as e: - raise fault.BadRequestFault( - "Cannot parse passwordCredentials", str(e)) - - def to_dom(self): - dom = etree.Element("passwordCredentials", - xmlns="http://docs.openstack.org/identity/api/v2.0") - if self.user_name: - dom.set("username", unicode(self.user_name)) - if self.password: - dom.set("password", unicode(self.password)) - return dom - - def to_xml(self): - return etree.tostring(self.to_dom()) - - def to_dict(self): - password_credentials = {} - if self.user_name: - password_credentials["username"] = unicode(self.user_name) - if self.password: - password_credentials['password'] = unicode(self.password) - return {'passwordCredentials': password_credentials} - - def to_json(self): - return json.dumps(self.to_dict()) - - -class Credentials(object): - "A collection of credentials." - - def __init__(self, values, links): - self.values = values - self.links = links - - def to_xml(self): - dom = etree.Element("credentials") - dom.set(u"xmlns", "http://docs.openstack.org/identity/api/v2.0") - - for t in self.values: - dom.append(t.to_dom()) - - for t in self.links: - dom.append(t.to_dom()) - - return etree.tostring(dom) - - def to_dom(self): - dom = etree.Element("credentials") - dom.set(u"xmlns", "http://docs.openstack.org/identity/api/v2.0") - - for t in self.values: - dom.append(t.to_dom()) - - for t in self.links: - dom.append(t.to_dom()) - - return dom - - def to_json(self): - values = [t.to_dict() for t in self.values] - links = [t.to_dict()["links"] for t in self.links] - return json.dumps({"credentials": values, "credentials_links": links}) diff --git a/keystone/logic/types/endpoint.py b/keystone/logic/types/endpoint.py deleted file mode 100644 index 3b4c8d64ff..0000000000 --- a/keystone/logic/types/endpoint.py +++ /dev/null @@ -1,363 +0,0 @@ -# Copyright (c) 2010-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. - -import json -from lxml import etree -from keystone.logic.types import fault - - -class EndpointTemplate(object): - """Document me!""" - - @staticmethod - def from_xml(xml_str): - try: - dom = etree.Element("root") - dom.append(etree.fromstring(xml_str)) - root = dom.find( - "{http://docs.openstack.org/identity"\ - "/api/ext/OS-KSCATALOG/v1.0}" \ - "endpointTemplate") - if root is None: - raise fault.BadRequestFault("Expecting endpointTemplate") - id = root.get("id") - region = root.get("region") - name = root.get("name") - type = root.get("type") - public_url = root.get("publicURL") - admin_url = root.get("adminURL") - internal_url = root.get("internalURL") - enabled = root.get("enabled") - is_global = root.get("global") - version = root.find( - "{http://docs.openstack.org/identity/"\ - "api/v2.0}" \ - "version") - version_id = None - version_info = None - version_list = None - if version is not None: - if version.get('id'): - version_id = version.get("id") - if version.get('info'): - version_info = version.get("info") - if version.get('list'): - version_list = version.get("list") - - return EndpointTemplate(id, region, - name, type, public_url, admin_url, - internal_url, enabled, is_global, - version_id, version_list, version_info) - except etree.LxmlError as e: - raise fault.BadRequestFault("Cannot parse endpointTemplate", - str(e)) - - @staticmethod - def from_json(json_str): - try: - obj = json.loads(json_str) - region = None - name = None - type = None - public_url = None - admin_url = None - internal_url = None - enabled = None - is_global = None - version_id = None - version_list = None - version_info = None - if not "OS-KSCATALOG:endpointTemplate" in obj: - raise fault.BadRequestFault( - "Expecting OS-KSCATALOG:endpointTemplate") - endpoint_template = obj["OS-KSCATALOG:endpointTemplate"] - - # Check that fields are valid - invalid = [key for key in endpoint_template if key not in - ['id', 'region', 'name', 'type', 'publicURL', - 'adminURL', 'internalURL', 'enabled', 'global', - 'versionId', 'versionInfo', 'versionList']] - if invalid != []: - raise fault.BadRequestFault("Invalid attribute(s): %s" - % invalid) - - if not "id" in endpoint_template: - id = None - else: - id = endpoint_template["id"] - - if 'region' in endpoint_template: - region = endpoint_template["region"] - if 'name' in endpoint_template: - name = endpoint_template["name"] - if 'type' in endpoint_template: - type = endpoint_template["type"] - if 'publicURL' in endpoint_template: - public_url = endpoint_template["publicURL"] - if 'adminURL' in endpoint_template: - admin_url = endpoint_template["adminURL"] - if 'internalURL' in endpoint_template: - internal_url = endpoint_template["internalURL"] - if 'enabled' in endpoint_template: - enabled = endpoint_template["enabled"] - if 'global' in endpoint_template: - is_global = endpoint_template["global"] - if 'versionId' in endpoint_template: - version_id = endpoint_template["versionId"] - else: - version_id = None - if 'versionInfo' in endpoint_template: - version_info = endpoint_template["versionInfo"] - else: - version_info = None - if 'versionList' in endpoint_template: - version_list = endpoint_template["versionList"] - else: - version_list = None - - return EndpointTemplate( - id, region, name, type, public_url, admin_url, - internal_url, enabled, is_global, version_id, - version_list, version_info) - except (ValueError, TypeError) as e: - raise fault.BadRequestFault(\ - "Cannot parse endpointTemplate", str(e)) - - def __init__(self, id, region, name, type, public_url, admin_url, - internal_url, enabled, is_global, - version_id=None, version_list=None, version_info=None): - self.id = id - self.region = region - self.name = name - self.type = type - self.public_url = public_url - self.admin_url = admin_url - self.internal_url = internal_url - self.enabled = bool(enabled) - self.is_global = bool(is_global) - self.version_id = version_id - self.version_list = version_list - self.version_info = version_info - - def to_dom(self): - dom = etree.Element("endpointTemplate", - xmlns="http://docs.openstack.org/" - "identity/api/ext/OS-KSCATALOG/v1.0") - if self.id: - dom.set("id", str(self.id)) - if self.region: - dom.set("region", self.region) - if self.name: - dom.set("name", str(self.name)) - if self.type: - dom.set("type", str(self.type)) - if self.public_url: - dom.set("publicURL", self.public_url) - if self.admin_url: - dom.set("adminURL", self.admin_url) - if self.internal_url: - dom.set("internalURL", self.internal_url) - if self.enabled: - dom.set("enabled", str(self.enabled).lower()) - if self.is_global: - dom.set("global", str(self.is_global).lower()) - version = etree.Element("version", - xmlns="http://docs.openstack.org" - "/identity/api/v2.0") - if self.version_id: - version.set("id", self.version_id) - if self.version_info: - version.set("info", self.version_info) - if self.version_list: - version.set("list", self.version_list) - dom.append(version) - return dom - - def to_xml(self): - return etree.tostring(self.to_dom()) - - def to_dict(self): - endpoint_template = {} - if self.id: - endpoint_template["id"] = unicode(self.id) - if self.region: - endpoint_template["region"] = self.region - if self.name: - endpoint_template["name"] = self.name - if self.type: - endpoint_template["type"] = self.type - if self.public_url: - endpoint_template["publicURL"] = self.public_url - if self.admin_url: - endpoint_template["adminURL"] = self.admin_url - if self.internal_url: - endpoint_template["internalURL"] = self.internal_url - if self.enabled: - endpoint_template["enabled"] = self.enabled - if self.is_global: - endpoint_template["global"] = self.is_global - if self.version_id: - endpoint_template["versionId"] = self.version_id - if self.version_info: - endpoint_template["versionInfo"] = self.version_info - if self.version_list: - endpoint_template["versionList"] = self.version_list - return {'OS-KSCATALOG:endpointTemplate': endpoint_template} - - def to_json(self): - return json.dumps(self.to_dict()) - - -class EndpointTemplates(object): - """A collection of endpointTemplates.""" - - def __init__(self, values, links): - self.values = values - self.links = links - - def to_xml(self): - dom = etree.Element("endpointTemplates") - dom.set(u"xmlns", - "http://docs.openstack.org/identity/api/ext/OS-KSCATALOG/v1.0") - - for t in self.values: - dom.append(t.to_dom()) - - for t in self.links: - dom.append(t.to_dom()) - - return etree.tostring(dom) - - def to_json(self): - values = [t.to_dict()["OS-KSCATALOG:endpointTemplate"] - for t in self.values] - links = [t.to_dict()["links"] for t in self.links] - return json.dumps({"OS-KSCATALOG:endpointTemplates": values, - "OS-KSCATALOG:endpointTemplates_links": links}) - - -class Endpoint(object): - """Document me!""" - - def __init__(self, id, tenant_id, region, - name, type, public_url, admin_url, - internal_url, version_id=None, - version_list=None, version_info=None): - self.id = id - self.tenant_id = tenant_id - self.region = region - self.name = name - self.type = type - self.public_url = self.substitute_tenant_id(public_url) - self.admin_url = self.substitute_tenant_id(admin_url) - self.internal_url = self.substitute_tenant_id(internal_url) - self.version_id = version_id - self.version_list = version_list - self.version_info = version_info - - def to_dom(self): - dom = etree.Element("endpoint", - xmlns="http://docs.openstack.org/identity/api/v2.0") - if self.id: - dom.set("id", str(self.id)) - if self.tenant_id: - dom.set("tenantId", str(self.tenant_id)) - if self.region: - dom.set("region", self.region) - if self.name: - dom.set("name", str(self.name)) - if self.type: - dom.set("type", str(self.type)) - if self.public_url: - dom.set("publicURL", self.public_url) - if self.admin_url: - dom.set("adminURL", self.admin_url) - if self.internal_url: - dom.set("internalURL", self.internal_url) - version = etree.Element("version", - xmlns="http://docs.openstack.org" - "/identity/api/v2.0") - if self.version_id: - version.set("id", self.version_id) - if self.version_info: - version.set("info", self.version_info) - if self.version_list: - version.set("list", self.version_list) - dom.append(version) - return dom - - def to_xml(self): - return etree.tostring(self.to_dom()) - - def to_dict(self): - endpoint = {} - if self.id: - endpoint["id"] = self.id - if self.tenant_id: - endpoint["tenantId"] = self.tenant_id - if self.region: - endpoint["region"] = self.region - if self.name: - endpoint["name"] = self.name - if self.type: - endpoint["type"] = self.type - if self.public_url: - endpoint["publicURL"] = self.public_url - if self.admin_url: - endpoint["adminURL"] = self.admin_url - if self.internal_url: - endpoint["internalURL"] = self.internal_url - if self.version_id: - endpoint["versionId"] = self.version_id - if self.version_info: - endpoint["versionInfo"] = self.version_info - if self.version_list: - endpoint["versionList"] = self.version_list - return {'endpoint': endpoint} - - def substitute_tenant_id(self, url): - if url: - return url.replace('%tenant_id%', - str(self.tenant_id)) - return url - - def to_json(self): - return json.dumps(self.to_dict()) - - -class Endpoints(object): - """A collection of endpoints.""" - - def __init__(self, values, links): - self.values = values - self.links = links - - def to_xml(self): - dom = etree.Element("endpoints") - dom.set(u"xmlns", - "http://docs.openstack.org/identity/api/v2.0") - - for t in self.values: - dom.append(t.to_dom()) - - for t in self.links: - dom.append(t.to_dom()) - - return etree.tostring(dom) - - def to_json(self): - values = [t.to_dict()["endpoint"] for t in self.values] - links = [t.to_dict()["links"] for t in self.links] - return json.dumps({"endpoints": values, "endpoints_links": links}) diff --git a/keystone/logic/types/extension.py b/keystone/logic/types/extension.py deleted file mode 100644 index bc8bab5970..0000000000 --- a/keystone/logic/types/extension.py +++ /dev/null @@ -1,12 +0,0 @@ -class Extensions(object): - """An extensions type to hold static extensions content.""" - - def __init__(self, json_content, xml_content): - self.xml_content = xml_content - self.json_content = json_content - - def to_json(self): - return self.json_content - - def to_xml(self): - return self.xml_content diff --git a/keystone/logic/types/fault.py b/keystone/logic/types/fault.py deleted file mode 100755 index 9ef4f8f0de..0000000000 --- a/keystone/logic/types/fault.py +++ /dev/null @@ -1,165 +0,0 @@ -# Copyright (c) 2010-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. - -import json -from lxml import etree - - -class IdentityFault(Exception): - """Base Exception type for all auth exceptions""" - - def __init__(self, msg, details=None, code=500): - self.args = (code, msg, details) - self.code = code - self.msg = msg - self.details = details - self.key = "IdentityFault" - - @property - def message(self): - return self.msg - - def to_xml(self): - dom = etree.Element(self.key, - xmlns="http://docs.openstack.org/identity/api/v2.0") - dom.set("code", str(self.code)) - msg = etree.Element("message") - msg.text = self.msg - dom.append(msg) - if self.details and len(self.details.strip()): - desc = etree.Element("details") - desc.text = self.details - dom.append(desc) - return etree.tostring(dom) - - def to_json(self): - fault = {} - fault["message"] = self.msg - fault["code"] = str(self.code) - if self.details and len(self.details.strip()): - fault["details"] = self.details - ret = {} - ret[self.key] = fault - return json.dumps(ret) - - -class ServiceUnavailableFault(IdentityFault): - """The auth service is unavailable""" - - def __init__(self, msg, details=None, code=503): - super(ServiceUnavailableFault, self).__init__(msg, details, code) - self.key = "serviceUnavailable" - - -class BadRequestFault(IdentityFault): - """Bad user request""" - - def __init__(self, msg, details=None, code=400): - super(BadRequestFault, self).__init__(msg, details, code) - self.key = "badRequest" - - -class UnauthorizedFault(IdentityFault): - """User is unauthorized""" - - def __init__(self, msg, details=None, code=401): - super(UnauthorizedFault, self).__init__(msg, details, code) - self.key = "unauthorized" - - -class ForbiddenFault(IdentityFault): - """The user is forbidden""" - - def __init__(self, msg, details=None, code=403): - super(ForbiddenFault, self).__init__(msg, details, code) - self.key = "forbidden" - - -class ItemNotFoundFault(IdentityFault): - """The item is not found""" - - def __init__(self, msg, details=None, code=404): - super(ItemNotFoundFault, self).__init__(msg, details, code) - self.key = "itemNotFound" - - -class TenantDisabledFault(IdentityFault): - """The tenant is disabled""" - - def __init__(self, msg, details=None, code=403): - super(TenantDisabledFault, self).__init__(msg, details, code) - self.key = "tenantDisabled" - - -class TenantConflictFault(IdentityFault): - """The tenant already exists?""" - - def __init__(self, msg, details=None, code=409): - super(TenantConflictFault, self).__init__(msg, details, code) - self.key = "tenantConflict" - - -class OverlimitFault(IdentityFault): - """A limit has been exceeded""" - - def __init__(self, msg, details=None, code=409, retry_at=None): - super(OverlimitFault, self).__init__(msg, details, code) - self.args = (code, msg, details, retry_at) - self.retry_at = retry_at - self.key = "overLimit" - - -class UserConflictFault(IdentityFault): - """The User already exists?""" - - def __init__(self, msg, details=None, code=409): - super(UserConflictFault, self).__init__(msg, details, code) - self.key = "userConflict" - - -class UserDisabledFault(IdentityFault): - """The user is disabled""" - - def __init__(self, msg, details=None, code=403): - super(UserDisabledFault, self).__init__(msg, details, code) - self.key = "userDisabled" - - -class EmailConflictFault(IdentityFault): - """The Email already exists?""" - - def __init__(self, msg, details=None, code=409): - super(EmailConflictFault, self).__init__(msg, details, code) - self.key = "emailConflict" - - -class RoleConflictFault(IdentityFault): - """The User already exists?""" - - def __init__(self, msg, details=None, code=409): - super(RoleConflictFault, self).__init__(msg, details, code) - self.key = "roleConflict" - - -class ServiceConflictFault(IdentityFault): - """The Service already exists?""" - - def __init__(self, msg, details=None, code=409): - super(ServiceConflictFault, self).__init__(msg, details, code) - self.key = "serviceConflict" - - -class DatabaseMigrationError(IdentityFault): - message = _("There was an error migrating the database.") diff --git a/keystone/logic/types/tenant.py b/keystone/logic/types/tenant.py deleted file mode 100644 index ca6e024b19..0000000000 --- a/keystone/logic/types/tenant.py +++ /dev/null @@ -1,150 +0,0 @@ -# Copyright (c) 2010-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. - -import json -from lxml import etree - -from keystone.logic.types import fault -from keystone import models - - -class Tenant(object): - """Describes a tenant in the auth system""" - id = None - name = None - description = None - enabled = None - - def __init__(self, id=None, name=None, description=None, enabled=None): - self.id = id # pylint: disable=C0103 - self.name = name - self.description = description - if enabled is not None: - self.enabled = bool(enabled) - else: - self.enabled = None - - @staticmethod - def from_xml(xml_str): - try: - dom = etree.Element("root") - dom.append(etree.fromstring(xml_str)) - root = dom.find( - "{http://docs.openstack.org/identity/api/v2.0}tenant") - if root is None: - raise fault.BadRequestFault("Expecting Tenant") - id = root.get("id") - name = root.get("name") - enabled = root.get("enabled") - if enabled is None or enabled == "true" or enabled == "yes": - set_enabled = True - elif enabled == "false" or enabled == "no": - set_enabled = False - else: - raise fault.BadRequestFault("Bad enabled attribute!") - desc = root.find("{http://docs.openstack.org/identity/api/v2.0}" - "description") - if desc is None: - description = None - else: - description = desc.text - return models.Tenant(id=id, name=name, description=description, - enabled=set_enabled) - except etree.LxmlError as e: - raise fault.BadRequestFault("Cannot parse Tenant", str(e)) - - @staticmethod - def from_json(json_str): - try: - obj = json.loads(json_str) - if not "tenant" in obj: - raise fault.BadRequestFault("Expecting tenant") - tenant = obj["tenant"] - - # Check that fields are valid - invalid = [key for key in tenant if key not in\ - ['id', 'name', 'enabled', 'description']] - if invalid != []: - raise fault.BadRequestFault("Invalid attribute(s): %s" - % invalid) - - id = tenant.get("id", None) - name = tenant.get("name", None) - set_enabled = True - if "enabled" in tenant: - set_enabled = tenant["enabled"] - if not isinstance(set_enabled, bool): - raise fault.BadRequestFault("Bad enabled attribute!") - description = tenant.get("description") - return Tenant(id=id, name=name, description=description, - enabled=set_enabled) - except (ValueError, TypeError) as e: - raise fault.BadRequestFault("Cannot parse Tenant", str(e)) - - def to_dom(self): - dom = etree.Element("tenant", - xmlns="http://docs.openstack.org/identity/api/v2.0", - enabled=str(self.enabled).lower()) - if self.id: - dom.set("id", unicode(self.id)) - if self.name: - dom.set("name", unicode(self.name)) - desc = etree.Element("description") - if self.description: - desc.text = unicode(self.description) - dom.append(desc) - return dom - - def to_xml(self): - return etree.tostring(self.to_dom()) - - def to_dict(self): - tenant = { - "enabled": self.enabled} - if self.description: - tenant['description'] = unicode(self.description) - if self.id: - tenant["id"] = unicode(self.id) - if self.name: - tenant["name"] = unicode(self.name) - return {"tenant": tenant} - - def to_json(self): - return json.dumps(self.to_dict()) - - -class Tenants(object): - """A collection of tenants.""" - - def __init__(self, values, links): - self.values = values - self.links = links - - def to_xml(self): - dom = etree.Element("tenants") - dom.set(u"xmlns", "http://docs.openstack.org/identity/api/v2.0") - - for t in self.values: - dom.append(t.to_dom()) - - for t in self.links: - dom.append(t.to_dom()) - - return etree.tostring(dom) - - def to_json(self): - values = [t.to_dict()["tenant"] for t in self.values] - links = [t.to_dict()["links"] for t in self.links] - return json.dumps({"tenants": values, "tenants_links": links}) diff --git a/keystone/logic/types/user.py b/keystone/logic/types/user.py deleted file mode 100755 index 5be5d4889a..0000000000 --- a/keystone/logic/types/user.py +++ /dev/null @@ -1,280 +0,0 @@ -# Copyright (c) 2010-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. - -import json -from lxml import etree - -from keystone.logic.types import fault -from keystone import utils - - -class User(object): - """Document me!""" - - def __init__(self, password=None, id=None, name=None, tenant_id=None, - email=None, enabled=None, tenant_roles=None): - self.id = id - self.name = name - self.tenant_id = tenant_id - self.password = password - self.email = email - self.enabled = enabled and True or False - self.tenant_roles = tenant_roles - - @staticmethod - def from_xml(xml_str): - try: - dom = etree.Element("root") - dom.append(etree.fromstring(xml_str)) - root = dom.find("{http://docs.openstack.org/identity/api/v2.0}" \ - "user") - if root is None: - raise fault.BadRequestFault("Expecting User") - - name = root.get("name") - tenant_id = root.get("tenantId") - email = root.get("email") - password = root.get("password") - enabled = root.get("enabled") - utils.check_empty_string(name, "Expecting User Name") - utils.check_empty_string(password, "Expecting User Password") - utils.check_empty_string(email, "Expecting User email") - enabled = enabled is None or enabled.lower() in ["true", "yes"] - - return User(password, id, name, tenant_id, email, enabled) - except etree.LxmlError as e: - raise fault.BadRequestFault("Cannot parse User", str(e)) - - @staticmethod - def from_json(json_str): - try: - obj = json.loads(json_str) - if not "user" in obj: - raise fault.BadRequestFault("Expecting User") - user = obj["user"] - - # Check that fields are valid - invalid = [key for key in user if key not in - ['id', 'name', 'password', 'tenantId', 'email', - 'enabled']] - if invalid != []: - raise fault.BadRequestFault("Invalid attribute(s): %s" - % invalid) - - id = user.get('id', None) - name = user.get('name', None) - - if not "password" in user: - raise fault.BadRequestFault("Expecting User Password") - password = user["password"] - - if "tenantId" in user: - tenant_id = user["tenantId"] - else: - tenant_id = None - if "email" not in user: - raise fault.BadRequestFault("Expecting User Email") - email = user["email"] - utils.check_empty_string(name, "Expecting User Name") - utils.check_empty_string(password, "Expecting User Password") - utils.check_empty_string(email, "Expecting User email") - if "enabled" in user: - set_enabled = user["enabled"] - if not isinstance(set_enabled, bool): - raise fault.BadRequestFault("Bad enabled attribute!") - else: - set_enabled = True - return User(password, id, name, tenant_id, email, set_enabled) - except (ValueError, TypeError) as e: - raise fault.BadRequestFault("Cannot parse User", str(e)) - - def to_dom(self): - dom = etree.Element("user", - xmlns="http://docs.openstack.org/identity/api/v2.0") - if self.email: - dom.set("email", unicode(self.email)) - if self.tenant_id: - dom.set("tenantId", unicode(self.tenant_id)) - if self.id: - dom.set("id", unicode(self.id)) - if self.name: - dom.set("name", unicode(self.name)) - if self.enabled: - dom.set("enabled", unicode(self.enabled).lower()) - if self.password: - dom.set("password", unicode(self.password)) - if self.tenant_roles: - dom_roles = etree.Element("tenantRoles") - for role in self.tenant_roles: - dom_role = etree.Element("tenantRole") - dom_role.text = role - dom_roles.append(dom_role) - dom.append(dom_roles) - return dom - - def to_xml(self): - return etree.tostring(self.to_dom()) - - def to_dict(self): - user = {} - - if self.id: - user["id"] = unicode(self.id) - if self.name: - user["name"] = unicode(self.name) - if self.tenant_id: - user["tenantId"] = unicode(self.tenant_id) - if self.password: - user["password"] = unicode(self.password) - user["email"] = unicode(self.email) - user["enabled"] = self.enabled - if self.tenant_roles: - user["tenantRoles"] = list(self.tenant_roles) - return {'user': user} - - def to_json(self): - return json.dumps(self.to_dict()) - - -class User_Update(object): - """Document me!""" - - def __init__(self, password=None, id=None, name=None, tenant_id=None, - email=None, enabled=None): - self.id = id - self.name = name - self.tenant_id = tenant_id - self.password = password - self.email = email - self.enabled = bool(enabled) if enabled is not None else None - - @staticmethod - def from_xml(xml_str): - try: - dom = etree.Element("root") - dom.append(etree.fromstring(xml_str)) - root = dom.find("{http://docs.openstack.org/identity/api/v2.0}" \ - "user") - if root is None: - raise fault.BadRequestFault("Expecting User") - id = root.get("id") - name = root.get("name") - tenant_id = root.get("tenantId") - email = root.get("email") - password = root.get("password") - enabled = root.get("enabled") - if enabled is None or enabled == "true" or enabled == "yes": - set_enabled = True - elif enabled == "false" or enabled == "no": - set_enabled = False - else: - raise fault.BadRequestFault("Bad enabled attribute!") - - return User(password=password, id=id, name=name, - tenant_id=tenant_id, email=email, enabled=set_enabled) - except etree.LxmlError as e: - raise fault.BadRequestFault("Cannot parse User", str(e)) - - @staticmethod - def from_json(json_str): - try: - obj = json.loads(json_str) - if not "user" in obj: - raise fault.BadRequestFault("Expecting User") - user = obj["user"] - - # Check that fields are valid - invalid = [key for key in user if key not in - ['id', 'name', 'password', 'tenantId', 'email', - 'enabled']] - if invalid != []: - raise fault.BadRequestFault("Invalid attribute(s): %s" - % invalid) - - id = user.get('id', None) - name = user.get('name', None) - password = user.get('password', None) - tenant_id = user.get('tenantId', None) - email = user.get('email', None) - enabled = user.get('enabled', True) - - if not isinstance(enabled, bool): - raise fault.BadRequestFault("Bad enabled attribute!") - - return User(password, id, name, tenant_id, email, enabled) - except (ValueError, TypeError) as e: - raise fault.BadRequestFault("Cannot parse Tenant", str(e)) - - def to_dom(self): - dom = etree.Element("user", - xmlns="http://docs.openstack.org/identity/api/v2.0") - if self.email: - dom.set("email", unicode(self.email)) - if self.tenant_id: - dom.set("tenantId", unicode(self.tenant_id)) - if self.id: - dom.set("id", unicode(self.id)) - if self.name: - dom.set("name", unicode(self.name)) - if self.enabled is not None: - dom.set("enabled", unicode(self.enabled).lower()) - if self.password: - dom.set("password", unicode(self.password)) - return dom - - def to_xml(self): - return etree.tostring(self.to_dom()) - - def to_dict(self): - user = {} - - if self.id: - user["id"] = unicode(self.id) - if self.name: - user["name"] = unicode(self.name) - if self.tenant_id: - user["tenantId"] = unicode(self.tenant_id) - if self.password: - user["password"] = unicode(self.password) - if self.email: - user["email"] = unicode(self.email) - if self.enabled is not None: - user["enabled"] = self.enabled - return {'user': user} - - def to_json(self): - return json.dumps(self.to_dict()) - - -class Users(object): - """A collection of users.""" - - def __init__(self, values, links): - self.values = values - self.links = links - - def to_xml(self): - dom = etree.Element("users") - dom.set(u"xmlns", "http://docs.openstack.org/identity/api/v2.0") - for t in self.values: - dom.append(t.to_dom()) - for t in self.links: - dom.append(t.to_dom()) - return etree.tostring(dom) - - def to_json(self): - values = [t.to_dict()["user"] for t in self.values] - links = [t.to_dict()["links"] for t in self.links] - return json.dumps({"users": values, "users_links": links}) diff --git a/keystone/manage/__init__.py b/keystone/manage/__init__.py deleted file mode 100644 index 3b9d0f916f..0000000000 --- a/keystone/manage/__init__.py +++ /dev/null @@ -1,507 +0,0 @@ -#!/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. -# 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. - -""" -Keystone Identity Server - CLI Management Interface -""" - -import logging -import optparse # deprecated in 2.7, in favor of argparse -import os -import sys -import tempfile - -from keystone import version -import keystone.backends as db -from keystone.backends.sqlalchemy import migration -# Need to give it a different alias -from keystone import config as new_config -from keystone.common import config -from keystone.logic.types import fault -from keystone.manage import api -from keystone import utils - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - -# We're using two config systems here, so we need to be clear -# which one we're working with. -CONF = new_config.CONF - - -# CLI feature set -OBJECTS = ['user', 'tenant', 'role', 'service', - 'endpointTemplates', 'token', 'endpoint', 'credentials', 'database'] -ACTIONS = ['add', 'list', 'disable', 'delete', 'grant', 'revoke', - 'sync', 'downgrade', 'upgrade', 'version_control', 'version', - 'goto'] - - -# Messages -OBJECT_NOT_SPECIFIED = 'No object type specified for first argument' -ACTION_NOT_SPECIFIED = 'No action specified for second argument' -ID_NOT_SPECIFIED = 'No ID specified for third argument' -SUPPORTED_OBJECTS = "Supported objects: %s" % (", ".join(OBJECTS)) -SUPPORTED_ACTIONS = "Supported actions: %s" % (", ".join(ACTIONS)) -ACTION_NOT_SUPPORTED = 'Action not supported for %s' - - -class RaisingOptionParser(optparse.OptionParser): - def error(self, msg): - self.print_usage(sys.stderr) - raise optparse.OptParseError(msg) - - -def parse_args(args=None): - usage = """ - Usage: keystone-manage [options] type action [id [attributes]] - type : %s - action : %s - id : name or id - attributes : depending on type... - users : password, tenant - tokens : user, tenant, expiration - - role list [tenant] will list roles granted on that tenant - database [sync | downgrade | upgrade | version_control | version] - - options - -c | --config-file : config file to use - -d | --debug : debug mode - - Example: keystone-manage user add Admin P@ssw0rd - """ % (", ".join(OBJECTS), ", ".join(ACTIONS)) - - # Initialize a parser for our configuration paramaters - parser = RaisingOptionParser(usage, version='%%prog %s' - % version.version()) - config.add_common_options(parser) - config.add_log_options(parser) - - # Parse command-line and load config - (options, args) = config.parse_options(parser, args) - - if not args or args[0] != 'database': - # Use the legacy code to find the config file - config_file = config.find_config_file(options, sys.argv[1:]) - # Now init the CONF for the backends - CONF(config_files=[config_file]) - - db.configure_backends() - return args - - -def get_options(args=None): - # Initialize a parser for our configuration paramaters - parser = RaisingOptionParser() - config.add_common_options(parser) - config.add_log_options(parser) - - # Parse command-line and load config - (options, args) = config.parse_options(parser, list(args)) - - _config_file, conf = config.load_paste_config('admin', options, args) - conf.global_conf.update(conf.local_conf) - - return conf.global_conf - - -# pylint: disable=R0912,R0915 -def process(*args): - # Check arguments - if len(args) == 0: - raise optparse.OptParseError(OBJECT_NOT_SPECIFIED) - else: - object_type = args[0] - if object_type not in OBJECTS: - raise optparse.OptParseError(SUPPORTED_OBJECTS) - - if len(args) == 1: - raise optparse.OptParseError(ACTION_NOT_SPECIFIED) - else: - action = args[1] - if action not in ACTIONS: - raise optparse.OptParseError(SUPPORTED_ACTIONS) - - if action not in ['list', 'sync', 'version_control', 'version']: - if len(args) == 2: - raise optparse.OptParseError(ID_NOT_SPECIFIED) - else: - object_id = args[2] - - # Helper functions - def require_args(args, min, msg): - """Ensure there are at least `min` arguments""" - if len(args) < min: - raise optparse.OptParseError(msg) - - def optional_arg(args, index): - return ((len(args) > index) and str(args[index]).strip()) or None - - if object_type == 'database': - options = get_options(args) - - # Execute command - if (object_type, action) == ('user', 'add'): - require_args(args, 4, 'No password specified for fourth argument') - if api.add_user(name=object_id, password=args[3], - tenant=optional_arg(args, 4)): - print ("SUCCESS: User %s created." % object_id) - - elif (object_type, action) == ('user', 'list'): - print (Table('Users', ['id', 'name', 'enabled', 'tenant'], - api.list_users())) - - elif (object_type, action) == ('user', 'disable'): - if api.disable_user(name=object_id): - print ("SUCCESS: User %s disabled." % object_id) - - elif object_type == 'user': - raise optparse.OptParseError(ACTION_NOT_SUPPORTED % ('users')) - - elif (object_type, action) == ('tenant', 'add'): - if api.add_tenant(name=object_id): - print ("SUCCESS: Tenant %s created." % object_id) - - elif (object_type, action) == ('tenant', 'list'): - print Table('Tenants', ['id', 'name', 'enabled'], api.list_tenants()) - - elif (object_type, action) == ('tenant', 'disable'): - if api.disable_tenant(name=object_id): - print ("SUCCESS: Tenant %s disabled." % object_id) - - elif object_type == 'tenant': - raise optparse.OptParseError(ACTION_NOT_SUPPORTED % ('tenants')) - - elif (object_type, action) == ('role', 'add'): - if api.add_role(name=object_id, service_name=optional_arg(args, 3)): - print ("SUCCESS: Role %s created successfully." % object_id) - - elif (object_type, action) == ('role', 'list'): - tenant = optional_arg(args, 2) - if tenant: - # print with users - print (Table('Role assignments for tenant %s' % - tenant, ['User', 'Role'], - api.list_roles(tenant=tenant))) - else: - # print without tenants - print (Table('Roles', ['id', 'name', 'service_id', 'description'], - api.list_roles())) - - elif (object_type, action) == ('role', 'grant'): - require_args(args, 4, "Missing arguments: role grant 'role' 'user' " - "'tenant (optional)'") - tenant = optional_arg(args, 4) - if api.grant_role(object_id, args[3], tenant): - print ("SUCCESS: Granted %s the %s role on %s." % - (args[3], object_id, tenant)) - - elif object_type == 'role': - raise optparse.OptParseError(ACTION_NOT_SUPPORTED % ('roles')) - - elif (object_type, action) == ('endpointTemplates', 'add'): - require_args(args, 9, "Missing arguments: endpointTemplates add " - "'region' 'service_name' 'publicURL' 'adminURL' 'internalURL' " - "'enabled' 'global'") - version_id = optional_arg(args, 9) - version_list = optional_arg(args, 10) - version_info = optional_arg(args, 11) - if api.add_endpoint_template(region=args[2], service=args[3], - public_url=args[4], admin_url=args[5], internal_url=args[6], - enabled=args[7], is_global=args[8], - version_id=version_id, version_list=version_list, - version_info=version_info): - print ("SUCCESS: Created EndpointTemplates for %s " - "pointing to %s." % (args[3], args[4])) - - elif (object_type, action) == ('endpointTemplates', 'list'): - tenant = optional_arg(args, 2) - if tenant: - print Table('Endpoints for tenant %s' % tenant, - ['id', 'service', 'region', 'Public URL'], - api.list_tenant_endpoints(tenant)) - else: - print Table('All EndpointTemplates', ['id', 'service', 'type', - 'region', 'enabled', 'is_global', 'Public URL', - 'Admin URL'], - api.list_endpoint_templates()) - - elif (object_type, action) == ('endpoint', 'add'): - require_args(args, 4, "Missing arguments: endPoint add tenant " - "endPointTemplate") - if api.add_endpoint(tenant=args[2], endpoint_template=args[3]): - print ("SUCCESS: Endpoint %s added to tenant %s." % - (args[3], args[2])) - - elif object_type == 'endpoint': - raise optparse.OptParseError(ACTION_NOT_SUPPORTED % ('endpoints')) - - elif (object_type, action) == ('token', 'add'): - require_args(args, 6, 'Creating a token requires a token id, user, ' - 'tenant, and expiration') - if api.add_token(token=object_id, user=args[3], tenant=args[4], - expires=args[5]): - print ("SUCCESS: Token %s created." % object_id) - - elif (object_type, action) == ('token', 'list'): - print Table('Tokens', ('token', 'user', 'expiration', 'tenant'), - api.list_tokens()) - - elif (object_type, action) == ('token', 'delete'): - if api.delete_token(token=object_id): - print ('SUCCESS: Token %s deleted.' % (object_id,)) - - elif object_type == 'token': - raise optparse.OptParseError(ACTION_NOT_SUPPORTED % ('tokens')) - - elif (object_type, action) == ('service', 'add'): - require_args(args, 4, "Missing arguments: service add " \ - "[type] [desc] [owner_id]" - "type") - type = optional_arg(args, 3) - desc = optional_arg(args, 4) - owner_id = optional_arg(args, 5) - - if api.add_service(name=object_id, type=type, desc=desc, - owner_id=owner_id): - print ("SUCCESS: Service %s created successfully." - % (object_id,)) - - elif (object_type, action) == ('service', 'list'): - print (Table('Services', ('id', 'name', 'type', 'owner_id', - 'description'), api.list_services())) - - elif object_type == 'service': - raise optparse.OptParseError(ACTION_NOT_SUPPORTED % ('services')) - - elif (object_type, action) == ('credentials', 'add'): - require_args(args, 6, 'Creating a credentials requires a type, key, ' - 'secret, and tenant_id (id is user_id)') - if api.add_credentials(user=object_id, type=args[3], key=args[4], - secrete=args[5], tenant=optional_arg(args, 6)): - print ("SUCCESS: Credentials %s created." % - (object_id,)) - - elif object_type == 'credentials': - raise optparse.OptParseError(ACTION_NOT_SUPPORTED % ('credentials')) - - elif (object_type, action) == ('database', 'sync'): - require_args(args, 1, 'Syncing database requires a version #') - backend_names = options.get('backends', None) - if backend_names: - if 'keystone.backends.sqlalchemy' in backend_names.split(','): - do_db_sync(options['keystone.backends.sqlalchemy'], - args) - else: - raise optparse.OptParseError( - 'SQL alchemy backend not specified in config') - - elif (object_type, action) == ('database', 'upgrade'): - require_args(args, 1, 'Upgrading database requires a version #') - backend_names = options.get('backends', None) - if backend_names: - if 'keystone.backends.sqlalchemy' in backend_names.split(','): - do_db_upgrade(options['keystone.backends.sqlalchemy'], - args) - else: - raise optparse.OptParseError( - 'SQL alchemy backend not specified in config') - - elif (object_type, action) == ('database', 'downgrade'): - require_args(args, 1, 'Downgrading database requires a version #') - backend_names = options.get('backends', None) - if backend_names: - if 'keystone.backends.sqlalchemy' in backend_names.split(','): - do_db_downgrade(options['keystone.backends.sqlalchemy'], - args) - else: - raise optparse.OptParseError( - 'SQL alchemy backend not specified in config') - - elif (object_type, action) == ('database', 'version_control'): - backend_names = options.get('backends', None) - if backend_names: - if 'keystone.backends.sqlalchemy' in backend_names.split(','): - do_db_version_control(options['keystone.backends.sqlalchemy']) - else: - raise optparse.OptParseError( - 'SQL alchemy backend not specified in config') - - elif (object_type, action) == ('database', 'version'): - backend_names = options.get('backends', None) - if backend_names: - if 'keystone.backends.sqlalchemy' in backend_names.split(','): - do_db_version(options['keystone.backends.sqlalchemy']) - else: - raise optparse.OptParseError( - 'SQL alchemy backend not specified in config') - - elif (object_type, action) == ('database', 'goto'): - require_args(args, 1, 'Jumping database versions requires a ' - 'version #') - backend_names = options.get('backends', None) - if backend_names: - if 'keystone.backends.sqlalchemy' in backend_names.split(','): - do_db_goto_version(options['keystone.backends.sqlalchemy'], - target_version=args[2]) - else: - raise optparse.OptParseError( - 'SQL alchemy backend not specified in config') - - else: - # Command recognized but not handled: should never reach this - raise NotImplementedError() - - -# -# Database Migration commands (from Glance-manage) -# -def do_db_version(options): - """Print database's current migration level""" - print (migration.db_version(options['sql_connection'])) - - -def do_db_goto_version(options, target_version): - """Override the database's current migration level""" - if migration.db_goto_version(options['sql_connection'], target_version): - msg = ('Jumped to version=%s (without performing intermediate ' - 'migrations)') % target_version - print (msg) - - -def do_db_upgrade(options, args): - """Upgrade the database's migration level""" - try: - db_version = args[2] - except IndexError: - db_version = None - - print ("Upgrading database to version %s" % db_version) - migration.upgrade(options['sql_connection'], version=db_version) - - -def do_db_downgrade(options, args): - """Downgrade the database's migration level""" - try: - db_version = args[2] - except IndexError: - raise Exception("downgrade requires a version argument") - - migration.downgrade(options['sql_connection'], version=db_version) - - -def do_db_version_control(options): - """Place a database under migration control""" - migration.version_control(options['sql_connection']) - print ("Database now under version control") - - -def do_db_sync(options, args): - """Place a database under migration control and upgrade""" - try: - db_version = args[2] - except IndexError: - db_version = None - migration.db_sync(options['sql_connection'], version=db_version) - - -class Table: - """Prints data in table for console output - - Syntax print Table("This is the title", - ["Header1","Header2","H3"], - [[1,2,3],["Hi","everybody","How are you??"],[None,True,[1,2]]]) - - """ - def __init__(self, title=None, headers=None, rows=None): - self.title = title - self.headers = headers if headers is not None else [] - self.rows = rows if rows is not None else [] - self.nrows = len(self.rows) - self.fieldlen = [] - - ncols = len(headers) - - for h in range(ncols): - max = 0 - for row in rows: - if len(str(row[h])) > max: - max = len(str(row[h])) - self.fieldlen.append(max) - - for i in range(len(headers)): - if len(str(headers[i])) > self.fieldlen[i]: - self.fieldlen[i] = len(str(headers[i])) - - self.width = sum(self.fieldlen) + (ncols - 1) * 3 + 4 - - def __str__(self): - hbar = "-" * self.width - if self.title: - title = "| " + self.title + " " * \ - (self.width - 3 - (len(self.title))) + "|" - out = [hbar, title, hbar] - else: - out = [] - header = "" - for i in range(len(self.headers)): - header += "| %s" % (str(self.headers[i])) + " " * \ - (self.fieldlen[i] - len(str(self.headers[i]))) + " " - header += "|" - out.append(header) - out.append(hbar) - for i in self.rows: - line = "" - for j in range(len(i)): - line += "| %s" % (str(i[j])) + " " * \ - (self.fieldlen[j] - len(str(i[j]))) + " " - out.append(line + "|") - - out.append(hbar) - return "\r\n".join(out) - - -def main(args=None): - try: - process(*parse_args(args)) - except (optparse.OptParseError, fault.DatabaseMigrationError) as exc: - print >> sys.stderr, exc - sys.exit(2) - except Exception as exc: - logstr = str(exc) - loginfo = None - if len(exc.args) > 1: - logstr = exc.args[0] - loginfo = exc.args[1] - - errmsg = "ERROR: %s" % logstr - if loginfo: - errmsg += ": %s" % loginfo - - print errmsg - logger.error(logstr, exc_info=loginfo) - raise - - -if __name__ == '__main__': - try: - main() - except StandardError: - sys.exit(1) diff --git a/keystone/manage/api.py b/keystone/manage/api.py deleted file mode 100644 index de5d2d5640..0000000000 --- a/keystone/manage/api.py +++ /dev/null @@ -1,210 +0,0 @@ -import datetime - -import keystone.backends.api as db_api -import keystone.backends.models as db_models -import keystone.models as models - - -def add_user(name, password, tenant=None): - if tenant: - tenant = db_api.TENANT.get_by_name(tenant).id - - obj = models.User() - obj.name = name - obj.password = password - obj.enabled = True - obj.tenant_id = tenant - return db_api.USER.create(obj) - - -def disable_user(name): - user = db_api.USER.get_by_name(name) - if user is None: - raise IndexError("User %s not found" % name) - user.enabled = False - return db_api.USER.update(user.id, user) - - -def list_users(): - objects = db_api.USER.get_all() - if objects is None: - raise IndexError("No users found") - return [[o.id, o.name, o.enabled, o.tenant_id] for o in objects] - - -def add_tenant(name): - obj = models.Tenant() - obj.name = name - obj.enabled = True - db_api.TENANT.create(obj) - - -def list_tenants(): - objects = db_api.TENANT.get_all() - if objects is None: - raise IndexError("Tenants not found") - return [[o.id, o.name, o.enabled] for o in objects] - - -def disable_tenant(name): - obj = db_api.TENANT.get_by_name(name) - if obj is None: - raise IndexError("Tenant %s not found" % name) - obj.enabled = False - return db_api.TENANT.update(obj.id, obj) - - -def add_role(name, service_name=None): - obj = models.Role() - obj.name = name - - names = name.split(":") - if len(names) == 2: - service_name = names[0] or service_name - if service_name: - # we have a role with service prefix, fill in the service ID - service = db_api.SERVICE.get_by_name(name=service_name) - obj.service_id = service.id - return db_api.ROLE.create(obj) - - -def list_role_assignments(tenant): - objects = db_api.TENANT.get_role_assignments(tenant) - if objects is None: - raise IndexError("Assignments not found") - return [[o.user.name, o.role.name] for o in objects] - - -def list_roles(tenant=None): - if tenant: - tenant = db_api.TENANT.get_by_name(tenant).id - return list_role_assignments(tenant) - else: - objects = db_api.ROLE.get_all() - if objects is None: - raise IndexError("Roles not found") - return [[o.id, o.name, o.service_id, o.description] for o in objects] - - -def grant_role(role, user, tenant=None): - """Grants `role` to `user` (and optionally, on `tenant`)""" - role = db_api.ROLE.get_by_name(name=role).id - user = db_api.USER.get_by_name(name=user).id - - if tenant: - tenant = db_api.TENANT.get_by_name(name=tenant).id - - obj = db_models.UserRoleAssociation() - obj.role_id = role - obj.user_id = user - obj.tenant_id = tenant - - return db_api.USER.user_role_add(obj) - - -def add_endpoint_template(region, service, public_url, admin_url, internal_url, - enabled, is_global, version_id, version_list, version_info): - db_service = db_api.SERVICE.get_by_name(service) - if db_service is None: - raise IndexError("Service %s not found" % service) - obj = db_models.EndpointTemplates() - obj.region = region - obj.service_id = db_service.id - obj.public_url = public_url - obj.admin_url = admin_url - obj.internal_url = internal_url - obj.enabled = enabled - obj.is_global = is_global - obj.version_id = version_id - obj.version_list = version_list - obj.version_info = version_info - return db_api.ENDPOINT_TEMPLATE.create(obj) - - -def list_tenant_endpoints(tenant): - objects = db_api.ENDPOINT_TEMPLATE.endpoint_get_by_tenant(tenant) - if objects is None: - raise IndexError("URLs not found") - return [[db_api.SERVICE.get(o.service_id).name, - o.region, o.public_url] for o in objects] - - -def list_endpoint_templates(): - objects = db_api.ENDPOINT_TEMPLATE.get_all() - if objects is None: - raise IndexError("URLs not found") - return [[o.id, - db_api.SERVICE.get(o.service_id).name, - db_api.SERVICE.get(o.service_id).type, - o.region, o.enabled, o.is_global, - o.public_url, o.admin_url] for o in objects] - - -def add_endpoint(tenant, endpoint_template): - tenant = db_api.TENANT.get_by_name(name=tenant).id - endpoint_template = db_api.ENDPOINT_TEMPLATE.get(id=endpoint_template).id - - obj = db_models.Endpoints() - obj.tenant_id = tenant - obj.endpoint_template_id = endpoint_template - db_api.ENDPOINT_TEMPLATE.endpoint_add(obj) - return obj - - -def add_token(token, user, tenant, expires): - user = db_api.USER.get_by_name(name=user).id - if tenant: - tenant = db_api.TENANT.get_by_name(name=tenant).id - - obj = models.Token() - obj.id = token - obj.user_id = user - obj.tenant_id = tenant - obj.expires = datetime.datetime.strptime(expires.replace("-", ""), - "%Y%m%dT%H:%M") - return db_api.TOKEN.create(obj) - - -def list_tokens(): - objects = db_api.TOKEN.get_all() - if objects is None: - raise IndexError("Tokens not found") - return [[o.id, o.user_id, o.expires, o.tenant_id] for o in objects] - - -def delete_token(token): - obj = db_api.TOKEN.get(token) - if obj is None: - raise IndexError("Token %s not found" % (token,)) - return db_api.TOKEN.delete(token) - - -def add_service(name, type, desc, owner_id): - obj = models.Service() - obj.name = name - obj.type = type - obj.description = desc - obj.owner_id = owner_id - return db_api.SERVICE.create(obj) - - -def list_services(): - objects = db_api.SERVICE.get_all() - if objects is None: - raise IndexError("Services not found") - return [[o.id, o.name, o.type, o.owner_id, o.description] for o in objects] - - -def add_credentials(user, type, key, secrete, tenant=None): - user = db_api.USER.get_by_name(user).id - - if tenant: - tenant = db_api.TENANT.get_by_name(tenant).id - - obj = models.Credentials() - obj.user_id = user - obj.type = type - obj.key = key - obj.secret = secrete - obj.tenant_id = tenant - return db_api.CREDENTIALS.create(obj) diff --git a/keystone/manage2/__init__.py b/keystone/manage2/__init__.py deleted file mode 100644 index 4336730bae..0000000000 --- a/keystone/manage2/__init__.py +++ /dev/null @@ -1,107 +0,0 @@ -"""OpenStack Identity (Keystone) Management""" - - -import argparse -import optparse -import os -import pkgutil -import sys - -from keystone.common import config as legacy_config -from keystone import config -from keystone.manage2 import commands - -CONF = config.CONF - -# builds a complete path to the commands package -PACKAGE_PATH = os.path.dirname(commands.__file__) - -# builds a list of modules in the commands package -MODULES = [tupl for tupl in pkgutil.iter_modules([PACKAGE_PATH])] - - -def load_module(module_name): - """Imports a module given the module name""" - try: - module_loader, name, _is_package = [md for md in MODULES - if md[1] == module_name][0] - except IndexError: - raise ValueError("No module found named '%s'" % module_name) - - loader = module_loader.find_module(name) - module = loader.load_module(name) - return module - - -# pylint: disable=W0612 -def init_config(): - """Uses legacy config module to parse out legacy settings and provide - them to the new cfg.py parser. - - This is a hack until we find a better way to have cfg.py ignore - unknown arguments - """ - - class SilentOptParser(optparse.OptionParser): - """ Class used to prevent OptionParser from exiting when it detects - invalid options coming in """ - def exit(self, status=0, msg=None): - pass - - def error(self, msg): - pass - - # Initialize a parser for our configuration paramaters - parser = SilentOptParser() - legacy_config.add_common_options(parser) - legacy_config.add_log_options(parser) - - # Parse command-line and load config - (options, args) = legacy_config.parse_options(parser) - (legacy_args, unknown_args) = parser.parse_args() - - cfgfile = getattr(legacy_args, 'config_file', None) - if cfgfile: - known_args = ['--config-file', cfgfile] - else: - known_args = [] - - # Use the legacy code to find the config file - config_file = legacy_config.find_config_file(options, known_args) - - # Now init the CONF for the backends using only known args - old_args = sys.argv[:] - sys.argv = known_args - try: - CONF(config_files=[config_file]) - except StandardError: - raise - finally: - sys.argv = old_args - - -def main(): - # discover command modules - module_names = [name for _, name, _ in MODULES] - - # build an argparser for keystone manage itself - parser = argparse.ArgumentParser(description=__doc__) - subparsers = parser.add_subparsers(dest='command', - help='Management commands') - - # append each command as a subparser - for module_name in module_names: - module = load_module(module_name) - subparser = subparsers.add_parser(module_name, - help=module.Command.__doc__) - - module.Command.append_parser(subparser) - - # actually parse the command line args or print help - args = parser.parse_args() - - # configure and run command - init_config() - module = load_module(args.command) - cmd = module.Command() - exit(cmd.run(args)) diff --git a/keystone/manage2/base.py b/keystone/manage2/base.py deleted file mode 100644 index bfd5babd44..0000000000 --- a/keystone/manage2/base.py +++ /dev/null @@ -1,111 +0,0 @@ -import argparse - -from keystone import config -from keystone.manage2 import common - - -class BaseCommand(object): - """Provides a common pattern for keystone-manage commands""" - - # pylint: disable=W0613 - def __init__(self, *args, **kwargs): - self.parser = argparse.ArgumentParser(prog=self.__module__, - description=self.__doc__) - self.append_parser(self.parser) - - def true_or_false(self, args, positive, negative): - """Evaluates a complementary pair of args to determine True/False. - - Fails if both args were provided. - - """ - - if getattr(args, positive) and getattr(args, negative): - self.parser.error("Unable to apply both: --%s and --%s" % ( - tuple([x.replace('_', '-') for x in (positive, negative)]))) - - return getattr(args, positive) and not getattr(args, negative) - - @classmethod - def append_parser(cls, parser): - """Appends this command's arguments to an argparser - - :param parser: argparse.ArgumentParser - """ - args = getattr(cls, '_args', {}) - - for name in args.keys(): - try: - parser.add_argument(name, **args[name]) - except TypeError: - print "Unable to add argument (%s) %s" % (name, args[name]) - raise - - def run(self, args): - """Handles argparse args and prints command results to stdout - - :param args: argparse Namespace - """ - raise NotImplementedError() - - -# pylint: disable=W0223 -class BaseSqlalchemyCommand(BaseCommand): - """Common functionality for database management commands""" - - def __init__(self, *args, **kwargs): - super(BaseSqlalchemyCommand, self).__init__(*args, **kwargs) - - @staticmethod - def _get_connection_string(): - sqla = config.CONF['keystone.backends.sqlalchemy'] - return sqla.sql_connection - - -# pylint: disable=E1101,W0223 -class BaseBackendCommand(BaseCommand): - """Common functionality for commands requiring backend access""" - - def __init__(self, managers=None, *args, **kwargs): - super(BaseBackendCommand, self).__init__(*args, **kwargs) - - # we may need to initialize our own managers - managers = managers or common.init_managers() - - # managers become available as self.attributes - for name, manager in managers.iteritems(): - setattr(self, name, manager) - - @staticmethod - def _get(obj_name, manager, id=None): - """Get an object from a manager, or fail if not found""" - if id is not None: - obj = manager.get(id) - - if obj is None: - raise KeyError("%s ID not found: %s" % (obj_name, id)) - - return obj - - def get_user(self, id): - return BaseBackendCommand._get("User", self.user_manager, id) - - def get_tenant(self, id): - return BaseBackendCommand._get("Tenant", self.tenant_manager, id) - - def get_token(self, id): - return BaseBackendCommand._get("Token", self.token_manager, id) - - def get_credential(self, id): - return BaseBackendCommand._get("Credential", self.credential_manager, - id) - - def get_role(self, id): - return BaseBackendCommand._get("Role", self.role_manager, id) - - def get_service(self, id): - return BaseBackendCommand._get("Service", self.service_manager, id) - - def get_endpoint_template(self, id): - return BaseBackendCommand._get("Endpoint Template", - self.endpoint_template_manager, id) diff --git a/keystone/manage2/commands/__init__.py b/keystone/manage2/commands/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/keystone/manage2/commands/create_credential.py b/keystone/manage2/commands/create_credential.py deleted file mode 100644 index d65bb59770..0000000000 --- a/keystone/manage2/commands/create_credential.py +++ /dev/null @@ -1,43 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import common -from keystone.manage2 import mixins -from keystone.backends import models - - -@common.arg('--user-id', - required=True, - help='identifies the user who can authenticate with this credential') -@common.arg('--tenant-id', - required=False, - help='identifies the tenant upon which the crednetial is valid') -@common.arg('--type', - required=True, - help="credential type (e.g. 'EC2')") -@common.arg('--key', - required=True) -@common.arg('--secret', - required=True) -class Command(base.BaseBackendCommand, mixins.DateTimeMixin): - """Creates a new credential.""" - - # pylint: disable=E1101,R0913 - def create_credential(self, user_id, credential_type, key, secret, - tenant_id=None): - self.get_user(user_id) - self.get_tenant(tenant_id) - - obj = models.Credentials() - obj.user_id = user_id - obj.tenant_id = tenant_id - obj.type = credential_type - obj.key = key - obj.secret = secret - - return self.credential_manager.create(obj) - - def run(self, args): - """Process argparse args, and print results to stdout""" - credential = self.create_credential(user_id=args.user_id, - tenant_id=args.tenant_id, credential_type=args.type, - key=args.key, secret=args.secret) - print credential.id diff --git a/keystone/manage2/commands/create_endpoint_template.py b/keystone/manage2/commands/create_endpoint_template.py deleted file mode 100644 index dfb5829467..0000000000 --- a/keystone/manage2/commands/create_endpoint_template.py +++ /dev/null @@ -1,62 +0,0 @@ -from keystone import models -from keystone.manage2 import base -from keystone.manage2 import common -from keystone.manage2 import mixins - - -@common.arg('--region', - required=True, - help='identifies the region where the endpoint exists') -@common.arg('--service-id', - required=True, - help='references the service that owns the endpoint, by ID') -@common.arg('--public-url', - required=True, - help='url to access the endpoint over a public network (e.g. the ' - 'internet)') -@common.arg('--admin-url', - required=True, - help='url to access service administrator api') -@common.arg('--internal-url', - required=True, - help='url to access the endpoint over a high bandwidth, low latency, ' - 'unmetered network (e.g. LAN)') -@common.arg('--global', - action='store_true', - required=False, - default=False, - help='indicates whether the endpoint should be mapped to tenants ' - '(tenant-specific) or not (global)') -@common.arg('--disabled', - action='store_true', - required=False, - default=False, - help="create the endpoint in a disabled state (endpoints are enabled by " - "default)") -class Command(base.BaseBackendCommand, mixins.DateTimeMixin): - """Creates a new endpoint template.""" - - # pylint: disable=E1101,R0913 - def create_endpoint_template(self, region, service_id, public_url, - admin_url, internal_url, is_global=False, is_enabled=True): - self.get_service(service_id) - - obj = models.EndpointTemplate() - obj.region = region - obj.service_id = service_id - obj.public_url = public_url - obj.admin_url = admin_url - obj.internal_url = internal_url - obj.is_global = is_global - obj.enabled = is_enabled - - return self.endpoint_template_manager.create(obj) - - def run(self, args): - """Process argparse args, and print results to stdout""" - endpoint_template = self.create_endpoint_template(region=args.region, - service_id=args.service_id, public_url=args.public_url, - admin_url=args.admin_url, internal_url=args.internal_url, - is_global=getattr(args, 'global'), - is_enabled=(not args.disabled)) - print endpoint_template.id diff --git a/keystone/manage2/commands/create_role.py b/keystone/manage2/commands/create_role.py deleted file mode 100644 index 0460da69d8..0000000000 --- a/keystone/manage2/commands/create_role.py +++ /dev/null @@ -1,36 +0,0 @@ -from keystone import models -from keystone.manage2 import base -from keystone.manage2 import common - - -@common.arg('--name', - required=True, - help='a unique role name') -@common.arg('--description', - required=False, - help='describe the role') -@common.arg('--service-id', - required=False, - help='service which owns the role') -class Command(base.BaseBackendCommand): - """Creates a new role. - - Optionally, specify a service to own the role. - """ - - # pylint: disable=E1101 - def create_role(self, name, description=None, service_id=None): - self.get_service(service_id) - - obj = models.Role() - obj.name = name - obj.description = description - obj.service_id = service_id - - return self.role_manager.create(obj) - - def run(self, args): - """Process argparse args, and print results to stdout""" - role = self.create_role(name=args.name, - description=args.description, service_id=args.service_id) - print role.id diff --git a/keystone/manage2/commands/create_service.py b/keystone/manage2/commands/create_service.py deleted file mode 100644 index 5bbbdb415b..0000000000 --- a/keystone/manage2/commands/create_service.py +++ /dev/null @@ -1,42 +0,0 @@ -from keystone.manage2 import common -from keystone.manage2 import base -from keystone.backends import models - - -@common.arg('--name', - required=True, - help='unique service name') -@common.arg('--type', - required=True, - help='service type (e.g. identity, compute, object-storage, etc)') -@common.arg('--description', - required=False, - help='describe the service') -@common.arg('--owner-id', - required=False, - help='user who owns the service') -class Command(base.BaseBackendCommand): - """Creates a new service. - - Optionally, specify a user to own the service. - """ - - # pylint: disable=E1101,R0913 - def create_service(self, name, service_type, description=None, - owner_id=None): - self.get_user(owner_id) - - obj = models.Service() - obj.name = name - obj.type = service_type - obj.owner_id = owner_id - obj.desc = description - - return self.service_manager.create(obj) - - def run(self, args): - """Process argparse args, and print results to stdout""" - service = self.create_service(name=args.name, - service_type=args.type, description=args.description, - owner_id=args.owner_id) - print service.id diff --git a/keystone/manage2/commands/create_tenant.py b/keystone/manage2/commands/create_tenant.py deleted file mode 100644 index 3fed308911..0000000000 --- a/keystone/manage2/commands/create_tenant.py +++ /dev/null @@ -1,38 +0,0 @@ -from keystone.manage2 import common -from keystone.manage2 import base -from keystone.backends import models - - -@common.arg('--id', - required=False, - help='a unique identifier used in URLs') -@common.arg('--name', - required=True, - help='a unique name') -@common.arg('--disabled', - action='store_true', - required=False, - default=False, - help="create the tenant in a disabled state (tenants are enabled by " - "default)") -class Command(base.BaseBackendCommand): - """Creates a new tenant, enabled by default. - - The tenant is enabled by default, but can optionally be disabled upon - creation. - """ - - # pylint: disable=E1101 - def create_tenant(self, name, id=None, enabled=True): - obj = models.Tenant() - obj.id = id - obj.name = name - obj.enabled = enabled - - return self.tenant_manager.create(obj) - - def run(self, args): - """Process argparse args, and print results to stdout""" - tenant = self.create_tenant(id=args.id, name=args.name, - enabled=(not args.disabled)) - print tenant.id diff --git a/keystone/manage2/commands/create_token.py b/keystone/manage2/commands/create_token.py deleted file mode 100644 index 14778b3495..0000000000 --- a/keystone/manage2/commands/create_token.py +++ /dev/null @@ -1,59 +0,0 @@ -import uuid - -from keystone.backends import models -from keystone.manage2 import base -from keystone.manage2 import common -from keystone.manage2 import mixins - - -@common.arg('--id', - required=False, - help='a unique token ID') -@common.arg('--user-id', - required=True, - help='identifies the user who can authenticate with this token') -@common.arg('--tenant-id', - required=False, - help='identifies the tenant upon which the token is valid') -@common.arg('--expires', - required=False, - help='identifies the POSIX date/time until which the token is valid ' - '(e.g. 1999-01-31T23:59)') -class Command(base.BaseBackendCommand, mixins.DateTimeMixin): - """Creates a new token. - - If a token ID is not provided, one will be generated automatically. - - If a tenant ID is not provided, the token will be unscoped. - - If an expiration datetime is not provided, the token will expires 24 - hours after creation. - """ - - # pylint: disable=E1101,R0913 - def create_token(self, user_id, token_id=None, tenant_id=None, - expires=None): - self.get_user(user_id) - self.get_tenant(tenant_id) - - obj = models.Token() - obj.user_id = user_id - obj.tenant_id = tenant_id - - if token_id is not None: - obj.id = token_id - else: - obj.id = uuid.uuid4().hex - - if expires is not None: - obj.expires = self.str_to_datetime(expires) - else: - obj.expires = Command.get_datetime_tomorrow() - - return self.token_manager.create(obj) - - def run(self, args): - """Process argparse args, and print results to stdout""" - token = self.create_token(token_id=args.id, user_id=args.user_id, - tenant_id=args.tenant_id, expires=args.expires) - print token.id diff --git a/keystone/manage2/commands/create_user.py b/keystone/manage2/commands/create_user.py deleted file mode 100644 index efe5b13182..0000000000 --- a/keystone/manage2/commands/create_user.py +++ /dev/null @@ -1,55 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import common -from keystone.backends import models - - -@common.arg('--id', - required=False, - help='a unique identifier used in URLs') -@common.arg('--name', - required=True, - help='a unique username used for authentication') -@common.arg('--email', - required=False, - help='a unique email address') -@common.arg('--password', - required=True, - help='used for authentication') -@common.arg('--tenant-id', - required=False, - help='default tenant ID') -@common.arg('--disabled', - action='store_true', - required=False, - default=False, - help="create the user in a disabled state (users are enabled by " - "default)") -class Command(base.BaseBackendCommand): - """Creates a new user, enabled by default. - - Optionally, specify a default tenant for the user. - The user is enabled by default, but can be disabled upon creation as - well. - """ - - # pylint: disable=E1101,R0913 - def create_user(self, name, password, id=None, email=None, tenant_id=None, - enabled=True): - self.get_tenant(tenant_id) - - obj = models.User() - obj.id = id - obj.name = name - obj.password = password - obj.email = email - obj.enabled = enabled - obj.tenant_id = tenant_id - - return self.user_manager.create(obj) - - def run(self, args): - """Process argparse args, and print results to stdout""" - user = self.create_user(id=args.id, name=args.name, - password=args.password, email=args.email, - tenant_id=args.tenant_id, enabled=(not args.disabled)) - print user.id diff --git a/keystone/manage2/commands/delete_credential.py b/keystone/manage2/commands/delete_credential.py deleted file mode 100644 index 50f0c41bbc..0000000000 --- a/keystone/manage2/commands/delete_credential.py +++ /dev/null @@ -1,18 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import common - - -@common.arg('--where-id', - required=True, - help='identify the credential to be deleted by ID') -class Command(base.BaseBackendCommand): - """Deletes the specified credential.""" - - # pylint: disable=E1101 - def delete_credential(self, id): - credential = self.get_credential(id) - self.credential_manager.delete(credential.id) - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.delete_credential(id=args.where_id) diff --git a/keystone/manage2/commands/delete_endpoint_template.py b/keystone/manage2/commands/delete_endpoint_template.py deleted file mode 100644 index b39bed1e17..0000000000 --- a/keystone/manage2/commands/delete_endpoint_template.py +++ /dev/null @@ -1,18 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import common - - -@common.arg('--where-id', - required=True, - help='identify the endpoint template to be deleted by ID') -class Command(base.BaseBackendCommand): - """Deletes the specified endpoint_template.""" - - # pylint: disable=E1101 - def delete_endpoint_template(self, id): - endpoint_template = self.get_endpoint_template(id) - self.endpoint_template_manager.delete(endpoint_template.id) - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.delete_endpoint_template(id=args.where_id) diff --git a/keystone/manage2/commands/delete_role.py b/keystone/manage2/commands/delete_role.py deleted file mode 100644 index d841efb9d4..0000000000 --- a/keystone/manage2/commands/delete_role.py +++ /dev/null @@ -1,18 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import common - - -@common.arg('--where-id', - required=True, - help='identify the role to be deleted by ID') -class Command(base.BaseBackendCommand): - """Deletes the specified role.""" - - # pylint: disable=E1101 - def delete_role(self, id): - role = self.get_role(id) - self.role_manager.delete(role.id) - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.delete_role(id=args.where_id) diff --git a/keystone/manage2/commands/delete_service.py b/keystone/manage2/commands/delete_service.py deleted file mode 100644 index 8119659fd7..0000000000 --- a/keystone/manage2/commands/delete_service.py +++ /dev/null @@ -1,18 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import common - - -@common.arg('--where-id', - required=True, - help='identify the service to be deleted by ID') -class Command(base.BaseBackendCommand): - """Deletes the specified service.""" - - # pylint: disable=E1101 - def delete_service(self, id): - service = self.get_service(id) - self.service_manager.delete(service.id) - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.delete_service(id=args.where_id) diff --git a/keystone/manage2/commands/delete_tenant.py b/keystone/manage2/commands/delete_tenant.py deleted file mode 100644 index 2adbf90486..0000000000 --- a/keystone/manage2/commands/delete_tenant.py +++ /dev/null @@ -1,21 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import common - - -@common.arg('--where-id', - required=True, - help='identify the tenant to be deleted') -class Command(base.BaseBackendCommand): - """Deletes the specified tenant. - - This command is irreversible! To simply disable a tenant, - use `update_tenant --disable`.""" - - # pylint: disable=E1101 - def delete_tenant(self, id): - tenant = self.get_tenant(id) - self.tenant_manager.delete(tenant.id) - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.delete_tenant(id=args.where_id) diff --git a/keystone/manage2/commands/delete_token.py b/keystone/manage2/commands/delete_token.py deleted file mode 100644 index 544855c86d..0000000000 --- a/keystone/manage2/commands/delete_token.py +++ /dev/null @@ -1,18 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import common - - -@common.arg('--where-id', - required=True, - help='identify the token to be deleted by ID') -class Command(base.BaseBackendCommand): - """Deletes the specified token.""" - - # pylint: disable=E1101 - def delete_token(self, id): - token = self.get_token(id) - self.token_manager.delete(token.id) - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.delete_token(id=args.where_id) diff --git a/keystone/manage2/commands/delete_user.py b/keystone/manage2/commands/delete_user.py deleted file mode 100644 index 701df76fc6..0000000000 --- a/keystone/manage2/commands/delete_user.py +++ /dev/null @@ -1,21 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import common - - -@common.arg('--where-id', - required=True, - help='identify the user to be deleted by ID') -class Command(base.BaseBackendCommand): - """Deletes the specified user. - - This command is irreversible! To simply disable a user, - use `update_user --disable`.""" - - # pylint: disable=E1101 - def delete_user(self, id): - user = self.get_user(id) - self.user_manager.delete(user.id) - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.delete_user(id=args.where_id) diff --git a/keystone/manage2/commands/downgrade_database.py b/keystone/manage2/commands/downgrade_database.py deleted file mode 100644 index 3bfbcc20f2..0000000000 --- a/keystone/manage2/commands/downgrade_database.py +++ /dev/null @@ -1,19 +0,0 @@ -from keystone.backends.sqlalchemy import migration -from keystone.manage2 import base -from keystone.manage2 import common - - -@common.arg('--version', - required=True, - help='specify the desired database version') -class Command(base.BaseSqlalchemyCommand): - """Downgrades the database to the specified version""" - - @staticmethod - def downgrade_database(version): - """Downgrade database to the specified version""" - migration.downgrade(Command._get_connection_string(), version=version) - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.downgrade_database(version=args.version) diff --git a/keystone/manage2/commands/goto_database.py b/keystone/manage2/commands/goto_database.py deleted file mode 100644 index d419c1c776..0000000000 --- a/keystone/manage2/commands/goto_database.py +++ /dev/null @@ -1,26 +0,0 @@ -from keystone.backends.sqlalchemy import migration -from keystone.manage2 import base -from keystone.manage2 import common - - -@common.arg('--version', - required=True, - help='specify the desired database version') -class Command(base.BaseSqlalchemyCommand): - """Jumps to the specified database version without running migrations. - - Useful for initializing your version control at a version other than zero - (e.g. you have an existing post-diablo database). - - """ - - @staticmethod - def goto_database_version(version): - """Override database's current migration level""" - if not migration.db_goto_version(Command._get_connection_string(), - version): - raise Exception("Unable to jump to specified version") - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.goto_database_version(version=args.version) diff --git a/keystone/manage2/commands/grant_role.py b/keystone/manage2/commands/grant_role.py deleted file mode 100644 index 770d988790..0000000000 --- a/keystone/manage2/commands/grant_role.py +++ /dev/null @@ -1,45 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import common -from keystone.backends import models - - -@common.arg('--user-id', - required=True, - help='identify the user to grant the role to by ID') -@common.arg('--role-id', - required=True, - help='identify the role to be granted by ID') -@common.arg('--tenant-id', - required=False, - help='identify the tenant for the granted role is valid (the role is ' - 'global if a tenant is not specified)') -class Command(base.BaseBackendCommand): - """Grants a role to a user, and optionally, for a specific tenant. - - If a tenant is not specified, the role is granted globally.""" - - # pylint: disable=E1101,R0913 - def grant_role(self, user_id, role_id, tenant_id=None): - self.get_user(user_id) - self.get_role(role_id) - self.get_tenant(tenant_id) - - # this is a bit of a hack to validate that the grant doesn't exist - grant = self.grant_manager.rolegrant_get_by_ids(user_id, role_id, - tenant_id) - if grant is not None: - raise KeyError('Grant already exists for User ID %s, ' - 'Role ID %s and Tenant ID %s' % (user_id, role_id, - tenant_id)) - - obj = models.UserRoleAssociation() - obj.user_id = user_id - obj.role_id = role_id - obj.tenant_id = tenant_id - - self.user_manager.user_role_add(obj) - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.grant_role(user_id=args.user_id, role_id=args.role_id, - tenant_id=args.tenant_id) diff --git a/keystone/manage2/commands/list_credentials.py b/keystone/manage2/commands/list_credentials.py deleted file mode 100644 index 4bc8a5b25f..0000000000 --- a/keystone/manage2/commands/list_credentials.py +++ /dev/null @@ -1,23 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import mixins - - -class Command(base.BaseBackendCommand, mixins.ListMixin, - mixins.DateTimeMixin): - """Lists all credentials in the system.""" - - # pylint: disable=E1101 - def get_credentials(self): - return self.credential_manager.get_all() - - def run(self, args): - """Process argparse args, and print results to stdout""" - table = self.build_table(["ID", "User ID", "Tenant ID", - "Type", "Key", "Secret"]) - - for obj in self.get_credentials(): - row = [obj.id, obj.user_id, obj.tenant_id, - obj.type, obj.key, obj.secret] - table.add_row(row) - - self.print_table(table) diff --git a/keystone/manage2/commands/list_endpoint_templates.py b/keystone/manage2/commands/list_endpoint_templates.py deleted file mode 100644 index fbfc53410a..0000000000 --- a/keystone/manage2/commands/list_endpoint_templates.py +++ /dev/null @@ -1,23 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import mixins - - -class Command(base.BaseBackendCommand, mixins.ListMixin): - """Lists all endpoint templates in the system.""" - - # pylint: disable=E1101 - def get_endpoint_templates(self): - return self.endpoint_template_manager.get_all() - - def run(self, args): - """Process argparse args, and print results to stdout""" - table = self.build_table(['ID', 'Service ID', 'Region', 'Enabled', - 'Global', 'Public URL', 'Admin URL', 'Internal URL']) - - for obj in self.get_endpoint_templates(): - row = [obj.id, obj.service_id, obj.region, obj.enabled, - obj.is_global, obj.public_url, obj.admin_url, - obj.internal_url] - table.add_row(row) - - self.print_table(table) diff --git a/keystone/manage2/commands/list_endpoints.py b/keystone/manage2/commands/list_endpoints.py deleted file mode 100644 index dfebb41055..0000000000 --- a/keystone/manage2/commands/list_endpoints.py +++ /dev/null @@ -1,20 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import mixins - - -class Command(base.BaseBackendCommand, mixins.ListMixin): - """Lists all roles in the system.""" - - # pylint: disable=E1101 - def get_roles(self): - return self.endpoint_manager.get_all() - - def run(self, args): - """Process argparse args, and print results to stdout""" - table = self.build_table(["Endpoint Template ID", "Tenant ID"]) - - for obj in self.get_roles(): - row = [obj.endpoint_template_id, obj.tenant_id] - table.add_row(row) - - self.print_table(table) diff --git a/keystone/manage2/commands/list_role_grants.py b/keystone/manage2/commands/list_role_grants.py deleted file mode 100644 index cd69ed33fb..0000000000 --- a/keystone/manage2/commands/list_role_grants.py +++ /dev/null @@ -1,50 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import common -from keystone.manage2 import mixins - - -@common.arg('--where-user-id', - required=False, - help='lists roles granted to a specific user') -@common.arg('--where-role-id', - required=False, - help='lists users and tenants a role has been granted to') -@common.arg('--where-tenant-id', - required=False, - help='lists roles granted on a specific tenant') -@common.arg('--where-global', - action='store_true', - required=False, - default=False, - help="lists roles that have been granted globally") -class Command(base.BaseBackendCommand, mixins.ListMixin): - """Lists the users and tenants a role has been granted to.""" - - # pylint: disable=E1101,R0913 - def list_role_grants(self, role_id=None, user_id=None, tenant_id=None, - is_global=False): - self.get_user(user_id) - self.get_role(role_id) - self.get_tenant(tenant_id) - - if is_global: - tenant_id = False - - return self.grant_manager.list_role_grants(user_id=user_id, - role_id=role_id, tenant_id=tenant_id) - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.true_or_false(args, 'where_tenant_id', 'where_global') - - table = self.build_table(["Role ID", "User ID", "Tenant ID", - "Global"]) - - for obj in self.list_role_grants(role_id=args.where_role_id, - user_id=args.where_user_id, tenant_id=args.where_tenant_id, - is_global=args.where_global): - row = [obj.role_id, obj.user_id, obj.tenant_id, - obj.tenant_id is None] - table.add_row(row) - - self.print_table(table) diff --git a/keystone/manage2/commands/list_roles.py b/keystone/manage2/commands/list_roles.py deleted file mode 100644 index 9e381fc7cc..0000000000 --- a/keystone/manage2/commands/list_roles.py +++ /dev/null @@ -1,20 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import mixins - - -class Command(base.BaseBackendCommand, mixins.ListMixin): - """Lists all roles in the system.""" - - # pylint: disable=E1101 - def get_roles(self): - return self.role_manager.get_all() - - def run(self, args): - """Process argparse args, and print results to stdout""" - table = self.build_table(["ID", "Name", "Service ID", "Description"]) - - for obj in self.get_roles(): - row = [obj.id, obj.name, obj.service_id, obj.desc] - table.add_row(row) - - self.print_table(table) diff --git a/keystone/manage2/commands/list_services.py b/keystone/manage2/commands/list_services.py deleted file mode 100644 index 0496c50ba1..0000000000 --- a/keystone/manage2/commands/list_services.py +++ /dev/null @@ -1,21 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import mixins - - -class Command(base.BaseBackendCommand, mixins.ListMixin): - """Lists all services in the system.""" - - # pylint: disable=E1101 - def get_services(self): - return self.service_manager.get_all() - - def run(self, args): - """Process argparse args, and print results to stdout""" - table = self.build_table(["ID", "Name", "Type", "Owner ID", - "Description"]) - - for obj in self.get_services(): - row = [obj.id, obj.name, obj.type, obj.owner_id, obj.desc] - table.add_row(row) - - self.print_table(table) diff --git a/keystone/manage2/commands/list_tenants.py b/keystone/manage2/commands/list_tenants.py deleted file mode 100644 index 81a15ca491..0000000000 --- a/keystone/manage2/commands/list_tenants.py +++ /dev/null @@ -1,22 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import mixins - - -class Command(base.BaseBackendCommand, mixins.ListMixin): - """Lists all tenants in the system.""" - - # pylint: disable=E1101 - def get_tenants(self): - return self.tenant_manager.get_all() - - def run(self, args): - """Process argparse args, and print results to stdout""" - - table = self.build_table(["ID", "Name", "Enabled"]) - - # populate the table - for tenant in self.get_tenants(): - row = [tenant.id, tenant.name, tenant.enabled] - table.add_row(row) - - self.print_table(table) diff --git a/keystone/manage2/commands/list_tokens.py b/keystone/manage2/commands/list_tokens.py deleted file mode 100644 index 9bdc3063db..0000000000 --- a/keystone/manage2/commands/list_tokens.py +++ /dev/null @@ -1,23 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import mixins - - -class Command(base.BaseBackendCommand, mixins.ListMixin, - mixins.DateTimeMixin): - """Lists all tokens in the system.""" - - # pylint: disable=E1101 - def get_tokens(self): - return self.token_manager.get_all() - - def run(self, args): - """Process argparse args, and print results to stdout""" - table = self.build_table(["ID", "User ID", "Tenant ID", - "Expiration"]) - - for obj in self.get_tokens(): - row = [obj.id, obj.user_id, obj.tenant_id, - self.datetime_to_str(obj.expires)] - table.add_row(row) - - self.print_table(table) diff --git a/keystone/manage2/commands/list_users.py b/keystone/manage2/commands/list_users.py deleted file mode 100644 index a23202dce3..0000000000 --- a/keystone/manage2/commands/list_users.py +++ /dev/null @@ -1,23 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import mixins - - -class Command(base.BaseBackendCommand, mixins.ListMixin): - """Lists all users in the system.""" - - # pylint: disable=E1101 - def get_users(self): - return self.user_manager.get_all() - - def run(self, args): - """Process argparse args, and print results to stdout""" - table = self.build_table(["ID", "Name", "Email", "Default Tenant ID", - "Enabled"]) - - for user in self.get_users(): - row = [user.id, user.name, user.email, user.tenant_id, - user.enabled] - table.add_row(row) - - # TODO(dolph): sort order and subsets could become CLI options - self.print_table(table) diff --git a/keystone/manage2/commands/map_endpoint.py b/keystone/manage2/commands/map_endpoint.py deleted file mode 100644 index ab63ec9dd0..0000000000 --- a/keystone/manage2/commands/map_endpoint.py +++ /dev/null @@ -1,37 +0,0 @@ -from keystone import models -from keystone.manage2 import base -from keystone.manage2 import common - - -@common.arg('--tenant-id', - required=True, - help='identify the tenant to be mapped by ID') -@common.arg('--endpoint-template-id', - required=True, - help='identify the endpoint to be mapped by ID') -class Command(base.BaseBackendCommand): - """Maps a non-global endpoint to a tenant. - - If a mapping exists between a tenant and an endpoint template, then - the endpoint will appear in the tenant's service catalog, customized - for the tenant. - - Global endpoints are already available to all tenants and therefore don't - need to be mapped. - """ - - # pylint: disable=E1101,R0913 - def create_endpoint(self, endpoint_template_id, tenant_id): - self.get_endpoint_template(endpoint_template_id) - self.get_tenant(tenant_id) - - obj = models.Endpoint() - obj.endpoint_template_id = endpoint_template_id - obj.tenant_id = tenant_id - - self.endpoint_manager.create(obj) - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.create_endpoint(endpoint_template_id=args.endpoint_template_id, - tenant_id=args.tenant_id) diff --git a/keystone/manage2/commands/revoke_role.py b/keystone/manage2/commands/revoke_role.py deleted file mode 100644 index 6b403d7d9f..0000000000 --- a/keystone/manage2/commands/revoke_role.py +++ /dev/null @@ -1,40 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import common - - -@common.arg('--user-id', - required=True, - help='identify the user to revoke the role from by ID') -@common.arg('--role-id', - required=True, - help='identify the role to be revoked by ID') -@common.arg('--tenant-id', - required=False, - help='identify the tenant for the role to be revoked from by ID (the ' - 'role is assumed to be global if a tenant is not specified)') -class Command(base.BaseBackendCommand): - """Revoke a role from a user, and optionally, from a specific tenant. - - If a tenant is not specified, then the role is assumed to be global, - and revoked as a global role. - """ - - # pylint: disable=E1101,R0913 - def revoke_role(self, user_id, role_id, tenant_id=None): - self.get_user(user_id) - self.get_role(role_id) - self.get_tenant(tenant_id) - - grant = self.grant_manager.rolegrant_get_by_ids(user_id, role_id, - tenant_id) - - if grant is None: - raise KeyError('Grant not found for User ID %s, Role ID %s and ' - 'Tenant ID %s' % (user_id, role_id, tenant_id)) - - self.grant_manager.rolegrant_delete(grant.id) - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.revoke_role(user_id=args.user_id, role_id=args.role_id, - tenant_id=args.tenant_id) diff --git a/keystone/manage2/commands/sync_database.py b/keystone/manage2/commands/sync_database.py deleted file mode 100644 index 62092d50e6..0000000000 --- a/keystone/manage2/commands/sync_database.py +++ /dev/null @@ -1,19 +0,0 @@ -from keystone.backends.sqlalchemy import migration -from keystone.manage2 import base -from keystone.manage2 import common - - -@common.arg('--version', - required=False, - help='specify the desired database version') -class Command(base.BaseSqlalchemyCommand): - """Upgrades the database to the latest schema.""" - - @staticmethod - def sync_database(version=None): - """Place database under migration control & automatically upgrade""" - migration.db_sync(Command._get_connection_string(), version=version) - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.sync_database(version=args.version) diff --git a/keystone/manage2/commands/unmap_endpoint.py b/keystone/manage2/commands/unmap_endpoint.py deleted file mode 100644 index 991e597e43..0000000000 --- a/keystone/manage2/commands/unmap_endpoint.py +++ /dev/null @@ -1,28 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import common - - -@common.arg('--tenant-id', - required=True, - help='identify the tenant to be unmapped by ID') -@common.arg('--endpoint-template-id', - required=True, - help='identify the endpoint template to be unmapped by ID') -class Command(base.BaseBackendCommand): - """Unmap an endpoint template from a tenant.""" - - # pylint: disable=E1101,R0913 - def delete_endpoint(self, endpoint_template_id, tenant_id): - obj = self.endpoint_manager.get_by_ids(endpoint_template_id, tenant_id) - - if obj is None: - raise KeyError("Endpoint mapping not found for " - "endpoint_template_id=%s, tenant_id=%s" % ( - endpoint_template_id, tenant_id)) - - self.endpoint_manager.delete(obj.id) - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.delete_endpoint(endpoint_template_id=args.endpoint_template_id, - tenant_id=args.tenant_id) diff --git a/keystone/manage2/commands/update_credential.py b/keystone/manage2/commands/update_credential.py deleted file mode 100644 index eb15ef0366..0000000000 --- a/keystone/manage2/commands/update_credential.py +++ /dev/null @@ -1,55 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import common -from keystone.manage2 import mixins - - -@common.arg('--where-id', - required=True, - help='identifies the credential to update by ID') -@common.arg('--user-id', - required=False, - help='change the user the credential applies to, by ID') -@common.arg('--tenant-id', - required=False, - help='change the tenant this credential applies to, by ID') -@common.arg('--type', - required=True, - help="change the credential type (e.g. 'EC2')") -@common.arg('--key', - required=True, - help="change the credential key") -@common.arg('--secret', - required=True, - help="change the credential secret") -class Command(base.BaseBackendCommand, mixins.DateTimeMixin): - """Updates the specified credential.""" - - # pylint: disable=E1101,R0913 - def update_credential(self, id, user_id=None, tenant_id=None, - cred_type=None, secret=None, key=None): - obj = self.get_credential(id) - self.get_user(user_id) - self.get_tenant(tenant_id) - - if user_id is not None: - obj.user_id = user_id - - if tenant_id is not None: - obj.tenant_id = tenant_id - - if cred_type is not None: - obj.type = cred_type - - if key is not None: - obj.key = key - - if secret is not None: - obj.secret = secret - - self.credential_manager.update(id, obj) - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.update_credential(id=args.where_id, user_id=args.user_id, - tenant_id=args.tenant_id, cred_type=args.type, - key=args.key, secret=args.secret) diff --git a/keystone/manage2/commands/update_endpoint_template.py b/keystone/manage2/commands/update_endpoint_template.py deleted file mode 100644 index 3f0f68937d..0000000000 --- a/keystone/manage2/commands/update_endpoint_template.py +++ /dev/null @@ -1,88 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import common -from keystone.manage2 import mixins - - -@common.arg('--where-id', - required=True, - help='identifies the endpoint template to update by ID') -@common.arg('--region', - required=True, - help='identifies the region where the endpoint exists') -@common.arg('--service-id', - required=True, - help='references the service that owns the endpoint, by ID') -@common.arg('--public-url', - required=True, - help='url to access the endpoint over a public network (e.g. the ' - 'internet)') -@common.arg('--admin-url', - required=True, - help='url to access service administrator api') -@common.arg('--internal-url', - required=True, - help='url to access the endpoint over a high bandwidth, low latency, ' - 'unmetered network (e.g. LAN)') -@common.arg('--global', - action='store_true', - required=False, - default=False, - help='indicates whether the endpoint should apply to all tenants') -@common.arg('--non-global', - action='store_true', - required=False, - default=False, - help='indicates whether the endpoint should be mapped to specific tenants') -@common.arg('--enable', - action='store_true', - required=False, - default=False, - help="enable the endpoint template") -@common.arg('--disable', - action='store_true', - required=False, - default=False, - help="disable the endpoint template") -class Command(base.BaseBackendCommand, mixins.DateTimeMixin): - """Updates an existing endpoint template.""" - - # pylint: disable=E1101,R0913 - def update_endpoint_template(self, id, region, service_id, public_url, - admin_url, internal_url, is_global=False, is_enabled=True): - obj = self.get_endpoint_template(id) - - self.get_service(service_id) - - if region is not None: - obj.region = region - - if service_id is not None: - obj.service_id = service_id - - if public_url is not None: - obj.public_url = public_url - - if admin_url is not None: - obj.admin_url = admin_url - - if internal_url is not None: - obj.internal_url = internal_url - - if is_global is not None: - obj.is_global = is_global - - if is_enabled is not None: - obj.enabled = is_enabled - - self.endpoint_template_manager.update(obj) - - def run(self, args): - """Process argparse args, and print results to stdout""" - is_global = self.true_or_false(args, 'global', 'non_global') - enabled = self.true_or_false(args, 'enable', 'disable') - - self.update_endpoint_template(id=args.where_id, - region=args.region, service_id=args.service_id, - public_url=args.public_url, admin_url=args.admin_url, - internal_url=args.internal_url, is_global=is_global, - is_enabled=enabled) diff --git a/keystone/manage2/commands/update_role.py b/keystone/manage2/commands/update_role.py deleted file mode 100644 index 2f4596614c..0000000000 --- a/keystone/manage2/commands/update_role.py +++ /dev/null @@ -1,40 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import common - - -@common.arg('--where-id', - required=True, - help='identifies the role to update by ID') -@common.arg('--name', - required=False, - help='a unique role name') -@common.arg('--description', - required=False, - help='describe the role') -@common.arg('--service-id', - required=False, - help='service which owns the role') -class Command(base.BaseBackendCommand): - """Updates the specified role.""" - - # pylint: disable=E1101,R0913 - def update_role(self, id, name=None, description=None, service_id=None): - obj = self.get_role(id) - - if name is not None: - obj.name = name - - if description is not None: - obj.description = description - - if service_id is not None: - service = self.get_service(service_id) - obj.service_id = service.id - - self.role_manager.update(obj) - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.update_role(id=args.where_id, name=args.name, - description=args.description, - service_id=args.service_id) diff --git a/keystone/manage2/commands/update_service.py b/keystone/manage2/commands/update_service.py deleted file mode 100644 index 7c7c3dacdf..0000000000 --- a/keystone/manage2/commands/update_service.py +++ /dev/null @@ -1,47 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import common - - -@common.arg('--where-id', - required=True, - help='identifies the service to update by ID') -@common.arg('--name', - required=False, - help='unique service name') -@common.arg('--type', - required=False, - help='service type (e.g. identity, compute, object-storage, etc)') -@common.arg('--description', - required=False, - help='describe the service') -@common.arg('--owner-id', - required=False, - help='user who owns the service') -class Command(base.BaseBackendCommand): - """Updates the specified service.""" - - # pylint: disable=E1101,R0913 - def update_service(self, id, name=None, service_type=None, - description=None, owner_id=None): - obj = self.get_service(id) - - if name is not None: - obj.name = name - - if service_type is not None: - obj.type = service_type - - if description is not None: - obj.description = description - - if owner_id is not None: - owner = self.get_user(owner_id) - obj.owner_id = owner.id - - self.service_manager.update(obj) - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.update_service(id=args.where_id, name=args.name, - service_type=args.type, - description=args.description, owner_id=args.owner_id) diff --git a/keystone/manage2/commands/update_tenant.py b/keystone/manage2/commands/update_tenant.py deleted file mode 100644 index b2f844f126..0000000000 --- a/keystone/manage2/commands/update_tenant.py +++ /dev/null @@ -1,40 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import common - - -@common.arg('--where-id', - required=True, - help='identifies the tenant to update by ID') -@common.arg('--name', - required=False, - help="change the tenant's name") -@common.arg('--enable', - action='store_true', - required=False, - default=False, - help="enable the tenant") -@common.arg('--disable', - action='store_true', - required=False, - default=False, - help="disable the tenant") -class Command(base.BaseBackendCommand): - """Updates the specified tenant.""" - - # pylint: disable=E1101 - def update_tenant(self, id, name=None, enabled=None): - tenant = self.get_tenant(id) - - if name is not None: - tenant.name = name - - if enabled is not None: - tenant.enabled = enabled - - self.tenant_manager.update(tenant) - - def run(self, args): - """Process argparse args, and print results to stdout""" - enabled = self.true_or_false(args, 'enable', 'disable') - - self.update_tenant(id=args.where_id, name=args.name, enabled=enabled) diff --git a/keystone/manage2/commands/update_token.py b/keystone/manage2/commands/update_token.py deleted file mode 100644 index f2c78328df..0000000000 --- a/keystone/manage2/commands/update_token.py +++ /dev/null @@ -1,42 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import common -from keystone.manage2 import mixins - - -@common.arg('--where-id', - required=True, - help='identifies the token to update by ID') -@common.arg('--user-id', - required=False, - help='change the user the token applies to, by ID') -@common.arg('--tenant-id', - required=False, - help='change the tenant this token applies to, by ID') -@common.arg('--expires', - required=False, - help="change the token's expiration date") -class Command(base.BaseBackendCommand, mixins.DateTimeMixin): - """Updates the specified token.""" - - # pylint: disable=E1101,R0913 - def update_token(self, id, user_id=None, tenant_id=None, - expires=None): - obj = self.get_token(id) - self.get_user(user_id) - self.get_tenant(tenant_id) - - if user_id is not None: - obj.user_id = user_id - - if tenant_id is not None: - obj.tenant_id = tenant_id - - if expires is not None: - obj.expires = self.str_to_datetime(expires) - - self.token_manager.update(id, obj) - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.update_token(id=args.where_id, user_id=args.user_id, - tenant_id=args.tenant_id, expires=args.expires) diff --git a/keystone/manage2/commands/update_user.py b/keystone/manage2/commands/update_user.py deleted file mode 100644 index dafce642df..0000000000 --- a/keystone/manage2/commands/update_user.py +++ /dev/null @@ -1,62 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import common - - -@common.arg('--where-id', - required=True, - help='identifies the user to update by ID') -@common.arg('--name', - required=False, - help="change the user's name") -@common.arg('--password', - required=False, - help="change the user's password") -@common.arg('--email', - required=False, - help="change the user's email address") -@common.arg('--tenant_id', - required=False, - help="change the user's default tenant") -@common.arg('--enable', - action='store_true', - required=False, - default=False, - help="enable the user") -@common.arg('--disable', - action='store_true', - required=False, - default=False, - help="disable the user") -class Command(base.BaseBackendCommand): - """Updates the specified user.""" - - # pylint: disable=E1101,R0913 - def update_user(self, id, name=None, password=None, email=None, - tenant_id=None, enabled=None): - user = self.get_user(id) - - if name is not None: - user.name = name - - if password is not None: - user.password = password - - if email is not None: - user.email = email - - if tenant_id is not None: - tenant = self.get_tenant(tenant_id) - user.tenant = tenant.id - - if enabled is not None: - user.enabled = enabled - - self.user_manager.update(user) - - def run(self, args): - """Process argparse args, and print results to stdout""" - enabled = self.true_or_false(args, 'enable', 'disable') - - self.update_user(id=args.where_id, name=args.name, - password=args.password, email=args.email, - tenant_id=args.tenant_id, enabled=enabled) diff --git a/keystone/manage2/commands/upgrade_database.py b/keystone/manage2/commands/upgrade_database.py deleted file mode 100644 index 9488444252..0000000000 --- a/keystone/manage2/commands/upgrade_database.py +++ /dev/null @@ -1,19 +0,0 @@ -from keystone.backends.sqlalchemy import migration -from keystone.manage2 import base -from keystone.manage2 import common - - -@common.arg('--version', - required=True, - help='specify the desired database version') -class Command(base.BaseSqlalchemyCommand): - """Upgrades the database to the specified version.""" - - @staticmethod - def upgrade_database(version): - """Upgrade database to the specified version""" - migration.upgrade(Command._get_connection_string(), version=version) - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.upgrade_database(version=args.version) diff --git a/keystone/manage2/commands/version.py b/keystone/manage2/commands/version.py deleted file mode 100644 index 8e97346fa4..0000000000 --- a/keystone/manage2/commands/version.py +++ /dev/null @@ -1,53 +0,0 @@ -from keystone.backends.sqlalchemy import migration -from keystone import version -from keystone.manage2 import base -from keystone.manage2 import common -from keystone.logic.types import fault - - -@common.arg('--api', action='store_true', - default=False, - help='only print the API version') -@common.arg('--implementation', action='store_true', - default=False, - help='only print the implementation version') -@common.arg('--database', action='store_true', - default=False, - help='only print the database version') -class Command(base.BaseSqlalchemyCommand): - """Returns keystone version data. - - Provides the latest API version, implementation version, database version, - or all of the above, if none is specified. - """ - - @staticmethod - def get_api_version(): - """Returns a complete API version string""" - return ' '.join([version.API_VERSION, version.API_VERSION_STATUS]) - - @staticmethod - def get_implementation_version(): - """Returns a complete implementation version string""" - return version.version() - - @staticmethod - def get_database_version(): - """Returns database's current migration level""" - return migration.db_version(Command._get_connection_string()) - - def run(self, args): - """Process argparse args, and print results to stdout""" - show_all = not (args.api or args.implementation or args.database) - - if args.api or show_all: - print 'API v%s' % Command.get_api_version() - if args.implementation or show_all: - print 'Implementation v%s' % Command.get_implementation_version() - if args.database or show_all: - try: - version_str = 'v%s' % (self.get_database_version()) - except fault.DatabaseMigrationError: - version_str = 'not under version control' - - print 'Database %s' % (version_str) diff --git a/keystone/manage2/commands/version_control_database.py b/keystone/manage2/commands/version_control_database.py deleted file mode 100644 index f69e206c9b..0000000000 --- a/keystone/manage2/commands/version_control_database.py +++ /dev/null @@ -1,15 +0,0 @@ -from keystone.backends.sqlalchemy import migration -from keystone.manage2 import base - - -class Command(base.BaseSqlalchemyCommand): - """Places an existing database under version control.""" - - @staticmethod - def version_control_database(): - """Place database under migration control""" - migration.version_control(Command._get_connection_string()) - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.version_control_database() diff --git a/keystone/manage2/common.py b/keystone/manage2/common.py deleted file mode 100644 index 5562410989..0000000000 --- a/keystone/manage2/common.py +++ /dev/null @@ -1,64 +0,0 @@ -import optparse -import sys - -from keystone import backends -from keystone import config as new_config -from keystone import version -from keystone.common import config -from keystone.managers.credential import Manager as CredentialManager -from keystone.managers.endpoint import Manager as EndpointManager -from keystone.managers.endpoint_template import Manager as \ - EndpointTemplateManager -from keystone.managers.grant import Manager as GrantManager -from keystone.managers.role import Manager as RoleManager -from keystone.managers.service import Manager as ServiceManager -from keystone.managers.tenant import Manager as TenantManager -from keystone.managers.token import Manager as TokenManager -from keystone.managers.user import Manager as UserManager - - -def arg(name, **kwargs): - """Decorate the command class with an argparse argument""" - def _decorator(cls): - if not hasattr(cls, '_args'): - setattr(cls, '_args', {}) - args = getattr(cls, '_args') - args[name] = kwargs - return cls - return _decorator - - -def get_options(): - # Initialize a parser for our configuration paramaters - parser = optparse.OptionParser("Usage", version='%%prog %s' - % version.version()) - config.add_common_options(parser) - config.add_log_options(parser) - - # Parse command-line and load config - (options, args) = config.parse_options(parser, []) # pylint: disable=W0612 - - return options - - -def init_managers(): - """Initializes backend storage and return managers""" - if new_config.CONF.backends is None: - # Get merged config and CLI options and admin-specific settings - options = get_options() - config_file = config.find_config_file(options, sys.argv[1:]) - new_config.CONF(config_files=[config_file]) - - backends.configure_backends() - - managers = {} - managers['credential_manager'] = CredentialManager() - managers['token_manager'] = TokenManager() - managers['tenant_manager'] = TenantManager() - managers['endpoint_manager'] = EndpointManager() - managers['endpoint_template_manager'] = EndpointTemplateManager() - managers['user_manager'] = UserManager() - managers['role_manager'] = RoleManager() - managers['grant_manager'] = GrantManager() - managers['service_manager'] = ServiceManager() - return managers diff --git a/keystone/manage2/mixins.py b/keystone/manage2/mixins.py deleted file mode 100644 index 67d3b4fd11..0000000000 --- a/keystone/manage2/mixins.py +++ /dev/null @@ -1,42 +0,0 @@ -import datetime -import prettytable - - -class ListMixin(object): - """Implements common patterns for list_* commands""" - - @staticmethod - def build_table(fields): - table = prettytable.PrettyTable(fields) - - # set default alignment - for field in fields: - table.set_field_align(field, "l") - - return table - - @staticmethod - def print_table(table): - if "Name" in table.fields: - table.printt(sortby="Name") - else: - table.printt() - - -class DateTimeMixin(object): - datetime_format = '%Y-%m-%dT%H:%M' - - def datetime_to_str(self, dt): - """Return a string representing the given datetime""" - return dt.strftime(self.datetime_format) - - def str_to_datetime(self, string): - """Return a datetime representing the given string""" - return datetime.datetime.strptime(string, self.datetime_format) - - @staticmethod - def get_datetime_tomorrow(): - """Returns a datetime representing 24 hours from now""" - today = datetime.datetime.utcnow() - tomorrow = today + datetime.timedelta(days=1) - return tomorrow diff --git a/keystone/managers/__init__.py b/keystone/managers/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/keystone/managers/credential.py b/keystone/managers/credential.py deleted file mode 100644 index 0fc8388a48..0000000000 --- a/keystone/managers/credential.py +++ /dev/null @@ -1,46 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (C) 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. - -""" Credential manager module """ - -import logging - -import keystone.backends.api as api - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class Manager(object): - def __init__(self): - self.driver = api.CREDENTIALS - - def create(self, token): - return self.driver.create(token) - - def update(self, id, credential): - return self.driver.update(id, credential) - - def get(self, credential_id): - return self.driver.get(credential_id) - - def get_all(self): - return self.driver.get_all() - - def get_by_access(self, access): - return self.driver.get_by_access(access) - - def delete(self, credential_id): - return self.driver.delete(credential_id) diff --git a/keystone/managers/endpoint.py b/keystone/managers/endpoint.py deleted file mode 100644 index 06401a0d7d..0000000000 --- a/keystone/managers/endpoint.py +++ /dev/null @@ -1,65 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (C) 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. - -""" Endpoint manager module """ - -import logging - -import keystone.backends.api as api - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class Manager(object): - def __init__(self): - self.driver = api.ENDPOINT_TEMPLATE - - def endpoint_get_by_endpoint_template(self, endpoint_template_id): - """ Get all endpoints by endpoint template """ - return self.driver.endpoint_get_by_endpoint_template( - endpoint_template_id) - - def delete(self, endpoint_id): - """ Delete Endpoint """ - self.driver.endpoint_delete(endpoint_id) - - def endpoint_get_by_tenant_get_page(self, tenant_id, marker, limit): - """ Get endpoints by tenant """ - return self.driver.endpoint_get_by_tenant_get_page( - tenant_id, marker, limit) - - def endpoint_get_by_tenant_get_page_markers(self, tenant_id, marker, - limit): - return self.driver.endpoint_get_by_tenant_get_page_markers( - tenant_id, marker, limit) - - def create(self, endpoint): - """ Create a new Endpoint """ - return self.driver.endpoint_add(endpoint) - - def get(self, endpoint_id): - """ Returns Endpoint by ID """ - return self.driver.endpoint_get(endpoint_id) - - # pylint: disable=E1103 - def get_by_ids(self, endpoint_template_id, tenant_id): - """ Returns Endpoint by ID """ - return self.driver.endpoint_get_by_ids(endpoint_template_id, tenant_id) - - # pylint: disable=E1103 - def get_all(self): - """ Returns all Endpoint Templates """ - return self.driver.endpoint_get_all() diff --git a/keystone/managers/endpoint_template.py b/keystone/managers/endpoint_template.py deleted file mode 100644 index 9be49e727b..0000000000 --- a/keystone/managers/endpoint_template.py +++ /dev/null @@ -1,69 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (C) 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. - -""" EndpointTemplate manager module """ - -import logging - -import keystone.backends.api as api - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class Manager(object): - def __init__(self): - self.driver = api.ENDPOINT_TEMPLATE - - def create(self, obj): - """ Create a new Endpoint Template """ - return self.driver.create(obj) - - def get_all(self): - """ Returns all endpoint templates """ - return self.driver.get_all() - - def get(self, endpoint_template_id): - """ Returns Endpoint Template by ID """ - return self.driver.get(endpoint_template_id) - - def get_page(self, marker, limit): - """ Get one page of endpoint template list """ - return self.driver.get_page(marker, limit) - - def get_page_markers(self, marker, limit): - """ Calculate pagination markers for endpoint template list """ - return self.driver.get_page_markers(marker, limit) - - def get_by_service(self, service_id): - """ Returns Endpoint Templates by service """ - return self.driver.get_by_service(service_id) - - def get_by_service_get_page(self, service_id, marker, limit): - """ Get one page of endpoint templates by service""" - return self.driver.get_by_service_get_page(service_id, marker, limit) - - def get_by_service_get_page_markers(self, service_id, marker, limit): - """ Calculate pagination markers for endpoint templates by service """ - return self.driver.get_by_service_get_page_markers(service_id, marker, - limit) - - def update(self, endpoint_template): - """ Update Endpoint Template """ - return self.driver.update(endpoint_template['id'], endpoint_template) - - def delete(self, endpoint_template_id): - """ Delete Endpoint Template """ - self.driver.delete(endpoint_template_id) diff --git a/keystone/managers/grant.py b/keystone/managers/grant.py deleted file mode 100644 index 41104ce66f..0000000000 --- a/keystone/managers/grant.py +++ /dev/null @@ -1,59 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (C) 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. - -""" Role-Grant manager module """ - -import logging - -import keystone.backends.api as api - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class Manager(object): - def __init__(self): - self.driver = api.ROLE - - # - # Role-Grant Methods - # - def rolegrant_get_page(self, user_id, tenant_id, marker, limit): - """ Get one page of role grant list """ - return self.driver.rolegrant_get_page(user_id, tenant_id, marker, - limit) - - def rolegrant_get_page_markers(self, user_id, tenant_id, marker, limit): - """ Calculate pagination markers for role grants list """ - return self.driver.rolegrant_get_page_markers(user_id, tenant_id, - marker, limit) - - def list_global_roles_for_user(self, user_id): - return self.driver.list_global_roles_for_user(user_id) - - def list_tenant_roles_for_user(self, user_id, tenant_id): - return self.driver.list_tenant_roles_for_user(user_id, tenant_id) - - def rolegrant_list_by_role(self, role_id): - return self.driver.rolegrant_list_by_role(role_id) - - def rolegrant_get_by_ids(self, user_id, role_id, tenant_id): - return self.driver.rolegrant_get_by_ids(user_id, role_id, tenant_id) - - def rolegrant_delete(self, grant_id): - return self.driver.rolegrant_delete(grant_id) - - def list_role_grants(self, role_id, user_id, tenant_id): - return self.driver.list_role_grants(role_id, user_id, tenant_id) diff --git a/keystone/managers/role.py b/keystone/managers/role.py deleted file mode 100644 index 0c3782e5ff..0000000000 --- a/keystone/managers/role.py +++ /dev/null @@ -1,74 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (C) 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. - -""" Role manager module """ - -import logging - -import keystone.backends.api as api - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class Manager(object): - def __init__(self): - self.driver = api.ROLE - - def create(self, role): - """ Create a new role """ - return self.driver.create(role) - - def get(self, role_id): - """ Returns role by ID """ - return self.driver.get(role_id) - - def get_by_name(self, name): - """ Returns role by name """ - return self.driver.get_by_name(name=name) - - def get_all(self): - """ Returns all roles """ - return self.driver.get_all() - - def get_page(self, marker, limit): - """ Get one page of roles list """ - return self.driver.get_page(marker, limit) - - def get_page_markers(self, marker, limit): - """ Calculate pagination markers for roles list """ - return self.driver.get_page_markers(marker, limit) - - def get_by_service(self, service_id): - """ Returns role by service """ - return self.driver.get_by_service(service_id) - - def get_by_service_get_page(self, service_id, marker, limit): - """ Get one page of roles by service""" - return self.driver.get_by_service_get_page(service_id, marker, limit) - - def get_by_service_get_page_markers(self, service_id, marker, limit): - """ Calculate pagination markers for roles by service """ - return self.driver.get_by_service_get_page_markers(service_id, marker, - limit) - - # pylint: disable=E1103 - def update(self, role): - """ Update role """ - return self.driver.update(role['id'], role) - - def delete(self, role_id): - """ Delete role """ - self.driver.delete(role_id) diff --git a/keystone/managers/service.py b/keystone/managers/service.py deleted file mode 100644 index c3063be194..0000000000 --- a/keystone/managers/service.py +++ /dev/null @@ -1,65 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (C) 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. - -""" Service manager module """ - -import logging - -import keystone.backends.api as api - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class Manager(object): - def __init__(self): - self.driver = api.SERVICE - - def create(self, service): - """ Create a new service """ - return self.driver.create(service) - - def get(self, service_id): - """ Returns service by ID """ - return self.driver.get(service_id) - - def get_by_name(self, name): - """ Returns service by name """ - return self.driver.get_by_name(name=name) - - def get_all(self): - """ Returns all services """ - return self.driver.get_all() - - def get_page(self, marker, limit): - """ Get one page of services list """ - return self.driver.get_page(marker, limit) - - def get_page_markers(self, marker, limit): - """ Calculate pagination markers for services list """ - return self.driver.get_page_markers(marker, limit) - - def get_by_name_and_type(self, name, service_type): - """ Returns service by name and type """ - return self.driver.get_by_name_and_type(name, service_type) - - # pylint: disable=E1103 - def update(self, service): - """ Update service """ - return self.driver.update(service['id'], service) - - def delete(self, service_id): - """ Delete service """ - self.driver.delete(service_id) diff --git a/keystone/managers/tenant.py b/keystone/managers/tenant.py deleted file mode 100644 index d69c089fdf..0000000000 --- a/keystone/managers/tenant.py +++ /dev/null @@ -1,75 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (C) 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. - -""" Tenant manager module - -TODO: move functionality into here. Ex: - - def get_tenant(self, context, tenant_id): - '''Return info for a tenant if it is valid.''' - return self.driver.get(tenant_id) -""" - -import logging - -import keystone.backends.api as api - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class Manager(object): - def __init__(self): - self.driver = api.TENANT - - def create(self, tenant): - return self.driver.create(tenant) - - def get(self, tenant_id): - """ Returns tenant by ID """ - return self.driver.get(tenant_id) - - def get_by_name(self, name): - """ Returns tenant by name """ - return self.driver.get_by_name(name=name) - - def get_all(self): - """ Returns all tenants """ - return self.driver.get_all() - - def get_page(self, marker, limit): - """ Get one page of tenants """ - return self.driver.get_page(marker, limit) - - def get_page_markers(self, marker, limit): - """ Calculate pagination markers for tenant list """ - return self.driver.get_page_markers(marker, limit) - - def list_for_user_get_page(self, user_id, marker, limit): - return self.driver.list_for_user_get_page(user_id, marker, limit) - - def list_for_user_get_page_markers(self, user_id, marker, limit): - return self.driver.list_for_user_get_page_markers(user_id, marker, - limit) - - def update(self, tenant): - """ Update tenant """ - return self.driver.update(tenant['id'], tenant) - - def delete(self, tenant_id): - self.driver.delete(tenant_id) - - def get_all_endpoints(self, tenant_id): - return self.driver.get_all_endpoints(tenant_id) diff --git a/keystone/managers/token.py b/keystone/managers/token.py deleted file mode 100644 index 9f21b35abc..0000000000 --- a/keystone/managers/token.py +++ /dev/null @@ -1,61 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (C) 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. - -""" Token manager module """ - -import logging - -import keystone.backends.api as api - -LOG = logging.getLogger(__name__) - - -class Manager(object): - def __init__(self): - self.driver = api.TOKEN - - def create(self, token): - return self.driver.create(token) - - # pylint: disable=E1103 - def update(self, id, token): - return self.driver.update(id, token) - - def get(self, token_id): - """ Returns token by ID """ - return self.driver.get(token_id) - - def get_all(self): - """ Returns all tokens """ - return self.driver.get_all() - - def find(self, user_id, tenant_id=None): - """ Finds token by user ID and, optionally, tenant ID - - :param user_id: user id as a string - :param tenant_id: tenant id as a string (optional) - :returns: Token object or None - :raises: RuntimeError is user_id is None - """ - if user_id is None: - raise RuntimeError("User ID is required when looking up tokens") - if tenant_id: - return self.driver.get_for_user_by_tenant(user_id, tenant_id) - else: - return self.driver.get_for_user(user_id) - - def delete(self, token_id): - self.driver.delete(token_id) diff --git a/keystone/managers/user.py b/keystone/managers/user.py deleted file mode 100644 index 6ed049d01f..0000000000 --- a/keystone/managers/user.py +++ /dev/null @@ -1,84 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (C) 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. - -""" User manager module """ - -import logging - -import keystone.backends.api as api - -LOG = logging.getLogger(__name__) - - -class Manager(object): - def __init__(self): - self.driver = api.USER - - def create(self, user): - """ Create user from dict or model, assign id if not there """ - return self.driver.create(user) - - def get(self, user_id): - """ Returns user by ID """ - return self.driver.get(user_id) - - def get_by_name(self, name): - """ Returns user by name """ - return self.driver.get_by_name(name=name) - - def get_by_email(self, email): - """ Returns user by email """ - return self.driver.get_by_email(email=email) - - def get_all(self): - """ Returns all users """ - return self.driver.get_all() - - def users_get_page(self, marker, limit): - """ Get one page of users list """ - return self.driver.users_get_page(marker, limit) - - def users_get_page_markers(self, marker, limit): - """ Calculate pagination markers for users list """ - return self.driver.users_get_page_markers(marker, limit) - - def get_by_tenant(self, user_id, tenant_id): - """ Get user if associated with tenant, else None """ - return self.driver.get_by_tenant(user_id, tenant_id) - - def users_get_by_tenant_get_page(self, tenant_id, role_id, marker, limit): - """ Get one page of users list for a tenant """ - return self.driver.users_get_by_tenant_get_page( - tenant_id, role_id, marker, limit) - - def users_get_by_tenant_get_page_markers(self, tenant_id, role_id, - marker, limit): - """ Calculate pagination markers for users list on a tenant """ - return self.driver.users_get_by_tenant_get_page_markers( - tenant_id, role_id, marker, limit) - - def update(self, user): - """ Update user """ - return self.driver.update(user['id'], user) - - def delete(self, user_id): - self.driver.delete(user_id) - - def check_password(self, user_id, password): - return self.driver.check_password(user_id, password) - - def user_role_add(self, values): - self.driver.user_role_add(values) diff --git a/keystone/middleware/__init__.py b/keystone/middleware/__init__.py index e69de29bb2..e2e9a993df 100644 --- a/keystone/middleware/__init__.py +++ b/keystone/middleware/__init__.py @@ -0,0 +1 @@ +from keystone.middleware.core import * diff --git a/keystone/middleware/auth_basic.py b/keystone/middleware/auth_basic.py deleted file mode 100644 index 3a5ce4167f..0000000000 --- a/keystone/middleware/auth_basic.py +++ /dev/null @@ -1,190 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010-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. - - -""" -BASIC AUTH MIDDLEWARE - STUB - -This WSGI component should perform multiple jobs: - -* validate incoming basic claims -* perform all basic auth interactions with clients -* collect and forward identity information from the authentication process - such as user name, groups, etc... - -This is an Auth component as per: http://wiki.openstack.org/openstack-authn - -""" - -import eventlet -from eventlet import wsgi -import os -import logging -from paste.deploy import loadapp -import urlparse -from webob.exc import Request, Response -from webob.exc import HTTPUnauthorized - -from keystone.common.bufferedhttp import http_connect_raw as http_connect - -PROTOCOL_NAME = "Basic Authentication" - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -def _decorate_request_headers(header, value, proxy_headers, env): - proxy_headers[header] = value - env["HTTP_%s" % header] = value - - -class AuthProtocol(object): - """Auth Middleware that handles authenticating client calls""" - - def __init__(self, app, conf): - logger.info("Starting the %s component", PROTOCOL_NAME) - - self.conf = conf - self.app = app - #if app is set, then we are in a WSGI pipeline and requests get passed - # on to app. If it is not set, this component should forward requests - - # where to find the OpenStack service (if not in local WSGI chain) - # these settings are only used if this component is acting as a proxy - # and the OpenSTack service is running remotely - self.service_protocol = conf.get('service_protocol', 'https') - self.service_host = conf.get('service_host') - self.service_port = int(conf.get('service_port')) - self.service_url = '%s://%s:%s' % (self.service_protocol, - self.service_host, - self.service_port) - # used to verify this component with the OpenStack service or PAPIAuth - self.service_pass = conf.get('service_pass') - - # delay_auth_decision means we still allow unauthenticated requests - # through and we let the downstream service make the final decision - self.delay_auth_decision = int(conf.get('delay_auth_decision', 0)) - - def __call__(self, env, start_response): - def custom_start_response(status, headers): - if self.delay_auth_decision: - headers.append(('WWW-Authenticate', - "Basic realm='Use guest/guest'")) - return start_response(status, headers) - - #Prep headers to proxy request to remote service - proxy_headers = env.copy() - user = '' - - #Look for authentication - if 'HTTP_AUTHORIZATION' not in env: - #No credentials were provided - if self.delay_auth_decision: - _decorate_request_headers("X_IDENTITY_STATUS", "Invalid", - proxy_headers, env) - else: - # If the user isn't authenticated, we reject the request and - # return 401 indicating we need Basic Auth credentials. - ret = HTTPUnauthorized("Authentication required", - [('WWW-Authenticate', - 'Basic realm="Use guest/guest"')]) - return ret(env, start_response) - else: - # Claims were provided - validate them - import base64 - auth_header = env['HTTP_AUTHORIZATION'] - _auth_type, encoded_creds = auth_header.split(None, 1) - user, password = base64.b64decode(encoded_creds).split(':', 1) - if not self.validateCreds(user, password): - #Claims were rejected - if not self.delay_auth_decision: - # Reject request (or ask for valid claims) - ret = HTTPUnauthorized("Authentication required", - [('WWW-Authenticate', - 'Basic realm="Use guest/guest"')]) - return ret(env, start_response) - else: - # Claims are valid, forward request - _decorate_request_headers("X_IDENTITY_STATUS", "Invalid", - proxy_headers, env) - - # TODO(Ziad): add additional details we may need, - # like tenant and group info - _decorate_request_headers('X_AUTHORIZATION', "Proxy %s" % user, - proxy_headers, env) - _decorate_request_headers("X_IDENTITY_STATUS", "Confirmed", - proxy_headers, env) - _decorate_request_headers('X_TENANT', 'blank', - proxy_headers, env) - #Auth processed, headers added now decide how to pass on the call - if self.app: - # Pass to downstream WSGI component - env['HTTP_AUTHORIZATION'] = "Basic %s" % self.service_pass - return self.app(env, custom_start_response) - - proxy_headers['AUTHORIZATION'] = "Basic %s" % self.service_pass - # We are forwarding to a remote service (no downstream WSGI app) - req = Request(proxy_headers) - parsed = urlparse(req.url) - conn = http_connect(self.service_host, self.service_port, \ - req.method, parsed.path, \ - proxy_headers, \ - ssl=(self.service_protocol == 'https')) - resp = conn.getresponse() - data = resp.read() - #TODO(ziad): use a more sophisticated proxy - # we are rewriting the headers now - return Response(status=resp.status, body=data)(env, start_response) - - def validateCreds(self, username, password): - #stub for password validation. - # import ConfigParser - # import hashlib - #usersConfig = ConfigParser.ConfigParser() - #usersConfig.readfp(open('/etc/openstack/users.ini')) - #password = hashlib.sha1(password).hexdigest() - #for un, pwd in usersConfig.items('users'): - #TODO(Ziad): add intelligent credential validation (instead of hard - # coded) - if username == 'guest' and password == 'guest': - return True - return False - - -def filter_factory(global_conf, ** local_conf): - """Returns a WSGI filter app for use with paste.deploy.""" - conf = global_conf.copy() - conf.update(local_conf) - - def auth_filter(app): - return AuthProtocol(app, conf) - return auth_filter - - -def app_factory(global_conf, ** local_conf): - conf = global_conf.copy() - conf.update(local_conf) - return AuthProtocol(None, conf) - -if __name__ == "__main__": - app = loadapp("config:" + \ - os.path.join(os.path.abspath(os.path.dirname(__file__)), - os.pardir, - os.pardir, - "examples/paste/auth_basic.ini"), - global_conf={"log_name": "auth_basic.log"}) - wsgi.server(eventlet.listen(('', 8090)), app) diff --git a/keystone/middleware/auth_openid.py b/keystone/middleware/auth_openid.py deleted file mode 100644 index 3debe2a4b7..0000000000 --- a/keystone/middleware/auth_openid.py +++ /dev/null @@ -1,125 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010-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. - -""" -OPENID AUTH MIDDLEWARE - STUB - -This WSGI component should perform multiple jobs: -- validate incoming openid claims -- perform all openid interactions with clients -- collect and forward identity information from the openid authentication - such as user name, groups, etc... - -This is an Auth component as per: http://wiki.openstack.org/openstack-authn -""" - -import logging -import eventlet -from eventlet import wsgi -import os -from paste.deploy import loadapp -import urlparse -from webob.exc import Request, Response - -from keystone.common.bufferedhttp import http_connect_raw as http_connect - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - -PROTOCOL_NAME = "OpenID Authentication" - - -class AuthProtocol(object): - """Auth Middleware that handles authenticating client calls""" - - def __init__(self, app, conf): - logger.info("Starting the %s component", PROTOCOL_NAME) - - self.conf = conf - self.app = app - #if app is set, then we are in a WSGI pipeline and requests get passed - # on to app. If it is not set, this component should forward requests - - # where to find the OpenStack service (if not in local WSGI chain) - # these settings are only used if this component is acting as a proxy - # and the OpenSTack service is running remotely - self.service_protocol = conf.get('service_protocol', 'http') - self.service_host = conf.get('service_host', '127.0.0.1') - self.service_port = int(conf.get('service_port', 8090)) - self.service_url = '%s://%s:%s' % (self.service_protocol, - self.service_host, - self.service_port) - # used to verify this component with the OpenStack service or PAPIAuth - self.service_pass = conf.get('service_pass', 'dTpw') - - # delay_auth_decision means we still allow unauthenticated requests - # through and we let the downstream service make the final decision - self.delay_auth_decision = int(conf.get('delay_auth_decision', 0)) - - def __call__(self, env, start_response): - def custom_start_response(status, headers): - if self.delay_auth_decision: - headers.append(('WWW-Authenticate', "Basic realm='API Realm'")) - return start_response(status, headers) - - #TODO(Rasib): PERFORM OPENID AUTH - - #Auth processed, headers added now decide how to pass on the call - if self.app: - # Pass to downstream WSGI component - env['HTTP_AUTHORIZATION'] = "Basic %s" % self.service_pass - return self.app(env, custom_start_response) - - proxy_headers = [] - proxy_headers['AUTHORIZATION'] = "Basic %s" % self.service_pass - # We are forwarding to a remote service (no downstream WSGI app) - req = Request(proxy_headers) - parsed = urlparse(req.url) - conn = http_connect(self.service_host, self.service_port, \ - req.method, parsed.path, \ - proxy_headers, \ - ssl=(self.service_protocol == 'https')) - resp = conn.getresponse() - data = resp.read() - #TODO(ziad): use a more sophisticated proxy - # we are rewriting the headers now - return Response(status=resp.status, body=data)(env, start_response) - - -def filter_factory(global_conf, **local_conf): - """Returns a WSGI filter app for use with paste.deploy.""" - conf = global_conf.copy() - conf.update(local_conf) - - def auth_filter(app): - return AuthProtocol(app, conf) - return auth_filter - - -def app_factory(global_conf, **local_conf): - conf = global_conf.copy() - conf.update(local_conf) - return AuthProtocol(None, conf) - -if __name__ == "__main__": - app = loadapp("config:" + \ - os.path.join(os.path.abspath(os.path.dirname(__file__)), - os.pardir, - os.pardir, - "examples/paste/auth_openid.ini"), - global_conf={"log_name": "auth_openid.log"}) - wsgi.server(eventlet.listen(('', 8090)), app) diff --git a/keystone/middleware/auth_token.py b/keystone/middleware/auth_token.py old mode 100644 new mode 100755 index 8cde88a634..4a0d501aa6 --- a/keystone/middleware/auth_token.py +++ b/keystone/middleware/auth_token.py @@ -1,6 +1,5 @@ -#!/usr/bin/env python # vim: tabstop=4 shiftwidth=4 softtabstop=4 -# + # Copyright (c) 2010-2011 OpenStack, LLC. # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,17 +18,18 @@ """ TOKEN-BASED AUTH MIDDLEWARE -This WSGI component: +This WSGI component performs multiple jobs: -* Verifies that incoming client requests have valid tokens by validating +* it verifies that incoming client requests have valid tokens by verifying tokens with the auth service. -* Rejects unauthenticated requests UNLESS it is in 'delay_auth_decision' +* it will reject unauthenticated requests UNLESS it is in 'delay_auth_decision' mode, which means the final decision is delegated to the downstream WSGI component (usually the OpenStack service) -* Collects and forwards identity information based on a valid token - such as user name, tenant, etc +* it will collect and forward identity information from a valid token + such as user name etc... + +Refer to: http://wiki.openstack.org/openstack-authn -Refer to: http://keystone.openstack.org/middleware_architecture.html HEADERS ------- @@ -41,11 +41,11 @@ Coming in from initial call from client or customer ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ HTTP_X_AUTH_TOKEN - The client token being passed in. + the client token being passed in HTTP_X_STORAGE_TOKEN - The client token being passed in (legacy Rackspace use) to support - swift/cloud files + the client token being passed in (legacy Rackspace use) to support + cloud files Used for communication between components ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -60,93 +60,35 @@ What we add to the request for use by the OpenStack service ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ HTTP_X_AUTHORIZATION - The client identity being passed in - -HTTP_X_IDENTITY_STATUS - 'Confirmed' or 'Invalid' - The underlying service will only see a value of 'Invalid' if the Middleware - is configured to run in 'delay_auth_decision' mode - -HTTP_X_TENANT - *Deprecated* in favor of HTTP_X_TENANT_ID and HTTP_X_TENANT_NAME - Keystone-assigned unique identifier, deprecated - -HTTP_X_TENANT_ID - Identity service managed unique identifier, string - -HTTP_X_TENANT_NAME - Unique tenant identifier, string - -HTTP_X_USER - *Deprecated* in favor of HTTP_X_USER_ID and HTTP_X_USER_NAME - Unique user name, string - -HTTP_X_USER_ID - Identity-service managed unique identifier, string - -HTTP_X_USER_NAME - Unique user identifier, string - -HTTP_X_ROLE - *Deprecated* in favor of HTTP_X_ROLES - This is being renamed, and the new header contains the same data. - -HTTP_X_ROLES - Comma delimited list of case-sensitive Roles + the client identity being passed in """ - -from datetime import datetime -from dateutil import parser -import errno -import eventlet -from eventlet import wsgi import httplib import json -# memcache is imported in __init__ if memcache caching is configured -import logging import os -from paste.deploy import loadapp -import time -import urllib + +import eventlet +from eventlet import wsgi +from paste import deploy from urlparse import urlparse +import webob +import webob.exc from webob.exc import HTTPUnauthorized -from webob.exc import Request, Response from keystone.common.bufferedhttp import http_connect_raw as http_connect -logger = logging.getLogger(__name__) # pylint: disable=C0103 - -PROTOCOL_NAME = "Token Authentication" -# The time format of the 'expires' property of a token -EXPIRE_TIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%f" -MAX_CACHE_TIME = 86400 - - -class ValidationFailed(Exception): - pass - - -class TokenExpired(Exception): - pass - - -class KeystoneUnreachable(Exception): - pass +PROTOCOL_NAME = 'Token Authentication' class AuthProtocol(object): """Auth Middleware that handles authenticating client calls""" - # pylint: disable=W0613 def _init_protocol_common(self, app, conf): - """ Common initialization code - - When we eventually superclass this, this will be the superclass - initialization code that applies to all protocols - """ - logger.info("Starting the %s component", PROTOCOL_NAME) + """ Common initialization code""" + print 'Starting the %s component' % PROTOCOL_NAME + self.conf = conf + self.app = app #if app is set, then we are in a WSGI pipeline and requests get passed # on to app. If it is not set, this component should forward requests @@ -155,18 +97,10 @@ class AuthProtocol(object): # and the OpenSTack service is running remotely self.service_protocol = conf.get('service_protocol', 'https') self.service_host = conf.get('service_host') - service_port = conf.get('service_port') - service_ids = conf.get('service_ids') - self.service_id_querystring = '' - if service_ids: - self.service_id_querystring = '?HP-IDM-serviceId=%s' % \ - (urllib.quote(service_ids)) - if service_port: - self.service_port = int(service_port) + self.service_port = int(conf.get('service_port')) self.service_url = '%s://%s:%s' % (self.service_protocol, self.service_host, self.service_port) - self.service_timeout = conf.get('service_timeout', 30) # used to verify this component with the OpenStack service or PAPIAuth self.service_pass = conf.get('service_pass') @@ -181,94 +115,27 @@ class AuthProtocol(object): self.auth_host = conf.get('auth_host') self.auth_port = int(conf.get('auth_port')) self.auth_protocol = conf.get('auth_protocol', 'https') - self.auth_timeout = float(conf.get('auth_timeout', 30)) # where to tell clients to find the auth service (default to url # constructed based on endpoint we have for the service to use) self.auth_location = conf.get('auth_uri', - "%s://%s:%s" % (self.auth_protocol, + '%s://%s:%s' % (self.auth_protocol, self.auth_host, self.auth_port)) - logger.debug("Authentication Service:%s", self.auth_location) # Credentials used to verify this component with the Auth service since # validating tokens is a privileged call self.admin_token = conf.get('admin_token') - self.admin_user = conf.get('admin_user', None) - self.admin_password = conf.get('admin_password', None) - # Certificate file and key file used to authenticate with Keystone - # server - self.cert_file = conf.get('certfile', None) - self.key_file = conf.get('keyfile', None) - # Caching - self.cache = conf.get('cache', None) - self.memcache_hosts = conf.get('memcache_hosts', None) - if self.memcache_hosts: - if self.cache is None: - self.cache = "keystone.cache" - self.tested_for_osksvalidate = False - self.last_test_for_osksvalidate = None - self.osksvalidate = self._supports_osksvalidate() def __init__(self, app, conf): """ Common initialization code """ + #TODO(ziad): maybe we refactor this into a superclass - # Defining instance variables here for improving pylint score - # NOTE(salvatore-orlando): the following vars are assigned values - # either in init_protocol or init_protocol_common. We should not - # worry about them being initialized to None - self.conf = conf - self.app = app - self.admin_password = None - self.admin_token = None - self.admin_user = None - self.auth_api_version = None - self.auth_host = None - self.auth_location = None - self.auth_port = None - self.auth_protocol = None - self.auth_timeout = None - self.cert_file = None - self.key_file = None - self.delay_auth_decision = None - self.service_pass = None - self.service_host = None - self.service_port = None - self.service_protocol = None - self.service_timeout = None - self.service_url = None - self.service_id_querystring = None - self.osksvalidate = None - self.tested_for_osksvalidate = None - self.last_test_for_osksvalidate = None - self.cache = None - self.memcache_hosts = None self._init_protocol_common(app, conf) # Applies to all protocols self._init_protocol(conf) # Specific to this protocol def __call__(self, env, start_response): """ Handle incoming request. Authenticate. And send downstream. """ - logger.debug("entering AuthProtocol.__call__") - # Initialize caching client - if self.memcache_hosts: - # This will only be used if the configuration calls for memcache - import memcache - - if env.get(self.cache, None) is None: - memcache_client = memcache.Client([self.memcache_hosts]) - env[self.cache] = memcache_client - - # Check if we're set up to use OS-KSVALIDATE periodically if not on - if self.tested_for_osksvalidate != True: - if self.last_test_for_osksvalidate is None or \ - (time.time() - self.last_test_for_osksvalidate) > 60: - # Try test again every 60 seconds if failed - # this also handles if middleware was started before - # the keystone server - try: - self.osksvalidate = self._supports_osksvalidate() - except (httplib.HTTPException, StandardError): - pass #Prep headers to forward request to local or remote downstream service proxy_headers = env.copy() @@ -278,410 +145,230 @@ class AuthProtocol(object): del proxy_headers[header] #Look for authentication claims - token = self._get_claims(env) - if not token: - logger.debug("No claims provided") + claims = self._get_claims(env) + if not claims: + #No claim(s) provided if self.delay_auth_decision: #Configured to allow downstream service to make final decision. #So mark status as Invalid and forward the request downstream - logger.debug("delay_auth_decision is %s, so sending request " - "down the pipeline" % self.delay_auth_decision) - self._decorate_request("X_IDENTITY_STATUS", - "Invalid", env, proxy_headers) + self._decorate_request('X_IDENTITY_STATUS', + 'Invalid', env, proxy_headers) else: #Respond to client as appropriate for this auth protocol return self._reject_request(env, start_response) else: # this request is presenting claims. Let's validate them - try: - claims = self._verify_claims(env, token) - except (ValidationFailed, TokenExpired): + valid = self._validate_claims(claims) + if not valid: # Keystone rejected claim if self.delay_auth_decision: # Downstream service will receive call still and decide - self._decorate_request("X_IDENTITY_STATUS", - "Invalid", env, proxy_headers) + self._decorate_request('X_IDENTITY_STATUS', + 'Invalid', env, proxy_headers) else: #Respond to client as appropriate for this auth protocol return self._reject_claims(env, start_response) else: - self._decorate_request("X_IDENTITY_STATUS", - "Confirmed", env, proxy_headers) + self._decorate_request('X_IDENTITY_STATUS', + 'Confirmed', env, proxy_headers) + + #Collect information about valid claims + if valid: + claims = self._expound_claims(claims) # Store authentication data if claims: - self._decorate_request('X_AUTHORIZATION', "Proxy %s" % - claims['user']['name'], env, proxy_headers) + self._decorate_request('X_AUTHORIZATION', 'Proxy %s' % + claims['user'], env, proxy_headers) - self._decorate_request('X_TENANT_ID', - claims['tenant']['id'], env, proxy_headers) - self._decorate_request('X_TENANT_NAME', - claims['tenant']['name'], env, proxy_headers) - - self._decorate_request('X_USER_ID', - claims['user']['id'], env, proxy_headers) - self._decorate_request('X_USER_NAME', - claims['user']['name'], env, proxy_headers) - - roles = ','.join(claims['roles']) - self._decorate_request('X_ROLES', - roles, env, proxy_headers) - - # Deprecated in favor of X_TENANT_ID and _NAME + # For legacy compatibility before we had ID and Name self._decorate_request('X_TENANT', - claims['tenant']['id'], env, proxy_headers) + claims['tenant'], env, proxy_headers) + + # Services should use these + self._decorate_request('X_TENANT_NAME', + claims.get('tenant_name', claims['tenant']), + env, proxy_headers) + self._decorate_request('X_TENANT_ID', + claims['tenant'], env, proxy_headers) - # Deprecated in favor of X_USER_ID and _NAME - # TODO(zns): documentation says this should be the username - # the user logged in with. We've been returning the id... self._decorate_request('X_USER', - claims['user']['id'], env, proxy_headers) + claims['user'], env, proxy_headers) + if 'roles' in claims and len(claims['roles']) > 0: + if claims['roles'] != None: + roles = '' + for role in claims['roles']: + if len(roles) > 0: + roles += ',' + roles += role + self._decorate_request('X_ROLE', + roles, env, proxy_headers) - # Deprecated in favor of X_ROLES - self._decorate_request('X_ROLE', - roles, env, proxy_headers) + # NOTE(todd): unused + self.expanded = True #Send request downstream return self._forward_request(env, start_response, proxy_headers) - @staticmethod - def _convert_date(date): - """ Convert datetime to unix timestamp for caching """ - return time.mktime(parser.parse(date).utctimetuple()) - - # pylint: disable=W0613 - @staticmethod - def _protect_claims(token, claims): - """ encrypt or mac claims if necessary """ - return claims - - # pylint: disable=W0613 - @staticmethod - def _unprotect_claims(token, pclaims): - """ decrypt or demac claims if necessary """ - return pclaims - - def _cache_put(self, env, token, claims, valid): - """ Put a claim into the cache """ - cache = self._cache(env) - if cache and claims: - key = 'tokens/%s' % (token) - if "timeout" in cache.set.func_code.co_varnames: - # swift cache - expires = self._convert_date(claims['expires']) - claims = self._protect_claims(token, claims) - cache.set(key, (claims, expires, valid), - timeout=expires - time.time()) - else: - # normal memcache client - expires = self._convert_date(claims['expires']) - timeout = expires - time.time() - if timeout > MAX_CACHE_TIME or not valid: - # Limit cache to one day (and cache bad tokens for a day) - timeout = MAX_CACHE_TIME - claims = self._protect_claims(token, claims) - cache.set(key, (claims, expires, valid), time=timeout) - - def _cache_get(self, env, token): - """ Return claim and relevant information (expiration and validity) - from cache """ - cache = self._cache(env) - if cache: - key = 'tokens/%s' % (token) - cached_claims = cache.get(key) - if cached_claims: - claims, expires, valid = cached_claims - if valid: - if "timeout" in cache.set.func_code.co_varnames: - if expires > time.time(): - claims = self._unprotect_claims(token, claims) - else: - if expires > time.time(): - claims = self._unprotect_claims(token, claims) - return (claims, expires, valid) - return None - - def _cache(self, env): - """ Return a cache to use for token caching, or none """ - if self.cache is not None: - return env.get(self.cache, None) - return None - - @staticmethod - def _get_claims(env): - """Get claims from request""" - logger.debug("Looking for authentication claims in _get_claims") - claims = env.get('HTTP_X_AUTH_TOKEN', env.get('HTTP_X_STORAGE_TOKEN')) - return claims - - def _reject_request(self, env, start_response): - """Redirect client to auth server""" - logger.debug("Rejecting request - authentication required") - return HTTPUnauthorized("Authentication required", - [("WWW-Authenticate", - "Keystone uri='%s'" % self.auth_location)])(env, - start_response) - - @staticmethod - def _reject_claims(env, start_response): - """Client sent bad claims""" - logger.debug("Rejecting request - bad claim or token") - return HTTPUnauthorized()(env, - start_response) - - def _build_token_uri(self): - return '/v2.0/tokens/%s' % self.service_id_querystring - - def _get_admin_auth_token(self, username, password): + # NOTE(todd): unused + def get_admin_auth_token(self, username, password): """ This function gets an admin auth token to be used by this service to validate a user's token. Validate_token is a priviledged call so it needs to be authenticated by a service that is calling it """ - headers = { - "Content-type": "application/json", - "Accept": "application/json"} - params = { - "auth": { - "passwordCredentials": { - "username": username, - "password": password, - } - } - } - if self.auth_protocol == "http": - conn = httplib.HTTPConnection(self.auth_host, self.auth_port) - else: - conn = httplib.HTTPSConnection(self.auth_host, self.auth_port, - cert_file=self.cert_file) - conn.request("POST", self._build_token_uri(), json.dumps(params), + headers = {'Content-type': 'application/json', + 'Accept': 'application/json'} + params = {'passwordCredentials': {'username': username, + 'password': password, + 'tenantId': '1'}} + conn = httplib.HTTPConnection('%s:%s' \ + % (self.auth_host, self.auth_port)) + conn.request('POST', '/v2.0/tokens', json.dumps(params), \ headers=headers) response = conn.getresponse() data = response.read() return data - def _verify_claims(self, env, claims, retry=True): - """Verify claims and extract identity information, if applicable.""" + def _get_claims(self, env): + """Get claims from request""" + claims = env.get('HTTP_X_AUTH_TOKEN', env.get('HTTP_X_STORAGE_TOKEN')) + return claims - cached_claims = self._cache_get(env, claims) - if cached_claims: - logger.debug("Found cached claims") - claims, expires, valid = cached_claims - if not valid: - logger.debug("Claims not valid (according to cache)") - raise ValidationFailed() - if expires <= time.time(): - logger.debug("Claims (token) expired (according to cache)") - raise TokenExpired() - return claims + def _reject_request(self, env, start_response): + """Redirect client to auth server""" + return webob.exc.HTTPUnauthorized('Authentication required', + [('WWW-Authenticate', + "Keystone uri='%s'" % self.auth_location)])(env, + start_response) + + def _reject_claims(self, env, start_response): + """Client sent bad claims""" + return webob.exc.HTTPUnauthorized()(env, + start_response) + + def _validate_claims(self, claims): + """Validate claims, and provide identity information isf applicable """ # Step 1: We need to auth with the keystone service, so get an # admin token - if not self.admin_token: - auth = self._get_admin_auth_token(self.admin_user, - self.admin_password) - self.admin_token = json.loads(auth)["access"]["token"]["id"] + #TODO(ziad): Need to properly implement this, where to store creds + # for now using token from ini + #auth = self.get_admin_auth_token('admin', 'secrete', '1') + #admin_token = json.loads(auth)['auth']['token']['id'] # Step 2: validate the user's token with the auth service # since this is a priviledged op,m we need to auth ourselves # by using an admin token - headers = {"Content-type": "application/json", - "Accept": "application/json", - "X-Auth-Token": self.admin_token} - if self.osksvalidate: - headers['X-Subject-Token'] = claims - path = '/v2.0/OS-KSVALIDATE/token/validate/%s' % \ - self.service_id_querystring - logger.debug("Connecting to %s://%s:%s to check claims using the" - "OS-KSVALIDATE extension" % (self.auth_protocol, - self.auth_host, self.auth_port)) - else: - path = '/v2.0/tokens/%s%s' % (claims, self.service_id_querystring) - logger.debug("Connecting to %s://%s:%s to check claims" % ( - self.auth_protocol, self.auth_host, self.auth_port)) + headers = {'Content-type': 'application/json', + 'Accept': 'application/json', + 'X-Auth-Token': self.admin_token} + ##TODO(ziad):we need to figure out how to auth to keystone + #since validate_token is a priviledged call + #Khaled's version uses creds to get a token + # 'X-Auth-Token': admin_token} + # we're using a test token from the ini file for now + conn = http_connect(self.auth_host, self.auth_port, 'GET', + '/v2.0/tokens/%s' % claims, headers=headers) + resp = conn.getresponse() + # data = resp.read() + conn.close() - try: - conn = http_connect(self.auth_host, self.auth_port, 'GET', - path, - headers=headers, - ssl=(self.auth_protocol == 'https'), - key_file=self.key_file, - cert_file=self.cert_file, - timeout=self.auth_timeout) - resp = conn.getresponse() - data = resp.read() - except EnvironmentError as exc: - if exc.errno == errno.ECONNREFUSED: - logger.error("Keystone server not responding on %s://%s:%s " - "to check claims" % (self.auth_protocol, - self.auth_host, - self.auth_port)) - raise KeystoneUnreachable("Unable to connect to authentication" - " server") - else: - logger.exception(exc) - raise - - logger.debug("Response received: %s" % resp.status) if not str(resp.status).startswith('20'): - # Cache it if there is a cache available - if self.cache: - logger.debug("Caching that results were invalid") - self._cache_put(env, claims, - claims={'expires': - datetime.strftime(time.time(), - EXPIRE_TIME_FORMAT)}, - valid=False) - if retry: - self.admin_token = None - return self._verify_claims(env, claims, False) - else: - # Keystone rejected claim - logger.debug("Failing the validation") - raise ValidationFailed() + # Keystone rejected claim + return False + else: + #TODO(Ziad): there is an optimization we can do here. We have just + #received data from Keystone that we can use instead of making + #another call in _expound_claims + return True + + def _expound_claims(self, claims): + # Valid token. Get user data and put it in to the call + # so the downstream service can use it + headers = {'Content-type': 'application/json', + 'Accept': 'application/json', + 'X-Auth-Token': self.admin_token} + ##TODO(ziad):we need to figure out how to auth to keystone + #since validate_token is a priviledged call + #Khaled's version uses creds to get a token + # 'X-Auth-Token': admin_token} + # we're using a test token from the ini file for now + conn = http_connect(self.auth_host, self.auth_port, 'GET', + '/v2.0/tokens/%s' % claims, headers=headers) + resp = conn.getresponse() + data = resp.read() + conn.close() + + if not str(resp.status).startswith('20'): + raise LookupError('Unable to locate claims: %s' % resp.status) token_info = json.loads(data) + roles = [] + role_refs = token_info['access']['user']['roles'] + if role_refs != None: + for role_ref in role_refs: + # Nova looks for the non case-sensitive role 'Admin' + # to determine admin-ness + roles.append(role_ref['name']) - roles = [role['name'] for role in token_info[ - "access"]["user"]["roles"]] - - # in diablo, there were two ways to get tenant data - tenant = token_info['access']['token'].get('tenant') - if tenant: - # post diablo - tenant_id = tenant['id'] - tenant_name = tenant['name'] - else: - # diablo only - tenant_id = token_info['access']['user'].get('tenantId') + try: + tenant = token_info['access']['token']['tenant']['id'] + tenant_name = token_info['access']['token']['tenant']['name'] + except: + tenant = None + tenant_name = None + if not tenant: + tenant = token_info['access']['user'].get('tenantId') tenant_name = token_info['access']['user'].get('tenantName') - logger.debug("Tenant identified: id=%s, name=%s" % (tenant_id, - tenant_name)) - - verified_claims = { - 'user': { - 'id': token_info['access']['user']['id'], - 'name': token_info['access']['user']['name'], - }, - 'tenant': { - 'id': tenant_id, - 'name': tenant_name - }, - 'roles': roles, - 'expires': token_info['access']['token']['expires']} - logger.debug("User identified: id=%s, name=%s" % ( - token_info['access']['user']['id'], - token_info['access']['user']['name'])) - - expires = self._convert_date(verified_claims['expires']) - if expires <= time.time(): - logger.debug("Claims (token) expired: %s" % str(expires)) - # Cache it if there is a cache available (we also cached bad - # claims) - if self.cache: - logger.debug("Caching expired claim (token)") - self._cache_put(env, claims, verified_claims, valid=False) - raise TokenExpired() - - # Cache it if there is a cache available - if self.cache: - logger.debug("Caching validated claim") - self._cache_put(env, claims, verified_claims, valid=True) - logger.debug("Returning successful validation") + verified_claims = {'user': token_info['access']['user']['username'], + 'tenant': tenant, + 'roles': roles} + if tenant_name: + verified_claims['tenantName'] = tenant_name return verified_claims - @staticmethod - def _decorate_request(index, value, env, proxy_headers): + def _decorate_request(self, index, value, env, proxy_headers): """Add headers to request""" - logger.debug("Decorating request with HTTP_%s=%s" % (index, value)) proxy_headers[index] = value - env["HTTP_%s" % index] = value + env['HTTP_%s' % index] = value def _forward_request(self, env, start_response, proxy_headers): """Token/Auth processed & claims added to headers""" self._decorate_request('AUTHORIZATION', - "Basic %s" % self.service_pass, env, proxy_headers) + 'Basic %s' % self.service_pass, env, proxy_headers) #now decide how to pass on the call if self.app: # Pass to downstream WSGI component - logger.debug("Sending request to next app in WSGI pipeline") return self.app(env, start_response) #.custom_start_response) else: # We are forwarding to a remote service (no downstream WSGI app) - logger.debug("Sending request to %s" % self.service_url) - req = Request(proxy_headers) + req = webob.Request(proxy_headers) parsed = urlparse(req.url) - # pylint: disable=E1101 conn = http_connect(self.service_host, self.service_port, req.method, parsed.path, proxy_headers, - ssl=(self.service_protocol == 'https'), - timeout=self.service_timeout) + ssl=(self.service_protocol == 'https')) resp = conn.getresponse() data = resp.read() - logger.debug("Response was %s" % resp.status) #TODO(ziad): use a more sophisticated proxy # we are rewriting the headers now - if resp.status in (401, 305): + if resp.status == 401 or resp.status == 305: # Add our own headers to the list - headers = [("WWW_AUTHENTICATE", + headers = [('WWW_AUTHENTICATE', "Keystone uri='%s'" % self.auth_location)] - return Response(status=resp.status, body=data, - headerlist=headers)(env, - start_response) + return webob.Response(status=resp.status, + body=data, + headerlist=headers)(env, start_response) else: - return Response(status=resp.status, body=data)(env, - start_response) - - def _supports_osksvalidate(self): - """Check if target Keystone server supports OS-KSVALIDATE.""" - if self.tested_for_osksvalidate: - return self.osksvalidate - - headers = {"Accept": "application/json"} - logger.debug("Connecting to %s://%s:%s to check extensions" % ( - self.auth_protocol, self.auth_host, self.auth_port)) - try: - self.last_test_for_osksvalidate = time.time() - conn = http_connect(self.auth_host, self.auth_port, 'GET', - '/v2.0/extensions/', - headers=headers, - ssl=(self.auth_protocol == 'https'), - key_file=self.key_file, - cert_file=self.cert_file, - timeout=self.auth_timeout) - resp = conn.getresponse() - data = resp.read() - - logger.debug("Response received: %s" % resp.status) - if not str(resp.status).startswith('20'): - logger.debug("Failed to detect extensions. " - "Falling back to core API") - return False - except EnvironmentError as exc: - if exc.errno == errno.ECONNREFUSED: - logger.warning("Keystone server not responding. Extension " - "detection will be retried later.") - else: - logger.exception("Unexpected error trying to detect " - "extensions.") - logger.debug("Falling back to core API behavior (using tokens in " - "URL)") - return False - except httplib.HTTPException as exc: - logger.exception("Error trying to detect extensions.") - logger.debug("Falling back to core API behavior (using tokens in " - "URL)") - return False - - self.tested_for_osksvalidate = True - return "OS-KSVALIDATE" in data + return webob.Response(status=resp.status, + body=data)(env, start_response) def filter_factory(global_conf, **local_conf): @@ -689,8 +376,8 @@ def filter_factory(global_conf, **local_conf): conf = global_conf.copy() conf.update(local_conf) - def auth_filter(filteredapp): - return AuthProtocol(filteredapp, conf) + def auth_filter(app): + return AuthProtocol(app, conf) return auth_filter @@ -699,20 +386,11 @@ def app_factory(global_conf, **local_conf): conf.update(local_conf) return AuthProtocol(None, conf) - -def main(): - """Called when the middleware is started up separately (as in a remote - proxy configuration) - """ - config_file = os.path.join(os.path.abspath(os.path.dirname(__file__)), +if __name__ == '__main__': + app = deploy.loadapp('config:' + \ + os.path.join(os.path.abspath(os.path.dirname(__file__)), os.pardir, os.pardir, - "examples/paste/auth_token.ini") - logger.debug("Initializing with config file: %s" % config_file) - wsgiapp = loadapp("config:%s" % config_file, - global_conf={"log_name": "auth_token.log"}) - wsgi.server(eventlet.listen(('', 8090)), wsgiapp) - - -if __name__ == "__main__": - main() + 'examples/paste/auth_token.ini'), + global_conf={'log_name': 'auth_token.log'}) + wsgi.server(eventlet.listen(('', 8090)), app) diff --git a/keystone/middleware/core.py b/keystone/middleware/core.py new file mode 100644 index 0000000000..09b86bbf6e --- /dev/null +++ b/keystone/middleware/core.py @@ -0,0 +1,112 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +import json + +import webob.exc + +from keystone import config +from keystone.common import wsgi + + +CONF = config.CONF + + +# Header used to transmit the auth token +AUTH_TOKEN_HEADER = 'X-Auth-Token' + + +# Environment variable used to pass the request context +CONTEXT_ENV = 'openstack.context' + + +# Environment variable used to pass the request params +PARAMS_ENV = 'openstack.params' + + +class TokenAuthMiddleware(wsgi.Middleware): + def process_request(self, request): + token = request.headers.get(AUTH_TOKEN_HEADER) + context = request.environ.get(CONTEXT_ENV, {}) + context['token_id'] = token + request.environ[CONTEXT_ENV] = context + + +class AdminTokenAuthMiddleware(wsgi.Middleware): + """A trivial filter that checks for a pre-defined admin token. + + Sets 'is_admin' to true in the context, expected to be checked by + methods that are admin-only. + + """ + + def process_request(self, request): + token = request.headers.get(AUTH_TOKEN_HEADER) + context = request.environ.get(CONTEXT_ENV, {}) + context['is_admin'] = (token == CONF.admin_token) + request.environ[CONTEXT_ENV] = context + + +class PostParamsMiddleware(wsgi.Middleware): + """Middleware to allow method arguments to be passed as POST parameters. + + Filters out the parameters `self`, `context` and anything beginning with + an underscore. + + """ + + def process_request(self, request): + params_parsed = request.params + params = {} + for k, v in params_parsed.iteritems(): + if k in ('self', 'context'): + continue + if k.startswith('_'): + continue + params[k] = v + + request.environ[PARAMS_ENV] = params + + +class JsonBodyMiddleware(wsgi.Middleware): + """Middleware to allow method arguments to be passed as serialized JSON. + + Accepting arguments as JSON is useful for accepting data that may be more + complex than simple primitives. + + In this case we accept it as urlencoded data under the key 'json' as in + json= but this could be extended to accept raw JSON + in the POST body. + + Filters out the parameters `self`, `context` and anything beginning with + an underscore. + + """ + def process_request(self, request): + # Ignore unrecognized content types. Empty string indicates + # the client did not explicitly set the header + if not request.content_type in ('application/json', ''): + return + + params_json = request.body + if not params_json: + return + + params_parsed = {} + try: + params_parsed = json.loads(params_json) + except ValueError: + msg = "Malformed json in request body" + raise webob.exc.HTTPBadRequest(explanation=msg) + finally: + if not params_parsed: + params_parsed = {} + + params = {} + for k, v in params_parsed.iteritems(): + if k in ('self', 'context'): + continue + if k.startswith('_'): + continue + params[k] = v + + request.environ[PARAMS_ENV] = params diff --git a/keystone/middleware/crypt.py b/keystone/middleware/crypt.py deleted file mode 100644 index bb25620dab..0000000000 --- a/keystone/middleware/crypt.py +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# 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. - -""" -Routines for URL-safe encrypting/decrypting - -Keep this file in sync with all copies: -- glance/common/crypt.py -- keystone/middleware/crypt.py -- keystone/common/crypt.py - -""" - -import base64 - -from Crypto.Cipher import AES -from Crypto import Random -from Crypto.Random import random - - -def urlsafe_encrypt(key, plaintext, blocksize=16): - """ - Encrypts plaintext. Resulting ciphertext will contain URL-safe characters - :param key: AES secret key - :param plaintext: Input text to be encrypted - :param blocksize: Non-zero integer multiple of AES blocksize in bytes (16) - - :returns : Resulting ciphertext - """ - def pad(text): - """ - Pads text to be encrypted - """ - pad_length = (blocksize - len(text) % blocksize) - sr = random.StrongRandom() - pad = ''.join(chr(sr.randint(1, 0xFF)) for i in range(pad_length - 1)) - # We use chr(0) as a delimiter between text and padding - return text + chr(0) + pad - - # random initial 16 bytes for CBC - init_vector = Random.get_random_bytes(16) - cypher = AES.new(key, AES.MODE_CBC, init_vector) - padded = cypher.encrypt(pad(str(plaintext))) - return base64.urlsafe_b64encode(init_vector + padded) - - -def urlsafe_decrypt(key, ciphertext): - """ - Decrypts URL-safe base64 encoded ciphertext - :param key: AES secret key - :param ciphertext: The encrypted text to decrypt - - :returns : Resulting plaintext - """ - # Cast from unicode - ciphertext = base64.urlsafe_b64decode(str(ciphertext)) - cypher = AES.new(key, AES.MODE_CBC, ciphertext[:16]) - padded = cypher.decrypt(ciphertext[16:]) - return padded[:padded.rfind(chr(0))] diff --git a/keystone/middleware/ec2_token.py b/keystone/middleware/ec2_token.py new file mode 100644 index 0000000000..cc3094a3be --- /dev/null +++ b/keystone/middleware/ec2_token.py @@ -0,0 +1,92 @@ +# 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. +""" +Starting point for routing EC2 requests. + +""" + +from urlparse import urlparse + +from eventlet.green import httplib +import webob.dec +import webob.exc + +from nova import flags +from nova import utils +from nova import wsgi + + +FLAGS = flags.FLAGS +flags.DEFINE_string('keystone_ec2_url', + 'http://localhost:5000/v2.0/ec2tokens', + 'URL to get token from ec2 request.') + + +class EC2Token(wsgi.Middleware): + """Authenticate an EC2 request with keystone and convert to token.""" + + @webob.dec.wsgify(RequestClass=wsgi.Request) + def __call__(self, req): + # Read request signature and access id. + try: + signature = req.params['Signature'] + access = req.params['AWSAccessKeyId'] + except KeyError: + raise webob.exc.HTTPBadRequest() + + # Make a copy of args for authentication and signature verification. + auth_params = dict(req.params) + # Not part of authentication args + auth_params.pop('Signature') + + # Authenticate the request. + creds = {'ec2Credentials': {'access': access, + 'signature': signature, + 'host': req.host, + 'verb': req.method, + 'path': req.path, + 'params': auth_params, + }} + creds_json = utils.dumps(creds) + headers = {'Content-Type': 'application/json'} + + # Disable 'has no x member' pylint error + # for httplib and urlparse + # pylint: disable-msg=E1101 + o = urlparse(FLAGS.keystone_ec2_url) + if o.scheme == 'http': + conn = httplib.HTTPConnection(o.netloc) + else: + conn = httplib.HTTPSConnection(o.netloc) + conn.request('POST', o.path, body=creds_json, headers=headers) + response = conn.getresponse().read() + conn.close() + + # NOTE(vish): We could save a call to keystone by + # having keystone return token, tenant, + # user, and roles from this call. + + result = utils.loads(response) + try: + token_id = result['access']['token']['id'] + except (AttributeError, KeyError): + raise webob.exc.HTTPBadRequest() + + # Authenticated! + req.headers['X-Auth-Token'] = token_id + return self.application diff --git a/keystone/middleware/glance_auth_token.py b/keystone/middleware/glance_auth_token.py index cc689bc8cf..6bef13901c 100644 --- a/keystone/middleware/glance_auth_token.py +++ b/keystone/middleware/glance_auth_token.py @@ -30,12 +30,9 @@ middleware. Example: examples/paste/glance-api.conf, examples/paste/glance-registry.conf """ -import logging from glance.common import context -logger = logging.getLogger(__name__) # pylint: disable=C0103 - class KeystoneContextMiddleware(context.ContextMiddleware): """Glance keystone integration middleware.""" @@ -47,8 +44,6 @@ class KeystoneContextMiddleware(context.ContextMiddleware): """ # Only accept the authentication information if the identity # has been confirmed--presumably by upstream - logger.debug('X_IDENTITY_STATUS=%s' % - req.headers.get('X_IDENTITY_STATUS')) if req.headers.get('X_IDENTITY_STATUS', 'Invalid') != 'Confirmed': # Use the default empty context req.context = self.make_context(read_only=True) @@ -57,7 +52,7 @@ class KeystoneContextMiddleware(context.ContextMiddleware): # OK, let's extract the information we need auth_tok = req.headers.get('X_AUTH_TOKEN', req.headers.get('X_STORAGE_TOKEN')) - user = req.headers.get('X_USER_ID') or req.headers.get('X_USER') + user = req.headers.get('X_USER') tenant = req.headers.get('X_TENANT') roles = [r.strip() for r in req.headers.get('X_ROLE', '').split(',')] is_admin = 'Admin' in roles @@ -74,11 +69,10 @@ def filter_factory(global_conf, **local_conf): """ Factory method for paste.deploy """ - context_opts = context.cfg.ConfigOpts() conf = global_conf.copy() conf.update(local_conf) def filter(app): - return KeystoneContextMiddleware(app, context_opts, **conf) + return KeystoneContextMiddleware(app, conf) return filter diff --git a/keystone/middleware/nova_auth_token.py b/keystone/middleware/nova_auth_token.py new file mode 100644 index 0000000000..ad6bcbb810 --- /dev/null +++ b/keystone/middleware/nova_auth_token.py @@ -0,0 +1,104 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2010-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. + + +""" +NOVA LAZY PROVISIONING AUTH MIDDLEWARE + +This WSGI component allows keystone act as an identity service for nova by +lazy provisioning nova projects/users as authenticated by auth_token. + +Use by applying after auth_token in the nova paste config. +Example: docs/nova-api-paste.ini +""" + +from nova import auth +from nova import context +from nova import flags +from nova import utils +from nova import wsgi +from nova import exception +import webob.dec +import webob.exc + + +FLAGS = flags.FLAGS + + +class KeystoneAuthShim(wsgi.Middleware): + """Lazy provisioning nova project/users from keystone tenant/user""" + + def __init__(self, application, db_driver=None): + if not db_driver: + db_driver = FLAGS.db_driver + self.db = utils.import_object(db_driver) + self.auth = auth.manager.AuthManager() + super(KeystoneAuthShim, self).__init__(application) + + @webob.dec.wsgify(RequestClass=wsgi.Request) + def __call__(self, req): + # find or create user + try: + user_id = req.headers['X_USER'] + except: + return webob.exc.HTTPUnauthorized() + try: + user_ref = self.auth.get_user(user_id) + except: + user_ref = self.auth.create_user(user_id) + + # get the roles + roles = [r.strip() for r in req.headers.get('X_ROLE', '').split(',')] + + # set user admin-ness to keystone admin-ness + # FIXME: keystone-admin-role value from keystone.conf is not + # used neither here nor in glance_auth_token! + roles = [r.strip() for r in req.headers.get('X_ROLE', '').split(',')] + is_admin = 'Admin' in roles + if user_ref.is_admin() != is_admin: + self.auth.modify_user(user_ref, admin=is_admin) + + # create a project for tenant + if 'X_TENANT_ID' in req.headers: + # This is the new header since Keystone went to ID/Name + project_id = req.headers['X_TENANT_ID'] + else: + # This is for legacy compatibility + project_id = req.headers['X_TENANT'] + + if project_id: + try: + project_ref = self.auth.get_project(project_id) + except: + project_ref = self.auth.create_project(project_id, user_id) + # ensure user is a member of project + if not self.auth.is_project_member(user_id, project_id): + self.auth.add_to_project(user_id, project_id) + else: + project_ref = None + + # Get the auth token + auth_token = req.headers.get('X_AUTH_TOKEN', + req.headers.get('X_STORAGE_TOKEN')) + + # Build a context, including the auth_token... + ctx = context.RequestContext(user_id, project_id, + is_admin=('Admin' in roles), + auth_token=auth_token) + + req.environ['nova.context'] = ctx + return self.application diff --git a/keystone/middleware/nova_keystone_context.py b/keystone/middleware/nova_keystone_context.py new file mode 100644 index 0000000000..5c41bc87da --- /dev/null +++ b/keystone/middleware/nova_keystone_context.py @@ -0,0 +1,69 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 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. +""" +Nova Auth Middleware. + +""" + +import webob.dec +import webob.exc + +from nova import context +from nova import flags +from nova import wsgi + + +FLAGS = flags.FLAGS +flags.DECLARE('use_forwarded_for', 'nova.api.auth') + + +class NovaKeystoneContext(wsgi.Middleware): + """Make a request context from keystone headers""" + + @webob.dec.wsgify(RequestClass=wsgi.Request) + def __call__(self, req): + try: + user_id = req.headers['X_USER'] + except KeyError: + return webob.exc.HTTPUnauthorized() + # get the roles + roles = [r.strip() for r in req.headers.get('X_ROLE', '').split(',')] + + if 'X_TENANT_ID' in req.headers: + # This is the new header since Keystone went to ID/Name + project_id = req.headers['X_TENANT_ID'] + else: + # This is for legacy compatibility + project_id = req.headers['X_TENANT'] + + # Get the auth token + auth_token = req.headers.get('X_AUTH_TOKEN', + req.headers.get('X_STORAGE_TOKEN')) + + # Build a context, including the auth_token... + remote_address = getattr(req, 'remote_address', '127.0.0.1') + remote_address = req.remote_addr + if FLAGS.use_forwarded_for: + remote_address = req.headers.get('X-Forwarded-For', remote_address) + ctx = context.RequestContext(user_id, + project_id, + roles=roles, + auth_token=auth_token, + strategy='keystone', + remote_address=remote_address) + + req.environ['nova.context'] = ctx + return self.application diff --git a/keystone/middleware/quantum_auth_token.py b/keystone/middleware/quantum_auth_token.py deleted file mode 100755 index 6d027c4109..0000000000 --- a/keystone/middleware/quantum_auth_token.py +++ /dev/null @@ -1,461 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010-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. - - -""" -TOKEN-BASED AUTH MIDDLEWARE - -This WSGI component performs multiple jobs: - -- it verifies that incoming client requests have valid tokens by verifying - tokens with the auth service. -- it will reject unauthenticated requests UNLESS it is in 'delay_auth_decision' - mode, which means the final decision is delegated to the downstream WSGI - component (usually the OpenStack service) -- it will collect and forward identity information from a valid token - such as user name, groups, etc... - -Refer to: http://wiki.openstack.org/openstack-authn - -This WSGI component has been derived from Keystone's auth_token -middleware module. It contains some specialization for Quantum. - -HEADERS -======= - -Headers starting with ``HTTP_`` is a standard http header -Headers starting with ``HTTP_X`` is an extended http header - -Coming in from initial call from client or customer ---------------------------------------------------- - -HTTP_X_AUTH_TOKEN - The client token being passed in - -HTTP_X_STORAGE_TOKEN - The client token being passed in (legacy Rackspace use) to support - cloud files - -Used for communication between components ------------------------------------------ - -www-Authenticate - Only used if this component is being used remotely - -HTTP_AUTHORIZATION - Basic auth password used to validate the connection - -What we add to the request for use by the OpenStack service ------------------------------------------------------------ - -HTTP_X_AUTHORIZATION - The client identity being passed in - -""" - -import httplib -import json -import logging -import urllib -from urlparse import urlparse -from webob.exc import HTTPUnauthorized, Request, Response - -from keystone.common.bufferedhttp import http_connect_raw as http_connect - -PROTOCOL_NAME = "Quantum Token Authentication" -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -# pylint: disable=R0902 -class AuthProtocol(object): - """Auth Middleware that handles authenticating client calls""" - - def _init_protocol_common(self, app, conf): - """ Common initialization code""" - logger.info("Starting the %s component", PROTOCOL_NAME) - - self.conf = conf - self.app = app - #if app is set, then we are in a WSGI pipeline and requests get passed - # on to app. If it is not set, this component should forward requests - - # where to find the Quantum service (if not in local WSGI chain) - # these settings are only used if this component is acting as a proxy - # and the OpenSTack service is running remotely - if not self.app: - self.service_protocol = conf.get('quantum_protocol', 'https') - self.service_host = conf.get('quantum_host') - self.service_port = int(conf.get('quantum_port')) - self.service_url = '%s://%s:%s' % (self.service_protocol, - self.service_host, - self.service_port) - - # delay_auth_decision means we still allow unauthenticated requests - # through and we let the downstream service make the final decision - self.delay_auth_decision = int(conf.get('delay_auth_decision', 0)) - - def _init_protocol(self, _app, conf): - """ Protocol specific initialization """ - - # where to find the auth service (we use this to validate tokens) - self.auth_host = conf.get('auth_host') - self.auth_port = int(conf.get('auth_port')) - self.auth_protocol = conf.get('auth_protocol', 'http') - self.cert_file = conf.get('certfile', None) - self.key_file = conf.get('keyfile', None) - self.auth_timeout = conf.get('auth_timeout', 30) - self.auth_api_version = conf.get('auth_version', '2.0') - self.auth_location = "%s://%s:%s" % (self.auth_protocol, - self.auth_host, - self.auth_port) - self.auth_uri = conf.get('auth_uri', self.auth_location) - logger.debug("Authentication Service:%s", self.auth_location) - # Credentials used to verify this component with the Auth service - # since validating tokens is a privileged call - self.admin_user = conf.get('admin_user') - self.admin_password = conf.get('admin_password') - self.admin_token = conf.get('admin_token') - # bind to one or more service instances - service_ids = conf.get('service_ids') - self.serviceId_qs = '' - if service_ids: - self.serviceId_qs = '?HP-IDM-serviceId=%s' % \ - (urllib.quote(service_ids)) - - def _build_token_uri(self, claims=None): - claim_str = "/%s" % claims if claims else "" - return "/v%s/tokens%s%s" % (self.auth_api_version, claim_str, - self.serviceId_qs or '') - - def __init__(self, app, conf): - """ Common initialization code """ - # Defining instance variables here for improving pylint score - # NOTE(salvatore-orlando): the following vars are assigned values - # either in init_protocol or init_protocol_common. We should not - # worry about them being initialized to None - self.admin_password = None - self.admin_token = None - self.admin_user = None - self.auth_api_version = None - self.auth_host = None - self.auth_location = None - self.auth_uri = None - self.auth_port = None - self.auth_protocol = None - self.auth_timeout = None - self.cert_file = None - self.key_file = None - self.service_host = None - self.service_port = None - self.service_protocol = None - self.service_url = None - self.proxy_headers = None - self.start_response = None - self.app = None - self.conf = None - self.env = None - self.delay_auth_decision = None - self.expanded = None - self.claims = None - - self._init_protocol_common(app, conf) # Applies to all protocols - self._init_protocol(app, conf) # Specific to this protocol - - # pylint: disable=R0912 - def __call__(self, env, start_response): - """ Handle incoming request. Authenticate. And send downstream. """ - logger.debug("entering AuthProtocol.__call__") - logger.debug("start response:%s", start_response) - self.start_response = start_response - self.env = env - - #Prep headers to forward request to local or remote downstream service - self.proxy_headers = env.copy() - for header in self.proxy_headers.iterkeys(): - if header[0:5] == 'HTTP_': - self.proxy_headers[header[5:]] = self.proxy_headers[header] - del self.proxy_headers[header] - - #Look for authentication claims - logger.debug("Looking for authentication claims") - self.claims = self._get_claims(env) - if not self.claims: - #No claim(s) provided - logger.debug("No claims provided") - if self.delay_auth_decision: - #Configured to allow downstream service to make final decision. - #So mark status as Invalid and forward the request downstream - self._decorate_request("X_IDENTITY_STATUS", "Invalid") - else: - #Respond to client as appropriate for this auth protocol - return self._reject_request() - else: - # this request is presenting claims. Let's validate them - logger.debug("Claims found. Validating.") - valid = self._validate_claims(self.claims) - if not valid: - # Keystone rejected claim - if self.delay_auth_decision: - # Downstream service will receive call still and decide - self._decorate_request("X_IDENTITY_STATUS", "Invalid") - else: - #Respond to client as appropriate for this auth protocol - return self._reject_claims() - else: - self._decorate_request("X_IDENTITY_STATUS", "Confirmed") - - #Collect information about valid claims - if valid: - logger.debug("Validation successful") - claims = self._expound_claims() - - # Store authentication data - if claims: - # TODO(Ziad): add additional details we may need, - # like tenant and group info - self._decorate_request('X_AUTHORIZATION', "Proxy %s" % - claims['user']) - - self._decorate_request('X_TENANT_ID', - claims['tenant']['id'],) - self._decorate_request('X_TENANT_NAME', - claims['tenant']['name']) - - self._decorate_request('X_USER_ID', - claims['user']['id']) - self._decorate_request('X_USER_NAME', - claims['user']['name']) - - self._decorate_request('X_TENANT', claims['tenant']['id']) - self._decorate_request('X_USER', claims['user']['id']) - - if 'group' in claims: - self._decorate_request('X_GROUP', claims['group']) - if 'roles' in claims and len(claims['roles']) > 0: - if claims['roles'] is not None: - roles = '' - for role in claims['roles']: - if len(roles) > 0: - roles += ',' - roles += role - self._decorate_request('X_ROLE', roles) - - # NOTE(todd): unused - self.expanded = True - logger.debug("About to forward request") - #Send request downstream - return self._forward_request() - - # NOTE(salvatore-orlando): this function is now used again - def get_admin_auth_token(self, username, password): - """ - This function gets an admin auth token to be used by this service to - validate a user's token. Validate_token is a priviledged call so - it needs to be authenticated by a service that is calling it - """ - headers = { - "Content-type": "application/json", - "Accept": "application/json"} - params = { - "auth": - { - "passwordCredentials": - { - "username": username, - "password": password - } - } - } - if self.auth_protocol == "http": - conn = httplib.HTTPConnection(self.auth_host, self.auth_port) - else: - conn = httplib.HTTPSConnection(self.auth_host, self.auth_port, - cert_file=self.cert_file) - conn.request("POST", self._build_token_uri(), json.dumps(params), \ - headers=headers) - response = conn.getresponse() - data = response.read() - return data - - @staticmethod - def _get_claims(env): - """Get claims from request""" - claims = env.get('HTTP_X_AUTH_TOKEN', env.get('HTTP_X_STORAGE_TOKEN')) - return claims - - def _reject_request(self): - """Redirect client to auth server""" - return HTTPUnauthorized("Authentication required", - [("WWW-Authenticate", - "Keystone uri='%s'" % self.auth_uri)])(self.env, - self.start_response) - - def _reject_claims(self): - """Client sent bad claims""" - return HTTPUnauthorized()(self.env, self.start_response) - - def _validate_claims(self, claims, retry=False): - """Validate claims, and provide identity information if applicable """ - - # Step 1: We need to auth with the keystone service, so get an - # admin token - # TODO(ziad): Need to properly implement this, where to store creds - # for now using token from ini - # NOTE(salvatore-orlando): Temporarily restoring auth token retrieval, - # with credentials in configuration file - if not self.admin_token: - auth = self.get_admin_auth_token(self.admin_user, - self.admin_password) - self.admin_token = json.loads(auth)["access"]["token"]["id"] - - # Step 2: validate the user's token with the auth service - # since this is a priviledged op,m we need to auth ourselves - # by using an admin token - headers = {"Content-type": "application/json", - "Accept": "application/json", - "X-Auth-Token": self.admin_token} - conn = http_connect(self.auth_host, self.auth_port, 'GET', - self._build_token_uri(claims), headers=headers, - ssl=(self.auth_protocol == 'https'), - key_file=self.key_file, cert_file=self.cert_file, - timeout=self.auth_timeout) - resp = conn.getresponse() - # pylint: disable=E1103 - conn.close() - - if not str(resp.status).startswith('20'): - # Keystone rejected claim - # In case a 404 error it might just be that the token has expired - # Therefore try and get a new token - # of course assuming admin credentials have been specified - # Note(salvatore-orlando): the 404 here is not really - # what should be returned - if self.admin_user and self.admin_password and \ - not retry and str(resp.status) == '404': - logger.warn("Unable to validate token." + - "Admin token possibly expired.") - self.admin_token = None - return self._validate_claims(claims, True) - return False - else: - #TODO(Ziad): there is an optimization we can do here. We have just - #received data from Keystone that we can use instead of making - #another call in _expound_claims - logger.info("Claims successfully validated") - return True - - def _expound_claims(self): - # Valid token. Get user data and put it in to the call - # so the downstream service can use it - headers = {"Content-type": "application/json", - "Accept": "application/json", - "X-Auth-Token": self.admin_token} - conn = http_connect(self.auth_host, self.auth_port, 'GET', - self._build_token_uri(self.claims), - headers=headers, - ssl=(self.auth_protocol == 'https'), - key_file=self.key_file, cert_file=self.cert_file, - timeout=self.auth_timeout) - resp = conn.getresponse() - data = resp.read() - # pylint: disable=E1103 - conn.close() - - if not str(resp.status).startswith('20'): - raise LookupError('Unable to locate claims: %s' % resp.status) - - token_info = json.loads(data) - #TODO(Ziad): make this more robust - #first_group = token_info['auth']['user']['groups']['group'][0] - roles = [] - rolegrants = token_info["access"]["user"]["roles"] - if rolegrants is not None: - roles = [rolegrant["id"] for rolegrant in rolegrants] - - token_info = json.loads(data) - - roles = [role['name'] for role in token_info[ - "access"]["user"]["roles"]] - - # in diablo, there were two ways to get tenant data - tenant = token_info['access']['token'].get('tenant') - if tenant: - # post diablo - tenant_id = tenant['id'] - tenant_name = tenant['name'] - else: - # diablo only - tenant_id = token_info['access']['user'].get('tenantId') - tenant_name = token_info['access']['user'].get('tenantName') - - verified_claims = { - 'user': { - 'id': token_info['access']['user']['id'], - 'name': token_info['access']['user']['name'], - }, - 'tenant': { - 'id': tenant_id, - 'name': tenant_name - }, - 'roles': roles} - - return verified_claims - - def _decorate_request(self, index, value): - """Add headers to request""" - self.proxy_headers[index] = value - self.env["HTTP_%s" % index] = value - - def _forward_request(self): - """Token/Auth processed & claims added to headers""" - #now decide how to pass on the call - if self.app: - # Pass to downstream WSGI component - return self.app(self.env, self.start_response) - #.custom_start_response) - else: - # We are forwarding to a remote service (no downstream WSGI app) - req = Request(self.proxy_headers) - # pylint: disable=E1101 - parsed = urlparse(req.url) - conn = http_connect(self.service_host, - self.service_port, - req.method, - parsed.path, - self.proxy_headers, - ssl=(self.service_protocol == 'https')) - resp = conn.getresponse() - data = resp.read() - return Response(status=resp.status, body=data)(self.proxy_headers, - self.start_response) - - -def filter_factory(global_conf, **local_conf): - """Returns a WSGI filter app for use with paste.deploy.""" - conf = global_conf.copy() - conf.update(local_conf) - - def auth_filter(application): - return AuthProtocol(application, conf) - return auth_filter - - -def app_factory(global_conf, **local_conf): - conf = global_conf.copy() - conf.update(local_conf) - return AuthProtocol(None, conf) diff --git a/keystone/middleware/remoteauth.py b/keystone/middleware/remoteauth.py deleted file mode 100644 index 07ba70325f..0000000000 --- a/keystone/middleware/remoteauth.py +++ /dev/null @@ -1,128 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010 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. - - -""" -Auth Middleware that handles auth for a service - -This module can be installed as a filter in front of your service to validate -that requests are coming from a trusted component that has handled -authenticating the call. If a call comes from an untrusted source, it will -redirect it back to be properly authenticated. This is done by sending our a -305 proxy redirect response with the URL for the auth service. - -The auth service settings are specified in the INI file (keystone.ini). The ini -file is passed in as the WSGI config file when starting the service. For this -proof of concept, the ini file is in echo/echo/echo.ini. - -In the current implementation use a basic auth password to verify that the -request is coming from a valid auth component or service - -Refer to: http://wiki.openstack.org/openstack-authn - - -HEADERS -------- - -* HTTP\_ is a standard http header -* HTTP_X is an extended http header - -Coming in from initial call -^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -HTTP_X_AUTH_TOKEN - the client token being passed in - -HTTP_X_STORAGE_TOKEN - the client token being passed in (legacy Rackspace use) to support - cloud files - -Used for communication between components -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -www-authenticate - only used if this component is being used remotely - -HTTP_AUTHORIZATION - basic auth password used to validate the connection - -What we add to the request for use by the OpenStack service -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -HTTP_X_AUTHORIZATION - the client identity being passed in - - -""" -import logging -from webob.exc import HTTPUseProxy, HTTPUnauthorized - -PROTOCOL_NAME = "Remote Proxy Authentication" -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class RemoteAuth(object): - - # app is the downstream WSGI component, usually the OpenStack service - # - # if app is not provided, the assumption is this filter is being run - # from a separate server. - - def __init__(self, app, conf): - logger.info("Starting the %s component", PROTOCOL_NAME) - # app is the next app in WSGI chain - eventually the OpenStack service - self.app = app - self.conf = conf - # where to redirect untrusted requests to - self.proxy_location = conf.get('proxy_location') - # secret that will tell us a request is coming from a trusted auth - # component - self.remote_auth_pass = conf.get('remote_auth_pass') - - def __call__(self, env, start_response): - # Validate the request is trusted - # Authenticate the Auth component itself. - headers = [('www-authenticate', 'Basic realm="API Auth"')] - if 'HTTP_AUTHORIZATION' not in env: - logger.debug("Unauthenticated: redirecting to %s" % - self.proxy_location) - # Redirect to proxy (auth component) and show that basic auth is - # required - return HTTPUseProxy(location=self.proxy_location, - headers=headers)(env, start_response) - else: - auth_type, encoded_creds = env['HTTP_AUTHORIZATION'].split(None, 1) - if encoded_creds != self.remote_auth_pass: - logger.warn('Bad proxy credentials received') - return HTTPUnauthorized(headers=headers)(env, start_response) - - # Make sure that the user has been authenticated by the Auth Service - if 'HTTP_X_AUTHORIZATION' not in env: - return HTTPUnauthorized()(env, start_response) - - return self.app(env, start_response) - - -def filter_factory(global_conf, **local_conf): - """Returns a WSGI filter app for use with paste.deploy.""" - conf = global_conf.copy() - conf.update(local_conf) - - def auth_filter(app): - return RemoteAuth(app, conf) - return auth_filter diff --git a/keystone/middleware/s3_token.py b/keystone/middleware/s3_token.py index 31114b7c05..202e2c275a 100644 --- a/keystone/middleware/s3_token.py +++ b/keystone/middleware/s3_token.py @@ -20,33 +20,27 @@ # This source code is based ./auth_token.py and ./ec2_token.py. # See them for their copyright. -""" -Starting point for routing S3 requests. - -""" +"""Starting point for routing S3 requests.""" import httplib import json -from webob.dec import wsgify -from urlparse import urlparse + +import webob + +from swift.common import utils as swift_utils + PROTOCOL_NAME = "S3 Token Authentication" class S3Token(object): - """Auth Middleware that handles S3 authenticating client calls""" + """Auth Middleware that handles S3 authenticating client calls.""" - def _init_protocol_common(self, app, conf): - """ Common initialization code""" - print "Starting the %s component" % PROTOCOL_NAME - - self.conf = conf + def __init__(self, app, conf): + """Common initialization code.""" self.app = app - #if app is set, then we are in a WSGI pipeline and requests get passed - # on to app. If it is not set, this component should forward requests - - def _init_protocol(self, conf): - """ Protocol specific initialization """ + self.logger = swift_utils.get_logger(conf, log_route='s3_token') + self.logger.debug('Starting the %s component' % PROTOCOL_NAME) # where to find the auth service (we use this to validate tokens) self.auth_host = conf.get('auth_host') @@ -56,68 +50,38 @@ class S3Token(object): # where to tell clients to find the auth service (default to url # constructed based on endpoint we have for the service to use) self.auth_location = conf.get('auth_uri', - "%s://%s:%s" % (self.auth_protocol, - self.auth_host, - self.auth_port)) + '%s://%s:%s' % (self.auth_protocol, + self.auth_host, + self.auth_port)) # Credentials used to verify this component with the Auth service since # validating tokens is a privileged call self.admin_token = conf.get('admin_token') - def __init__(self, app, conf): - """ Common initialization code """ - - #TODO(ziad): maybe we refactor this into a superclass - self._init_protocol_common(app, conf) # Applies to all protocols - self._init_protocol(conf) # Specific to this protocol - self.app = None - self.auth_port = None - self.auth_protocol = None - self.auth_location = None - self.auth_host = None - self.admin_token = None - self.conf = None - - #@webob.dec.wsgify(RequestClass=webob.exc.Request) - # pylint: disable=R0914 - @wsgify - def __call__(self, req): - """ Handle incoming request. Authenticate. And send downstream. """ + def __call__(self, environ, start_response): + """Handle incoming request. authenticate and send downstream.""" + req = webob.Request(environ) + parts = swift_utils.split_path(req.path, 1, 4, True) + version, account, container, obj = parts # Read request signature and access id. if not 'Authorization' in req.headers: - return self.app - try: - account, signature = \ - req.headers['Authorization'].split(' ')[-1].rsplit(':', 1) - #del(req.headers['Authorization']) - except StandardError: - return self.app + return self.app(environ, start_response) + token = req.headers.get('X-Auth-Token', + req.headers.get('X-Storage-Token')) - #try: - # account, tenant = access.split(':') - #except Exception: - # account = access + auth_header = req.headers['Authorization'] + access, signature = auth_header.split(' ')[-1].rsplit(':', 1) # Authenticate the request. - creds = {'OS-KSS3:s3Credentials': {'access': account, - 'signature': signature, - 'verb': req.method, - 'path': req.path, - 'expire': req.headers['Date'], - }} - - if req.headers.get('Content-Type'): - creds['s3Credentials']['content_type'] = \ - req.headers['Content-Type'] - if req.headers.get('Content-MD5'): - creds['s3Credentials']['content_md5'] = req.headers['Content-MD5'] - xheaders = {} - for key, value in req.headers.iteritems(): - if key.startswith('X-Amz'): - xheaders[key.lower()] = value - if xheaders: - creds['s3Credentials']['xheaders'] = xheaders + creds = {'credentials': {'access': access, + 'token': token, + 'signature': signature, + 'host': req.host, + 'verb': req.method, + 'path': req.path, + 'expire': req.headers['Date'], + }} creds_json = json.dumps(creds) headers = {'Content-Type': 'application/json'} @@ -126,29 +90,40 @@ class S3Token(object): else: conn = httplib.HTTPSConnection(self.auth_host, self.auth_port) - conn.request('POST', '/v2.0/tokens', body=creds_json, headers=headers) - response = conn.getresponse().read() + conn.request('POST', '/v2.0/s3tokens', + body=creds_json, + headers=headers) + resp = conn.getresponse() + if resp.status < 200 or resp.status >= 300: + raise Exception('Keystone reply error: status=%s reason=%s' % ( + resp.status, + resp.reason)) + + # NOTE(vish): We could save a call to keystone by having + # keystone return token, tenant, user, and roles + # from this call. + # + # NOTE(chmou): We still have the same problem we would need to + # change token_auth to detect if we already + # identified and not doing a second query and just + # pass it through to swiftauth in this case. + # identity_info = json.loads(response) + output = resp.read() conn.close() - - # NOTE(vish): We could save a call to keystone by - # having keystone return token, tenant, - # user, and roles from this call. - result = json.loads(response) - endpoint_path = '' + identity_info = json.loads(output) try: - token_id = str(result['access']['token']['id']) - for endpoint in result['access']['serviceCatalog']: - if endpoint['type'] == 'Swift Service': - ep = urlparse(endpoint['endpoints'][0]['internalURL']) - endpoint_path = str(ep.path) # pylint: disable=E1101 - break - except KeyError: - return self.app + token_id = str(identity_info['access']['token']['id']) + tenant = (identity_info['access']['token']['tenant']['id'], + identity_info['access']['token']['tenant']['name']) + except (KeyError, IndexError): + self.logger.debug('Error getting keystone reply: %s' % + (str(output))) + raise - # Authenticated! req.headers['X-Auth-Token'] = token_id - req.headers['X-Endpoint-Path'] = endpoint_path - return self.app + environ['PATH_INFO'] = environ['PATH_INFO'].replace( + account, 'AUTH_%s' % tenant[0]) + return self.app(environ, start_response) def filter_factory(global_conf, **local_conf): diff --git a/keystone/middleware/swift_auth.py b/keystone/middleware/swift_auth.py index 56fa0e0904..d12ac162fc 100644 --- a/keystone/middleware/swift_auth.py +++ b/keystone/middleware/swift_auth.py @@ -1,4 +1,6 @@ -# Copyright (c) 2011 OpenStack, LLC. +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2012 OpenStack, LLC. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,17 +15,16 @@ # See the License for the specific language governing permissions and # limitations under the License. -from webob.exc import HTTPForbidden, HTTPNotFound, HTTPUnauthorized +import webob -from swift.common.utils import get_logger, split_path, get_remote_client -from swift.common.middleware.acl import clean_acl, parse_acl, referrer_allowed +from swift.common import utils as swift_utils +from swift.common.middleware import acl as swift_acl class SwiftAuth(object): - """ - Keystone to Swift authorization system. + """Swift middleware to Keystone authorization system. - Add to your pipeline in proxy-server.conf, such as:: + In Swift's proxy-server.conf add the middleware to your pipeline:: [pipeline:main] pipeline = catch_errors cache tokenauth swiftauth proxy-server @@ -37,8 +38,8 @@ class SwiftAuth(object): [filter:swiftauth] use = egg:keystone#swiftauth - keystone_swift_operator_roles = Admin, SwiftOperator - keystone_tenant_user_admin = true + operator_roles = admin, SwiftOperator + is_admin = true If Swift memcache is to be used for caching tokens, add the additional property in the tokenauth filter: @@ -51,11 +52,11 @@ class SwiftAuth(object): This maps tenants to account in Swift. The user whose able to give ACL / create Containers permissions - will be the one that are inside the keystone_swift_operator_roles + will be the one that are inside the operator_roles setting which by default includes the Admin and the SwiftOperator roles. - The option keystone_tenant_user_admin if set to true will allow the + The option is_admin if set to true will allow the username that has the same name as the account name to be the owner. Example: If we have the account called hellocorp with a user @@ -68,23 +69,20 @@ class SwiftAuth(object): def __init__(self, app, conf): self.app = app self.conf = conf - self.logger = get_logger(conf, log_route='keystone') + self.logger = swift_utils.get_logger(conf, log_route='keystoneauth') self.reseller_prefix = conf.get('reseller_prefix', 'AUTH').strip() - self.keystone_swift_operator_roles = \ - conf.get('keystone_swift_operator_roles', 'Admin, SwiftOperator') - self.keystone_tenant_user_admin = \ - conf.get('keystone_tenant_user_admin', "false").lower() in \ - ('true', 't', '1', 'on', 'yes', 'y') - self.allowed_sync_hosts = [h.strip() - for h in conf.get('allowed_sync_hosts', '127.0.0.1').split(',') - if h.strip()] + self.operator_roles = conf.get('operator_roles', + 'admin, SwiftOperator') + config_is_admin = conf.get('is_admin', "false").lower() + self.is_admin = config_is_admin in ('true', 't', '1', 'on', 'yes', 'y') + cfg_synchosts = conf.get('allowed_sync_hosts', '127.0.0.1') + self.allowed_sync_hosts = [h.strip() for h in cfg_synchosts.split(',') + if h.strip()] def __call__(self, environ, start_response): - self.logger.debug('Initialise keystone middleware') identity = self._keystone_identity(environ) if not identity: - #TODO: non authenticated access allow via refer environ['swift.authorize'] = self.denied_response return self.app(environ, start_response) @@ -92,24 +90,24 @@ class SwiftAuth(object): environ['keystone.identity'] = identity environ['REMOTE_USER'] = identity.get('tenant') environ['swift.authorize'] = self.authorize - environ['swift.clean_acl'] = clean_acl + environ['swift.clean_acl'] = swift_acl.clean_acl return self.app(environ, start_response) def _keystone_identity(self, environ): - """ Extract the identity from the Keystone auth component """ - if (environ.get('HTTP_X_IDENTITY_STATUS') != 'Confirmed'): - return None + """Extract the identity from the Keystone auth component.""" + if environ.get('HTTP_X_IDENTITY_STATUS') != 'Confirmed': + return roles = [] - if ('HTTP_X_ROLES' in environ): - roles = environ.get('HTTP_X_ROLES').split(',') - identity = {'user': environ.get('HTTP_X_USER_NAME'), + if 'HTTP_X_ROLE' in environ: + roles = environ['HTTP_X_ROLE'].split(',') + identity = {'user': environ.get('HTTP_X_USER'), 'tenant': (environ.get('HTTP_X_TENANT_ID'), environ.get('HTTP_X_TENANT_NAME')), 'roles': roles} return identity def _reseller_check(self, account, tenant_id): - """ Check reseller prefix """ + """Check reseller prefix.""" return account == '%s_%s' % (self.reseller_prefix, tenant_id) def authorize(self, req): @@ -118,77 +116,82 @@ class SwiftAuth(object): tenant = env_identity.get('tenant') try: - version, account, container, obj = split_path(req.path, 1, 4, True) + part = swift_utils.split_path(req.path, 1, 4, True) + version, account, container, obj = part except ValueError: - return HTTPNotFound(request=req) + return webob.exc.HTTPNotFound(request=req) if not self._reseller_check(account, tenant[0]): - self.logger.debug('tenant mismatch') + log_msg = 'tenant mismatch: %s != %s' % (account, tenant[0]) + self.logger.debug(log_msg) return self.denied_response(req) user_groups = env_identity.get('roles', []) - # If user is in the swift operator group then make the owner of it. - for _group in self.keystone_swift_operator_roles.split(','): - _group = _group.strip() - if _group in user_groups: - self.logger.debug( - "User in group: %s allow to manage this account" % \ - (_group)) + # Check the groups the user is belonging to. If the user is + # part of the group defined in the config variable + # operator_roles (like Admin) then it will be + # promoted as an Admin of the account/tenant. + for group in self.operator_roles.split(','): + group = group.strip() + if group in user_groups: + log_msg = "allow user in group %s as account admin" % group + self.logger.debug(log_msg) req.environ['swift_owner'] = True - return None + return # If user is of the same name of the tenant then make owner of it. user = env_identity.get('user', '') - if self.keystone_tenant_user_admin and user == tenant[1]: - self.logger.debug("user: %s == %s tenant and option "\ - "keystone_tenant_user_admin is set" % \ - (user, tenant)) + if self.is_admin and user == tenant[1]: req.environ['swift_owner'] = True - return None + return - # Allow container sync - if (req.environ.get('swift_sync_key') and - req.environ['swift_sync_key'] == - req.headers.get('x-container-sync-key', None) and - 'x-timestamp' in req.headers and - (req.remote_addr in self.allowed_sync_hosts or - get_remote_client(req) in self.allowed_sync_hosts)): - self.logger.debug('allowing container-sync') - return None + # Allow container sync. + if (req.environ.get('swift_sync_key') + and req.environ['swift_sync_key'] == + req.headers.get('x-container-sync-key', None) + and 'x-timestamp' in req.headers + and (req.remote_addr in self.allowed_sync_hosts + or swift_utils.get_remote_client(req) + in self.allowed_sync_hosts)): + log_msg = 'allowing proxy %s for container-sync' % req.remote_addr + self.logger.debug(log_msg) + return - # Check if Referrer allow it - referrers, groups = parse_acl(getattr(req, 'acl', None)) - if referrer_allowed(req.referer, referrers): + # Check if referrer is allowed. + referrers, groups = swift_acl.parse_acl(getattr(req, 'acl', None)) + if swift_acl.referrer_allowed(req.referer, referrers): if obj or '.rlistings' in groups: - self.logger.debug('authorizing via ACL') - return None + log_msg = 'authorizing %s via referer ACL' % req.referrer + self.logger.debug(log_msg) + return return self.denied_response(req) # Allow ACL at individual user level (tenant:user format) if '%s:%s' % (tenant[0], user) in groups: - self.logger.debug('user explicitly allowed in ACL authorizing') - return None + log_msg = 'user %s:%s allowed in ACL authorizing' + self.logger.debug(log_msg % (tenant[0], user)) + return # Check if we have the group in the usergroups and allow it for user_group in user_groups: if user_group in groups: - self.logger.debug('user in group which is allowed in' \ - ' ACL: %s authorizing' % (user_group)) - return None + log_msg = 'user %s:%s allowed in ACL: %s authorizing' + self.logger.debug(log_msg % (tenant[0], user, user_group)) + return - # last but not least retun deny return self.denied_response(req) def denied_response(self, req): - """ + """Deny WSGI Response. + Returns a standard WSGI response callable with the status of 403 or 401 depending on whether the REMOTE_USER is set or not. """ if req.remote_user: - return HTTPForbidden(request=req) + return webob.exc.HTTPForbidden(request=req) else: - return HTTPUnauthorized(request=req) + return webob.exc.HTTPUnauthorized(request=req) def filter_factory(global_conf, **local_conf): diff --git a/keystone/middleware/url.py b/keystone/middleware/url.py deleted file mode 100644 index d75313a1a0..0000000000 --- a/keystone/middleware/url.py +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010 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. - -""" -DEPRECATED - moved to keystone.frontends.normalizer - -This file only exists to maintain compatibility with configuration files -that load keystone.middleware.url -""" - -import logging - -from keystone.frontends.normalizer import filter_factory\ - as new_filter_factory - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -def filter_factory(global_conf, **local_conf): - """Returns a WSGI filter app for use with paste.deploy. - - In this case, we return the class that has been moved""" - logger.warning("'%s' has been moved to 'keystone.frontends.normalizer'. " - "Update your configuration file to reflect the change" % - __name__) - return new_filter_factory(global_conf, **local_conf) diff --git a/keystone/models.py b/keystone/models.py deleted file mode 100644 index 0f8006ceb4..0000000000 --- a/keystone/models.py +++ /dev/null @@ -1,813 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (C) 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. - -""" Module that contains all object models - -The models are used to hold Keystone 'business' objects and their validation, -serialization, and backend interaction code. - -The models are based off of python's dict. - -The uses supported are: - # can be initialized with static properties - tenant = Tenant(name='A1000') - - # handles writing to correct backend - tenant.save() - - # static properties - id = tenant.id - tenant = None - - # Acts as a dict - tenant is a dict - tenant.dict points to the data dict (i.e. tenant["tenant"]) - - # can be retrieved by static property - tenant_by_name = Tenant.get(name='A1000') - - # can be retrieved by id default, so name not needed - tenant_by_id = Tenant.get(id) - assertIsEquals(tenant_by_id, tenant_by_name) - - # handles serialization - print tenant_by_id - print tenant_by_id.to_json() # Keystone latest contract - print tenant_by_id.to_json_20() # Keystone 2.0 contract - - Serialization routines can take hints in this format: - { - "contract_attributes": ["id", "name", ...], - "types": [("id", int), (...)] - } - attribute/value can be: - contract_attributes: list of contract attributes (see initializer) - format is a list of attributes names (ex ['id', 'name']) - types: list of attribute/type mappings - format is a list of name/type tuples (ex [('id', int)]) - tags: list of attributes that go into XML tags - format is a list of attribute names(ex ['description'] - maps: list of attributes to rename - format is from/to values (ex {'serviceId": "service_id",}) - Default hints can be stored in the class as cls.hints -""" - -import json -from lxml import etree - -from keystone import utils -from keystone.utils import fault - - -class AttrDict(dict): - """Lets us do setattr and getattr since dict does not allow it""" - pass - - -class Resource(AttrDict): - """ Base class for models - - Provides basic functionality that can be overridden """ - - hints = {} - xmlns = None - - def __init__(self, *args, **kw): - """ Initialize object - kwargs contain static properties - """ - super(Resource, self).__init__(*args, **kw) - # attributes that can be used as attributes. Example: - # tenant.id - here id is a contract attribute - super(Resource, self).__setattr__("contract_attributes", []) - if kw: - self.contract_attributes.extend(kw.keys()) - for name, value in kw.iteritems(): - self[name] = value - - # - # model properties - # - # Override built-in classes to allow for user.id (as well as user["id"]) - # for attributes defined in the Keystone contract - # - def __repr__(self): - return "<%s(%s)>" % (self.__class__.__name__, ', '.join(['%s=%s' % - (attrib, self[attrib].__repr__()) for attrib in - self.contract_attributes])) - - def __str__(self): - """Returns string representation including the class name.""" - return str(self.to_dict()) - - def __getattr__(self, name): - """ Supports reading contract attributes (ex. tenant.id) - - This should only be called if the original call did not match - an attribute (Python's rules)""" - if name in self.contract_attributes: - if name in self: - return self[name] - return None - elif name == 'desc': # TODO(zns): deprecate this - # We need to maintain this compatibility with this nasty attribute - # until we're done refactoring - return self.description - else: - if hasattr(super(Resource, self), name): - return getattr(super(Resource, self), name) - else: - raise AttributeError("'%s' not found on object of class '%s'" - % (name, self.__class__.__name__)) - - def __setattr__(self, name, value): - """ Supports setting contract attributes (ex. tenant.name = 'A1') """ - - if name in self.contract_attributes: - # Put those into the dict (and not as attrs) - if value is not None: - self[name] = value - else: - super(Resource, self).__setattr__(name, value) - - def __getitem__(self, name): - if name in self.contract_attributes: - if super(Resource, self).__contains__(name): - return super(Resource, self).__getitem__(name) - return None - elif name == 'desc': # TODO(zns): deprecate this - # We need to maintain this compatibility with this nasty attribute - # until we're done refactoring - return self.description - elif name == self.__class__.__name__.lower(): - # Supports using dict syntax to access the attributes of the - # class. Ex: Resource(id=1)['resource']['id'] - return self - else: - return super(Resource, self).__getitem__(name) - - def __contains__(self, key): - if key in self.contract_attributes: - return True - return super(Resource, self).__contains__(key) - - # - # Serialization Functions - may be moved to a different class - # - def to_dict(self, model_name=None, hints=None): - """ For compatibility with logic.types """ - if model_name is None: - model_name = self.__class__.__name__.lower() - result = self.strip_null_fields(self.copy()) - if hints is None: - hints = self.hints - if hints: - if "types" in hints: - Resource.apply_type_mappings( - result, - hints["types"]) - if "maps" in hints: - Resource.apply_name_mappings( - result, - hints["maps"]) - return {model_name: result} - - def to_json(self, hints=None, model_name=None): - """ Serializes object to json - implies latest Keystone contract """ - d = self.to_dict(model_name=model_name) - if hints is None: - hints = self.hints - if hints: - if "types" in hints: - Resource.apply_type_mappings( - d[model_name or self.__class__.__name__.lower()], - hints["types"]) - if "maps" in hints: - Resource.apply_name_mappings( - d[model_name or self.__class__.__name__.lower()], - hints["maps"]) - return json.dumps(d) - - def to_xml(self, hints=None, model_name=None): - """ Serializes object to XML string - - implies latest Keystone contract - :param hints: see introduction on format""" - if hints is None: - hints = self.hints - dom = self.to_dom(hints=hints, model_name=model_name) - return etree.tostring(dom) - - def to_dom(self, xmlns=None, hints=None, model_name=None): - """ Serializes object to XML objec - - implies latest Keystone contract - :param xmlns: accepts an optional namespace for XML - :param tags: accepts a list of attribute names that should go into XML - tags instead of attributes - """ - if xmlns is None: - xmlns = self.xmlns - if hints is None: - hints = self.hints - if model_name is None: - model_name = self.__class__.__name__.lower() - if xmlns: - dom = etree.Element(model_name, xmlns=xmlns) - else: - dom = etree.Element(model_name) - Resource.write_dict_to_xml(self, dom, hints) - return dom - - # - # Deserialization functions - # - @classmethod - def from_json(cls, json_str, hints=None, model_name=None): - """ Deserializes object from json - assumes latest Keystone - contract - """ - if hints is None: - hints = cls.hints - try: - obj = json.loads(json_str) - if model_name is None: - model_name = cls.__name__.lower() - if model_name in obj: - # Ignore class name if it is there - obj = obj[model_name] - if hints and ('maps' in hints): - name_mappings = hints['maps'] - if name_mappings: - Resource.reverse_name_mappings(obj, name_mappings) - model_object = None - if hints: - if 'contract_attributes' in hints: - # build mapping and instantiate object with - # contract_attributes provided - params = {} - for name in hints['contract_attributes']: - if name in obj: - params[name] = obj[name] - else: - params[name] = None - model_object = cls(**params) - if model_object is None: - model_object = cls() - model_object.update(obj) - - if hints and ('types' in hints): - type_mappings = hints['types'] - Resource.apply_type_mappings(model_object, type_mappings) - return model_object - except (ValueError, TypeError) as e: - raise fault.BadRequestFault("Cannot parse '%s' json" % \ - cls.__name__, str(e)) - - @classmethod - def from_xml(cls, xml_str, hints=None): - """ Deserializes object from XML - assumes latest Keystone - contract """ - if hints is None: - hints = cls.hints - try: - object = etree.fromstring(xml_str) - model_object = None - type_mappings = None - name_mappings = None - if hints: - if 'types' in hints: - type_mappings = hints['types'] - if 'contract_attributes' in hints: - # build mapping and instantiate object with - # contract_attributes provided - params = {} - for name in hints['contract_attributes']: - params[name] = object.get(name, None) - model_object = cls(**params) - if 'maps' in hints: - name_mappings = hints['maps'] - if model_object is None: - model_object = cls() - cls.write_xml_to_dict(object, model_object) - if type_mappings: - Resource.apply_type_mappings(model_object, type_mappings) - if name_mappings: - Resource.reverse_name_mappings(model_object, name_mappings) - return model_object - except etree.LxmlError as e: - raise fault.BadRequestFault("Cannot parse '%s' xml" % cls.__name__, - str(e)) - - # - # Validation calls - # - def validate(self): - """ Validates object attributes. Raises error if object not valid - - This calls inspect() in fail_fast mode, so it gets back the first - validation error and raises it. It is up to the code in inspect() - to determine what validations take precedence and are returned - first - - :returns: True if no validation errors raise""" - errors = self.inspect(fail_fast=True) - if errors: - raise errors[0][0](errors[0][1]) - return True - - # pylint: disable=W0613, R0201 - def inspect(self, fail_fast=None): - """ Validates and retuns validation results without raising any errors - :param fail_fast" return after first validation failure - - :returns: [(faultClass, message), ...], ordered by relevance - - if None, then no errors found - """ - return [] - - # - # Formatting, hint processing functions - # - - @staticmethod - def strip_null_fields(dict_object): - """ Strips null fields from dict""" - for k, v in dict_object.items(): - if v is None: - del dict_object[k] - return dict_object - - @staticmethod - def write_dict_to_xml(dict_object, xml, hints=None): - """ Attempts to convert a dict into XML as best as possible. - Converts named keys and attributes and recursively calls for - any values are are embedded dicts - - :param hints: handles tags (a list of attribute names that should go - into XML tags instead of attributes) and maps - """ - tags = [] - rename = [] - maps = {} - if hints is not None: - if 'tags' in hints: - tags = hints['tags'] - if 'maps' in hints: - maps = hints['maps'] - rename = maps.values() - for name, value in dict_object.iteritems(): - if name in rename: - name = maps.keys()[rename.index(name)] - if isinstance(value, dict): - element = etree.SubElement(xml, name) - Resource.write_dict_to_xml(value, element) - elif name in tags: - element = xml.find(name) - if isinstance(value, dict): - if element is None: - element = etree.SubElement(xml, name) - Resource.write_dict_to_xml(value, element) - else: - if value is not None: - if element is None: - element = etree.SubElement(xml, name) - element.text = str(value) - else: - if value is not None: - if isinstance(value, dict): - Resource.write_dict_to_xml(value, xml) - elif isinstance(value, bool): - xml.set(name, str(value).lower()) - else: - xml.set(name, str(value)) - else: - if name in xml: - del xml.attrib[name] - - @staticmethod - def write_xml_to_dict(xml, dict_object): - """ Attempts to update a dict with XML as best as possible.""" - for key, value in xml.items(): - dict_object[key] = value - for element in xml.iterdescendants(): - name = element.tag - if "}" in name: - #trim away namespace if it is there - name = name[name.rfind("}") + 1:] - if element.attrib == {}: - dict_object[name] = element.text - else: - dict_object[name] = {} - Resource.write_xml_to_dict(element, dict_object[element.tag]) - - @staticmethod - def apply_type_mappings(target, type_mappings): - """Applies type mappings to dict values""" - if type_mappings: - for name, type in type_mappings: - if type is int: - target[name] = int(target[name]) - elif issubclass(type, basestring): - # Move sub to string - if name in target: - value = target[name] - if isinstance(value, dict): - value = value[0] - if value: - target[name] = str(value) - elif type is bool: - target[name] = str(target[name]).lower() not in ['0', - 'false'] - else: - raise NotImplementedError("Model type mappings cannot \ - handle '%s' types" % type.__name__) - - @staticmethod - def apply_name_mappings(target, name_mappings): - """ Applies name mappings to dict values """ - if name_mappings: - for outside, inside in name_mappings.iteritems(): - if inside in target: - target[outside] = target.pop(inside) - - @staticmethod - def reverse_name_mappings(target, name_mappings): - """ Extracts names from mappings to dict values """ - if name_mappings: - for outside, inside in name_mappings.iteritems(): - if outside in target: - target[inside] = target.pop(outside) - - # - # Backend management - # - def save(self): - """ Handles finding correct backend and writing to it - Supports both saving new object (create) and updating an existing one - """ - #if self.status == 'new': - # #backends[find the class].create(self) - #elif self.status == 'existing': - # #backends[find the class].update(self) - pass - - def delete(self): - """ Handles finding correct backend and deleting object from it """ - pass - - @classmethod - def get(cls, id=None, *args, **kw): - # backends[find the class].get(id, *args, **kw) - return cls(*args, **kw) - - -class Service(Resource): - """ Service model """ - def __init__(self, id=None, name=None, type=None, description=None, - owner_id=None, *args, **kw): - super(Service, self).__init__(id=id, name=name, type=type, - description=description, - owner_id=owner_id, *args, **kw) - # pylint: disable=E0203 - if isinstance(self.id, int): - self.id = str(self.id) - - def to_dict(self, model_name=None): - if model_name is None: - model_name = 'OS-KSADM:service' - return super(Service, self).to_dict(model_name=model_name) - - def to_dom(self, xmlns=None, hints=None, model_name=None): - if xmlns is None: - xmlns = "http://docs.openstack.org/identity/api/ext/OS-KSADM/v1.0" - return super(Service, self).to_dom(xmlns, hints=hints, - model_name=model_name) - - @classmethod - def from_json(cls, json_str, hints=None, model_name=None): - if model_name is None: - model_name = 'OS-KSADM:service' - result = super(Service, cls).from_json(json_str, hints=hints, - model_name=model_name) - result.validate() # TODO(zns): remove; compatibility with logic.types - return result - - @classmethod - def from_xml(cls, xml_str, hints=None): - result = super(Service, cls).from_xml(xml_str, hints=hints) - result.validate() # TODO(zns): remove; compatibility with logic.types - return result - - def inspect(self, fail_fast=None): - result = super(Service, self).inspect(fail_fast) - if fail_fast and result: - return result - - # Check that fields are valid - invalid = [key for key in result if key not in - ['id', 'name', 'type', 'description', 'owner_id']] - if invalid: - result.append((fault.BadRequestFault, "Invalid attribute(s): %s" - % invalid)) - if fail_fast: - return result - - if utils.is_empty_string(self.name): - result.append((fault.BadRequestFault, "Expecting Service Name")) - if fail_fast: - return result - - if utils.is_empty_string(self.type): - result.append((fault.BadRequestFault, "Expecting Service Type")) - if fail_fast: - return result - return result - - -class Services(object): - "A collection of services." - - def __init__(self, values, links): - self.values = values - self.links = links - - def to_xml(self, model_name=None): - dom = etree.Element("services") - dom.set(u"xmlns", - "http://docs.openstack.org/identity/api/ext/OS-KSADM/v1.0") - - for t in self.values: - dom.append(t.to_dom(model_name=model_name)) - - for t in self.links: - dom.append(t.to_dom(model_name=model_name)) - - return etree.tostring(dom) - - def to_json(self, model_name=None): - services = [t.to_dict()["OS-KSADM:service"] - for t in self.values] - services_links = [t.to_dict()["links"] - for t in self.links] - return json.dumps({"OS-KSADM:services": services, - "OS-KSADM:services_links": services_links}) - - -class Tenant(Resource): - """ Tenant model """ - # pylint: disable=E0203,C0103 - def __init__(self, id=None, name=None, description=None, enabled=None, - *args, **kw): - super(Tenant, self).__init__(id=id, name=name, - description=description, enabled=enabled, - *args, **kw) - if isinstance(self.id, int): - self.id = str(self.id) - if isinstance(self.enabled, basestring): - self.enabled = self.enabled.lower() == 'true' - - @classmethod - def from_xml(cls, xml_str, hints=None): - if hints is None: - hints = {} - if 'contract_attributes' not in hints: - hints['contract_attributes'] = ['id', 'name', 'description', - 'enabled'] - if 'tags' not in hints: - hints['tags'] = ["description"] - return super(Tenant, cls).from_xml(xml_str, hints=hints) - - def to_dom(self, xmlns=None, hints=None, model_name=None): - if hints is None: - hints = {} - if 'tags' not in hints: - hints['tags'] = ["description"] - if xmlns is None: - xmlns = "http://docs.openstack.org/identity/api/v2.0" - - return super(Tenant, self).to_dom(xmlns=xmlns, hints=hints, - model_name=model_name) - - def to_xml(self, hints=None, model_name=None): - if hints is None: - hints = {} - if 'tags' not in hints: - hints['tags'] = ["description"] - return super(Tenant, self).to_xml(hints=hints, model_name=model_name) - - def to_json(self, hints=None, model_name=None): - if hints is None: - hints = {} - return super(Tenant, self).to_json(hints=hints, model_name=model_name) - - -class User(Resource): - """ User model - - Attribute Notes: - default_tenant_id (formerly tenant_id): this attribute can be enabled or - disabled by configuration. When enabled, any authentication call - without a tenant gets authenticated to this tenant. - """ - # pylint: disable=R0913 - def __init__(self, id=None, password=None, name=None, - tenant_id=None, - email=None, enabled=None, - *args, **kw): - super(User, self).__init__(id=id, password=password, name=name, - tenant_id=tenant_id, email=email, - enabled=enabled, *args, **kw) - - def to_json(self, hints=None, model_name=None): - results = super(User, self).to_json(hints, model_name=model_name) - assert 'password":' not in results - return results - - def to_xml(self, hints=None): - results = super(User, self).to_xml(hints) - assert 'password"=' not in results - return results - - -class EndpointTemplate(Resource): - """ EndpointTemplate model """ - # pylint: disable=R0913 - def __init__(self, id=None, region=None, service_id=None, public_url=None, - admin_url=None, internal_url=None, enabled=None, is_global=None, - version_id=None, version_list=None, version_info=None, *args, - **kw): - super(EndpointTemplate, self).__init__(id=id, region=region, - service_id=service_id, public_url=public_url, - admin_url=admin_url, internal_url=internal_url, - enabled=enabled, is_global=is_global, version_id=version_id, - version_list=version_list, version_info=version_info, *args, - **kw) - - -class Endpoint(Resource): - """ Endpoint model """ - # pylint: disable=R0913 - def __init__(self, id=None, endpoint_template_id=None, tenant_id=None, - *args, **kw): - super(Endpoint, self).__init__(id=id, tenant_id=tenant_id, - endpoint_template_id=endpoint_template_id, *args, **kw) - - -class Role(Resource): - """ Role model """ - hints = {"maps": - {"userId": "user_id", - "roleId": "role_id", - "serviceId": "service_id", - "tenantId": "tenant_id"}, - "contract_attributes": ['id', 'name', 'service_id', - 'tenant_id', 'description'], - "types": [('id', basestring), ('service_id', basestring)], - } - xmlns = "http://docs.openstack.org/identity/api/v2.0" - - def __init__(self, id=None, name=None, description=None, service_id=None, - tenant_id=None, *args, **kw): - super(Role, self).__init__(id=id, name=name, - description=description, - service_id=service_id, - tenant_id=tenant_id, - *args, **kw) - # pylint: disable=E0203 - if isinstance(self.id, int): - self.id = str(self.id) - # pylint: disable=E0203 - if isinstance(self.service_id, int): - self.service_id = str(self.service_id) - - @classmethod - def from_json(cls, json_str, hints=None, model_name=None): - # Check that fields are valid - role = json.loads(json_str) - if model_name is None: - model_name = "role" - if model_name in role: - role = role[model_name] - - invalid = [key for key in role if key not in - ['id', 'name', 'description', 'serviceId', - # TODO(zns): remove those when we separate grants - # from Roles - 'tenantId', 'userId']] - if invalid: - raise fault.BadRequestFault("Invalid attribute(s): %s" - % invalid) - - return super(Role, cls).from_json(json_str, hints=hints, - model_name=model_name) - - -class Roles(object): - "A collection of roles." - - def __init__(self, values, links): - self.values = values - self.links = links - - def to_xml(self): - dom = etree.Element("roles") - dom.set(u"xmlns", "http://docs.openstack.org/identity/api/v2.0") - - for t in self.values: - dom.append(t.to_dom()) - - for t in self.links: - dom.append(t.to_dom()) - - return etree.tostring(dom) - - def to_dom(self): - dom = etree.Element("roles") - dom.set(u"xmlns", "http://docs.openstack.org/identity/api/v2.0") - - if self.values: - for t in self.values: - dom.append(t.to_dom()) - - if self.links: - for t in self.links: - dom.append(t.to_dom()) - - return dom - - def to_json(self): - values = [t.to_dict()["role"] - for t in self.values] - links = [t.to_dict()["links"] - for t in self.links] - model_name = "roles" - return json.dumps({model_name: values, - ("%s_links" % model_name): links}) - - def to_json_values(self): - values = [t.to_dict()["role"] for t in self.values] - return values - - -class Token(Resource): - """ Token model """ - def __init__(self, id=None, user_id=None, expires=None, tenant_id=None, - *args, **kw): - super(Token, self).__init__(id=id, user_id=user_id, expires=expires, - tenant_id=tenant_id, *args, **kw) - - -class UserRoleAssociation(Resource): - """ Role Grant model """ - - hints = { - 'contract_attributes': ['id', 'role_id', 'user_id', 'tenant_id'], - 'types': [('user_id', basestring), ('tenant_id', basestring)], - 'maps': {'userId': 'user_id', 'roleId': 'role_id', - 'tenantId': 'tenant_id'} - } - - def __init__(self, user_id=None, role_id=None, tenant_id=None, - *args, **kw): - # pylint: disable=E0203 - super(UserRoleAssociation, self).__init__(user_id=user_id, - role_id=role_id, tenant_id=tenant_id, - *args, **kw) - if isinstance(self.user_id, int): - # pylint: disable=E0203 - self.user_id = str(self.user_id) - if isinstance(self.tenant_id, int): - self.tenant_id = str(self.tenant_id) - - def to_json(self, hints=None, model_name=None): - if model_name is None: - model_name = "role" - return super(UserRoleAssociation, self).to_json(hints=hints, - model_name=model_name) - - def to_xml(self, hints=None, model_name=None): - if model_name is None: - model_name = "role" - return super(UserRoleAssociation, self).to_xml(hints=hints, - model_name=model_name) - - -class Credentials(Resource): - # pylint: disable=R0913 - def __init__(self, id=None, user_id=None, tenant_id=None, type=None, - key=None, secret=None, *args, **kw): - super(Credentials, self).__init__(id=id, user_id=user_id, - tenant_id=tenant_id, type=type, key=key, secret=secret, *args, - **kw) diff --git a/keystone/policy/__init__.py b/keystone/policy/__init__.py new file mode 100644 index 0000000000..d16de59fc2 --- /dev/null +++ b/keystone/policy/__init__.py @@ -0,0 +1 @@ +from keystone.policy.core import * diff --git a/keystone/contrib/extensions/admin/osec2/__init__.py b/keystone/policy/backends/__init__.py similarity index 100% rename from keystone/contrib/extensions/admin/osec2/__init__.py rename to keystone/policy/backends/__init__.py diff --git a/keystone/policy/backends/simple.py b/keystone/policy/backends/simple.py new file mode 100644 index 0000000000..ec4840fe4d --- /dev/null +++ b/keystone/policy/backends/simple.py @@ -0,0 +1,23 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + + +from keystone.common import logging + + +class TrivialTrue(object): + def can_haz(self, target, credentials): + return True + + +class SimpleMatch(object): + def can_haz(self, target, credentials): + """Check whether key-values in target are present in credentials.""" + # TODO(termie): handle ANDs, probably by providing a tuple instead of a + # string + for requirement in target: + key, match = requirement.split(':', 1) + check = credentials.get(key) + if check is None or isinstance(check, basestring): + check = [check] + if match in check: + return True diff --git a/keystone/policy/core.py b/keystone/policy/core.py new file mode 100644 index 0000000000..694f62853b --- /dev/null +++ b/keystone/policy/core.py @@ -0,0 +1,21 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +"""Main entry point into the Policy service.""" + +from keystone import config +from keystone.common import manager + + +CONF = config.CONF + + +class Manager(manager.Manager): + """Default pivot point for the Policy backend. + + See :mod:`keystone.common.manager.Manager` for more details on how this + dynamically calls the backend. + + """ + + def __init__(self): + super(Manager, self).__init__(CONF.policy.driver) diff --git a/keystone/routers/__init__.py b/keystone/routers/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/keystone/routers/admin.py b/keystone/routers/admin.py deleted file mode 100755 index 63ceb4f09a..0000000000 --- a/keystone/routers/admin.py +++ /dev/null @@ -1,145 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 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 logging -import routes - -from keystone.common import wsgi -from keystone.controllers.token import TokenController -from keystone.controllers.roles import RolesController -from keystone.controllers.staticfiles import StaticFilesController -from keystone.controllers.tenant import TenantController -from keystone.controllers.user import UserController -from keystone.controllers.version import VersionController -from keystone.controllers.extensions import ExtensionsController -import keystone.contrib.extensions.admin as extension - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class AdminApi(wsgi.Router): - """WSGI entry point for admin Keystone API requests.""" - - def __init__(self): - mapper = routes.Mapper() - - # Load extensions first so they can override core if they need to - extension.get_extension_configurer().configure(mapper) - - # Token Operations - auth_controller = TokenController() - mapper.connect("/tokens", controller=auth_controller, - action="authenticate", - conditions=dict(method=["POST"])) - mapper.connect("/tokens/{token_id}", controller=auth_controller, - action="validate_token", - conditions=dict(method=["GET"])) - mapper.connect("/tokens/{token_id}", controller=auth_controller, - action="check_token", - conditions=dict(method=["HEAD"])) - # Do we need this. API doesn't have delete token. - mapper.connect("/tokens/{token_id}", controller=auth_controller, - action="delete_token", - conditions=dict(method=["DELETE"])) - mapper.connect("/tokens/{token_id}/endpoints", - controller=auth_controller, - action="endpoints", - conditions=dict(method=["GET"])) - - # Tenant Operations - tenant_controller = TenantController() - mapper.connect("/tenants", controller=tenant_controller, - action="get_tenants", conditions=dict(method=["GET"])) - mapper.connect("/tenants/{tenant_id}", - controller=tenant_controller, - action="get_tenant", conditions=dict(method=["GET"])) - roles_controller = RolesController() - mapper.connect("/tenants/{tenant_id}/users/{user_id}/roles", - controller=roles_controller, action="get_user_roles", - conditions=dict(method=["GET"])) - # User Operations - user_controller = UserController() - mapper.connect("/users/{user_id}", - controller=user_controller, - action="get_user", - conditions=dict(method=["GET"])) - mapper.connect("/users/{user_id}/roles", - controller=roles_controller, action="get_user_roles", - conditions=dict(method=["GET"])) - # Miscellaneous Operations - version_controller = VersionController() - mapper.connect("/", controller=version_controller, - action="get_version_info", file="admin/version", - conditions=dict(method=["GET"])) - - extensions_controller = ExtensionsController() - mapper.connect("/extensions", - controller=extensions_controller, - action="get_extensions_info", - conditions=dict(method=["GET"])) - - # Static Files Controller - static_files_controller = StaticFilesController() - mapper.connect("/identityadminguide.pdf", - controller=static_files_controller, - action="get_pdf_contract", - root="content/admin/", pdf="identityadminguide.pdf", - conditions=dict(method=["GET"])) - mapper.connect("/identity-admin.wadl", - controller=static_files_controller, - action="get_wadl_contract", - root="content/admin/", wadl="identity-admin.wadl", - conditions=dict(method=["GET"])) - mapper.connect("/common.ent", - controller=static_files_controller, - action="get_wadl_contract", - root="content/common/", wadl="common.ent", - conditions=dict(method=["GET"])) - mapper.connect("/xsd/{xsd}", - controller=static_files_controller, - action="get_xsd_contract", - root="content/common/", - conditions=dict(method=["GET"])) - mapper.connect("/xsd/atom/{xsd}", - controller=static_files_controller, - action="get_xsd_atom_contract", - root="content/common/", - conditions=dict(method=["GET"])) - mapper.connect("/xslt/{file:.*}", - controller=static_files_controller, - action="get_static_file", - root="content/common/", path="xslt/", - mimetype="application/xml", - conditions=dict(method=["GET"])) - mapper.connect("/js/{file:.*}", - controller=static_files_controller, - action="get_static_file", - root="content/common/", path="js/", - mimetype="application/javascript", - conditions=dict(method=["GET"])) - mapper.connect("/style/{file:.*}", - controller=static_files_controller, - action="get_static_file", - root="content/common/", path="style/", - mimetype="application/css", - conditions=dict(method=["GET"])) - mapper.connect("/samples/{file:.*}", - controller=static_files_controller, - action="get_static_file", - root="content/common/", path="samples/", - conditions=dict(method=["GET"])) - super(AdminApi, self).__init__(mapper) diff --git a/keystone/routers/service.py b/keystone/routers/service.py deleted file mode 100644 index 529e5b4443..0000000000 --- a/keystone/routers/service.py +++ /dev/null @@ -1,117 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 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 logging -import routes - -from keystone.common import wsgi -from keystone.controllers.token import TokenController -from keystone.controllers.tenant import TenantController -from keystone.controllers.version import VersionController -from keystone.controllers.staticfiles import StaticFilesController -from keystone.controllers.extensions import ExtensionsController - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class ServiceApi(wsgi.Router): - """WSGI entry point for Keystone Service API requests.""" - - def __init__(self): - mapper = routes.Mapper() - - # Token Operations - auth_controller = TokenController() - mapper.connect("/tokens", controller=auth_controller, - action="authenticate", - conditions=dict(method=["POST"])) - # TODO(zns): this should be deprecated - mapper.connect("/ec2tokens", controller=auth_controller, - action="authenticate_ec2", - conditions=dict(method=["POST"])) - - tenant_controller = TenantController(True) - mapper.connect("/tenants", - controller=tenant_controller, - action="get_tenants", - conditions=dict(method=["GET"])) - - # Miscellaneous Operations - version_controller = VersionController() - mapper.connect("/", - controller=version_controller, - action="get_version_info", file="service/version", - conditions=dict(method=["GET"])) - - extensions_controller = ExtensionsController(True) - mapper.connect("/extensions", - controller=extensions_controller, - action="get_extensions_info", - conditions=dict(method=["GET"])) - - # Static Files Controller - static_files_controller = StaticFilesController() - mapper.connect("/identitydevguide.pdf", - controller=static_files_controller, - action="get_pdf_contract", - root="content/service/", pdf="identitydevguide.pdf", - conditions=dict(method=["GET"])) - mapper.connect("/identity.wadl", - controller=static_files_controller, - action="get_wadl_contract", - root="content/service/", wadl="identity.wadl", - conditions=dict(method=["GET"])) - mapper.connect("/common.ent", - controller=static_files_controller, - action="get_wadl_contract", - wadl="common.ent", root="content/common/", - conditions=dict(method=["GET"])) - mapper.connect("/xslt/{file:.*}", - controller=static_files_controller, - action="get_static_file", path="common/xslt/", - mimetype="application/xml", - conditions=dict(method=["GET"])) - mapper.connect("/style/{file:.*}", - controller=static_files_controller, - action="get_static_file", path="common/style/", - mimetype="application/css", - conditions=dict(method=["GET"])) - mapper.connect("/js/{file:.*}", - controller=static_files_controller, - action="get_static_file", path="common/js/", - mimetype="application/javascript", - conditions=dict(method=["GET"])) - mapper.connect("/samples/{file:.*}", - controller=static_files_controller, - action="get_static_file", path="common/samples/", - conditions=dict(method=["GET"])) - mapper.connect("/xsd/{xsd:.*}", - controller=static_files_controller, - action="get_xsd_contract", root="content/common/", - conditions=dict(method=["GET"])) - mapper.connect("/xsd/atom/{xsd}", - controller=static_files_controller, - action="get_xsd_atom_contract", - root="content/common/", - conditions=dict(method=["GET"])) - mapper.connect("/xsd/atom/{xsd}", - controller=static_files_controller, - action="get_xsd_atom_contract", - root="content/common/", - conditions=dict(method=["GET"])) - - super(ServiceApi, self).__init__(mapper) diff --git a/keystone/server.py b/keystone/server.py deleted file mode 100755 index de04094869..0000000000 --- a/keystone/server.py +++ /dev/null @@ -1,186 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 2010-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. - - -""" -Service that stores identities and issues and manages tokens - -HEADERS -------- - -* HTTP\_ is a standard http header -* HTTP_X is an extended http header - -Coming in from initial call -^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -HTTP_X_AUTH_TOKEN - the client token being passed in - -HTTP_X_STORAGE_TOKEN - the client token being passed in (legacy Rackspace use) to support - cloud files - -Used for communication between components -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -www-authenticate - only used if this component is being used remotely - -HTTP_AUTHORIZATION - basic auth password used to validate the connection - -What we add to the request for use by the OpenStack SERVICE -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -HTTP_X_AUTHORIZATION - the client identity being passed in - -""" - -# pylint: disable=W0613 - -import logging - -from keystone import config -from keystone.common import config as common_config -from keystone.common import wsgi -from keystone.routers.service import ServiceApi -from keystone.routers.admin import AdminApi - -CONF = config.CONF - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -def service_app_factory(global_conf, **local_conf): - """paste.deploy app factory for creating OpenStack API server apps""" - return ServiceApi() - - -def admin_app_factory(global_conf, **local_conf): - """paste.deploy app factory for creating OpenStack API server apps""" - return AdminApi() - - -# pylint: disable=R0902 -class Server(): - """Used to start and stop Keystone servers - - This class is called from shell and command-line scripts and is the - entry-point for starting and stopping Keystone servers. - - The initializer can take option and argument overrides, but otherwise will - parse arguments and configuration files itself to determine how to start - the server. - """ - - def __init__(self, name='admin', config_name=None, args=None): - """Initizalizer which takes the following paramaters: - - :param name: A cosmetic name for the server (ex. Admin API) - :param config: the paste config name to look for when starting the - server - :param args: override for sys.argv (otherwise sys.argv is used) - """ - logger.debug("Init server '%s' with config=%s" % (name, config_name)) - - self.name = name - self.config = config_name or self.name - self.args = args - self.key = None - self.server = None - self.port = None - self.host = None - self.protocol = None - self.options = CONF.to_dict() - - def start(self, host=None, port=None, wait=True): - """Starts the Keystone server - - :param host: the IP address to listen on - :param port: the TCP/IP port to listen on - :param wait: whether to wait (block) for the server to terminate or - return to the caller without waiting - """ - logger.debug("Starting API server") - conf, app = common_config.load_paste_app(self.config, self.options, - self.args) - - debug = CONF.debug in [True, "True", "1"] - verbose = CONF.verbose in [True, "True", "1"] - - if debug or verbose: - config_file = common_config.find_config_file(self.options, - self.args) - logger.info("Starting '%s' with config: %s" % - (self.config, config_file)) - - if port is None: - if self.config == 'admin': - # Legacy - port = int(CONF.admin_port or 35357) - else: - port = int(CONF.service_port or CONF.bind_port or 5000) - if host is None: - host = CONF.bind_host or CONF.service_host or "0.0.0.0" - - self.key = "%s-%s:%s" % (self.name, host, port) - - # Safely get SSL options - service_ssl = CONF.service_ssl in [True, "True", "1"] - - # Load the server - if service_ssl: - cert_required = conf.get('cert_required', False) - cert_required = cert_required in [True, "True", "1"] - certfile = conf.get('certfile') - keyfile = conf.get('keyfile') - ca_certs = conf.get('ca_certs') - - self.server = wsgi.SslServer() - self.server.start(app, port, host, - certfile=certfile, keyfile=keyfile, - ca_certs=ca_certs, - cert_required=cert_required, - key=self.key) - self.protocol = 'https' - else: - self.server = wsgi.Server() - self.server.start(app, port, host, - key="%s-%s:%s" % (self.config, host, port)) - self.protocol = 'http' - - self.port = port - self.host = host - - logger.info("%s listening on %s://%s:%s" % ( - self.name, ['http', 'https'][service_ssl], host, port)) - - # Wait until done - if wait: - self.server.wait() - - def stop(self): - """Stops the Keystone server - - This should be called always to release the network socket - """ - if self.server is not None: - if self.key in self.server.threads: - logger.debug("Killing %s" % self.key) - self.server.threads[self.key].kill() - self.server = None diff --git a/keystone/service.py b/keystone/service.py new file mode 100644 index 0000000000..2c361e400b --- /dev/null +++ b/keystone/service.py @@ -0,0 +1,448 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +import uuid + +import routes +import webob.dec +import webob.exc + +from keystone import catalog +from keystone import exception +from keystone import identity +from keystone import policy +from keystone import token +from keystone.common import logging +from keystone.common import utils +from keystone.common import wsgi + + +class AdminRouter(wsgi.ComposingRouter): + def __init__(self): + mapper = routes.Mapper() + + # Token Operations + auth_controller = TokenController() + mapper.connect('/tokens', + controller=auth_controller, + action='authenticate', + conditions=dict(method=['POST'])) + mapper.connect('/tokens/{token_id}', + controller=auth_controller, + action='validate_token', + conditions=dict(method=['GET'])) + mapper.connect('/tokens/{token_id}', + controller=auth_controller, + action='delete_token', + conditions=dict(method=['DELETE'])) + mapper.connect('/tokens/{token_id}/endpoints', + controller=auth_controller, + action='endpoints', + conditions=dict(method=['GET'])) + + # Miscellaneous Operations + extensions_controller = ExtensionsController() + mapper.connect('/extensions', + controller=extensions_controller, + action='get_extensions_info', + conditions=dict(method=['GET'])) + identity_router = identity.AdminRouter() + routers = [identity_router] + super(AdminRouter, self).__init__(mapper, routers) + + +class PublicRouter(wsgi.ComposingRouter): + def __init__(self): + mapper = routes.Mapper() + + noop_controller = NoopController() + mapper.connect('/', + controller=noop_controller, + action='noop') + + # Token Operations + auth_controller = TokenController() + mapper.connect('/tokens', + controller=auth_controller, + action='authenticate', + conditions=dict(method=['POST'])) + + # Miscellaneous + extensions_controller = ExtensionsController() + mapper.connect('/extensions', + controller=extensions_controller, + action='get_extensions_info', + conditions=dict(method=['GET'])) + + identity_router = identity.PublicRouter() + routers = [identity_router] + + super(PublicRouter, self).__init__(mapper, routers) + + +class PublicVersionRouter(wsgi.ComposingRouter): + def __init__(self): + mapper = routes.Mapper() + version_controller = VersionController('public') + mapper.connect('/', + controller=version_controller, + action='get_versions') + routers = [] + super(PublicVersionRouter, self).__init__(mapper, routers) + + +class AdminVersionRouter(wsgi.ComposingRouter): + def __init__(self): + mapper = routes.Mapper() + version_controller = VersionController('admin') + mapper.connect('/', + controller=version_controller, + action='get_versions') + routers = [] + super(AdminVersionRouter, self).__init__(mapper, routers) + + +class VersionController(wsgi.Application): + def __init__(self, version_type): + self.catalog_api = catalog.Manager() + self.url_key = "%sURL" % version_type + super(VersionController, self).__init__() + + def _get_identity_url(self, context): + catalog_ref = self.catalog_api.get_catalog( + context=context, + user_id=None, + tenant_id=None) + for region, region_ref in catalog_ref.iteritems(): + for service, service_ref in region_ref.iteritems(): + if service == 'identity': + return service_ref[self.url_key] + + raise NotImplemented() + + def get_versions(self, context): + identity_url = self._get_identity_url(context) + if not identity_url.endswith('/'): + identity_url = identity_url + '/' + return { + "versions": { + "values": [{ + "id": "v2.0", + "status": "beta", + "updated": "2011-11-19T00:00:00Z", + "links": [{ + "rel": "self", + "href": identity_url, + }, { + "rel": "describedby", + "type": "text/html", + "href": "http://docs.openstack.org/api/openstack-" + "identity-service/2.0/content/" + }, { + "rel": "describedby", + "type": "application/pdf", + "href": "http://docs.openstack.org/api/openstack-" + "identity-service/2.0/identity-dev-guide-" + "2.0.pdf" + }], + "media-types": [{ + "base": "application/json", + "type": "application/vnd.openstack.identity-v2.0" + "+json" + }] + }] + } + } + + +class NoopController(wsgi.Application): + def __init__(self): + super(NoopController, self).__init__() + + def noop(self, context): + return {} + + +class TokenController(wsgi.Application): + def __init__(self): + self.catalog_api = catalog.Manager() + self.identity_api = identity.Manager() + self.token_api = token.Manager() + self.policy_api = policy.Manager() + super(TokenController, self).__init__() + + def authenticate(self, context, auth=None): + """Authenticate credentials and return a token. + + Accept auth as a dict that looks like:: + + { + "auth":{ + "passwordCredentials":{ + "username":"test_user", + "password":"mypass" + }, + "tenantName":"customer-x" + } + } + + In this case, tenant is optional, if not provided the token will be + considered "unscoped" and can later be used to get a scoped token. + + Alternatively, this call accepts auth with only a token and tenant + that will return a token that is scoped to that tenant. + """ + + token_id = uuid.uuid4().hex + if 'passwordCredentials' in auth: + username = auth['passwordCredentials'].get('username', '') + password = auth['passwordCredentials'].get('password', '') + tenant_name = auth.get('tenantName', None) + + if username: + user_ref = self.identity_api.get_user_by_name( + context=context, user_name=username) + user_id = user_ref['id'] + else: + user_id = auth['passwordCredentials'].get('userId', None) + + # more compat + if tenant_name: + tenant_ref = self.identity_api.get_tenant_by_name( + context=context, tenant_name=tenant_name) + tenant_id = tenant_ref['id'] + else: + tenant_id = auth.get('tenantId', None) + + try: + (user_ref, tenant_ref, metadata_ref) = \ + self.identity_api.authenticate(context=context, + user_id=user_id, + password=password, + tenant_id=tenant_id) + + # If the user is disabled don't allow them to authenticate + if not user_ref.get('enabled', True): + raise webob.exc.HTTPForbidden('User has been disabled') + except AssertionError as e: + raise webob.exc.HTTPForbidden(e.message) + + token_ref = self.token_api.create_token( + context, token_id, dict(id=token_id, + user=user_ref, + tenant=tenant_ref, + metadata=metadata_ref)) + if tenant_ref: + catalog_ref = self.catalog_api.get_catalog( + context=context, + user_id=user_ref['id'], + tenant_id=tenant_ref['id'], + metadata=metadata_ref) + else: + catalog_ref = {} + + elif 'token' in auth: + token = auth['token'].get('id', None) + + tenant_name = auth.get('tenantName') + + # more compat + if tenant_name: + tenant_ref = self.identity_api.get_tenant_by_name( + context=context, tenant_name=tenant_name) + tenant_id = tenant_ref['id'] + else: + tenant_id = auth.get('tenantId', None) + + try: + old_token_ref = self.token_api.get_token(context=context, + token_id=token) + except exception.NotFound: + raise exception.Unauthorized() + + user_ref = old_token_ref['user'] + + tenants = self.identity_api.get_tenants_for_user(context, + user_ref['id']) + if tenant_id: + assert tenant_id in tenants + + tenant_ref = self.identity_api.get_tenant(context=context, + tenant_id=tenant_id) + if tenant_ref: + metadata_ref = self.identity_api.get_metadata( + context=context, + user_id=user_ref['id'], + tenant_id=tenant_ref['id']) + catalog_ref = self.catalog_api.get_catalog( + context=context, + user_id=user_ref['id'], + tenant_id=tenant_ref['id'], + metadata=metadata_ref) + else: + metadata_ref = {} + catalog_ref = {} + + token_ref = self.token_api.create_token( + context, token_id, dict(id=token_id, + user=user_ref, + tenant=tenant_ref, + metadata=metadata_ref)) + + # TODO(termie): optimize this call at some point and put it into the + # the return for metadata + # fill out the roles in the metadata + roles_ref = [] + for role_id in metadata_ref.get('roles', []): + roles_ref.append(self.identity_api.get_role(context, role_id)) + logging.debug('TOKEN_REF %s', token_ref) + return self._format_authenticate(token_ref, roles_ref, catalog_ref) + + # admin only + def validate_token(self, context, token_id, belongs_to=None): + """Check that a token is valid. + + Optionally, also ensure that it is owned by a specific tenant. + + """ + # TODO(termie): this stuff should probably be moved to middleware + self.assert_admin(context) + + token_ref = self.token_api.get_token(context=context, + token_id=token_id) + + if belongs_to: + assert token_ref['tenant']['id'] == belongs_to + + # TODO(termie): optimize this call at some point and put it into the + # the return for metadata + # fill out the roles in the metadata + metadata_ref = token_ref['metadata'] + roles_ref = [] + for role_id in metadata_ref.get('roles', []): + roles_ref.append(self.identity_api.get_role(context, role_id)) + return self._format_token(token_ref, roles_ref) + + def delete_token(self, context, token_id): + """Delete a token, effectively invalidating it for authz.""" + # TODO(termie): this stuff should probably be moved to middleware + self.assert_admin(context) + + self.token_api.delete_token(context=context, token_id=token_id) + + def endpoints(self, context, token_id): + """Return service catalog endpoints.""" + try: + token_ref = self.token_api.get_token(context=context, + token_id=token_id) + except exception.NotFound: + raise exception.Unauthorized() + + catalog_ref = self.catalog_api.get_catalog(context, + token_ref['user']['id'], + token_ref['tenant']['id']) + return {'token': {'serviceCatalog': self._format_catalog(catalog_ref)}} + + def _format_authenticate(self, token_ref, roles_ref, catalog_ref): + o = self._format_token(token_ref, roles_ref) + o['access']['serviceCatalog'] = self._format_catalog(catalog_ref) + return o + + def _format_token(self, token_ref, roles_ref): + user_ref = token_ref['user'] + metadata_ref = token_ref['metadata'] + expires = token_ref['expires'] + if expires is not None: + expires = utils.isotime(expires) + o = {'access': {'token': {'id': token_ref['id'], + 'expires': expires, + }, + 'user': {'id': user_ref['id'], + 'name': user_ref['name'], + 'username': user_ref['name'], + 'roles': roles_ref, + 'roles_links': metadata_ref.get('roles_links', + []) + } + } + } + if 'tenant' in token_ref and token_ref['tenant']: + token_ref['tenant']['enabled'] = True + o['access']['token']['tenant'] = token_ref['tenant'] + return o + + def _format_catalog(self, catalog_ref): + """Munge catalogs from internal to output format + Internal catalogs look like: + + {$REGION: { + {$SERVICE: { + $key1: $value1, + ... + } + } + } + + The legacy api wants them to look like + + [{'name': $SERVICE[name], + 'type': $SERVICE, + 'endpoints': [{ + 'tenantId': $tenant_id, + ... + 'region': $REGION, + }], + 'endpoints_links': [], + }] + + """ + if not catalog_ref: + return {} + + services = {} + for region, region_ref in catalog_ref.iteritems(): + for service, service_ref in region_ref.iteritems(): + new_service_ref = services.get(service, {}) + new_service_ref['name'] = service_ref.pop('name') + new_service_ref['type'] = service + new_service_ref['endpoints_links'] = [] + service_ref['region'] = region + + endpoints_ref = new_service_ref.get('endpoints', []) + endpoints_ref.append(service_ref) + + new_service_ref['endpoints'] = endpoints_ref + services[service] = new_service_ref + + return services.values() + + +class ExtensionsController(wsgi.Application): + def __init__(self): + super(ExtensionsController, self).__init__() + + def get_extensions_info(self, context): + raise NotImplemented() + + +def public_app_factory(global_conf, **local_conf): + conf = global_conf.copy() + conf.update(local_conf) + return PublicRouter() + + +def admin_app_factory(global_conf, **local_conf): + conf = global_conf.copy() + conf.update(local_conf) + return AdminRouter() + + +def public_version_app_factory(global_conf, **local_conf): + conf = global_conf.copy() + conf.update(local_conf) + return PublicVersionRouter() + + +def admin_version_app_factory(global_conf, **local_conf): + conf = global_conf.copy() + conf.update(local_conf) + return AdminVersionRouter() diff --git a/keystone/test.py b/keystone/test.py new file mode 100644 index 0000000000..f28ce6f709 --- /dev/null +++ b/keystone/test.py @@ -0,0 +1,248 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +import os +import unittest +import subprocess +import sys +import time + +from paste import deploy + +from keystone import config +from keystone.common import kvs +from keystone.common import logging +from keystone.common import utils +from keystone.common import wsgi + + +ROOTDIR = os.path.dirname(os.path.dirname(__file__)) +VENDOR = os.path.join(ROOTDIR, 'vendor') +TESTSDIR = os.path.join(ROOTDIR, 'tests') +ETCDIR = os.path.join(ROOTDIR, 'etc') +CONF = config.CONF + + +cd = os.chdir + + +def rootdir(*p): + return os.path.join(ROOTDIR, *p) + + +def etcdir(*p): + return os.path.join(ETCDIR, *p) + + +def testsdir(*p): + return os.path.join(TESTSDIR, *p) + + +def checkout_vendor(repo, rev): + # TODO(termie): this function is a good target for some optimizations :PERF + name = repo.split('/')[-1] + if name.endswith('.git'): + name = name[:-4] + + working_dir = os.getcwd() + revdir = os.path.join(VENDOR, '%s-%s' % (name, rev.replace('/', '_'))) + modcheck = os.path.join(VENDOR, '.%s-%s' % (name, rev.replace('/', '_'))) + try: + if os.path.exists(modcheck): + mtime = os.stat(modcheck).st_mtime + if int(time.time()) - mtime < 10000: + return revdir + + if not os.path.exists(revdir): + utils.git('clone', repo, revdir) + + cd(revdir) + utils.git('checkout', '-q', 'master') + utils.git('pull', '-q') + utils.git('checkout', '-q', rev) + + # write out a modified time + with open(modcheck, 'w') as fd: + fd.write('1') + except subprocess.CalledProcessError as e: + logging.warning('Failed to checkout %s', repo) + cd(working_dir) + return revdir + + +class TestClient(object): + def __init__(self, app=None, token=None): + self.app = app + self.token = token + + def request(self, method, path, headers=None, body=None): + if headers is None: + headers = {} + + if self.token: + headers.setdefault('X-Auth-Token', self.token) + + req = wsgi.Request.blank(path) + req.method = method + for k, v in headers.iteritems(): + req.headers[k] = v + if body: + req.body = body + return req.get_response(self.app) + + def get(self, path, headers=None): + return self.request('GET', path=path, headers=headers) + + def post(self, path, headers=None, body=None): + return self.request('POST', path=path, headers=headers, body=body) + + def put(self, path, headers=None, body=None): + return self.request('PUT', path=path, headers=headers, body=body) + + +class TestCase(unittest.TestCase): + def __init__(self, *args, **kw): + super(TestCase, self).__init__(*args, **kw) + self._paths = [] + self._memo = {} + + def setUp(self): + super(TestCase, self).setUp() + self.config() + + def config(self): + CONF(config_files=[etcdir('keystone.conf'), + testsdir('test_overrides.conf')]) + + def tearDown(self): + for path in self._paths: + if path in sys.path: + sys.path.remove(path) + kvs.INMEMDB.clear() + CONF.reset() + super(TestCase, self).tearDown() + + def load_backends(self): + """Hacky shortcut to load the backends for data manipulation.""" + self.identity_api = utils.import_object(CONF.identity.driver) + self.token_api = utils.import_object(CONF.token.driver) + self.catalog_api = utils.import_object(CONF.catalog.driver) + + def load_fixtures(self, fixtures): + """Hacky basic and naive fixture loading based on a python module. + + Expects that the various APIs into the various services are already + defined on `self`. + + """ + # TODO(termie): doing something from json, probably based on Django's + # loaddata will be much preferred. + for tenant in fixtures.TENANTS: + rv = self.identity_api.create_tenant(tenant['id'], tenant) + setattr(self, 'tenant_%s' % tenant['id'], rv) + + for user in fixtures.USERS: + user_copy = user.copy() + tenants = user_copy.pop('tenants') + rv = self.identity_api.create_user(user['id'], user_copy.copy()) + for tenant_id in tenants: + self.identity_api.add_user_to_tenant(tenant_id, user['id']) + setattr(self, 'user_%s' % user['id'], user_copy) + + for role in fixtures.ROLES: + rv = self.identity_api.create_role(role['id'], role) + setattr(self, 'role_%s' % role['id'], rv) + + for metadata in fixtures.METADATA: + metadata_ref = metadata.copy() + # TODO(termie): these will probably end up in the model anyway, + # so this may be futile + del metadata_ref['user_id'] + del metadata_ref['tenant_id'] + rv = self.identity_api.create_metadata(metadata['user_id'], + metadata['tenant_id'], + metadata_ref) + setattr(self, + 'metadata_%s%s' % (metadata['user_id'], + metadata['tenant_id']), rv) + + def _paste_config(self, config): + if not config.startswith('config:'): + test_path = os.path.join(TESTSDIR, config) + etc_path = os.path.join(ROOTDIR, 'etc', config) + for path in [test_path, etc_path]: + if os.path.exists('%s.conf' % path): + return 'config:%s.conf' % path + return config + + def loadapp(self, config, name='main'): + return deploy.loadapp(self._paste_config(config), name=name) + + def appconfig(self, config): + return deploy.appconfig(self._paste_config(config)) + + def serveapp(self, config, name=None): + app = self.loadapp(config, name=name) + server = wsgi.Server(app, 0) + server.start(key='socket') + + # Service catalog tests need to know the port we ran on. + port = server.socket_info['socket'][1] + CONF.public_port = port + CONF.admin_port = port + return server + + def client(self, app, *args, **kw): + return TestClient(app, *args, **kw) + + def add_path(self, path): + sys.path.insert(0, path) + self._paths.append(path) + + def clear_module(self, module): + for x in sys.modules.keys(): + if x.startswith(module): + del sys.modules[x] + + def assertListEquals(self, actual, expected): + copy = expected[:] + #print expected, actual + self.assertEquals(len(actual), len(expected)) + while copy: + item = copy.pop() + matched = False + for x in actual: + #print 'COMPARE', item, x, + try: + self.assertDeepEquals(x, item) + matched = True + #print 'MATCHED' + break + except AssertionError as e: + #print e + pass + if not matched: + raise AssertionError('Expected: %s\n Got: %s' % (expected, + actual)) + + def assertDictEquals(self, actual, expected): + for k in expected: + self.assertTrue(k in actual, + 'Expected key %s not in %s.' % (k, actual)) + self.assertDeepEquals(expected[k], actual[k]) + + for k in actual: + self.assertTrue(k in expected, + 'Unexpected key %s in %s.' % (k, actual)) + + def assertDeepEquals(self, actual, expected): + try: + if type(expected) is type([]) or type(expected) is type(tuple()): + # assert items equal, ignore order + self.assertListEquals(actual, expected) + elif type(expected) is type({}): + self.assertDictEquals(actual, expected) + else: + self.assertEquals(actual, expected) + except AssertionError as e: + raise + raise AssertionError('Expected: %s\n Got: %s' % (expected, actual)) diff --git a/keystone/test/EchoSOAPUI.xml b/keystone/test/EchoSOAPUI.xml deleted file mode 100644 index 8a74eee056..0000000000 --- a/keystone/test/EchoSOAPUI.xml +++ /dev/null @@ -1,2 +0,0 @@ - -http://localhost:8090X-Auth-TokenHEADERxs:string*/*application/xml200v1:echoapplication/json200<xml-fragment/>http://localhost:8090*/*application/xml200v1:echoapplication/json200http://localhost:8090*/*application/xml200v1:echoapplication/json200http://localhost:8090*/*application/xml200v1:echoapplication/json200http://localhost:8090*/*application/xml200v1:echoapplication/json200http://localhost:8090*/*application/xml200v1:echoapplication/json200http://localhost:8090 \ No newline at end of file diff --git a/keystone/test/IdentitySOAPUI.xml b/keystone/test/IdentitySOAPUI.xml deleted file mode 100644 index de072afbfa..0000000000 --- a/keystone/test/IdentitySOAPUI.xml +++ /dev/null @@ -1,1355 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]>file:/Users/jorgew/projects/keystone/keystone/identity.wadl - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]>http://wadl.dev.java.net/2009/02file:/Users/jorgew/projects/keystone/keystone/xsd/api.xsd<schema elementFormDefault="qualified" attributeFormDefault="unqualified" targetNamespace="http://docs.openstack.org/identity/api/v2.0" xmlns="http://www.w3.org/2001/XMLSchema" xmlns:identity="http://docs.openstack.org/identity/api/v2.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> - <include schemaLocation="token.xsd"/> - <include schemaLocation="tenant.xsd"/> - <include schemaLocation="fault.xsd"/> -</schema>http://www.w3.org/2001/XMLSchemafile:/Users/jorgew/projects/keystone/keystone/xsd/token.xsd - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -]]>http://www.w3.org/2001/XMLSchemafile:/Users/jorgew/projects/keystone/keystone/xsd/tenant.xsd - - - - - - - - - - - - - - - - - - - - - - - -]]>http://www.w3.org/2001/XMLSchemafile:/Users/jorgew/projects/keystone/keystone/xsd/atom/atom.xsd - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - See section 3.4 of the ATOM RFC - RFC4287 - - - - - - - TODO - - - - - - - TODO - - - - - - - TODO - - - - - - - TODO - - - - - - - TODO - - - - - - -]]>http://www.w3.org/2001/XMLSchemafile:/Users/jorgew/projects/keystone/keystone/xsd/atom/xml.xsd - - - -
-

About the XML namespace

-
-

This schema document describes the XML namespace, in a form - suitable for import by other schema documents.

-

- See - http://www.w3.org/XML/1998/namespace.html - and - http://www.w3.org/TR/REC-xml - for information - about this namespace. -

-

Note that local names in this namespace are intended to be - defined only by the World Wide Web Consortium or its subgroups. - The names currently defined in this namespace are listed below. - They should not be used with conflicting semantics by any Working - Group, specification, or document instance.

-

- See further below in this document for more information about - how to refer to this schema document from your own - XSD schema documents - and about - the - namespace-versioning policy governing this schema document - . -

-
-
-
-
- - - -
-

lang (as an attribute name)

-

denotes an attribute whose value - is a language code for the natural language of the content of - any element; its value is inherited. This name is reserved - by virtue of its definition in the XML specification.

-
-
-

Notes

-

Attempting to install the relevant ISO 2- and 3-letter - codes as the enumerated possible values is probably never - going to be a realistic possibility.

-

- See BCP 47 at - http://www.rfc-editor.org/rfc/bcp/bcp47.txt - and the IANA language subtag registry at - http://www.iana.org/assignments/language-subtag-registry - for further information. -

-

The union allows for the 'un-declaration' of xml:lang with - the empty string.

-
-
-
- - - - - - - - - -
- - - -
-

space (as an attribute name)

-

denotes an attribute whose - value is a keyword indicating what whitespace processing - discipline is intended for the content of the element; its - value is inherited. This name is reserved by virtue of its - definition in the XML specification.

-
-
-
- - - - - - -
- - - -
-

base (as an attribute name)

-

denotes an attribute whose value - provides a URI to be used as the base for interpreting any - relative URIs in the scope of the element on which it - appears; its value is inherited. This name is reserved - by virtue of its definition in the XML Base specification.

-

- See - http://www.w3.org/TR/xmlbase/ - for information about this attribute. -

-
-
-
-
- - - -
-

id (as an attribute name)

-

denotes an attribute whose value - should be interpreted as if declared to be of type ID. - This name is reserved by virtue of its definition in the - xml:id specification.

-

- See - http://www.w3.org/TR/xml-id/ - for information about this attribute. -

-
-
-
-
- - - - - - - - -
-

Father (in any context at all)

-
-

denotes Jon Bosak, the chair of - the original XML Working Group. This name is reserved by - the following decision of the W3C XML Plenary and - XML Coordination groups:

-
-

In appreciation for his vision, leadership and - dedication the W3C XML Plenary on this 10th day of - February, 2000, reserves for Jon Bosak in perpetuity - the XML name "xml:Father".

-
-
-
-
-
- - -
-

- About this schema document -

-
-

- This schema defines attributes and an attribute group suitable - for use by schemas wishing to allow - xml:base - , - xml:lang - , - xml:space - or - xml:id - attributes on elements they define. -

-

To enable this, such a schema must import this schema for - the XML namespace, e.g. as follows:

-
<schema . . .>
-           . . .
-           <import namespace="http://www.w3.org/XML/1998/namespace"
-                      schemaLocation="http://www.w3.org/2001/xml.xsd"/>
-

or

-
<import namespace="http://www.w3.org/XML/1998/namespace"
-                      schemaLocation="http://www.w3.org/2009/01/xml.xsd"/>
-

Subsequently, qualified reference to any of the attributes or the - group defined below will have the desired effect, e.g.

-
<type . . .>
-           . . .
-           <attributeGroup ref="xml:specialAttrs"/>
-

will define a type which will schema-validate an instance element - with any of those attributes.

-
-
-
-
- - -
-

- Versioning policy for this schema document -

-
-

- In keeping with the XML Schema WG's standard versioning - policy, this schema document will persist at - http://www.w3.org/2009/01/xml.xsd - . -

-

- At the date of issue it can also be found at - http://www.w3.org/2001/xml.xsd - . -

-

- The schema document at that URI may however change in the future, - in order to remain compatible with the latest version of XML - Schema itself, or with the XML namespace itself. In other words, - if the XML Schema or XML namespaces change, the version of this - document at - http://www.w3.org/2001/xml.xsd - will change accordingly; the version at - http://www.w3.org/2009/01/xml.xsd - will not change. -

-

Previous dated (and unchanging) versions of this schema - document are at:

- -
-
-
-
-
]]>
http://www.w3.org/2001/XMLSchema
file:/Users/jorgew/projects/keystone/keystone/xsd/fault.xsd - - - - - - - - - - - - - - - - -

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

-
-
-
- - - -

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

-
-
-
- -
- - - -

The HTTP status code associated with the current fault.

-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

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

-
-
-
-
-
-
-]]>
http://www.w3.org/2001/XMLSchema
file:/Users/jorgew/projects/keystone/keystone/xsd/api-common.xsd - - - - - Open Stack Common API Schema Types 1.0 - - - - - -

This is the main index XML Schema document - for Common API Schema Types Version 1.0.

-
-
- - - -

Types related to extensions.

-
-
-
- - - -

Types related to API version details.

-
-
-
-
]]>
http://www.w3.org/2001/XMLSchema
file:/Users/jorgew/projects/keystone/keystone/xsd/extensions.xsd - - - - - - - - - - - - - - - - - - - - - - - - - -

There should be at least one atom link - with a describedby relation.

-
-
-
-
- - - - - -]]>
http://www.w3.org/2001/XMLSchema
file:/Users/jorgew/projects/keystone/keystone/xsd/version.xsd - - - - - - - - - - - - - The VersionStatus type describes a service's operational status. - - - - - - - - - - - - - A version choice list outlines a collection of service version choices. - - - - - - - - - - - In version lists, every single version must - contain at least one self link. - - - - - - - - - - - When used as a root element, a version choice - must contain at least one describedby link. - - - - - - - - - - A version choice contains relevant information about an available service - that a user can then use to target a specific version of the service. Note - that both the descriptive media types and the atom link references are - not manditory and are offered as message enrichment elements rather - than message requirements. - - - - - - - - - - - The ID of a version choice represents the service version's unique - identifier. This ID is guaranteed to be unique only among the - service version choices outlined in the VersionChoiceList. - - - - - - - A version choice's status describes the current operational state of - the given service version. The operational status is captured in a - simple type enumeration called VersionStatus. - - - - - - - - A version choice's updated attribute describes - the time when the version was updated. The - time should be updated anytime - anything - in the - version has changed: documentation, - extensions, bug fixes. - - - - - - - - - - A MediaTypeList outlines a collection of valid media types for a given - service version. - - - - - - - - - - - - A MediaType describes what content types the service version understands. - - - - - - - - - The base of a given media type describes the simple MIME type - that then a more complicated media type can be derived from. These - types are basic and provide no namespace or version specific - data are are only provided as a convenience. Because of this the - base attribute is declared as optional. - - - - - - - - The type attribute of a MediaType describes the MIME specific - identifier of the media type in question. This identifier should include - a vendor namespace ( - See RFC 2048 - ) - as well as a version suffix. - - - - - - -]]>http://www.w3.org/2001/XMLSchema
http://localhost:5000aliasTEMPLATExs:stringapplication/xml200 203v1:extensionapplication/json200 203application/xml400v1:badRequestapplication/xml404v1:itemNotFoundapplication/xml500v1:identityFaultapplication/xml503v1:serviceUnavailableapplication/json400 404 500 503<xml-fragment/>http://localhost:8080application/xml200 203v1:extensionsapplication/json200 203application/xml400v1:badRequestapplication/xml500v1:identityFaultapplication/xml503v1:serviceUnavailableapplication/json400 500 503<xml-fragment/>http://localhost:8080X-Auth-TokenHEADERxs:stringtokenIdTEMPLATExs:stringbelongsToQUERYxs:stringapplication/xml200 203v1:authapplication/json200 203application/xml401v1:unauthorizedapplication/xml403v1:forbiddenapplication/xml403v1:userDisabledapplication/xml400v1:badRequestapplication/xml404v1:itemNotFoundapplication/xml500v1:identityFaultapplication/xml503v1:serviceUnavailableapplication/json400 401 403 404 500 503<xml-fragment/>http://localhost:8080 - - - -application/xml401v1:unauthorizedapplication/xml403v1:forbiddenapplication/xml400v1:badRequestapplication/xml404v1:itemNotFoundapplication/xml500v1:identityFaultapplication/xml503v1:serviceUnavailableapplication/json400 401 403 404 500 503<xml-fragment/>http://localhost:5000 - - -application/xmlv1:passwordCredentialsapplication/jsonapplication/xml200 -203v1:authapplication/json200 -203application/xml401v1:unauthorizedapplication/xml403v1:userDisabledapplication/xml400v1:badRequestapplication/xml500v1:identityFaultapplication/xml503v1:serviceUnavailableapplication/json401 -403 400 500 503<xml-fragment/>http://localhost:5000<passwordCredentials -password="secrete" username="joeuser" -xmlns="http://docs.openstack.org/identity/api/v2.0"/>X-Auth-TokenHEADERxs:stringtenantIdTEMPLATExs:stringapplication/xml200 -203v1:tenantapplication/json200 -203application/xml401v1:unauthorizedapplication/xml403v1:forbiddenapplication/xml400v1:badRequestapplication/xml404v1:itemNotFoundapplication/xml500v1:identityFaultapplication/xml503v1:serviceUnavailableapplication/json400 -401 403 404 500 503<xml-fragment/>http://localhost:8080 - - -application/xmlv1:tenantapplication/jsonapplication/xml200v1:tenantapplication/json200application/xml401v1:unauthorizedapplication/xml403v1:forbiddenapplication/xml404v1:itemNotFoundapplication/xml400v1:badRequestapplication/xml500v1:identityFaultapplication/xml503v1:serviceUnavailableapplication/json401 -403 404 400 500 503<xml-fragment/>http://localhost:8080<v1:tenant enabled="true" xmlns:v1="http://docs.openstack.org/identity/api/v2.0"> - <v1:description>New Description</v1:description> -</v1:tenant> - - -application/xml401v1:unauthorizedapplication/xml403v1:forbiddenapplication/xml400v1:badRequestapplication/xml404v1:itemNotFoundapplication/xml500v1:identityFaultapplication/xml503v1:serviceUnavailableapplication/json400 -401 403 404 500 503<entry key="Accept" value="application/xml" xmlns="http://eviware.com/soapui/config"/>http://localhost:8080 - - -markerQUERYxs:stringlimitQUERYxs:intapplication/xml200 -203v1:tenantsapplication/json200 -203application/xml401v1:unauthorizedapplication/xml403v1:forbiddenapplication/xml400v1:badRequestapplication/xml404v1:itemNotFoundapplication/xml500v1:identityFaultapplication/xml503v1:serviceUnavailableapplication/json400 -401 403 404 500 503<xml-fragment/>http://localhost:8080application/xmlv1:tenantapplication/jsonapplication/xml201v1:tenantapplication/json201application/xml401v1:unauthorizedapplication/xml403v1:forbiddenapplication/xml -409v1:tenantConflictapplication/xml -400v1:badRequestapplication/xml500v1:identityFaultapplication/xml503v1:serviceUnavailableapplication/json401 -403 400 409 500 503<xml-fragment/>http://localhost:8080<v1:tenant -enabled="true" id="my_new_tenant" -xmlns:v1="http://docs.openstack.org/identity/api/v2.0"><v1:description>This -is a description of my tenant. Thank you very -much.</v1:description></v1:tenant>application/xml200 -203v1:versionapplication/json200 -203application/xml400v1:badRequestapplication/xml500v1:identityFaultapplication/xml503v1:serviceUnavailableapplication/json400 -500 503<xml-fragment/>http://localhost:8080
SEQUENTIAL<xml-fragment/>http://localhost:8080authfalsefalsetokenfalsefalseuserfalsefalse - - - -<xml-fragment/>http://localhost:8080unauthorizedfalsefalse401falsefalse - - - -<xml-fragment/>http://localhost:8080unauthorizedfalsefalse401falsefalse - - - -<xml-fragment/>http://localhost:8080unauthorizedfalsefalse401falsefalse - - - -<xml-fragment/>http://localhost:8080forbiddenfalsefalse403falsefalse - - - -<xml-fragment/>http://localhost:8080userDisabledfalsefalse403falsefalse - - - -<xml-fragment/>http://localhost:8080authfalsefalsetokenfalsefalseuserfalsefalse - - - -<xml-fragment/>http://localhost:8080authfalsefalsetokenfalsefalseuserfalsefalse - - -<xml-fragment/>http://localhost:8080itemNotFoundfalsefalse404falsefalse - - - -<xml-fragment/>http://localhost:8080itemNotFoundfalsefalse404falsefalse - - -<xml-fragment/>http://localhost:8080itemNotFoundfalsefalse404falsefalse - - -<xml-fragment/>http://localhost:8080<passwordCredentials password="P@ssword1" username="testuser" xmlns="http://docs.openstack.org/identity/api/v2.0"/>401falsefalseunauthorizedfalsefalse<xml-fragment/>http://localhost:8080<passwordCredentials password="1234" username="disabled" xmlns="http://docs.openstack.org/identity/api/v2.0"/>403falsefalseuserDisabledfalsefalse<xml-fragment/>http://localhost:8080<passwordCredentials password="123774" username="joeuser" xmlns="http://docs.openstack.org/identity/api/v2.0"/>401falsefalseunauthorizedfalsefalse<xml-fragment/>http://localhost:8080<passwordCredentials password="secrete" username="admin" xmlns="http://docs.openstack.org/identity/api/v2.0"/>userfalsefalsetokenfalsefalseAdminfalsefalsedeclare namespace auth='http://docs.openstack.org/identity/api/v2.0'; -/auth:auth/auth:user/auth:groups/auth:group/@id='Admin'truefalsefalse<xml-fragment/>http://localhost:8080<passwordCredentials password="secrete" username="joeuser" xmlns="http://docs.openstack.org/identity/api/v2.0"/>userfalsefalsetokenfalsefalseAdminfalsefalsedeclare namespace auth='http://docs.openstack.org/identity/api/v2.0'; -/auth:auth/auth:user/auth:groups/auth:group/@id='Admin'falsefalsefalse<xml-fragment/>http://localhost:8080{ - "passwordCredentials" : { - "username" : "testuser", - "password" : "P@ssword1" - } -}401falsefalseunauthorizedfalsefalse<xml-fragment/>http://localhost:8080{ - "passwordCredentials" : { - "username" : "disabled", - "password" : "1234" - } -}403falsefalseuserDisabledfalsefalse<xml-fragment/>http://localhost:8080{ - "passwordCredentials" : { - "username" : "joeuser", - "password" : "123774" - } -}401falsefalseunauthorizedfalsefalse<xml-fragment/>http://localhost:8080{ - "passwordCredentials" : { - "username" : "admin", - "password" : "secrete" - } -}userfalsefalsetokenfalsefalseAdminfalsefalse<xml-fragment/>http://localhost:8080{ - "passwordCredentials" : { - "username" : "joeuser", - "password" : "secrete" - } -}userfalsefalsetokenfalsefalseAdminfalsefalse<entry key="Accept" value="application/xml" xmlns="http://eviware.com/soapui/config"/>http://localhost:8080404falsefalseitemNotFoundfalsefalse - - -<xml-fragment/>http://localhost:8080<passwordCredentials password="secrete" username="joeuser" xmlns="http://docs.openstack.org/identity/api/v2.0"/>userfalsefalsetokenfalsefalseAdminfalsefalsedeclare namespace auth='http://docs.openstack.org/identity/api/v2.0'; -/auth:auth/auth:user/auth:groups/auth:group/@id='Admin'falsefalsefalsedeclare namespace auth='http://docs.openstack.org/identity/api/v2.0'; -/auth:auth/auth:token/@id887665443383838falsefalse<entry key="Accept" value="application/xml" xmlns="http://eviware.com/soapui/config"/>http://localhost:8080assert(context.response==null) - - -<xml-fragment/>http://localhost:8080itemNotFoundfalsefalse404falsefalse - - -<xml-fragment/>http://localhost:8080<passwordCredentials password="secrete" username="joeuser" xmlns="http://docs.openstack.org/identity/api/v2.0"/>userfalsefalsetokenfalsefalseAdminfalsefalsedeclare namespace auth='http://docs.openstack.org/identity/api/v2.0'; -/auth:auth/auth:user/auth:groups/auth:group/@id='Admin'falsefalsefalsedeclare namespace auth='http://docs.openstack.org/identity/api/v2.0'; -/auth:auth/auth:token/@id="887665443383838"falsefalsefalse<xml-fragment/>http://localhost:8080<v1:tenant enabled="true" id="my_new_tenant" xmlns:v1="http://docs.openstack.org/identity/api/v2.0"><v1:description>This is a description of my tenant. Thank you very much.</v1:description></v1:tenant>declare namespace ns1='http://docs.openstack.org/identity/api/v2.0'; -/ns1:tenant/@enabled = "true" and /ns1:tenant/@id="my_new_tenant" and /ns1:tenant/ns1:description = "This is a description of my tenant. Thank you very much."truefalsefalse<xml-fragment/>http://localhost:8080<v1:tenant enabled="true" id="my_new_tenant" xmlns:v1="http://docs.openstack.org/identity/api/v2.0"><v1:description>This is a description of my tenant. Thank you very much.</v1:description></v1:tenant>tenantConflictfalsefalse409falsefalse<xml-fragment/>http://localhost:8080<v1:tenant enabled="false" id="mt2" xmlns:v1="http://docs.openstack.org/identity/api/v2.0"><v1:description>New Disabled Tenant</v1:description></v1:tenant>declare namespace ns1='http://docs.openstack.org/identity/api/v2.0'; -/ns1:tenant/@enabled = "false" and /ns1:tenant/@id="mt2" and /ns1:tenant/ns1:description = "New Disabled Tenant"truefalsefalse<xml-fragment/>http://localhost:8080<v1:tenant id="mt3" xmlns:v1="http://docs.openstack.org/identity/api/v2.0"><v1:description>New Tenant 3</v1:description></v1:tenant>declare namespace ns1='http://docs.openstack.org/identity/api/v2.0'; -/ns1:tenant/@id="mt3" and /ns1:tenant/ns1:description = "New Tenant 3"truefalsefalse<xml-fragment/>http://localhost:8080<v1:tenant enabled="true" xmlns:v1="http://docs.openstack.org/identity/api/v2.0"><v1:description>New Tenant No ID</v1:description></v1:tenant>400falsefalsebadRequestfalsefalse<xml-fragment/>http://localhost:8080<v1:tenant enabled="true" id="my_new_tenant" xmlns:v1="http://docs.openstack.org/identity/api/v2.0"></v1:tenant>400falsefalsebadRequestfalsefalse<xml-fragment/>http://localhost:8080{"tenant": - { - "id": "JGroup", - "description": "A description ...", - "enabled": true - } -} -declare namespace ns1='http://localhost/v1.0/tenants'; -ns1:Response/ns1:tenant/ns1:id="JGroup" and ns1:Response/ns1:tenant/ns1:enabled="true" and ns1:Response/ns1:tenant/ns1:description="A description ..."truefalsefalse<xml-fragment/>http://localhost:8080{"tenant": - { - "id": "JGroup", - "description": "A description ...", - "enabled": true - } -}tenantConflictfalsefalse409falsefalse<xml-fragment/>http://localhost:8080{"tenant": - { - "id": "JGroup33", - "description": "A description...", - "enabled": false - } -}declare namespace ns1='http://localhost/v1.0/tenants'; -ns1:Response/ns1:tenant/ns1:id = "JGroup33" and ns1:Response/ns1:tenant/ns1:enabled="false" and ns1:Response/ns1:tenant/ns1:description="A description..."truefalsefalse<xml-fragment/>http://localhost:8080{"tenant": - { - "id": "JGroup65", - "description": "A description..." - } -}declare namespace ns1='http://localhost/v1.0/tenants'; -ns1:Response/ns1:tenant/ns1:id = "JGroup65" and ns1:Response/ns1:tenant/ns1:description = "A description..."truefalsefalse<xml-fragment/>http://localhost:8080{"tenant": - { - "description": "A description...", - "enabled" : true - } -}400falsefalsebadRequestfalsefalse<xml-fragment/>http://localhost:8080{"tenant": - { - "id": "JGroup95", - "enabled": true - } -}400falsefalsebadRequestfalsefalse<xml-fragment/>http://localhost:8080{"tenant": - { - "id": "JGroup95", - "description" : "A description...", - "enabled": "true" - } -}400falsefalsebadRequestfalsefalse<xml-fragment/>http://localhost:8080declare namespace ns1='http://docs.openstack.org/identity/api/v2.0'; -count(//ns1:tenant)8falsefalse<xml-fragment/>http://localhost:8080declare namespace ns1='http://localhost/v1.0/tenants'; -count(//ns1:e)8falsefalse<xml-fragment/>http://localhost:8080declare namespace ns1='http://docs.openstack.org/identity/api/v2.0'; -ns1:tenant/@id1234falsefalsedeclare namespace ns1='http://docs.openstack.org/identity/api/v2.0'; -/ns1:tenant/@enabled and /ns1:tenant/ns1:descriptiontruefalsefalse - - -<xml-fragment/>http://localhost:8080declare namespace ns1='http://localhost/v1.0/tenants/1234'; -ns1:Response/ns1:tenant/ns1:id1234falsefalsedeclare namespace ns1='http://localhost/v1.0/tenants/1234'; -/ns1:Response/ns1:tenant/ns1:enabled and /ns1:Response/ns1:tenant/ns1:descriptiontruefalsefalse - - -<xml-fragment/>http://localhost:8080404falsefalseitemNotFoundfalsefalse - - -<xml-fragment/>http://localhost:8080<v1:tenant enabled="true" id="to_delete" - xmlns:v1="http://docs.openstack.org/identity/api/v2.0"> - <v1:description>To Be Deleted</v1:description> -</v1:tenant>declare namespace ns1='http://docs.openstack.org/identity/api/v2.0'; -/ns1:tenant/@enabled = "true" and /ns1:tenant/@id="to_delete" and /ns1:tenant/ns1:description = "To Be Deleted"truefalsefalse<xml-fragment/>http://localhost:8080declare namespace ns1='http://docs.openstack.org/identity/api/v2.0'; -ns1:tenant/@idto_deletefalsefalsedeclare namespace ns1='http://docs.openstack.org/identity/api/v2.0'; -/ns1:tenant/@enabled and /ns1:tenant/ns1:descriptiontruefalsefalse - - -<entry key="Accept" value="application/xml" xmlns="http://eviware.com/soapui/config"/>http://localhost:8080assert(context.response == null) - - -<xml-fragment/>http://localhost:8080404falsefalseitemNotFoundfalsefalse - - -<entry key="Accept" value="application/xml" xmlns="http://eviware.com/soapui/config"/>http://localhost:8080forbiddenfalsefalse403falsefalse - - -<entry key="Accept" value="application/xml" xmlns="http://eviware.com/soapui/config"/>http://localhost:8080forbiddenfalsefalse403falsefalse - - -<xml-fragment/>http://localhost:8080<v1:tenant enabled="true" id="to_update" xmlns:v1="http://docs.openstack.org/identity/api/v2.0"> - <v1:description>ToUpdate</v1:description> -</v1:tenant>declare namespace ns1='http://docs.openstack.org/identity/api/v2.0'; -/ns1:tenant/@enabled = "true" and /ns1:tenant/@id="to_update" and /ns1:tenant/ns1:description = "ToUpdate"truefalsefalse<xml-fragment/>http://localhost:8080declare namespace ns1='http://docs.openstack.org/identity/api/v2.0'; -/ns1:tenant/@id="to_update" and /ns1:tenant/@enabled = "true" and /ns1:tenant/@id="to_update" and /ns1:tenant/ns1:description = "ToUpdate"truefalsefalse - - -<xml-fragment/>http://localhost:8080<v1:tenant enabled="true" xmlns:v1="http://docs.openstack.org/identity/api/v2.0"> - <v1:description>ToUpdate2</v1:description> -</v1:tenant>declare namespace ns1='http://docs.openstack.org/identity/api/v2.0'; -/ns1:tenant/@id="to_update" and /ns1:tenant/@enabled = "true" and /ns1:tenant/ns1:description = "ToUpdate2"truefalsefalse - - -<xml-fragment/>http://localhost:8080declare namespace ns1='http://docs.openstack.org/identity/api/v2.0'; -/ns1:tenant/@id="to_update" and /ns1:tenant/@enabled = "true" and /ns1:tenant/@id="to_update" and /ns1:tenant/ns1:description = "ToUpdate2"truefalsefalse - - -<xml-fragment/>http://localhost:8080<v1:tenant enabled="false" xmlns:v1="http://docs.openstack.org/identity/api/v2.0"> - <v1:description>ToUpdate2</v1:description> -</v1:tenant>declare namespace ns1='http://docs.openstack.org/identity/api/v2.0'; -/ns1:tenant/@id="to_update" and /ns1:tenant/@enabled = "false" and /ns1:tenant/ns1:description = "ToUpdate2"truefalsefalse - - -<xml-fragment/>http://localhost:8080declare namespace ns1='http://docs.openstack.org/identity/api/v2.0'; -/ns1:tenant/@id="to_update" and /ns1:tenant/@enabled = "false" and /ns1:tenant/@id="to_update" and /ns1:tenant/ns1:description = "ToUpdate2"truefalsefalse - - -<xml-fragment/>http://localhost:8080<v1:tenant id="boogabooga" enabled="false" xmlns:v1="http://docs.openstack.org/identity/api/v2.0"> - <v1:description>ToUpdate2</v1:description> -</v1:tenant>declare namespace ns1='http://docs.openstack.org/identity/api/v2.0'; -/ns1:tenant/@id="to_update" and /ns1:tenant/@enabled = "false" and /ns1:tenant/ns1:description = "ToUpdate2"truefalsefalse - - -<xml-fragment/>http://localhost:8080declare namespace ns1='http://docs.openstack.org/identity/api/v2.0'; -/ns1:tenant/@id="to_update" and /ns1:tenant/@enabled = "false" and /ns1:tenant/@id="to_update" and /ns1:tenant/ns1:description = "ToUpdate2"truefalsefalse - - -<xml-fragment/>http://localhost:8080<v1:tenant enabled="true" xmlns:v1="http://docs.openstack.org/identity/api/v2.0"> - <v1:description>ToUpdate3</v1:description> -</v1:tenant>declare namespace ns1='http://docs.openstack.org/identity/api/v2.0'; -/ns1:tenant/@id="to_update" and /ns1:tenant/@enabled = "true" and /ns1:tenant/ns1:description = "ToUpdate3"truefalsefalse - - -<xml-fragment/>http://localhost:8080declare namespace ns1='http://docs.openstack.org/identity/api/v2.0'; -/ns1:tenant/@id="to_update" and /ns1:tenant/@enabled = "true" and /ns1:tenant/@id="to_update" and /ns1:tenant/ns1:description = "ToUpdate3"truefalsefalse - - -<entry key="Accept" value="application/xml" xmlns="http://eviware.com/soapui/config"/>http://localhost:8080assert(context.response == null) - - -<xml-fragment/>http://localhost:8080declare namespace ns1='http://docs.openstack.org/common/api/v1.0'; -count(/ns1:extensions)1falsefalse<xml-fragment/>http://localhost:8080declare namespace ns1='http://localhost/v1.0/extensions'; -count(//ns1:extensions)1falsefalse<xml-fragment/>http://localhost:8080404falsefalseitemNotFoundfalsefalse<xml-fragment/>http://localhost:8080404falsefalseitemNotFoundfalsefalse<xml-fragment/>http://localhost:8080declare namespace ns1='http://docs.openstack.org/common/api/v1.0'; -count(//ns1:version)1falsefalse<xml-fragment/>http://localhost:8080declare namespace ns1='http://localhost/v1.0'; -count(//ns1:version)1falsefalse
\ No newline at end of file diff --git a/keystone/test/__init__.py b/keystone/test/__init__.py deleted file mode 100644 index 26c4d9b34a..0000000000 --- a/keystone/test/__init__.py +++ /dev/null @@ -1,723 +0,0 @@ -# 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. -# -# Code copied from Nova and other OpenStack projects: -# Colorizers -# Classes starting with Nova -# other setup and initialization code -# -""" Module that handles starting the Keystone server and running -test suites""" - -import heapq -import logging -from nose import config as noseconfig -from nose import core -from nose import result -import optparse -import os -import sys -import tempfile -import time -import unittest - -import keystone -import keystone.server -import keystone.version -from keystone import config as config_module -from keystone.common import config -from keystone.test import utils -from keystone.test import client as client_tests -from keystone import utils as main_utils - -TEST_DIR = os.path.abspath(os.path.dirname(__file__)) -BASE_DIR = os.path.abspath(os.path.join(TEST_DIR, os.pardir, os.pardir)) -TEST_CERT = os.path.join(BASE_DIR, 'examples/ssl/certs/middleware-key.pem') - -logger = logging.getLogger(__name__) - -CONF = config_module.CONF - - -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 'red' - elif elapsed_time > 0.25: - return 'yellow' - else: - return 'green' - - -class NovaTestResult(result.TextTestResult): - def __init__(self, *args, **kw): - self.show_elapsed = kw.pop('show_elapsed') - result.TextTestResult.__init__(self, *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 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): - 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): - 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. - """ - 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) - - def startTest(self, test): - unittest.TestResult.startTest(self, test) - self.start_time = time.time() - current_case = test.test.__class__.__name__ - - if self.showAll: - if current_case != self._last_case: - self.stream.writeln(current_case) - self._last_case = current_case - - self.stream.write( - ' %s' % str(test.test._testMethodName).ljust(60)) - self.stream.flush() - - -class NovaTestRunner(core.TextTestRunner): - def __init__(self, *args, **kwargs): - self.show_elapsed = kwargs.pop('show_elapsed') - core.TextTestRunner.__init__(self, *args, **kwargs) - - def _makeResult(self): - return NovaTestResult(self.stream, - self.descriptions, - self.verbosity, - self.config, - show_elapsed=self.show_elapsed) - - 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 run(self, test): - result_ = core.TextTestRunner.run(self, test) - if self.show_elapsed: - self._writeSlowTests(result_) - return result_ - - -class KeystoneTest(object): - """Primary test class for invoking keystone tests. Controls - initialization of environment with temporary configuration files, - starts keystone admin and service API WSIG servers, and then uses - :py:mod:`unittest2` to discover and iterate over existing tests. - - :py:class:`keystone.test.KeystoneTest` is expected to be - subclassed and invoked in ``run_tests.py`` where subclasses define - a config_name (that matches a template existing in - ``keystone/test/etc``) and test_files (that are cleared at the - end of test execution from the temporary space used to run these - tests). - """ - config_params = {'test_dir': TEST_DIR, 'base_dir': BASE_DIR} - isSsl = False - hpidmDisabled = False - config_name = None - test_files = None - server = None - admin_server = None - conf_fp = None - directory_base = None - - def clear_database(self): - """Remove any test databases or files generated by previous tests.""" - if self.test_files: - for fname in self.test_files: - paths = [os.path.join(os.curdir, fname), - os.path.join(os.getcwd(), fname), - os.path.join(TEST_DIR, fname)] - for fpath in paths: - if os.path.exists(fpath): - logger.debug("Removing test file %s" % fname) - os.unlink(fpath) - - def construct_temp_conf_file(self): - """Populates a configuration template, and writes to a file pointer.""" - template_fpath = os.path.join(TEST_DIR, 'etc', self.config_name) - conf_contents = open(template_fpath).read() - self.config_params['service_port'] = utils.get_unused_port() - logger.debug("Assigned port %s to service" % - self.config_params['service_port']) - self.config_params['admin_port'] = utils.get_unused_port() - logger.debug("Assigned port %s to admin" % - self.config_params['admin_port']) - - conf_contents = conf_contents % self.config_params - self.conf_fp = tempfile.NamedTemporaryFile() - self.conf_fp.write(conf_contents) - self.conf_fp.flush() - logger.debug("Create test configuration file: %s" % self.conf_fp.name) - client_tests.TEST_CONFIG_FILE_NAME = self.conf_fp.name - - def setUp(self): - pass - - def startServer(self): - """ Starts a Keystone server on random ports for testing """ - self.server = None - self.admin_server = None - - self.construct_temp_conf_file() - - # Set client certificate for test client - if self.isSsl: - logger.debug("SSL testing will use cert_file %s" % TEST_CERT) - os.environ['cert_file'] = TEST_CERT - else: - if 'cert_file' in os.environ: - del os.environ['cert_file'] - - # indicating HP-IDM is disabled - if self.hpidmDisabled: - logger.debug("HP-IDM extensions is disabled") - os.environ['HP-IDM_Disabled'] = 'True' - else: - if 'HP-IDM_Disabled' in os.environ: - del os.environ['HP-IDM_Disabled'] - - # run the keystone server - logger.info("Starting the keystone server...") - - class SilentOptParser(optparse.OptionParser): - """ Class used to prevent OptionParser from exiting when it detects - options coming in for nose/testing """ - def exit(): - pass - - def error(self, msg): - pass - - parser = SilentOptParser(version='%%prog %s' % - keystone.version.version()) - common_group = config.add_common_options(parser) - config.add_log_options(parser) - - # Handle a special argument to support starting two endpoints - common_group.add_option( - '-a', '--admin-port', dest="admin_port", metavar="PORT", - help="specifies port for Admin API to listen " - "on (default is 35357)") - - # Parse arguments and load config - (options, args) = config.parse_options(parser) - options['config_file'] = self.conf_fp.name - - # Populate the CONF module - CONF.reset() - CONF(config_files=[self.conf_fp.name]) - - try: - # Load Service API Server - service = keystone.server.Server(name="Service API", - config_name='keystone-legacy-auth', - args=args) - service.start(wait=False) - - # Client tests will use these globals to find out where - # the server is - client_tests.TEST_TARGET_SERVER_SERVICE_PROTOCOL = service.protocol - client_tests.TEST_TARGET_SERVER_SERVICE_ADDRESS = service.host - client_tests.TEST_TARGET_SERVER_SERVICE_PORT = service.port - - except RuntimeError, e: - logger.exception(e) - raise e - - try: - # Load Admin API server - port = int(CONF.admin_port or - client_tests.TEST_TARGET_SERVER_ADMIN_PORT) - host = (CONF.admin_host or - client_tests.TEST_TARGET_SERVER_ADMIN_ADDRESS) - admin = keystone.server.Server(name='Admin API', - config_name='admin', args=args) - admin.start(host=host, port=port, wait=False) - - # Client tests will use these globals to find out where - # the server is - client_tests.TEST_TARGET_SERVER_ADMIN_PROTOCOL = admin.protocol - client_tests.TEST_TARGET_SERVER_ADMIN_ADDRESS = admin.host - client_tests.TEST_TARGET_SERVER_ADMIN_PORT = admin.port - - except RuntimeError, e: - logger.exception(e) - raise e - - self.server = service - self.admin_server = admin - - # Load bootstrap data - from keystone import manage - manage_args = ['--config-file', self.conf_fp.name] - manage.parse_args(args=manage_args) - - #TODO(zns): this should end up being run by a 'bootstrap' script - fixtures = [ - ('role', 'add', CONF.keystone_admin_role), - ('user', 'add', 'admin', 'secrete'), - ('role', 'grant', CONF.keystone_admin_role, 'admin'), - ('role', 'add', CONF.keystone_service_admin_role), - ('role', 'add', 'Member'), - ] - for cmd in fixtures: - manage.process(*cmd) - - def tearDown(self): - try: - if self.server is not None: - print "Stopping the Service API..." - self.server.stop() - self.server = None - if self.admin_server is not None: - print "Stopping the Admin API..." - self.admin_server.stop() - self.admin_server = None - if self.conf_fp: - self.conf_fp.close() - self.conf_fp = None - except Exception as e: - logger.exception(e) - print "Error cleaning up %s" % e - raise e - finally: - self.clear_database() - if 'cert_file' in os.environ: - del os.environ['cert_file'] - if 'HP-IDM_Disabled' in os.environ: - del os.environ['HP-IDM_Disabled'] - reload(client_tests) - - def run(self, args=None): - try: - print 'Running test suite: %s' % self.__class__.__name__ - - self.setUp() - - # discover and run tests - - # If any argument looks like a test name but doesn't have - # "keystone.test" in front of it, automatically add that so we - # don't have to type as much - show_elapsed = True - argv = [] - if args is None: - args = sys.argv - has_base = False - for x in args: - if x.startswith(('functional', 'unit', 'client')): - argv.append('keystone.test.%s' % x) - has_base = True - elif x.startswith('--hide-elapsed'): - show_elapsed = False - elif x.startswith('-'): - argv.append(x) - else: - argv.append(x) - if x != args[0]: - has_base = True - - if not has_base and self.directory_base is not None: - argv.append(self.directory_base) - argv = ['--no-path-adjustment'] + argv[1:] - logger.debug("Running set of tests with args=%s" % argv) - - c = noseconfig.Config(stream=sys.stdout, - env=os.environ, - verbosity=3, - workingDir=TEST_DIR, - plugins=core.DefaultPluginManager(), - args=argv) - - runner = NovaTestRunner(stream=c.stream, - verbosity=c.verbosity, - config=c, - show_elapsed=show_elapsed) - - result = not core.run(config=c, testRunner=runner, argv=argv) - return int(result) # convert to values applicable to sys.exit() - except Exception, exc: - logger.exception(exc) - raise exc - finally: - self.tearDown() - - -def runtests(): - """This function can be called from 'python setup.py test'.""" - return SQLTest().run() - - -class UnitTests(KeystoneTest): - """ Class that runs unit tests """ - directory_base = 'unit' - - def run(self): - """ Run unit tests - - Filters arguments and leaves only ones relevant to unit tests - """ - - argv = [] - scoped_to_unit = False - for x in sys.argv: - if x.startswith(('functional', 'client')): - # Skip, since we're not running unit tests - return - elif x.startswith('unit'): - argv.append('keystone.test.%s' % x) - scoped_to_unit = True - else: - argv.append(x) - - if not scoped_to_unit: - argv.append('keystone.test.unit') - - return super(UnitTests, self).run(args=argv) - - -class ClientTests(KeystoneTest): - """ Class that runs client tests - - Client tests are the tests that need a running http[s] server running - and make web service calls to that server - - """ - config_name = 'sql.conf.template' - directory_base = 'client' - - def run(self): - """ Run client tests - - Filters arguments and leaves only ones relevant to client tests - """ - - argv = [] - scoped_to_client = False - for x in sys.argv: - if x.startswith(('functional', 'unit')): - # Skip, since we're not running client tests - return - elif x.startswith('client'): - argv.append('keystone.test.%s' % x) - scoped_to_client = True - else: - argv.append(x) - - if not scoped_to_client: - argv.append('keystone.test.client') - - self.startServer() - - return super(ClientTests, self).run(args=argv) - - -class SQLTest(KeystoneTest): - """Test defined using only SQLAlchemy back-end""" - config_name = 'sql.conf.template' - test_files = ('keystone.sqltest.db',) - directory_base = 'functional' - - def run(self): - """ Run client tests - - Filters arguments and leaves only ones relevant to client tests - """ - - argv = [] - scoped_to_functional = False - for x in sys.argv: - if x.startswith(('client', 'unit')): - # Skip, since we're not running functional tests - return - elif x.startswith('functional'): - argv.append('keystone.test.%s' % x) - scoped_to_functional = True - else: - argv.append(x) - - if not scoped_to_functional: - argv.append('keystone.test.functional') - - return super(SQLTest, self).run(args=argv) - - def clear_database(self): - # Disconnect the database before deleting - from keystone.backends import sqlalchemy - sqlalchemy.unregister_models() - - super(SQLTest, self).clear_database() - - -class SSLTest(ClientTests): - config_name = 'ssl.conf.template' - isSsl = True - test_files = ('keystone.ssltest.db',) - - -class MemcacheTest(SQLTest): - """Test defined using only SQLAlchemy and Memcache back-end""" - config_name = 'memcache.conf.template' - test_files = ('keystone.memcachetest.db',) - - -class LDAPTest(SQLTest): - """Test defined using only SQLAlchemy and LDAP back-end""" - config_name = 'ldap.conf.template' - test_files = ('keystone.ldaptest.db', 'ldap.db', 'ldap.db.db',) - - def clear_database(self): - super(LDAPTest, self).clear_database() - from keystone.backends.ldap.fakeldap import FakeShelve - db = FakeShelve().get_instance() - db.clear() - - -class ClientWithoutHPIDMTest(ClientTests): - """Test with HP-IDM disabled to make sure it is backward compatible""" - config_name = 'sql_no_hpidm.conf.template' - hpidmDisabled = True - test_files = ('keystone.nohpidm.db',) diff --git a/keystone/test/client/__init__.py b/keystone/test/client/__init__.py deleted file mode 100644 index 99f6188514..0000000000 --- a/keystone/test/client/__init__.py +++ /dev/null @@ -1,39 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 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. - -""" Client Tests - -Client tests are tests that use HTTP(S) calls to a Keystone server to exercise -request/response test cases. - -In order to avoid port conflicts, client tests use the global settings below -to know which server to talk to. - -When a server is started for testing purposes (usually by the -keystone.test.KeystoneTest class) it will update these values so client tests -know where to find the server - -""" -TEST_TARGET_SERVER_ADMIN_PROTOCOL = 'http' -TEST_TARGET_SERVER_ADMIN_ADDRESS = '127.0.0.1' -TEST_TARGET_SERVER_ADMIN_PORT = 35357 - -TEST_TARGET_SERVER_SERVICE_PROTOCOL = 'http' -TEST_TARGET_SERVER_SERVICE_ADDRESS = '127.0.0.1' -TEST_TARGET_SERVER_SERVICE_PORT = 5000 - -TEST_CONFIG_FILE_NAME = None diff --git a/keystone/test/client/test_client.py b/keystone/test/client/test_client.py deleted file mode 100644 index 6e95d4439f..0000000000 --- a/keystone/test/client/test_client.py +++ /dev/null @@ -1,101 +0,0 @@ -import unittest - -import keystone.common.exception -import keystone.client -from keystone.test.functional.common import isSsl -from keystone.test import client as client_tests - - -class TestAdminClient(unittest.TestCase): - """ - Quick functional tests for the Keystone HTTP admin client. - """ - use_server = True - - def setUp(self): - """ - Run before each test. - """ - cert_file = isSsl() - self.client = keystone.client.AdminClient( - client_tests.TEST_TARGET_SERVER_ADMIN_ADDRESS, - port=client_tests.TEST_TARGET_SERVER_ADMIN_PORT, - is_ssl=(cert_file is not None), - cert_file=cert_file, - admin_name="admin", - admin_pass="secrete") - - def test_admin_validate_token(self): - """ - Test that our admin token is valid. (HTTP GET) - """ - token = self.client.admin_token - result = self.client.validate_token(token) - self.assertEquals("admin", - result["access"]["user"]["name"]) - - def test_admin_check_token(self): - """ - Test that our admin token is valid. (HTTP HEAD) - """ - token = self.client.admin_token - self.assertTrue(self.client.check_token(token)) - - def test_admin_validate_token_fail(self): - """ - Test that validating an invalid token results in None. (HTTP GET) - """ - token = "bad_token" - self.assertTrue(self.client.validate_token(token) is None) - - def test_admin_check_token_fail(self): - """ - Test that checking an invalid token results in False. (HTTP HEAD) - """ - token = "bad_token" - self.assertFalse(self.client.check_token(token)) - - def test_admin_get_token(self): - """ - Test that we can generate a token given correct credentials. - """ - token = self.client.get_token("admin", "secrete") - self.assertEquals(self.client.admin_token, token) - - def test_admin_get_token_bad_auth(self): - """ - Test incorrect credentials generates a client error. - """ - self.assertRaises(keystone.common.exception.ClientError, - self.client.get_token, "bad_user", "bad_pass") - - -class TestServiceClient(unittest.TestCase): - """ - Quick functional tests for the Keystone HTTP service client. - """ - - def setUp(self): - """ - Run before each test. - """ - cert_file = isSsl() - self.client = keystone.client.ServiceClient( - client_tests.TEST_TARGET_SERVER_SERVICE_ADDRESS, - port=client_tests.TEST_TARGET_SERVER_SERVICE_PORT, - is_ssl=(cert_file is not None), - cert_file=cert_file) - - def test_admin_get_token(self): - """ - Test that we can generate a token given correct credentials. - """ - token = self.client.get_token("admin", "secrete") - self.assertTrue(36, len(token)) - - def test_admin_get_token_bad_auth(self): - """ - Test incorrect credentials generates a client error. - """ - self.assertRaises(keystone.common.exception.ClientError, - self.client.get_token, "bad_user", "bad_pass") diff --git a/keystone/test/client/test_d5_compat_calls.py b/keystone/test/client/test_d5_compat_calls.py deleted file mode 100644 index 865bc46d80..0000000000 --- a/keystone/test/client/test_d5_compat_calls.py +++ /dev/null @@ -1,169 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 2010-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. - - -import unittest2 as unittest - -from keystone.test.functional import common - - -class D5_AuthenticationTest(common.FunctionalTestCase): - """ Tests the functionality of the D5 compat module """ - use_server = True - - def setUp(self, *args, **kwargs): - super(D5_AuthenticationTest, self).setUp(*args, **kwargs) - - password = common.unique_str() - self.tenant = self.create_tenant().json['tenant'] - self.user = self.create_user(user_password=password, - tenant_id=self.tenant['id']).json['user'] - self.user['password'] = password - - self.services = {} - self.endpoint_templates = {} - self.services = self.fixture_create_service() - self.endpoint_templates = self.create_endpoint_template( - name=self.services['name'], - type=self.services['type']).\ - json['OS-KSCATALOG:endpointTemplate'] - self.create_endpoint_for_tenant(self.tenant['id'], - self.endpoint_templates['id']) - - def test_validate_unscoped_token(self): - """Admin should be able to validate a user's token""" - # Authenticate as user to get a token - self.service_token = self.post_token(as_json={ - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password']}}).\ - json['auth']['token']['id'] - - # In the real world, the service user would then pass his/her token - # to some service that depends on keystone, which would then need to - # use keystone to validate the provided token. - - # Admin independently validates the user token - r = self.get_token(self.service_token) - self.assertEqual(r.json['auth']['token']['id'], self.service_token) - self.assertTrue(r.json['auth']['token']['expires']) - self.assertEqual(r.json['auth']['user']['username'], - self.user['name']) - self.assertEqual(r.json['auth']['user']['roleRefs'], []) - - def test_validate_scoped_token(self): - """Admin should be able to validate a user's scoped token""" - # Authenticate as user to get a token - self.service_token = self.post_token(as_json={ - 'passwordCredentials': { - 'tenantId': self.tenant['id'], - 'username': self.user['name'], - 'password': self.user['password']}}).\ - json['auth']['token']['id'] - - # In the real world, the service user would then pass his/her token - # to some service that depends on keystone, which would then need to - # use keystone to validate the provided token. - - # Admin independently validates the user token - r = self.get_token(self.service_token) - self.assertEqual(r.json['auth']['token']['id'], self.service_token) - self.assertEqual(r.json['auth']['token']['tenantId'], - self.tenant['id']) - self.assertTrue(r.json['auth']['token']['expires']) - self.assertEqual(r.json['auth']['user']['username'], - self.user['name']) - self.assertEqual(r.json['auth']['user']['roleRefs'], []) - - def test_authenticate_for_a_tenant(self): - r = self.authenticate_D5(self.user['name'], self.user['password'], - self.tenant['id'], assert_status=200) - - self.assertIsNotNone(r.json['auth']['token']) - service_catalog = r.json['auth']['serviceCatalog'] - self.assertIsNotNone(service_catalog) - self.check_urls_for_regular_user(service_catalog) - - def test_authenticate_for_a_tenant_xml(self): - data = (' ' - '') % ( - self.xmlns, self.tenant['id'], - self.user['name'], self.user['password']) - r = self.post_token(as_xml=data, assert_status=200) - - self.assertEquals(r.xml.tag, '{%s}auth' % self.xmlns) - service_catalog = r.xml.find('{%s}serviceCatalog' % self.xmlns) - self.check_urls_for_regular_user_xml(service_catalog) - - def test_authenticate_for_a_tenant_on_admin_api(self): - r = self.authenticate_D5(self.user['name'], self.user['password'], - self.tenant['id'], assert_status=200, request_type='admin') - - self.assertIsNotNone(r.json['auth']['token']) - self.assertIsNotNone(r.json['auth']['serviceCatalog']) - service_catalog = r.json['auth']['serviceCatalog'] - self.check_urls_for_regular_user(service_catalog) - - def test_authenticate_for_a_tenant_xml_on_admin_api(self): - data = (' ' - '') % ( - self.xmlns, self.tenant['id'], - self.user['name'], self.user['password']) - r = self.post_token(as_xml=data, assert_status=200, - request_type='admin') - - self.assertEquals(r.xml.tag, '{%s}auth' % self.xmlns) - service_catalog = r.xml.find('{%s}serviceCatalog' % self.xmlns) - self.check_urls_for_regular_user_xml(service_catalog) - - def test_authenticate_user_disabled(self): - self.disable_user(self.user['id']) - self.authenticate_D5(self.user['name'], self.user['password'], - self.tenant['id'], assert_status=403) - - def test_authenticate_user_wrong(self): - data = {"passwordCredentials": { - "username-field-completely-wrong": self.user['name'], - "password": self.user['password'], - "tenantId": self.tenant['id']}} - self.post_token(as_json=data, assert_status=400) - - def test_authenticate_user_wrong_xml(self): - data = (' ' - '') % ( - self.user['name'], self.user['password'], self.tenant['id']) - - self.post_token(as_xml=data, assert_status=400) - - def check_urls_for_regular_user(self, service_catalog): - self.assertIsNotNone(service_catalog) - for k in service_catalog.keys(): - endpoints = service_catalog[k] - for endpoint in endpoints: - for key in endpoint: - #Checks whether adminURL is not present. - self.assertNotEquals(key, 'adminURL') - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/client/test_extensions.py b/keystone/test/client/test_extensions.py deleted file mode 100644 index bb5d40a81f..0000000000 --- a/keystone/test/client/test_extensions.py +++ /dev/null @@ -1,68 +0,0 @@ -import os -import unittest2 as unittest -from keystone.test.functional import common - - -class TestExtensions(common.FunctionalTestCase): - use_server = True - - def test_extensions_json(self): - r = self.service_request(path='/extensions.json') - self.assertTrue('json' in r.getheader('Content-Type')) - content = r.json - self.assertIsNotNone(content['extensions']) - self.assertIsNotNone(content['extensions']['values']) - - def test_extensions_xml(self): - r = self.service_request(path='/extensions.xml') - self.assertTrue('xml' in r.getheader('Content-Type')) - - -class TestAdminExtensions(common.ApiTestCase): - use_server = True - - def test_extensions_json(self): - r = self.admin_request(path='/extensions.json') - self.assertTrue('json' in r.getheader('Content-Type')) - content = r.json - self.assertIsNotNone(content['extensions']) - self.assertIsNotNone(content['extensions']['values']) - found_osksadm = False - found_oskscatalog = False - found_hpidm = False - for value in content['extensions']['values']: - if value['extension']['alias'] == 'OS-KSADM': - found_osksadm = True - if value['extension']['alias'] == 'OS-KSCATALOG': - found_oskscatalog = True - if value['extension']['alias'] == 'HP-IDM': - found_hpidm = True - self.assertTrue(found_osksadm, "Missing OS-KSADM extension.") - self.assertTrue(found_oskscatalog, "Missing OS-KSCATALOG extension.") - if not common.isSsl() and 'HP-IDM_Disabled' not in os.environ: - self.assertTrue(found_hpidm, "Missing HP-IDM extension.") - - def test_extensions_xml(self): - r = self.admin_request(path='/extensions.xml') - self.assertTrue('xml' in r.getheader('Content-Type')) - content = r.xml - extensions = content.findall( - "{http://docs.openstack.org/common/api/v1.0}extension") - found_osksadm = False - found_oskscatalog = False - found_hpidm = False - for extension in extensions: - if extension.get("alias") == 'OS-KSADM': - found_osksadm = True - if extension.get("alias") == 'OS-KSCATALOG': - found_oskscatalog = True - if extension.get("alias") == 'HP-IDM': - found_hpidm = True - self.assertTrue(found_osksadm, "Missing OS-KSADM extension.") - self.assertTrue(found_oskscatalog, "Missing OS-KSCATALOG extension.") - if not common.isSsl() and 'HP-IDM_Disabled' not in os.environ: - self.assertTrue(found_hpidm, "Missing HP-IDM extension.") - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/client/test_frontends.py b/keystone/test/client/test_frontends.py deleted file mode 100644 index 636f66075d..0000000000 --- a/keystone/test/client/test_frontends.py +++ /dev/null @@ -1,38 +0,0 @@ -import unittest2 as unittest -from keystone.test.functional import common - - -class LegacyAuthenticationTest(common.FunctionalTestCase): - use_server = True - - def setUp(self, *args, **kwargs): - super(LegacyAuthenticationTest, self).setUp(*args, **kwargs) - - password = common.unique_str() - self.tenant = self.create_tenant().json['tenant'] - self.user = self.create_user(user_password=password, - tenant_id=self.tenant['id']).json['user'] - self.user['password'] = password - - self.services = {} - self.endpoint_templates = {} - for x in range(5): - self.services[x] = self.create_service().json['OS-KSADM:service'] - self.endpoint_templates[x] = self.create_endpoint_template( - name=self.services[x]['name'], \ - type=self.services[x]['type']).\ - json['OS-KSCATALOG:endpointTemplate'] - self.create_endpoint_for_tenant(self.tenant['id'], - self.endpoint_templates[x]['id']) - - def test_authenticate_legacy(self): - r = self.service_request(version='1.0', assert_status=204, headers={ - "X-Auth-User": self.user['name'], - "X-Auth-Key": self.user['password']}) - - self.assertIsNotNone(r.getheader('x-auth-token')) - for service in self.services.values(): - self.assertIsNotNone(r.getheader('x-' + service['name'])) - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/client/test_keystone_manage.py b/keystone/test/client/test_keystone_manage.py deleted file mode 100644 index d60e8a5c04..0000000000 --- a/keystone/test/client/test_keystone_manage.py +++ /dev/null @@ -1,54 +0,0 @@ -import os -import subprocess -import sys -import unittest2 as unittest - -import keystone.test.client as client_tests -from keystone.test import sampledata -from keystone import manage - -# Calculate root path so ewe call files in bin -possible_topdir = os.path.normpath(os.path.join(os.path.abspath(__file__), - os.pardir, - os.pardir, - os.pardir, - os.pardir)) - - -class TestKeystoneManage(unittest.TestCase): - """ - Tests for the keystone-manage client. - """ - - def test_check_can_call_keystone_manage(self): - """ - Test that we can call keystone-manage - """ - cmd = [ - os.path.join(possible_topdir, 'bin', 'keystone-manage'), - '--help', - ] - process = subprocess.Popen(cmd, stdout=subprocess.PIPE) - result = process.communicate()[0] - self.assertIn('usage', result.lower()) - - def test_keystone_manage_calls(self): - """ - Test that we can call keystone-manage and all sampledata calls work - """ - cmd = [ - os.path.join(possible_topdir, 'bin', 'keystone-manage'), - '-c', client_tests.TEST_CONFIG_FILE_NAME, - '--log-file', os.path.join(possible_topdir, 'bin', 'keystone.log'), - 'service', 'list' - ] - # This will init backends - manage.parse_args(cmd[1:]) - - # Loop through and try sampledata calls - sampledata_calls = sampledata.DEFAULT_FIXTURE - for call in sampledata_calls: - manage.process(*call) - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/client/test_middleware.py b/keystone/test/client/test_middleware.py deleted file mode 100644 index 543e0b7017..0000000000 --- a/keystone/test/client/test_middleware.py +++ /dev/null @@ -1,124 +0,0 @@ -import unittest2 as unittest -import uuid - -import keystone.common.exception -import keystone.backends.api as db_api -from keystone.test.functional import common -from keystone.test import client as client_tests - -# -# Auth Token -# -from keystone.middleware import auth_token - - -class TestAuthTokenMiddleware(common.MiddlewareTestCase): - """ - Tests for Keystone WSGI middleware: Auth Token - """ - - def setUp(self): - super(TestAuthTokenMiddleware, self).setUp(auth_token) - - -class TestAuthTokenMiddlewareWithNoAdminToken(common.MiddlewareTestCase): - """ - Tests for Keystone WSGI middleware: Auth Token - """ - - def setUp(self): - settings = {'delay_auth_decision': '0', - 'auth_host': client_tests.TEST_TARGET_SERVER_ADMIN_ADDRESS, - 'auth_port': client_tests.TEST_TARGET_SERVER_ADMIN_PORT, - 'auth_protocol': - client_tests.TEST_TARGET_SERVER_ADMIN_PROTOCOL, - 'auth_uri': ('%s://%s:%s/' % \ - (client_tests.TEST_TARGET_SERVER_SERVICE_PROTOCOL, - client_tests.TEST_TARGET_SERVER_SERVICE_ADDRESS, - client_tests.TEST_TARGET_SERVER_SERVICE_PORT)), - 'admin_user': self.admin_username, - 'admin_password': self.admin_password} - super(TestAuthTokenMiddlewareWithNoAdminToken, self).setUp(auth_token, - settings) - -# -# Glance -# -try: - from keystone.middleware import glance_auth_token -except ImportError as e: - print 'Could not load glance_auth_token: %s' % e - - -@unittest.skipUnless('glance_auth_token' in vars(), - "Glance Auth Token not imported") -class TestGlanceMiddleware(common.MiddlewareTestCase): - """ - Tests for Keystone WSGI middleware: Glance - """ - - def setUp(self): - super(TestGlanceMiddleware, self).setUp( - (auth_token, glance_auth_token)) - - -# -# Quantum -# -from keystone.middleware import quantum_auth_token - - -class TestQuantumMiddleware(common.MiddlewareTestCase): - """ - Tests for Keystone WSGI middleware: Glance - """ - - def setUp(self): - access = self.authenticate(self.admin_username, self.admin_password).\ - json['access'] - self.admin_token = access['token']['id'] - settings = {'delay_auth_decision': '0', - 'auth_host': client_tests.TEST_TARGET_SERVER_ADMIN_ADDRESS, - 'auth_port': client_tests.TEST_TARGET_SERVER_ADMIN_PORT, - 'auth_protocol': - client_tests.TEST_TARGET_SERVER_ADMIN_PROTOCOL, - 'auth_uri': ('%s://%s:%s/' % \ - (client_tests.TEST_TARGET_SERVER_SERVICE_PROTOCOL, - client_tests.TEST_TARGET_SERVER_SERVICE_ADDRESS, - client_tests.TEST_TARGET_SERVER_SERVICE_PORT)), - 'auth_version': '2.0', - 'admin_token': self.admin_token, - 'admin_user': self.admin_username, - 'admin_password': self.admin_password} - super(TestQuantumMiddleware, self).setUp(quantum_auth_token, settings) - - -# -# Swift -# -try: - from keystone.middleware import swift_auth -except ImportError as e: - print 'Could not load swift_auth: %s' % e - -#TODO(Ziad): find out how to disable swift logging -#@unittest.skipUnless('swift_auth' in vars(), -# "swift_auth not imported") -#class TestSwiftMiddleware(common.MiddlewareTestCase): -# """ -# Tests for Keystone WSGI middleware: Glance -# """ -# -# def setUp(self): -# settings = {'delay_auth_decision': '0', -# 'auth_host': '127.0.0.1', -# 'auth_port': '35357', -# 'auth_protocol': 'http', -# 'auth_uri': 'http://localhost:35357/', -# 'admin_token': self.admin_token, -# 'set log_facility': 'LOG_NULL'} -# super(TestSwiftMiddleware, self).setUp(swift_auth, settings) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/client/test_request_specs.py b/keystone/test/client/test_request_specs.py deleted file mode 100644 index 39118a07a4..0000000000 --- a/keystone/test/client/test_request_specs.py +++ /dev/null @@ -1,124 +0,0 @@ -import unittest2 as unittest -from keystone.test.functional import common - - -class TestResponseHeaders(common.FunctionalTestCase): - """Tests API's response headers""" - use_server = True - - def test_vary_header_on_error(self): - """A Vary header should be provided to support caching responses.""" - r = self.admin_request(path='/tokens/not-a-valid-token', - assert_status=404) - self.assertIn('X-Auth-Token', r.getheader('Vary')) - - def test_vary_header_on_admin(self): - """A Vary header should be provided to support caching responses.""" - r = self.admin_request(path='/tokens/%s' % self.admin_token) - self.assertIn('X-Auth-Token', r.getheader('Vary')) - - def test_vary_header_on_service(self): - """A Vary header should be provided to support caching responses.""" - r = self.service_request(path='/tenants') - self.assertIn('X-Auth-Token', r.getheader('Vary')) - - def test_vary_header_on_legacy(self): - """A Vary header should be provided to support caching responses.""" - r = self.admin_request('1.1/tenants') - self.assertIn('X-Auth-Token', r.getheader('Vary')) - - def test_vary_header_on_static(self): - """A Vary header should not be provided on a static request.""" - r = self.service_request('2.0/') - self.assertEqual(None, r.getheader('Vary')) - - -class TestUrlHandling(common.FunctionalTestCase): - """Tests API's global URL handling behaviors""" - use_server = True - - def test_optional_trailing_slash(self): - """Same response returned regardless of a trailing slash in the url.""" - r1 = self.service_request(path='/') - r2 = self.service_request(path='') - self.assertEqual(r1.read(), r2.read()) - - -class TestContentTypes(common.FunctionalTestCase): - """Tests API's Content-Type handling""" - use_server = True - - def test_default_content_type(self): - """Service returns JSON without being asked to""" - r = self.service_request() - self.assertTrue('application/json' in r.getheader('Content-Type')) - - def test_xml_extension(self): - """Service responds to .xml URL extension""" - r = self.service_request(path='.xml') - self.assertTrue('application/xml' in r.getheader('Content-Type')) - - def test_json_extension(self): - """Service responds to .json URL extension""" - r = self.service_request(path='.json') - self.assertTrue('application/json' in r.getheader('Content-Type')) - - def test_xml_accept_header(self): - """Service responds to xml Accept header""" - r = self.service_request(headers={'Accept': 'application/xml'}) - self.assertTrue('application/xml' in r.getheader('Content-Type')) - - def test_json_accept_header(self): - """Service responds to json Accept header""" - r = self.service_request(headers={'Accept': 'application/json'}) - self.assertTrue('application/json' in r.getheader('Content-Type')) - - def test_versioned_xml_accept_header(self): - """Service responds to versioned xml Accept header""" - r = self.service_request(headers={ - 'Accept': 'application/vnd.openstack.identity-v2.0+xml'}) - self.assertTrue('application/xml' in r.getheader('Content-Type')) - - def test_versioned_json_accept_header(self): - """Service responds to versioned json Accept header""" - r = self.service_request(headers={ - 'Accept': 'application/vnd.openstack.identity-v2.0+json'}) - self.assertTrue('application/json' in r.getheader('Content-Type')) - - def test_xml_extension_overrides_conflicting_header(self): - """Service returns XML when Accept header conflicts with extension""" - r = self.service_request(path='.xml', - headers={'Accept': 'application/json'}) - - self.assertTrue('application/xml' in r.getheader('Content-Type')) - - def test_json_extension_overrides_conflicting_header(self): - """Service returns JSON when Accept header conflicts with extension""" - r = self.service_request(path='.json', - headers={'Accept': 'application/xml'}) - - self.assertTrue('application/json' in r.getheader('Content-Type')) - - def test_xml_content_type_on_404(self): - """Content-Type should be honored even on 404 errors (Issue #13)""" - r = self.service_request(path='/completely-invalid-path', - headers={'Accept': 'application/xml'}, - assert_status=404) - - # Commenting this assertion out, as it currently fails - self.assertTrue('application/xml' in r.getheader('Content-Type'), - 'application/xml not in %s' % r.getheader('Content-Type')) - - def test_json_content_type_on_404(self): - """Content-Type should be honored even on 404 errors (Issue #13)""" - r = self.service_request(path='/completely-invalid-path', - headers={'Accept': 'application/json'}, - assert_status=404) - - # Commenting this assertion out, as it currently fails - self.assertTrue('application/json' in r.getheader('Content-Type'), - 'application/json not in %s' % r.getheader('Content-Type')) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/client/test_static_files.py b/keystone/test/client/test_static_files.py deleted file mode 100644 index df3bc62085..0000000000 --- a/keystone/test/client/test_static_files.py +++ /dev/null @@ -1,94 +0,0 @@ -import unittest2 as unittest -from keystone.test.functional import common - - -class TestStaticFiles(common.ApiTestCase): - use_server = True - - def test_pdf_contract(self): - if not common.isSsl(): - #TODO(ziad): Caller hangs in SSL (but works with cURL) - r = self.service_request(path='/identitydevguide.pdf') - self.assertTrue('pdf' in r.getheader('Content-Type')) - - def test_wadl_contract(self): - r = self.service_request(path='/identity.wadl') - self.assertTrue('xml' in r.getheader('Content-Type')) - - def test_wadl_common(self): - r = self.service_request(path='/common.ent') - self.assertTrue('xml' in r.getheader('Content-Type')) - - def test_xsd_contract(self): - r = self.service_request(path='/xsd/api.xsd') - self.assertTrue('xml' in r.getheader('Content-Type')) - - def test_xsd_atom_contract(self): - r = self.service_request(path='/xsd/atom/atom.xsd') - self.assertTrue('xml' in r.getheader('Content-Type')) - - def test_xslt(self): - r = self.service_request(path='/xslt/schema.xslt') - self.assertTrue('xml' in r.getheader('Content-Type')) - - def test_js(self): - r = self.service_request(path='/js/shjs/sh_java.js') - self.assertTrue('javascript' in r.getheader('Content-Type')) - - def test_xml_sample(self): - r = self.service_request(path='/samples/auth.xml') - self.assertTrue('xml' in r.getheader('Content-Type')) - - def test_json_sample(self): - r = self.service_request(path='/samples/auth.json') - self.assertTrue('json' in r.getheader('Content-Type')) - - def test_stylesheet(self): - r = self.service_request(path='/style/shjs/sh_acid.css') - self.assertTrue('css' in r.getheader('Content-Type')) - - -class TestAdminStaticFiles(common.FunctionalTestCase): - use_server = True - - def test_pdf_contract(self): - if not common.isSsl(): - #TODO(ziad): Caller hangs in SSL (but works with cURL) - r = self.admin_request(path='/identityadminguide.pdf') - self.assertTrue('pdf' in r.getheader('Content-Type')) - - def test_wadl_contract(self): - r = self.admin_request(path='/identity-admin.wadl') - self.assertTrue('xml' in r.getheader('Content-Type')) - - def test_xsd_contract(self): - r = self.admin_request(path='/xsd/api.xsd') - self.assertTrue('xml' in r.getheader('Content-Type')) - - def test_xsd_atom_contract(self): - r = self.admin_request(path='/xsd/atom/atom.xsd') - self.assertTrue('xml' in r.getheader('Content-Type')) - - def test_xslt(self): - r = self.admin_request(path='/xslt/schema.xslt') - self.assertTrue('xml' in r.getheader('Content-Type')) - - def test_js(self): - r = self.admin_request(path='/js/shjs/sh_java.js') - self.assertTrue('javascript' in r.getheader('Content-Type')) - - def test_xml_sample(self): - r = self.admin_request(path='/samples/auth.xml') - self.assertTrue('xml' in r.getheader('Content-Type')) - - def test_json_sample(self): - r = self.admin_request(path='/samples/auth.json') - self.assertTrue('json' in r.getheader('Content-Type')) - - def test_stylesheet(self): - r = self.admin_request(path='/style/shjs/sh_acid.css') - self.assertTrue('css' in r.getheader('Content-Type')) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/etc/ldap.conf.template b/keystone/test/etc/ldap.conf.template deleted file mode 100644 index 385afc9311..0000000000 --- a/keystone/test/etc/ldap.conf.template +++ /dev/null @@ -1,60 +0,0 @@ -[DEFAULT] -verbose = False -debug = False -default_store = sqlite -log_file = %(test_dir)s/keystone.log -log_dir = %(test_dir)s -backends = keystone.backends.sqlalchemy,keystone.backends.ldap -extensions= osksadm, oskscatalog, hpidm -service-header-mappings = { - 'nova' : 'X-Server-Management-Url', - 'swift' : 'X-Storage-Url', - 'cdn' : 'X-CDN-Management-Url'} -service_host = 0.0.0.0 -service_port = %(service_port)s -service_ssl = False -admin_host = 0.0.0.0 -admin_port = %(admin_port)s -admin_ssl = False -keystone-admin-role = Admin -keystone-service-admin-role = KeystoneServiceAdmin -hash-password = True - -[keystone.backends.sqlalchemy] -sql_connection = sqlite:// -sql_idle_timeout = 30 -backend_entities = ['Endpoints', 'Credentials', 'EndpointTemplates', 'Token', 'Service'] - -[keystone.backends.ldap] -ldap_url = fake://memory -ldap_user = cn=Admin -ldap_password = password -backend_entities = ['Tenant', 'User', 'UserRoleAssociation', 'Role'] - -[pipeline:admin] -pipeline = - urlnormalizer - d5_compat - admin_api - -[pipeline:keystone-legacy-auth] -pipeline = - urlnormalizer - legacy_auth - d5_compat - service_api - -[app:service_api] -paste.app_factory = keystone.server:service_app_factory - -[app:admin_api] -paste.app_factory = keystone.server:admin_app_factory - -[filter:urlnormalizer] -paste.filter_factory = keystone.frontends.normalizer:filter_factory - -[filter:d5_compat] -paste.filter_factory = keystone.frontends.d5_compat:filter_factory - -[filter:legacy_auth] -paste.filter_factory = keystone.frontends.legacy_token_auth:filter_factory diff --git a/keystone/test/etc/memcache.conf.template b/keystone/test/etc/memcache.conf.template deleted file mode 100644 index 6cb434aced..0000000000 --- a/keystone/test/etc/memcache.conf.template +++ /dev/null @@ -1,58 +0,0 @@ -[DEFAULT] -verbose = False -debug = False -default_store = sqlite -log_file = %(test_dir)s/keystone.log -log_dir = %(test_dir)s -backends = keystone.backends.sqlalchemy,keystone.backends.memcache -extensions= osksadm, oskscatalog, hpidm -service-header-mappings = { - 'nova' : 'X-Server-Management-Url', - 'swift' : 'X-Storage-Url', - 'cdn' : 'X-CDN-Management-Url'} -service_host = 0.0.0.0 -service_port = %(service_port)s -service_ssl = False -admin_host = 0.0.0.0 -admin_port = %(admin_port)s -admin_ssl = False -keystone-admin-role = Admin -keystone-service-admin-role = KeystoneServiceAdmin - -[keystone.backends.sqlalchemy] -sql_connection = sqlite:// -sql_idle_timeout = 30 -backend_entities = ['Endpoints', 'Credentials', 'EndpointTemplates', 'Tenant', 'User', 'UserRoleAssociation', 'Role', 'Service'] - -[keystone.backends.memcache] -memcache_hosts = 127.0.0.1:11211 -backend_entities = ['Token'] -cache_time = 86400 - -[pipeline:admin] -pipeline = - urlnormalizer - d5_compat - admin_api - -[pipeline:keystone-legacy-auth] -pipeline = - urlnormalizer - legacy_auth - d5_compat - service_api - -[app:service_api] -paste.app_factory = keystone.server:service_app_factory - -[app:admin_api] -paste.app_factory = keystone.server:admin_app_factory - -[filter:urlnormalizer] -paste.filter_factory = keystone.frontends.normalizer:filter_factory - -[filter:d5_compat] -paste.filter_factory = keystone.frontends.d5_compat:filter_factory - -[filter:legacy_auth] -paste.filter_factory = keystone.frontends.legacy_token_auth:filter_factory diff --git a/keystone/test/etc/sql.conf.template b/keystone/test/etc/sql.conf.template deleted file mode 100644 index c8b7b4b0bf..0000000000 --- a/keystone/test/etc/sql.conf.template +++ /dev/null @@ -1,54 +0,0 @@ -[DEFAULT] -verbose = False -debug = False -default_store = sqlite -log_file = %(test_dir)s/keystone.log -log_dir = %(test_dir)s -backends = keystone.backends.sqlalchemy -extensions= osksadm, oskscatalog, hpidm -service-header-mappings = { - 'nova' : 'X-Server-Management-Url', - 'swift' : 'X-Storage-Url', - 'cdn' : 'X-CDN-Management-Url'} -service_host = 0.0.0.0 -service_port = %(service_port)s -service_ssl = False -admin_host = 0.0.0.0 -admin_port = %(admin_port)s -admin_ssl = False -keystone-admin-role = Admin -keystone-service-admin-role = KeystoneServiceAdmin -hash-password = True - -[keystone.backends.sqlalchemy] -sql_connection = sqlite:// -sql_idle_timeout = 30 -backend_entities = ['Endpoints', 'Credentials', 'EndpointTemplates', 'Tenant', 'User', 'UserRoleAssociation', 'Role', 'Token', 'Service'] - -[pipeline:admin] -pipeline = - urlnormalizer - d5_compat - admin_api - -[pipeline:keystone-legacy-auth] -pipeline = - urlnormalizer - legacy_auth - d5_compat - service_api - -[app:service_api] -paste.app_factory = keystone.server:service_app_factory - -[app:admin_api] -paste.app_factory = keystone.server:admin_app_factory - -[filter:urlnormalizer] -paste.filter_factory = keystone.frontends.normalizer:filter_factory - -[filter:d5_compat] -paste.filter_factory = keystone.frontends.d5_compat:filter_factory - -[filter:legacy_auth] -paste.filter_factory = keystone.frontends.legacy_token_auth:filter_factory diff --git a/keystone/test/etc/sql_no_hpidm.conf.template b/keystone/test/etc/sql_no_hpidm.conf.template deleted file mode 100644 index 2f5cefa4e1..0000000000 --- a/keystone/test/etc/sql_no_hpidm.conf.template +++ /dev/null @@ -1,54 +0,0 @@ -[DEFAULT] -verbose = False -debug = False -default_store = sqlite -log_file = %(test_dir)s/keystone.log -log_dir = %(test_dir)s -backends = keystone.backends.sqlalchemy -extensions= osksadm, oskscatalog -service-header-mappings = { - 'nova' : 'X-Server-Management-Url', - 'swift' : 'X-Storage-Url', - 'cdn' : 'X-CDN-Management-Url'} -service_host = 0.0.0.0 -service_port = %(service_port)s -service_ssl = False -admin_host = 0.0.0.0 -admin_port = %(admin_port)s -admin_ssl = False -keystone-admin-role = Admin -keystone-service-admin-role = KeystoneServiceAdmin -hash-password = True - -[keystone.backends.sqlalchemy] -sql_connection = sqlite:// -sql_idle_timeout = 30 -backend_entities = ['Endpoints', 'Credentials', 'EndpointTemplates', 'Tenant', 'User', 'UserRoleAssociation', 'Role', 'Token', 'Service'] - -[pipeline:admin] -pipeline = - urlnormalizer - d5_compat - admin_api - -[pipeline:keystone-legacy-auth] -pipeline = - urlnormalizer - legacy_auth - d5_compat - service_api - -[app:service_api] -paste.app_factory = keystone.server:service_app_factory - -[app:admin_api] -paste.app_factory = keystone.server:admin_app_factory - -[filter:urlnormalizer] -paste.filter_factory = keystone.frontends.normalizer:filter_factory - -[filter:d5_compat] -paste.filter_factory = keystone.frontends.d5_compat:filter_factory - -[filter:legacy_auth] -paste.filter_factory = keystone.frontends.legacy_token_auth:filter_factory diff --git a/keystone/test/etc/ssl.conf.template b/keystone/test/etc/ssl.conf.template deleted file mode 100644 index 1889c7d666..0000000000 --- a/keystone/test/etc/ssl.conf.template +++ /dev/null @@ -1,58 +0,0 @@ -[DEFAULT] -verbose = False -debug = False -default_store = sqlite -log_file = %(test_dir)s/keystone.log -log_dir = %(test_dir)s -backends = keystone.backends.sqlalchemy -extensions= osksadm, oskscatalog, hpidm -service-header-mappings = { - 'nova' : 'X-Server-Management-Url', - 'swift' : 'X-Storage-Url', - 'cdn' : 'X-CDN-Management-Url'} -service_host = 0.0.0.0 -service_port = %(service_port)s -service_ssl = True -admin_host = 0.0.0.0 -admin_port = %(admin_port)s -admin_ssl = True -keystone-admin-role = Admin -keystone-service-admin-role = KeystoneServiceAdmin -hash-password = True -certfile = %(base_dir)s/examples/ssl/certs/keystone.pem -keyfile = %(base_dir)s/examples/ssl/private/keystonekey.pem -ca_certs = %(base_dir)s/examples/ssl/certs/ca.pem -cert_required = True - -[keystone.backends.sqlalchemy] -sql_connection = sqlite:// -sql_idle_timeout = 30 -backend_entities = ['Endpoints', 'Credentials', 'EndpointTemplates', 'Tenant', 'User', 'UserRoleAssociation', 'Role', 'Token', 'Service'] - -[pipeline:admin] -pipeline = - urlnormalizer - d5_compat - admin_api - -[pipeline:keystone-legacy-auth] -pipeline = - urlnormalizer - legacy_auth - d5_compat - service_api - -[app:service_api] -paste.app_factory = keystone.server:service_app_factory - -[app:admin_api] -paste.app_factory = keystone.server:admin_app_factory - -[filter:urlnormalizer] -paste.filter_factory = keystone.frontends.normalizer:filter_factory - -[filter:d5_compat] -paste.filter_factory = keystone.frontends.d5_compat:filter_factory - -[filter:legacy_auth] -paste.filter_factory = keystone.frontends.legacy_token_auth:filter_factory diff --git a/keystone/test/functional/__init__.py b/keystone/test/functional/__init__.py deleted file mode 100644 index df704bbec9..0000000000 --- a/keystone/test/functional/__init__.py +++ /dev/null @@ -1,25 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 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. - -""" Functional Tests - -Functional tests are tests that run calls through the core logic modues of -Keystone through to the backends. The backend components will eventually be -tested separately, but have not been stubbed out yet. - -They do not test http(s) calls or run a server on a TCP/IP port. -""" diff --git a/keystone/test/functional/common.py b/keystone/test/functional/common.py deleted file mode 100644 index 80a10819cf..0000000000 --- a/keystone/test/functional/common.py +++ /dev/null @@ -1,1738 +0,0 @@ -import datetime -import httplib -import json -import logging -import os -import random -import unittest2 as unittest -import uuid -from webob import Request, Response -from xml.etree import ElementTree - -from keystone import server -import keystone.backends.api as db_api -from keystone.test import client as client_tests -from keystone import utils - -logger = logging.getLogger(__name__) - - -def isSsl(): - """ See if we are testing with SSL. If cert is non-empty, we are! """ - if 'cert_file' in os.environ: - return os.environ['cert_file'] - return None - - -class HttpTestCase(unittest.TestCase): - """Performs generic HTTP request testing. - - Defines a ``request`` method for use in test cases that makes - HTTP requests, and two new asserts: - - * assertResponseSuccessful - * assertResponseStatus - """ - - def request(self, host='127.0.0.1', protocol='http', port=80, method='GET', - path='/', headers=None, body=None, assert_status=None): - """Perform request and fetch httplib.HTTPResponse from the server""" - - # Initialize headers dictionary - headers = {} if not headers else headers - - logger.debug("Connecting to %s://%s:%s", protocol, host, port) - if protocol == 'https': - cert_file = isSsl() - connection = httplib.HTTPSConnection(host, port, - cert_file=cert_file, - timeout=20) - else: - connection = httplib.HTTPConnection(host, port, timeout=20) - - # Perform the request - connection.request(method, path, body, headers) - - # Retrieve the response so can go ahead and close the connection - response = connection.getresponse() - logger.debug("%s %s returned %s", method, path, response.status) - - response.body = response.read() - if response.status != httplib.OK: - logger.debug("Response Body:") - for line in response.body.split("\n"): - logger.debug(line) - - # Close the connection - connection.close() - - # Automatically assert HTTP status code - if assert_status: - self.assertResponseStatus(response, assert_status) - else: - self.assertResponseSuccessful(response) - - # Contains the response headers, body, etc - return response - - def assertResponseSuccessful(self, response): - """Asserts that a status code lies inside the 2xx range - - :param response: :py:class:`httplib.HTTPResponse` to be - verified to have a status code between 200 and 299. - - example:: - - >>> self.assertResponseSuccessful(response, 203) - """ - self.assertTrue(response.status >= 200 and response.status <= 299, - 'Status code %d is outside of the expected range (2xx)\n\n%s' % - (response.status, response.body)) - - def assertResponseStatus(self, response, assert_status): - """Asserts a specific status code on the response - - :param response: :py:class:`httplib.HTTPResponse` - :param assert_status: The specific ``status`` result expected - - example:: - - >>> self.assertResponseStatus(response, 203) - """ - self.assertEqual(response.status, assert_status, - 'Status code %s is not %s, as expected)\n\n%s' % - (response.status, assert_status, response.body)) - - -class RestfulTestCase(HttpTestCase): - """Performs restful HTTP request testing""" - - def restful_request(self, headers=None, as_json=None, as_xml=None, - **kwargs): - """Encodes and decodes (JSON & XML) HTTP requests and responses. - - Dynamically encodes json or xml as request body if one is provided. - - .. WARNING:: - - * Existing Content-Type header will be overwritten. - * If both as_json and as_xml are provided, as_xml is ignored. - * If either as_json or as_xml AND a body is provided, the body - is ignored. - - Dynamically returns 'as_json' or 'as_xml' attribute based on the - detected response type, and fails the current test case if - unsuccessful. - - response.as_json: standard python dictionary - - response.as_xml: as_etree.ElementTree - """ - - # Initialize headers dictionary - headers = {} if not headers else headers - - # Attempt to encode JSON and XML automatically, if requested - if as_json: - body = RestfulTestCase._encode_json(as_json) - headers['Content-Type'] = 'application/json' - elif as_xml: - body = as_xml - headers['Content-Type'] = 'application/xml' - - # Assume the client wants xml back if it didn't specify - if 'Accept' not in headers: - headers['Accept'] = 'application/xml' - elif 'body' in kwargs: - body = kwargs.pop('body') - else: - body = None - - # Perform the HTTP request/response - response = self.request(headers=headers, body=body, **kwargs) - - # Attempt to parse JSON and XML automatically, if detected - response = self._decode_response_body(response) - - # Contains the decoded response as_json/as_xml, etc - return response - - @staticmethod - def _encode_json(data): - """Returns a JSON-encoded string of the given python dictionary - - :param data: python object to be encoded into JSON - :returns: string of JSON encoded data - """ - return json.dumps(data) - - def _decode_response_body(self, response): - """Detects response body type, and attempts to decode it - - :param response: :py:class:`httplib.HTTPResponse` - :returns: response object with additions: - - If context type is application/json, the response will have an - additional attribute ``json`` that will have the decoded JSON - result (typically a dict) - - If context type is application/xml, the response will have an - additional attribute ``xml`` that will have the an ElementTree - result. - """ - if response.body is not None and response.body.strip(): - if 'application/json' in response.getheader('Content-Type', ''): - response.json = self._decode_json(response.body) - elif 'application/xml' in response.getheader('Content-Type', ''): - response.xml = self._decode_xml(response.body) - return response - - @staticmethod - def _decode_json(json_str): - """Returns a dict of the given JSON string""" - return json.loads(json_str) - - @staticmethod - def _decode_xml(xml_str): - """Returns an ElementTree of the given XML string""" - return ElementTree.XML(xml_str) - - -class ApiTestCase(RestfulTestCase): - """Abstracts REST verbs & resources of the service & admin API.""" - use_server = False - - admin_role_name = 'Admin' - service_admin_role_name = 'KeystoneServiceAdmin' - member_role_name = 'Member' - - # Same as KeystoneTest settings - admin_username = 'admin' - admin_password = 'secrete' - - service_token = None - admin_token = None - - service_api = None - admin_api = None - - """ - Dict of configuration options to pass to the API controller - """ - options = { - 'backends': "keystone.backends.sqlalchemy", - 'keystone.backends.sqlalchemy': { - # in-memory db - 'sql_connection': 'sqlite://', - 'verbose': False, - 'debug': False, - 'backend_entities': - "['UserRoleAssociation', 'Endpoints', 'Role', 'Tenant', " - "'Tenant', 'User', 'Credentials', 'EndpointTemplates', " - "'Token', 'Service']", - }, - 'extensions': 'osksadm, oskscatalog, hpidm', - 'keystone-admin-role': 'Admin', - 'keystone-service-admin-role': 'KeystoneServiceAdmin', - 'hash-password': 'True', - } - # Populate the CONF module with these values - utils.set_configuration(options) - - def fixture_create_role(self, **kwargs): - """ - Creates a role fixture. - - :params \*\*kwargs: Attributes of the role to create - """ - values = kwargs.copy() - role = db_api.ROLE.create(values) - logger.debug("Created role fixture %s (id=%s)", role.name, role.id) - return role - - def fixture_create_token(self, **kwargs): - """ - Creates a token fixture. - - :params \*\*kwargs: Attributes of the token to create - """ - values = kwargs.copy() - token = db_api.TOKEN.create(values) - logger.debug("Created token fixture %s", token.id) - return token - - def fixture_create_tenant(self, **kwargs): - """ - Creates a tenant fixture. - - :params \*\*kwargs: Attributes of the tenant to create - """ - values = kwargs.copy() - tenant = db_api.TENANT.create(values) - logger.debug("Created tenant fixture %s (id=%s)", tenant.name, - tenant.id) - return tenant - - def fixture_create_user(self, **kwargs): - """ - Creates a user fixture. If the user's tenant ID is set, and the tenant - does not exist in the database, the tenant is created. - - :params \*\*kwargs: Attributes of the user to create - """ - values = kwargs.copy() - tenant_name = values.get('tenant_name') - if tenant_name: - if not db_api.TENANT.get_by_name(tenant_name): - tenant = db_api.TENANT.create({'name': tenant_name, - 'enabled': True, - 'desc': tenant_name}) - values['tenant_id'] = tenant.id - user = db_api.USER.create(values) - logger.debug("Created user fixture %s (id=%s)", user.name, user.id) - return user - - def fixture_create_service(self, service_name=None, service_type=None, - service_description=None, **kwargs): - """ - Creates a service fixture. - - :params \*\*kwargs: Additional attributes of the service to create - """ - values = kwargs.copy() - service_name = optional_str(service_name) - if service_type is None: - service_type = ['compute', 'identity', 'image-service', - 'object-store', 'ext:extension-service' - ][random.randrange(5)] - service_description = optional_str(service_description) - values["name"] = service_name - values["type"] = service_type - values["description"] = service_description - - service = db_api.SERVICE.create(values) - logger.debug("Created service fixture %s (id=%s)", service.name, - service.id) - return service - - def setUp(self): - super(ApiTestCase, self).setUp() - if self.use_server: - return - - self.service_api = server.ServiceApi() - self.admin_api = server.AdminApi() - - # ADMIN ROLE - self.admin_role = self.fixture_create_role( - name=self.admin_role_name) - - # ADMIN - password = unique_str() - self.admin_user = self.fixture_create_user( - name="admin-user-%s" % uuid.uuid4().hex, enabled=True, - password=password) - self.admin_user['password'] = password - self.admin_password = password - self.admin_username = self.admin_user['name'] - - obj = {} - obj['role_id'] = self.admin_role['id'] - obj['user_id'] = self.admin_user['id'] - obj['tenant_id'] = None - result = db_api.USER.user_role_add(obj) - logger.debug("Created grant fixture %s", result.id) - - # SERVICE ADMIN ROLE - self.service_admin_role = self.fixture_create_role( - name=self.service_admin_role_name) - - # MEMBER ROLE - self.member_role = self.fixture_create_role( - name='Member') - - def tearDown(self): - super(ApiTestCase, self).tearDown() - # Explicitly release these to limit memory use. - self.service_api = self.admin_api = self.admin_role = None - self.admin_user = self.admin_password = self.admin_username = None - self.service_admin_role = self.member_role = None - - def request(self, host='127.0.0.1', protocol='http', port=80, method='GET', - path='/', headers=None, body=None, assert_status=None, - server=None): - """Overrides HttpTestCase and uses local calls""" - if self.use_server: - # Call a real server (bypass the override) - return super(ApiTestCase, self).request(host=host, port=port, - protocol=protocol, method=method, - path=path, headers=headers, body=body, - assert_status=assert_status) - - req = Request.blank(path) - req.method = method - req.headers = headers - if isinstance(body, unicode): - req.body = body.encode('utf-8') - else: - req.body = body - - res = req.get_response(server) - logger.debug("%s %s returned %s", req.method, req.path_qs, - res.status) - if res.status_int != httplib.OK: - logger.debug("Response Body:") - for line in res.body.split("\n"): - logger.debug(line) - - # Automatically assert HTTP status code - if assert_status: - self.assertEqual(res.status_int, assert_status, - 'Status code %s is not %s, as expected)\n\n%s' % - (res.status_int, assert_status, res.body)) - else: - self.assertTrue(299 >= res.status_int >= 200, - 'Status code %d is outside of the expected range (2xx)\n\n%s' % - (res.status_int, res.body)) - - # Contains the response headers, body, etc - return res - - def _decode_response_body(self, response): - """Override to support webob.Response. - """ - if self.use_server: - # Call a real server (bypass the override) - return super(ApiTestCase, self)._decode_response_body(response) - - if response.body is not None and response.body.strip(): - if 'application/json' in response.content_type: - response.json = self._decode_json(response.body) - elif 'application/xml' in response.content_type: - response.xml = self._decode_xml(response.body) - return response - - def assertResponseSuccessful(self, response): - """Asserts that a status code lies inside the 2xx range - - :param response: :py:class:`webob.Response` to be - verified to have a status code between 200 and 299. - - example:: - - >>> self.assertResponseSuccessful(response, 203) - """ - if self.use_server: - # Call a real server (bypass the override) - return super(ApiTestCase, self).assertResponseSuccessful(response) - - self.assertTrue(response.status_int >= 200 and - response.status_int <= 299, - 'Status code %d is outside of the expected range (2xx)\n\n%s' % - (response.status_int, response.body)) - - def assertResponseStatus(self, response, assert_status): - """Asserts a specific status code on the response - - :param response: :py:class:`webob.Response` - :param assert_status: The specific ``status`` result expected - - example:: - - >>> self.assertResponseStatus(response, 203) - """ - if self.use_server: - # Call a real server (bypass the override) - return super(ApiTestCase, self).assertResponseStatus(response, - assert_status) - - self.assertEqual(response.status_int, assert_status, - 'Status code %s is not %s, as expected)\n\n%s' % - (response.status_int, assert_status, response.body)) - - def service_request(self, version='2.0', path='', port=None, headers=None, - host=None, protocol=None, **kwargs): - """Returns a request to the service API""" - - # Initialize headers dictionary - headers = {} if not headers else headers - - if self.use_server: - path = ApiTestCase._version_path(version, path) - if port is None: - port = client_tests.TEST_TARGET_SERVER_SERVICE_PORT or 5000 - if host is None: - host = (client_tests.TEST_TARGET_SERVER_SERVICE_ADDRESS - or '127.0.0.1') - if protocol is None: - protocol = (client_tests.TEST_TARGET_SERVER_SERVICE_PROTOCOL - or 'http') - - if 'use_token' in kwargs: - headers['X-Auth-Token'] = kwargs.pop('use_token') - elif self.service_token: - headers['X-Auth-Token'] = self.service_token - elif self.admin_token: - headers['X-Auth-Token'] = self.admin_token - - return self.restful_request(host=host, protocol=protocol, port=port, - path=path, headers=headers, server=self.service_api, **kwargs) - - def admin_request(self, version='2.0', path='', port=None, headers=None, - host=None, protocol=None, **kwargs): - """Returns a request to the admin API""" - - # Initialize headers dictionary - headers = {} if not headers else headers - - if self.use_server: - path = ApiTestCase._version_path(version, path) - if port is None: - port = client_tests.TEST_TARGET_SERVER_ADMIN_PORT or 35357 - if host is None: - host = (client_tests.TEST_TARGET_SERVER_ADMIN_ADDRESS - or '127.0.0.1') - if protocol is None: - protocol = (client_tests.TEST_TARGET_SERVER_ADMIN_PROTOCOL - or 'http') - - if 'use_token' in kwargs: - headers['X-Auth-Token'] = kwargs.pop('use_token') - elif self.admin_token: - headers['X-Auth-Token'] = self.admin_token - - return self.restful_request(host=host, protocol=protocol, port=port, - path=path, headers=headers, server=self.admin_api, **kwargs) - - @staticmethod - def _version_path(version, path): - """Prepend the given path with the API version. - - An empty version results in no version being prepended.""" - if version: - return '/v' + str(version) + str(path) - else: - return str(path) - - def post_token(self, **kwargs): - """POST /tokens""" - #Setting service call as the default behavior.""" - if 'request_type' in kwargs and \ - kwargs.pop('request_type') == 'admin': - return self.admin_request(method='POST', - path='/tokens', **kwargs) - else: - return self.service_request(method='POST', - path='/tokens', **kwargs) - - def get_token(self, token_id, **kwargs): - """GET /tokens/{token_id}""" - return self.admin_request(method='GET', - path='/tokens/%s' % (token_id,), **kwargs) - - def get_token_belongsto(self, token_id, tenant_id, **kwargs): - """GET /tokens/{token_id}?belongsTo={tenant_id}""" - return self.admin_request(method='GET', - path='/tokens/%s?belongsTo=%s' % (token_id, tenant_id), **kwargs) - - def check_token(self, token_id, **kwargs): - """HEAD /tokens/{token_id}""" - return self.admin_request(method='HEAD', - path='/tokens/%s' % (token_id,), **kwargs) - - def check_token_belongs_to(self, token_id, tenant_id, **kwargs): - """HEAD /tokens/{token_id}?belongsTo={tenant_id}""" - return self.admin_request(method='HEAD', - path='/tokens/%s?belongsTo=%s' % (token_id, tenant_id), **kwargs) - - def delete_token(self, token_id, **kwargs): - """DELETE /tokens/{token_id}""" - return self.admin_request(method='DELETE', - path='/tokens/%s' % (token_id,), **kwargs) - - def post_tenant(self, **kwargs): - """POST /tenants""" - return self.admin_request(method='POST', path='/tenants', **kwargs) - - def get_tenants(self, **kwargs): - """GET /tenants""" - if 'request_type' in kwargs and \ - kwargs.pop('request_type') == 'service': - return self.service_request(method='GET', - path='/tenants', **kwargs) - else: - return self.admin_request(method='GET', path='/tenants', **kwargs) - - def get_tenant(self, tenant_id, **kwargs): - """GET /tenants/{tenant_id}""" - return self.admin_request(method='GET', - path='/tenants/%s' % (tenant_id,), **kwargs) - - def get_tenant_by_name(self, tenant_name, **kwargs): - """GET /tenants?name=tenant_name""" - return self.admin_request(method='GET', - path='/tenants?name=%s' % (tenant_name,), **kwargs) - - def post_tenant_for_update(self, tenant_id, **kwargs): - """GET /tenants/{tenant_id}""" - return self.admin_request(method='POST', - path='/tenants/%s' % (tenant_id,), **kwargs) - - def get_tenant_users(self, tenant_id, **kwargs): - """GET /tenants/{tenant_id}/users""" - return self.admin_request(method='GET', - path='/tenants/%s/users' % (tenant_id,), **kwargs) - - def get_tenant_users_by_role(self, tenant_id, role_id, **kwargs): - """GET /tenants/{tenant_id}/users?roleId={roleId}""" - return self.admin_request(method='GET', - path='/tenants/%s/users?roleId=%s' % (\ - tenant_id, role_id), **kwargs) - - def delete_tenant(self, tenant_id, **kwargs): - """DELETE /tenants/{tenant_id}""" - return self.admin_request(method='DELETE', - path='/tenants/%s' % (tenant_id,), **kwargs) - - def post_user(self, **kwargs): - """POST /users""" - return self.admin_request(method='POST', path='/users', **kwargs) - - def get_users(self, **kwargs): - """GET /users""" - return self.admin_request(method='GET', path='/users', **kwargs) - - def get_user(self, user_id, **kwargs): - """GET /users/{user_id}""" - return self.admin_request(method='GET', - path='/users/%s' % (user_id,), **kwargs) - - def query_user(self, user_name, **kwargs): - """GET /users?name={user_name}""" - return self.admin_request(method='GET', - path='/users?name=%s' % (user_name,), **kwargs) - - def post_user_for_update(self, user_id, **kwargs): - """POST /users/{user_id}""" - return self.admin_request(method='POST', - path='/users/%s' % (user_id,), **kwargs) - - def put_user_password(self, user_id, **kwargs): - """PUT /users/{user_id}/OS-KSADM/password""" - return self.admin_request(method='PUT', - path='/users/%s/OS-KSADM/password' % (user_id,), **kwargs) - - def put_user_tenant(self, user_id, **kwargs): - """PUT /users/{user_id}/OS-KSADM/tenant""" - return self.admin_request(method='PUT', - path='/users/%s/OS-KSADM/tenant' % (user_id,), **kwargs) - - def put_user_enabled(self, user_id, **kwargs): - """PUT /users/{user_id}/OS-KSADM/enabled""" - return self.admin_request(method='PUT', - path='/users/%s/OS-KSADM/enabled' % (user_id,), **kwargs) - - def delete_user(self, user_id, **kwargs): - """DELETE /users/{user_id}""" - return self.admin_request(method='DELETE', - path='/users/%s' % (user_id,), **kwargs) - - def get_user_roles(self, user_id, **kwargs): - """GET /users/{user_id}/roles""" - return self.admin_request(method='GET', - path='/users/%s/roles' % (user_id,), **kwargs) - - def put_user_role(self, user_id, role_id, tenant_id, **kwargs): - if tenant_id is None: - # PUT /users/{user_id}/roles/OS-KSADM/{role_id} - return self.admin_request(method='PUT', - path='/users/%s/roles/OS-KSADM/%s' % - (user_id, role_id), **kwargs) - else: - # PUT /tenants/{tenant_id}/users/{user_id}/ - # roles/OS-KSADM/{role_id} - return self.admin_request(method='PUT', - path='/tenants/%s/users/%s/roles/OS-KSADM/%s' % (tenant_id, - user_id, role_id,), **kwargs) - - def delete_user_role(self, user_id, role_id, tenant_id, **kwargs): - """DELETE /users/{user_id}/roles/{role_id}""" - if tenant_id is None: - return self.admin_request(method='DELETE', - path='/users/%s/roles/OS-KSADM/%s' - % (user_id, role_id), **kwargs) - else: - return self.admin_request(method='DELETE', - path='/tenants/%s/users/%s/roles/OS-KSADM/%s' % - (tenant_id, user_id, role_id), **kwargs) - - def post_role(self, **kwargs): - """POST /roles""" - return self.admin_request(method='POST', - path='/OS-KSADM/roles', **kwargs) - - def get_roles(self, **kwargs): - """GET /OS-KSADM/roles""" - return self.admin_request(method='GET', - path='/OS-KSADM/roles', **kwargs) - - def get_roles_by_service(self, service_id, **kwargs): - """GET /OS-KSADM/roles""" - return self.admin_request(method='GET', path=( - '/OS-KSADM/roles?serviceId=%s') - % (service_id), - **kwargs) - - def get_role(self, role_id, **kwargs): - """GET /roles/{role_id}""" - return self.admin_request(method='GET', - path='/OS-KSADM/roles/%s' % (role_id,), **kwargs) - - def get_role_by_name(self, role_name, **kwargs): - """GET /roles?name={role_name}""" - return self.admin_request(method='GET', - path='/OS-KSADM/roles?name=%s' % (role_name,), **kwargs) - - def delete_role(self, role_id, **kwargs): - """DELETE /roles/{role_id}""" - return self.admin_request(method='DELETE', - path='/OS-KSADM/roles/%s' % (role_id,), **kwargs) - - def get_endpoint_templates(self, **kwargs): - """GET /OS-KSCATALOG/endpointTemplates""" - return self.admin_request(method='GET', - path='/OS-KSCATALOG/endpointTemplates', - **kwargs) - - def get_endpoint_templates_by_service(self, service_id, **kwargs): - """GET /OS-KSCATALOG/endpointTemplates""" - return self.admin_request(method='GET', path=( - '/OS-KSCATALOG/endpointTemplates?serviceId=%s') - % (service_id), - **kwargs) - - def post_endpoint_template(self, **kwargs): - """POST /OS-KSCATALOG/endpointTemplates""" - return self.admin_request(method='POST', - path='/OS-KSCATALOG/endpointTemplates', - **kwargs) - - def put_endpoint_template(self, endpoint_template_id, **kwargs): - """PUT /OS-KSCATALOG/endpointTemplates/{endpoint_template_id}""" - return self.admin_request(method='PUT', - path='/OS-KSCATALOG/endpointTemplates/%s' - % (endpoint_template_id,), - **kwargs) - - def get_endpoint_template(self, endpoint_template_id, **kwargs): - """GET /OS-KSCATALOG/endpointTemplates/{endpoint_template_id}""" - return self.admin_request(method='GET', - path='/OS-KSCATALOG/endpointTemplates/%s' - % (endpoint_template_id,), - **kwargs) - - def delete_endpoint_template(self, endpoint_template_id, **kwargs): - """DELETE /OS-KSCATALOG/endpointTemplates/{endpoint_template_id}""" - return self.admin_request(method='DELETE', - path='/OS-KSCATALOG/endpointTemplates/%s' % - (endpoint_template_id,), - **kwargs) - - def get_tenant_endpoints(self, tenant_id, **kwargs): - """GET /tenants/{tenant_id}/OS-KSCATALOG/endpoints""" - return self.admin_request(method='GET', - path='/tenants/%s/OS-KSCATALOG/endpoints' % - (tenant_id,), - **kwargs) - - def post_tenant_endpoint(self, tenant_id, **kwargs): - """POST /tenants/{tenant_id}/OS-KSCATALOG/endpoints""" - return self.admin_request(method='POST', - path='/tenants/%s/OS-KSCATALOG/endpoints' % - (tenant_id,), **kwargs) - - def delete_tenant_endpoint(self, tenant_id, endpoint_id, **kwargs): - """DELETE /tenants/{tenant_id}/OS-KSCATALOG/endpoints/{endpoint_id}""" - return self.admin_request(method='DELETE', - path='/tenants/%s/OS-KSCATALOG/endpoints/%s' % - (tenant_id, endpoint_id,), - **kwargs) - - def get_token_endpoints(self, token_id, **kwargs): - """GET /tokens/{token_id}/endpoints""" - return self.admin_request(method='GET', - path='/tokens/%s/endpoints' % - (token_id,), - **kwargs) - - def post_service(self, **kwargs): - """POST /services""" - return self.admin_request(method='POST', - path='/OS-KSADM/services', **kwargs) - - def get_services(self, **kwargs): - """GET /services""" - return self.admin_request(method='GET', - path='/OS-KSADM/services', **kwargs) - - def get_service(self, service_id, **kwargs): - """GET /services/{service_id}""" - return self.admin_request(method='GET', - path='/OS-KSADM/services/%s' % (service_id,), **kwargs) - - def get_service_by_name(self, service_name, **kwargs): - """GET /services?name={service_name}""" - return self.admin_request(method='GET', - path='/OS-KSADM/services?name=%s' % (service_name,), **kwargs) - - def delete_service(self, service_id, **kwargs): - """DELETE /services/{service_id}""" - return self.admin_request(method='DELETE', - path='/OS-KSADM/services/%s' % (service_id,), **kwargs) - - def get_root(self, **kwargs): - """GET /""" - return self.service_request(method='GET', path='/', **kwargs) - - def get_extensions(self, **kwargs): - """GET /extensions""" - return self.service_request(method='GET', path='/extensions', **kwargs) - - def get_admin_guide(self, **kwargs): - """GET /identityadminguide.pdf""" - return self.service_request(method='GET', - path='/identityadminguide.pdf', **kwargs) - - def get_admin_wadl(self, **kwargs): - """GET /identity-admin.wadl""" - return self.service_request(method='GET', path='/identity-admin.wadl', - **kwargs) - - def get_common_ent(self, **kwargs): - """GET /common.ent""" - return self.service_request(method='GET', path='/common.ent', - **kwargs) - - def get_xsd(self, filename, **kwargs): - """GET /xsd/{xsd}""" - return self.service_request(method='GET', path='/xsd/%s' % (filename,), - **kwargs) - - def get_xsd_atom(self, filename, **kwargs): - """GET /xsd/atom/{xsd}""" - return self.service_request(method='GET', - path='/xsd/atom/%s' % (filename,), **kwargs) - - def get_xslt(self, filename, **kwargs): - """GET /xslt/{file:.*}""" - return self.service_request(method='GET', - path='/xslt/%s' % (filename,), **kwargs) - - def get_javascript(self, filename, **kwargs): - """GET /js/{file:.*}""" - return self.service_request(method='GET', path='/js/%s' % (filename,), - **kwargs) - - def get_style(self, filename, **kwargs): - """GET /style/{file:.*}""" - return self.service_request(method='GET', - path='/style/%s' % (filename,), **kwargs) - - def get_sample(self, filename, **kwargs): - """GET /samples/{file:.*}""" - return self.service_request(method='GET', - path='/samples/%s' % (filename,), **kwargs) - - def get_user_credentials(self, user_id, **kwargs): - """GET /users/{user_id}/OS-KSADM/credentials""" - return self.admin_request(method='GET', - path='/users/%s/OS-KSADM/credentials' % (user_id,), **kwargs) - - def get_user_credentials_by_type(self, - user_id, credentials_type, **kwargs): - """GET /users/{user_id}/OS-KSADM/credentials/{credentials_type}""" - return self.admin_request(method='GET', - path='/users/%s/OS-KSADM/credentials/%s'\ - % (user_id, credentials_type,), **kwargs) - - def post_credentials(self, user_id, **kwargs): - """POST /users/{user_id}/OS-KSADM/credentials""" - return self.admin_request(method='POST', - path='/users/%s/OS-KSADM/credentials' % (user_id,), **kwargs) - - def post_credentials_by_type(self, user_id, credentials_type, **kwargs): - """POST /users/{user_id}/OS-KSADM/credentials/{credentials_type}""" - return self.admin_request(method='POST', - path='/users/%s/OS-KSADM/credentials/%s' %\ - (user_id, credentials_type), **kwargs) - - def delete_user_credentials_by_type(self, user_id, - credentials_type, **kwargs): - """DELETE /users/{user_id}/OS-KSADM/credentials/{credentials_type}""" - return self.admin_request(method='DELETE', - path='/users/%s/OS-KSADM/credentials/%s' %\ - (user_id, credentials_type,), **kwargs) - - -def unique_str(): - """Generates and return a unique string""" - return str(uuid.uuid4()) - - -def unique_email(): - """Generates and return a unique email""" - return "%s@openstack.org" % unique_str() - - -def unique_url(): - """Generates and return a unique email""" - return "http://%s" % unique_str() - - -def optional_str(val): - """Automatically populates optional string fields""" - return val if val is not None else unique_str() - - -def optional_email(val): - """Automatically populates optional email fields""" - return val if val is not None else unique_email() - - -def optional_url(val): - """Automatically populates optional url fields""" - return val if val is not None else unique_url() - - -class FunctionalTestCase(ApiTestCase): - """Abstracts functional CRUD of the identity API""" - admin_user_id = None - - admin_token = None - service_token = None - expired_admin_token = None - disabled_admin_token = None - service_admin_token = None - - user = None - user_token = None - service_user = None - - tenant = None - tenant_user = None # user with default tenant - tenant_user_token = None - - disabled_tenant = None - disabled_user = None - - xmlns = 'http://docs.openstack.org/identity/api/v2.0' - xmlns_ksadm = 'http://docs.openstack.org/identity/api/ext/OS-KSADM/v1.0' - xmlns_kscatalog = "http://docs.openstack.org/identity/api/ext"\ - + "/OS-KSCATALOG/v1.0" - - def setUp(self): - """Prepare keystone for system tests""" - super(FunctionalTestCase, self).setUp() - - # Authenticate as admin user to establish admin_token - access = self.authenticate(self.admin_username, self.admin_password).\ - json['access'] - - self.admin_token = access['token']['id'] - self.admin_user_id = access['user']['id'] - - def fixture_create_service_admin(self): - if self.service_user: - return - # SERVICE ADMIN - password = unique_str() - self.service_user = self.fixture_create_user( - name="service-user-%s" % uuid.uuid4().hex, enabled=True, - password=password) - self.service_user['password'] = password - - self.service_admin_role = self.fetch_role_by_name( - self.service_admin_role_name, - assert_status=200).json['role'] - self.grant_global_role_to_user(self.service_user['id'], - self.service_admin_role['id'], - assert_status=201) - - self.service_user_token = self.authenticate(self.service_user['name'], - self.service_user['password']).\ - json['access']['token'] - self.service_admin_token = self.service_user_token['id'] - - def fixture_create_normal_tenant(self): - if self.tenant: - return - # TENANT - self.tenant = self.fixture_create_tenant( - name="tenant-%s" % uuid.uuid4().hex, enabled=True) - - def fixture_create_disabled_tenant(self): - if self.disabled_tenant: - return - # DISABLED TENANT - self.disabled_tenant = self.fixture_create_tenant( - name="disabled-tenant-%s" % uuid.uuid4().hex, enabled=False) - - def fixture_create_normal_user(self): - if self.user: - return - # USER - password = unique_str() - self.user = self.fixture_create_user( - name="user-%s" % uuid.uuid4().hex, enabled=True, - password=password) - self.user['password'] = password - - self.user_token = self.authenticate(self.user['name'], - self.user['password']).\ - json['access']['token'] - - def fixture_create_tenant_user(self): - if self.tenant_user: - return - self.fixture_create_tenant() - # USER with DEFAULT TENANT - password = unique_str() - self.tenant_user = self.fixture_create_user( - name="user_in_tenant-%s" % uuid.uuid4().hex, enabled=True, - tenant_id=self.tenant.id, password=password) - self.tenant_user['password'] = password - - self.tenant_user_token = self.authenticate(self.tenant_user['name'], - self.tenant_user['password'], - self.tenant.id).\ - json['access']['token'] - - def fixture_create_disabled_user_and_token(self): - if self.disabled_user: - return - self.fixture_create_normal_tenant() - # DISABLED USER - self.disabled_user = self.fixture_create_user( - name="disabled_user-%s" % uuid.uuid4().hex, enabled=False) - - # TOKEN for DISABLED user - token = self.fixture_create_token( - id="disabled-user-tenant-token-%s" % uuid.uuid4().hex, - user_id=self.disabled_user.id, - tenant_id=self.tenant.id, - expires=datetime.datetime.now() + datetime.timedelta(1)) - self.disabled_admin_token = token.id - - def fixture_create_expired_token(self): - if self.expired_admin_token: - return - self.fixture_create_normal_tenant() - # EXPIRED token (for enabled user) - token = self.fixture_create_token( - id="expired-admin-token-%s" % uuid.uuid4().hex, - user_id=self.admin_user_id, - tenant_id=self.tenant.id, - expires=datetime.datetime.now() - datetime.timedelta(1)) - self.expired_admin_token = token.id - - def authenticate(self, user_name=None, user_password=None, tenant_id=None, - **kwargs): - user_name = optional_str(user_name) - user_password = optional_str(user_password) - - data = { - "auth": { - "passwordCredentials": { - "username": user_name, - "password": user_password}}} - if tenant_id: - data["auth"]["tenantId"] = tenant_id - - return self.post_token(as_json=data, **kwargs) - - def authenticate_D5(self, user_name=None, user_password=None, - tenant_id=None, **kwargs): - user_name = optional_str(user_name) - user_password = optional_str(user_password) - - data = {"passwordCredentials": { - "username": user_name, - "password": user_password}} - if tenant_id: - data["passwordCredentials"]["tenantId"] = tenant_id - - return self.post_token(as_json=data, **kwargs) - - def authenticate_using_token(self, token, tenant_id=None, - **kwargs): - - data = { - "auth": { - "token": { - "id": token}}} - - if tenant_id: - data["auth"]["tenantId"] = tenant_id - - return self.post_token(as_json=data, **kwargs) - - def validate_token(self, token_id=None, tenant_id=None, **kwargs): - token_id = optional_str(token_id) - - if tenant_id: - # validate scoped token - return self.get_token_belongsto(token_id, tenant_id, **kwargs) - else: - # validate unscoped token - return self.get_token(token_id, **kwargs) - - def remove_token(self, token_id=None, **kwargs): - token_id = optional_str(token_id) - return self.delete_token(token_id, **kwargs) - - def create_tenant(self, tenant_name=None, tenant_description=None, - tenant_enabled=True, **kwargs): - """Creates a tenant for testing - - The tenant name and description are generated from UUIDs. - """ - tenant_name = optional_str(tenant_name) - tenant_description = optional_str(tenant_description) - - data = { - "tenant": { - "name": tenant_name, - "description": tenant_description, - "enabled": tenant_enabled}} - - return self.post_tenant(as_json=data, **kwargs) - - def list_tenants(self, **kwargs): - return self.get_tenants(**kwargs) - - def fetch_tenant(self, tenant_id=None, **kwargs): - tenant_id = optional_str(tenant_id) - return self.get_tenant(tenant_id, **kwargs) - - def fetch_tenant_by_name(self, tenant_name=None, **kwargs): - tenant_name = optional_str(tenant_name) - if tenant_name: - return self.get_tenant_by_name(tenant_name, **kwargs) - - def update_tenant(self, tenant_id=None, tenant_name=None, - tenant_description=None, tenant_enabled=True, **kwargs): - tenant_id = optional_str(tenant_id) - tenant_description = optional_str(tenant_description) - - data = {"tenant": {}} - - if tenant_name is not None: - data['tenant']['name'] = tenant_name - - data['tenant']['description'] = tenant_description - - if tenant_enabled is not None: - data['tenant']['enabled'] = tenant_enabled - - return self.post_tenant_for_update(tenant_id, as_json=data, **kwargs) - - def list_tenant_users(self, tenant_id, role_id=None, **kwargs): - tenant_id = optional_str(tenant_id) - if role_id: - return self.get_tenant_users_by_role(tenant_id, role_id, **kwargs) - else: - return self.get_tenant_users(tenant_id, **kwargs) - - def remove_tenant(self, tenant_id=None, **kwargs): - tenant_id = optional_str(tenant_id) - return self.delete_tenant(tenant_id, **kwargs) - - def create_user(self, user_name=None, user_password=None, user_email=None, - tenant_id=None, user_enabled=True, **kwargs): - """Creates a user for testing - - The user name is generated from UUIDs. - """ - user_name = optional_str(user_name) - user_password = optional_str(user_password) - user_email = optional_email(user_email) - - data = { - "user": { - "password": user_password, - "name": user_name, - "tenantId": tenant_id, - "email": user_email, - "enabled": user_enabled}} - - return self.post_user(as_json=data, **kwargs) - - def create_user_with_known_password(self, **kwargs): - """Manually injects the new user's password into the response data""" - - password = unique_str() - r = self.create_user(user_password=password, **kwargs) - r.json['user']['password'] = password - return r - - def list_users(self, **kwargs): - return self.get_users(**kwargs) - - def fetch_user(self, user_id=None, **kwargs): - user_id = optional_str(user_id) - return self.get_user(user_id, **kwargs) - - def fetch_user_by_name(self, user_name=None, **kwargs): - user_name = optional_str(user_name) - return self.query_user(user_name, **kwargs) - - def update_user(self, user_id=None, user_email=None, user_enabled=None, - user_name=None, **kwargs): - user_id = optional_str(user_id) - - data = {"user": {}} - - if user_email is not None: - data['user']['email'] = user_email - - if user_enabled is not None: - data['user']['enabled'] = user_enabled - if user_name is not None: - data['user']['name'] = user_name - return self.post_user_for_update(user_id, as_json=data, **kwargs) - - def update_user_password(self, user_id=None, user_password=None, **kwargs): - user_id = optional_str(user_id) - user_password = optional_str(user_password) - - data = {"user": {"password": user_password}} - return self.put_user_password(user_id, as_json=data, **kwargs) - - def update_user_tenant(self, user_id=None, tenant_id=None, **kwargs): - user_id = optional_str(user_id) - tenant_id = optional_str(tenant_id) - - data = {"user": {"tenantId": tenant_id}} - return self.put_user_tenant(user_id, as_json=data, **kwargs) - - def _enable_disable_user(self, user_id, user_enabled, **kwargs): - """Private function to enable and disable a user. - - Use enable_user() and disable_user() instead.""" - data = {"user": {"enabled": user_enabled}} - - return self.put_user_enabled(user_id, as_json=data, **kwargs) - - def enable_user(self, user_id=None, **kwargs): - user_id = optional_str(user_id) - return self._enable_disable_user(user_id, True, **kwargs) - - def disable_user(self, user_id=None, **kwargs): - user_id = optional_str(user_id) - return self._enable_disable_user(user_id, False, **kwargs) - - def remove_user(self, user_id=None, **kwargs): - user_id = optional_str(user_id) - return self.delete_user(user_id, **kwargs) - - def grant_role_to_user(self, user_id=None, role_id=None, tenant_id=None, - **kwargs): - user_id = optional_str(user_id) - role_id = optional_str(role_id) - tenant_id = optional_str(tenant_id) - return self.put_user_role(user_id, role_id, tenant_id, **kwargs) - - def grant_global_role_to_user(self, user_id=None, role_id=None, - **kwargs): - user_id = optional_str(user_id) - role_id = optional_str(role_id) - return self.put_user_role(user_id, role_id, None, **kwargs) - - def revoke_global_role_from_user(self, - user_id=None, role_id=None, **kwargs): - user_id = optional_str(user_id) - role_id = optional_str(role_id) - return self.delete_user_role(user_id, role_id, **kwargs) - - def revoke_role_from_user(self, - user_id=None, role_id=None, tenant_id=None, **kwargs): - user_id = optional_str(user_id) - role_id = optional_str(role_id) - tenant_id = optional_str(tenant_id) - return self.delete_user_role(user_id, tenant_id, **kwargs) - - def create_role(self, role_name=None, role_description=None, - service_id=None, service_name=None, **kwargs): - """Creates a role for testing - - The role name and description are generated from UUIDs. - """ - if service_name and not role_name: - role_name = "%s:%s" % (service_name, optional_str(role_name)) - else: - role_name = optional_str(role_name) - role_description = optional_str(role_description) - - data = { - "role": { - "name": role_name, - "description": role_description}} - - if service_id is not None: - data['role']['serviceId'] = service_id - - return self.post_role(as_json=data, **kwargs) - - def list_roles(self, service_id=None, **kwargs): - if service_id is None: - return self.get_roles(**kwargs) - else: - return self.get_roles_by_service(service_id, **kwargs) - - def fetch_role(self, role_id=None, **kwargs): - role_id = optional_str(role_id) - return self.get_role(role_id, **kwargs) - - def fetch_role_by_name(self, role_name=None, **kwargs): - role_name = optional_str(role_name) - return self.get_role_by_name(role_name, **kwargs) - - def remove_role(self, role_id=None, **kwargs): - role_id = optional_str(role_id) - return self.delete_role(role_id, **kwargs) - - def create_service(self, service_name=None, service_type=None, - service_description=None, **kwargs): - service_name = optional_str(service_name) - if service_type is None: - service_type = ['compute', 'identity', 'image-service', - 'object-store', 'ext:extension-service' - ][random.randrange(5)] - service_description = optional_str(service_description) - data = { - "OS-KSADM:service": { - "name": service_name, - "type": service_type, - "description": service_description}} - return self.post_service(as_json=data, **kwargs) - - def list_services(self, **kwargs): - return self.get_services(**kwargs) - - def fetch_service(self, service_id=None, **kwargs): - service_id = optional_str(service_id) - return self.get_service(service_id, **kwargs) - - def fetch_service_by_name(self, service_name=None, **kwargs): - service_name = optional_str(service_name) - return self.get_service_by_name(service_name, **kwargs) - - def remove_service(self, service_id=None, **kwargs): - service_id = optional_str(service_id) - self.delete_service(service_id, **kwargs) - - def create_endpoint_for_tenant(self, tenant_id=None, - endpoint_template_id=None, **kwargs): - tenant_id = optional_str(tenant_id) - endpoint_template_id = optional_str(endpoint_template_id) - - data = {"OS-KSCATALOG:endpointTemplate": {"id": endpoint_template_id}} - - return self.post_tenant_endpoint(tenant_id, as_json=data, **kwargs) - - def list_tenant_endpoints(self, tenant_id=None, **kwargs): - tenant_id = optional_str(tenant_id) - return self.get_tenant_endpoints(tenant_id, **kwargs) - - def remove_endpoint_from_tenant(self, tenant_id=None, endpoint_id=None, - **kwargs): - tenant_id = optional_str(tenant_id) - endpoint_id = optional_str(endpoint_id) - - """TODO: Should this be an 'endpoint_id' or 'endpoint_template_id'??""" - return self.delete_tenant_endpoint(tenant_id, endpoint_id, **kwargs) - - def remove_tenant_endpoint(self, tenant_id=None, endpoint_id=None, - **kwargs): - tenant_id = optional_str(tenant_id) - endpoint_id = optional_str(endpoint_id) - - """TODO: Should this be an 'endpoint_id' or 'endpoint_template_id'??""" - return self.delete_tenant_endpoint(tenant_id, endpoint_id, **kwargs) - - def list_endpoint_templates(self, service_id=None, **kwargs): - if service_id is None: - return self.get_endpoint_templates(**kwargs) - else: - return self.get_endpoint_templates_by_service(service_id, **kwargs) - - def create_endpoint_template(self, region=None, name=None, type=None, - public_url=None, admin_url=None, internal_url=None, enabled=True, - is_global=True, version_id=None, - version_list=None, version_info=None, **kwargs): - - region = optional_str(region) - name = optional_str(name) - type = optional_str(type) - public_url = optional_url(public_url) - admin_url = optional_url(admin_url) - internal_url = optional_url(internal_url) - version_id = optional_str(version_id)[:20] - version_list = optional_str(version_list) - version_info = optional_str(version_info) - - data = { - "OS-KSCATALOG:endpointTemplate": { - "region": region, - "name": name, - "type": type, - "publicURL": public_url, - "adminURL": admin_url, - "internalURL": internal_url, - "enabled": enabled, - "global": is_global, - "versionId": version_id, - "versionInfo": version_info, - "versionList": version_list}} - return self.post_endpoint_template(as_json=data, **kwargs) - - def remove_endpoint_template(self, endpoint_template_id=None, **kwargs): - endpoint_template_id = optional_str(endpoint_template_id) - return self.delete_endpoint_template(endpoint_template_id, **kwargs) - - def fetch_endpoint_template(self, endpoint_template_id, **kwargs): - endpoint_template_id = optional_str(endpoint_template_id) - return self.get_endpoint_template(endpoint_template_id, **kwargs) - - def update_endpoint_template(self, endpoint_template_id=None, region=None, - name=None, type=None, public_url=None, admin_url=None, - internal_url=None, enabled=None, is_global=None, - version_id=None, version_list=None, version_info=None, **kwargs): - - data = {"OS-KSCATALOG:endpointTemplate": {}} - - if region is not None: - data['OS-KSCATALOG:endpointTemplate']['region'] = region - - if name is not None: - data['OS-KSCATALOG:endpointTemplate']['name'] = name - - if type is not None: - data['OS-KSCATALOG:endpointTemplate']['type'] = type - - if public_url is not None: - data['OS-KSCATALOG:endpointTemplate']['publicURL'] = public_url - - if admin_url is not None: - data['OS-KSCATALOG:endpointTemplate']['adminURL'] = admin_url - - if internal_url is not None: - data['OS-KSCATALOG:endpointTemplate']['internalURL'] = internal_url - - if enabled is not None: - data['OS-KSCATALOG:endpointTemplate']['enabled'] = enabled - - if is_global is not None: - data['OS-KSCATALOG:endpointTemplate']['global'] = is_global - - if version_id is not None: - data['OS-KSCATALOG:endpointTemplate']['versionId'] = version_id - - if version_list is not None: - data['OS-KSCATALOG:endpointTemplate']['versionList'] = version_list - - if version_info is not None: - data['OS-KSCATALOG:endpointTemplate']['versionInfo'] = version_info - - return self.put_endpoint_template(endpoint_template_id, as_json=data, - **kwargs) - - def fetch_user_credentials(self, user_id=None, **kwargs): - user_id = optional_str(user_id) - return self.get_user_credentials(user_id, **kwargs) - - def fetch_password_credentials(self, user_id=None, **kwargs): - user_id = optional_str(user_id) - return self.get_user_credentials_by_type( - user_id, 'passwordCredentials', **kwargs) - - def create_password_credentials(self, user_id, user_name, - password=None, **kwargs): - user_id = optional_str(user_id) - password = optional_str(password) - data = { - "passwordCredentials": { - "username": user_name, - "password": password}} - return self.post_credentials(user_id, as_json=data, **kwargs) - - def update_password_credentials(self, user_id, user_name, - password=None, **kwargs): - user_id = optional_str(user_id) - password = optional_str(password) - data = { - "passwordCredentials": { - "username": user_name, - "password": password}} - return self.post_credentials_by_type( - user_id, 'passwordCredentials', as_json=data, **kwargs) - - def delete_password_credentials(self, user_id, **kwargs): - user_id = optional_str(user_id) - return self.delete_user_credentials_by_type( - user_id, 'passwordCredentials', **kwargs) - - def check_urls_for_regular_user(self, service_catalog): - self.assertIsNotNone(service_catalog) - for x in range(0, len(service_catalog)): - endpoints = service_catalog[x]['endpoints'] - for y in range(0, len(endpoints)): - endpoint = endpoints[y] - for key in endpoint: - #Checks whether adminURL is not present. - self.assertNotEquals(key, 'adminURL') - - def check_urls_for_regular_user_xml(self, service_catalog): - self.assertIsNotNone(service_catalog) - services = service_catalog.findall('{%s}service' % self.xmlns) - self.assertIsNotNone(services) - for service in services: - endpoints = service.findall('{%s}endpoint' % self.xmlns) - self.assertIsNotNone(endpoints) - for endpoint in endpoints: - #Checks whether adminURL is not present. - self.assertIsNone(endpoint.get('adminURL')) - self.assertIsNotNone(service_catalog) - - def check_urls_for_admin_user(self, service_catalog): - self.assertIsNotNone(service_catalog) - for x in range(0, len(service_catalog)): - endpoints = service_catalog[x]['endpoints'] - is_admin__url_present = None - for y in range(0, len(endpoints)): - endpoint = endpoints[y] - for key in endpoint: - if key == 'adminURL': - is_admin__url_present = True - self.assertTrue(is_admin__url_present, - "Admin API does not return admin URL") - - def check_urls_for_admin_user_xml(self, service_catalog): - self.assertIsNotNone(service_catalog) - services = service_catalog.findall('{%s}service' % self.xmlns) - self.assertIsNotNone(services) - is_admin_url_present = None - for service in services: - endpoints = service.findall('{%s}endpoint' % self.xmlns) - self.assertIsNotNone(endpoints) - for endpoint in endpoints: - if endpoint.get('adminURL'): - is_admin_url_present = True - self.assertTrue(is_admin_url_present, - "Admin API does not return admin URL") - - -class HeaderApp(object): - """ - Dummy WSGI app the returns HTTP headers in the body - - This is useful for making sure the headers we want - aer being passwed down to the downstream WSGI app. - """ - def __init__(self): - pass - - def __call__(self, env, start_response): - self.request = Request.blank('', environ=env) - body = '' - for key in env: - if key.startswith('HTTP_'): - body += '%s: %s\n' % (key, env[key]) - return Response(status="200 OK", - body=body)(env, start_response) - - -class BlankApp(object): - """ - Dummy WSGI app - does not do anything - """ - def __init__(self): - pass - - def __call__(self, env, start_response): - self.request = Request.blank('', environ=env) - return Response(status="200 OK", - body={})(env, start_response) - - -class MiddlewareTestCase(FunctionalTestCase): - """ - Base class to run tests for Keystone WSGI middleware. - """ - use_server = True - - def _setup_test_middleware(self): - test_middleware = None - if isinstance(self.middleware, tuple): - test_middleware = HeaderApp() - for filter in self.middleware: - test_middleware = \ - filter.filter_factory(self.settings)(test_middleware) - else: - test_middleware = \ - self.middleware.filter_factory(self.settings)(HeaderApp()) - return test_middleware - - def setUp(self, middleware, settings=None): - super(MiddlewareTestCase, self).setUp() - if settings is None: - settings = {'delay_auth_decision': '0', - 'auth_host': client_tests.TEST_TARGET_SERVER_ADMIN_ADDRESS, - 'auth_port': client_tests.TEST_TARGET_SERVER_ADMIN_PORT, - 'auth_protocol': - client_tests.TEST_TARGET_SERVER_ADMIN_PROTOCOL, - 'auth_uri': ('%s://%s:%s/' % \ - (client_tests.TEST_TARGET_SERVER_SERVICE_PROTOCOL, - client_tests.TEST_TARGET_SERVER_SERVICE_ADDRESS, - client_tests.TEST_TARGET_SERVER_SERVICE_PORT)), - 'admin_token': self.admin_token, - 'admin_user': self.admin_username, - 'admin_password': self.admin_password} - cert_file = isSsl() - if cert_file: - settings['certfile'] = cert_file - self.settings = settings - self.middleware = middleware - self.test_middleware = self._setup_test_middleware() - - name = unique_str() - r = self.create_tenant(tenant_name=name, assert_status=201) - self.tenant = r.json.get('tenant') - - user_name = unique_str() - password = unique_str() - r = self.create_user(user_name=user_name, - user_password=password, - tenant_id=self.tenant['id']) - self.tenant_user = r.json.get('user') - self.tenant_user['password'] = password - - access = self.authenticate(user_name, password).\ - json['access'] - self.tenant_user_token = access['token'] - - self.services = {} - self.endpoint_templates = {} - for x in range(0, 5): - self.services[x] = self.create_service().json['OS-KSADM:service'] - self.endpoint_templates[x] = self.create_endpoint_template( - name=self.services[x]['name'], \ - type=self.services[x]['type']).\ - json['OS-KSCATALOG:endpointTemplate'] - self.create_endpoint_for_tenant(self.tenant['id'], - self.endpoint_templates[x]['id']) - - @unittest.skipIf(isSsl() or 'HP-IDM_Disabled' in os.environ, - "Skipping SSL or HP-IDM tests") - def test_with_service_id(self): - if isSsl() or ('HP-IDM_Disabled' in os.environ): - # TODO(zns): why is this not skipping with the decorator?! - raise unittest.SkipTest("Skipping SSL or HP-IDM tests") - # create a service role so the scope token validation will succeed - role_resp = self.create_role(service_name=self.services[0]['name']) - role = role_resp.json['role'] - self.grant_role_to_user(self.tenant_user['id'], - role['id'], self.tenant['id']) - auth_resp = self.authenticate(self.tenant_user['name'], - self.tenant_user['password'], - self.tenant['id'], assert_status=200) - user_token = auth_resp.json['access']['token']['id'] - self.settings['service_ids'] = "%s" % self.services[0]['id'] - test_middleware = self._setup_test_middleware() - resp = Request.blank('/', - headers={'X-Auth-Token': user_token}) \ - .get_response(test_middleware) - self.assertEquals(resp.status_int, 200) - - # now give it a bogus service ID to make sure we get a 401 - self.settings['service_ids'] = "boguzz" - test_middleware = self._setup_test_middleware() - resp = Request.blank('/', - headers={'X-Auth-Token': user_token}) \ - .get_response(test_middleware) - self.assertEquals(resp.status_int, 401) - - @unittest.skipUnless(not isSsl() and 'HP-IDM_Disabled' in os.environ, - "Skipping since HP-IDM is enabled") - def test_with_service_id_with_hpidm_disabled(self): - # create a service role so the scope token validation will succeed - role_resp = self.create_role(service_name=self.services[0]['name']) - role = role_resp.json['role'] - self.grant_role_to_user(self.tenant_user['id'], - role['id'], self.tenant['id']) - auth_resp = self.authenticate(self.tenant_user['name'], - self.tenant_user['password'], - self.tenant['id'], assert_status=200) - user_token = auth_resp.json['access']['token']['id'] - self.settings['service_ids'] = "%s" % self.services[0]['id'] - test_middleware = self._setup_test_middleware() - resp = Request.blank('/', - headers={'X-Auth-Token': user_token}) \ - .get_response(test_middleware) - self.assertEquals(resp.status_int, 200) - - # now give it a bogus service ID to make sure it got ignored - self.settings['service_ids'] = "boguzz" - test_middleware = self._setup_test_middleware() - resp = Request.blank('/', - headers={'X-Auth-Token': user_token}) \ - .get_response(test_middleware) - self.assertEquals(resp.status_int, 200) - - def test_401_without_token(self): - resp = Request.blank('/').get_response(self.test_middleware) - self.assertEquals(resp.status_int, 401) - headers = resp.headers - self.assertTrue("WWW-Authenticate" in headers) - self.assertEquals(headers['WWW-Authenticate'], - "Keystone uri='%s://%s:%s/'" % \ - (client_tests.TEST_TARGET_SERVER_SERVICE_PROTOCOL, - client_tests.TEST_TARGET_SERVER_SERVICE_ADDRESS, - client_tests.TEST_TARGET_SERVER_SERVICE_PORT)) - - def test_401_bad_token(self): - resp = Request.blank('/', - headers={'X-Auth-Token': 'MADE_THIS_UP'}) \ - .get_response(self.test_middleware) - self.assertEquals(resp.status_int, 401) - - def test_200_good_token(self): - resp = Request.blank('/', - headers={'X-Auth-Token': self.tenant_user_token['id']}) \ - .get_response(self.test_middleware) - - self.assertEquals(resp.status_int, 200) - - headers = resp.body.split('\n') - - header = "HTTP_X_IDENTITY_STATUS: Confirmed" - self.assertTrue(header in headers, "Missing %s" % header) - - header = "HTTP_X_USER_ID: %s" % self.tenant_user['id'] - self.assertTrue(header in headers, "Missing %s" % header) - - header = "HTTP_X_USER_NAME: %s" % self.tenant_user['name'] - self.assertTrue(header in headers, "Missing %s" % header) - - header = "HTTP_X_TENANT_ID: %s" % self.tenant['id'] - self.assertTrue(header in headers, "Missing %s" % header) - - header = "HTTP_X_TENANT_NAME: %s" % self.tenant['name'] - self.assertTrue(header in headers, "Missing %s" % header) - - # These are here for legacy support and should be removed by F - header = "HTTP_X_TENANT: %s" % self.tenant['id'] - self.assertTrue(header in headers, "Missing %s" % header) - - header = "HTTP_X_USER: %s" % self.tenant_user['id'] - self.assertTrue(header in headers, "Missing %s" % header) diff --git a/keystone/test/functional/test_auth.py b/keystone/test/functional/test_auth.py deleted file mode 100644 index 661901fdfc..0000000000 --- a/keystone/test/functional/test_auth.py +++ /dev/null @@ -1,494 +0,0 @@ -import unittest2 as unittest -from keystone.test.functional import common - - -class TestAdminAuthentication(common.FunctionalTestCase): - """Test admin-side user authentication""" - - def test_bootstrapped_admin_user(self): - """Bootstrap script should create an 'admin' user with 'Admin' role""" - # Authenticate as admin - unscoped = self.authenticate(self.admin_username, - self.admin_password).json['access'] - - # Assert we get back a token with an expiration date - self.assertTrue(unscoped['token']['id']) - self.assertTrue(unscoped['token']['expires']) - - # Make sure there's no default tenant going on - self.assertIsNone(unscoped['token'].get('tenant')) - self.assertIsNone(unscoped['user'].get('tenantId')) - - -class TestAdminAuthenticationNegative(common.FunctionalTestCase): - """Negative test admin-side user authentication""" - - def test_admin_user_scoping_to_tenant_with_role(self): - """A Keystone Admin SHOULD be able to retrieve a scoped token... - - But only if the Admin has some Role on the Tenant other than Admin. - - Formerly: - test_admin_user_trying_to_scope_to_tenant_with_established_role - """ - tenant = self.create_tenant().json['tenant'] - role = self.create_role().json['role'] - - self.grant_role_to_user(self.admin_user_id, role['id'], tenant['id']) - - # Try to authenticate for this tenant - access = self.post_token(as_json={ - 'auth': { - 'token': { - 'id': self.admin_token}, - 'tenantId': tenant['id']}}).json['access'] - - self.assertEqual(access['token']['tenant']['id'], tenant['id']) - - def test_admin_user_trying_to_scope_to_tenant(self): - """A Keystone Admin should NOT be able to retrieve a scoped token""" - tenant = self.create_tenant().json['tenant'] - - # Try (and fail) to authenticate for this tenant - self.post_token(as_json={ - 'auth': { - 'token': { - 'id': self.admin_token}, - 'tenantId': tenant['id']}}, assert_status=401) - - def test_service_token_as_admin_token(self): - """Admin actions should fail for mere service tokens""" - - # Admin create a user - password = common.unique_str() - user = self.create_user(user_password=password).json['user'] - user['password'] = password - - # Replace our admin_token with a mere service token - self.admin_token = self.authenticate(user['name'], user['password']).\ - json['access']['token']['id'] - - # Try creating another user using the wrong token - self.create_user(assert_status=401) - - -class TestServiceAuthentication(common.FunctionalTestCase): - """Test service-side user authentication""" - - def setUp(self): - super(TestServiceAuthentication, self).setUp() - - # Create a user - password = common.unique_str() - self.user = self.create_user(user_password=password).json['user'] - self.tenant = self.create_tenant().json['tenant'] - self.user['password'] = password - self.services = {} - self.endpoint_templates = {} - self.services = self.create_service().json['OS-KSADM:service'] - self.endpoint_templates = self.create_endpoint_template( - name=self.services['name'], \ - type=self.services['type']).\ - json['OS-KSCATALOG:endpointTemplate'] - self.create_endpoint_for_tenant(self.tenant['id'], - self.endpoint_templates['id']) - - def test_authenticate_twice(self): - """Authenticating twice in a row should not result in a new token - - The original token should not have expired and should be provided again - """ - first_token = self.post_token(as_json={ - 'auth': { - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password']}}}).\ - json['access']['token']['id'] - - second_token = self.post_token(as_json={ - 'auth': { - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password']}}}).\ - json['access']['token']['id'] - - self.assertEqual(first_token, second_token) - - def test_authenticate_twice_for_tenant(self): - """Authenticating twice in a row should not result in a new token - - The original token should not have expired and should be provided again - """ - # Additonal setUp - role = self.create_role().json['role'] - self.grant_role_to_user(self.user['id'], role['id'], self.tenant['id']) - - self.service_token = self.post_token(as_json={ - 'auth': { - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password']}}}).\ - json['access']['token']['id'] - - tenant = self.service_request(method='GET', path='/tenants').\ - json['tenants'][0] - - first_token = self.post_token(as_json={ - 'auth': { - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password']}, - 'tenantId': tenant['id']}}).json['access']['token']['id'] - - second_token = self.post_token(as_json={ - 'auth': { - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password']}, - 'tenantId': tenant['id']}}).json['access']['token']['id'] - - # unscoped token should be different than the two scoped tokens - self.assertNotEqual(self.service_token, first_token) - self.assertEqual(first_token, second_token) - - def test_unscoped_user_auth(self): - """Admin should be able to validate a user's token""" - # Authenticate as user to get a token - self.service_token = self.post_token(as_json={ - 'auth': { - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password']}}}).\ - json['access']['token']['id'] - - # In the real world, the service user would then pass his/her token - # to some service that depends on keystone, which would then need to - # use keystone to validate the provided token. - - # Admin independently validates the user token - r = self.get_token(self.service_token) - self.assertEqual(r.json['access']['token']['id'], self.service_token) - self.assertTrue(r.json['access']['token']['expires']) - self.assertEqual(r.json['access']['user']['id'], self.user['id']) - self.assertEqual(r.json['access']['user']['name'], - self.user['name']) - self.assertEqual(r.json['access']['user']['roles'], []) - - def test_user_auth_with_role_on_tenant(self): - # Additonal setUp - role = self.create_role().json['role'] - self.grant_role_to_user(self.user['id'], role['id'], self.tenant['id']) - - # Create an unscoped token - unscoped = self.post_token(as_json={ - 'auth': { - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password']}}}).json['access'] - - # The token shouldn't be scoped to a tenant nor have roles just yet - self.assertIsNone(unscoped['token'].get('tenant')) - self.assertIsNotNone(unscoped.get('user')) - self.assertIsNotNone(unscoped['user'].get('roles')) - self.assertEqual(len(unscoped['user']['roles']), 0) - self.assertEqual(unscoped['user'].get('id'), self.user['id']) - self.assertEqual(unscoped['user'].get('name'), self.user['name']) - - # Request our tenant list as a service user - self.service_token = unscoped['token']['id'] - tenants = self.service_request(method='GET', path='/tenants').\ - json['tenants'] - self.service_token = None # Should become a service_request() param... - - # Our tenant should be the only tenant in the list - self.assertEqual(len(tenants), 1, tenants) - self.assertEqual(self.tenant['id'], tenants[0]['id']) - self.assertEqual(self.tenant['name'], tenants[0]['name']) - self.assertEqual(self.tenant['description'], tenants[0]['description']) - self.assertEqual(self.tenant['enabled'], tenants[0]['enabled']) - - # We can now get a token scoped to our tenant - scoped = self.post_token(as_json={ - 'auth': { - 'token': { - 'id': unscoped['token']['id']}, - 'tenantId': self.tenant['id']}}).json['access'] - - self.assertEqual(scoped['token']['tenant']['id'], self.tenant['id']) - self.assertEqual(scoped['token']['tenant']['name'], - self.tenant['name']) - self.assertIn('tenants', scoped['token']) - self.assertEqual(scoped['token']['tenants'][0]['id'], - self.tenant['id']) - self.assertEqual(scoped['token']['tenants'][0]['name'], - self.tenant['name']) - self.assertEqual( - scoped['user']['roles'][0]['id'], role['id']) - self.assertEqual(scoped['user']['roles'][0]['name'], role['name']) - self.assertEqual(scoped['user']['roles'][0]['tenantId'], - self.tenant['id']) - - # And an admin should be able to validate that our new token is scoped - r = self.validate_token(scoped['token']['id'], self.tenant['id']) - access = r.json['access'] - - self.assertEqual(access['user']['id'], self.user['id']) - self.assertEqual(access['user']['name'], self.user['name']) - self.assertEqual(access['token']['tenant']['id'], self.tenant['id']) - self.assertEqual(access['token']['tenant']['name'],\ - self.tenant['name']) - - def test_user_auth_with_role_on_tenant_xml(self): - # Additonal setUp - tenant = self.create_tenant().json['tenant'] - role = self.create_role().json['role'] - self.grant_role_to_user(self.user['id'], role['id'], tenant['id']) - - # Create an unscoped token - r = self.post_token(as_xml='' - '' - '' - '' % (self.user['name'], self.user['password'])) - - # The token shouldn't be scoped to a tenant nor have roles just yet - self.assertEqual(r.xml.tag, '{%s}access' % self.xmlns) - - token = r.xml.find('{%s}token' % self.xmlns) - self.assertIsNotNone(token) - self.assertIsNotNone(token.get('id')) - self.assertIsNotNone(token.get('expires')) - - user = r.xml.find('{%s}user' % self.xmlns) - self.assertIsNotNone(user) - self.assertEqual(user.get('id'), self.user['id']) - self.assertEqual(user.get('name'), self.user['name']) - self.assertIsNone(user.get('tenantId')) - - roles = user.find('{%s}roles' % self.xmlns) - self.assertIsNotNone(roles) - self.assertEqual(len(roles), 0) - - # Request our tenant list as a service user - self.service_token = token.get('id') - r = self.service_request(method='GET', path='/tenants', headers={ - 'Accept': 'application/xml'}) - - self.assertEqual(r.xml.tag, '{%s}tenants' % self.xmlns) - tenants = r.xml.findall('{%s}tenant' % self.xmlns) - - # Our tenant should be the only tenant in the list - self.assertEqual(len(tenants), 1, tenants) - self.assertEqual(tenant['id'], tenants[0].get('id')) - self.assertEqual(tenant['name'], tenants[0].get('name')) - self.assertEqual(str(tenant['enabled']).lower(), - tenants[0].get('enabled')) - description = tenants[0].find('{%s}description' % self.xmlns) - self.assertEqual(tenant['description'], description.text) - - # We can now get a token scoped to our tenant - r = self.post_token(as_xml='' - '' - '' - '' % (tenant['id'], self.user['name'], - self.user['password'])) - - self.assertEqual(r.xml.tag, '{%s}access' % self.xmlns) - - token = r.xml.find('{%s}token' % self.xmlns) - self.assertIsNotNone(token) - tenant_scope = token.find('{%s}tenant' % self.xmlns) - self.assertIsNotNone(tenant_scope) - self.assertEqual(tenant_scope.get('id'), tenant['id']) - self.assertEqual(tenant_scope.get('name'), tenant['name']) - - # And an admin should be able to validate that our new token is scoped - r = self.validate_token(token.get('id'), tenant['id'], headers={ - 'Accept': 'application/xml'}) - self.assertEqual(r.xml.tag, '{%s}access' % self.xmlns) - - token = r.xml.find('{%s}token' % self.xmlns) - self.assertIsNotNone(token) - tenant_scope = token.find('{%s}tenant' % self.xmlns) - self.assertIsNotNone(tenant_scope) - self.assertEqual(tenant_scope.get('id'), tenant['id']) - self.assertEqual(tenant_scope.get('name'), tenant['name']) - - user = r.xml.find('{%s}user' % self.xmlns) - self.assertIsNotNone(user) - self.assertEqual(user.get('id'), self.user['id']) - self.assertEqual(user.get('name'), self.user['name']) - self.assertIsNone(user.get('tenantId')) - - def test_scope_to_tenant_by_name(self): - # Additonal setUp - tenant = self.create_tenant().json['tenant'] - role = self.create_role().json['role'] - self.grant_role_to_user(self.user['id'], role['id'], tenant['id']) - - # Create an unscoped token - unscoped = self.post_token(as_json={ - 'auth': { - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password']}}}).json['access'] - - # We can now get a token scoped to our tenant - scoped = self.post_token(as_json={ - 'auth': { - 'token': { - 'id': unscoped['token']['id']}, - 'tenantName': tenant['name']}}).json['access'] - - self.assertEqual(scoped['token']['tenant']['id'], tenant['id']) - self.assertEqual(scoped['token']['tenant']['name'], tenant['name']) - - # And an admin should be able to validate that our new token is scoped - r = self.validate_token(scoped['token']['id'], tenant['id']) - access = r.json['access'] - - self.assertEqual(access['user']['id'], self.user['id']) - self.assertEqual(access['user']['name'], self.user['name']) - self.assertEqual(access['token']['tenant']['id'], tenant['id']) - self.assertEqual(access['token']['tenant']['name'], tenant['name']) - - def test_scope_to_tenant_by_name_with_credentials(self): - # Additonal setUp - tenant = self.create_tenant().json['tenant'] - role = self.create_role().json['role'] - self.grant_role_to_user(self.user['id'], role['id'], tenant['id']) - - scoped = self.post_token(as_json={ - 'auth': { - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password']}, - 'tenantName': tenant['name']}}).json['access'] - - self.assertEqual(scoped['token']['tenant']['id'], tenant['id']) - self.assertEqual(scoped['token']['tenant']['name'], tenant['name']) - - # And an admin should be able to validate that our new token is scoped - r = self.validate_token(scoped['token']['id'], tenant['id']) - access = r.json['access'] - - self.assertEqual(access['user']['id'], self.user['id']) - self.assertEqual(access['user']['name'], self.user['name']) - self.assertEqual(access['token']['tenant']['id'], tenant['id']) - self.assertEqual(access['token']['tenant']['name'], tenant['name']) - - def test_user_auth_against_nonexistent_tenant(self): - # Create an unscoped token - unscoped = self.post_token(as_json={ - 'auth': { - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password']}}}).json['access'] - - # Invalid tenant id - self.post_token(assert_status=401, as_json={ - 'auth': { - 'token': {'id': unscoped['token']['id']}, - 'tenantId': common.unique_str()}}) - - # Invalid tenant name - self.post_token(assert_status=401, as_json={ - 'auth': { - 'token': {'id': unscoped['token']['id']}, - 'tenantName': common.unique_str()}}) - - # Invalid tenant id - self.post_token(assert_status=401, as_json={ - 'auth': { - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password']}, - 'tenantId': common.unique_str()}}) - - # Invalid tenant name - self.post_token(assert_status=401, as_json={ - 'auth': { - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password']}, - 'tenantName': common.unique_str()}}) - - def test_scope_to_tenant_by_bad_request(self): - # Additonal setUp - tenant = self.create_tenant().json['tenant'] - role = self.create_role().json['role'] - self.grant_role_to_user(self.user['id'], role['id'], tenant['id']) - - # Create an unscoped token - unscoped = self.post_token(as_json={ - 'auth': { - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password']}}}).json['access'] - - # tenant Name & ID should never be provided together - self.post_token(as_json={ - 'auth': { - 'tokenId': unscoped['token']['id'], - 'tenantId': tenant['id'], - 'tenantName': tenant['name']}}, assert_status=400) - - def test_get_request_fails(self): - """GET /tokens should return a 404 (Github issue #5)""" - self.service_request(method='GET', path='/tokens', assert_status=404) - - def test_user_auth_with_malformed_request_body(self): - """Authenticating with unnexpected json returns a 400""" - # Authenticate as user to get a token - self.post_token(assert_status=400, as_json={ - 'this-is-completely-wrong': { - 'username': self.user['name'], - 'password': self.user['password']}}) - - def test_user_auth_with_wrong_name(self): - """Authenticating with an unknown username returns a 401""" - # Authenticate as user to get a token - self.post_token(assert_status=401, as_json={ - 'auth': {'passwordCredentials': { - 'username': 'this-is-completely-wrong', - 'password': self.user['password']}}}) - - def test_user_auth_with_no_name(self): - """Authenticating without a username returns a 400""" - # Authenticate as user to get a token - self.post_token(assert_status=400, as_json={ - 'auth': {'passwordCredentials': { - 'password': self.user['password']}}}) - - def test_user_auth_with_wrong_password(self): - """Authenticating with an invalid password returns a 401""" - # Authenticate as user to get a token - self.post_token(assert_status=401, as_json={ - 'auth': {'passwordCredentials': { - 'username': self.user['name'], - 'password': 'this-is-completely-wrong'}}}) - - def test_user_auth_with_no_password(self): - """Authenticating with an invalid password returns a 400""" - # Authenticate as user to get a token - self.post_token(assert_status=400, as_json={ - 'auth': {'passwordCredentials': { - 'username': self.user['name'], - 'password': None}}}) - - def test_user_auth_with_invalid_tenant(self): - """Authenticating with an invalid password returns a 401""" - # Authenticate as user to get a token - self.post_token(assert_status=401, as_json={ - 'auth': { - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password'], - }, - 'tenantId': 'this-is-completely-wrong'}}) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/functional/test_authentication.py b/keystone/test/functional/test_authentication.py deleted file mode 100644 index a029fbd7c4..0000000000 --- a/keystone/test/functional/test_authentication.py +++ /dev/null @@ -1,352 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 2010-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. - - -import unittest2 as unittest - -from keystone.test.functional import common - - -class AuthenticationTest(common.FunctionalTestCase): - def setUp(self, *args, **kwargs): - super(AuthenticationTest, self).setUp(*args, **kwargs) - - password = common.unique_str() - self.tenant = self.create_tenant().json['tenant'] - self.user = self.create_user(user_password=password, - tenant_id=self.tenant['id']).json['user'] - self.user['password'] = password - - self.services = {} - self.endpoint_templates = {} - self.services = self.create_service().json['OS-KSADM:service'] - self.endpoint_templates = self.create_endpoint_template( - name=self.services['name'], \ - type=self.services['type']).\ - json['OS-KSCATALOG:endpointTemplate'] - self.create_endpoint_for_tenant(self.tenant['id'], - self.endpoint_templates['id']) - - def test_authenticate_for_a_tenant(self): - response = self.authenticate(self.user['name'], self.user['password'], - self.tenant['id'], assert_status=200) - - self.assertIsNotNone(response.json['access']['token']) - service_catalog = response.json['access']['serviceCatalog'] - self.assertIsNotNone(service_catalog) - self.check_urls_for_regular_user(service_catalog) - - def test_authenticate_for_a_tenant_xml(self): - data = (' ' - '' - ' ') % ( - self.xmlns, self.tenant['id'], - self.user['name'], self.user['password']) - response = self.post_token(as_xml=data, assert_status=200) - - self.assertEquals(response.xml.tag, '{%s}access' % self.xmlns) - service_catalog = response.xml.find('{%s}serviceCatalog' % self.xmlns) - self.check_urls_for_regular_user_xml(service_catalog) - - def test_authenticate_for_a_tenant_on_admin_api(self): - response = self.authenticate(self.user['name'], self.user['password'], - self.tenant['id'], assert_status=200, request_type='admin') - - self.assertIsNotNone(response.json['access']['token']) - self.assertIsNotNone(response.json['access']['serviceCatalog']) - service_catalog = response.json['access']['serviceCatalog'] - self.check_urls_for_regular_user(service_catalog) - - def test_authenticate_for_a_tenant_xml_on_admin_api(self): - data = (' ' - '' - ' ') % ( - self.xmlns, self.tenant['id'], - self.user['name'], self.user['password']) - response = self.post_token(as_xml=data, assert_status=200, - request_type='admin') - - self.assertEquals(response.xml.tag, '{%s}access' % self.xmlns) - service_catalog = response.xml.find('{%s}serviceCatalog' % self.xmlns) - self.check_urls_for_regular_user_xml(service_catalog) - - def test_authenticate_user_disabled(self): - self.disable_user(self.user['id']) - self.authenticate(self.user['name'], self.user['password'], - self.tenant['id'], assert_status=403) - - def test_authenticate_user_wrong(self): - data = { - "auth": { - "passwordCredentials": { - "username-field-completely-wrong": self.user['name'], - "password": self.user['password']}, - "tenantId": self.tenant['id']}} - self.post_token(as_json=data, assert_status=400) - - def test_authenticate_user_wrong_xml(self): - data = (' ' - '') % ( - self.user['name'], self.user['password'], self.tenant['id']) - - self.post_token(as_xml=data, assert_status=400) - - -class AuthenticationUsingTokenTest(common.FunctionalTestCase): - def setUp(self, *args, **kwargs): - super(AuthenticationUsingTokenTest, self).setUp(*args, **kwargs) - password = common.unique_str() - self.tenant = self.create_tenant().json['tenant'] - self.user = self.create_user(user_password=password, - tenant_id=self.tenant['id']).json['user'] - self.user['password'] = password - - self.services = {} - self.endpoint_templates = {} - for x in range(0, 5): - self.services[x] = self.create_service().json['OS-KSADM:service'] - self.endpoint_templates[x] = self.create_endpoint_template( - name=self.services[x]['name'], \ - type=self.services[x]['type']).\ - json['OS-KSCATALOG:endpointTemplate'] - self.create_endpoint_for_tenant(self.tenant['id'], - self.endpoint_templates[x]['id']) - self.token = self.authenticate(self.user['name'], - self.user['password']).json['access']['token']['id'] - - def test_authenticate_for_a_tenant_using_token(self): - response = self.authenticate_using_token(self.token, - self.tenant['id'], assert_status=200) - - self.assertIsNotNone(response.json['access']['token']) - service_catalog = response.json['access']['serviceCatalog'] - self.assertIsNotNone(service_catalog) - self.check_urls_for_regular_user(service_catalog) - - def test_authenticate_for_a_tenant_xml(self): - data = (' ' - '' - ' ') % ( - self.xmlns, self.tenant['id'], - self.token) - response = self.post_token(as_xml=data, assert_status=200) - - self.assertEquals(response.xml.tag, '{%s}access' % self.xmlns) - service_catalog = response.xml.find('{%s}serviceCatalog' % self.xmlns) - self.check_urls_for_regular_user_xml(service_catalog) - - def test_authenticate_for_a_tenant_on_admin_api(self): - response = self.authenticate_using_token(self.token, - self.tenant['id'], request_type='admin') - - self.assertIsNotNone(response.json['access']['token']) - self.assertIsNotNone(response.json['access']['serviceCatalog']) - service_catalog = response.json['access']['serviceCatalog'] - self.check_urls_for_regular_user(service_catalog) - - def test_authenticate_for_a_tenant_xml_on_admin_api(self): - data = (' ' - '' - ' ') % ( - self.xmlns, self.tenant['id'], - self.token) - response = self.post_token(as_xml=data, assert_status=200, - request_type='admin') - - self.assertEquals(response.xml.tag, '{%s}access' % self.xmlns) - service_catalog = response.xml.find('{%s}serviceCatalog' % self.xmlns) - self.check_urls_for_regular_user_xml(service_catalog) - - -class UnScopedAuthenticationTest(common.FunctionalTestCase): - def setUp(self, *args, **kwargs): - super(UnScopedAuthenticationTest, self).setUp(*args, **kwargs) - - self.tenant = self.create_tenant().json['tenant'] - self.user = self.create_user_with_known_password( - tenant_id=self.tenant['id']).json['user'] - - self.services = {} - self.endpoint_templates = {} - for x in range(0, 5): - self.services[x] = self.create_service().json['OS-KSADM:service'] - self.endpoint_templates[x] = self.create_endpoint_template( - name=self.services[x]['name'], \ - type=self.services[x]['type']).\ - json['OS-KSCATALOG:endpointTemplate'] - self.create_endpoint_for_tenant(self.tenant['id'], - self.endpoint_templates[x]['id']) - - def test_authenticate(self): - response = self.authenticate(self.user['name'], self.user['password'],\ - assert_status=200) - - self.assertIsNotNone(response.json['access']['token']) - service_catalog = response.json['access'].get('serviceCatalog') - self.assertIsNotNone(service_catalog, response.json) - self.check_urls_for_regular_user(service_catalog) - - def test_authenticate_xml(self): - data = (' ' - '' - ' ') % ( - self.xmlns, self.user['name'], - self.user['password']) - response = self.post_token(as_xml=data, assert_status=200) - - self.assertEquals(response.xml.tag, '{%s}access' % self.xmlns) - service_catalog = response.xml.find('{%s}serviceCatalog' % self.xmlns) - self.check_urls_for_regular_user_xml(service_catalog) - - def test_authenticate_on_admin_api(self): - response = self.authenticate(self.user['name'], self.user['password'], - assert_status=200, request_type='admin') - - self.assertIsNotNone(response.json['access'].get('token'), - response.json) - self.assertIsNotNone(response.json['access'].get('serviceCatalog'), - response.json) - service_catalog = response.json['access']['serviceCatalog'] - self.check_urls_for_regular_user(service_catalog) - - def test_authenticate_for_a_tenant_xml_on_admin_api(self): - data = (' ' - '' - ' ') % ( - self.xmlns, self.tenant['id'], - self.user['name'], self.user['password']) - response = self.post_token(as_xml=data, - assert_status=200, request_type='admin') - - self.assertEquals(response.xml.tag, '{%s}access' % self.xmlns) - service_catalog = response.xml.find('{%s}serviceCatalog' % self.xmlns) - self.check_urls_for_regular_user_xml(service_catalog) - - def test_authenticate_without_default_tenant(self): - # Create user with no default tenant set (but granted a role) - self.nodefaultuser = self.create_user_with_known_password()\ - .json['user'] - self.role = self.create_role().json['role'] - self.grant_role_to_user(self.nodefaultuser['id'], self.role['id'], - self.tenant['id']) - - response = self.authenticate(self.nodefaultuser['name'], - self.nodefaultuser['password'], - tenant_id=None, assert_status=200) - - self.assertIsNotNone(response.json['access']['token']) - self.assertNotIn('tenant', response.json['access']['token']) - - -class AdminUserAuthenticationTest(common.FunctionalTestCase): - def setUp(self, *args, **kwargs): - super(AdminUserAuthenticationTest, self).setUp(*args, **kwargs) - - password = common.unique_str() - self.tenant = self.create_tenant().json['tenant'] - self.user = self.create_user(user_password=password, - tenant_id=self.tenant['id']).json['user'] - self.role = self.get_role_by_name('Admin').json['role'] - self.grant_global_role_to_user(self.user['id'], self.role['id']) - self.user['password'] = password - - self.services = {} - self.endpoint_templates = {} - for x in range(0, 5): - self.services[x] = self.create_service().json['OS-KSADM:service'] - self.endpoint_templates[x] = self.create_endpoint_template( - name=self.services[x]['name'], \ - type=self.services[x]['type']).\ - json['OS-KSCATALOG:endpointTemplate'] - self.create_endpoint_for_tenant(self.tenant['id'], - self.endpoint_templates[x]['id']) - - def test_authenticate(self): - response = self.authenticate(self.user['name'], self.user['password'],\ - assert_status=200) - - self.assertIsNotNone(response.json['access']['token']) - service_catalog = response.json['access']['serviceCatalog'] - self.assertIsNotNone(service_catalog) - self.check_urls_for_admin_user(service_catalog) - - def test_authenticate_xml(self): - data = (' ' - '' - ' ') % ( - self.xmlns, self.user['name'], - self.user['password']) - response = self.post_token(as_xml=data, assert_status=200) - - self.assertEquals(response.xml.tag, '{%s}access' % self.xmlns) - service_catalog = response.xml.find('{%s}serviceCatalog' % self.xmlns) - self.check_urls_for_admin_user_xml(service_catalog) - - def test_authenticate_for_a_tenant(self): - response = self.authenticate(self.user['name'], self.user['password'], - self.tenant['id'], assert_status=200) - - self.assertIsNotNone(response.json['access']['token']) - service_catalog = response.json['access']['serviceCatalog'] - self.assertIsNotNone(service_catalog) - self.check_urls_for_admin_user(service_catalog) - - def test_authenticate_for_a_tenant_xml(self): - data = (' ' - '' - ' ') % ( - self.xmlns, self.tenant['id'], - self.user['name'], self.user['password']) - response = self.post_token(as_xml=data, assert_status=200) - - self.assertEquals(response.xml.tag, '{%s}access' % self.xmlns) - service_catalog = response.xml.find('{%s}serviceCatalog' % self.xmlns) - self.check_urls_for_admin_user_xml(service_catalog) - - -class MultiTokenTest(common.FunctionalTestCase): - def setUp(self, *args, **kwargs): - super(MultiTokenTest, self).setUp(*args, **kwargs) - - self.tenants = {} - self.users = {} - for x in range(0, 2): - self.tenants[x] = self.create_tenant().json['tenant'] - - password = common.unique_str() - self.users[x] = self.create_user(user_password=password, - tenant_id=self.tenants[x]['id']).json['user'] - self.users[x]['password'] = password - - def test_unassigned_user(self): - self.authenticate(self.users[1]['name'], self.users[1]['password'], - self.tenants[0]['id'], assert_status=401) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/functional/test_credentials.py b/keystone/test/functional/test_credentials.py deleted file mode 100644 index 2db0157994..0000000000 --- a/keystone/test/functional/test_credentials.py +++ /dev/null @@ -1,231 +0,0 @@ -import unittest2 as unittest -from keystone.test.functional import common - - -class TestGetCredentials(common.FunctionalTestCase): - """Test Get credentials operations""" - - def setUp(self): - super(TestGetCredentials, self).setUp() - self.fixture_create_normal_user() - - def test_get_user_credentials(self): - password_credentials = self.fetch_user_credentials( - self.user['id']).json['credentials'][0]['passwordCredentials'] - self.assertEquals(password_credentials['username'], self.user['name']) - - def test_get_user_credentials_xml(self): - r = self.fetch_user_credentials(self.user['id'], - assert_status=200, headers={ - 'Accept': 'application/xml'}) - self.assertEquals(r.xml.tag, '{%s}credentials' % self.xmlns) - password_credentials =\ - r.xml.find('{%s}passwordCredentials' % self.xmlns) - self.assertEqual( - password_credentials.get('username'), self.user['name']) - - def test_get_user_credentials_using_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.fetch_user_credentials(self.user['id'], assert_status=403) - - def test_get_user_credentials_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.fetch_user_credentials(self.user['id'], assert_status=403) - - def test_get_user_credentials_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.fetch_user_credentials(self.user['name'], assert_status=403) - - def test_get_user_credentials_using_missing_token(self): - self.admin_token = '' - self.fetch_user_credentials(self.user['id'], assert_status=401) - - def test_get_user_credentials_using_invalid_token(self): - self.admin_token = common.unique_str() - self.fetch_user_credentials(self.user['id'], assert_status=401) - - -class TestGetPasswordCredentials(common.FunctionalTestCase): - """Test get password credentials operations""" - - def setUp(self): - super(TestGetPasswordCredentials, self).setUp() - self.fixture_create_normal_user() - - def test_get_user_credentials(self): - password_credentials = self.fetch_password_credentials( - self.user['id']).json['passwordCredentials'] - self.assertEquals(password_credentials['username'], self.user['name']) - - def test_get_user_credentials_xml(self): - r = self.fetch_password_credentials(self.user['id'], - assert_status=200, headers={ - 'Accept': 'application/xml'}) - password_credentials = r.xml - self.assertEqual( - password_credentials.get('username'), self.user['name']) - - def test_get_user_credentials_using_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.fetch_password_credentials(self.user['id'], assert_status=403) - - def test_get_user_credentials_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.fetch_password_credentials(self.user['id'], assert_status=403) - - def test_get_user_credentials_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.fetch_password_credentials(self.user['name'], assert_status=403) - - def test_get_user_credentials_using_missing_token(self): - self.admin_token = '' - self.fetch_password_credentials(self.user['id'], assert_status=401) - - def test_get_user_credentials_using_invalid_token(self): - self.admin_token = common.unique_str() - self.fetch_password_credentials(self.user['id'], assert_status=401) - - -class TestCreatePasswordCredentials(common.FunctionalTestCase): - """Test create password credentials operations""" - - def setUp(self): - super(TestCreatePasswordCredentials, self).setUp() - self.fixture_create_normal_user() - self.delete_user_credentials_by_type( - self.user['id'], 'passwordCredentials') - - def test_create_password_credentials(self): - self.create_password_credentials( - self.user['id'], self.user['name'], - assert_status=201) - - def test_create_password_credentials_using_empty_password(self): - self.create_password_credentials( - user_id=self.user['id'], user_name=self.user['name'], password='',\ - assert_status=400) - - def test_create_password_credentials_xml(self): - data = (' ' - '') % ( - self.xmlns, self.user['name'], 'passw0rd') - self.post_credentials(self.user['id'], as_xml=data, assert_status=201) - - def test_create_password_credentials_xml_using_empty_password(self): - data = (' ' - '') % ( - self.xmlns, self.user['name'], '') - self.post_credentials(self.user['id'], as_xml=data, assert_status=400) - - def test_create_password_credentials_twice(self): - self.create_password_credentials(self.user['id'], self.user['name'], - assert_status=201) - self.create_password_credentials(self.user['id'], self.user['name'], - assert_status=400) - - def test_create_password_credentials_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.create_password_credentials(self.user['id'], self.user['name'], - assert_status=403) - - def test_create_password_credentials_missing_token(self): - self.admin_token = '' - self.create_password_credentials(self.user['id'], self.user['name'], - assert_status=401) - - def test_create_password_credentials_invalid_token(self): - self.admin_token = common.unique_str() - self.create_password_credentials(self.user['id'], self.user['name'], - assert_status=401) - - -class TestUpdatePasswordCredentials(common.FunctionalTestCase): - """Test update password credentials operations""" - - def setUp(self): - super(TestUpdatePasswordCredentials, self).setUp() - self.fixture_create_normal_user() - - def test_update_password_credentials(self): - self.update_password_credentials(self.user['id'], self.user['name'], - assert_status=200) - - def test_update_password_credentials_xml(self): - data = (' ' - '') % ( - self.xmlns, self.user['name'], 'passw0rd') - self.post_credentials_by_type(self.user['id'], 'passwordCredentials', - as_xml=data, assert_status=200) - - def test_update_password_credentials_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.update_password_credentials(self.user['id'], self.user['name'], - assert_status=403) - - def test_update_password_credentials_missing_token(self): - self.admin_token = '' - self.update_password_credentials(self.user['id'], self.user['name'], - assert_status=401) - - def test_update_password_credentials_invalid_token(self): - self.admin_token = common.unique_str() - self.update_password_credentials(self.user['id'], self.user['name'], - assert_status=401) - - -class TestDeletePasswordCredentials(common.FunctionalTestCase): - """Test delete password credentials operations""" - - def setUp(self): - super(TestDeletePasswordCredentials, self).setUp() - self.fixture_create_normal_user() - - def test_delete_password_credentials(self): - self.delete_password_credentials(self.user['id'], - assert_status=204) - - def test_delete_password_credentials_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.delete_password_credentials(self.user['id'], - assert_status=403) - - def test_delete_password_credentials_missing_token(self): - self.admin_token = '' - self.delete_password_credentials(self.user['id'], - assert_status=401) - - def test_delete_password_credentials_invalid_token(self): - self.admin_token = common.unique_str() - self.delete_password_credentials(self.user['id'], - assert_status=401) - - -class TestAuthentication(common.FunctionalTestCase): - """Test authentication after a password update.""" - def setUp(self): - super(TestAuthentication, self).setUp() - self.fixture_create_normal_user() - - def test_authentication_after_password_change(self): - self.authenticate(self.user['name'], self.user['password'], - assert_status=200) - password = common.unique_str() - self.update_password_credentials(self.user['id'], self.user['name'], - password=password, assert_status=200) - self.authenticate(self.user['name'], password, - assert_status=200) - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/functional/test_endpoints.py b/keystone/test/functional/test_endpoints.py deleted file mode 100644 index 9f7c989ece..0000000000 --- a/keystone/test/functional/test_endpoints.py +++ /dev/null @@ -1,740 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 2010-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. - -import unittest2 as unittest -from keystone.test.functional import common - - -class EndpointTemplatesTest(common.FunctionalTestCase): - def setUp(self, *args, **kwargs): - super(EndpointTemplatesTest, self).setUp(*args, **kwargs) - - self.service = self.create_service().json['OS-KSADM:service'] - - self.endpoint_template = self.create_endpoint_template( - name=self.service['name'], \ - type=self.service['type']).\ - json['OS-KSCATALOG:endpointTemplate'] - - self.fixture_create_service_admin() - admin_token = self.admin_token - self.admin_token = self.service_admin_token - self.my_service = self.create_service().json['OS-KSADM:service'] - self.admin_token = admin_token - - -class CreateEndpointTemplatesTest(EndpointTemplatesTest): - def test_create_endpoint_template(self): - endpoint_template = self.create_endpoint_template( - name=self.service['name'], - type=self.service['type'], - assert_status=201).\ - json['OS-KSCATALOG:endpointTemplate'] - - self.assertIsNotNone(endpoint_template['id'], endpoint_template) - self.assertIsNotNone(endpoint_template['name'], endpoint_template) - self.assertIsNotNone(endpoint_template['type'], endpoint_template) - - def test_create_endpoint_template_with_empty_name(self): - self.create_endpoint_template( - name=self.service['name'], - type='', - assert_status=400) - - def test_create_endpoint_template_with_empty_type(self): - self.create_endpoint_template( - name='', - type=self.service['type'], - assert_status=400) - - def test_create_endpoint_template_xml(self): - region = common.unique_str() - public_url = common.unique_url() - admin_url = common.unique_url() - internal_url = common.unique_url() - enabled = True - is_global = True - - data = (' ' - '' - ) % (self.xmlns_kscatalog, region, self.service['name'], - self.service['type'], public_url, admin_url, internal_url, - enabled, is_global) - r = self.post_endpoint_template(as_xml=data, assert_status=201) - - self.assertEqual(r.xml.tag, - '{%s}endpointTemplate' % self.xmlns_kscatalog) - - self.assertIsNotNone(r.xml.get("id")) - self.assertEqual(r.xml.get("name"), self.service['name']) - self.assertEqual(r.xml.get("type"), self.service['type']) - self.assertEqual(r.xml.get("region"), region) - self.assertEqual(r.xml.get("publicURL"), public_url) - self.assertEqual(r.xml.get("adminURL"), admin_url) - self.assertEqual(r.xml.get("internalURL"), internal_url) - self.assertEqual(r.xml.get("enabled"), str(enabled).lower()) - self.assertEqual(r.xml.get("global"), str(is_global).lower()) - - def test_create_endpoint_template_xml_using_empty_type(self): - region = common.unique_str() - public_url = common.unique_url() - admin_url = common.unique_url() - internal_url = common.unique_url() - enabled = True - is_global = True - - data = (' ' - '' - ) % (self.xmlns_kscatalog, region, self.service['name'], - '', public_url, admin_url, internal_url, - enabled, is_global) - self.post_endpoint_template(as_xml=data, assert_status=400) - - def test_create_endpoint_template_xml_using_empty_name(self): - region = common.unique_str() - public_url = common.unique_url() - admin_url = common.unique_url() - internal_url = common.unique_url() - enabled = True - is_global = True - - data = (' ' - '' - ) % (self.xmlns_kscatalog, region, '', - self.service['type'], public_url, admin_url, internal_url, - enabled, is_global) - self.post_endpoint_template(as_xml=data, assert_status=400) - - def test_delete_endpoint_template_that_has_dependencies(self): - tenant = self.create_tenant().json['tenant'] - - self.create_endpoint_for_tenant(tenant['id'], - self.endpoint_template['id'], assert_status=201) - - self.remove_endpoint_template(self.endpoint_template['id'], - assert_status=204) - - def test_create_endpoint_template_using_service_admin_token(self): - self.admin_token = self.service_admin_token - endpoint_template = self.create_endpoint_template( - name=self.my_service['name'], - type=self.my_service['type'], - assert_status=201).\ - json['OS-KSCATALOG:endpointTemplate'] - - self.assertIsNotNone(endpoint_template['id']) - self.assertEqual(endpoint_template['name'], self.my_service['name']) - self.assertEqual(endpoint_template['type'], self.my_service['type']) - - -class GetEndpointTemplatesTest(EndpointTemplatesTest): - def test_get_endpoint_templates(self): - r = self.list_endpoint_templates(assert_status=200) - self.assertIsNotNone(r.json['OS-KSCATALOG:endpointTemplates']) - - def test_get_endpoint_templates_using_service_admin_token(self): - self.admin_token = self.service_admin_token - r = self.list_endpoint_templates(assert_status=200) - self.assertIsNotNone(r.json['OS-KSCATALOG:endpointTemplates']) - - def test_get_endpoint_templates_using_expired_auth_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.list_endpoint_templates(assert_status=403) - - def test_get_endpoint_templates_using_disabled_auth_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.list_endpoint_templates(assert_status=403) - - def test_get_endpoint_templates_using_missing_auth_token(self): - self.admin_token = '' - self.list_endpoint_templates(assert_status=401) - - def test_get_endpoint_templates_using_invalid_auth_token(self): - self.admin_token = common.unique_str() - self.list_endpoint_templates(assert_status=401) - - def test_get_endpoint_templates_xml(self): - r = self.get_endpoint_templates(assert_status=200, headers={ - 'Accept': 'application/xml'}) - self.assertEqual(r.xml.tag, - "{%s}endpointTemplates" % self.xmlns_kscatalog) - - def test_get_endpoint_templates_xml_expired_auth_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.get_endpoint_templates(assert_status=403, headers={ - 'Accept': 'application/xml'}) - - def test_get_endpoint_templates_xml_disabled_auth_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.get_endpoint_templates(assert_status=403, headers={ - 'Accept': 'application/xml'}) - - def test_get_endpoint_templates_xml_missing_auth_token(self): - self.admin_token = '' - self.get_endpoint_templates(assert_status=401, headers={ - 'Accept': 'application/xml'}) - - def test_get_endpoint_templates_xml_invalid_auth_token(self): - self.admin_token = common.unique_str() - self.get_endpoint_templates(assert_status=401, headers={ - 'Accept': 'application/xml'}) - - -class GetEndpointTemplatesByServiceTest(EndpointTemplatesTest): - def test_get_endpoint_templates(self): - r = self.list_endpoint_templates( - service_id=self.service['id'], assert_status=200) - self.assertIsNotNone(r.json['OS-KSCATALOG:endpointTemplates']) - self.assertEquals(len(r.json['OS-KSCATALOG:endpointTemplates']), 1) - self.assertEquals(r.json['OS-KSCATALOG:endpointTemplates'][0]['name'], - self.service['name']) - self.assertEquals(r.json['OS-KSCATALOG:endpointTemplates'][0]['type'], - self.service['type']) - - def test_get_endpoint_templates_using_service_admin_token(self): - self.admin_token = self.service_admin_token - r = self.list_endpoint_templates(service_id=self.service['id'], - assert_status=200) - self.assertIsNotNone(r.json['OS-KSCATALOG:endpointTemplates']) - self.assertEquals(len(r.json['OS-KSCATALOG:endpointTemplates']), 1) - self.assertEquals(r.json['OS-KSCATALOG:endpointTemplates'][0]['name'], - self.service['name']) - self.assertEquals( - r.json['OS-KSCATALOG:endpointTemplates'][0]['type'], - self.service['type']) - - def test_get_endpoint_templates_using_expired_auth_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.list_endpoint_templates( - service_id=self.service['id'], assert_status=403) - - def test_get_endpoint_templates_using_disabled_auth_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.list_endpoint_templates( - service_id=self.service['id'], assert_status=403) - - def test_get_endpoint_templates_using_missing_auth_token(self): - self.admin_token = '' - self.list_endpoint_templates(service_id=self.service['id'], - assert_status=401) - - def test_get_endpoint_templates_using_invalid_auth_token(self): - self.admin_token = common.unique_str() - self.list_endpoint_templates(service_id=self.service['id'], - assert_status=401) - - def test_get_endpoint_templates_xml(self): - r = self.get_endpoint_templates_by_service( - service_id=self.service['id'], - assert_status=200, headers={'Accept': 'application/xml'}) - self.assertEqual(r.xml.tag, - "{%s}endpointTemplates" % self.xmlns_kscatalog) - endpoint_template = r.xml.find( - '{%s}endpointTemplate' % self.xmlns_kscatalog) - self.assertIsNotNone(endpoint_template) - self.assertEqual(endpoint_template.get('name'), self.service['name']) - self.assertEqual(endpoint_template.get('type'), self.service['type']) - - def test_get_endpoint_templates_xml_expired_auth_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.get_endpoint_templates_by_service( - service_id=self.service['id'], assert_status=403, headers={ - 'Accept': 'application/xml'}) - - def test_get_endpoint_templates_xml_disabled_auth_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.get_endpoint_templates_by_service( - service_id=self.service['id'], assert_status=403, headers={ - 'Accept': 'application/xml'}) - - def test_get_endpoint_templates_xml_missing_auth_token(self): - self.admin_token = '' - self.get_endpoint_templates_by_service( - service_id=self.service['id'], assert_status=401, headers={ - 'Accept': 'application/xml'}) - - def test_get_endpoint_templates_xml_invalid_auth_token(self): - self.admin_token = common.unique_str() - self.get_endpoint_templates_by_service( - service_id=self.service['id'], assert_status=401, headers={ - 'Accept': 'application/xml'}) - - -class GetEndpointTemplateTest(EndpointTemplatesTest): - def test_get_endpoint(self): - r = self.fetch_endpoint_template(self.endpoint_template['id']) - self.assertIsNotNone(r.json['OS-KSCATALOG:endpointTemplate']) - -# def test_get_endpoint_using_service_admin_token(self): -# self.admin_token = service_admin_token -# r = self.fetch_endpoint_template(self.endpoint_template['id']) -# self.assertIsNotNone(r.json['endpointTemplate']) - - def test_get_endpoint_using_expired_auth_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.fetch_endpoint_template(self.endpoint_template['id'], - assert_status=403) - - def test_get_endpoint_using_disabled_auth_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.fetch_endpoint_template(self.endpoint_template['id'], - assert_status=403) - - def test_get_endpoint_using_missing_auth_token(self): - self.admin_token = '' - self.fetch_endpoint_template(self.endpoint_template['id'], - assert_status=401) - - def test_get_endpoint_using_invalid_auth_token(self): - self.admin_token = common.unique_str() - self.fetch_endpoint_template(self.endpoint_template['id'], - assert_status=401) - - def test_get_endpoint_xml(self): - r = self.get_endpoint_template(self.endpoint_template['id'], - headers={'Accept': 'application/xml'}, assert_status=200) - - self.assertEqual(r.xml.tag, - "{%s}endpointTemplate" % self.xmlns_kscatalog) - - def test_non_existent_get_endpoint(self): - self.fetch_endpoint_template('99999999', - assert_status=404) - - -class UpdateEndpointTemplateTest(EndpointTemplatesTest): - def test_update_endpoint(self): - self.update_endpoint_template(self.endpoint_template['id'], - name=self.service['name'], type=self.service['type'], - assert_status=201) -# self.assertIsNotNone(r.json['endpointTemplate'].get('enabled'), r.json) - - def test_update_endpoint_with_empty_name(self): - self.update_endpoint_template(self.endpoint_template['id'], - name='', type=self.service['type'], - assert_status=400) - - def test_update_endpoint_with_empty_type(self): - self.update_endpoint_template(self.endpoint_template['id'], - name=self.service['name'], type='', - assert_status=400) - - def test_update_endpoint_xml(self): - region = common.unique_str() - public_url = common.unique_url() - admin_url = common.unique_url() - internal_url = common.unique_url() - enabled = True - is_global = True - - data = (' ' - '') % ( - self.xmlns_kscatalog, region, - self.service['name'], self.service['type'], - public_url, admin_url, internal_url, - enabled, is_global) - r = self.put_endpoint_template(self.endpoint_template['id'], - as_xml=data, assert_status=201, headers={ - 'Accept': 'application/xml'}) - - self.assertEqual(r.xml.tag, - '{%s}endpointTemplate' % self.xmlns_kscatalog) - - self.assertIsNotNone(r.xml.get("id")) - self.assertEqual(r.xml.get("name"), self.service['name']) - self.assertEqual(r.xml.get("type"), self.service['type']) - self.assertEqual(r.xml.get("region"), region) - self.assertEqual(r.xml.get("publicURL"), public_url) - self.assertEqual(r.xml.get("adminURL"), admin_url) - self.assertEqual(r.xml.get("internalURL"), internal_url) - self.assertEqual(r.xml.get("enabled"), str(enabled).lower()) - self.assertEqual(r.xml.get("global"), str(is_global).lower()) - -# def test_update_endpoint_using_service_admin_token(self): -# self.admin_token = service_admin_token -# region = common.unique_str() -# public_url = common.unique_url() -# admin_url = common.unique_url() -# internal_url = common.unique_url() -# enabled = True -# is_global = True -# -# r = self.update_endpoint_template(self.endpoint_template['id'], -# region, self.service['id'], public_url, admin_url, internal_url, -# enabled, is_global, assert_status=201) -# -# endpoint_template = r.json.get('endpointTemplate') -# -# self.assertIsNotNone(endpoint_template.get("id"), r.json) -# self.assertEqual(endpoint_template.get("serviceId"), -# self.service['id']) -# self.assertEqual(endpoint_template.get("region"), region) -# self.assertEqual(endpoint_template.get("publicURL"), public_url) -# self.assertEqual(endpoint_template.get("adminURL"), admin_url) -# self.assertEqual(endpoint_template.get("internalURL"), internal_url) -# self.assertEqual(endpoint_template.get("enabled"), -# str(enabled).lower()) -# self.assertEqual(endpoint_template.get("global"), -# str(is_global).lower()) - -# def test_update_endpoint_xml_using_service_admin_token(self): -# self.admin_token = service_admin_token -# -# self.test_update_endpoint_xml() - - def test_update_endpoint_template_with_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.update_endpoint_template(self.endpoint_template['id'], - assert_status=403) - - def test_update_endpoint_template_with_missing_token(self): - self.admin_token = '' - self.update_endpoint_template(self.endpoint_template['id'], - assert_status=401) - - def test_update_endpoint_template_with_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.update_endpoint_template(self.endpoint_template['id'], - assert_status=403) - - def test_update_endpoint_template_with_invalid_token(self): - self.admin_token = common.unique_str() - self.update_endpoint_template(self.endpoint_template['id'], - assert_status=401) - - def test_update_invalid_endpoint_template(self): - self.update_endpoint_template(assert_status=404) - - -class CreateEndpointsTest(EndpointTemplatesTest): - - def setUp(self, *args, **kwargs): - super(CreateEndpointsTest, self).setUp(*args, **kwargs) - self.fixture_create_normal_tenant() - - def test_endpoint_create_json_using_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.create_endpoint_for_tenant(self.tenant['id'], - self.endpoint_template['id'], assert_status=403) - - def test_endpoint_create_json_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.create_endpoint_for_tenant(self.tenant['id'], - self.endpoint_template['id'], assert_status=403) - - def test_endpoint_create_json_using_missing_token(self): - self.admin_token = '' - self.create_endpoint_for_tenant(self.tenant['id'], - self.endpoint_template['id'], assert_status=401) - - def test_endpoint_create_json_using_invalid_token(self): - self.admin_token = common.unique_str() - self.create_endpoint_for_tenant(self.tenant['id'], - self.endpoint_template['id'], assert_status=401) - - def test_endpoint_create_json(self): - endpoint = self.create_endpoint_for_tenant(self.tenant['id'], - self.endpoint_template['id'], assert_status=201).json['endpoint'] - self.assertEqual(str(endpoint["name"]), str(self.service['name'])) - self.assertEqual(str(endpoint["type"]), str(self.service['type'])) - self.assertEqual(endpoint["region"], self.endpoint_template["region"]) - self.assertEqual(endpoint["publicURL"], - self.endpoint_template["publicURL"]) - self.assertEqual(endpoint["adminURL"], - self.endpoint_template["adminURL"]) - self.assertEqual(endpoint["internalURL"], - self.endpoint_template["internalURL"]) - -# def test_endpoint_create_using_service_admin_token(self): -# self.admin_token = service_admin_token -# self.create_endpoint_template(assert_status=201) - - def test_endpoint_create_xml(self): - data = (' ' - '' - '') % (self.xmlns_kscatalog, - self.endpoint_template["id"]) - r = self.post_tenant_endpoint(self.tenant['id'], - as_xml=data, assert_status=201, - headers={'Accept': 'application/xml'}) - - self.assertEqual(r.xml.tag, - '{%s}endpoint' % self.xmlns) - - self.assertIsNotNone(r.xml.get("id")) - self.assertEqual(r.xml.get("name"), self.service['name']) - self.assertEqual(r.xml.get("type"), self.service['type']) - self.assertEqual(r.xml.get("region"), self.endpoint_template["region"]) - self.assertEqual(r.xml.get("publicURL"), - self.endpoint_template["publicURL"]) - self.assertEqual(r.xml.get("adminURL"), - self.endpoint_template["adminURL"]) - self.assertEqual(r.xml.get("internalURL"), - self.endpoint_template["internalURL"]) - - def test_endpoint_create_xml_using_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - data = (' ' - '') % ( - self.xmlns_kscatalog, - common.unique_str(), - self.service['name'], - self.service['type'], common.unique_url(), - common.unique_url(), common.unique_url(), True, True) - self.post_endpoint_template(as_xml=data, assert_status=403, headers={ - 'Accept': 'application/xml'}) - - def test_endpoint_create_xml_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - data = (' ' - '') % ( - self.xmlns_kscatalog, common.unique_str(), - self.service['name'], - self.service['type'], common.unique_url(), - common.unique_url(), common.unique_url(), True, True) - self.post_endpoint_template(as_xml=data, assert_status=403, headers={ - 'Accept': 'application/xml'}) - - def test_endpoint_create_xml_using_missing_token(self): - self.admin_token = '' - data = (' ' - '') % ( - self.xmlns_kscatalog, - common.unique_str(), - self.service['name'], self.service['type'], - common.unique_url(), - common.unique_url(), common.unique_url(), True, True) - self.post_endpoint_template(as_xml=data, assert_status=401, headers={ - 'Accept': 'application/xml'}) - - def test_endpoint_create_xml_using_invalid_token(self): - self.admin_token = common.unique_str() - data = (' ' - '') % ( - self.xmlns_kscatalog, common.unique_str(), - self.service['name'], - self.service['type'], common.unique_url(), - common.unique_url(), common.unique_url(), True, True) - self.post_endpoint_template(as_xml=data, assert_status=401, headers={ - 'Accept': 'application/xml'}) - - -class GetEndpointsTest(EndpointTemplatesTest): - def setUp(self, *args, **kwargs): - super(GetEndpointsTest, self).setUp(*args, **kwargs) - self.fixture_create_normal_tenant() - - def test_get_tenant_endpoint_xml(self): - self.get_tenant_endpoints(self.tenant['id'], assert_status=200, - headers={"Accept": "application/xml"}) - - def test_get_tenant_endpoint_xml_using_expired_auth_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.get_tenant_endpoints(self.tenant['id'], assert_status=403, - headers={"Accept": "application/xml"}) - - def test_get_tenant_endpoint_xml_using_disabled_auth_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.get_tenant_endpoints(self.tenant['id'], assert_status=403, - headers={"Accept": "application/xml"}) - - def test_get_tenant_endpoint_xml_using_missing_auth_token(self): - self.admin_token = '' - self.get_tenant_endpoints(self.tenant['id'], assert_status=401, - headers={"Accept": "application/xml"}) - - def test_get_tenant_endpoint_xml_using_invalid_auth_token(self): - self.admin_token = common.unique_str() - self.get_tenant_endpoints(self.tenant['id'], assert_status=401, - headers={"Accept": "application/xml"}) - - def test_get_tenant_endpoint_json(self): - r = self.get_tenant_endpoints(self.tenant['id'], assert_status=200) - self.assertIsNotNone(r.json.get('endpoints'), r.json) - - def test_get_tenant_endpoint_json_using_expired_auth_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.get_tenant_endpoints(self.tenant['id'], assert_status=403) - - def test_get_endpoint_json_using_disabled_auth_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.get_tenant_endpoints(self.tenant['id'], assert_status=403) - - def test_get_endpoint_json_using_missing_auth_token(self): - self.admin_token = '' - self.get_tenant_endpoints(self.tenant['id'], assert_status=401) - - def test_get_endpoint_json_using_invalid_auth_token(self): - self.admin_token = common.unique_str() - self.get_tenant_endpoints(self.tenant['id'], assert_status=401) - - -class DeleteEndpointsTest(EndpointTemplatesTest): - def setUp(self, *args, **kwargs): - super(DeleteEndpointsTest, self).setUp(*args, **kwargs) - self.fixture_create_normal_tenant() - self.create_endpoint_for_tenant(self.tenant['id'], - self.endpoint_template['id']) - - def test_delete_endpoint(self): - self.delete_tenant_endpoint(self.tenant['id'], - self.endpoint_template['id'], assert_status=204) - - def test_delete_endpoint_using_expired_auth_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.delete_tenant_endpoint(self.tenant['id'], - self.endpoint_template['id'], assert_status=403) - - def test_delete_endpoint_using_disabled_auth_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.delete_tenant_endpoint(self.tenant['id'], - self.endpoint_template['id'], assert_status=403) - - def test_delete_endpoint_using_missing_auth_token(self): - self.admin_token = '' - self.delete_tenant_endpoint(self.tenant['id'], - self.endpoint_template['id'], assert_status=401) - - def test_delete_endpoint_using_invalid_auth_token(self): - self.admin_token = common.unique_str() - self.delete_tenant_endpoint(self.tenant['id'], - self.endpoint_template['id'], assert_status=401) - - -class GetEndpointsForTokenTest(EndpointTemplatesTest): - def setUp(self, *args, **kwargs): - super(GetEndpointsForTokenTest, self).setUp(*args, **kwargs) - self.fixture_create_normal_tenant() - self.fixture_create_tenant_user() - - self.services = {} - self.endpoint_templates = {} - for x in range(0, 5): - self.services[x] = self.create_service().json['OS-KSADM:service'] - self.endpoint_templates[x] = self.create_endpoint_template( - name=self.services[x]['name'], \ - type=self.services[x]['type']).\ - json['OS-KSCATALOG:endpointTemplate'] - self.create_endpoint_for_tenant(self.tenant['id'], - self.endpoint_templates[x]['id']) - - def test_get_token_endpoints_xml(self): - self.get_token_endpoints(self.tenant_user_token['id'], - assert_status=200, - headers={"Accept": "application/xml"}) - - def test_get_token_endpoints_xml_using_expired_auth_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.get_token_endpoints(self.tenant_user_token['id'], - assert_status=403, - headers={"Accept": "application/xml"}) - - def test_get_token_endpoints_xml_using_disabled_auth_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.get_token_endpoints(self.tenant_user_token['id'], - assert_status=403, - headers={"Accept": "application/xml"}) - - def test_get_token_endpoints_xml_using_missing_auth_token(self): - self.admin_token = '' - self.get_token_endpoints(self.tenant_user_token['id'], - assert_status=401, - headers={"Accept": "application/xml"}) - - def test_get_token_endpoints_xml_using_invalid_auth_token(self): - self.admin_token = common.unique_str() - self.get_token_endpoints(self.tenant_user_token['id'], - assert_status=401, - headers={"Accept": "application/xml"}) - - def test_get_token_endpoints_json(self): - r = self.get_token_endpoints(self.tenant_user_token['id'], - assert_status=200) - self.assertIsNotNone(r.json.get('endpoints'), r.json) - - def test_get_token_endpoints_json_using_expired_auth_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.get_token_endpoints(self.tenant_user_token['id'], - assert_status=403) - - def test_get_token_endpoints_json_using_disabled_auth_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.get_token_endpoints(self.tenant_user_token['id'], - assert_status=403) - - def test_get_token_endpoints_json_using_missing_auth_token(self): - self.admin_token = '' - self.get_token_endpoints(self.tenant_user_token['id'], - assert_status=401) - - def test_get_token_endpoints_json_using_invalid_auth_token(self): - self.admin_token = common.unique_str() - self.get_token_endpoints(self.tenant_user_token['id'], - assert_status=401) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/functional/test_extensions.py b/keystone/test/functional/test_extensions.py deleted file mode 100644 index ba090bda0c..0000000000 --- a/keystone/test/functional/test_extensions.py +++ /dev/null @@ -1,258 +0,0 @@ -import unittest2 as unittest -from keystone.test.functional import common - - -class TestHPIDMTokensExtension(common.FunctionalTestCase): - """Test HP-IDM token validation extension""" - - def setUp(self): - super(TestHPIDMTokensExtension, self).setUp() - password = common.unique_str() - self.user = self.create_user(user_password=password).json['user'] - self.user['password'] = password - self.tenant = self.create_tenant().json['tenant'] - self.service = self.create_service().json['OS-KSADM:service'] - r = self.create_role(service_name=self.service['name']) - self.role = r.json['role'] - self.another_service = self.create_service().json['OS-KSADM:service'] - self.service_with_no_users = self.create_service().\ - json['OS-KSADM:service'] - ar = self.create_role(service_name=self.another_service['name']) - self.another_role = ar.json['role'] - rnu = self.create_role(service_name=self.service_with_no_users['name']) - self.role_with_no_users = rnu.json['role'] - rns = self.create_role() - self.role_with_no_service = rns.json['role'] - self.grant_role_to_user(self.user['id'], - self.role['id'], self.tenant['id']) - self.grant_role_to_user(self.user['id'], - self.role_with_no_service['id'], - self.tenant['id']) - self.grant_role_to_user(self.user['id'], - self.another_role['id'], self.tenant['id']) - self.global_role = self.create_role().json['role'] - # crete a global role - self.put_user_role(self.user['id'], self.global_role['id'], None) - - def get_token_belongsto(self, token_id, tenant_id, service_ids, **kwargs): - """GET /tokens/{token_id}?belongsTo={tenant_id} - [&HP-IDM-serviceId={service_ids}]""" - serviceId_qs = "" - if service_ids: - serviceId_qs = "&HP-IDM-serviceId=%s" % (service_ids) - return self.admin_request(method='GET', - path='/tokens/%s?belongsTo=%s%s' % (token_id, tenant_id, - serviceId_qs), **kwargs) - - def check_token_belongs_to(self, token_id, tenant_id, service_ids, - **kwargs): - """HEAD /tokens/{token_id}?belongsTo={tenant_id} - [&HP-IDM-serviceId={service_ids}]""" - serviceId_qs = "" - if service_ids: - serviceId_qs = "&HP-IDM-serviceId=%s" % (service_ids) - return self.admin_request(method='HEAD', - path='/tokens/%s?belongsTo=%s%s' % (token_id, tenant_id, - serviceId_qs), **kwargs) - - @unittest.skipIf(common.isSsl(), - "Skipping SSL tests") - def test_token_validation_with_serviceId(self): - scoped = self.post_token(as_json={ - 'auth': { - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password']}, - 'tenantName': self.tenant['name']}}).json['access'] - - self.assertEqual(scoped['token']['tenant']['id'], self.tenant['id']) - self.assertEqual(scoped['token']['tenant']['name'], - self.tenant['name']) - # And an admin should be able to validate that our new token is scoped - r = self.get_token_belongsto(token_id=scoped['token']['id'], - tenant_id=self.tenant['id'], service_ids=self.service['id']) - access = r.json['access'] - - self.assertEqual(access['user']['id'], self.user['id']) - self.assertEqual(access['user']['name'], self.user['name']) - self.assertEqual(access['token']['tenant']['id'], self.tenant['id']) - self.assertEqual(access['token']['tenant']['name'], - self.tenant['name']) - - # make sure only the service roles are returned - self.assertIsNotNone(access['user'].get('roles')) - self.assertEqual(len(access['user']['roles']), 1) - self.assertEqual(access['user']['roles'][0]['name'], - self.role['name']) - - # make sure check token also works - self.check_token_belongs_to(token_id=scoped['token']['id'], - tenant_id=self.tenant['id'], service_ids=self.service['id'], - assert_status=200) - - @unittest.skipIf(common.isSsl(), - "Skipping SSL tests") - def test_token_validation_with_all_serviceId(self): - scoped = self.post_token(as_json={ - 'auth': { - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password']}, - 'tenantName': self.tenant['name']}}).json['access'] - - self.assertEqual(scoped['token']['tenant']['id'], self.tenant['id']) - self.assertEqual(scoped['token']['tenant']['name'], - self.tenant['name']) - # And an admin should be able to validate that our new token is scoped - service_ids = "%s,%s" % \ - (self.service['id'], self.another_service['id']) - r = self.get_token_belongsto(token_id=scoped['token']['id'], - tenant_id=self.tenant['id'], service_ids=service_ids) - access = r.json['access'] - - self.assertEqual(access['user']['id'], self.user['id']) - self.assertEqual(access['user']['name'], self.user['name']) - self.assertEqual(access['token']['tenant']['id'], self.tenant['id']) - self.assertEqual(access['token']['tenant']['name'], - self.tenant['name']) - - # make sure only the service roles are returned - self.assertIsNotNone(access['user'].get('roles')) - self.assertEqual(len(access['user']['roles']), 2) - role_names = map(lambda x: x['name'], access['user']['roles']) - self.assertTrue(self.role['name'] in role_names) - self.assertTrue(self.another_role['name'] in role_names) - - @unittest.skipIf(common.isSsl(), - "Skipping SSL tests") - def test_token_validation_with_no_user_service(self): - scoped = self.post_token(as_json={ - 'auth': { - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password']}, - 'tenantName': self.tenant['name']}}).json['access'] - - self.assertEqual(scoped['token']['tenant']['id'], self.tenant['id']) - self.assertEqual(scoped['token']['tenant']['name'], - self.tenant['name']) - # And an admin should be able to validate that our new token is scoped - service_ids = "%s,%s,%s" % (self.service['id'], - self.another_service['id'], - self.service_with_no_users['id']) - r = self.get_token_belongsto(token_id=scoped['token']['id'], - tenant_id=self.tenant['id'], service_ids=service_ids) - access = r.json['access'] - - self.assertEqual(access['user']['id'], self.user['id']) - self.assertEqual(access['user']['name'], self.user['name']) - self.assertEqual(access['token']['tenant']['id'], self.tenant['id']) - self.assertEqual(access['token']['tenant']['name'], - self.tenant['name']) - - # make sure only the service roles are returned, excluding the one - # with no users - self.assertIsNotNone(access['user'].get('roles')) - self.assertEqual(len(access['user']['roles']), 2) - role_names = map(lambda x: x['name'], access['user']['roles']) - self.assertTrue(self.role['name'] in role_names) - self.assertTrue(self.another_role['name'] in role_names) - - # make sure check token also works - self.check_token_belongs_to(token_id=scoped['token']['id'], - tenant_id=self.tenant['id'], service_ids=service_ids, - assert_status=200) - - @unittest.skipIf(common.isSsl(), - "Skipping SSL tests") - def test_token_validation_without_serviceId(self): - scoped = self.post_token(as_json={ - 'auth': { - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password']}, - 'tenantName': self.tenant['name']}}).json['access'] - - self.assertEqual(scoped['token']['tenant']['id'], self.tenant['id']) - self.assertEqual(scoped['token']['tenant']['name'], - self.tenant['name']) - # And an admin should be able to validate that our new token is scoped - r = self.get_token_belongsto(token_id=scoped['token']['id'], - tenant_id=self.tenant['id'], service_ids=None) - access = r.json['access'] - - self.assertEqual(access['user']['id'], self.user['id']) - self.assertEqual(access['user']['name'], self.user['name']) - self.assertEqual(access['token']['tenant']['id'], self.tenant['id']) - self.assertEqual(access['token']['tenant']['name'], - self.tenant['name']) - - # make sure all the roles are returned - self.assertIsNotNone(access['user'].get('roles')) - self.assertEqual(len(access['user']['roles']), 4) - role_names = map(lambda x: x['name'], access['user']['roles']) - self.assertTrue(self.role['name'] in role_names) - self.assertTrue(self.another_role['name'] in role_names) - self.assertTrue(self.global_role['name'] in role_names) - self.assertTrue(self.role_with_no_service['name'] in role_names) - - @unittest.skipIf(common.isSsl(), - "Skipping SSL tests") - def test_token_validation_with_global_service_id(self): - scoped = self.post_token(as_json={ - 'auth': { - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password']}, - 'tenantName': self.tenant['name']}}).json['access'] - - self.assertEqual(scoped['token']['tenant']['id'], self.tenant['id']) - self.assertEqual(scoped['token']['tenant']['name'], - self.tenant['name']) - service_ids = "%s,%s,global" % (self.service['id'], - self.another_service['id']) - r = self.get_token_belongsto(token_id=scoped['token']['id'], - tenant_id=self.tenant['id'], service_ids=service_ids) - access = r.json['access'] - - self.assertEqual(access['user']['id'], self.user['id']) - self.assertEqual(access['user']['name'], self.user['name']) - self.assertEqual(access['token']['tenant']['id'], self.tenant['id']) - self.assertEqual(access['token']['tenant']['name'], - self.tenant['name']) - - # make sure only the service roles are returned - self.assertIsNotNone(access['user'].get('roles')) - self.assertEqual(len(access['user']['roles']), 3) - role_names = map(lambda x: x['name'], access['user']['roles']) - self.assertTrue(self.role['name'] in role_names) - self.assertTrue(self.another_role['name'] in role_names) - self.assertTrue(self.global_role['name'] in role_names) - - @unittest.skipIf(common.isSsl(), - "Skipping SSL tests") - def test_token_validation_with_bogus_service_id(self): - scoped = self.post_token(as_json={ - 'auth': { - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password']}, - 'tenantName': self.tenant['name']}}).json['access'] - - self.assertEqual(scoped['token']['tenant']['id'], self.tenant['id']) - self.assertEqual(scoped['token']['tenant']['name'], - self.tenant['name']) - service_ids = "%s,%s,boguzzz" % (self.service['id'], - self.another_service['id']) - self.get_token_belongsto(token_id=scoped['token']['id'], - tenant_id=self.tenant['id'], service_ids=service_ids, - assert_status=401) - - # make sure check token also works - self.check_token_belongs_to(token_id=scoped['token']['id'], - tenant_id=self.tenant['id'], service_ids=service_ids, - assert_status=401) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/functional/test_issue_85.py b/keystone/test/functional/test_issue_85.py deleted file mode 100644 index a44d147c2c..0000000000 --- a/keystone/test/functional/test_issue_85.py +++ /dev/null @@ -1,43 +0,0 @@ -import unittest2 as unittest -from keystone.test.functional import common - - -class TestIssue85(common.FunctionalTestCase): - """Illustrates github issue #85""" - - def test_disabling_tenant_disables_token(self): - """Disabling a tenant should invalidate previously-issued tokens""" - # Create a user for a specific tenant - tenant = self.create_tenant().json['tenant'] - password = common.unique_str() - user = self.create_user(user_password=password, - tenant_id=tenant['id']).json['user'] - user['password'] = password - - # Authenticate as user to get a token *for a specific tenant* - user_token = self.authenticate(user['name'], user['password'], - tenant['id']).json['access']['token']['id'] - - # Validate and check that token belongs to tenant - print self.get_token(user_token).body - tenant_id = self.get_token(user_token).\ - json['access']['token']['tenant']['id'] - self.assertEqual(tenant_id, tenant['id']) - - # Disable tenant - r = self.admin_request(method='POST', - path='/tenants/%s' % tenant['id'], - as_json={ - 'tenant': { - 'name': tenant['name'], - 'description': 'description', - 'enabled': False}}) - self.assertEquals(r.json['tenant']['enabled'], False) - - # Assert that token belonging to disabled tenant is invalid - r = self.admin_request(path='/tokens/%s?belongsTo=%s' % - (user_token, tenant['id']), assert_status=403) - self.assertTrue(r.json['tenantDisabled'], 'Tenant is disabled') - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/functional/test_roles.py b/keystone/test/functional/test_roles.py deleted file mode 100644 index 0444e7382a..0000000000 --- a/keystone/test/functional/test_roles.py +++ /dev/null @@ -1,559 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 2010-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. - -import unittest2 as unittest -from keystone.test.functional import common - - -class RolesTest(common.FunctionalTestCase): - expected_roles = ['Admin', 'Member', 'KeystoneServiceAdmin'] - - def setUp(self, *args, **kwargs): - super(RolesTest, self).setUp(*args, **kwargs) - - def tearDown(self, *args, **kwargs): - super(RolesTest, self).tearDown(*args, **kwargs) - - -class CreateRolesTest(RolesTest): - def test_create_role(self): - self.create_role(assert_status=201) - - def test_create_role_using_blank_name(self): - self.create_role(role_name='', assert_status=400) - - def test_create_role_using_service_token(self): - user = self.create_user_with_known_password().json['user'] - self.admin_token = self.authenticate(user['name'], - user['password']).json['access']['token']['id'] - self.create_role(assert_status=401) - - def test_create_roles_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.create_role(assert_status=403) - - def test_create_roles_using_missing_token(self): - self.admin_token = '' - self.create_role(assert_status=401) - - def test_create_roles_using_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.create_role(assert_status=403) - - def test_create_roles_using_invalid_token(self): - self.admin_token = common.unique_str() - self.create_role(assert_status=401) - - def test_create_role_mapped_to_a_service(self): - service = self.create_service().json['OS-KSADM:service'] - role_name = service['name'] + ':' + common.unique_str() - role = self.create_role(role_name=role_name, - service_id=service['id']).json['role'] - self.assertEqual(role_name, role['name']) - self.assertEqual(service['id'], role['serviceId']) - - def test_create_role_mapped_to_a_service_xml(self): - service = self.create_service().json['OS-KSADM:service'] - name = service['name'] + ':' + common.unique_str() - description = common.unique_str() - - data = (' ' - '') % ( - self.xmlns, name, description, service['id']) - id = self.post_role(assert_status=201, as_xml=data).xml.get('id') - self.assertIsNotNone(id) - - role = self.fetch_role(id, assert_status=200).json['role'] - self.assertEqual(role['name'], name) - self.assertEqual(role['description'], description) - self.assertEqual(role['serviceId'], service['id']) - - def test_create__service_role_using_incorrect_role_name(self): - """ test_create_role_mapped_to_a_service_using_incorrect_role_name """ - self.create_role(common.unique_str(), service_id=common.unique_str(), - assert_status=400) - - def test_create_role_using_empty_name_xml(self): - name = '' - description = common.unique_str() - data = (' ' - '') % ( - self.xmlns, name, description) - self.post_role(assert_status=400, as_xml=data) - - -class DeleteRoleTest(RolesTest): - def setUp(self, *args, **kwargs): - super(DeleteRoleTest, self).setUp(*args, **kwargs) - - self.role = self.create_role().json['role'] - - def test_delete_roles_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.delete_role(self.role['id'], assert_status=403) - - def test_delete_roles_using_missing_token(self): - self.admin_token = '' - self.delete_role(self.role['id'], assert_status=401) - - def test_delete_roles_using_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.delete_role(self.role['id'], assert_status=403) - - def test_delete_roles_using_invalid_token(self): - self.admin_token = common.unique_str() - self.delete_role(self.role['id'], assert_status=401) - - def test_create_and_delete_role_that_has_references(self): - tenant = self.create_tenant().json['tenant'] - user = self.create_user(tenant_id=tenant['id']).json['user'] - self.grant_role_to_user(user['id'], self.role['id'], tenant['id']) - self.remove_role(self.role['id'], assert_status=204) - - def test_create_role_mapped_to_a_service(self): - service = self.create_service().json['OS-KSADM:service'] - role_name = service['name'] + ':' + common.unique_str() - role = self.create_role(role_name=role_name, - service_id=service['id']).json['role'] - self.assertEqual(service['id'], role['serviceId']) - - def test_create_role_mapped_to_a_service_xml(self): - service = self.create_service().json['OS-KSADM:service'] - name = service['name'] + ':' + common.unique_str() - description = common.unique_str() - - data = (' ' - '') % ( - self.xmlns, name, description, service['id']) - role_id = self.post_role(assert_status=201, as_xml=data).xml.get('id') - - role = self.fetch_role(role_id, assert_status=200).json['role'] - self.assertEqual(role['id'], role_id) - self.assertEqual(role['serviceId'], service['id']) - - def test_create_service_role_using_incorrect_role_name(self): - """ Formerly: - test_create_role_mapped_to_a_service_using_incorrect_role_name""" - self.create_role(common.unique_str(), service_id=common.unique_str(), - assert_status=400) - - -class GetRolesByServiceTest(common.FunctionalTestCase): - def setUp(self, *args, **kwargs): - super(GetRolesByServiceTest, self).setUp(*args, **kwargs) - service = self.create_service().json['OS-KSADM:service'] - role_name = service['name'] + ':' + common.unique_str() - role = self.create_role(role_name=role_name, - service_id=service['id']).json['role'] - self.service_id = service['id'] - - def tearDown(self, *args, **kwargs): - super(GetRolesByServiceTest, self).tearDown(*args, **kwargs) - - def test_get_roles(self): - r = self.list_roles(assert_status=200, service_id=self.service_id) - self.assertTrue(len(r.json['roles'])) - - def test_get_roles_xml(self): - r = self.get_roles_by_service(assert_status=200, headers={ - 'Accept': 'application/xml'}, service_id=self.service_id,) - self.assertEquals(r.xml.tag, '{%s}roles' % self.xmlns) - roles = r.xml.findall('{%s}role' % self.xmlns) - - for role in roles: - self.assertIsNotNone(role.get('id')) - - def test_get_roles_exp_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.get_roles_by_service( - service_id=self.service_id, assert_status=403) - - def test_get_roles_exp_token_xml(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.get_roles_by_service( - service_id=self.service_id, assert_status=403, headers={ - 'Accept': 'application/xml'}) - - -class GetRolesTest(RolesTest): - def test_get_roles(self): - r = self.list_roles(assert_status=200) - self.assertTrue(len(r.json['roles'])) - - def test_get_roles_xml(self): - r = self.get_roles(assert_status=200, headers={ - 'Accept': 'application/xml'}) - self.assertEquals(r.xml.tag, '{%s}roles' % self.xmlns) - roles = r.xml.findall('{%s}role' % self.xmlns) - - for role in roles: - self.assertIsNotNone(role.get('id')) - - def test_get_roles_exp_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.get_roles(assert_status=403) - - def test_get_roles_exp_token_xml(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.get_roles(assert_status=403, headers={ - 'Accept': 'application/xml'}) - - -class GetRoleTest(RolesTest): - def setUp(self, *args, **kwargs): - super(GetRoleTest, self).setUp(*args, **kwargs) - self.role = self.create_role().json['role'] - - def test_get_role(self): - role = self.fetch_role(self.role['id'], assert_status=200).json['role'] - self.assertEqual(role['id'], self.role['id']) - self.assertEqual(role['name'], self.role['name']) - self.assertEqual(role['description'], self.role['description']) - self.assertEqual(role.get('serviceId'), self.role.get('serviceId')) - - def test_get_role_xml(self): - r = self.get_role(self.role['id'], assert_status=200, headers={ - 'Accept': 'application/xml'}) - self.assertEqual(r.xml.tag, '{%s}role' % self.xmlns) - self.assertEqual(r.xml.get('id'), self.role['id']) - self.assertEqual(r.xml.get('name'), self.role['name']) - self.assertEqual(r.xml.get('description'), self.role['description']) - self.assertEqual(r.xml.get('serviceId'), self.role.get('serviceId')) - - def test_get_role_bad(self): - self.fetch_role(common.unique_str(), assert_status=404) - - def test_get_role_xml_bad(self): - self.get_role(common.unique_str(), assert_status=404, headers={ - 'Accept': 'application/xml'}) - - def test_get_role_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.fetch_role(self.role['id'], assert_status=403) - - def test_get_role_xml_using_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.get_role(self.role['id'], assert_status=403, headers={ - 'Accept': 'application/xml'}) - - def test_get_role_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.fetch_role(self.role['id'], assert_status=403) - - def test_get_role_xml_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.get_role(self.role['id'], assert_status=403, headers={ - 'Accept': 'application/xml'}) - - def test_get_role_using_missing_token(self): - self.admin_token = '' - self.fetch_role(self.role['id'], assert_status=401) - - def test_get_role_xml_using_missing_token(self): - self.admin_token = '' - self.get_role(self.role['id'], assert_status=401, headers={ - 'Accept': 'application/xml'}) - - def test_get_role_using_invalid_token(self): - self.admin_token = common.unique_str() - self.fetch_role(self.role['id'], assert_status=401) - - def test_get_role_xml_using_invalid_token(self): - self.admin_token = common.unique_str() - self.get_role(self.role['id'], assert_status=401, headers={ - 'Accept': 'application/xml'}) - - -class GetRoleByNameTest(RolesTest): - def setUp(self, *args, **kwargs): - super(GetRoleByNameTest, self).setUp(*args, **kwargs) - - self.role = self.create_role().json['role'] - - def test_get_role(self): - role = self.fetch_role_by_name( - self.role['name'], assert_status=200).json['role'] - self.assertEqual(role['id'], self.role['id']) - self.assertEqual(role['name'], self.role['name']) - self.assertEqual(role['description'], self.role['description']) - self.assertEqual(role.get('serviceId'), self.role.get('serviceId')) - - def test_get_role_xml(self): - r = self.get_role_by_name(self.role['name'], - assert_status=200, headers={ - 'Accept': 'application/xml'}) - self.assertEqual(r.xml.tag, '{%s}role' % self.xmlns) - self.assertEqual(r.xml.get('id'), self.role['id']) - self.assertEqual(r.xml.get('name'), self.role['name']) - self.assertEqual(r.xml.get('description'), self.role['description']) - self.assertEqual(r.xml.get('serviceId'), self.role.get('serviceId')) - - def test_get_role_bad(self): - self.fetch_role_by_name(common.unique_str(), assert_status=404) - - def test_get_role_xml_bad(self): - self.get_role_by_name(common.unique_str(), assert_status=404, headers={ - 'Accept': 'application/xml'}) - - def test_get_role_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.fetch_role_by_name(self.role['name'], assert_status=403) - - def test_get_role_xml_using_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.get_role_by_name(self.role['name'], assert_status=403, headers={ - 'Accept': 'application/xml'}) - - def test_get_role_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.fetch_role_by_name(self.role['name'], assert_status=403) - - def test_get_role_xml_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.get_role_by_name(self.role['name'], assert_status=403, headers={ - 'Accept': 'application/xml'}) - - def test_get_role_using_missing_token(self): - self.admin_token = '' - self.fetch_role_by_name(self.role['name'], assert_status=401) - - def test_get_role_xml_using_missing_token(self): - self.admin_token = '' - self.get_role_by_name(self.role['name'], assert_status=401, headers={ - 'Accept': 'application/xml'}) - - def test_get_role_using_invalid_token(self): - self.admin_token = common.unique_str() - self.fetch_role_by_name(self.role['name'], assert_status=401) - - def test_get_role_xml_using_invalid_token(self): - self.admin_token = common.unique_str() - self.get_role_by_name(self.role['name'], assert_status=401, headers={ - 'Accept': 'application/xml'}) - - -class CreateRoleAssignmentTest(RolesTest): - def setUp(self, *args, **kwargs): - super(CreateRoleAssignmentTest, self).setUp(*args, **kwargs) - - self.fixture_create_normal_tenant() - self.fixture_create_tenant_user() - - self.role = self.create_role().json['role'] - - def test_grant_role(self): - self.grant_role_to_user(self.tenant_user['id'], self.role['id'], - self.tenant['id'], assert_status=201) - -# def test_grant_role_json_using_service_admin_token(self): -# tenant = self.create_tenant().json['tenant'] -# service_admin = self.create_user_with_known_password().json['user'] -# self.grant_role_to_user(service_admin['id'], 'KeystoneServiceAdmin', -# tenant['id'], assert_status=201) -# -# service_admin_token_id = self.authenticate(service_admin['name'], -# service_admin['password']).json['auth']['token']['id'] -# -# user = self.create_user().json['user'] -# -# role = self.create_role().json['role'] -# self.admin_token = service_admin_token_id -# self.grant_role_to_user(user['id'], role['id'], tenant['id'], -# assert_status=201) - - def test_grant_role_using_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.grant_role_to_user(self.tenant_user['id'], self.role['id'], - self.tenant['id'], assert_status=403) - - def test_grant_role_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.grant_role_to_user(self.tenant_user['id'], self.role['id'], - self.tenant['id'], assert_status=403) - - def test_grant_role_using_missing_token(self): - self.admin_token = '' - self.grant_role_to_user(self.tenant_user['id'], self.role['id'], - self.tenant['id'], assert_status=401) - - def test_grant_role_using_invalid_token(self): - self.admin_token = common.unique_str() - self.grant_role_to_user(self.tenant_user['id'], self.role['id'], - self.tenant['id'], assert_status=401) - - def test_grant_global_role_json(self): - self.grant_global_role_to_user( - self.tenant_user['id'], self.role['id'], assert_status=201) - - -class GetRoleAssignmentsTest(RolesTest): - def setUp(self, *args, **kwargs): - super(GetRoleAssignmentsTest, self).setUp(*args, **kwargs) - self.fixture_create_normal_tenant() - self.fixture_create_tenant_user() - - self.role = self.create_role().json['role'] - self.grant_role_to_user(self.tenant_user['id'], self.role['id'], - self.tenant['id']) - - def test_get_role_assignments(self): - r = self.get_user_roles(self.tenant_user['id'], assert_status=200) - self.assertIsNotNone(r.json['roles']) - - def test_get_roler_assignments_xml(self): - r = self.get_user_roles(self.tenant_user['id'], assert_status=200, - headers={'Accept': 'application/xml'}) - self.assertEqual(r.xml.tag, "{%s}roles" % self.xmlns) - - def test_get_role_assignments_using_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.get_user_roles(self.tenant_user['id'], assert_status=403) - - def test_get_role_assignments_xml_using_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.get_user_roles(self.tenant_user['id'], assert_status=403, - headers={'Accept': 'application/xml'}) - - def test_get_role_assignments_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.get_user_roles(self.tenant_user['id'], assert_status=403) - - def test_get_role_assignments_xml_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.get_user_roles(self.tenant_user['id'], assert_status=403, - headers={'Accept': 'application/xml'}) - - def test_get_role_assignments_using_missing_token(self): - self.admin_token = '' - self.get_user_roles(self.tenant_user['id'], assert_status=401) - - def test_get_role_assignments_xml_using_missing_token(self): - self.admin_token = '' - self.get_user_roles(self.tenant_user['id'], assert_status=401, - headers={'Accept': 'application/xml'}) - - def test_get_role_assignments_json_using_invalid_token(self): - self.admin_token = common.unique_str() - self.get_user_roles(self.tenant_user['id'], assert_status=401) - - def test_get_role_assignments_xml_using_invalid_token(self): - self.admin_token = common.unique_str() - self.get_user_roles(self.tenant_user['id'], assert_status=401, - headers={'Accept': 'application/xml'}) - - -class DeleteRoleAssignmentsTest(RolesTest): - def setUp(self, *args, **kwargs): - super(DeleteRoleAssignmentsTest, self).setUp(*args, **kwargs) - - self.fixture_create_normal_tenant() - self.fixture_create_tenant_user() - - self.role = self.create_role().json['role'] - self.grant_role_to_user(self.tenant_user['id'], self.role['id'], - self.tenant['id']) - self.roles = self.get_user_roles(self.tenant_user['id']).\ - json['roles'] - - def test_delete_role_assignment(self): - self.delete_user_role(self.tenant_user['id'], self.role['id'], - self.tenant['id'], assert_status=204) - - def test_delete_role_assignment_using_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.delete_user_role(self.tenant_user['id'], self.role['id'], - self.tenant['id'], assert_status=403) - - def test_delete_role_assignment_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.delete_user_role(self.tenant_user['id'], self.role['id'], - self.tenant['id'], assert_status=403) - - def test_delete_role_assignment_using_missing_token(self): - self.admin_token = '' - self.delete_user_role(self.tenant_user['id'], self.role['id'], - self.tenant['id'], assert_status=401) - - def test_delete_role_assignment_using_invalid_token(self): - self.admin_token = common.unique_str() - self.delete_user_role(self.tenant_user['id'], self.role['id'], - self.tenant['id'], assert_status=401) - - -class DeleteGlobalRoleAssignmentsTest(RolesTest): - def setUp(self, *args, **kwargs): - super(DeleteGlobalRoleAssignmentsTest, self).setUp(*args, **kwargs) - - self.fixture_create_normal_tenant() - self.fixture_create_tenant_user() - - self.role = self.create_role().json['role'] - self.grant_global_role_to_user(self.tenant_user['id'], self.role['id']) - self.roles = self.get_user_roles(self.tenant_user['id']).\ - json['roles'] - - def test_delete_role_assignment(self): - self.delete_user_role(self.tenant_user['id'], self.role['id'], - None, assert_status=204) - - def test_delete_role_assignment_using_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.delete_user_role(self.tenant_user['id'], self.role['id'], - None, assert_status=403) - - def test_delete_role_assignment_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.delete_user_role(self.tenant_user['id'], self.role['id'], - None, assert_status=403) - - def test_delete_role_assignment_using_missing_token(self): - self.admin_token = '' - self.delete_user_role(self.tenant_user['id'], self.role['id'], - None, assert_status=401) - - def test_delete_role_assignment_using_invalid_token(self): - self.admin_token = common.unique_str() - self.delete_user_role(self.tenant_user['id'], self.role['id'], - None, assert_status=401) - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/functional/test_services.py b/keystone/test/functional/test_services.py deleted file mode 100644 index 276a5c190d..0000000000 --- a/keystone/test/functional/test_services.py +++ /dev/null @@ -1,294 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 2010-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. - -import unittest2 as unittest -import uuid - -from keystone.test.functional import common - - -class ServicesTest(common.FunctionalTestCase): - def setUp(self, *args, **kwargs): - super(ServicesTest, self).setUp(*args, **kwargs) - - service = self.create_service( - service_name="service-%s" % uuid.uuid4().hex, - service_type='identity', - service_description='Sample service', - assert_status=201).json['OS-KSADM:service'] - - def tearDown(self, *args, **kwargs): - super(ServicesTest, self).tearDown(*args, **kwargs) - - -class GetServicesTest(ServicesTest): - def test_get_services_using_keystone_admin_token_json(self): - services = self.list_services(assert_status=200).\ - json['OS-KSADM:services'] - - self.assertTrue(len(services)) - - def test_get_services_using_keystone_admin_token_xml(self): - r = self.list_services(assert_status=200, headers={ - 'Accept': 'application/xml'}) - - self.assertEqual(r.xml.tag, "{%s}services" % self.xmlns_ksadm) - services = r.xml.findall("{%s}service" % self.xmlns_ksadm) - self.assertTrue(len(services)) - - def test_get_services_using_service_admin_token(self): - self.fixture_create_service_admin() - self.admin_token = self.service_admin_token - services = self.list_services(assert_status=200).\ - json['OS-KSADM:services'] - - self.assertTrue(len(services)) - - def test_get_services_using_service_admin_token_xml(self): - self.fixture_create_service_admin() - self.admin_token = self.service_admin_token - r = self.get_services(assert_status=200, headers={ - 'Accept': 'application/xml'}) - - self.assertEqual(r.xml.tag, "{%s}services" % self.xmlns_ksadm) - services = r.xml.findall("{%s}service" % self.xmlns_ksadm) - self.assertTrue(len(services)) - - def test_get_services_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.list_services(assert_status=403) - - def test_get_services_using_missing_token(self): - self.admin_token = '' - self.list_services(assert_status=401) - - def test_get_services_using_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.list_services(assert_status=403) - - def test_get_services_using_invalid_token(self): - self.admin_token = common.unique_str() - self.list_services(assert_status=401) - - -class GetServiceTest(ServicesTest): - def setUp(self, *args, **kwargs): - super(ServicesTest, self).setUp(*args, **kwargs) - - self.service = self.create_service().json['OS-KSADM:service'] - - def test_service_get_json(self): - service = self.fetch_service(service_id=self.service['id'], - assert_status=200).json['OS-KSADM:service'] - - self.assertIsNotNone(service['id']) - self.assertIsNotNone(service['description']) - - def test_service_get_xml(self): - service = self.fetch_service(service_id=self.service['id'], - assert_status=200, headers={'Accept': 'application/xml'}).xml - - self.assertEqual(service.tag, '{%s}service' % self.xmlns_ksadm) - self.assertIsNotNone(service.get('id')) - self.assertIsNotNone(service.get('description')) - - def test_get_service_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.fetch_service(service_id=self.service['id'], assert_status=403) - - def test_get_service_using_missing_token(self): - self.admin_token = '' - self.fetch_service(service_id=self.service['id'], assert_status=401) - - def test_get_service_using_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.fetch_service(service_id=self.service['id'], assert_status=403) - - def test_get_service_using_invalid_token(self): - self.admin_token = common.unique_str() - self.fetch_service(service_id=self.service['id'], assert_status=401) - - -class GetServiceByNameTest(ServicesTest): - def setUp(self, *args, **kwargs): - super(GetServiceByNameTest, self).setUp(*args, **kwargs) - self.service = self.create_service().json['OS-KSADM:service'] - - def test_service_get_json(self): - service = self.fetch_service_by_name(service_name=self.service['name'], - assert_status=200).json['OS-KSADM:service'] - - self.assertIsNotNone(service['id']) - self.assertIsNotNone(service['name']) - self.assertIsNotNone(service['description']) - - def test_service_get_xml(self): - service = self.fetch_service_by_name(service_name=self.service['name'], - assert_status=200, headers={'Accept': 'application/xml'}).xml - - self.assertEqual(service.tag, '{%s}service' % self.xmlns_ksadm) - self.assertIsNotNone(service.get('id')) - self.assertIsNotNone(service.get('name')) - self.assertIsNotNone(service.get('description')) - - def test_get_service_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.fetch_service_by_name( - service_name=self.service['name'], assert_status=403) - - def test_get_service_using_missing_token(self): - self.admin_token = '' - self.fetch_service_by_name( - service_name=self.service['name'], assert_status=401) - - def test_get_service_using_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.fetch_service_by_name( - service_name=self.service['name'], assert_status=403) - - def test_get_service_using_invalid_token(self): - self.admin_token = common.unique_str() - self.fetch_service_by_name( - service_name=self.service['name'], assert_status=401) - - -class CreateServiceTest(ServicesTest): - def test_service_create_json(self): - name = common.unique_str() - type = common.unique_str() - description = common.unique_str() - - service = self.create_service(service_name=name, service_type=type, - service_description=description, - assert_status=201).json['OS-KSADM:service'] - - self.assertIsNotNone(service.get('id')) - self.assertEqual(name, service.get('name')) - self.assertEqual(type, service.get('type')) - self.assertEqual(description, service.get('description')) - - def test_service_create_xml(self): - name = common.unique_str() - type = common.unique_str() - description = common.unique_str() - data = (' ' - '') % ( - self.xmlns_ksadm, name, type, description) - r = self.post_service(as_xml=data, assert_status=201) - self.assertEqual(r.xml.tag, "{%s}service" % self.xmlns_ksadm) - self.assertIsNotNone(r.xml.get('id')) - self.assertEqual(name, r.xml.get('name')) - self.assertEqual(type, r.xml.get('type')) - self.assertEqual(description, r.xml.get('description')) - - def test_service_create_duplicate_json(self): - service_name = common.unique_str() - self.create_service(service_name=service_name, assert_status=201) - self.create_service(service_name=service_name, assert_status=409) - - def test_service_create_using_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.create_service(assert_status=403) - - def test_service_create_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.create_service(assert_status=403) - - def test_service_create_json_using_missing_token(self): - self.admin_token = '' - self.create_service(assert_status=401) - - def test_service_create_json_using_invalid_token(self): - self.admin_token = common.unique_str() - self.create_service(assert_status=401) - - def test_service_create_json_missing_name(self): - self.create_service(service_name='', assert_status=400) - - def test_service_create_json_missing_type(self): - self.create_service(service_type='', assert_status=400) - - def test_service_create_xml_using_missing_name(self): - name = '' - type = common.unique_str() - description = common.unique_str() - data = (' ' - '') % ( - self.xmlns_ksadm, name, type, description) - self.post_service(as_xml=data, assert_status=400) - - def test_service_create_xml_using_empty_type(self): - name = common.unique_str() - type = '' - description = common.unique_str() - data = (' ' - '') % ( - self.xmlns_ksadm, name, type, description) - self.post_service(as_xml=data, assert_status=400) - - -class DeleteServiceTest(ServicesTest): - def setUp(self, *args, **kwargs): - super(DeleteServiceTest, self).setUp(*args, **kwargs) - - self.service = self.create_service().json['OS-KSADM:service'] - - def test_service_delete(self): - self.remove_service(self.service['id'], assert_status=204) - self.get_service(self.service['id'], assert_status=404) - - def test_delete_service_with_dependencies(self): - role_id = self.service['name'] + ':' + common.unique_str() - role = self.create_role(role_id, service_id=self.service['id'], - assert_status=201).json['role'] - - tenant = self.create_tenant().json['tenant'] - user = self.create_user(tenant_id=tenant['id']).json['user'] - - self.grant_role_to_user(user['id'], role['id'], tenant['id']) - self.create_endpoint_template(name=self.service['name'], - type=self.service['type']) - self.remove_service(self.service['id'], assert_status=204) - - def test_service_delete_json_using_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.remove_service(self.service['id'], assert_status=403) - - def test_service_delete_json_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.remove_service(self.service['id'], assert_status=403) - - def test_service_delete_json_using_missing_token(self): - self.admin_token = '' - self.remove_service(self.service['id'], assert_status=401) - - def test_service_delete_json_using_invalid_token(self): - self.admin_token = common.unique_str() - self.remove_service(self.service['id'], assert_status=401) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/functional/test_static_files.py b/keystone/test/functional/test_static_files.py deleted file mode 100644 index 75b445f6c2..0000000000 --- a/keystone/test/functional/test_static_files.py +++ /dev/null @@ -1,90 +0,0 @@ -import unittest2 as unittest -from keystone.test.functional import common - - -class TestStaticFiles(common.ApiTestCase): - def test_pdf_contract(self): - if not common.isSsl(): - #TODO(ziad): Caller hangs in SSL (but works with cURL) - r = self.service_request(path='/identitydevguide.pdf') - self.assertResponseSuccessful(r) - - def test_wadl_contract(self): - r = self.service_request(path='/identity.wadl') - self.assertResponseSuccessful(r) - - def test_wadl_common(self): - r = self.service_request(path='/common.ent') - self.assertResponseSuccessful(r) - - def test_xsd_contract(self): - r = self.service_request(path='/xsd/api.xsd') - self.assertResponseSuccessful(r) - - def test_xsd_atom_contract(self): - r = self.service_request(path='/xsd/atom/atom.xsd') - self.assertResponseSuccessful(r) - - def test_xslt(self): - r = self.service_request(path='/xslt/schema.xslt') - self.assertResponseSuccessful(r) - - def test_js(self): - r = self.service_request(path='/js/shjs/sh_java.js') - self.assertResponseSuccessful(r) - - def test_xml_sample(self): - r = self.service_request(path='/samples/auth.xml') - self.assertResponseSuccessful(r) - - def test_json_sample(self): - r = self.service_request(path='/samples/auth.json') - self.assertResponseSuccessful(r) - - def test_stylesheet(self): - r = self.service_request(path='/style/shjs/sh_acid.css') - self.assertResponseSuccessful(r) - - -class TestAdminStaticFiles(common.FunctionalTestCase): - def test_pdf_contract(self): - if not common.isSsl(): - #TODO(ziad): Caller hangs in SSL (but works with cURL) - r = self.admin_request(path='/identityadminguide.pdf') - self.assertResponseSuccessful(r) - - def test_wadl_contract(self): - r = self.admin_request(path='/identity-admin.wadl') - self.assertResponseSuccessful(r) - - def test_xsd_contract(self): - r = self.admin_request(path='/xsd/api.xsd') - self.assertResponseSuccessful(r) - - def test_xsd_atom_contract(self): - r = self.admin_request(path='/xsd/atom/atom.xsd') - self.assertResponseSuccessful(r) - - def test_xslt(self): - r = self.admin_request(path='/xslt/schema.xslt') - self.assertResponseSuccessful(r) - - def test_js(self): - r = self.admin_request(path='/js/shjs/sh_java.js') - self.assertResponseSuccessful(r) - - def test_xml_sample(self): - r = self.admin_request(path='/samples/auth.xml') - self.assertResponseSuccessful(r) - - def test_json_sample(self): - r = self.admin_request(path='/samples/auth.json') - self.assertResponseSuccessful(r) - - def test_stylesheet(self): - r = self.admin_request(path='/style/shjs/sh_acid.css') - self.assertResponseSuccessful(r) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/functional/test_tenants.py b/keystone/test/functional/test_tenants.py deleted file mode 100644 index 708edbabfa..0000000000 --- a/keystone/test/functional/test_tenants.py +++ /dev/null @@ -1,598 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 2010-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. - - -import unittest2 as unittest -from keystone.test.functional import common - - -class TenantTest(common.FunctionalTestCase): - def _assertValidTenant(self, tenant): - self.assertIsNotNone(tenant.get('id')) - self.assertIsNotNone(tenant.get('name')) - self.assertIsNotNone(tenant.get('enabled')) - self.assertRaises(ValueError, int, tenant.get('id')) - self.assertTrue(0 < len(tenant.get('id')) < 256, - "ID must be between 1 and 255 characters long") - self.assertFalse('/' in tenant.get('id'), - "ID cannot contain / character") - - def _assertValidJsonTenant(self, tenant): - self._assertValidTenant(tenant) - # TODO(dolph): this is still a valid assertion in some cases - # self.assertIsNotNone(tenant.get('description')) - self.assertIn(tenant.get('enabled'), [True, False], tenant) - - def _assertValidXmlTenant(self, xml): - self.assertEquals(xml.tag, '{%s}tenant' % self.xmlns) - self._assertValidTenant(xml) - # TODO(zns): this is still a valid assertion in some cases - # description = xml.find('{%s}description' % self.xmlns) - # self.assertIsNotNone(description) - self.assertIn(xml.get('enabled'), ['true', 'false']) - return xml - - def assertValidJsonTenantResponse(self, response): - tenant = response.json.get('tenant') - self._assertValidJsonTenant(tenant) - return tenant - - def assertValidXmlTenantResponse(self, response): - return self._assertValidXmlTenant(response.xml) - - def _assertValidTenantList(self, tenants): - pass - - def _assertValidXmlTenantList(self, xml): - self.assertEquals(xml.tag, '{%s}tenants' % self.xmlns) - tenants = xml.findall('{%s}tenant' % self.xmlns) - - self._assertValidTenantList(tenants) - for tenant in tenants: - self._assertValidXmlTenant(tenant) - return tenants - - def _assertValidJsonTenantList(self, tenants): - self._assertValidTenantList(tenants) - for tenant in tenants: - self._assertValidJsonTenant(tenant) - return tenants - - def assertValidXmlTenantListResponse(self, response): - return self._assertValidXmlTenantList(response.xml) - - def assertValidJsonTenantListResponse(self, response): - tenants = response.json.get('tenants') - self.assertIsNotNone(tenants) - return self._assertValidJsonTenantList(tenants) - - def setUp(self, *args, **kwargs): - super(TenantTest, self).setUp(*args, **kwargs) - - def tearDown(self, *args, **kwargs): - super(TenantTest, self).tearDown(*args, **kwargs) - - -class CreateTenantTest(TenantTest): - def test_create_tenant(self): - name = common.unique_str() - description = common.unique_str() - response = self.create_tenant(tenant_name=name, - tenant_description=description, assert_status=201) - tenant = self.assertValidJsonTenantResponse(response) - self.assertEqual(name, tenant.get('name')) - self.assertEqual(description, tenant.get('description')) - - def test_create_tenant_blank_name(self): - self.create_tenant(tenant_name='', assert_status=400) - - def test_create_tenant_xml(self): - name = common.unique_str() - description = common.unique_str() - data = (' ' - ' ' - '%s ' - '') % (name, description) - response = self.post_tenant(as_xml=data, assert_status=201, headers={ - 'Accept': 'application/xml'}) - tenant = self.assertValidXmlTenantResponse(response) - self.assertEqual(name, tenant.get('name')) - self.assertEqual(description, - tenant.find('{%s}description' % self.xmlns).text) - - def test_create_tenant_again(self): - tenant = self.create_tenant().json['tenant'] - self.create_tenant(tenant_name=tenant['name'], assert_status=409) - - def test_create_tenant_again_xml(self): - tenant = self.create_tenant().json['tenant'] - - data = (' ' - ' ' - 'A description... ' - '') % (tenant['name'],) - - self.create_tenant(tenant_name=tenant['name'], as_xml=data, - assert_status=409) - - def test_create_tenant_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.create_tenant(assert_status=403) - - def test_create_tenant_expired_token_xml(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - data = ' \ - \ - A description... \ - ' % (common.unique_str()) - - self.post_tenant(as_xml=data, assert_status=403) - - def test_create_tenant_missing_token(self): - self.admin_token = '' - self.create_tenant(assert_status=401) - - def test_create_tenant_missing_token_xml(self): - self.admin_token = '' - data = ' \ - \ - A description... \ - ' % (common.unique_str()) - - self.post_tenant(as_xml=data, assert_status=401) - - def test_create_tenant_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.create_tenant(assert_status=403) - - def test_create_tenant_disabled_token_xml(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - data = ' \ - \ - A description... \ - ' % (common.unique_str()) - - self.post_tenant(as_xml=data, assert_status=403) - - def test_create_tenant_invalid_token(self): - self.admin_token = common.unique_str() - self.create_tenant(assert_status=401) - - def test_create_tenant_invalid_token_xml(self): - self.admin_token = common.unique_str() - data = ' \ - \ - A description... \ - ' % (common.unique_str()) - - self.post_tenant(as_xml=data, assert_status=401) - - def test_create_tenant_missing_name_xml(self): - data = (' ' - ' ' - 'A description... ' - '') % ('',) - self.post_tenant(as_xml=data, assert_status=400, headers={ - 'Accept': 'application/xml'}) - - -class GetTenantsTest(TenantTest): - def test_get_tenants_using_admin_token(self): - response = self.list_tenants(assert_status=200) - self.assertValidJsonTenantListResponse(response) - - def test_get_tenants_using_admin_token_xml(self): - response = self.get_tenants(assert_status=200, headers={ - 'Accept': 'application/xml'}) - self.assertValidXmlTenantListResponse(response) - - def test_get_tenants_using_admin_token_xml_on_service_api(self): - response = self.create_tenant() - tenant = self.assertValidJsonTenantResponse(response) - role = self.create_role().json['role'] - user = self.create_user_with_known_password(tenant_id=tenant['id']).\ - json['user'] - self.grant_role_to_user(user_id=user['id'], - role_id=role['id'], tenant_id=tenant['id']) - - # find the admin role - admin_role = self.get_role_by_name('Admin').json['role'] - - # grant global admin to user - self.grant_global_role_to_user(user_id=user['id'], - role_id=admin_role['id']) - - # authenticate as our new admin - self.service_token = self.authenticate(user['name'], - user['password']).json['access']['token']['id'] - - # make a service call with our admin token - response = self.get_tenants(assert_status=200, headers={ - 'Accept': 'application/xml'}, request_type='service') - tenants = self.assertValidXmlTenantListResponse(response) - self.assertEquals(len(tenants), 1) - self.assertIn(tenant['id'], [t.get('id') for t in tenants]) - - def test_get_tenants_using_user_token(self): - response = self.create_tenant() - tenant = self.assertValidJsonTenantResponse(response) - user = self.create_user_with_known_password(tenant_id=tenant['id']).\ - json['user'] - token = self.authenticate(user['name'], user['password'], - tenant['id']).json['access']['token'] - tmp = self.service_token - self.service_token = token['id'] - response = self.service_request(method='GET', path='/tenants', - assert_status=200) - self.service_token = tmp - tenants = self.assertValidJsonTenantListResponse(response) - self.assertTrue(len(tenants) == 1) - self.assertIn(tenant['id'], [t['id'] for t in tenants]) - - def test_get_tenants_using_user_token_xml(self): - response = self.create_tenant() - tenant = self.assertValidJsonTenantResponse(response) - user = self.create_user_with_known_password(tenant_id=tenant['id']).\ - json['user'] - token = self.authenticate(user['name'], user['password'], - tenant['id']).json['access']['token'] - tmp = self.service_token - self.service_token = token['id'] - response = self.service_request(method='GET', path='/tenants', - assert_status=200, headers={'Accept': 'application/xml'}) - self.service_token = tmp - tenants = self.assertValidXmlTenantListResponse(response) - self.assertIn(tenant['id'], [t.get('id') for t in tenants]) - - def test_get_tenants_exp_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.list_tenants(assert_status=403) - - def test_get_tenants_exp_token_xml(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.get_tenants(assert_status=403, headers={ - 'Accept': 'application/xml'}) - - def test_get_tenants_blank(self): - # Create user with no default tenant set - self.nodefaultuser = self.create_user_with_known_password()\ - .json['user'] - response = self.authenticate(self.nodefaultuser['name'], - self.nodefaultuser['password'], - tenant_id=None, assert_status=200) - token_id = response.json['access']['token']['id'] - response = self.get_tenants(request_type='service', use_token=token_id) - self.assertTrue(len(response.json['tenants']) == 0, - "No tenants should be returned") - - def test_get_tenants_default_and_role(self): - """ Validates that the GET /tenants call returns tenants that are - assigned to the user by role AND by default tenant setting """ - self.fixture_create_normal_tenant() - self.fixture_create_tenant_user() - other_tenant = self.create_tenant().json['tenant'] - role = self.create_role().json['role'] - self.grant_role_to_user(self.tenant_user['id'], role['id'], - other_tenant['id']) - - response = self.authenticate(self.tenant_user['name'], - self.tenant_user['password'], - tenant_id=None, assert_status=200) - token_id = response.json['access']['token']['id'] - response = self.get_tenants(request_type='service', use_token=token_id) - tenants = response.json['tenants'] - second_tenant = [tenant for tenant in tenants - if tenant['id'] == other_tenant['id']] - self.assertTrue(len(second_tenant) > 0, - "Tenants with roles assigned should be returned") - - default_tenant = [tenant for tenant in tenants - if tenant['id'] == self.tenant['id']] - self.assertTrue(len(default_tenant) > 0, - "Default tenant should be returned") - - def test_get_tenants_does_not_return_default(self): - self.fixture_create_normal_tenant() - self.fixture_create_tenant_user() - other_tenant = self.create_tenant().json['tenant'] - role = self.create_role().json['role'] - self.grant_role_to_user(self.tenant_user['id'], role['id'], - other_tenant['id']) - - # Authenticate scoped to the non-default tenant - response = self.authenticate(self.tenant_user['name'], - self.tenant_user['password'], - tenant_id=other_tenant['id'], - assert_status=200) - token_id = response.json['access']['token']['id'] - response = self.get_tenants(request_type='service', use_token=token_id) - tenants = response.json['tenants'] - second_tenant = [tenant for tenant in tenants - if tenant['id'] == other_tenant['id']] - self.assertTrue(len(second_tenant) > 0, - "Tenants with roles assigned should be returned") - - default_tenant = [tenant for tenant in tenants - if tenant['id'] == self.tenant['id']] - self.assertTrue(len(default_tenant) == 0, - "Default tenant should not be returned") - - -class GetTenantTest(TenantTest): - def setUp(self, *args, **kwargs): - super(GetTenantTest, self).setUp(*args, **kwargs) - self.fixture_create_normal_tenant() - - def test_get_tenant(self): - response = self.fetch_tenant(self.tenant['id'], assert_status=200) - tenant = self.assertValidJsonTenantResponse(response) - self.assertEquals(self.tenant['id'], tenant['id']) - self.assertEquals(self.tenant['name'], tenant['name']) - self.assertFalse('description' in tenant) - self.assertEquals(self.tenant['enabled'], tenant['enabled']) - - def test_get_tenant_xml(self): - response = self.fetch_tenant(self.tenant['id'], assert_status=200, - headers={"Accept": "application/xml"}) - tenant = self.assertValidXmlTenantResponse(response) - self.assertEquals(self.tenant['id'], tenant.get('id')) - self.assertEquals(self.tenant['name'], tenant.get('name')) - self.assertEquals(str(self.tenant['enabled']).lower(), - tenant.get('enabled')) - - def test_get_tenant_not_found(self): - self.fetch_tenant(assert_status=404) - - def test_get_tenant_not_found_xml(self): - self.get_tenant(common.unique_str(), assert_status=404, headers={ - 'Accept': 'application/xml'}) - - -class GetTenantUsersTest(TenantTest): - def setUp(self, *args, **kwargs): - super(GetTenantUsersTest, self).setUp(*args, **kwargs) - self.fixture_create_normal_tenant() - self.fixture_create_normal_user() - - role = self.create_role().json['role'] - self.grant_role_to_user(self.user['id'], role['id'], self.tenant['id']) - - def test_list_tenant_users(self): - user = self.list_tenant_users(self.tenant['id'], - assert_status=200).json['users'][0] - self.assertEquals(user['name'], self.user['name']) - - def test_list_tenant_users_xml(self): - response = self.list_tenant_users(self.tenant['id'], - assert_status=200, headers={ - "Accept": "application/xml"}) - self.assertEquals(response.xml.tag, '{%s}users' % self.xmlns) - users = response.xml.findall('{%s}user' % self.xmlns) - for user in users: - self.assertEqual(user.get('name'), self.user['name']) - - def test_list_tenant_users_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.list_tenant_users(self.tenant['id'], assert_status=403) - - def test_list_tenant_users_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.list_tenant_users(self.tenant['id'], assert_status=403) - - def test_list_tenant_users_missing_token(self): - self.admin_token = '' - self.list_tenant_users(self.tenant['id'], assert_status=401) - - def test_list_tenant_users_invalid_token(self): - self.admin_token = common.unique_str() - self.list_tenant_users(self.tenant['id'], assert_status=401) - - -class GetTenantUsersByRoleTest(TenantTest): - def setUp(self, *args, **kwargs): - super(GetTenantUsersByRoleTest, self).setUp(*args, **kwargs) - self.fixture_create_normal_tenant() - self.fixture_create_normal_user() - - self.role = self.create_role().json['role'] - self.grant_role_to_user(self.user['id'], - self.role['id'], self.tenant['id']) - - def test_list_tenant_users(self): - user = self.list_tenant_users(self.tenant['id'], - self.role['id'], assert_status=200).json['users'][0] - self.assertEquals(user['name'], self.user['name']) - - def test_list_tenant_users_xml(self): - response = self.list_tenant_users(self.tenant['id'], - self.role['id'], assert_status=200, headers={ - "Accept": "application/xml"}) - self.assertEquals(response.xml.tag, '{%s}users' % self.xmlns) - users = response.xml.findall('{%s}user' % self.xmlns) - for user in users: - self.assertEqual(user.get('name'), self.user['name']) - - def test_list_tenant_users_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.list_tenant_users(self.tenant['id'], - self.role['id'], assert_status=403) - - def test_list_tenant_users_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.list_tenant_users(self.tenant['id'], - self.role['id'], assert_status=403) - - def test_list_tenant_users_missing_token(self): - self.admin_token = '' - self.list_tenant_users(self.tenant['id'], - self.role['id'], assert_status=401) - - def test_list_tenant_users_invalid_token(self): - self.admin_token = common.unique_str() - self.list_tenant_users(self.tenant['id'], - self.role['id'], assert_status=401) - - -class GetTenantByNameTest(TenantTest): - def setUp(self, *args, **kwargs): - super(GetTenantByNameTest, self).setUp(*args, **kwargs) - self.fixture_create_normal_tenant() - - def test_get_tenant(self): - response = self.fetch_tenant_by_name(self.tenant['name'], - assert_status=200) - tenant = self.assertValidJsonTenantResponse(response) - self.assertEquals(self.tenant['id'], tenant['id']) - self.assertEquals(self.tenant['name'], tenant['name']) - self.assertEquals(self.tenant['enabled'], tenant['enabled']) - - def test_get_tenant_data(self): - tenant = self.fixture_create_tenant(name=common.unique_str(), - description=common.unique_str(), - enabled=True) - response = self.fetch_tenant_by_name(tenant['name'], assert_status=200) - returned = self.assertValidJsonTenantResponse(response) - self.assertEquals(returned['id'], tenant['id']) - self.assertEquals(returned['name'], tenant['name']) - self.assertEquals(returned['description'], tenant['description']) - self.assertEquals(returned['enabled'], tenant['enabled']) - - def test_get_tenant_xml(self): - response = self.fetch_tenant_by_name( - self.tenant['name'], assert_status=200, headers={ - "Accept": "application/xml"}) - tenant = self.assertValidXmlTenantResponse(response) - - self.assertEquals(self.tenant['id'], tenant.get('id')) - self.assertEquals(self.tenant['name'], tenant.get('name')) - self.assertEquals(str(self.tenant['enabled']).lower(), - tenant.get('enabled')) - - def test_get_tenant_not_found(self): - self.fetch_tenant_by_name(assert_status=404) - - def test_get_tenant_not_found_xml(self): - self.fetch_tenant_by_name( - common.unique_str(), assert_status=404, headers={ - 'Accept': 'application/xml'}) - - -class UpdateTenantTest(TenantTest): - def setUp(self, *args, **kwargs): - super(UpdateTenantTest, self).setUp(*args, **kwargs) - self.fixture_create_normal_tenant() - - def test_update_tenant(self): - new_tenant_name = common.unique_str() - new_description = common.unique_str() - response = self.update_tenant(self.tenant['id'], - tenant_name=new_tenant_name, tenant_enabled=False, - tenant_description=new_description, assert_status=200) - updated_tenant = self.assertValidJsonTenantResponse(response) - self.assertEqual(updated_tenant['name'], new_tenant_name) - self.assertEqual(updated_tenant['description'], new_description) - self.assertEqual(updated_tenant['enabled'], False) - - def test_update_tenant_xml(self): - new_tenant_name = common.unique_str() - new_description = common.unique_str() - data = (' ' - ' ' - '%s ' - '') % (new_tenant_name, new_description,) - response = self.post_tenant_for_update( - self.tenant['id'], as_xml=data, assert_status=200) - updated = self.assertValidXmlTenantResponse(response) - - self.assertEqual(updated.get('id'), self.tenant['id']) - self.assertEqual(updated.get('name'), new_tenant_name) - description = updated.find("{%s}description" % self.xmlns) - self.assertEqual(description.text, new_description) - self.assertEqual(updated.get('enabled'), 'false') - - def test_update_tenant_bad(self): - data = '{"tenant": { "description_bad": "A NEW description...",\ - "enabled":true }}' - self.post_tenant_for_update( - self.tenant['id'], as_json=data, assert_status=400) - - def test_update_tenant_bad_xml(self): - data = ' \ - \ - A NEW description... \ - ' - self.post_tenant_for_update( - self.tenant['id'], as_xml=data, assert_status=400) - - def test_update_tenant_not_found(self): - self.update_tenant(assert_status=404) - - def test_update_tenant_not_found_xml(self): - data = ('' - ' ' - 'A NEW description... ' - '') - self.post_tenant_for_update( - common.unique_str(), as_xml=data, assert_status=404) - - -class DeleteTenantTest(TenantTest): - def setUp(self, *args, **kwargs): - super(DeleteTenantTest, self).setUp(*args, **kwargs) - self.fixture_create_normal_tenant() - - def test_delete_tenant(self): - self.remove_tenant(self.tenant['id'], assert_status=204) - self.get_tenant(self.tenant['id'], assert_status=404) - self.update_tenant(self.tenant['id'], assert_status=404) - - def test_delete_tenant_xml(self): - self.delete_tenant(self.tenant['id'], assert_status=204, headers={ - 'Accept': 'application/xml'}) - self.get_tenant(self.tenant['id'], assert_status=404) - self.update_tenant(self.tenant['id'], assert_status=404) - - def test_delete_tenant_not_found(self): - self.remove_tenant(common.unique_str(), assert_status=404) - - def test_delete_tenant_not_found_xml(self): - self.delete_tenant(common.unique_str(), assert_status=404, headers={ - 'Accept': 'application/xml'}) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/functional/test_token.py b/keystone/test/functional/test_token.py deleted file mode 100644 index 54a41ce0df..0000000000 --- a/keystone/test/functional/test_token.py +++ /dev/null @@ -1,207 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 2010-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. - -# pylint: disable=W0613 - -import unittest2 as unittest - -from keystone.backends import api -from keystone.logic.types import fault -from keystone.logic import service -from keystone.test.functional import common - - -class ValidateToken(common.FunctionalTestCase): - def setUp(self, *args, **kwargs): - super(ValidateToken, self).setUp(*args, **kwargs) - self.fixture_create_normal_tenant() - - self.user = self.create_user_with_known_password( - tenant_id=self.tenant['id']).json['user'] - self.role = self.create_role().json['role'] - self.rolegrant = self.grant_role_to_user(self.user['id'], - self.role['id'], self.tenant['id']) - self.token = self.authenticate(self.user['name'], - self.user['password'], self.tenant['id']).json['access']['token'] - - def test_validate_token_true(self): - r = self.get_token_belongsto(self.token['id'], self.tenant['id'], - assert_status=200) - self.assertIsNotNone(r.json['access']['user']["roles"]) - self.assertEqual(r.json['access']['user']["roles"][0]['id'], - self.role['id']) - self.assertEqual(r.json['access']['user']["roles"][0]['name'], - self.role['name']) - self.assertIsNotNone(r.json['access']['user']['id'], self.user['id']) - self.assertIsNotNone(r.json['access']['user']['name'], - self.user['name']) - self.assertIn('tenants', r.json['access']['token']) - - def test_validate_token_true_using_service_token(self): - self.fixture_create_service_admin() - self.admin_token = self.service_admin_token - r = self.get_token_belongsto(self.token['id'], self.tenant['id'], - assert_status=200) - - self.assertIsNotNone(r.json['access']['user']["roles"]) - self.assertEqual(r.json['access']['user']["roles"][0]['id'], - self.role['id']) - self.assertEqual(r.json['access']['user']["roles"][0]['name'], - self.role['name']) - - def test_validate_token_true_xml(self): - r = self.get_token_belongsto(self.token['id'], self.tenant['id'], - assert_status=200, headers={'Accept': 'application/xml'}) - - self.assertEqual(r.xml.tag, '{%s}access' % self.xmlns) - - user = r.xml.find('{%s}user' % self.xmlns) - self.assertIsNotNone(user) - self.assertEqual(self.user['id'], user.get('id')) - self.assertEqual(self.user['name'], user.get('name')) - - roles = user.find('{%s}roles' % self.xmlns) - self.assertIsNotNone(roles) - - role = roles.find('{%s}role' % self.xmlns) - self.assertIsNotNone(role) - self.assertEqual(self.role['id'], role.get("id")) - self.assertEqual(self.role['name'], role.get("name")) - - def test_validate_token_expired(self): - self.get_token(self.expired_admin_token, assert_status=404) - - def test_validate_token_expired_xml(self): - self.get_token(self.expired_admin_token, assert_status=404, headers={ - 'Accept': 'application/xml'}) - - def test_validate_token_invalid(self): - self.get_token(common.unique_str(), assert_status=404) - - def test_validate_token_invalid_xml(self): - self.get_token(common.unique_str(), assert_status=404, headers={ - 'Accept': 'application/xml'}) - - -class CheckToken(common.FunctionalTestCase): - def setUp(self, *args, **kwargs): - super(CheckToken, self).setUp(*args, **kwargs) - self.fixture_create_normal_tenant() - self.fixture_create_tenant_user() - - self.token = self.authenticate(self.tenant_user['name'], - self.tenant_user['password'], - self.tenant['id']).json['access']['token'] - - def test_validate_token_true(self): - self.check_token_belongs_to(self.token['id'], self.tenant['id'], - assert_status=200) - - def test_validate_token_true_using_service_token(self): - self.fixture_create_service_admin() - self.admin_token = self.service_admin_token - self.check_token_belongs_to(self.token['id'], self.tenant['id'], - assert_status=200) - - def test_validate_token_expired(self): - self.fixture_create_expired_token() - self.check_token(self.expired_admin_token, assert_status=404) - - def test_validate_token_expired_xml(self): - self.fixture_create_expired_token() - self.check_token(self.expired_admin_token, assert_status=404, headers={ - 'Accept': 'application/xml'}) - - def test_validate_token_invalid(self): - self.check_token(common.unique_str(), assert_status=404) - - -# pylint: disable=E1101,E1120 -class TokenEndpointTest(unittest.TestCase): - def _noop_validate_admin_token(self, admin_token): - pass - - class FakeDtoken(object): - expires = 'now' - tenant_id = 1 - id = 2 - - def _fake_token_get(self, token_id): - return self.FakeDtoken() - - def _fake_missing_token_get(self, token_id): - return None - - class FakeEndpoint(object): - service = 'foo' - - def _fake_tenant_get_all_endpoints(self, tenant_id): - return [self.FakeEndpoint()] - - def _fake_exploding_tenant_get_all_endpoints(self, tenant_id): - raise Exception("boom") - - def setUp(self): - self.stubout = stubout.StubOutForTesting() - - self.identity = service.IdentityService() - # The downside of python "private" methods ... you - # have to do stuff like this to stub them out. - self.stubout.SmartSet(self.identity, - "_IdentityService__validate_admin_token", - self._noop_validate_admin_token) - - def tearDown(self): - self.stubout.SmartUnsetAll() - self.stubout.UnsetAll() - - def test_endpoints_from_good_token(self): - """Happy Day scenario.""" - self.stubout.SmartSet(api.TOKEN, - 'get', self._fake_token_get) - - self.stubout.SmartSet(api.BaseTenantAPI, - 'get_all_endpoints', - self._fake_tenant_get_all_endpoints) - - auth_data = self.identity.get_endpoints_for_token("admin token", - "token id") - self.assertEquals(auth_data.base_urls[0].service, 'foo') - self.assertEquals(len(auth_data.base_urls), 1) - - def test_endpoints_from_bad_token(self): - self.stubout.SmartSet(api.TOKEN, - 'get', self._fake_missing_token_get) - - self.assertRaises(fault.ItemNotFoundFault, - self.identity.get_endpoints_for_token, - "admin token", "token id") - - def test_bad_endpoints(self): - self.stubout.SmartSet(api.TOKEN, - 'get', self._fake_token_get) - - self.stubout.SmartSet(api.TENANT, - 'get_all_endpoints', - self._fake_exploding_tenant_get_all_endpoints) - - endpoints = self.identity.get_endpoints_for_token("admin token", - "token id") - self.assertEquals(endpoints.base_urls, []) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/functional/test_users.py b/keystone/test/functional/test_users.py deleted file mode 100644 index 7027986c96..0000000000 --- a/keystone/test/functional/test_users.py +++ /dev/null @@ -1,406 +0,0 @@ -# -# 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 unittest2 as unittest -from keystone.test.functional import common - - -class UserTest(common.FunctionalTestCase): - def setUp(self, *args, **kwargs): - super(UserTest, self).setUp(*args, **kwargs) - - -class CreateUserTest(UserTest): - def setUp(self, *args, **kwargs): - super(CreateUserTest, self).setUp(*args, **kwargs) - - def test_create_user_with_tenant(self): - tenant = self.create_tenant().json['tenant'] - self.user = self.create_user(tenant_id=tenant['id'], - assert_status=201) - - def test_user_with_no_tenant(self): - self.create_user(assert_status=201) - - def test_create_user_disabled_tenant(self): - tenant = self.create_tenant(tenant_enabled=False).json['tenant'] - self.create_user(tenant_id=tenant['id'], assert_status=403) - - def test_create_user_again(self): - user_name = common.unique_str() - self.create_user(user_name) - self.create_user(user_name, assert_status=409) - - def test_create_users_with_duplicate_emails(self): - email = common.unique_email() - self.create_user(user_email=email) - self.create_user(user_email=email, assert_status=409) - - def test_create_user_with_empty_password(self): - self.create_user(user_password='', assert_status=400) - - def test_create_user_with_empty_username(self): - self.create_user(user_name='', assert_status=400) - - def test_create_user_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.create_user(assert_status=403) - - def test_create_user_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.create_user(assert_status=403) - - def test_create_user_missing_token(self): - self.admin_token = '' - self.create_user(assert_status=401) - - def test_create_user_invalid_token(self): - self.admin_token = common.unique_str() - self.create_user(assert_status=401) - - def test_create_user_xml_missing_name(self): - data = (' ' - '') - self.post_user(as_xml=data, assert_status=400) - - -class GetUserTest(UserTest): - def setUp(self, *args, **kwargs): - super(GetUserTest, self).setUp(*args, **kwargs) - - self.user = self.create_user().json['user'] - - def test_get_user(self): - self.fetch_user(self.user['id']) - - def test_query_user(self): - self.fetch_user_by_name(self.user['name']) - - def test_get_user_using_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.fetch_user(self.user['id'], assert_status=403) - - def test_query_user_using_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.fetch_user_by_name(self.user['name'], assert_status=403) - - def test_get_user_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.fetch_user(self.user['id'], assert_status=403) - - def test_query_user_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.fetch_user_by_name(self.user['name'], assert_status=403) - - def test_get_user_using_missing_token(self): - self.admin_token = '' - self.fetch_user(self.user['id'], assert_status=401) - - def test_query_user_using_missing_token(self): - self.admin_token = '' - self.fetch_user_by_name(self.user['name'], assert_status=401) - - def test_get_user_using_invalid_token(self): - self.admin_token = common.unique_str() - self.fetch_user(self.user['id'], assert_status=401) - - def test_query_user_using_invalid_token(self): - self.admin_token = common.unique_str() - self.fetch_user_by_name(self.user['name'], assert_status=401) - - def test_get_disabled_user(self): - self.disable_user(self.user['id']) - user = self.fetch_user(self.user['id']).json['user'] - self.assertFalse(user['enabled']) - - def test_query_disabled_user(self): - self.disable_user(self.user['id']) - self.fetch_user_by_name(self.user['name']) - - -class DeleteUserTest(UserTest): - def setUp(self, *args, **kwargs): - super(DeleteUserTest, self).setUp(*args, **kwargs) - - self.user = self.create_user().json['user'] - - def test_user_delete(self): - self.remove_user(self.user['id'], assert_status=204) - - def test_user_delete_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.remove_user(self.user['id'], assert_status=403) - - def test_user_delete_missing_token(self): - self.admin_token = '' - self.remove_user(self.user['id'], assert_status=401) - - def test_user_delete_invalid_token(self): - self.admin_token = common.unique_str() - self.remove_user(self.user['id'], assert_status=401) - - -class GetAllUsersTest(UserTest): - def setUp(self, *args, **kwargs): - super(GetAllUsersTest, self).setUp(*args, **kwargs) - - for _x in range(0, 3): - self.create_user() - - def test_list_users(self): - self.list_users(assert_status=200) - - def test_list_users_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.list_users(assert_status=403) - - def test_list_users_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.list_users(assert_status=403) - - def test_list_users_missing_token(self): - self.admin_token = '' - self.list_users(assert_status=401) - - def test_list_users_invalid_token(self): - self.admin_token = common.unique_str() - self.list_users(assert_status=401) - - -class UpdateUserTest(UserTest): - def setUp(self, *args, **kwargs): - super(UpdateUserTest, self).setUp(*args, **kwargs) - - self.user = self.create_user().json['user'] - - def test_update_user_email(self): - new_user_email = common.unique_email() - self.update_user(self.user['id'], user_name=self.user['name'], - user_email=new_user_email) - r = self.fetch_user(self.user['id']) - self.assertTrue(r.json['user']['email'], new_user_email) - - def test_update_user_name(self): - new_user_name = common.unique_str() - new_user_email = common.unique_email() - self.update_user(self.user['id'], user_name=new_user_name, - user_email=new_user_email) - r = self.fetch_user(self.user['id']) - self.assertTrue(r.json['user']['name'], new_user_name) - - def test_enable_disable_user(self): - self.assertFalse(self.disable_user(self.user['id']).\ - json['user']['enabled']) - self.assertFalse(self.fetch_user(self.user['id']).\ - json['user']['enabled']) - self.assertTrue(self.enable_user(self.user['id']).\ - json['user']['enabled']) - self.assertTrue(self.fetch_user(self.user['id']).\ - json['user']['enabled']) - - def test_update_user_bad_request(self): - data = '{"user_bad": { "bad": "%s"}}' % (common.unique_email(),) - self.post_user_for_update( - self.user['id'], assert_status=400, body=data, headers={ - "Content-Type": "application/json"}) - - def test_update_user_expired_token(self): - self.fixture_create_expired_token() - self.fixture_create_normal_user() - - self.admin_token = self.expired_admin_token - self.update_user(self.user['id'], assert_status=403) - - def test_update_user_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.update_user(self.user['id'], user_email=common.unique_email(), - assert_status=403) - - def test_update_user_invalid_token(self): - self.admin_token = common.unique_str() - self.update_user(self.user['id'], assert_status=401) - - def test_update_user_missing_token(self): - self.admin_token = '' - self.update_user(self.user['id'], assert_status=401) - - -class TestUpdateConflict(UserTest): - def setUp(self, *args, **kwargs): - super(TestUpdateConflict, self).setUp(*args, **kwargs) - - self.users = {} - for x in range(0, 2): - self.users[x] = self.create_user().json['user'] - - def test_update_user_email_conflict(self): - """Replace the second user's email with that of the first""" - self.update_user(user_id=self.users[1]['id'], - user_name=self.users[1]['name'], - user_email=self.users[0]['email'], assert_status=409) - - -class SetPasswordTest(UserTest): - def setUp(self, *args, **kwargs): - super(SetPasswordTest, self).setUp(*args, **kwargs) - self.fixture_create_normal_user() - - def test_update_user_password(self): - new_password = common.unique_str() - r = self.update_user_password(self.user['id'], new_password) - self.assertEqual(r.json['user']['password'], new_password) - - def test_update_disabled_users_password(self): - self.disable_user(self.user['id']) - - new_password = common.unique_str() - r = self.update_user_password(self.user['id'], new_password) - self.assertEqual(r.json['user']['password'], new_password) - - def test_user_password_bad_request(self): - data = '{"user_bad": { "password": "p@ssword"}}' - self.put_user_password(self.user['id'], body=data, assert_status=400, - headers={ - "Content-Type": "application/json"}) - - def test_user_password_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.update_user_password(self.user['id'], assert_status=403) - - def test_user_password_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.update_user_password(self.user['id'], assert_status=403) - - def test_user_password_invalid_token(self): - self.admin_token = common.unique_str() - self.update_user_password(self.user['id'], assert_status=401) - - def test_user_password_missing_token(self): - self.admin_token = '' - self.update_user_password(self.user['id'], assert_status=401) - - -class SetEnabledTest(UserTest): - def setUp(self, *args, **kwargs): - super(SetEnabledTest, self).setUp(*args, **kwargs) - self.fixture_create_normal_user() - - def test_user_enabled_bad_request(self): - data = '{"user_bad": { "enabled": true}}' - self.put_user_enabled(self.user['id'], body=data, assert_status=400, - headers={ - "Content-Type": "application/json"}) - - def test_user_enabled_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.disable_user(self.user['id'], assert_status=403) - - def test_user_enabled_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.disable_user(self.user['id'], assert_status=403) - - -class TenantUpdateTest(UserTest): - def setUp(self, *args, **kwargs): - super(TenantUpdateTest, self).setUp(*args, **kwargs) - self.fixture_create_normal_user() - self.fixture_create_normal_tenant() - - def test_update_user_tenant(self): - r = self.update_user_tenant(self.user['id'], self.tenant['id']) - self.assertEqual(r.json['user']['tenantId'], self.tenant['id']) - - def test_update_user_tenant_using_invalid_tenant(self): - self.update_user_tenant(self.user['id'], assert_status=404) - - def test_update_user_tenant_using_disabled_tenant(self): - disabled_tenant = self.create_tenant( - tenant_enabled=False).json['tenant'] - self.assertIsNotNone(disabled_tenant['id']) - self.update_user_tenant(self.user['id'], disabled_tenant['id'], - assert_status=403) - - def test_update_user_tenant_using_missing_token(self): - self.admin_token = '' - self.update_user_tenant(self.user['id'], self.tenant['id'], - assert_status=401) - - def test_update_user_tenant_using_invalid_token(self): - self.admin_token = common.unique_str() - self.update_user_tenant(self.user['id'], self.tenant['id'], - assert_status=401) - - def test_update_user_tenant_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.update_user_tenant(self.user['id'], self.tenant['id'], - assert_status=403) - - def test_update_user_tenant_using_exp_admin_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.update_user_tenant(self.user['id'], self.tenant['id'], - assert_status=403) - - -class AddUserTest(UserTest): - def setUp(self, *args, **kwargs): - super(AddUserTest, self).setUp(*args, **kwargs) - self.fixture_create_normal_tenant() - - def test_add_user_tenant(self): - self.create_user(tenant_id=self.tenant['id'], assert_status=201) - - def test_add_user_tenant_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.create_user(assert_status=403) - - def test_add_user_tenant_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.create_user(assert_status=403) - - def test_add_user_tenant_invalid_token(self): - self.admin_token = common.unique_str() - self.create_user(assert_status=401) - - def test_add_user_tenant_missing_token(self): - self.admin_token = '' - self.create_user(assert_status=401) - - def test_add_user_tenant_disabled_tenant(self): - self.tenant = self.create_tenant(tenant_enabled=False).json['tenant'] - self.create_user(tenant_id=self.tenant['id'], assert_status=403) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/sampledata.py b/keystone/test/sampledata.py deleted file mode 100644 index 410e291d6f..0000000000 --- a/keystone/test/sampledata.py +++ /dev/null @@ -1,137 +0,0 @@ -import keystone.manage - -DEFAULT_FIXTURE = [ -# Tenants - ('tenant', 'add', 'customer-x'), - ('tenant', 'add', 'ANOTHER:TENANT'), - ('tenant', 'add', 'project-y'), - ('tenant', 'disable', 'project-y'), - ('tenant', 'add', 'coffee-tea'), -# Add some services for the service roles - ('service', 'add', 'coffee', - 'coffee service', 'coffee service'), - ('service', 'add', 'tea', - 'tea house', 'tea house'), -# Users - ('user', 'add', 'joeuser', 'secrete', 'customer-x'), - ('user', 'add', 'pete', 'secrete', 'coffee-tea'), - ('user', 'add', 'joeadmin', 'secrete', 'customer-x'), - ('user', 'add', 'admin', 'secrete'), - ('user', 'add', 'serviceadmin', 'secrete', 'customer-x'), - ('user', 'add', 'disabled', 'secrete', 'customer-x'), - ('user', 'add', 'nodefaulttenant', 'secrete'), - ('user', 'disable', 'disabled'), -# Roles - ('role', 'add', 'Admin'), - ('role', 'add', 'KeystoneServiceAdmin'), - ('role', 'grant', 'Admin', 'admin'), - ('role', 'grant', 'KeystoneServiceAdmin', 'serviceadmin'), - ('role', 'grant', 'Admin', 'joeadmin', 'customer-x'), - ('role', 'grant', 'Admin', 'joeadmin', 'ANOTHER:TENANT'), - ('role', 'grant', 'Admin', 'nodefaulttenant', 'customer-x'), - ('role', 'add', 'Member'), - ('role', 'grant', 'Member', 'joeuser', 'customer-x'), - ('role', 'add', 'barista', 'coffee'), - ('role', 'add', 'barista', 'tea'), - ('role', 'grant', 'barista', 'pete'), - ('role', 'add', 'barista-global'), - ('role', 'grant', 'barista-global', 'pete'), -# Add Services - #1 Service Name:exampleservice Type:example type - ('service', 'add', 'exampleservice', - 'example type', 'example description'), - #2 Service Name:swift Type:object-store - ('service', 'add', 'swift', - 'object-store', 'Swift-compatible service'), - #3 Service Name:cdn Type:object-store - ('service', 'add', 'cdn', - 'object-store', 'Swift-compatible service'), - #4 Service Name:nova Type:compute - ('service', 'add', 'nova', - 'compute', 'OpenStack Compute Service'), - #5 Service Name:nova_compat Type:Compute - ('service', 'add', 'nova_compat', - 'compute', 'OpenStack Compute Service'), - #6 Service Name:glance Type:image - ('service', 'add', 'glance', - 'image', 'OpenStack Image Service'), - #7 Service Name:keystone Type:identity - ('service', 'add', 'identity', - 'identity', 'OpenStack Identity Service'), -# Keeping for compatibility for a while till dashboard catches up - ('endpointTemplates', 'add', 'RegionOne', 'swift', - 'http://swift.publicinternets.com/v1/AUTH_%tenant_id%', - 'http://swift.admin-nets.local:8080/', - 'http://127.0.0.1:8080/v1/AUTH_%tenant_id%', '1', '0'), - ('endpointTemplates', 'add', 'RegionOne', 'nova_compat', - 'http://nova.publicinternets.com/v1.0/', - 'http://127.0.0.1:8774/v1.0', 'http://localhost:8774/v1.0', '1', '0'), - ('endpointTemplates', 'add', 'RegionOne', 'nova', - 'http://nova.publicinternets.com/v1.1/', 'http://127.0.0.1:8774/v1.1', - 'http://localhost:8774/v1.1', '1', '0'), - ('endpointTemplates', 'add', 'RegionOne', 'glance', - 'http://glance.publicinternets.com/v1.1/', - 'http://nova.admin-nets.local/v1.1/', - 'http://127.0.0.1:9292/v1.1/', '1', '0'), - ('endpointTemplates', 'add', 'RegionOne', 'cdn', - 'http://cdn.publicinternets.com/v1.1/%tenant_id%', - 'http://cdn.admin-nets.local/v1.1/%tenant_id%', - 'http://127.0.0.1:7777/v1.1/%tenant_id%', '1', - '0', '1.1', 'http://127.0.0.1:7777/', 'http://127.0.0.1:7777/v1.1'), -# endpointTemplates - ('endpointTemplates', 'add', 'RegionOne', 'swift', - 'http://swift.publicinternets.com/v1/AUTH_%tenant_id%', - 'http://swift.admin-nets.local:8080/', - 'http://127.0.0.1:8080/v1/AUTH_%tenant_id%', '1', '0'), - ('endpointTemplates', 'add', 'RegionOne', 'nova', - 'http://nova.publicinternets.com/v1.0/', 'http://127.0.0.1:8774/v1.0', - 'http://localhost:8774/v1.0', '1', '0'), - ('endpointTemplates', 'add', 'RegionOne', 'nova_compat', - 'http://nova.publicinternets.com/v1.1/', 'http://127.0.0.1:8774/v1.1', - 'http://localhost:8774/v1.1', '1', '0'), - ('endpointTemplates', 'add', 'RegionOne', 'glance', - 'http://glance.publicinternets.com/v1.1/', - 'http://nova.admin-nets.local/v1.1/', - 'http://127.0.0.1:9292/v1.1/', '1', '0'), - ('endpointTemplates', 'add', 'RegionOne', 'cdn', - 'http://cdn.publicinternets.com/v1.1/%tenant_id%', - 'http://cdn.admin-nets.local/v1.1/%tenant_id%', - 'http://127.0.0.1:7777/v1.1/%tenant_id%', '1', '0'), -# Global endpointTemplate - ('endpointTemplates', 'add', 'RegionOne', 'identity', - 'http://keystone.publicinternets.com/v2.0', - 'http://127.0.0.1:35357/v2.0', 'http://127.0.0.1:5000/v2.0', '1', '1'), -# Tokens - ('token', 'add', '887665443383838', 'joeuser', 'customer-x', - '2012-02-05T00:00'), - ('token', 'add', '999888777666', 'admin', None, - '2015-02-05T00:00'), - ('token', 'add', '111222333444', 'serviceadmin', None, - '2015-02-05T00:00'), - ('token', 'add', '000999', 'admin', 'customer-x', '2010-02-05T00:00'), - ('token', 'add', '999888777', 'disabled', 'customer-x', - '2015-02-05T00:00'), -# Tenant endpointsGlobal endpoint not added - ('endpoint', 'add', 'customer-x', '1'), - ('endpoint', 'add', 'customer-x', '2'), - ('endpoint', 'add', 'customer-x', '3'), - ('endpoint', 'add', 'customer-x', '4'), - ('endpoint', 'add', 'customer-x', '5'), -# Add Credentials - ('credentials', 'add', 'admin', 'EC2', 'admin:admin', 'admin', - 'customer-x'), -] - - -def load_fixture(fixture=DEFAULT_FIXTURE, args=None): - keystone.manage.parse_args(args) - for cmd in fixture: - keystone.manage.process(*cmd) - - -def main(): - load_fixture() - - -if __name__ == '__main__': - main() diff --git a/keystone/test/unit/__init__.py b/keystone/test/unit/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/keystone/test/unit/base.py b/keystone/test/unit/base.py deleted file mode 100644 index 0ad0c327e8..0000000000 --- a/keystone/test/unit/base.py +++ /dev/null @@ -1,385 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 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. - -"""Base test case classes for the unit tests""" - -import datetime -import functools -import httplib -import logging -from lxml import etree, objectify -import pprint -import os -import sys -import tempfile -import unittest2 as unittest -import webob - -from keystone import config -from keystone import server -import keystone.backends.sqlalchemy as db -import keystone.backends.api as db_api -from keystone import backends - -logger = logging.getLogger('test.unit.base') - -CONF = config.CONF - - -class ServiceAPITest(unittest.TestCase): - """ - Base test case class for any unit test that tests the main service API. - """ - def __init__(self, *args, **kwargs): - super(ServiceAPITest, self).__init__(*args, **kwargs) - # The `api` attribute for this base class is the `server.KeystoneAPI` - # controller. - self.api_class = server.ServiceApi - # Set of dicts of tenant attributes we start each test case with - self.tenant_fixtures = [ - {'name': 'tenant1', - 'enabled': True, - 'desc': 'tenant1'}] - # Attributes of the Service used for the roles. - self.service_attrs = { - 'id': "0", - 'name': 'test_service', - 'type': 'test', - 'desc': 'test service', - 'owner_id': "0"} - # Set of Role fixtures to create for each test - self.role_fixtures = [ - {'id': "0", - 'name': 'regular_role', - 'desc': 'regular role', - 'service_id': self.service_attrs['id']}, - {'id': "1", - 'name': 'Admin', - 'desc': 'Admin role', - 'service_id': self.service_attrs['id']}, - ] - # Attributes of the user the test creates for each test case that - # will authenticate against the API. The `auth_user` attribute - # will contain the created user with the following attributes. - self.user_attrs = { - 'auth_user': - {'name': 'auth_user', - 'password': 'auth_pass', - 'email': 'auth_user@example.com', - 'enabled': True, - 'tenant_name': 'tenant1', - 'roles': [self.role_fixtures[0]], - }, - 'admin_user': - {'name': 'admin_user', - 'password': 'admin_pass', - 'email': 'admin_user@example.com', - 'enabled': True, - 'tenant_name': 'tenant1', - 'roles': [self.role_fixtures[1]], - }} - # Special attribute that is the identifier of the token we use in - # authenticating. Makes it easy to test the authentication process. - self.auth_token_id = 'SPECIALAUTHTOKEN' - # The special attribute for identifying the admin token. - self.admin_token_id = 'SPECIALADMINTOKEN' - # Content-type of requests. Generally, you don't need to manually - # change this. Instead, :see test.unit.decorators - self.content_type = 'json' - # Version of the API to test - self.api_version = '2.0' - # Save the original configuration - self.conf_save = CONF - # Set the desired conf options - conf_text = """ -[DEFAULT] -backends = keystone.backends.sqlalchemy -keystone_admin_role = Admin -keystone_service_admin_role = KeystoneServiceAdmin -hash_password = True -verbose = False -debug = False - -[keystone.backends.sqlalchemy] -sql_connection = sqlite:// -backend_entities = ['UserRoleAssociation', - 'Endpoints', 'Role', 'Tenant', 'User', - 'Credentials', 'EndpointTemplates', 'Token', 'Service'] -""" - self.update_CONF(conf_text) - - @staticmethod - def update_CONF(txt): - """ - Resets the CONF file, and reads in the passed configuration text. - """ - CONF.reset() - fd, tmpname = tempfile.mkstemp() - os.close(fd) - with file(tmpname, "w") as fconf: - fconf.write(txt) - CONF(config_files=[tmpname]) - os.remove(tmpname) - - def setUp(self): - self.api = self.api_class() - - dt = datetime - self.expires = dt.datetime.utcnow() + dt.timedelta(days=1) - self.clear_all_data() - - # Create all our base tenants - for tenant in self.tenant_fixtures: - self.fixture_create_tenant(**tenant) - - # Create the test service - self.fixture_create_service(**self.service_attrs) - - # Create all our roles - for role in self.role_fixtures: - self.fixture_create_role(**role) - - # Create the user we will authenticate with - auth_user_attrs = self.user_attrs.get("auth_user") - admin_user_attrs = self.user_attrs.get("admin_user") - self.auth_user = self.fixture_create_user(**auth_user_attrs) - self.admin_user = self.fixture_create_user(**admin_user_attrs) - self.auth_token = self.fixture_create_token( - id=self.auth_token_id, - user_id=self.auth_user['id'], - tenant_id=self.auth_user['tenant_id'], - expires=self.expires, - ) - self.admin_token = self.fixture_create_token( - id=self.admin_token_id, - user_id=self.admin_user['id'], - tenant_id=self.admin_user['tenant_id'], - expires=self.expires, - ) - - self.add_verify_status_helpers() - - def tearDown(self): - self.clear_all_data() - setattr(self, 'req', None) - setattr(self, 'res', None) - - def clear_all_data(self): - """ - Purges the database of all data - """ - db.unregister_models() - logger.debug("Cleared all data from database") - reload(db) - backends.configure_backends() - - def fixture_create_credentials(self, **kwargs): - """ - Creates a tenant fixture. - - :params \*\*kwargs: Attributes of the tenant to create - """ - values = kwargs.copy() - user = db_api.USER.get_by_name(values['user_name']) - if user: - values['user_id'] = user.id - credentials = db_api.CREDENTIALS.create(values) - logger.debug("Created credentials fixture %s", - credentials['user_id']) - return credentials - - def fixture_create_tenant(self, **kwargs): - """ - Creates a tenant fixture. - - :params \*\*kwargs: Attributes of the tenant to create - """ - values = kwargs.copy() - tenant = db_api.TENANT.create(values) - logger.debug("Created tenant fixture %s", values['name']) - return tenant - - def fixture_create_service(self, **kwargs): - """ - Creates a service fixture. - - :params \*\*kwargs: Attributes of the service to create - """ - values = kwargs.copy() - service = db_api.SERVICE.create(values) - logger.debug("Created service fixture %s", values['name']) - return service - - def fixture_create_user(self, **kwargs): - """ - Creates a user fixture. If the user's tenant ID is set, and the tenant - does not exist in the database, the tenant is created. - - :params \*\*kwargs: Attributes of the user to create - """ - values = kwargs.copy() - roles = values.pop("roles", []) - tenant_name = values.get('tenant_name') - if tenant_name: - if not db_api.TENANT.get_by_name(tenant_name): - tenant = db_api.TENANT.create({'name': tenant_name, - 'enabled': True, - 'desc': tenant_name}) - values['tenant_id'] = tenant.id - user = db_api.USER.create(values) - logger.debug("Created user fixture %s", user.id) - for role in roles: - user_role = db_api.USER.user_role_add( - {'user_id': user['id'], 'tenant_id': user['tenant_id'], - 'role_id': role['id']}) - logger.debug("Created user-role association %s", user_role.id) - return user - - def fixture_create_role(self, **kwargs): - """ - Creates a role fixture. - - :params \*\*kwargs: Attributes of the role to create - """ - values = kwargs.copy() - role = db_api.ROLE.create(values) - logger.debug("Created role fixture %s", role.id) - return role - - def fixture_create_token(self, **kwargs): - """ - Creates a token fixture. - - :params \*\*kwargs: Attributes of the token to create - """ - values = kwargs.copy() - token = db_api.TOKEN.create(values) - logger.debug("Created token fixture %s", values['id']) - return token - - def get_request(self, method, url, headers=None): - """ - Sets the `req` attribute to a `webob.Request` object that - is constructed with the supplied method and url. Supplied - headers are added to appropriate Content-type headers. - """ - headers = headers or {} - self.req = webob.Request.blank(url) - self.req.method = method - self.req.headers = headers - if 'content-type' not in headers: - ct = 'application/%s' % self.content_type - self.req.headers['content-type'] = ct - self.req.headers['accept'] = ct - return self.req - - def get_response(self): - """ - Sets the appropriate headers for the `req` attribute for - the current content type, then calls `req.get_response()` and - sets the `res` attribute to the returned `webob.Response` object - """ - self.res = self.req.get_response(self.api) - logger.debug("%s %s returned %s", self.req.method, self.req.path_qs, - self.res.status) - if self.res.status_int != httplib.OK: - logger.debug("Response Body:") - for line in self.res.body.split("\n"): - logger.debug(line) - return self.res - - def verify_status(self, status_code): - """ - Simple convenience wrapper for validating a response's status - code. - """ - if not getattr(self, 'res'): - raise RuntimeError("Called verify_status() before calling " - "get_response()!") - - self.assertEqual(status_code, self.res.status_int, - "Incorrect status code %d. Expected %d" % - (self.res.status_int, status_code)) - - def add_verify_status_helpers(self): - """ - Adds some convenience helpers using partials... - """ - self.status_ok = functools.partial(self.verify_status, - httplib.OK) - self.status_not_found = functools.partial(self.verify_status, - httplib.NOT_FOUND) - self.status_unauthorized = functools.partial(self.verify_status, - httplib.UNAUTHORIZED) - self.status_bad_request = functools.partial(self.verify_status, - httplib.BAD_REQUEST) - - def assert_dict_equal(self, expected, got): - """ - Compares two dicts for equality and prints the dictionaries - nicely formatted for easy comparison if there is a failure. - """ - self.assertEqual(expected, got, "Mappings are not equal.\n" - "Got:\n%s\nExpected:\n%s" % - (pprint.pformat(got), - pprint.pformat(expected))) - - def assert_xml_strings_equal(self, expected, got): - """ - Compares two XML strings for equality by parsing them both - into DOMs. Prints the DOMs nicely formatted for easy comparison - if there is a failure. - """ - # This is a nice little trick... objectify.fromstring() returns - # a DOM different from etree.fromstring(). The objectify version - # removes any different whitespacing... - got = objectify.fromstring(got) - expected = objectify.fromstring(expected) - self.assertEqual(etree.tostring(expected), - etree.tostring(got), "DOMs are not equal.\n" - "Got:\n%s\nExpected:\n%s" % - (etree.tostring(got, pretty_print=True), - etree.tostring(expected, pretty_print=True))) - - -class AdminAPITest(ServiceAPITest): - """ - Base test case class for any unit test that tests the admin API. The - """ - def __init__(self, *args, **kwargs): - super(AdminAPITest, self).__init__(*args, **kwargs) - # The `api` attribute for this base class is the - # `server.KeystoneAdminAPI` controller. - self.api_class = server.AdminApi - # Set of dicts of tenant attributes we start each test case with - self.tenant_fixtures = [ - {'id': 'tenant1', - 'name': 'tenant1', - 'enabled': True, - 'desc': 'tenant1'}, - {'id': 'tenant2', - 'name': 'tenant2', - 'enabled': True, - 'desc': 'tenant2'}] - - # Attributes of the user the test creates for each test case that - # will authenticate against the API. - self.auth_user_attrs = {'id': 'admin_user', - 'password': 'admin_pass', - 'email': 'admin_user@example.com', - 'enabled': True, - 'tenant_id': 'tenant2', - 'roles': []} diff --git a/keystone/test/unit/decorators.py b/keystone/test/unit/decorators.py deleted file mode 100644 index 17a7d4329e..0000000000 --- a/keystone/test/unit/decorators.py +++ /dev/null @@ -1,49 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 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. - -"""Decorators useful in unit tests""" - -import functools - - -def content_type(func, content_type='json'): - """ - Decorator for a test case method that sets the test case's - content_type to 'json' or 'xml' and resets it afterwards to - the original setting. This also asserts that if there is a - value for the test object's `res` attribute, that the content-type - header of the response is correct. - """ - @functools.wraps(func) - def wrapped(*a, **kwargs): - test_obj = a[0] - orig_content_type = test_obj.content_type - try: - test_obj.content_type = content_type - func(*a, **kwargs) - if getattr(test_obj, 'res'): - expected = 'application/%s' % content_type - got = test_obj.res.headers['content-type'].split(';')[0] - test_obj.assertEqual(expected, got, - "Bad content type: %s. Expected: %s" % - (got, expected)) - finally: - test_obj.content_type = orig_content_type - return wrapped - - -jsonify = functools.partial(content_type, content_type='json') -xmlify = functools.partial(content_type, content_type='xml') diff --git a/keystone/test/unit/test_auth.py b/keystone/test/unit/test_auth.py deleted file mode 100644 index 458873e7b1..0000000000 --- a/keystone/test/unit/test_auth.py +++ /dev/null @@ -1,187 +0,0 @@ -import json -import unittest2 as unittest -import keystone.logic.types.auth as auth -import keystone.logic.types.fault as fault - - -class TestAuth(unittest.TestCase): - '''Unit tests for auth.py.''' - - pwd_xml = '\ - \ - ' - - def test_pwd_cred_marshall(self): - creds = auth.AuthWithPasswordCredentials.from_xml(self.pwd_xml) - self.assertEqual(creds.password, "secret") - self.assertEqual(creds.username, "disabled") - - def test_pwd_creds_from_json(self): - data = json.dumps({"auth": - {"passwordCredentials": - {"username": "foo", "password": "bar"}}}) - creds = auth.AuthWithPasswordCredentials.from_json(data) - self.assertEqual(creds.username, "foo") - self.assertEqual(creds.password, "bar") - self.assertIsNone(creds.tenant_id) - self.assertIsNone(creds.tenant_name) - - def test_pwd_creds_with_tenant_name_from_json(self): - data = json.dumps({"auth": - {"tenantName": "blaa", - "passwordCredentials": - {"username": "foo", "password": "bar"}}}) - creds = auth.AuthWithPasswordCredentials.from_json(data) - self.assertEqual(creds.username, "foo") - self.assertEqual(creds.password, "bar") - self.assertIsNone(creds.tenant_id) - self.assertEqual(creds.tenant_name, "blaa") - - def test_pwd_creds_with_tenant_id_from_json(self): - data = json.dumps({"auth": - {"tenantId": "blaa", - "passwordCredentials": - {"username": "foo", "password": "bar"}}}) - creds = auth.AuthWithPasswordCredentials.from_json(data) - self.assertEqual(creds.username, "foo") - self.assertEqual(creds.password, "bar") - self.assertEqual(creds.tenant_id, "blaa") - self.assertIsNone(creds.tenant_name) - - def test_pwd_not_both_tenant_from_json(self): - data = json.dumps({"auth": {"tenantId": "blaa", "tenantName": "aalb"}}) - self.assertRaisesRegexp(fault.BadRequestFault, - "not both", - auth.AuthWithPasswordCredentials.from_json, - data) - - def test_pwd_invalid_from_json(self): - self.assertRaisesRegexp(fault.BadRequestFault, - "Cannot parse", - auth.AuthWithPasswordCredentials.from_json, - "") - - def test_pwd_no_auth_from_json(self): - data = json.dumps({"foo": "bar"}) - self.assertRaisesRegexp(fault.BadRequestFault, - "Expecting auth", - auth.AuthWithPasswordCredentials.from_json, - data) - - def test_pwd_no_creds_from_json(self): - data = json.dumps({"auth": {}}) - self.assertRaisesRegexp(fault.BadRequestFault, - "Expecting passwordCredentials", - auth.AuthWithPasswordCredentials.from_json, - data) - - def test_pwd_invalid_attribute_from_json(self): - data = json.dumps({"auth": {"foo": "bar"}}) - self.assertRaisesRegexp(fault.BadRequestFault, - "Invalid", - auth.AuthWithPasswordCredentials.from_json, - data) - - def test_pwd_no_username_from_json(self): - data = json.dumps({"auth": {"passwordCredentials": {}}}) - self.assertRaisesRegexp(fault.BadRequestFault, - "Expecting passwordCredentials:username", - auth.AuthWithPasswordCredentials.from_json, - data) - - def test_pwd_no_password_from_json(self): - data = json.dumps({"auth": {"passwordCredentials": - {"username": "foo"}}}) - self.assertRaisesRegexp(fault.BadRequestFault, - "Expecting passwordCredentials:password", - auth.AuthWithPasswordCredentials.from_json, - data) - - def test_pwd_invalid_creds_attribute_from_json(self): - data = json.dumps({"auth": {"passwordCredentials": {"bar": "foo"}}}) - self.assertRaisesRegexp(fault.BadRequestFault, - "Invalid", - auth.AuthWithPasswordCredentials.from_json, - data) - - def test_token_creds_from_json(self): - data = json.dumps({"auth": {"token": {"id": "1"}}}) - creds = auth.AuthWithUnscopedToken.from_json(data) - self.assertEqual(creds.token_id, "1") - self.assertIsNone(creds.tenant_id) - self.assertIsNone(creds.tenant_name) - - def test_token_creds_with_tenant_name_from_json(self): - data = json.dumps({"auth": - {"tenantName": "blaa", - "token": {"id": "1"}}}) - creds = auth.AuthWithUnscopedToken.from_json(data) - self.assertEqual(creds.token_id, "1") - self.assertIsNone(creds.tenant_id) - self.assertEqual(creds.tenant_name, "blaa") - - def test_token_creds_with_tenant_id_from_json(self): - data = json.dumps({"auth": - {"tenantId": "blaa", - "token": {"id": "1"}}}) - creds = auth.AuthWithUnscopedToken.from_json(data) - self.assertEqual(creds.token_id, "1") - self.assertEqual(creds.tenant_id, "blaa") - self.assertIsNone(creds.tenant_name) - - def test_token_not_both_tenant_from_json(self): - data = json.dumps({"auth": - {"tenantId": "blaa", - "tenantName": "aalb", - "token": {"id": "1"}}}) - self.assertRaisesRegexp(fault.BadRequestFault, - "not both", - auth.AuthWithUnscopedToken.from_json, - data) - - def test_token_invalid_from_json(self): - self.assertRaisesRegexp(fault.BadRequestFault, - "Cannot parse", - auth.AuthWithUnscopedToken.from_json, - "") - - def test_token_no_auth_from_json(self): - data = json.dumps({"foo": "bar"}) - self.assertRaisesRegexp(fault.BadRequestFault, - "Expecting auth", - auth.AuthWithUnscopedToken.from_json, - data) - - def test_token_no_creds_from_json(self): - data = json.dumps({"auth": {}}) - self.assertRaisesRegexp(fault.BadRequestFault, - "Expecting token", - auth.AuthWithUnscopedToken.from_json, - data) - - def test_token_invalid_attribute_from_json(self): - data = json.dumps({"auth": {"foo": "bar"}}) - self.assertRaisesRegexp(fault.BadRequestFault, - "Invalid", - auth.AuthWithUnscopedToken.from_json, - data) - - def test_token_no_id_from_json(self): - data = json.dumps({"auth": {"token": {}}}) - self.assertRaisesRegexp(fault.BadRequestFault, - "Expecting token:id", - auth.AuthWithUnscopedToken.from_json, - data) - - def test_token_invalid_token_attribute_from_json(self): - data = json.dumps({"auth": {"token": {"bar": "foo"}}}) - self.assertRaisesRegexp(fault.BadRequestFault, - "Invalid", - auth.AuthWithUnscopedToken.from_json, - data) - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_authn_ec2.py b/keystone/test/unit/test_authn_ec2.py deleted file mode 100644 index e8cdd995d9..0000000000 --- a/keystone/test/unit/test_authn_ec2.py +++ /dev/null @@ -1,303 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 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. - -import json -import logging -import unittest2 as unittest - -import base -from keystone.test.unit.decorators import jsonify -from keystone.logic import signer -from keystone.logic.types import auth - -LOGGER = logging.getLogger(__name__) - - -class EC2AuthnMethods(base.ServiceAPITest): - - @jsonify - def test_valid_authn_ec2_success_json(self): - """Tests correct syntax with {"auth":} wrapper and extension """ - url = "/tokens" - access = "xpd285.access" - secret = "345fgi.secret" - kwargs = { - "user_name": self.auth_user['name'], - "tenant_id": self.auth_user['tenant_id'], - "type": "EC2", - "key": access, - "secret": secret, - } - self.fixture_create_credentials(**kwargs) - req = self.get_request('POST', url) - params = { - "SignatureVersion": "2", - "one_param": "5", - "two_params": "happy", - } - credentials = { - "access": access, - "verb": "GET", - "params": params, - "host": "some.host.com:8773", - "path": "services/Cloud", - "signature": None, - } - sign = signer.Signer(secret) - obj_creds = auth.Ec2Credentials(**credentials) - credentials['signature'] = sign.generate(obj_creds) - body = { - "auth": { - "OS-KSEC2:ec2Credentials": credentials, - } - } - req.body = json.dumps(body) - self.get_response() - - expected = { - u'access': { - u'token': { - u'id': self.auth_token_id, - u'expires': self.expires.strftime("%Y-%m-%dT%H:%M:%S.%f")}, - u'user': { - u'id': unicode(self.auth_user['id']), - u'name': self.auth_user['name'], - u'roles': [{u'description': u'regular role', - u'id': u'0', - u'name': u'regular_role'}]}}} - self.assert_dict_equal(expected, json.loads(self.res.body)) - self.status_ok() - - @jsonify - def test_authn_ec2_success_json(self): - """Tests syntax with {"auth":} wrapper """ - self._auth_to_url(url="/ec2tokens") - - @jsonify - def test_authn_ec2_success_json_contract(self): - """Tests syntax with {"auth":} wrapper """ - self._auth_to_url(url="/tokens") - - def _auth_to_url(self, url): - """ - Test that plain ec2 credentials returns a 200 OK - """ - access = "xpd285.access" - secret = "345fgi.secret" - kwargs = { - "user_name": self.auth_user['name'], - "tenant_id": self.auth_user['tenant_id'], - "type": "EC2", - "key": access, - "secret": secret, - } - self.fixture_create_credentials(**kwargs) - req = self.get_request('POST', url) - params = { - "SignatureVersion": "2", - "one_param": "5", - "two_params": "happy", - } - credentials = { - "access": access, - "verb": "GET", - "params": params, - "host": "some.host.com:8773", - "path": "services/Cloud", - "signature": None, - } - sign = signer.Signer(secret) - obj_creds = auth.Ec2Credentials(**credentials) - credentials['signature'] = sign.generate(obj_creds) - body = { - "auth": { - "ec2Credentials": credentials, - } - } - req.body = json.dumps(body) - self.get_response() - - expected = { - u'access': { - u'token': { - u'id': self.auth_token_id, - u'expires': self.expires.strftime("%Y-%m-%dT%H:%M:%S.%f")}, - u'user': { - u'id': unicode(self.auth_user['id']), - u'name': self.auth_user['name'], - u'roles': [{u'description': u'regular role', - u'id': u'0', - u'name': u'regular_role'}]}}} - self.assert_dict_equal(expected, json.loads(self.res.body)) - self.status_ok() - - @jsonify - def test_old_authn_ec2_success_json(self): - """Tests old syntax without {"auth":} wrapper """ - self._old_auth_to_url(url="/ec2tokens") - - @jsonify - def test_old_authn_ec2_success_json_contract(self): - """Tests old syntax without {"auth":} wrapper """ - self._old_auth_to_url(url="/tokens") - - def _old_auth_to_url(self, url): - """ - Test that old ec2 credentials returns a 200 OK - """ - access = "xpd285.access" - secret = "345fgi.secret" - kwargs = { - "user_name": self.auth_user['name'], - "tenant_id": self.auth_user['tenant_id'], - "type": "EC2", - "key": access, - "secret": secret, - } - self.fixture_create_credentials(**kwargs) - req = self.get_request('POST', url) - params = { - "SignatureVersion": "2", - "one_param": "5", - "two_params": "happy", - } - credentials = { - "access": access, - "verb": "GET", - "params": params, - "host": "some.host.com:8773", - "path": "services/Cloud", - "signature": None, - } - sign = signer.Signer(secret) - obj_creds = auth.Ec2Credentials(**credentials) - credentials['signature'] = sign.generate(obj_creds) - body = { - "ec2Credentials": credentials, - } - req.body = json.dumps(body) - self.get_response() - - expected = { - u'access': { - u'token': { - u'id': self.auth_token_id, - u'expires': self.expires.strftime("%Y-%m-%dT%H:%M:%S.%f")}, - u'user': { - u'id': unicode(self.auth_user['id']), - u'name': self.auth_user['name'], - u'roles': [{u'description': u'regular role', - u'id': u'0', - u'name': u'regular_role'}]}}} - self.assert_dict_equal(expected, json.loads(self.res.body)) - self.status_ok() - - @jsonify - def test_authn_ec2_success_json_bad_user(self): - """ - Test that bad credentials returns a 401 - """ - access = "xpd285.access" - secret = "345fgi.secret" - url = "/ec2tokens" - req = self.get_request('POST', url) - params = { - "SignatureVersion": "2", - "one_param": "5", - "two_params": "happy", - } - credentials = { - "access": access, - "verb": "GET", - "params": params, - "host": "some.host.com:8773", - "path": "services/Cloud", - "signature": None, - } - sign = signer.Signer(secret) - obj_creds = auth.Ec2Credentials(**credentials) - credentials['signature'] = sign.generate(obj_creds) - body = { - "ec2Credentials": credentials, - } - req.body = json.dumps(body) - self.get_response() - - expected = { - u'unauthorized': { - u'code': u'401', - u'message': u'No credentials found for %s' % access, - } - } - self.assert_dict_equal(expected, json.loads(self.res.body)) - self.assertEqual(self.res.status_int, 401) - - @jsonify - def test_authn_ec2_success_json_bad_tenant(self): - """ - Test that bad credentials returns a 401 - """ - # Create dummy tenant (or adding creds will fail) - self.fixture_create_tenant(id='bad', name='bad') - access = "xpd285.access" - secret = "345fgi.secret" - kwargs = { - "user_name": self.auth_user['name'], - "tenant_id": 'bad', - "type": "EC2", - "key": access, - "secret": secret, - } - self.fixture_create_credentials(**kwargs) - # Delete the 'bad' tenant, orphaning the creds - self.get_request('DELETE', '/tenants/bad') - - url = "/ec2tokens" - req = self.get_request('POST', url) - params = { - "SignatureVersion": "2", - "one_param": "5", - "two_params": "happy", - } - credentials = { - "access": access, - "verb": "GET", - "params": params, - "host": "some.host.com:8773", - "path": "services/Cloud", - "signature": None, - } - sign = signer.Signer(secret) - obj_creds = auth.Ec2Credentials(**credentials) - credentials['signature'] = sign.generate(obj_creds) - body = { - "ec2Credentials": credentials, - } - req.body = json.dumps(body) - self.get_response() - - expected = { - u'unauthorized': { - u'code': u'401', - u'message': u'Unauthorized on this tenant', - } - } - self.assert_dict_equal(expected, json.loads(self.res.body)) - self.assertEqual(self.res.status_int, 401) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_authn_password.py b/keystone/test/unit/test_authn_password.py deleted file mode 100644 index 947dfc60aa..0000000000 --- a/keystone/test/unit/test_authn_password.py +++ /dev/null @@ -1,65 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 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. - -import json -import logging -import unittest2 as unittest - -import base -from keystone.test.unit.decorators import jsonify -from keystone.logic.types import auth - -LOGGER = logging.getLogger(__name__) - - -class PasswordAuthnMethods(base.ServiceAPITest): - - @jsonify - def test_authn_password_success_json(self): - """ - Test that good password credentials returns a 200 OK - """ - url = "/tokens" - req = self.get_request('POST', url) - credentials = { - "username": self.auth_user['name'], - "password": "auth_pass", - } - body = {"auth": { - "passwordCredentials": credentials, - "tenantId": self.auth_user['tenant_id'], - } - } - req.body = json.dumps(body) - self.get_response() - - expected = { - u'access': { - u'token': { - u'id': self.auth_token_id, - u'expires': self.expires.strftime("%Y-%m-%dT%H:%M:%S.%f")}, - u'user': { - u'id': unicode(self.auth_user['id']), - u'name': self.auth_user['name'], - u'roles': [{u'description': u'regular role', u'id': u'0', - u'name': u'regular_role'}]}}} - - self.assert_dict_equal(expected, json.loads(self.res.body)) - self.status_ok() - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_authn_s3.py b/keystone/test/unit/test_authn_s3.py deleted file mode 100644 index 2cb3a3586a..0000000000 --- a/keystone/test/unit/test_authn_s3.py +++ /dev/null @@ -1,245 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 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. - -import json -import logging -import unittest2 as unittest - -import base -from keystone.test.unit.decorators import jsonify -from keystone.logic import signer -from keystone.logic.types import auth - -LOGGER = logging.getLogger('test.unit.test_s3_authn') - - -class S3AuthnMethods(base.ServiceAPITest): - - @jsonify - def test_valid_authn_s3_success_json(self): - """Tests correct syntax with {"auth":} wrapper and extension """ - url = "/tokens" - access = "xpd285.access" - secret = "345fgi.secret" - kwargs = { - "user_name": self.auth_user['name'], - "tenant_id": self.auth_user['tenant_id'], - "type": "EC2", - "key": access, - "secret": secret, - } - self.fixture_create_credentials(**kwargs) - req = self.get_request('POST', url) - params = { - "x-amz-acl": "public-read-write", - "x-amz-server-side-encryption": "AES256", - } - credentials = { - "access": access, - "verb": "PUT", - "path": "/test.txt", - "expire": 0, - "content_type": "text/plain", - "content_md5": "1234567890abcdef", - "xheaders": params, - "signature": None, - } - sign = signer.Signer(secret) - obj_creds = auth.S3Credentials(**credentials) - credentials['signature'] = sign.generate(obj_creds, s3=True) - body = { - "auth": { - "OS-KSS3:s3Credentials": credentials, - } - } - req.body = json.dumps(body) - self.get_response() - - expected = { - u'access': { - u'token': { - u'id': self.auth_token_id, - u'expires': self.expires.strftime("%Y-%m-%dT%H:%M:%S.%f")}, - u'user': { - u'id': unicode(self.auth_user['id']), - u'name': self.auth_user['name'], - u'roles': [{u'description': u'regular role', - u'id': u'0', - u'name': u'regular_role'}]}}} - self.assert_dict_equal(expected, json.loads(self.res.body)) - self.status_ok() - - @jsonify - def test_authn_s3_success_json(self): - """Tests correct syntax with {"auth":} wrapper """ - self._auth_to_url(url="/tokens") - - def _auth_to_url(self, url): - """ - Test that good s3 credentials returns a 200 OK - """ - access = "xpd285.access" - secret = "345fgi.secret" - kwargs = { - "user_name": self.auth_user['name'], - "tenant_id": self.auth_user['tenant_id'], - "type": "EC2", - "key": access, - "secret": secret, - } - self.fixture_create_credentials(**kwargs) - req = self.get_request('POST', url) - params = { - "x-amz-acl": "public-read-write", - "x-amz-server-side-encryption": "AES256", - } - credentials = { - "access": access, - "verb": "PUT", - "path": "/test.txt", - "expire": 0, - "content_type": "text/plain", - "content_md5": "1234567890abcdef", - "xheaders": params, - "signature": None, - } - sign = signer.Signer(secret) - obj_creds = auth.S3Credentials(**credentials) - credentials['signature'] = sign.generate(obj_creds, s3=True) - body = { - "auth": { - "OS-KSS3:s3Credentials": credentials, - } - } - req.body = json.dumps(body) - self.get_response() - - expected = { - u'access': { - u'token': { - u'id': self.auth_token_id, - u'expires': self.expires.strftime("%Y-%m-%dT%H:%M:%S.%f")}, - u'user': { - u'id': unicode(self.auth_user['id']), - u'name': self.auth_user['name'], - u'roles': [{u'description': u'regular role', - u'id': u'0', - u'name': u'regular_role'}]}}} - self.assert_dict_equal(expected, json.loads(self.res.body)) - self.status_ok() - - @jsonify - def test_authn_s3_success_json_bad_user(self): - """ - Test that bad credentials returns a 401 - """ - access = "xpd285.access" - secret = "345fgi.secret" - url = "/tokens" - req = self.get_request('POST', url) - params = { - "x-amz-acl": "public-read-write", - "x-amz-server-side-encryption": "AES256", - } - credentials = { - "access": access, - "verb": "PUT", - "path": "/test.txt", - "expire": 0, - "content_type": "text/plain", - "content_md5": "1234567890abcdef", - "xheaders": params, - "signature": None, - } - sign = signer.Signer(secret) - obj_creds = auth.S3Credentials(**credentials) - credentials['signature'] = sign.generate(obj_creds, s3=True) - body = { - "auth": { - "OS-KSS3:s3Credentials": credentials, - } - } - req.body = json.dumps(body) - self.get_response() - - expected = { - u'unauthorized': { - u'code': u'401', - u'message': u'No credentials found for %s' % access, - } - } - self.assert_dict_equal(expected, json.loads(self.res.body)) - self.assertEqual(self.res.status_int, 401) - - @jsonify - def test_authn_s3_success_json_bad_tenant(self): - """ - Test that bad credentials returns a 401 - """ - # Create dummy tenant (or adding creds will fail) - self.fixture_create_tenant(id='bad', name='bad') - access = "xpd285.access" - secret = "345fgi.secret" - kwargs = { - "user_name": self.auth_user['name'], - "tenant_id": 'bad', - "type": "EC2", - "key": access, - "secret": secret, - } - self.fixture_create_credentials(**kwargs) - # Delete the 'bad' tenant, orphaning the creds - self.get_request('DELETE', '/tenants/bad') - - url = "/tokens" - req = self.get_request('POST', url) - params = { - "x-amz-acl": "public-read-write", - "x-amz-server-side-encryption": "AES256", - } - credentials = { - "access": access, - "verb": "PUT", - "path": "/test.txt", - "expire": 0, - "content_type": "text/plain", - "content_md5": "1234567890abcdef", - "xheaders": params, - "signature": None, - } - sign = signer.Signer(secret) - obj_creds = auth.S3Credentials(**credentials) - credentials['signature'] = sign.generate(obj_creds, s3=True) - body = { - "auth": { - "OS-KSS3:s3Credentials": credentials, - } - } - req.body = json.dumps(body) - self.get_response() - - expected = { - u'unauthorized': { - u'code': u'401', - u'message': u'Unauthorized on this tenant', - } - } - self.assert_dict_equal(expected, json.loads(self.res.body)) - self.assertEqual(self.res.status_int, 401) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_backends.py b/keystone/test/unit/test_backends.py deleted file mode 100644 index c1b9de1433..0000000000 --- a/keystone/test/unit/test_backends.py +++ /dev/null @@ -1,199 +0,0 @@ -# Copyright (c) 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. - -import os -import unittest2 as unittest -import uuid - -from keystone import backends -from keystone import config -from keystone.cfg import OptGroup, NoSuchOptError -import keystone.backends.api as api -import keystone.backends.models as legacy_backend_models -import keystone.backends.sqlalchemy as db -from keystone import models -from keystone.test import KeystoneTest -from keystone import utils - -CONF = config.CONF - - -class BackendTestCase(unittest.TestCase): - """ - Base class to run tests for Keystone backends (and backend configs) - """ - def __init__(self, *args, **kwargs): - super(BackendTestCase, self).__init__(*args, **kwargs) - self.base_template = "sql.conf.template" - self.ldap_template = "ldap.conf.template" - self.current_template = self.base_template - - def setUp(self): - self.update_CONF(self.current_template) - db.unregister_models() - reload(db) - backends.configure_backends() - super(BackendTestCase, self).setUp() - - def tearDown(self): - self.current_template = self.base_template - - def update_CONF(self, template): - """ - Resets the CONF file, and reads in the passed configuration text. - """ - kt = KeystoneTest() - kt.config_name = template - kt.construct_temp_conf_file() - fname = kt.conf_fp.name - # Provide a hook for customizing the config if needed. - self.modify_conf(fname) - # Create the configuration - CONF.reset() - CONF(config_files=[fname]) - - def modify_conf(self, fname): - pass - - def tearDown(self): - db.unregister_models() - reload(db) - - def test_registration(self): - self.assertIsNotNone(backends.api.CREDENTIALS) - self.assertIsNotNone(backends.api.ENDPOINT_TEMPLATE) - self.assertIsNotNone(backends.api.ROLE) - self.assertIsNotNone(backends.api.SERVICE) - self.assertIsNotNone(backends.api.TENANT) - self.assertIsNotNone(backends.api.TOKEN) - self.assertIsNotNone(backends.api.USER) - - def test_basic_tenant_create(self): - tenant = models.Tenant(name="Tee One", description="This is T1", - enabled=True) - - original_tenant = tenant.copy() - new_tenant = api.TENANT.create(tenant) - self.assertIsInstance(new_tenant, models.Tenant) - for k, v in original_tenant.items(): - if k not in ['id'] and k in new_tenant: - self.assertEquals(new_tenant[k], v) - - def test_tenant_create_with_id(self): - tenant = models.Tenant(id="T2%s" % uuid.uuid4().hex, name="Tee Two", - description="This is T2", enabled=True) - - original_tenant = tenant.to_dict() - new_tenant = api.TENANT.create(tenant) - self.assertIsInstance(new_tenant, models.Tenant) - for k, v in original_tenant.items(): - if k in new_tenant: - self.assertEquals(new_tenant[k], v, - "'%s' did not match" % k) - self.assertEqual(original_tenant['tenant'], tenant, - "Backend modified provided tenant") - - def test_tenant_update(self): - id = "T3%s" % uuid.uuid4().hex - tenant = models.Tenant(id=id, name="Tee Three", - description="This is T3", enabled=True) - new_tenant = api.TENANT.create(tenant) - new_tenant.enabled = False - new_tenant.description = "This is UPDATED T3" - api.TENANT.update(id, new_tenant) - updated_tenant = api.TENANT.get(id) - self.assertEqual(new_tenant, updated_tenant) - - def test_endpointtemplate_create(self): - service = models.Service(name="glance", type="image-service") - service = api.SERVICE.create(service) - - global_ept = models.EndpointTemplate( - region="north", - name="global", - type=service.type, - is_global=True, - public_URL="http://global.public") - global_ept = api.ENDPOINT_TEMPLATE.create(global_ept) - self.assertIsNotNone(global_ept.id) - - ept = models.EndpointTemplate( - region="north", - name="floating", - type=service.type, - is_global=False, - public_URL="http://floating.public/%tenant_id%/") - ept = api.ENDPOINT_TEMPLATE.create(ept) - self.assertIsNotNone(ept.id) - - def test_endpoint_list(self): - self.test_endpointtemplate_create() - self.test_basic_tenant_create() - tenant = api.TENANT.get_by_name("Tee One") - - templates = api.ENDPOINT_TEMPLATE.get_all() - for template in templates: - if not template.is_global: - endpoint = legacy_backend_models.Endpoints() - endpoint.tenant_id = tenant.id - endpoint.endpoint_template_id = template.id - api.ENDPOINT_TEMPLATE.endpoint_add(endpoint) - - global_endpoints = api.TENANT.get_all_endpoints(None) - self.assertGreater(len(global_endpoints), 0) - - tenant_endpoints = api.TENANT.get_all_endpoints(tenant.id) - self.assertGreater(len(tenant_endpoints), 0) - - -class LDAPBackendTestCase(BackendTestCase): - def setUp(self): - self.current_template = self.ldap_template - super(LDAPBackendTestCase, self).setUp() - - -class SQLiteBackendTestCase(BackendTestCase): - """ Tests SQLite backend using actual file (not in memory) - - Since we have a code path that is specific to in-memory databases, we need - to test for when we have a real file behind the ORM - """ - def setUp(self): - self.current_template = self.base_template - self.database_name = os.path.abspath("%s.test.db" % \ - uuid.uuid4().hex) - super(SQLiteBackendTestCase, self).setUp() - - def modify_conf(self, fname): - # Need to override the connection - conn = "sqlite:///%s" % self.database_name - out = [] - with file(fname, "r") as conf_file: - for ln in conf_file: - if ln.rstrip() == "sql_connection = sqlite://": - out.append("sql_connection = %s" % conn) - else: - out.append(ln.rstrip()) - with file(fname, "w") as conf_file: - conf_file.write("\n".join(out)) - - def tearDown(self): - super(SQLiteBackendTestCase, self).tearDown() - if os.path.exists(self.database_name): - os.unlink(self.database_name) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_buffout.py b/keystone/test/unit/test_buffout.py deleted file mode 100644 index 715960e0d6..0000000000 --- a/keystone/test/unit/test_buffout.py +++ /dev/null @@ -1,91 +0,0 @@ -import unittest2 as unittest -import sys - -from keystone.tools import buffout - - -class TestStdoutIdentity(unittest.TestCase): - """Tests buffout's manipulation of the stdout pointer""" - def test_stdout(self): - stdout = sys.stdout - ob = buffout.OutputBuffer() - self.assertTrue(sys.stdout is stdout, - "sys.stdout was replaced") - ob.start() - self.assertTrue(sys.stdout is not stdout, - "sys.stdout not replaced") - ob.stop() - self.assertTrue(sys.stdout is stdout, - "sys.stdout not restored") - - -class TestOutputBufferContents(unittest.TestCase): - """Tests the contents of the buffer""" - def test_read_contents(self): - with buffout.OutputBuffer() as ob: - print 'foobar' - print 'wompwomp' - output = ob.read() - self.assertEquals(len(output), 16, output) - self.assertIn('foobar', output) - self.assertIn('ompwom', output) - - def test_read_lines(self): - with buffout.OutputBuffer() as ob: - print 'foobar' - print 'wompwomp' - lines = ob.read_lines() - self.assertTrue(isinstance(lines, list)) - self.assertEqual(len(lines), 2) - self.assertIn('foobar', lines) - self.assertIn('wompwomp', lines) - - def test_additional_output(self): - with buffout.OutputBuffer() as ob: - print 'foobar' - lines = ob.read_lines() - self.assertEqual(len(lines), 1) - print 'wompwomp' - lines = ob.read_lines() - self.assertEqual(len(lines), 2) - - def test_clear(self): - with buffout.OutputBuffer() as ob: - print 'foobar' - ob.clear() - print 'wompwomp' - output = ob.read() - self.assertNotIn('foobar', output) - self.assertIn('ompwom', output) - - def test_buffer_preservation(self): - ob = buffout.OutputBuffer() - ob.start() - - print 'foobar' - print 'wompwomp' - - ob.stop() - - output = ob.read() - self.assertIn('foobar', output) - self.assertIn('ompwom', output) - - def test_buffer_contents(self): - ob = buffout.OutputBuffer() - ob.start() - - print 'foobar' - print 'wompwomp' - - ob.stop() - - self.assertEqual('foobar\nwompwomp\n', unicode(ob)) - self.assertEqual('foobar\nwompwomp\n', str(ob)) - - def test_exception_raising(self): - def raise_value_error(): - with buffout.OutputBuffer(): - raise ValueError() - - self.assertRaises(ValueError, raise_value_error) diff --git a/keystone/test/unit/test_cfg.py b/keystone/test/unit/test_cfg.py deleted file mode 100644 index 9dc07727db..0000000000 --- a/keystone/test/unit/test_cfg.py +++ /dev/null @@ -1,826 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 Red Hat, Inc. -# -# 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 -import sys -import StringIO -import tempfile -import unittest - -import stubout - -from keystone.cfg import * - - -class BaseTestCase(unittest.TestCase): - - def setUp(self): - self.conf = ConfigOpts(prog='test', - version='1.0', - usage='%prog FOO BAR', - default_config_files=[]) - self.tempfiles = [] - self.stubs = stubout.StubOutForTesting() - - def tearDown(self): - self.remove_tempfiles() - self.stubs.UnsetAll() - - def create_tempfiles(self, files): - for (basename, contents) in files: - (fd, path) = tempfile.mkstemp(prefix=basename) - self.tempfiles.append(path) - try: - os.write(fd, contents) - finally: - os.close(fd) - return self.tempfiles[-len(files):] - - def remove_tempfiles(self): - for p in self.tempfiles: - os.remove(p) - - -class LeftoversTestCase(BaseTestCase): - - def test_leftovers(self): - self.conf.register_cli_opt(StrOpt('foo')) - self.conf.register_cli_opt(StrOpt('bar')) - - leftovers = self.conf(['those', '--foo', 'this', - 'thems', '--bar', 'that', 'these']) - - self.assertEquals(leftovers, ['those', 'thems', 'these']) - - -class FindConfigFilesTestCase(BaseTestCase): - - def test_find_config_files(self): - config_files = \ - [os.path.expanduser('~/.blaa/blaa.conf'), '/etc/foo.conf'] - - self.stubs.Set(os.path, 'exists', lambda p: p in config_files) - - self.assertEquals(find_config_files(project='blaa', prog='foo'), - config_files) - - -class CliOptsTestCase(BaseTestCase): - - def _do_cli_test(self, opt_class, default, cli_args, value): - self.conf.register_cli_opt(opt_class('foo', default=default)) - - self.conf(cli_args) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, value) - - def test_str_default(self): - self._do_cli_test(StrOpt, None, [], None) - - def test_str_arg(self): - self._do_cli_test(StrOpt, None, ['--foo', 'bar'], 'bar') - - def test_bool_default(self): - self._do_cli_test(BoolOpt, False, [], False) - - def test_bool_arg(self): - self._do_cli_test(BoolOpt, None, ['--foo'], True) - - def test_bool_arg_inverse(self): - self._do_cli_test(BoolOpt, None, ['--foo', '--nofoo'], False) - - def test_int_default(self): - self._do_cli_test(IntOpt, 10, [], 10) - - def test_int_arg(self): - self._do_cli_test(IntOpt, None, ['--foo=20'], 20) - - def test_float_default(self): - self._do_cli_test(FloatOpt, 1.0, [], 1.0) - - def test_float_arg(self): - self._do_cli_test(FloatOpt, None, ['--foo', '2.0'], 2.0) - - def test_list_default(self): - self._do_cli_test(ListOpt, ['bar'], [], ['bar']) - - def test_list_arg(self): - self._do_cli_test(ListOpt, None, - ['--foo', 'blaa,bar'], ['blaa', 'bar']) - - def test_multistr_default(self): - self._do_cli_test(MultiStrOpt, ['bar'], [], ['bar']) - - def test_multistr_arg(self): - self._do_cli_test(MultiStrOpt, None, - ['--foo', 'blaa', '--foo', 'bar'], ['blaa', 'bar']) - - def test_help(self): - self.stubs.Set(sys, 'stdout', StringIO.StringIO()) - self.assertRaises(SystemExit, self.conf, ['--help']) - self.assertTrue('FOO BAR' in sys.stdout.getvalue()) - self.assertTrue('--version' in sys.stdout.getvalue()) - self.assertTrue('--help' in sys.stdout.getvalue()) - self.assertTrue('--config-file=PATH' in sys.stdout.getvalue()) - - def test_version(self): - self.stubs.Set(sys, 'stdout', StringIO.StringIO()) - self.assertRaises(SystemExit, self.conf, ['--version']) - self.assertTrue('1.0' in sys.stdout.getvalue()) - - def test_config_file(self): - paths = self.create_tempfiles([('1.conf', '[DEFAULT]'), - ('2.conf', '[DEFAULT]')]) - - self.conf(['--config-file', paths[0], '--config-file', paths[1]]) - - self.assertEquals(self.conf.config_file, paths) - - -class ConfigFileOptsTestCase(BaseTestCase): - - def test_str_default(self): - self.conf.register_opt(StrOpt('foo', default='bar')) - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, 'bar') - - def test_str_value(self): - self.conf.register_opt(StrOpt('foo')) - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n' - 'foo = bar\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, 'bar') - - def test_str_value_file_override(self): - self.conf.register_cli_opt(StrOpt('foo')) - - paths = self.create_tempfiles([('1.conf', - '[DEFAULT]\n' - 'foo = baar\n'), - ('2.conf', - '[DEFAULT]\n' - 'foo = baaar\n')]) - - self.conf(['--config-file', paths[0], - '--config-file', paths[1]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, 'baaar') - - def test_str_value_arg_override(self): - self.conf.register_cli_opt(StrOpt('foo')) - - paths = self.create_tempfiles([('1.conf', - '[DEFAULT]\n' - 'foo = baar\n'), - ('2.conf', - '[DEFAULT]\n' - 'foo = baaar\n')]) - - self.conf(['--foo', 'bar', - '--config-file', paths[0], - '--config-file', paths[1]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, 'bar') - - def test_int_default(self): - self.conf.register_opt(IntOpt('foo', default=666)) - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, 666) - - def test_int_value(self): - self.conf.register_opt(IntOpt('foo')) - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n' - 'foo = 666\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, 666) - - def test_int_value_file_override(self): - self.conf.register_cli_opt(IntOpt('foo')) - - paths = self.create_tempfiles([('1.conf', - '[DEFAULT]\n' - 'foo = 66\n'), - ('2.conf', - '[DEFAULT]\n' - 'foo = 666\n')]) - - self.conf(['--config-file', paths[0], - '--config-file', paths[1]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, 666) - - def test_int_value_arg_override(self): - self.conf.register_cli_opt(IntOpt('foo')) - - paths = self.create_tempfiles([('1.conf', - '[DEFAULT]\n' - 'foo = 66\n'), - ('2.conf', - '[DEFAULT]\n' - 'foo = 666\n')]) - - self.conf(['--foo', '6', - '--config-file', paths[0], - '--config-file', paths[1]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, 6) - - def test_float_default(self): - self.conf.register_opt(FloatOpt('foo', default=6.66)) - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, 6.66) - - def test_float_value(self): - self.conf.register_opt(FloatOpt('foo')) - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n' - 'foo = 6.66\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, 6.66) - - def test_float_value_arg_override(self): - self.conf.register_cli_opt(FloatOpt('foo')) - - paths = self.create_tempfiles([('1.conf', - '[DEFAULT]\n' - 'foo = 6.6\n'), - ('2.conf', - '[DEFAULT]\n' - 'foo = 6.66\n')]) - - self.conf(['--foo', '6', - '--config-file', paths[0], - '--config-file', paths[1]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, 6.0) - - def test_float_value_file_override(self): - self.conf.register_cli_opt(FloatOpt('foo')) - - paths = self.create_tempfiles([('1.conf', - '[DEFAULT]\n' - 'foo = 6.6\n'), - ('2.conf', - '[DEFAULT]\n' - 'foo = 6.66\n')]) - - self.conf(['--config-file', paths[0], - '--config-file', paths[1]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, 6.66) - - def test_list_default(self): - self.conf.register_opt(ListOpt('foo', default=['bar'])) - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, ['bar']) - - def test_list_value(self): - self.conf.register_opt(ListOpt('foo')) - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n' - 'foo = bar\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, ['bar']) - - def test_list_value_override(self): - self.conf.register_cli_opt(ListOpt('foo')) - - paths = self.create_tempfiles([('1.conf', - '[DEFAULT]\n' - 'foo = bar,bar\n'), - ('2.conf', - '[DEFAULT]\n' - 'foo = b,a,r\n')]) - - self.conf(['--config-file', paths[0], - '--config-file', paths[1]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, ['b', 'a', 'r']) - - def test_multistr_default(self): - self.conf.register_opt(MultiStrOpt('foo', default=['bar'])) - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, ['bar']) - - def test_multistr_value(self): - self.conf.register_opt(MultiStrOpt('foo')) - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n' - 'foo = bar\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, ['bar']) - - def test_multistr_values_append(self): - self.conf.register_cli_opt(ListOpt('foo')) - - paths = self.create_tempfiles([('1.conf', - '[DEFAULT]\n' - 'foo = bar\n'), - ('2.conf', - '[DEFAULT]\n' - 'foo = bar\n')]) - - self.conf(['--foo', 'bar', - '--config-file', paths[0], - '--config-file', paths[1]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - - # FIXME(markmc): values spread across the CLI and multiple - # config files should be appended - # self.assertEquals(self.conf.foo, ['bar', 'bar', 'bar']) - - -class OptGroupsTestCase(BaseTestCase): - - def test_arg_group(self): - blaa_group = OptGroup('blaa') - self.conf.register_group(blaa_group) - self.conf.register_cli_opt(StrOpt('foo'), group=blaa_group) - - self.conf(['--blaa-foo', 'bar']) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'foo')) - self.assertEquals(self.conf.blaa.foo, 'bar') - - def test_arg_group_by_name(self): - self.conf.register_group(OptGroup('blaa')) - self.conf.register_cli_opt(StrOpt('foo'), group='blaa') - - self.conf(['--blaa-foo', 'bar']) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'foo')) - self.assertEquals(self.conf.blaa.foo, 'bar') - - def test_arg_group_with_default(self): - self.conf.register_group(OptGroup('blaa')) - self.conf.register_cli_opt(StrOpt('foo', default='bar'), group='blaa') - - self.conf([]) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'foo')) - self.assertEquals(self.conf.blaa.foo, 'bar') - - def test_arg_group_in_config_file(self): - self.conf.register_group(OptGroup('blaa')) - self.conf.register_opt(StrOpt('foo'), group='blaa') - - paths = self.create_tempfiles([('test.conf', - '[blaa]\n' - 'foo = bar\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'foo')) - self.assertEquals(self.conf.blaa.foo, 'bar') - - -class TemplateSubstitutionTestCase(BaseTestCase): - - def _prep_test_str_sub(self, foo_default=None, bar_default=None): - self.conf.register_cli_opt(StrOpt('foo', default=foo_default)) - self.conf.register_cli_opt(StrOpt('bar', default=bar_default)) - - def _assert_str_sub(self): - self.assertTrue(hasattr(self.conf, 'bar')) - self.assertEquals(self.conf.bar, 'blaa') - - def test_str_sub_default_from_default(self): - self._prep_test_str_sub(foo_default='blaa', bar_default='$foo') - - self.conf([]) - - self._assert_str_sub() - - def test_str_sub_default_from_arg(self): - self._prep_test_str_sub(bar_default='$foo') - - self.conf(['--foo', 'blaa']) - - self._assert_str_sub() - - def test_str_sub_default_from_config_file(self): - self._prep_test_str_sub(bar_default='$foo') - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n' - 'foo = blaa\n')]) - - self.conf(['--config-file', paths[0]]) - - self._assert_str_sub() - - def test_str_sub_arg_from_default(self): - self._prep_test_str_sub(foo_default='blaa') - - self.conf(['--bar', '$foo']) - - self._assert_str_sub() - - def test_str_sub_arg_from_arg(self): - self._prep_test_str_sub() - - self.conf(['--foo', 'blaa', '--bar', '$foo']) - - self._assert_str_sub() - - def test_str_sub_arg_from_config_file(self): - self._prep_test_str_sub() - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n' - 'foo = blaa\n')]) - - self.conf(['--config-file', paths[0], '--bar=$foo']) - - self._assert_str_sub() - - def test_str_sub_config_file_from_default(self): - self._prep_test_str_sub(foo_default='blaa') - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n' - 'bar = $foo\n')]) - - self.conf(['--config-file', paths[0]]) - - self._assert_str_sub() - - def test_str_sub_config_file_from_arg(self): - self._prep_test_str_sub() - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n' - 'bar = $foo\n')]) - - self.conf(['--config-file', paths[0], '--foo=blaa']) - - self._assert_str_sub() - - def test_str_sub_config_file_from_config_file(self): - self._prep_test_str_sub() - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n' - 'bar = $foo\n' - 'foo = blaa\n')]) - - self.conf(['--config-file', paths[0]]) - - self._assert_str_sub() - - def test_str_sub_group_from_default(self): - self.conf.register_cli_opt(StrOpt('foo', default='blaa')) - self.conf.register_group(OptGroup('ba')) - self.conf.register_cli_opt(StrOpt('r', default='$foo'), group='ba') - - self.conf([]) - - self.assertTrue(hasattr(self.conf, 'ba')) - self.assertTrue(hasattr(self.conf.ba, 'r')) - self.assertEquals(self.conf.ba.r, 'blaa') - - -class ReparseTestCase(BaseTestCase): - - def test_reparse(self): - self.conf.register_group(OptGroup('blaa')) - self.conf.register_cli_opt(StrOpt('foo', default='r'), group='blaa') - - paths = self.create_tempfiles([('test.conf', - '[blaa]\n' - 'foo = b\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'foo')) - self.assertEquals(self.conf.blaa.foo, 'b') - - self.conf(['--blaa-foo', 'a']) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'foo')) - self.assertEquals(self.conf.blaa.foo, 'a') - - self.conf([]) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'foo')) - self.assertEquals(self.conf.blaa.foo, 'r') - - -class OverridesTestCase(BaseTestCase): - - def test_no_default_override(self): - self.conf.register_opt(StrOpt('foo')) - self.conf([]) - self.assertEquals(self.conf.foo, None) - self.conf.set_default('foo', 'bar') - self.assertEquals(self.conf.foo, 'bar') - - def test_default_override(self): - self.conf.register_opt(StrOpt('foo', default='foo')) - self.conf([]) - self.assertEquals(self.conf.foo, 'foo') - self.conf.set_default('foo', 'bar') - self.assertEquals(self.conf.foo, 'bar') - self.conf.set_default('foo', None) - self.assertEquals(self.conf.foo, 'foo') - - def test_override(self): - self.conf.register_opt(StrOpt('foo')) - self.conf.set_override('foo', 'bar') - self.conf([]) - self.assertEquals(self.conf.foo, 'bar') - - def test_group_no_default_override(self): - self.conf.register_group(OptGroup('blaa')) - self.conf.register_opt(StrOpt('foo'), group='blaa') - self.conf([]) - self.assertEquals(self.conf.blaa.foo, None) - self.conf.set_default('foo', 'bar', group='blaa') - self.assertEquals(self.conf.blaa.foo, 'bar') - - def test_default_override_II(self): - self.conf.register_group(OptGroup('blaa')) - self.conf.register_opt(StrOpt('foo', default='foo'), group='blaa') - self.conf([]) - self.assertEquals(self.conf.blaa.foo, 'foo') - self.conf.set_default('foo', 'bar', group='blaa') - self.assertEquals(self.conf.blaa.foo, 'bar') - self.conf.set_default('foo', None, group='blaa') - self.assertEquals(self.conf.blaa.foo, 'foo') - - def test_override_II(self): - self.conf.register_group(OptGroup('blaa')) - self.conf.register_opt(StrOpt('foo'), group='blaa') - self.conf.set_override('foo', 'bar', group='blaa') - self.conf([]) - self.assertEquals(self.conf.blaa.foo, 'bar') - - -class SadPathTestCase(BaseTestCase): - - def test_unknown_attr(self): - self.conf([]) - self.assertFalse(hasattr(self.conf, 'foo')) - self.assertRaises(NoSuchOptError, getattr, self.conf, 'foo') - - def test_unknown_group_attr(self): - self.conf.register_group(OptGroup('blaa')) - - self.conf([]) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertFalse(hasattr(self.conf.blaa, 'foo')) - self.assertRaises(NoSuchOptError, getattr, self.conf.blaa, 'foo') - - def test_ok_duplicate(self): - opt = StrOpt('foo') - self.conf.register_cli_opt(opt) - self.conf.register_cli_opt(opt) - - self.conf([]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, None) - - def test_error_duplicate(self): - self.conf.register_cli_opt(StrOpt('foo')) - self.assertRaises(DuplicateOptError, - self.conf.register_cli_opt, StrOpt('foo')) - - def test_error_duplicate_with_different_dest(self): - self.conf.register_cli_opt(StrOpt('foo', dest='f')) - self.assertRaises(DuplicateOptError, - self.conf.register_cli_opt, StrOpt('foo')) - - def test_error_duplicate_short(self): - self.conf.register_cli_opt(StrOpt('foo', short='f')) - self.assertRaises(DuplicateOptError, - self.conf.register_cli_opt, StrOpt('bar', short='f')) - - def test_no_such_group(self): - self.assertRaises(NoSuchGroupError, self.conf.register_cli_opt, - StrOpt('foo'), group='blaa') - - def test_already_parsed(self): - self.conf([]) - - self.assertRaises(ArgsAlreadyParsedError, - self.conf.register_cli_opt, StrOpt('foo')) - - def test_bad_cli_arg(self): - self.stubs.Set(sys, 'stderr', StringIO.StringIO()) - - self.assertRaises(SystemExit, self.conf, ['--foo']) - - self.assertTrue('error' in sys.stderr.getvalue()) - self.assertTrue('--foo' in sys.stderr.getvalue()) - - def _do_test_bad_cli_value(self, opt_class): - self.conf.register_cli_opt(opt_class('foo')) - - self.stubs.Set(sys, 'stderr', StringIO.StringIO()) - - self.assertRaises(SystemExit, self.conf, ['--foo', 'bar']) - - self.assertTrue('foo' in sys.stderr.getvalue()) - self.assertTrue('bar' in sys.stderr.getvalue()) - - def test_bad_int_arg(self): - self._do_test_bad_cli_value(IntOpt) - - def test_bad_float_arg(self): - self._do_test_bad_cli_value(FloatOpt) - - def test_conf_file_not_found(self): - paths = self.create_tempfiles([('test.conf', 'foo')]) - - self.assertRaises(ConfigFileParseError, - self.conf, ['--config-file', paths[0]]) - - def _do_test_conf_file_bad_value(self, opt_class): - self.conf.register_opt(opt_class('foo')) - - def test_conf_file_bad_bool(self): - self._do_test_conf_file_bad_value(BoolOpt) - - def test_conf_file_bad_int(self): - self._do_test_conf_file_bad_value(IntOpt) - - def test_conf_file_bad_float(self): - self._do_test_conf_file_bad_value(FloatOpt) - - def test_str_sub_from_group(self): - self.conf.register_group(OptGroup('f')) - self.conf.register_cli_opt(StrOpt('oo', default='blaa'), group='f') - self.conf.register_cli_opt(StrOpt('bar', default='$f.oo')) - - self.conf([]) - - self.assertFalse(hasattr(self.conf, 'bar')) - self.assertRaises(TemplateSubstitutionError, getattr, self.conf, 'bar') - - def test_set_default_unknown_attr(self): - self.conf([]) - self.assertRaises(NoSuchOptError, self.conf.set_default, 'foo', 'bar') - - def test_set_default_unknown_group(self): - self.conf([]) - self.assertRaises(NoSuchGroupError, - self.conf.set_default, 'foo', 'bar', group='blaa') - - def test_set_override_unknown_attr(self): - self.conf([]) - self.assertRaises(NoSuchOptError, self.conf.set_override, 'foo', 'bar') - - def test_set_override_unknown_group(self): - self.conf([]) - self.assertRaises(NoSuchGroupError, - self.conf.set_override, 'foo', 'bar', group='blaa') - - -class OptDumpingTestCase(BaseTestCase): - - class FakeLogger: - - def __init__(self, test_case, expected_lvl): - self.test_case = test_case - self.expected_lvl = expected_lvl - self.logged = [] - - def log(self, lvl, fmt, *args): - self.test_case.assertEquals(lvl, self.expected_lvl) - self.logged.append(fmt % args) - - def test_log_opt_values(self): - self.conf.register_cli_opt(StrOpt('foo')) - self.conf.register_group(OptGroup('blaa')) - self.conf.register_cli_opt(StrOpt('bar'), 'blaa') - - self.conf(['--foo', 'this', '--blaa-bar', 'that']) - - logger = self.FakeLogger(self, 666) - - self.conf.log_opt_values(logger, 666) - - self.assertEquals(logger.logged, [ - "*" * 80, - "Configuration options gathered from:", - "command line args: ['--foo', 'this', '--blaa-bar', 'that']", - "config files: []", - "=" * 80, - "config_file = []", - "foo = this", - "blaa.bar = that", - "*" * 80, - ]) - - -class CommonOptsTestCase(BaseTestCase): - - def setUp(self): - super(CommonOptsTestCase, self).setUp() - self.conf = CommonConfigOpts() - - def test_debug_verbose(self): - self.conf(['--debug', '--verbose']) - - self.assertEquals(self.conf.debug, True) - self.assertEquals(self.conf.verbose, True) - - def test_logging_opts(self): - self.conf([]) - - self.assertTrue(self.conf.log_config is None) - self.assertTrue(self.conf.log_file is None) - self.assertTrue(self.conf.log_dir is None) - - self.assertEquals(self.conf.log_format, - CommonConfigOpts.DEFAULT_LOG_FORMAT) - self.assertEquals(self.conf.log_date_format, - CommonConfigOpts.DEFAULT_LOG_DATE_FORMAT) - - self.assertEquals(self.conf.use_syslog, False) - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_commands.py b/keystone/test/unit/test_commands.py deleted file mode 100644 index cb8e86dfec..0000000000 --- a/keystone/test/unit/test_commands.py +++ /dev/null @@ -1,1241 +0,0 @@ -import argparse -import datetime -import logging -import unittest2 as unittest -import uuid - -from keystone import backends -import keystone.backends.sqlalchemy as db -from keystone.manage2 import base -from keystone.manage2 import common -from keystone.manage2.commands import create_credential -from keystone.manage2.commands import create_endpoint_template -from keystone.manage2.commands import create_role -from keystone.manage2.commands import create_service -from keystone.manage2.commands import create_tenant -from keystone.manage2.commands import create_token -from keystone.manage2.commands import create_user -from keystone.manage2.commands import delete_credential -from keystone.manage2.commands import delete_endpoint_template -from keystone.manage2.commands import delete_role -from keystone.manage2.commands import delete_service -from keystone.manage2.commands import delete_tenant -from keystone.manage2.commands import delete_token -from keystone.manage2.commands import delete_user -from keystone.manage2.commands import grant_role -from keystone.manage2.commands import list_credentials -from keystone.manage2.commands import list_endpoint_templates -from keystone.manage2.commands import list_endpoints -from keystone.manage2.commands import list_role_grants -from keystone.manage2.commands import list_roles -from keystone.manage2.commands import list_services -from keystone.manage2.commands import list_tenants -from keystone.manage2.commands import list_tokens -from keystone.manage2.commands import list_users -from keystone.manage2.commands import map_endpoint -from keystone.manage2.commands import revoke_role -from keystone.manage2.commands import unmap_endpoint -from keystone.manage2.commands import update_credential -from keystone.manage2.commands import update_endpoint_template -from keystone.manage2.commands import update_role -from keystone.manage2.commands import update_service -from keystone.manage2.commands import update_tenant -from keystone.manage2.commands import update_token -from keystone.manage2.commands import update_user -from keystone.manage2.commands import version -from keystone.tools import buffout -from keystone import utils - - -LOGGER = logging.getLogger(__name__) - -OPTIONS = { - "keystone-service-admin-role": "KeystoneServiceAdmin", - "keystone-admin-role": "KeystoneAdmin", - "hash-password": "False", - 'backends': 'keystone.backends.sqlalchemy', - 'keystone.backends.sqlalchemy': { - "sql_connection": "sqlite://", - "backend_entities": "['UserRoleAssociation', " - "'Endpoints', 'Role', 'Tenant', 'User', " - "'Credentials', 'EndpointTemplates', 'Token', " - "'Service']", - "sql_idle_timeout": "30"}} -# Configure the CONF module to match -utils.set_configuration(OPTIONS) - - -class CommandTestCase(unittest.TestCase): - """Buffers stdout to test keystone-manage commands""" - - def run_cmd(self, module, args=None, use_managers=True): - """Runs the Command in the given module using the provided args""" - args = args if args is not None else [] - - managers = self.managers if use_managers else None - - cmd = module.Command(managers=managers) - parsed_args = cmd.parser.parse_args(args) - return cmd.run(parsed_args) - - def setUp(self): - self.managers = common.init_managers() - - # buffer stdout so we can assert what's printed - self.ob = buffout.OutputBuffer() - self.ob.start() - - def tearDown(self): - self.ob.stop() - - self.clear_all_data() - - def clear_all_data(self): - """ - Purges the database of all data - """ - db.unregister_models() - reload(db) - backends.configure_backends() - - def assertTableContainsRow(self, table, row): - """Assumes that row[0] is a unique identifier for the row""" - # ensure we're comparing str to str - row = [str(col) for col in row] - - # ensure the data we're looking for is *somewhere* in the table - self.assertIn(row[0], table) - - # find the matching row - matching_row = [r for r in table.split("\n") if row[0] in r][0] - matching_row = [c for c in matching_row.split() if c.strip() != '|'] - - self.assertEquals(row, matching_row) - - def _create_user(self): - # TODO(dolph): these ob.clear()'s need to be cleaned up - self.ob.clear() - self.run_cmd(create_user, [ - '--name', uuid.uuid4().hex, - '--password', uuid.uuid4().hex]) - obj_id = self.ob.read_lines()[0] - self.ob.clear() - return obj_id - - def _create_tenant(self): - self.ob.clear() - self.run_cmd(create_tenant, [ - '--name', uuid.uuid4().hex]) - obj_id = self.ob.read_lines()[0] - self.ob.clear() - return obj_id - - def _create_token(self, user_id): - self.ob.clear() - self.run_cmd(create_token, [ - '--user-id', user_id]) - obj_id = self.ob.read_lines()[0] - self.ob.clear() - return obj_id - - def _create_credential(self, user_id): - self.ob.clear() - cred_type = uuid.uuid4().hex - key = uuid.uuid4().hex - secret = uuid.uuid4().hex - self.run_cmd(create_credential, [ - '--user-id', user_id, - '--type', cred_type, - '--key', key, - '--secret', secret]) - obj_id = self.ob.read_lines()[0] - self.ob.clear() - return obj_id - - def _create_service(self): - self.ob.clear() - self.run_cmd(create_service, [ - '--name', uuid.uuid4().hex, - '--type', uuid.uuid4().hex]) - obj_id = self.ob.read_lines()[0] - self.ob.clear() - return obj_id - - def _create_role(self): - self.ob.clear() - self.run_cmd(create_role, [ - '--name', uuid.uuid4().hex]) - obj_id = self.ob.read_lines()[0] - self.ob.clear() - return obj_id - - def _create_endpoint_template(self, service_id): - self.ob.clear() - self.run_cmd(create_endpoint_template, [ - '--region', uuid.uuid4().hex, - '--service-id', service_id, - '--public-url', 'http://%s' % (uuid.uuid4().hex), - '--admin-url', 'http://%s' % (uuid.uuid4().hex), - '--internal-url', 'http://%s' % (uuid.uuid4().hex)]) - obj_id = self.ob.read_lines()[0] - self.ob.clear() - return obj_id - - def _map_endpoint(self, endpoint_template_id, tenant_id): - self.ob.clear() - self.run_cmd(map_endpoint, [ - '--endpoint-template-id', endpoint_template_id, - '--tenant-id', tenant_id]) - self.ob.clear() - - -class TestCommon(unittest.TestCase): - def test_enable_disable(self): - """$ manage [command] --enable --disable""" - args = argparse.Namespace(enable=True, disable=True) - with self.assertRaises(SystemExit): - cmd = base.BaseCommand() - cmd.true_or_false(args, 'enable', 'disable') - - "Unable to apply both: --enable and --disable" - - def test_enable(self): - """$ manage [command] --enable""" - args = argparse.Namespace(enable=True, disable=False) - cmd = base.BaseCommand() - self.assertTrue(cmd.true_or_false(args, 'enable', 'disable')) - - def test_disable(self): - """$ manage [command] --disable""" - args = argparse.Namespace(enable=False, disable=True) - cmd = base.BaseCommand() - self.assertFalse(cmd.true_or_false(args, 'enable', 'disable')) - - def test_no_args(self): - """$ manage [command]""" - args = argparse.Namespace(enable=False, disable=False) - cmd = base.BaseCommand() - self.assertFalse(cmd.true_or_false(args, 'enable', 'disable')) - - -class TestVersionCommand(CommandTestCase): - """Tests for ./bin/keystone-manage version""" - API_VERSION = '2.0 beta' - IMPLEMENTATION_VERSION = '2012.1-dev' - DATABASE_VERSION = 'not under version control' - - def test_api_version(self): - v = version.Command.get_api_version() - self.assertEqual(v, self.API_VERSION) - - def test_implementation_version(self): - v = version.Command.get_implementation_version() - self.assertEqual(v, self.IMPLEMENTATION_VERSION) - - def test_all_version_responses(self): - self.run_cmd(version, [], use_managers=False) - lines = self.ob.read_lines() - self.assertEqual(len(lines), 3, lines) - self.assertIn(self.API_VERSION, lines[0]) - self.assertIn(self.IMPLEMENTATION_VERSION, lines[1]) - self.assertIn(self.DATABASE_VERSION, lines[2]) - - def test_api_version_arg(self): - self.run_cmd(version, ['--api'], use_managers=False) - lines = self.ob.read_lines() - self.assertEqual(len(lines), 1, lines) - self.assertIn(self.API_VERSION, lines[0]) - - def test_implementation_version_arg(self): - self.run_cmd(version, ['--implementation'], use_managers=False) - lines = self.ob.read_lines() - self.assertEqual(len(lines), 1, lines) - self.assertIn(self.IMPLEMENTATION_VERSION, lines[0]) - - def test_database_version_arg(self): - self.run_cmd(version, ['--database'], use_managers=False) - lines = self.ob.read_lines() - self.assertEqual(len(lines), 1, lines) - self.assertIn(self.DATABASE_VERSION, lines[0]) - - -class TestCreateUserCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(create_user) - - def test_create_user_min_fields(self): - name = uuid.uuid4().hex - password = uuid.uuid4().hex - self.run_cmd(create_user, [ - '--name', name, - '--password', password]) - user_id = self.ob.read_lines()[0] - self.assertEqual(len(user_id), 32) - - self.ob.clear() - - self.run_cmd(list_users) - output = self.ob.read() - self.assertIn(user_id, output) - self.assertIn(name, output) - self.assertNotIn(password, output) - - def test_create_user_all_fields(self): - user_id = uuid.uuid4().hex - name = uuid.uuid4().hex - password = uuid.uuid4().hex - email = uuid.uuid4().hex - self.run_cmd(create_user, [ - '--id', user_id, - '--name', name, - '--password', password, - '--email', email, - '--disable']) - output = self.ob.read_lines() - self.assertEquals(user_id, output[0]) - - self.ob.clear() - - self.run_cmd(list_users) - self.assertTableContainsRow(self.ob.read(), [user_id, name, email, - str(None), str(False)]) - - -class TestListUsersCommand(CommandTestCase): - def test_no_args(self): - self.run_cmd(list_users) - lines = self.ob.read_lines() - row = [col.strip() for col in lines[1].split('|') if col.strip()] - self.assertEquals('ID', row[0]) - self.assertEquals('Name', row[1]) - self.assertEquals('Email', row[2]) - self.assertEquals('Default Tenant ID', row[3]) - self.assertEquals('Enabled', row[4]) - - -class TestUpdateUserCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(update_user) - - def test_invalid_id(self): - with self.assertRaises(KeyError): - self.run_cmd(update_user, [ - '--where-id', uuid.uuid4().hex]) - - def test_create_update_list(self): - user_id = uuid.uuid4().hex - self.run_cmd(create_user, [ - '--id', user_id, - '--name', uuid.uuid4().hex, - '--password', uuid.uuid4().hex, - '--email', uuid.uuid4().hex]) - - name = uuid.uuid4().hex - password = uuid.uuid4().hex - email = uuid.uuid4().hex - self.run_cmd(update_user, [ - '--where-id', user_id, - '--name', name, - '--password', password, - '--email', email, - '--disable']) - - self.ob.clear() - - self.run_cmd(list_users) - output = self.ob.read() - self.assertIn(user_id, output) - - lines = self.ob.read_lines() - row = [row for row in lines if user_id in row][0] - row = [col for col in row.split() if col.strip() != '|'] - self.assertEquals(user_id, row[0]) - self.assertEquals(name, row[1]) - self.assertEquals(email, row[2]) - self.assertEquals(str(None), row[3]) - self.assertEquals(str(False), row[4]) - - -class TestDeleteUserCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(delete_user) - - def test_invalid_id(self): - with self.assertRaises(KeyError): - self.run_cmd(delete_user, [ - '--where-id', uuid.uuid4().hex]) - - def test_create_delete_list(self): - user_id = uuid.uuid4().hex - self.run_cmd(create_user, [ - '--id', user_id, - '--name', uuid.uuid4().hex, - '--password', uuid.uuid4().hex, - '--email', uuid.uuid4().hex]) - - self.run_cmd(delete_user, [ - '--where-id', user_id]) - - self.ob.clear() - - self.run_cmd(list_users) - self.assertNotIn(user_id, self.ob.read()) - - -class TestCreateTenantCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(create_tenant) - - def test_create_enabled_tenant(self): - name = uuid.uuid4().hex - self.run_cmd(create_tenant, [ - '--name', name]) - tenant_id = self.ob.read_lines()[0] - self.assertEqual(len(tenant_id), 32) - - self.ob.clear() - - self.run_cmd(list_tenants) - output = self.ob.read() - self.assertIn(tenant_id, output) - self.assertIn(name, output) - - def test_create_disabled_tenant(self): - name = uuid.uuid4().hex - self.run_cmd(create_tenant, [ - '--name', name, - '--disabled']) - tenant_id = self.ob.read_lines()[0] - self.assertEqual(len(tenant_id), 32) - - self.ob.clear() - - self.run_cmd(list_tenants) - output = self.ob.read() - self.assertIn(tenant_id, output) - - lines = self.ob.read_lines() - row = [row for row in lines if tenant_id in row][0] - row = [col for col in row.split() if col.strip() != '|'] - self.assertEquals(tenant_id, row[0]) - self.assertEquals(name, row[1]) - self.assertEquals(str(False), row[2]) - - -class TestListTenantsCommand(CommandTestCase): - def test_no_args(self): - self.run_cmd(list_tenants) - lines = self.ob.read_lines() - row = [col.strip() for col in lines[1].split('|') if col.strip()] - self.assertEquals('ID', row[0]) - self.assertEquals('Name', row[1]) - self.assertEquals('Enabled', row[2]) - - -class TestUpdateTenantCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(update_tenant) - - def test_invalid_id(self): - with self.assertRaises(KeyError): - self.run_cmd(update_tenant, [ - '--where-id', uuid.uuid4().hex]) - - def test_create_update_list(self): - tenant_id = uuid.uuid4().hex - self.run_cmd(create_tenant, [ - '--id', tenant_id, - '--name', uuid.uuid4().hex]) - - name = uuid.uuid4().hex - self.run_cmd(update_tenant, [ - '--where-id', tenant_id, - '--name', name, - '--disable']) - - self.ob.clear() - - self.run_cmd(list_tenants) - output = self.ob.read() - self.assertIn(tenant_id, output) - - lines = self.ob.read_lines() - row = [row for row in lines if tenant_id in row][0] - row = [col for col in row.split() if col.strip() != '|'] - self.assertEquals(tenant_id, row[0]) - self.assertEquals(name, row[1]) - self.assertEquals(str(False), row[2]) - - -class TestDeleteTenantCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(delete_tenant) - - def test_invalid_id(self): - with self.assertRaises(KeyError): - self.run_cmd(delete_tenant, [ - '--where-id', uuid.uuid4().hex]) - - def test_create_delete_list(self): - tenant_id = uuid.uuid4().hex - self.run_cmd(create_tenant, [ - '--id', tenant_id, - '--name', uuid.uuid4().hex]) - - self.run_cmd(delete_tenant, [ - '--where-id', tenant_id]) - - self.ob.clear() - - self.run_cmd(list_tenants) - self.assertNotIn(tenant_id, self.ob.read()) - - -class TestCreateRoleCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(create_role) - - def test_create_role(self): - name = uuid.uuid4().hex - self.run_cmd(create_role, [ - '--name', name]) - role_id = self.ob.read_lines()[0] - self.assertTrue(int(role_id)) - - self.ob.clear() - - self.run_cmd(list_roles) - output = self.ob.read() - self.assertIn(role_id, output) - self.assertIn(name, output) - - def test_create_role_with_description(self): - name = uuid.uuid4().hex - description = uuid.uuid4().hex - self.run_cmd(create_role, [ - '--name', name, - '--description', description]) - role_id = self.ob.read_lines()[0] - self.assertTrue(int(role_id)) - - self.ob.clear() - - self.run_cmd(list_roles) - output = self.ob.read() - self.assertIn(role_id, output) - - lines = self.ob.read_lines() - row = [row for row in lines if role_id in row][0] - row = [col for col in row.split() if col.strip() != '|'] - self.assertEquals(role_id, row[0]) - self.assertEquals(name, row[1]) - self.assertEquals(str(None), row[2]) - self.assertEquals(description, row[3]) - - def test_create_role_owned_by_service(self): - self.run_cmd(create_service, [ - '--name', uuid.uuid4().hex, - '--type', uuid.uuid4().hex]) - service_id = self.ob.read_lines()[0] - - name = uuid.uuid4().hex - self.run_cmd(create_role, [ - '--name', name, - '--service-id', service_id]) - role_id = self.ob.read_lines()[0] - - self.ob.clear() - - self.run_cmd(list_roles) - output = self.ob.read() - self.assertIn(role_id, output) - - lines = self.ob.read_lines() - row = [row for row in lines if role_id in row][0] - row = [col for col in row.split() if col.strip() != '|'] - self.assertEquals(role_id, row[0]) - self.assertEquals(name, row[1]) - self.assertEquals(service_id, row[2]) - self.assertEquals(str(None), row[3]) - - -class TestUpdateRoleCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(update_role) - - def test_update_role(self): - old_name = uuid.uuid4().hex - old_description = uuid.uuid4().hex - old_service_id = self._create_service() - self.run_cmd(create_role, [ - '--name', old_name, - '--description', old_description, - '--service-id', old_service_id]) - role_id = self.ob.read_lines()[0] - - # update the role - name = uuid.uuid4().hex - description = uuid.uuid4().hex - service_id = self._create_service() - self.run_cmd(update_role, [ - '--where-id', role_id, - '--name', name, - '--description', description, - '--service-id', service_id]) - - self.ob.clear() - - self.run_cmd(list_roles) - self.assertTableContainsRow(self.ob.read(), [role_id, name, - service_id, description]) - - -class TestListRolesCommand(CommandTestCase): - def test_no_args(self): - self.run_cmd(list_roles) - lines = self.ob.read_lines() - row = [col.strip() for col in lines[1].split('|') if col.strip()] - self.assertEquals('ID', row[0]) - self.assertEquals('Name', row[1]) - self.assertEquals('Service ID', row[2]) - self.assertEquals('Description', row[3]) - - -class TestDeleteRoleCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(delete_role) - - def test_invalid_id(self): - with self.assertRaises(KeyError): - self.run_cmd(delete_role, [ - '--where-id', uuid.uuid4().hex]) - - def test_delete_role(self): - role_id = self._create_role() - - # delete it - self.run_cmd(delete_role, [ - '--where-id', role_id]) - - self.ob.clear() - - # ensure it's not returned - self.run_cmd(list_roles) - output = self.ob.read() - self.assertNotIn(role_id, output) - - -class TestListRoleGrants(CommandTestCase): - def test_no_args(self): - self.run_cmd(list_role_grants) - lines = self.ob.read_lines() - row = [col.strip() for col in lines[1].split('|') if col.strip()] - self.assertEquals(['Role ID', 'User ID', 'Tenant ID', 'Global'], row) - - -class TestGrantRoleCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(grant_role) - - def test_invalid_ids(self): - with self.assertRaises(KeyError): - self.run_cmd(grant_role, [ - '--user-id', uuid.uuid4().hex, - '--role-id', uuid.uuid4().hex]) - - def test_grant_global_role(self): - user_id = self._create_user() - role_id = self._create_role() - - self.run_cmd(grant_role, [ - '--user-id', user_id, - '--role-id', role_id]) - - # granting again should fail - # TODO(dolph): this should be an IntegrityError - with self.assertRaises(KeyError): - self.run_cmd(grant_role, [ - '--user-id', user_id, - '--role-id', role_id]) - - self.ob.clear() - - self.run_cmd(list_role_grants, [ - '--where-user-id', user_id, - '--where-role-id', role_id, - '--where-global']) - self.assertTableContainsRow(self.ob.read(), [role_id, user_id, - str(None), str(True)]) - - def test_grant_tenant_role(self): - user_id = self._create_user() - role_id = self._create_role() - tenant_id = self._create_tenant() - - self.run_cmd(grant_role, [ - '--user-id', user_id, - '--role-id', role_id, - '--tenant-id', tenant_id]) - - # granting again should fail - # TODO(dolph): this should be an IntegrityError - with self.assertRaises(KeyError): - self.run_cmd(grant_role, [ - '--user-id', user_id, - '--role-id', role_id, - '--tenant-id', tenant_id]) - - self.ob.clear() - - self.run_cmd(list_role_grants, [ - '--where-user-id', user_id, - '--where-role-id', role_id, - '--where-tenant-id', tenant_id]) - self.assertTableContainsRow(self.ob.read(), [role_id, user_id, - tenant_id, str(False)]) - - -class TestRevokeRoleCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(revoke_role) - - def test_revoke_global_role(self): - user_id = self._create_user() - role_id = self._create_role() - - self.run_cmd(grant_role, [ - '--user-id', user_id, - '--role-id', role_id]) - - self.run_cmd(revoke_role, [ - '--user-id', user_id, - '--role-id', role_id]) - - self.ob.clear() - - self.run_cmd(list_role_grants, [ - '--where-user-id', user_id, - '--where-role-id', role_id, - '--where-global']) - with self.assertRaises(AssertionError): - self.assertTableContainsRow(self.ob.read(), [role_id, user_id, - str(None), str(True)]) - - def test_revoke_tenant_role(self): - user_id = self._create_user() - role_id = self._create_role() - tenant_id = self._create_tenant() - - self.run_cmd(grant_role, [ - '--user-id', user_id, - '--role-id', role_id, - '--tenant-id', tenant_id]) - - self.run_cmd(revoke_role, [ - '--user-id', user_id, - '--role-id', role_id, - '--tenant-id', tenant_id]) - - self.ob.clear() - - self.run_cmd(list_role_grants, [ - '--where-user-id', user_id, - '--where-role-id', role_id, - '--where-tenant-id', tenant_id]) - with self.assertRaises(AssertionError): - self.assertTableContainsRow(self.ob.read(), [role_id, user_id, - tenant_id, str(False)]) - - -class TestCreateServiceCommand(CommandTestCase): - def test_create_service(self): - name = uuid.uuid4().hex - service_type = uuid.uuid4().hex - description = uuid.uuid4().hex - owner_id = self._create_user() - self.run_cmd(create_service, [ - '--name', name, - '--type', service_type, - '--description', description, - '--owner-id', owner_id]) - service_id = self.ob.read_lines()[0] - self.assertTrue(int(service_id)) - - self.ob.clear() - - self.run_cmd(list_services) - output = self.ob.read() - self.assertIn(service_id, output) - - lines = self.ob.read_lines() - row = [row for row in lines if service_id in row][0] - row = [col for col in row.split() if col.strip() != '|'] - self.assertEquals(service_id, row[0]) - self.assertEquals(name, row[1]) - self.assertEquals(service_type, row[2]) - self.assertEquals(owner_id, row[3]) - self.assertEquals(description, row[4]) - - -class TestUpdateServiceCommand(CommandTestCase): - def test_update_service(self): - old_name = uuid.uuid4().hex - old_type = uuid.uuid4().hex - old_description = uuid.uuid4().hex - old_owner_id = self._create_user() - self.run_cmd(create_service, [ - '--name', old_name, - '--type', old_type, - '--description', old_description, - '--owner-id', old_owner_id]) - service_id = self.ob.read_lines()[0] - - # update the service - name = uuid.uuid4().hex - service_type = uuid.uuid4().hex - description = uuid.uuid4().hex - owner_id = self._create_user() - self.run_cmd(update_service, [ - '--where-id', service_id, - '--name', name, - '--type', service_type, - '--description', description, - '--owner-id', owner_id]) - - self.ob.clear() - - self.run_cmd(list_services) - output = self.ob.read() - self.assertIn(service_id, output) - - lines = self.ob.read_lines() - row = [row for row in lines if service_id in row][0] - row = [col for col in row.split() if col.strip() != '|'] - self.assertEquals(service_id, row[0]) - self.assertEquals(name, row[1]) - self.assertEquals(service_type, row[2]) - self.assertEquals(owner_id, row[3]) - self.assertEquals(description, row[4]) - - -class TestListServicesCommand(CommandTestCase): - def test_no_args(self): - self.run_cmd(list_services) - lines = self.ob.read_lines() - row = [col.strip() for col in lines[1].split('|') if col.strip()] - self.assertEquals('ID', row[0]) - self.assertEquals('Name', row[1]) - self.assertEquals('Type', row[2]) - self.assertEquals('Owner ID', row[3]) - self.assertEquals('Description', row[4]) - - -class TestDeleteServiceCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(delete_service) - - def test_invalid_id(self): - with self.assertRaises(KeyError): - self.run_cmd(delete_service, [ - '--where-id', uuid.uuid4().hex]) - - def test_delete_service(self): - service_id = self._create_service() - - self.run_cmd(delete_service, [ - '--where-id', service_id]) - - self.ob.clear() - - self.run_cmd(list_services) - output = self.ob.read() - self.assertNotIn(service_id, output) - - -class TestCreateTokenCommand(CommandTestCase): - """Creates tokens and validates their attributes. - - This class has a known potential race condition, due to the expected - token expiration being 24 hours after token creation. If the - 'create_token' command runs immediately before the minute rolls over, - and the test class produces a timestamp for the subsequent minute, the - test will fail. - - """ - - @staticmethod - def _get_tomorrow_str(): - return (datetime.datetime.utcnow() + - datetime.timedelta(days=1)).strftime('%Y-%m-%dT%H:%M') - - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(create_token) - - def test_create_unscoped_token(self): - user_id = self._create_user() - self.run_cmd(create_token, [ - '--user-id', user_id]) - tomorrow = TestCreateTokenCommand._get_tomorrow_str() - token_id = self.ob.read_lines()[0] - self.assertEqual(len(token_id), 32) - - self.ob.clear() - - self.run_cmd(list_tokens) - self.assertTableContainsRow(self.ob.read(), [token_id, user_id, - str(None), tomorrow]) - - def test_create_scoped_token(self): - user_id = self._create_user() - tenant_id = self._create_tenant() - self.run_cmd(create_token, [ - '--user-id', user_id, - '--tenant-id', tenant_id]) - tomorrow = TestCreateTokenCommand._get_tomorrow_str() - token_id = self.ob.read_lines()[0] - self.assertEqual(len(token_id), 32) - - self.ob.clear() - - self.run_cmd(list_tokens) - self.assertTableContainsRow(self.ob.read(), [token_id, user_id, - tenant_id, tomorrow]) - - def test_create_expired_token(self): - user_id = self._create_user() - expiration = '1999-12-31T23:59' - self.run_cmd(create_token, [ - '--user-id', user_id, - '--expires', expiration]) - token_id = self.ob.read_lines()[0] - self.assertEqual(len(token_id), 32) - - self.ob.clear() - - self.run_cmd(list_tokens) - self.assertTableContainsRow(self.ob.read(), [token_id, user_id, - str(None), expiration]) - - def test_create_specific_token_id(self): - token_id = uuid.uuid4().hex - user_id = self._create_user() - self.run_cmd(create_token, [ - '--id', token_id, - '--user-id', user_id]) - tomorrow = TestCreateTokenCommand._get_tomorrow_str() - self.assertEqual(token_id, self.ob.read_lines()[0]) - - self.ob.clear() - - self.run_cmd(list_tokens) - self.assertTableContainsRow(self.ob.read(), [token_id, user_id, - str(None), tomorrow]) - - -class TestUpdateTokenCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(update_token) - - def test_update_token(self): - token_id = self._create_token(self._create_user()) - - user_id = self._create_user() - tenant_id = self._create_tenant() - expiration = '1999-12-31T23:59' - self.run_cmd(update_token, [ - '--where-id', token_id, - '--user-id', user_id, - '--tenant-id', tenant_id, - '--expires', expiration]) - - self.ob.clear() - - self.run_cmd(list_tokens) - self.assertTableContainsRow(self.ob.read(), [token_id, user_id, - tenant_id, expiration]) - - -class TestListTokensCommand(CommandTestCase): - def test_no_args(self): - self.run_cmd(list_tokens) - lines = self.ob.read_lines() - row = [col.strip() for col in lines[1].split('|') if col.strip()] - self.assertEquals(['ID', 'User ID', 'Tenant ID', 'Expiration'], - row) - - -class TestDeleteTokenCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(delete_token) - - def test_delete_token(self): - token_id = self._create_token(self._create_user()) - - self.run_cmd(delete_token, [ - '--where-id', token_id]) - - self.ob.clear() - - # ensure it's not returned - self.run_cmd(list_tokens) - output = self.ob.read() - self.assertNotIn(token_id, output) - - -class TestCreateCredentialCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(create_credential) - - def test_create_credential(self): - user_id = self._create_user() - cred_type = uuid.uuid4().hex - key = uuid.uuid4().hex - secret = uuid.uuid4().hex - self.run_cmd(create_credential, [ - '--user-id', user_id, - '--type', cred_type, - '--key', key, - '--secret', secret]) - credential_id = self.ob.read_lines()[0] - self.assertTrue(int(credential_id)) - - self.ob.clear() - - self.run_cmd(list_credentials) - self.assertTableContainsRow(self.ob.read(), [credential_id, - user_id, str(None), cred_type, key, secret]) - - def test_create_credential_with_tenant(self): - user_id = self._create_user() - tenant_id = self._create_tenant() - cred_type = uuid.uuid4().hex - key = uuid.uuid4().hex - secret = uuid.uuid4().hex - self.run_cmd(create_credential, [ - '--user-id', user_id, - '--tenant-id', tenant_id, - '--type', cred_type, - '--key', key, - '--secret', secret]) - credential_id = self.ob.read_lines()[0] - self.assertTrue(int(credential_id)) - - self.ob.clear() - - self.run_cmd(list_credentials) - self.assertTableContainsRow(self.ob.read(), [credential_id, - user_id, tenant_id, cred_type, key, secret]) - - -class TestUpdateCredentialCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(update_credential) - - def test_update_credential(self): - credential_id = self._create_credential(self._create_user()) - - user_id = self._create_user() - tenant_id = self._create_tenant() - cred_type = uuid.uuid4().hex - key = uuid.uuid4().hex - secret = uuid.uuid4().hex - self.run_cmd(update_credential, [ - '--where-id', credential_id, - '--user-id', user_id, - '--tenant-id', tenant_id, - '--type', cred_type, - '--key', key, - '--secret', secret]) - - self.ob.clear() - - self.run_cmd(list_credentials) - self.assertTableContainsRow(self.ob.read(), [credential_id, - user_id, tenant_id, cred_type, key, secret]) - - -class TestListCredentialsCommand(CommandTestCase): - def test_no_args(self): - self.run_cmd(list_credentials) - lines = self.ob.read_lines() - row = [col.strip() for col in lines[1].split('|') if col.strip()] - self.assertEquals(['ID', 'User ID', 'Tenant ID', 'Type', 'Key', - 'Secret'], row) - - -class TestDeleteCredentialCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(delete_credential) - - def test_delete_credential(self): - credential_id = self._create_credential(self._create_user()) - - self.run_cmd(delete_credential, [ - '--where-id', credential_id]) - - self.ob.clear() - - # ensure it's not returned - self.run_cmd(list_credentials) - output = self.ob.read() - self.assertNotIn(credential_id, output) - - -class TestCreateEndpointTemplateCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(create_endpoint_template) - - def test_create_global_endpoint_template(self): - region = uuid.uuid4().hex - service_id = self._create_service() - public_url = 'http://%s' % (uuid.uuid4().hex) - admin_url = 'http://%s' % (uuid.uuid4().hex) - internal_url = 'http://%s' % (uuid.uuid4().hex) - self.run_cmd(create_endpoint_template, [ - '--region', region, - '--service-id', service_id, - '--public-url', public_url, - '--admin-url', admin_url, - '--internal-url', internal_url, - '--global']) - endpoint_template_id = self.ob.read_lines()[0] - self.assertTrue(int(endpoint_template_id)) - - self.ob.clear() - - self.run_cmd(list_endpoint_templates) - self.assertTableContainsRow(self.ob.read(), [endpoint_template_id, - service_id, region, str(True), str(True), public_url, admin_url, - internal_url]) - - -class TestUpdateEndpointTemplateCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(update_endpoint_template) - - def test_update_endpoint_template(self): - endpoint_template_id = self._create_endpoint_template( - self._create_service()) - - region = uuid.uuid4().hex - service_id = self._create_service() - public_url = 'http://%s' % (uuid.uuid4().hex) - admin_url = 'http://%s' % (uuid.uuid4().hex) - internal_url = 'http://%s' % (uuid.uuid4().hex) - - self.run_cmd(update_endpoint_template, [ - '--where-id', endpoint_template_id, - '--region', region, - '--service-id', service_id, - '--public-url', public_url, - '--admin-url', admin_url, - '--internal-url', internal_url, - '--global', - '--disable']) - - self.ob.clear() - - self.run_cmd(list_endpoint_templates) - self.assertTableContainsRow(self.ob.read(), [endpoint_template_id, - service_id, region, str(False), str(True), public_url, admin_url, - internal_url]) - - -class TestListEndpointTemplatesCommand(CommandTestCase): - def test_no_args(self): - self.run_cmd(list_endpoint_templates) - lines = self.ob.read_lines() - row = [col.strip() for col in lines[1].split('|') if col.strip()] - self.assertEquals(['ID', 'Service ID', 'Region', 'Enabled', 'Global', - 'Public URL', 'Admin URL', 'Internal URL'], row) - - -class TestDeleteEndpointTemplateCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(delete_endpoint_template) - - def test_delete_endpoint_template(self): - endpoint_template_id = self._create_endpoint_template( - self._create_service()) - - self.run_cmd(delete_endpoint_template, [ - '--where-id', endpoint_template_id]) - - self.ob.clear() - - # ensure it's not returned - self.run_cmd(list_endpoint_templates) - output = self.ob.read() - self.assertNotIn(endpoint_template_id, output) - - -class TestCreateEndpointCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(map_endpoint) - - def test_create_global_endpoint(self): - tenant_id = self._create_tenant() - endpoint_template_id = self._create_endpoint_template( - self._create_service()) - self.run_cmd(map_endpoint, [ - '--endpoint-template-id', endpoint_template_id, - '--tenant-id', tenant_id]) - - self.ob.clear() - - self.run_cmd(list_endpoints) - self.assertTableContainsRow(self.ob.read(), [endpoint_template_id, - tenant_id]) - - -class TestListEndpointsCommand(CommandTestCase): - def test_no_args(self): - self.run_cmd(list_endpoints) - lines = self.ob.read_lines() - row = [col.strip() for col in lines[1].split('|') if col.strip()] - self.assertEquals(['Endpoint Template ID', 'Tenant ID'], row) - - -class TestDeleteEndpointCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(unmap_endpoint) - - def test_delete_endpoint(self): - tenant_id = self._create_tenant() - endpoint_template_id = self._create_endpoint_template( - self._create_service()) - self._map_endpoint(endpoint_template_id, tenant_id) - - self.run_cmd(unmap_endpoint, [ - '--tenant-id', tenant_id, - '--endpoint-template-id', endpoint_template_id]) - - self.ob.clear() - - # ensure it's not returned - self.run_cmd(list_endpoints) - output = self.ob.read() - self.assertNotIn(" | ".join([endpoint_template_id, tenant_id]), output) diff --git a/keystone/test/unit/test_commands_v1.py b/keystone/test/unit/test_commands_v1.py deleted file mode 100644 index abd722f638..0000000000 --- a/keystone/test/unit/test_commands_v1.py +++ /dev/null @@ -1,77 +0,0 @@ -import datetime -import unittest2 as unittest - -from keystone import backends -import keystone.backends.sqlalchemy as db -import keystone.backends.api as db_api -import keystone.manage.api as manage_api -from keystone import utils - - -class TestCommandsV1(unittest.TestCase): - """Tests for keystone-manage version 1 commands""" - - def __init__(self, *args, **kwargs): - super(TestCommandsV1, self).__init__(*args, **kwargs) - self.options = { - 'backends': 'keystone.backends.sqlalchemy', - 'keystone.backends.sqlalchemy': { - # in-memory db - 'sql_connection': 'sqlite://', - 'backend_entities': - "['UserRoleAssociation', 'Endpoints', 'Role', 'Tenant', " - "'Tenant', 'User', 'Credentials', 'EndpointTemplates', " - "'Token', 'Service']", - }, - } - # Need to populate the CONF module with these options - utils.set_configuration(self.options) - - def setUp(self): - self.clear_all_data() - manage_api.add_tenant('Test tenant') - self.user = manage_api.add_user('Test user', 'Test password', - 'Test tenant') - - def tearDown(self): - self.clear_all_data() - - @staticmethod - def clear_all_data(): - """ - Purges the database of all data - """ - db.unregister_models() - reload(db) - backends.configure_backends() - - def test_service_list(self): - result = manage_api.list_services() - self.assertEqual(result, []) - - def test_add_service(self): - data = { - 'name': 'Test name', - 'type': 'Test type', - 'desc': 'Test description', - 'owner_id': self.user.id, - } - manage_api.add_service(**data) - result = manage_api.list_services() - self.assertEqual(result, [['1', data['name'], data['type'], - data['owner_id'], data['desc']]]) - - def test_add_token(self): - data = { - 'token': 'Test token', - 'user': 'Test user', - 'tenant': 'Test tenant', - 'expires': '20120104T18:30', - } - manage_api.add_token(**data) - result = manage_api.list_tokens() - user = db_api.USER.get_by_name(data['user']) - tenant = db_api.TENANT.get_by_name(data['tenant']) - self.assertEqual(result, [[data['token'], user['id'], - datetime.datetime(2012, 1, 4, 18, 30), - tenant['id']]]) diff --git a/keystone/test/unit/test_config.py b/keystone/test/unit/test_config.py deleted file mode 100644 index b1827055de..0000000000 --- a/keystone/test/unit/test_config.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (c) 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. - -import ast -import unittest2 as unittest - -from keystone import config -from keystone import utils - -CONF = config.CONF - - -class ConfigTestCase(unittest.TestCase): - """ - Base class to test keystone/config.py - """ - def __init__(self, *args, **kwargs): - super(ConfigTestCase, self).__init__(*args, **kwargs) - - def setUp(self): - pass - - def tearDown(self): - pass - - def test_old_config_syntax(self): - options = { - 'verbose': True, - 'debug': False, - 'backends': "keystone.backends.sqlalchemy", - 'keystone.backends.sqlalchemy': { - # in-memory db - 'sql_connection': 'sqlite://', - 'backend_entities': - "['UserRoleAssociation', 'Endpoints', 'Role', 'Tenant', " - "'Tenant', 'User', 'Credentials', 'EndpointTemplates', " - "'Token', 'Service']", - }, - 'extensions': 'osksadm, oskscatalog, hpidm', - 'keystone-admin-role': 'Admin', - 'keystone-service-admin-role': 'KeystoneServiceAdmin', - 'hash-password': 'True', - } - utils.set_configuration(options) - self.assertTrue(CONF.verbose) - self.assertFalse(CONF.debug) - self.assertIn('hpidm', [ext.strip() for ext in CONF.extensions]) - self.assertIn('keystone.backends.sqlalchemy', CONF.backends) - self.assertTrue(CONF.hash_password) - self.assertEquals(CONF['keystone.backends.sqlalchemy'].sql_connection, - 'sqlite://') - self.assertIsInstance(ast.literal_eval( - CONF['keystone.backends.sqlalchemy'].backend_entities), - list) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_controller_version.py b/keystone/test/unit/test_controller_version.py deleted file mode 100644 index eccf629d03..0000000000 --- a/keystone/test/unit/test_controller_version.py +++ /dev/null @@ -1,139 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 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. - -import json -import logging -from lxml import etree -import unittest2 as unittest -from webob import Request - -from keystone.controllers.version import VersionController - -LOGGER = logging.getLogger(__name__) - - -class TestVersionController(unittest.TestCase): - def setUp(self): - self.controller = VersionController() - - def _default_version(self, file=None): - """ Verify default response for versions is JSON """ - if file is None: - file = 'admin/version' - req = Request.blank('/') - req.environ = {} - response = self.controller.get_version_info(req, file=file) - self.assertEqual(response.content_type, 'application/json') - data = json.loads(response.body) - self.assertIsNotNone(data) - - def _json_version(self, file=None): - """ Verify JSON response for versions - - Checks that JSON is returned when Accept is set to application/json. - Also checks that verions and version exist and that - values are as expected - - """ - if file is None: - file = 'admin/version' - req = Request.blank('/') - req.headers['Accept'] = 'application/json' - req.environ = {} - response = self.controller.get_version_info(req, file=file) - self.assertEqual(response.content_type, 'application/json') - data = json.loads(response.body) - self.assertIn("versions", data) - versions = data['versions'] - self.assertIn("values", versions) - values = versions['values'] - self.assertIsInstance(values, list) - for version in values: - for item in version: - self.assertIn(item, ["id", "status", "updated", "links", - "media-types"]) - - def _xml_version(self, file=None): - """ Verify XML response for versions - - Checks that XML is returned when Accept is set to application/xml. - Also checks that verions and version tags exist and that - attributes are as expected - - """ - if file is None: - file = 'admin/version' - req = Request.blank('/') - req.headers['Accept'] = 'application/xml' - req.environ = {} - response = self.controller.get_version_info(req, file=file) - self.assertEqual(response.content_type, 'application/xml') - data = etree.fromstring(response.body) - self.assertEqual(data.tag, - '{http://docs.openstack.org/common/api/v2.0}versions') - for version in data: - self.assertEqual(version.tag, - '{http://docs.openstack.org/common/api/v2.0}version') - for attribute in version.attrib: - self.assertIn(attribute, ["id", "status", "updated", "links", - "media-types"]) - - def _atom_version(self, file=None): - """ Verify ATOM response for versions - - Checks that ATOM XML is returned when Accept is set to - aapplication/atom+xml. - Also checks that verions and version tags exist and that - attributes are as expected - - """ - if file is None: - file = 'admin/version' - req = Request.blank('/') - req.headers['Accept'] = 'application/atom+xml' - req.environ = {} - response = self.controller.get_version_info(req, file=file) - self.assertEqual(response.content_type, 'application/atom+xml') - data = etree.fromstring(response.body) - self.assertEqual(data.tag, - '{http://www.w3.org/2005/Atom}feed') - - def test_default_version_admin(self): - self._default_version(file='admin/version') - - def test_default_version_service(self): - self._default_version(file='service/version') - - def test_xml_version_admin(self): - self._xml_version(file='admin/version') - - def test_xml_version_service(self): - self._xml_version(file='service/version') - - def test_json_version_admin(self): - self._json_version(file='admin/version') - - def test_json_version_service(self): - self._json_version(file='service/version') - - def test_atom_version_admin(self): - self._atom_version(file='admin/version') - - def test_atom_version_service(self): - self._atom_version(file='service/version') - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_d5_compat.py b/keystone/test/unit/test_d5_compat.py deleted file mode 100644 index 40d0e997bf..0000000000 --- a/keystone/test/unit/test_d5_compat.py +++ /dev/null @@ -1,174 +0,0 @@ -import json -import unittest2 as unittest -from keystone.frontends import d5_compat -import keystone.logic.types.fault as fault - - -class TestD5Auth(unittest.TestCase): - """Test to make sure Keystone honors the 'unofficial' D5 API contract. - - The main differences were: - - POST /v2.0/tokens without the "auth" wrapper - - POST /v2.0/tokens with tenantId in the passwordCredentials object - (instead of the auth wrapper) - - Response for validate token was wrapped in "auth" - - TODO(zns): deprecate this once we move to the next version of the API - """ - - pwd_xml = '\ - ' - - def test_pwd_cred_marshall(self): - creds = d5_compat.D5AuthWithPasswordCredentials.from_xml(self.pwd_xml) - self.assertEqual(creds.password, "secret") - self.assertEqual(creds.username, "disabled") - - def test_pwd_creds_from_json(self): - data = json.dumps({"passwordCredentials": - {"username": "foo", "password": "bar"}}) - creds = d5_compat.D5AuthWithPasswordCredentials.from_json(data) - self.assertEqual(creds.username, "foo") - self.assertEqual(creds.password, "bar") - self.assertIsNone(creds.tenant_id) - self.assertIsNone(creds.tenant_name) - - def test_pwd_creds_with_tenant_name_from_json(self): - data = json.dumps({"passwordCredentials": - {"tenantName": "blaa", "username": "foo", - "password": "bar"}}) - creds = d5_compat.D5AuthWithPasswordCredentials.from_json(data) - self.assertEqual(creds.username, "foo") - self.assertEqual(creds.password, "bar") - self.assertIsNone(creds.tenant_id) - self.assertEqual(creds.tenant_name, "blaa") - - def test_pwd_creds_with_tenant_id_from_json(self): - data = json.dumps({"passwordCredentials": - {"tenantId": "blaa", "username": "foo", - "password": "bar"}}) - creds = d5_compat.D5AuthWithPasswordCredentials.from_json(data) - self.assertEqual(creds.username, "foo") - self.assertEqual(creds.password, "bar") - self.assertEqual(creds.tenant_id, "blaa") - self.assertIsNone(creds.tenant_name) - - def test_pwd_not_both_tenant_from_json(self): - data = json.dumps({"tenantId": "blaa", "tenantName": "aalb"}) - self.assertRaisesRegexp(fault.BadRequestFault, - "Expecting passwordCredentials", - d5_compat.D5AuthWithPasswordCredentials.from_json, - data) - - def test_pwd_no_creds_from_json(self): - data = json.dumps({"auth": {}}) - self.assertRaisesRegexp(fault.BadRequestFault, - "Expecting passwordCredentials", - d5_compat.D5AuthWithPasswordCredentials.from_json, - data) - - def test_pwd_invalid_attribute_from_json(self): - data = json.dumps({"passwordCredentials": {"foo": "bar"}}) - self.assertRaisesRegexp(fault.BadRequestFault, - "Invalid", - d5_compat.D5AuthWithPasswordCredentials.from_json, - data) - - def test_pwd_no_username_from_json(self): - data = json.dumps({"passwordCredentials": {}}) - self.assertRaisesRegexp(fault.BadRequestFault, - "Expecting passwordCredentials:username", - d5_compat.D5AuthWithPasswordCredentials.from_json, - data) - - def test_pwd_no_password_from_json(self): - data = json.dumps({"passwordCredentials": - {"username": "foo"}}) - self.assertRaisesRegexp(fault.BadRequestFault, - "Expecting passwordCredentials:password", - d5_compat.D5AuthWithPasswordCredentials.from_json, - data) - - def test_pwd_invalid_creds_attribute_from_json(self): - data = json.dumps({"passwordCredentials": {"bar": "foo"}}) - self.assertRaisesRegexp(fault.BadRequestFault, - "Invalid", - d5_compat.D5AuthWithPasswordCredentials.from_json, - data) - - def test_json_pwd_creds_from_D5(self): - D5_data = json.dumps({"passwordCredentials": - {"username": "foo", "password": "bar"}}) - diablo_data = json.dumps({"auth": {"passwordCredentials": - {"username": "foo", "password": "bar"}}}) - creds = d5_compat.D5AuthWithPasswordCredentials.from_json(D5_data) - diablo = creds.to_json() - self.assertEquals(diablo, diablo_data) - - def test_json_authdata_from_D5(self): - pass - - def test_json_validatedata_from_D5(self): - diablo_data = { - "access": { - "token": { - "expires": "2011-12-07T21:31:49.215675", - "id": "92c8962a-7e9b-40d1-83eb-a2f3b6eb45c3" - }, - "user": { - "id": "3", - "name": "admin", - "roles": [ - { - "id": "1", - "name": "Admin" - } - ], - "username": "admin" - } - } - } - D5_data = {"auth": { - "token": { - "expires": "2011-12-07T21:31:49.215675", - "id": "92c8962a-7e9b-40d1-83eb-a2f3b6eb45c3" - }, - "user": { - "roleRefs": [ - { - "id": "1", - "roleId": "Admin" - } - ], - "username": "admin" - } - } - } - creds = d5_compat.D5ValidateData.from_json(json.dumps(diablo_data)) - D5 = json.loads(creds.to_json()) - self.assertEquals(diablo_data['access'], D5['access'], - "D5 compat response must contain Diablo format") - self.assertEquals(D5_data['auth'], D5['auth'], - "D5 compat response must contain D5 format") - - def test_no_catalog_in_response(self): - minimal_response = { - "access": { - "token": { - "expires": "2011-12-07T21:31:49.215675", - "id": "92c8962a-7e9b-40d1-83eb-a2f3b6eb45c3" - }, - "user": { - "id": "3", - "name": "admin", - } - } - } - d5 = d5_compat.D5toDiabloAuthData(init_json=minimal_response) - self.assertTrue(d5.to_json()) - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_extensions.py b/keystone/test/unit/test_extensions.py deleted file mode 100644 index 2a72d570a2..0000000000 --- a/keystone/test/unit/test_extensions.py +++ /dev/null @@ -1,81 +0,0 @@ -# Copyright (c) 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. - -from xml.etree import ElementTree -import json -import unittest2 as unittest - -from keystone import config -from keystone.contrib.extensions import CONFIG_EXTENSION_PROPERTY -from keystone.contrib.extensions.admin import EXTENSION_ADMIN_PREFIX -from keystone.logic.extension_reader import ExtensionsReader -from keystone.logic.extension_reader import get_supported_extensions - -CONF = config.CONF - - -class TestExtensionReader(unittest.TestCase): - """Unit tests for ExtensionsReader.These - tests check whether the returned extensions vary - when they are configured differently.""" - - def setUp(self): - self.original_extensions = CONF.extensions - CONF.set_override(CONFIG_EXTENSION_PROPERTY, ["osksadm"]) - self.extensions_reader = ExtensionsReader(EXTENSION_ADMIN_PREFIX) - - def tearDown(self): - CONF.set_override(CONFIG_EXTENSION_PROPERTY, self.original_extensions) - - def test_extensions_reader_getsupportedoptions(self): - self.assertIn('osksadm', get_supported_extensions()) - - def test_extensions_with_only_osksadm_json(self): - r = self.extensions_reader.get_extensions().to_json() - content = json.loads(r) - self.assertIsNotNone(content['extensions']) - self.assertIsNotNone(content['extensions']['values']) - found_osksadm = False - found_oskscatalog = False - for value in content['extensions']['values']: - if value['extension']['alias'] == 'OS-KSADM': - found_osksadm = True - if value['extension']['alias'] == 'OS-KSCATALOG': - found_oskscatalog = True - self.assertTrue(found_osksadm, - "Missing OS-KSADM extension.") - self.assertFalse(found_oskscatalog, - "Non configured OS-KSCATALOG extension returned.") - - def test_extensions_with_only_osksadm_xml(self): - r = self.extensions_reader.get_extensions().to_xml() - content = ElementTree.XML(r) - extensions = content.findall( - "{http://docs.openstack.org/common/api/v1.0}extension") - found_osksadm = False - found_oskscatalog = False - for extension in extensions: - if extension.get("alias") == 'OS-KSADM': - found_osksadm = True - if extension.get("alias") == 'OS-KSCATALOG': - found_oskscatalog = True - self.assertTrue(found_osksadm, - "Missing OS-KSADM extension.") - self.assertFalse(found_oskscatalog, - "Non configured OS-KSCATALOG extension returned.") - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_logic_auth.py b/keystone/test/unit/test_logic_auth.py deleted file mode 100644 index 3b61debf3c..0000000000 --- a/keystone/test/unit/test_logic_auth.py +++ /dev/null @@ -1,132 +0,0 @@ -# Copyright (c) 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. - -import json -import datetime -from lxml import etree -import unittest2 as unittest - -import base -from keystone.logic.types import auth as logic_auth -from keystone import models -from keystone.test import utils as test_utils - - -class LogicTypesAuthTestCase(base.ServiceAPITest): - """ - Base class to test keystone/logic/types/auth.py - """ - def __init__(self, *args, **kwargs): - super(LogicTypesAuthTestCase, self).__init__(*args, **kwargs) - - self.user = models.User(id='u1', name='john', username='john') - self.role = models.Role(id=1, name='Admin') - self.user.rolegrants = models.Roles([self.role], links=None) - self.token = models.Token(id='abc123T', user_id=self.user.id, - expires=datetime.date(2000, 1, 31)) - self.tenant = models.Tenant(id='ten8', name='The Tenant') - self.token.tenant = self.tenant - self.base_urls = [models.EndpointTemplate( - id="1", - internal_url="http://127.0.0.1/v1/%tenant_id%", - public_url="http://internet.com/v1/%tenant_id%", - admin_url="http://private.net/v1/", - version_id="v1", - version_url="http://127.0.0.1/v1/", - version_info="http://127.0.0.1/", - region="RegionOne", - service_id="0" - ), - models.EndpointTemplate( - id="2", - internal_url="http://127.0.0.1/v1/%tenant_id%", - public_url="http://internet.com/v1/%tenant_id%", - service_id="0" - )] - self.url_types = ["internal", "public", "admin"] - - def test_AuthData_json_serialization(self): - auth = logic_auth.AuthData(self.token, self.user) - data = json.loads(auth.to_json()) - expected = { - 'access': { - 'token': { - 'expires': '2000-01-31', - 'tenants': [{ - 'id': 'ten8', - 'name': 'The Tenant' - }], - 'id': 'abc123T', - 'tenant': { - 'id': 'ten8', - 'name': 'The Tenant' - } - }, - 'user': { - 'id': 'u1', - 'roles': [{ - 'name': 'Admin', - 'id': '1' - }], - 'name': 'john' - } - } - } - self.assertDictEqual(data, expected) - - def test_AuthData_xml_serialization(self): - auth = logic_auth.AuthData(self.token, self.user) - xml_str = auth.to_xml() - expected = ('') - self.assertTrue(test_utils.XMLTools.xmlEqual(xml_str, expected)) - - def test_AuthData_json_catalog(self): - auth = logic_auth.AuthData(self.token, self.user, self.base_urls) - data = json.loads(auth.to_json()) - self.assertIn("access", data) - self.assertIn("serviceCatalog", data['access']) - catalog = data['access']['serviceCatalog'] - self.assertTrue(len(catalog) > 0) - endpoints = catalog[0]['endpoints'] - self.assertTrue(len(endpoints) > 1) - endpoint = endpoints[0] - self.assertIn("publicURL", endpoint) - self.assertIn("versionId", endpoint) - self.assertIn("tenantId", endpoint) - - endpoint = endpoints[1] - self.assertNotIn("versionId", endpoint) - - def test_AuthData_xml_catalog(self): - auth = logic_auth.AuthData(self.token, self.user, self.base_urls) - xml_str = auth.to_xml() - dom = etree.fromstring(xml_str) - xmlns = "http://docs.openstack.org/identity/api/v2.0" - catalog = dom.find("{%s}serviceCatalog" % xmlns) - service = catalog.find("{%s}service" % xmlns) - endpoint = service.find("{%s}endpoint" % xmlns) - self.assertIsNotNone("publicURL", endpoint.attrib) - self.assertIn("versionId", endpoint.attrib) - self.assertIn("tenantId", endpoint.attrib) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_migrations.py b/keystone/test/unit/test_migrations.py deleted file mode 100644 index 9284386aff..0000000000 --- a/keystone/test/unit/test_migrations.py +++ /dev/null @@ -1,136 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010-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 for database migrations. The test case runs a series of test cases to -ensure that migrations work properly both upgrading and downgrading, and that -no data loss occurs if possible. -""" - -import os -import unittest2 as unittest -import urlparse - -from migrate.versioning.repository import Repository -from sqlalchemy import * -from sqlalchemy.pool import NullPool - -import keystone.backends.sqlalchemy.migration as migration_api -from keystone.logic.types import fault - - -class TestMigrations(unittest.TestCase): - - """Test sqlalchemy-migrate migrations""" - - TEST_DATABASES = {'sqlite': 'sqlite:///migration.db'} - - REPOSITORY_PATH = os.path.abspath(os.path.join(os.path.abspath(__file__), - os.pardir, os.pardir, os.pardir, 'backends', - 'sqlalchemy', 'migrate_repo')) - REPOSITORY = Repository(REPOSITORY_PATH) - - def __init__(self, *args, **kwargs): - super(TestMigrations, self).__init__(*args, **kwargs) - - def setUp(self): - # Load test databases - self.engines = {} - for key, value in TestMigrations.TEST_DATABASES.items(): - self.engines[key] = create_engine(value, poolclass=NullPool) - - # We start each test case with a completely blank slate. - self._reset_databases() - - def tearDown(self): - # We destroy the test data store between each test case, - # and recreate it, which ensures that we have no side-effects - # from the tests - self._reset_databases() - - def _reset_databases(self): - for key, engine in self.engines.items(): - conn_string = TestMigrations.TEST_DATABASES[key] - conn_pieces = urlparse.urlparse(conn_string) - if conn_string.startswith('sqlite'): - # We can just delete the SQLite database, which is - # the easiest and cleanest solution - db_path = conn_pieces.path.strip('/') - if os.path.exists(db_path): - os.unlink(db_path) - # No need to recreate the SQLite DB. SQLite will - # create it for us if it's not there... - elif conn_string.startswith('mysql'): - # We can execute the MySQL client to destroy and re-create - # the MYSQL database, which is easier and less error-prone - # than using SQLAlchemy to do this via MetaData...trust me. - database = conn_pieces.path.strip('/') - loc_pieces = conn_pieces.netloc.split('@') - host = loc_pieces[1] - auth_pieces = loc_pieces[0].split(':') - user = auth_pieces[0] - password = "" - if len(auth_pieces) > 1: - if auth_pieces[1].strip(): - password = "-p%s" % auth_pieces[1] - sql = ("drop database if exists %(database)s; " - "create database %(database)s;") % locals() - cmd = ("mysql -u%(user)s %(password)s -h%(host)s " - "-e\"%(sql)s\"") % locals() - exitcode, out, err = execute(cmd) - self.assertEqual(0, exitcode) - - def test_walk_versions(self): - """ - Walks all version scripts for each tested database, ensuring - that there are no errors in the version scripts for each engine - """ - for key, engine in self.engines.items(): - self._walk_versions(TestMigrations.TEST_DATABASES[key]) - - def _walk_versions(self, sql_connection): - # Determine latest version script from the repo, then - # upgrade from 1 through to the latest, with no data - # in the databases. This just checks that the schema itself - # upgrades successfully. - - # Assert we are not under version control... - self.assertRaises(fault.DatabaseMigrationError, - migration_api.db_version, - sql_connection) - # Place the database under version control - print migration_api.version_control(sql_connection) - - cur_version = migration_api.db_version(sql_connection) - self.assertEqual(0, cur_version) - - for version in xrange(1, TestMigrations.REPOSITORY.latest + 1): - migration_api.upgrade(sql_connection, version) - cur_version = migration_api.db_version(sql_connection) - self.assertEqual(cur_version, version) - - # Now walk it back down to 0 from the latest, testing - # the downgrade paths. - for version in reversed( - xrange(0, TestMigrations.REPOSITORY.latest)): - migration_api.downgrade(sql_connection, version) - cur_version = migration_api.db_version(sql_connection) - self.assertEqual(cur_version, version) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_models.py b/keystone/test/unit/test_models.py deleted file mode 100644 index dd096aabce..0000000000 --- a/keystone/test/unit/test_models.py +++ /dev/null @@ -1,131 +0,0 @@ -import json -import unittest2 as unittest - -from keystone.models import AttrDict, Resource -from keystone.test import utils as testutils - - -class TestModels(unittest.TestCase): - '''Unit tests for keystone/models.py.''' - - def test_attrdict_class(self): - ad = AttrDict() - ad.id = 1 - self.assertEqual(ad.id, 1) - ad._sa_instance_state = AttrDict() - self.assertIsInstance(ad._sa_instance_state, AttrDict) - - def test_resource(self): - resource = Resource() - self.assertEquals(str(resource.__class__), - "", - "Resource should be of instance " - "class keystone.models.Resource but instead " - "was '%s'" % str(resource.__class__)) - self.assertIsInstance(resource, dict, "") - resource._sa_instance_state = AttrDict() - self.assertIsInstance(resource._sa_instance_state, AttrDict) - - def test_resource_respresentation(self): - resource = Resource(id=1, name="John") - self.assertIn(resource.__repr__(), [ - "", - ""]) - self.assertIn(resource.__str__(), [ - "{'resource': {'name': 'John', 'id': 1}}", - "{'resource': {'id': 1, 'name': 'John'}}"]) - self.assertEqual(resource['resource']['id'], 1) - - def test_resource_static_properties(self): - resource = Resource(id=1, name="the resource", blank=None) - self.assertEquals(resource.id, 1) - self.assertEquals(resource.name, "the resource") - self.assertRaises(AttributeError, getattr, resource, - 'some_bad_property') - - def test_resource_keys(self): - resource = Resource(id=1, name="the resource", blank=None) - self.assertEquals(resource.id, 1) - self.assertEquals(resource['id'], 1) - - self.assertTrue('id' in resource) - self.assertTrue(hasattr(resource, 'id')) - - resource['dynamic'] = '1' - self.assertEquals(resource['dynamic'], '1') - - self.assertTrue('dynamic' in resource) - - def test_resource_dynamic_properties(self): - resource = Resource(id=1, name="the resource", blank=None) - resource["dynamic"] = "test" - self.assertEquals(resource["dynamic"], "test") - self.assertEquals(resource["name"], "the resource") - - def test_resource_json_serialization(self): - resource = Resource(id=1, name="the resource", blank=None) - json_str = resource.to_json() - d1 = json.loads(json_str) - d2 = json.loads('{"resource": {"name": "the resource", "id": 1}}') - self.assertDictEqual(d1, d2) - - def test_resource_json_serialization_mapping(self): - resource = Resource(id=1, name="the resource", rolegrant_id=12) - json_str = resource.to_json(hints={"maps": {"refId": "rolegrant_id", - }}) - d1 = json.loads(json_str) - d2 = {"resource": {"name": "the resource", "id": 1, "refId": 12}} - self.assertDictEqual(d1, d2) - - def test_resource_json_serialization_types(self): - resource = Resource(id=1, name="the resource", bool=True, int=5) - json_str = resource.to_json(hints={"types": - [("bool", bool), ("int", int)]}) - d1 = json.loads(json_str) - d2 = {"resource": {"name": "the resource", "id": 1, "bool": True, - "int": 5}} - self.assertDictEqual(d1, d2) - - def test_resource_xml_serialization(self): - resource = Resource(id=1, name="the resource", blank=None) - xml_str = resource.to_xml() - self.assertTrue(testutils.XMLTools.xmlEqual(xml_str, - '')) - - def test_resource_xml_serialization_mapping(self): - resource = Resource(id=1, name="the resource", rolegrant_id=12) - xml_str = resource.to_xml(hints={"maps": {"refId": "rolegrant_id", }}) - self.assertTrue(testutils.XMLTools.xmlEqual(xml_str, - '')) - - def test_resource_xml_deserialization(self): - resource = Resource.from_xml('', - hints={ - "contract_attributes": ['id', 'name'], - "types": [("id", int)]}) - self.assertIsInstance(resource, Resource) - self.assertEquals(resource.id, 1) - self.assertEquals(resource.name, "the resource") - - def test_resource_json_deserialization(self): - resource = Resource.from_json('{"resource": {"name": "the resource", \ - "id": 1}}', - hints={ - "contract_attributes": ['id', 'name'], - "types": [("id", int)]}) - self.assertIsInstance(resource, Resource) - self.assertEquals(resource.id, 1) - self.assertEquals(resource.name, "the resource") - - def test_resource_inspection(self): - resource = Resource(id=1, name="the resource", blank=None) - self.assertFalse(resource.inspect()) - - def test_resource_validation(self): - resource = Resource(id=1, name="the resource", blank=None) - self.assertTrue(resource.validate()) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_models_endpoint.py b/keystone/test/unit/test_models_endpoint.py deleted file mode 100644 index 1f216010e6..0000000000 --- a/keystone/test/unit/test_models_endpoint.py +++ /dev/null @@ -1,78 +0,0 @@ -import json -import unittest2 as unittest - -from keystone.models import Endpoint -from keystone.test import utils as testutils - - -class TestModelsEndpoint(unittest.TestCase): - '''Unit tests for keystone/models.py:Endpoint class.''' - - def test_endpoint(self): - endpoint = Endpoint() - self.assertEquals(str(endpoint.__class__), - "", - "endpoint should be of instance " - "class keystone.models.Endpoint but instead " - "was '%s'" % str(endpoint.__class__)) - self.assertIsInstance(endpoint, dict, "") - - def test_endpoint_static_properties(self): - endpoint = Endpoint(id=1, name="the endpoint", enabled=True, - blank=None) - self.assertEquals(endpoint.id, 1) - self.assertEquals(endpoint.name, "the endpoint") - self.assertTrue(endpoint.enabled) - self.assertRaises(AttributeError, getattr, endpoint, - 'some_bad_property') - - def test_endpoint_properties(self): - endpoint = Endpoint(id=2, name="the endpoint", blank=None) - endpoint["dynamic"] = "test" - self.assertEquals(endpoint["dynamic"], "test") - - def test_endpoint_json_serialization(self): - endpoint = Endpoint(id=3, name="the endpoint", blank=None) - endpoint["dynamic"] = "test" - json_str = endpoint.to_json() - d1 = json.loads(json_str) - d2 = json.loads('{"endpoint": {"name": "the endpoint", \ - "id": 3, "dynamic": "test"}}') - self.assertDictEqual(d1, d2) - - def test_endpoint_xml_serialization(self): - endpoint = Endpoint(id=4, name="the endpoint", blank=None) - xml_str = endpoint.to_xml() - self.assertTrue(testutils.XMLTools.xmlEqual(xml_str, - '')) - - def test_endpoint_json_deserialization(self): - endpoint = Endpoint.from_json('{"name": "the endpoint", "id": 5}', - hints={"contract_attributes": ['id', 'name']}) - self.assertIsInstance(endpoint, Endpoint) - self.assertEquals(endpoint.id, 5) - self.assertEquals(endpoint.name, "the endpoint") - - def test_endpoint_json_deserialization_rootless(self): - endpoint = Endpoint.from_json('{"endpoint": {"name": "the endpoint", \ - "id": 6}}', - hints={"contract_attributes": ['id', 'name']}) - self.assertIsInstance(endpoint, Endpoint) - self.assertEquals(endpoint.id, 6) - self.assertEquals(endpoint.name, "the endpoint") - - def test_endpoint_xml_deserialization(self): - endpoint = Endpoint(id=7, name="the endpoint", blank=None) - self.assertIsInstance(endpoint, Endpoint) - - def test_endpoint_inspection(self): - endpoint = Endpoint(id=8, name="the endpoint", blank=None) - self.assertFalse(endpoint.inspect()) - - def test_endpoint_validation(self): - endpoint = Endpoint(id=9, name="the endpoint", blank=None) - self.assertTrue(endpoint.validate()) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_models_endpoint_template.py b/keystone/test/unit/test_models_endpoint_template.py deleted file mode 100644 index 148410856e..0000000000 --- a/keystone/test/unit/test_models_endpoint_template.py +++ /dev/null @@ -1,79 +0,0 @@ -import json -import unittest2 as unittest - -from keystone.models import EndpointTemplate -from keystone.test import utils as testutils - - -class TestModelsEndpointTemplate(unittest.TestCase): - '''Unit tests for keystone/models.py:EndpointTemplate class.''' - - def test_endpointtemplate(self): - endpointtemplate = EndpointTemplate() - self.assertEquals(str(endpointtemplate.__class__), - "", - "endpointtemplate should be of instance " - "class keystone.models.EndpointTemplate but instead " - "was '%s'" % str(endpointtemplate.__class__)) - self.assertIsInstance(endpointtemplate, dict, "") - - def test_endpointtemplate_static_properties(self): - endpointtemplate = EndpointTemplate(id=1, name="the endpointtemplate", - enabled=True, blank=None) - self.assertEquals(endpointtemplate.id, 1) - self.assertEquals(endpointtemplate.name, "the endpointtemplate") - self.assertTrue(endpointtemplate.enabled) - self.assertEquals(endpointtemplate.admin_url, None) - self.assertRaises(AttributeError, getattr, endpointtemplate, - 'some_bad_property') - - def test_endpointtemplate_properties(self): - endpointtemplate = EndpointTemplate(id=1, name="the endpointtemplate", - blank=None) - endpointtemplate["dynamic"] = "test" - self.assertEquals(endpointtemplate["dynamic"], "test") - - def test_endpointtemplate_json_serialization(self): - endpointtemplate = EndpointTemplate(id=1, name="the endpointtemplate", - blank=None) - endpointtemplate["dynamic"] = "test" - json_str = endpointtemplate.to_json() - d1 = json.loads(json_str) - d2 = json.loads('{"endpointtemplate": {"name": "the endpointtemplate",\ - "id": 1, "dynamic": "test"}}') - self.assertDictEqual(d1, d2) - - def test_endpointtemplate_xml_serialization(self): - endpointtemplate = EndpointTemplate(id=1, name="the endpointtemplate", - blank=None) - xml_str = endpointtemplate.to_xml() - self.assertTrue(testutils.XMLTools.xmlEqual(xml_str, - '')) - - def test_endpointtemplate_json_deserialization(self): - endpointtemplate = EndpointTemplate.from_json('{"name": \ - "the endpointtemplate", "id": 1}', - hints={"contract_attributes": ['id', 'name']}) - self.assertIsInstance(endpointtemplate, EndpointTemplate) - self.assertEquals(endpointtemplate.id, 1) - self.assertEquals(endpointtemplate.name, "the endpointtemplate") - - def test_endpointtemplate_xml_deserialization(self): - endpointtemplate = EndpointTemplate(id=1, name="the endpointtemplate", - blank=None) - self.assertIsInstance(endpointtemplate, EndpointTemplate) - - def test_endpointtemplate_inspection(self): - endpointtemplate = EndpointTemplate(id=1, name="the endpointtemplate", - blank=None) - self.assertFalse(endpointtemplate.inspect()) - - def test_endpointtemplate_validation(self): - endpointtemplate = EndpointTemplate(id=1, name="the endpointtemplate", - blank=None) - self.assertTrue(endpointtemplate.validate()) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_models_role.py b/keystone/test/unit/test_models_role.py deleted file mode 100644 index 9c25448486..0000000000 --- a/keystone/test/unit/test_models_role.py +++ /dev/null @@ -1,115 +0,0 @@ -import json -import unittest2 as unittest - -from keystone.models import Role, UserRoleAssociation -from keystone.test import utils as testutils - - -class TestModelsRole(unittest.TestCase): - '''Unit tests for keystone/models.py:Role class.''' - - def test_role(self): - role = Role() - self.assertEquals(str(role.__class__), - "", - "role should be of instance " - "class keystone.models.Role but instead " - "was '%s'" % str(role.__class__)) - self.assertIsInstance(role, dict, "") - - def test_role_static_properties(self): - role = Role(id=1, name="the role", service_id=1, blank=None) - self.assertEquals(role.id, "1") - self.assertEquals(role.name, "the role") - self.assertEquals(role.service_id, "1") - self.assertEquals(role.description, None) - self.assertRaises(AttributeError, getattr, role, - 'some_bad_property') - - def test_role_properties(self): - role = Role(id=1, name="the role", blank=None) - role["dynamic"] = "test" - self.assertEquals(role["dynamic"], "test") - - def test_role_json_serialization(self): - role = Role(id=1, name="the role", blank=None) - role["dynamic"] = "test" - json_str = role.to_json() - d1 = json.loads(json_str) - d2 = {"role": {"name": "the role", "id": "1", "dynamic": "test"}} - self.assertDictEqual(d1, d2) - - def test_role_json_serialization_mapped(self): - role = Role(id=1, name="the role", - service_id="s1", - tenant_id="t1") - json_str = role.to_json() - d1 = json.loads(json_str) - d2 = {"role": {"name": "the role", "id": "1", "serviceId": "s1", - "tenantId": "t1"}} - self.assertDictEqual(d1, d2) - - def test_role_xml_serialization(self): - role = Role(id=1, name="the role", blank=None) - xml_str = role.to_xml() - expected = ('') - self.assertTrue(testutils.XMLTools.xmlEqual(xml_str, expected), - msg='%s != %s' % (xml_str, expected)) - - def test_role_xml_serialization_mapping(self): - role = Role(id=1, name="the role", - service_id="s1", - tenant_id="t1") - xml_str = role.to_xml() - expected = ('') - self.assertTrue(testutils.XMLTools.xmlEqual(xml_str, expected), - msg='%s != %s' % (xml_str, - expected)) - self.assertEquals(role.service_id, "s1") - - def test_role_json_deserialization(self): - role = Role.from_json('{"name": "the role", "id": "1"}', - hints={"contract_attributes": ['id', 'name']}) - self.assertIsInstance(role, Role) - self.assertEquals(role.id, "1") - self.assertEquals(role.name, "the role") - - role = Role.from_json('{"role":{"name": "r1", "serviceId": "s1"}}') - self.assertEquals(role.service_id, "s1") - - def test_role_json_deserialization_types(self): - role = Role.from_json('{"name": "the role", "id": 1}') - self.assertIsInstance(role, Role) - self.assertEquals(role.id, "1", - "'id' should always be returned as a string") - self.assertEquals(role.name, "the role") - - def test_role_xml_deserialization(self): - role = Role(id=1, name="the role", blank=None) - self.assertIsInstance(role, Role) - - def test_role_inspection(self): - role = Role(id=1, name="the role", blank=None) - self.assertFalse(role.inspect()) - - def test_role_validation(self): - role = Role(id=1, name="the role", blank=None) - self.assertTrue(role.validate()) - - # - # UserRoleAssociation - # - def test_userroleassociation_json_serialization_mapped(self): - ura = UserRoleAssociation(id=1, role_id=1, user_id="u1", - tenant_id="t1") - json_str = ura.to_json() - d1 = json.loads(json_str) - d2 = {"role": {"id": 1, "roleId": 1, "userId": "u1", - "tenantId": "t1"}} - self.assertDictEqual(d1, d2) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_models_service.py b/keystone/test/unit/test_models_service.py deleted file mode 100644 index a7a7347847..0000000000 --- a/keystone/test/unit/test_models_service.py +++ /dev/null @@ -1,90 +0,0 @@ -import json -import unittest2 as unittest - -from keystone.logic.types import fault -from keystone.models import Service -from keystone.test import utils as testutils - - -class TestModelsService(unittest.TestCase): - '''Unit tests for keystone/models.py:Service class.''' - - def test_service(self): - service = Service() - self.assertEquals(str(service.__class__), - "", - "service should be of instance " - "class keystone.models.Service but instead " - "was '%s'" % str(service.__class__)) - self.assertIsInstance(service, dict, "") - - def test_service_static_properties(self): - service = Service(id=1, name="the service", type="compute", blank=None) - self.assertEquals(service.id, "1") - self.assertEquals(service.name, "the service") - self.assertRaises(AttributeError, getattr, service, - 'some_bad_property') - - def test_service_properties(self): - service = Service(id=1, name="the service", type="compute", blank=None) - service["dynamic"] = "test" - self.assertEquals(service["dynamic"], "test") - - def test_service_json_serialization(self): - service = Service(id=1, name="the service", type="compute", blank=None) - service["dynamic"] = "test" - json_str = service.to_json() - d1 = json.loads(json_str) - d2 = json.loads('{"OS-KSADM:service": {"name": "the service", \ - "id": "1", "dynamic": "test", "type": "compute"}}') - self.assertDictEqual(d1, d2) - - def test_service_xml_serialization(self): - service = Service(id=1, name="the service", type="compute", blank=None) - xml_str = service.to_xml() - self.assertTrue(testutils.XMLTools.xmlEqual(xml_str, - '')) - - def test_service_json_deserialization(self): - service = Service.from_json('{"name": "the service", "id": 1,\ - "type": "compute"}', - hints={ - "contract_attributes": ['id', 'name'], - "types": [("id", int)]}) - self.assertIsInstance(service, Service) - self.assertEquals(service.id, 1) - self.assertEquals(service.name, "the service") - - def test_service_xml_deserialization(self): - service = Service(id=1, name="the service", blank=None) - self.assertIsInstance(service, Service) - - def test_service_inspection(self): - service = Service(id=1, name="the service", type="compute") - self.assertFalse(service.inspect()) - - def test_service_validation_from_json(self): - self.assertRaises(fault.BadRequestFault, Service.from_json, - '{"name": "", "id": 1}') - self.assertRaises(fault.BadRequestFault, Service.from_json, - '{"type": None, "id": 1}') - - def test_service_validation_from_xml(self): - self.assertRaises(fault.BadRequestFault, Service.from_xml, - '') - self.assertRaises(fault.BadRequestFault, Service.from_xml, - '') - - def test_service_validation(self): - service = Service(id=1, name="the service", type="compute") - self.assertTrue(service.validate()) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_models_services.py b/keystone/test/unit/test_models_services.py deleted file mode 100644 index 3ceb238588..0000000000 --- a/keystone/test/unit/test_models_services.py +++ /dev/null @@ -1,47 +0,0 @@ -import json -import unittest2 as unittest - -from keystone.logic.types import fault -from keystone.models import Service, Services -from keystone.test import utils as testutils - - -class TestModelsServices(unittest.TestCase): - '''Unit tests for keystone/models.py:Service class.''' - - def test_services(self): - services = Services(None, None) - self.assertEquals(str(services.__class__), - "", - "services should be of instance " - "class keystone.models.Services but instead " - "was '%s'" % str(services.__class__)) - - def test_xml_serialization(self): - service_list = [Service(name="keystone", type="identity"), - Service(name="nova", type="compute"), - Service(name="glance", type="image-service")] - services = Services(service_list, {}) - xml_str = services.to_xml() - self.assertTrue(testutils.XMLTools.xmlEqual(xml_str, - '')) - - def test_json_serialization(self): - service_list = [Service(name="keystone", type="identity"), - Service(name="nova", type="compute"), - Service(name="glance", type="image-service")] - services = Services(service_list, {}) - json_str = services.to_json() - self.assertEqual(json_str, - '{"OS-KSADM:services": [{"type": "identity", "name": \ -"keystone"}, {"type": "compute", "name": "nova"}, {"type": "image-service", \ -"name": "glance"}], "OS-KSADM:services_links": []}') - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_models_tenant.py b/keystone/test/unit/test_models_tenant.py deleted file mode 100644 index 9f8e7fa02f..0000000000 --- a/keystone/test/unit/test_models_tenant.py +++ /dev/null @@ -1,167 +0,0 @@ -import json -import unittest2 as unittest - -from keystone.models import Tenant -from keystone.test import utils as testutils - - -class TestModelsTenant(unittest.TestCase): - '''Unit tests for keystone/models.py:Tenant class.''' - - def test_tenant(self): - tenant = Tenant() - self.assertEquals(str(tenant.__class__), - "", - "tenant should be of instance " - "class keystone.models.Tenant but instead " - "was '%s'" % str(tenant.__class__)) - self.assertIsInstance(tenant, dict, "") - - def test_tenant_static_properties(self): - tenant = Tenant(id=1, name="the tenant", enabled=True, blank=None) - self.assertEquals(tenant.id, "1") - self.assertEquals(tenant.name, "the tenant") - self.assertTrue(tenant.enabled) - self.assertEquals(tenant.description, None) - self.assertRaises(AttributeError, getattr, tenant, 'some_bad_property') - - def test_tenant_properties(self): - tenant = Tenant(id=2, name="the tenant", blank=None) - tenant["dynamic"] = "test" - self.assertEquals(tenant["dynamic"], "test") - - def test_tenant_initialization(self): - tenant = Tenant(id=3, name="the tenant", enabled=True, blank=None) - self.assertTrue(tenant.enabled) - - tenant = Tenant(id=35, name="the tenant", enabled=0, blank=None) - self.assertEquals(tenant.enabled, False) - - json_str = tenant.to_json() - d1 = json.loads(json_str) - self.assertIn('tenant', d1) - self.assertIn('enabled', d1['tenant']) - self.assertEquals(d1['tenant']['enabled'], False) - - tenant = Tenant(id=36, name="the tenant", enabled=False, blank=None) - self.assertEquals(tenant.enabled, False) - - def test_tenant_json_serialization(self): - tenant = Tenant(id=3, name="the tenant", enabled=True, blank=None) - tenant["dynamic"] = "test" - json_str = tenant.to_json() - - d1 = json.loads(json_str) - d2 = {"tenant": {"name": "the tenant", "id": "3", "enabled": True, - "dynamic": "test"}} - self.assertDictEqual(d1, d2) - - def test_tenant_xml_serialization(self): - tenant = Tenant(id=4, name="the tenant", description="X", blank=None) - xml_str = tenant.to_xml() - self.assertTrue(testutils.XMLTools.xmlEqual(xml_str, - '\ - X')) - - def test_resource_json_serialization_mapping(self): - tenant = Tenant(id=1, name="the tenant", rolegrant_id=12) - json_str = tenant.to_json(hints={"maps": {"refId": "rolegrant_id", }}) - d1 = json.loads(json_str) - d2 = {"tenant": {"name": "the tenant", "id": "1", "refId": 12}} - self.assertDictEqual(d1, d2) - - def test_tenant_json_serialization_types(self): - tenant = Tenant(id=1, name="the tenant", bool=True, int=5) - json_str = tenant.to_json(hints={"types": - [("bool", bool), ("int", int)]}) - d1 = json.loads(json_str) - d2 = {"tenant": {"name": "the tenant", "id": "1", "bool": True, - "int": 5}} - self.assertDictEqual(d1, d2) - - def test_tenant_xml_serialization_mapping(self): - tenant = Tenant(id=1, name="the resource", rolegrant_id=12) - xml_str = tenant.to_xml(hints={"maps": {"refId": "rolegrant_id", }}) - self.assertTrue(testutils.XMLTools.xmlEqual(xml_str, - '')) - - def test_tenant_json_deserialization(self): - tenant = Tenant.from_json('{"tenant": {"name": "the tenant",\ - "id": 5, "extra": "some data"}}', - hints={"contract_attributes": ['id', 'name']}) - self.assertIsInstance(tenant, Tenant) - self.assertEquals(tenant.id, 5) - self.assertEquals(tenant.name, "the tenant") - - def test_tenant_xml_deserialization(self): - tenant = Tenant.from_xml('\ - qwerty text', - hints={ - "contract_attributes": ['id', 'name'], - "types": [("id", int), - ("description", str)]}) - self.assertIsInstance(tenant, Tenant) - self.assertEquals(tenant.id, 6) - self.assertEquals(tenant.name, "the tenant") - self.assertEquals(tenant.description, "qwerty text") - - def test_tenant_xml_deserialization_hintless(self): - tenant = Tenant.from_xml('\ - qwerty text') - self.assertIsInstance(tenant, Tenant) - self.assertEquals(tenant.id, "7") - self.assertEquals(tenant.name, "the tenant") - self.assertEquals(tenant.description, "qwerty text") - - def test_tenant_inspection(self): - tenant = Tenant(id=8, name="the tenant", blank=None) - self.assertFalse(tenant.inspect()) - - def test_tenant_validation(self): - tenant = Tenant(id=9, name="the tenant", blank=None) - self.assertTrue(tenant.validate()) - - def test_tenant_description_values(self): - tenant = Tenant(id=10, name="the tenant") - self.assertIsNone(tenant.description, - "Uninitialized description should be None") - xml = tenant.to_dom() - desc = xml.find("{http://docs.openstack.org/identity/api/v2.0}" - "description") - self.assertIsNone(desc, - "Uninitialized description should not exist in xml") - - tenant = Tenant(id=10, name="the tenant", description=None) - self.assertIsNone(tenant.description, - "Description initialized to None should be None") - xml = tenant.to_dom() - desc = xml.find("{http://docs.openstack.org/identity/api/v2.0}" - "description") - self.assertIsNone(desc, - "Uninitialized description should not exist in xml") - - tenant = Tenant(id=10, name="the tenant", description='') - self.assertEquals(tenant.description, '', - 'Description initialized to empty string should be empty string') - xml = tenant.to_dom() - desc = xml.find("description") - self.assertEquals(desc.text, '', - "Blank Description should show as blank tag in xml") - - tenant = Tenant(id=10, name="the tenant", description=None) - xml = tenant.to_xml(hints={"tags": ["description"]}) - xml = tenant.to_dom() - desc = xml.find("description") - self.assertEquals(desc, None, - "'None' Description should show as empty tag in xml") - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_models_token.py b/keystone/test/unit/test_models_token.py deleted file mode 100644 index 06affd9c1f..0000000000 --- a/keystone/test/unit/test_models_token.py +++ /dev/null @@ -1,70 +0,0 @@ -import json -from lxml import etree -import unittest2 as unittest - -from keystone.models import Token -from keystone.test import utils as testutils - - -class TestModelsToken(unittest.TestCase): - '''Unit tests for keystone/models.py:Token class.''' - - def test_token(self): - token = Token() - self.assertEquals(str(token.__class__), - "", - "token should be of instance " - "class keystone.models.Token but instead " - "was '%s'" % str(token.__class__)) - self.assertIsInstance(token, dict, "") - - def test_token_static_properties(self): - token = Token(id=1, name="the token", enabled=True, blank=None) - self.assertEquals(token.id, 1) - self.assertEquals(token.name, "the token") - self.assertTrue(token.enabled) - self.assertRaises(AttributeError, getattr, token, - 'some_bad_property') - - def test_token_properties(self): - token = Token(id=1, name="the token", blank=None) - token["dynamic"] = "test" - self.assertEquals(token["dynamic"], "test") - - def test_token_json_serialization(self): - token = Token(id=1, name="the token", blank=None) - token["dynamic"] = "test" - json_str = token.to_json() - d1 = json.loads(json_str) - d2 = json.loads('{"token": {"name": "the token", \ - "id": 1, "dynamic": "test"}}') - self.assertDictEqual(d1, d2) - - def test_token_xml_serialization(self): - token = Token(id=1, name="the token", blank=None) - xml_str = token.to_xml() - self.assertTrue(testutils.XMLTools.xmlEqual(xml_str, - '')) - - def test_token_json_deserialization(self): - token = Token.from_json('{"name": "the token", "id": 1}', - hints={"contract_attributes": ['id', 'name']}) - self.assertIsInstance(token, Token) - self.assertEquals(token.id, 1) - self.assertEquals(token.name, "the token") - - def test_token_xml_deserialization(self): - token = Token(id=1, name="the token", blank=None) - self.assertIsInstance(token, Token) - - def test_token_inspection(self): - token = Token(id=1, name="the token", blank=None) - self.assertFalse(token.inspect()) - - def test_token_validation(self): - token = Token(id=1, name="the token", blank=None) - self.assertTrue(token.validate()) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_models_user.py b/keystone/test/unit/test_models_user.py deleted file mode 100644 index 1a3f7c4f83..0000000000 --- a/keystone/test/unit/test_models_user.py +++ /dev/null @@ -1,69 +0,0 @@ -import json -from lxml import etree -import unittest2 as unittest - -from keystone.models import User -from keystone.test import utils as testutils - - -class TestModelsUser(unittest.TestCase): - '''Unit tests for keystone/models.py:User class.''' - - def test_user(self): - user = User() - self.assertEquals(str(user.__class__), - "", - "user should be of instance " - "class keystone.models.User but instead " - "was '%s'" % str(user.__class__)) - self.assertIsInstance(user, dict, "") - - def test_user_static_properties(self): - user = User(id=1, name="the user", blank=None) - self.assertEquals(user.id, 1) - self.assertEquals(user.name, "the user") - self.assertRaises(AttributeError, getattr, user, - 'some_bad_property') - - def test_user_properties(self): - user = User(id=1, name="the user", blank=None) - user["dynamic"] = "test" - self.assertEquals(user["dynamic"], "test") - - def test_user_json_serialization(self): - user = User(id=1, name="the user", blank=None) - user["dynamic"] = "test" - json_str = user.to_json() - d1 = json.loads(json_str) - d2 = json.loads('{"user": {"name": "the user", \ - "id": 1, "dynamic": "test"}}') - self.assertDictEqual(d1, d2) - - def test_user_xml_serialization(self): - user = User(id=1, name="the user", blank=None) - xml_str = user.to_xml() - self.assertTrue(testutils.XMLTools.xmlEqual(xml_str, - '')) - - def test_user_json_deserialization(self): - user = User.from_json('{"name": "the user", "id": 1}', - hints={"contract_attributes": ['id', 'name']}) - self.assertIsInstance(user, User) - self.assertEquals(user.id, 1) - self.assertEquals(user.name, "the user") - - def test_user_xml_deserialization(self): - user = User(id=1, name="the user", blank=None) - self.assertIsInstance(user, User) - - def test_user_inspection(self): - user = User(id=1, name="the user", blank=None) - self.assertFalse(user.inspect()) - - def test_user_validation(self): - user = User(id=1, name="the user", blank=None) - self.assertTrue(user.validate()) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_normalizingfilter.py b/keystone/test/unit/test_normalizingfilter.py deleted file mode 100644 index 95b7ea934e..0000000000 --- a/keystone/test/unit/test_normalizingfilter.py +++ /dev/null @@ -1,94 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 2010-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. - - -import unittest2 as unittest -from keystone.frontends.normalizer import NormalizingFilter - - -class MockWsgiApp(object): - - def __init__(self): - pass - - def __call__(self, env, start_response): - pass - - -def _start_response(): - pass - - -class NormalizingFilterTest(unittest.TestCase): - - def setUp(self): - self.filter = NormalizingFilter(MockWsgiApp(), {}) - - def test_trailing_slash(self): - env = {'PATH_INFO': '/v2.0/'} - self.filter(env, _start_response) - self.assertEqual('/', env['PATH_INFO']) - - def test_remove_trailing_slash_from_empty_path(self): - """Empty paths should still equate to a slash""" - env = {'PATH_INFO': '/'} - self.filter(env, _start_response) - self.assertEqual('/', env['PATH_INFO']) - - def test_no_extension(self): - env = {'PATH_INFO': '/v2.0/someresource'} - self.filter(env, _start_response) - self.assertEqual('/someresource', env['PATH_INFO']) - self.assertEqual('application/json', env['HTTP_ACCEPT']) - - def test_xml_extension(self): - env = {'PATH_INFO': '/v2.0/someresource.xml'} - self.filter(env, _start_response) - self.assertEqual('/someresource', env['PATH_INFO']) - self.assertEqual('application/xml', env['HTTP_ACCEPT']) - - def test_atom_extension(self): - env = {'PATH_INFO': '/v2.0/someresource.atom'} - self.filter(env, _start_response) - self.assertEqual('/someresource', env['PATH_INFO']) - self.assertEqual('application/atom+xml', env['HTTP_ACCEPT']) - - def test_json_extension(self): - env = {'PATH_INFO': '/v2.0/someresource.json'} - self.filter(env, _start_response) - self.assertEqual('/someresource', env['PATH_INFO']) - self.assertEqual('application/json', env['HTTP_ACCEPT']) - - def test_version_header(self): - env = {'PATH_INFO': '/someresource', - 'HTTP_ACCEPT': - 'application/vnd.openstack.identity+xml;version=2.0'} - self.filter(env, _start_response) - self.assertEqual('/someresource', env['PATH_INFO']) - self.assertEqual('application/xml', env['HTTP_ACCEPT']) - self.assertEqual('2.0', env['KEYSTONE_API_VERSION']) - - def test_extension_overrides_header(self): - env = { - 'PATH_INFO': '/v2.0/someresource.json', - 'HTTP_ACCEPT': 'application/xml'} - self.filter(env, _start_response) - self.assertEqual('/someresource', env['PATH_INFO']) - self.assertEqual('application/json', env['HTTP_ACCEPT']) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_server.py b/keystone/test/unit/test_server.py deleted file mode 100644 index 5ecd46f790..0000000000 --- a/keystone/test/unit/test_server.py +++ /dev/null @@ -1,88 +0,0 @@ -import unittest2 as unittest -from StringIO import StringIO -import datetime -import webob -from lxml import etree -import json - -from keystone import utils -from keystone.logic.types import auth -import keystone.logic.types.fault as fault - - -class TestServer(unittest.TestCase): - '''Unit tests for server.py.''' - - request = None - auth_data = None - - def setUp(self): - environ = {'wsgi.url_scheme': 'http'} - self.request = webob.Request(environ) - self.auth_data = auth.ValidateData(auth.Token(datetime.date.today(), - "2231312"), auth.User("id", "username", "12345", "aTenant")) - - #def tearDown(self): - - def test_is_xml_response(self): - self.assertFalse(utils.is_xml_response(self.request)) - self.request.headers["Accept"] = "application/xml" - self.request.content_type = "application/json" - self.assertTrue(utils.is_xml_response(self.request)) - - def test_send_result_xml(self): - self.request.headers["Accept"] = "application/xml" - response = utils.send_result(200, self.request, self.auth_data) - - self.assertTrue(response.headers['content-type'] == - "application/xml; charset=UTF-8") - xml = etree.fromstring(response.unicode_body) - - user = xml.find("{http://docs.openstack.org/identity/api/v2.0}user") - token = xml.find("{http://docs.openstack.org/identity/api/v2.0}token") - - self.assertTrue(user.get("name"), "username") - self.assertTrue(user.get("id"), "id") - self.assertTrue(user.get("tenantId"), '12345') - self.assertTrue(token.get("id"), '2231312') - self.assertTrue(token.get("expires"), datetime.date.today()) - - def test_send_result_json(self): - self.request.headers["Accept"] = "application/json" - response = utils.send_result(200, self.request, self.auth_data) - self.assertTrue(response.headers['content-type'] == - "application/json; charset=UTF-8") - dict = json.loads(response.unicode_body) - self.assertTrue(dict['access']['user']['id'], 'id') - self.assertTrue(dict['access']['user']['name'], 'username') - self.assertTrue(dict['access']['user']['tenantId'], '12345') - self.assertTrue(dict['access']['token']['id'], '2231312') - self.assertTrue(dict['access']['token']['expires'], - datetime.date.today()) - - def test_get_auth_token(self): - self.request.headers["X-Auth-Token"] = "Test token" - self.assertTrue(utils.get_auth_token(self.request), "Test Token") - - def test_get_normalized_request_content_exception(self): - self.assertRaises(fault.IdentityFault, - utils.get_normalized_request_content, None, self.request) - - def test_get_normalized_request_content_xml(self): - self.request.environ["CONTENT_TYPE"] = "application/xml" - auth.AuthWithPasswordCredentials("username", "password", "1") - body = ' \ - \ - ' - str = StringIO() - str.write(body) - self.request.environ["wsgi.input"] = str - self.request.environ["CONTENT_LENGTH"] = str.len - #TODO: I THINK THIS belongs in a test for auth.py. - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_service_logic.py b/keystone/test/unit/test_service_logic.py deleted file mode 100644 index d73d0e90a6..0000000000 --- a/keystone/test/unit/test_service_logic.py +++ /dev/null @@ -1,65 +0,0 @@ -import datetime as dt -import unittest2 as unittest - -import keystone.logic.service as service -from keystone.test.unit.base import ServiceAPITest, AdminAPITest -from keystone.logic.types.fault import ItemNotFoundFault, UnauthorizedFault -from keystone.logic.types.auth import ValidateData - - -class TestServiceLogic(AdminAPITest): - """Unit tests for logic/service.py.""" - def __init__(self, *args, **kwargs): - super(TestServiceLogic, self).__init__(*args, **kwargs) - self.api_class = service.IdentityService - - def setUp(self): - super(TestServiceLogic, self).setUp() - user_attrs = {'id': 'test_user', - 'password': 'test_pass', - 'email': 'test_user@example.com', - 'enabled': True, - 'tenant_id': 'tenant1'} - self.test_user = self.fixture_create_user(**user_attrs) - - def test_get_user(self): - user_id = self.test_user["id"] - user = self.api.get_user(self.admin_token_id, user_id) - # The returned user object is a different type: - # keystone.logic.types.user.User_Update - self.assertEqual(self.test_user["email"], user.email) - self.assertEqual(self.test_user["enabled"], user.enabled) - self.assertEqual(self.test_user["id"], user.id) - self.assertEqual(self.test_user["tenant_id"], user.tenant_id) - - def test_require_admin(self): - user_id = self.test_user["id"] - self.assertRaises(UnauthorizedFault, self.api.get_user, - self.auth_token_id, user_id) - - def test_require_service_admin(self): - self.assertRaises(UnauthorizedFault, self.api.validate_token, - self.auth_token_id, "any_id") - - def test_has_admin_role(self): - self.assertTrue(self.api.has_admin_role(self.admin_token_id)) - self.assertFalse(self.api.has_admin_role(self.auth_token_id)) - - def test_validate_token(self): - data = self.api.validate_token(self.admin_token_id, self.auth_token_id) - self.assertTrue(isinstance(data, ValidateData)) - - def test_remove_role_from_user(self): - auth_userid = self.auth_user["id"] - regular_role_id = self.role_fixtures[0]["id"] - admin_role_id = self.role_fixtures[1]["id"] - # Attempting to remove the admin role should raise an error. - self.assertRaises(ItemNotFoundFault, self.api.remove_role_from_user, - self.admin_token_id, auth_userid, admin_role_id) - # This should run without error - self.api.remove_role_from_user(self.admin_token_id, - auth_userid, regular_role_id) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_utils.py b/keystone/test/unit/test_utils.py deleted file mode 100644 index 338a12c7b6..0000000000 --- a/keystone/test/unit/test_utils.py +++ /dev/null @@ -1,89 +0,0 @@ -import json -import unittest2 as unittest - -from keystone import utils - - -class TestStringEmpty(unittest.TestCase): - """Unit tests for string functions of utils.py.""" - - def test_is_empty_for_a_valid_string(self): - self.assertFalse(utils.is_empty_string('asdfgf')) - - def test_is_empty_for_a_blank_string(self): - self.assertTrue(utils.is_empty_string('')) - - def test_is_empty_for_none(self): - self.assertTrue(utils.is_empty_string(None)) - - def test_is_empty_for_a_number(self): - self.assertFalse(utils.is_empty_string(0)) - - -class TestCredentialDetection(unittest.TestCase): - """Unit tests for credential type detection""" - - def test_detects_passwordCredentials(self): - self.content_type = "application/xml" - self.body = ''\ - '' - self.assertEquals(utils.detect_credential_type(self), - "passwordCredentials") - - def test_detects_passwordCredentials_unwrapped(self): - self.content_type = "application/xml" - self.body = '' - self.assertEquals(utils.detect_credential_type(self), - "passwordCredentials") - - def test_detects_no_creds(self): - self.content_type = "application/xml" - self.body = '' - self.assertRaises(Exception, utils.detect_credential_type, self) - - def test_detects_blank_creds(self): - self.content_type = "application/xml" - self.body = '' - self.assertRaises(Exception, utils.detect_credential_type, self) - - def test_detects_anyCredentials(self): - self.content_type = "application/xml" - self.body = ''\ - '' - self.assertEquals(utils.detect_credential_type(self), - "anyCredentials") - - def test_detects_anyCredentials_json(self): - self.content_type = "application/json" - self.body = json.dumps({'auth': {'anyCredentials': {}}}) - self.assertEquals(utils.detect_credential_type(self), - "anyCredentials") - - def test_detects_anyUnwrappedCredentials_json(self): - self.content_type = "application/json" - self.body = json.dumps({'anyCredentials': {}}) - self.assertEquals(utils.detect_credential_type(self), - "anyCredentials") - - def test_detects_anyCredentials_with_tenant_json(self): - self.content_type = "application/json" - self.body = json.dumps({'auth': {'tenantId': '1000', - 'anyCredentials': {}}}) - self.assertEquals(utils.detect_credential_type(self), - "anyCredentials") - - def test_detects_skips_tenant_json(self): - self.content_type = "application/json" - self.body = json.dumps({'auth': {'tenantId': '1000'}}) - self.assertRaises(Exception, utils.detect_credential_type, self) - - self.body = json.dumps({'auth': {'tenantName': '1000'}}) - self.assertRaises(Exception, utils.detect_credential_type, self) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_wsgi.py b/keystone/test/unit/test_wsgi.py deleted file mode 100644 index 48fe028f35..0000000000 --- a/keystone/test/unit/test_wsgi.py +++ /dev/null @@ -1,71 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# Copyright 2010 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 WSGI basics and provide some helper functions for other WSGI tests. -""" - -import unittest2 as unittest -import routes -import webob - -from keystone.common import wsgi -from keystone.test.functional import common - - -class TestWsgi(unittest.TestCase): - - def test_debug(self): - - class Application(common.BlankApp): - """Dummy application to test debug.""" - - def __call__(self, environ, start_response): - start_response("200", [("X-Test", "checking")]) - return ['Test result'] - - application = wsgi.Debug(Application()) - result = webob.Request.blank('/').get_response(application) - self.assertEqual(result.body, "Test result") - - def test_router(self): - - class Application(common.BlankApp): - """Test application to call from router.""" - - def __call__(self, environ, start_response): - start_response("200", []) - return ['Router result'] - - class Router(wsgi.Router): - """Test router.""" - - def __init__(self): - mapper = routes.Mapper() - mapper.connect("/test", controller=Application()) - super(Router, self).__init__(mapper) - - result = webob.Request.blank('/test').get_response(Router()) - self.assertEqual(result.body, "Router result") - result = webob.Request.blank('/bad').get_response(Router()) - self.assertNotEqual(result.body, "Router result") - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/utils.py b/keystone/test/utils.py deleted file mode 100644 index 40883e247f..0000000000 --- a/keystone/test/utils.py +++ /dev/null @@ -1,61 +0,0 @@ -# pylint: disable=C0103 -import re -import socket -from lxml import etree - - -# pylint: disable=W0232 -class XMLTools(): - @staticmethod - def xmlEqual(xmlStr1, xmlStr2): - et1 = etree.XML(xmlStr1) - et2 = etree.XML(xmlStr2) - - let1 = [x for x in et1.iter()] - let2 = [x for x in et2.iter()] - - if len(let1) != len(let2): - return False - - while let1: - el = let1.pop(0) - foundEl = XMLTools.findMatchingElem(el, let2) - if foundEl is None: - return False - let2.remove(foundEl) - return True - - @staticmethod - def findMatchingElem(el, eList): - for elem in eList: - if XMLTools.elemsEqual(el, elem): - return elem - return None - - @staticmethod - def elemsEqual(el1, el2): - if el1.tag != el2.tag or el1.attrib != el2.attrib: - return False - # no requirement for text checking for now - #if el1.text != el2.text or el1.tail != el2.tail: - #return False - path1 = el1.getroottree().getpath(el1) - path2 = el2.getroottree().getpath(el2) - idxRE = re.compile(r"(\[\d*\])") - path1 = idxRE.sub("", path1) - path2 = idxRE.sub("", path2) - if path1 != path2: - return False - - return True - - -def get_unused_port(): - """ - Returns an unused port on localhost. - """ - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.bind(('localhost', 0)) - addr, port = s.getsockname() # pylint: disable=W0612 - s.close() - return port diff --git a/keystone/token/__init__.py b/keystone/token/__init__.py new file mode 100644 index 0000000000..bf971a5aba --- /dev/null +++ b/keystone/token/__init__.py @@ -0,0 +1 @@ +from keystone.token.core import * diff --git a/keystone/contrib/extensions/admin/raxkey/__init__.py b/keystone/token/backends/__init__.py similarity index 100% rename from keystone/contrib/extensions/admin/raxkey/__init__.py rename to keystone/token/backends/__init__.py diff --git a/keystone/token/backends/kvs.py b/keystone/token/backends/kvs.py new file mode 100644 index 0000000000..990e107bee --- /dev/null +++ b/keystone/token/backends/kvs.py @@ -0,0 +1,32 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +import copy +import datetime + +from keystone.common import kvs +from keystone import exception +from keystone import token + + +class Token(kvs.Base, token.Driver): + # Public interface + def get_token(self, token_id): + token = self.db.get('token-%s' % token_id) + if (token and (token['expires'] is None + or token['expires'] > datetime.datetime.now())): + return token + else: + raise exception.TokenNotFound(token_id=token_id) + + def create_token(self, token_id, data): + data_copy = copy.deepcopy(data) + if 'expires' not in data: + data_copy['expires'] = self._get_default_expire_time() + self.db.set('token-%s' % token_id, data_copy) + return copy.deepcopy(data_copy) + + def delete_token(self, token_id): + try: + return self.db.delete('token-%s' % token_id) + except KeyError: + raise exception.TokenNotFound(token_id=token_id) diff --git a/keystone/token/backends/memcache.py b/keystone/token/backends/memcache.py new file mode 100644 index 0000000000..b9c2cbe1dd --- /dev/null +++ b/keystone/token/backends/memcache.py @@ -0,0 +1,58 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +from __future__ import absolute_import +import copy + +import memcache + +from keystone import config +from keystone import exception +from keystone import token +from keystone.common import utils + + +CONF = config.CONF +config.register_str('servers', group='memcache', default='localhost:11211') + + +class Token(token.Driver): + def __init__(self, client=None): + self._memcache_client = client + + @property + def client(self): + return self._memcache_client or self._get_memcache_client() + + def _get_memcache_client(self): + memcache_servers = CONF.memcache.servers.split(',') + self._memcache_client = memcache.Client(memcache_servers, debug=0) + return self._memcache_client + + def _prefix_token_id(self, token_id): + return 'token-%s' % token_id.encode('utf-8') + + def get_token(self, token_id): + ptk = self._prefix_token_id(token_id) + token = self.client.get(ptk) + if token is None: + raise exception.TokenNotFound(token_id=token_id) + + return token + + def create_token(self, token_id, data): + data_copy = copy.deepcopy(data) + ptk = self._prefix_token_id(token_id) + if 'expires' not in data_copy: + data_copy['expires'] = self._get_default_expire_time() + kwargs = {} + if data_copy['expires'] is not None: + expires_ts = utils.unixtime(data_copy['expires']) + kwargs['time'] = expires_ts + self.client.set(ptk, data_copy, **kwargs) + return copy.deepcopy(data_copy) + + def delete_token(self, token_id): + # Test for existence + self.get_token(token_id) + ptk = self._prefix_token_id(token_id) + return self.client.delete(ptk) diff --git a/keystone/token/backends/sql.py b/keystone/token/backends/sql.py new file mode 100644 index 0000000000..b57f32a876 --- /dev/null +++ b/keystone/token/backends/sql.py @@ -0,0 +1,69 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +import copy +import datetime + +from keystone.common import sql +from keystone import exception +from keystone import token + + +class TokenModel(sql.ModelBase, sql.DictBase): + __tablename__ = 'token' + id = sql.Column(sql.String(64), primary_key=True) + expires = sql.Column(sql.DateTime(), default=None) + extra = sql.Column(sql.JsonBlob()) + + @classmethod + def from_dict(cls, token_dict): + # shove any non-indexed properties into extra + extra = copy.deepcopy(token_dict) + data = {} + for k in ('id', 'expires'): + data[k] = extra.pop(k, None) + data['extra'] = extra + return cls(**data) + + def to_dict(self): + out = copy.deepcopy(self.extra) + out['id'] = self.id + out['expires'] = self.expires + return out + + +class Token(sql.Base, token.Driver): + # Public interface + def get_token(self, token_id): + session = self.get_session() + token_ref = session.query(TokenModel).filter_by(id=token_id).first() + now = datetime.datetime.now() + if token_ref and (not token_ref.expires or now < token_ref.expires): + return token_ref.to_dict() + else: + raise exception.TokenNotFound(token_id=token_id) + + def create_token(self, token_id, data): + data_copy = copy.deepcopy(data) + if 'expires' not in data_copy: + data_copy['expires'] = self._get_default_expire_time() + + token_ref = TokenModel.from_dict(data_copy) + token_ref.id = token_id + + session = self.get_session() + with session.begin(): + session.add(token_ref) + session.flush() + return token_ref.to_dict() + + def delete_token(self, token_id): + session = self.get_session() + token_ref = session.query(TokenModel)\ + .filter_by(id=token_id)\ + .first() + if not token_ref: + raise exception.TokenNotFound(token_id=token_id) + + with session.begin(): + session.delete(token_ref) + session.flush() diff --git a/keystone/token/core.py b/keystone/token/core.py new file mode 100644 index 0000000000..8c816efe2b --- /dev/null +++ b/keystone/token/core.py @@ -0,0 +1,82 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +"""Main entry point into the Token service.""" + +import datetime + +from keystone import config +from keystone.common import manager + + +CONF = config.CONF +config.register_int('expiration', group='token', default=86400) + + +class Manager(manager.Manager): + """Default pivot point for the Token backend. + + See :mod:`keystone.common.manager.Manager` for more details on how this + dynamically calls the backend. + + """ + + def __init__(self): + super(Manager, self).__init__(CONF.token.driver) + + +class Driver(object): + """Interface description for a Token driver.""" + + def get_token(self, token_id): + """Get a token by id. + + :param token_id: identity of the token + :type token_id: string + :returns: token_ref + :raises: keystone.exception.TokenNotFound + + """ + raise NotImplementedError() + + def create_token(self, token_id, data): + """Create a token by id and data. + + :param token_id: identity of the token + :type token_id: string + :param data: dictionary with additional reference information + + :: + + { + expires='' + id=token_id, + user=user_ref, + tenant=tenant_ref, + metadata=metadata_ref + } + + :type data: dict + :returns: token_ref or None. + + """ + raise NotImplementedError() + + def delete_token(self, token_id): + """Deletes a token by id. + + :param token_id: identity of the token + :type token_id: string + :returns: None. + :raises: keystone.exception.TokenNotFound + + """ + raise NotImplementedError() + + def _get_default_expire_time(self): + """Determine when a token should expire based on the config. + + :returns: datetime.datetime object + + """ + expire_delta = datetime.timedelta(seconds=CONF.token.expiration) + return datetime.datetime.now() + expire_delta diff --git a/keystone/tools/__init__.py b/keystone/tools/__init__.py deleted file mode 100644 index 2baa83b13a..0000000000 --- a/keystone/tools/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -#!/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. -# 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. diff --git a/keystone/tools/buffout.py b/keystone/tools/buffout.py deleted file mode 100644 index 84f313ebb4..0000000000 --- a/keystone/tools/buffout.py +++ /dev/null @@ -1,89 +0,0 @@ -import sys -import StringIO - - -class OutputBuffer(): - """Replaces stdout with a StringIO buffer""" - - def __init__(self): - """Initialize output buffering""" - # True if the OutputBuffer is started - self.buffering = False - - # a reference to the current StringIO buffer - self._buffer = None - - # stale if buffering is True; access buffer contents using .read() - self._contents = None - - def __enter__(self): - self.start() - return self - - def __exit__(self, exc_type, exc_value, traceback): - if exc_type is None: - self.stop() - - def __unicode__(self): - return self._contents - - def __str__(self): - return str(self._contents) - - def start(self): - """Replace stdout with a fresh buffer""" - assert not self.buffering - - self.buffering = True - self.old_stdout = sys.stdout - - self.clear() - - def read(self): - """Read the current buffer""" - if self.buffering: - self._contents = self._buffer.getvalue() - - return self._contents - - def read_lines(self): - """Returns the current buffer as a list - - Excludes the last line, which is empty. - - """ - return self.read().split("\n")[:-1] - - def clear(self): - """Resets the current buffer""" - assert self.buffering - - # dispose of the previous buffer, if any - if self._buffer is not None: - self._buffer.close() - - self._contents = '' - self._buffer = StringIO.StringIO() - sys.stdout = self._buffer - - def flush(self): - """Flushes and clears the current buffer contents""" - assert self.buffering - - self.old_stdout.write(self._contents) - self._contents = '' - self._buffer = StringIO.StringIO() - - def stop(self): - """Stop buffering and pass the output along""" - assert self.buffering - - # preserve the contents prior to closing the StringIO - self.read() - self._buffer.close() - - sys.stdout = self.old_stdout - print self.__unicode__() - self.buffering = False - - return unicode(self) diff --git a/keystone/tools/tracer.py b/keystone/tools/tracer.py deleted file mode 100644 index 6345e2ae28..0000000000 --- a/keystone/tools/tracer.py +++ /dev/null @@ -1,159 +0,0 @@ -#!/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. -# 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. -# -# Author: Ziad Sawalha (http://launchpad.net/~ziad-sawalha) -# Original maintained at: https://github.com/ziadsawalha/Python-tracer -# - -""" -OpenStack Call Tracing Tool - -To use this: -1. include the tools directory in your project (__init__.py and tracer.py) -2. import tools.tracer as early as possible into your module -3. add --trace-calls or -t to any argument parsers if you want the argument -to be shown in the usage page - -Usage: -Add this as early as possible in the first module called in your service:: - - import tools.tracer # @UnusedImport # module runs on import - -If a '-t' or '--trace-calls' parameter is found, it will trace calls to stdout -and space them to show the call graph. Exceptions (errors) will be displayed in -red. - -""" - -import linecache -import os -import sys - - -if '--trace-calls' in sys.argv or '-t' in sys.argv: - # Pop the trace arguments - for i in range(len(sys.argv)): - if sys.argv[i] in ['-t', '--trace-calls']: - sys.argv.pop(i) - - STACK_DEPTH = 0 - - # Calculate root project path - POSSIBLE_TOPDIR = os.path.normpath(os.path.join( - os.path.abspath(sys.argv[0]), - os.pardir, - os.pardir)) - - class ConsoleColors(): - HEADER = '\033[95m' - OKBLUE = '\033[94m' - OKGREEN = '\033[92m' - WARNING = '\033[93m' - FAIL = '\033[91m' - ENDC = '\033[0m' - - def localtrace(frame, event, arg): - if event == "return": - global STACK_DEPTH # pylint: disable=W0603 - STACK_DEPTH = STACK_DEPTH - 1 - elif event == "exception": - output_exception(frame, arg) - return None - - def selectivetrace(frame, event, arg): # pylint: disable=R0911 - global STACK_DEPTH # pylint: disable=W0603 - if event == "exception": - output_exception(frame, arg) - if event == 'call': - co = frame.f_code - func_name = co.co_name - if func_name == 'write': - # Ignore write() calls from print statements - return - func_filename = co.co_filename - if func_filename == "": - return - if func_filename.startswith(("/System", "/Library", - "/usr/lib/py")): - return - if 'python' in func_filename: - return - if 'macosx' in func_filename: - return - output_call(frame, arg) - global STACK_DEPTH # pylint: disable=W0603 - STACK_DEPTH = STACK_DEPTH + 1 - return localtrace - return - - def output_exception(frame, arg): - exc_type, exc_value, exc_traceback = arg # pylint: disable=W0612 - if exc_type is StopIteration: - return - global STACK_DEPTH # pylint: disable=W0603 - global POSSIBLE_TOPDIR # pylint: disable=W0603 - co = frame.f_code - local_vars = frame.f_locals - func_name = co.co_name - line_no = frame.f_lineno - func_filename = co.co_filename - func_filename = func_filename.replace(POSSIBLE_TOPDIR, '') - sys.stdout.write('%s%sERROR: %s %s in %s of %s:%s%s\n' - % (ConsoleColors.FAIL, ' ' * STACK_DEPTH, - exc_type.__name__, exc_value, func_name, - func_filename, line_no, ConsoleColors.ENDC)) - filename = co.co_filename - if filename == "": - filename = "%s.py" % __file__ - if (filename.endswith(".pyc") or - filename.endswith(".pyo")): - filename = filename[:-1] - line = linecache.getline(filename, line_no) - name = frame.f_globals["__name__"] - sys.stdout.write('%s%s %s:%s: %s%s\n' % - (ConsoleColors.HEADER, ' ' * STACK_DEPTH, name, - line_no, line.rstrip(), ConsoleColors.ENDC)) - - sys.stdout.write('%s locals: %s\n' - % (' ' * STACK_DEPTH, - local_vars)) - - def output_call(frame, arg): - caller = frame.f_back - - if caller: - global STACK_DEPTH # pylint: disable=W0603 - global POSSIBLE_TOPDIR # pylint: disable=W0603 - co = frame.f_code - func_name = co.co_name - func_line_no = frame.f_lineno - func_filename = co.co_filename - func_filename = func_filename.replace(POSSIBLE_TOPDIR, '') - caller_line_no = caller.f_lineno - caller_filename = caller.f_code.co_filename.replace( - POSSIBLE_TOPDIR, '') - if caller_filename == func_filename: - caller_filename = 'line' - sys.stdout.write('%s%s::%s:%s (from %s:%s)\n' % - (' ' * STACK_DEPTH, func_filename, func_name, func_line_no, - caller_filename, caller_line_no)) - - sys.stdout.write('Starting OpenStack call tracer\n') - sys.settrace(selectivetrace) diff --git a/keystone/utils.py b/keystone/utils.py deleted file mode 100755 index 7f32d3237d..0000000000 --- a/keystone/utils.py +++ /dev/null @@ -1,312 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 2010-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. - -# pylint: disable=W1201 - -import functools -import json -import logging -from lxml import etree -import os -import sys -import tempfile -from webob import Response - -from keystone import config -import keystone.logic.types.fault as fault - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - -CONF = config.CONF - - -def is_xml_response(req): - """Returns True when the request wants an XML response, False otherwise""" - return "Accept" in req.headers and "application/xml" in req.accept - - -def is_json_response(req): - """Returns True when the request wants a JSON response, False otherwise""" - return "Accept" in req.headers and "application/json" in req.accept - - -def is_atom_response(req): - """Returns True when the request wants an ATOM response, False otherwise""" - return "Accept" in req.headers and "application/atom+xml" in req.accept - - -def get_app_root(): - return os.path.abspath(os.path.dirname(__file__)) - - -def get_auth_token(req): - """Returns the auth token from request headers""" - return req.headers.get("X-Auth-Token") - - -def get_auth_user(req): - """Returns the auth user from request headers""" - return req.headers.get("X-Auth-User") - - -def get_auth_key(req): - """Returns the auth key from request headers""" - return req.headers.get("X-Auth-Key") - - -def wrap_error(func): - - # pylint: disable=W0703 - @functools.wraps(func) - def check_error(*args, **kwargs): - try: - return func(*args, **kwargs) - except Exception as err: - if isinstance(err, fault.IdentityFault): - return send_error(err.code, kwargs['req'], err) - elif isinstance(err, fault.ItemNotFoundFault): - return send_error(err.code, kwargs['req'], err) - else: - logging.exception(err) - return send_error(500, kwargs['req'], - fault.IdentityFault("Unhandled error", - str(err))) - return check_error - - -def get_normalized_request_content(model, req): - """Initialize a model from json/xml contents of request body""" - - if req.content_type == "application/xml": - return model.from_xml(req.body) - elif req.content_type == "application/json": - return model.from_json(req.body) - else: - logging.debug("Unsupported content-type passed: %s" % req.content_type) - raise fault.IdentityFault("I don't understand the content type", - code=415) - - -# pylint: disable=R0912 -def detect_credential_type(req): - """Return the credential type name by detecting them in json/xml body""" - - if req.content_type == "application/xml": - dom = etree.Element("root") - dom.append(etree.fromstring(req.body)) - root = dom.find("{http://docs.openstack.org/identity/api/v2.0}" - "auth") - if root is None: - # Try legacy without wrapper - creds = dom.find("*") - if creds: - logger.warning("Received old syntax credentials not wrapped in" - "'auth'") - else: - creds = root.find("*") - - if creds is None: - raise fault.BadRequestFault("Request is missing credentials") - - name = creds.tag - if "}" in name: - #trim away namespace if it is there - name = name[name.rfind("}") + 1:] - - return name - elif req.content_type == "application/json": - obj = json.loads(req.body) - if len(obj) == 0: - raise fault.BadRequestFault("Expecting 'auth'") - tag = obj.keys()[0] - if tag == "auth": - if len(obj[tag]) == 0: - raise fault.BadRequestFault("Expecting Credentials") - for key, value in obj[tag].iteritems(): # pylint: disable=W0612 - if key not in ['tenantId', 'tenantName']: - return key - raise fault.BadRequestFault("Credentials missing from request") - else: - credentials_type = tag - return credentials_type - else: - logging.debug("Unsupported content-type passed: %s" % req.content_type) - raise fault.IdentityFault("I don't understand the content type", - code=415) - - -def send_error(code, req, result): - content = None - - resp = Response() - resp.headers['content-type'] = None - resp.headers['Vary'] = 'X-Auth-Token' - resp.status = code - - if result: - if is_xml_response(req): - content = result.to_xml() - resp.headers['content-type'] = "application/xml" - else: - content = result.to_json() - resp.headers['content-type'] = "application/json" - - resp.content_type_params = {'charset': 'UTF-8'} - resp.unicode_body = content.decode('UTF-8') - - return resp - - -def send_result(code, req, result=None): - content = None - - resp = Response() - resp.headers['content-type'] = None - resp.headers['Vary'] = 'X-Auth-Token' - resp.status = code - if code > 399: - return resp - - if result: - if is_xml_response(req): - content = result.to_xml() - resp.headers['content-type'] = "application/xml" - else: - content = result.to_json() - resp.headers['content-type'] = "application/json" - resp.content_type_params = {'charset': 'UTF-8'} - resp.unicode_body = content.decode('UTF-8') - - return resp - - -def send_legacy_result(code, headers): - resp = Response() - if 'content-type' not in headers: - headers['content-type'] = "text/plain" - resp.headers['Vary'] = 'X-Auth-Token' - - headers['Vary'] = 'X-Auth-Token' - - resp.headers = headers - resp.status = code - if code > 399: - return resp - - resp.content_type_params = {'charset': 'UTF-8'} - - return resp - - -def import_module(module_name, class_name=None): - '''Import a class given a full module.class name or seperate - module and options. If no class_name is given, it is assumed to - be the last part of the module_name string.''' - if class_name is None: - try: - if module_name not in sys.modules: - __import__(module_name) - return sys.modules[module_name] - except ImportError as exc: - logging.exception(exc) - module_name, _separator, class_name = module_name.rpartition('.') - if not exc.args[0].startswith('No module named %s' % class_name): - raise - try: - if module_name not in sys.modules: - __import__(module_name) - return getattr(sys.modules[module_name], class_name) - except (ImportError, ValueError, AttributeError), exception: - logging.exception(exception) - raise ImportError(_('Class %s.%s cannot be found (%s)') % - (module_name, class_name, exception)) - - -def check_empty_string(value, message): - """ - Checks whether a string is empty and raises - fault for empty string. - """ - if is_empty_string(value): - raise fault.BadRequestFault(message) - - -def is_empty_string(value): - """ - Checks whether string is empty. - """ - if value is None: - return True - if not isinstance(value, basestring): - return False - if len(value.strip()) == 0: - return True - return False - - -def write_temp_file(txt): - """ - Writes the supplied text to a temporary file and returns the file path. - - When the file is no longer needed, it is up to the calling program to - delete it. - """ - fd, tmpname = tempfile.mkstemp() - os.close(fd) - with file(tmpname, "w") as fconf: - fconf.write(txt) - return tmpname - - -def opt_to_conf(options, create_temp=False): - """ - Takes a dict of options and either returns a string that represents the - equivalent CONF configuration file (when create_temp is False), or writes - the temp file and returns the name of that temp file. NOTE: it is up to - the calling program to delete the temp file when it is no longer needed. - """ - def parse_opt(options, section=None): - out = [] - subsections = [] - if section is None: - section = "DEFAULT" - # Create the section header - out.append("[%s]" % section) - for key, val in options.iteritems(): - if isinstance(val, dict): - # This is a subsection; parse recursively. - subsections.append(parse_opt(val, section=key)) - else: - out.append("%s = %s" % (key.replace("-", "_"), val)) - - # Add the subsections - for subsection in subsections: - out.append("") - out.append(subsection) - return "\n".join(out) - - txt = parse_opt(options) - if create_temp: - return write_temp_file(txt) - else: - return txt - - -def set_configuration(options): - """ Given a dict of options, populates the config.CONF module to match.""" - _config_file = opt_to_conf(options, create_temp=True) - CONF(config_files=[_config_file]) - os.remove(_config_file) diff --git a/keystone/version.py b/keystone/version.py deleted file mode 100644 index 0529b279f2..0000000000 --- a/keystone/version.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright (C) 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. - -API_VERSION = "2.0" -API_VERSION_STATUS = "beta" -API_VERSION_DATE = "2011-11-19T00:00:00Z" - -RELEASE_VERSION = "2012.1" -RELEASE_VERSION_FINAL = False # becomes true at Release Candidate time - - -def canonical_version(): - return RELEASE_VERSION - - -def version(): - if RELEASE_VERSION_FINAL: - return RELEASE_VERSION - else: - return '%s-dev' % (RELEASE_VERSION) diff --git a/pylintrc b/pylintrc deleted file mode 100644 index a18c307b01..0000000000 --- a/pylintrc +++ /dev/null @@ -1,43 +0,0 @@ -# The format of this file isn't really documented; just use --generate-rcfile -[MASTER] -# Add to the black list. It should be a base name, not a -# path. You may set this option multiple times. -ignore=test - -[Messages Control] -# NOTE(justinsb): We might want to have a 2nd strict pylintrc in future -# C0111: Don't require docstrings on every method -# W0511: TODOs in code comments are fine. -# W0142: *args and **kwargs are fine. -# W0622: Redefining id is fine. -# R0801: Duplicate lines of code fine. -disable=C0111,W0511,W0142,W0622,R0801 - -[Basic] -# Variable names can be 1 to 31 characters long, with lowercase and underscores -variable-rgx=[a-z_][a-z0-9_]{0,30}$ - -# Argument names can be 2 to 31 characters long, with lowercase and underscores -argument-rgx=[a-z_][a-z0-9_]{1,30}$ - -# Method names should be at least 3 characters long -# and be lowecased with underscores -method-rgx=([a-z_][a-z0-9_]{2,50}|setUp|tearDown)$ - -# Module names matching keystone-* are ok (files in bin/) -module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+)|(keystone-[a-z0-9_-]+))$ - -# Don't require docstrings on tests. -no-docstring-rgx=((__.*__)|([tT]est.*)|setUp|tearDown)$ - -[Design] -max-public-methods=100 -min-public-methods=0 -max-args=6 - -[Variables] - -# List of additional names supposed to be defined in builtins. Remember that -# you should avoid to define new builtins when possible. -# _ is used by our localization -additional-builtins=_ diff --git a/run_tests.py b/run_tests.py old mode 100755 new mode 100644 index 53463aee10..98ecb4a9d4 --- a/run_tests.py +++ b/run_tests.py @@ -1,104 +1,362 @@ #!/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 - functional.test_extensions:TestExtensions.test_extensions_json + python run_tests.py test_compute:ComputeTestCase.test_run_terminate To run a single test module: - python run_tests.py functional.test_extensions + 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 subprocess +import time -import keystone.tools.tracer # @UnusedImport # module runs on import -from keystone import test - -logger = logging.getLogger(__name__) - -TESTS = [ - test.UnitTests, - test.ClientTests, - test.SQLTest, - test.SSLTest, - test.ClientWithoutHPIDMTest, - test.LDAPTest, - # Waiting on instructions on how to start memcached in jenkins: - # But tests pass - # MemcacheTest, -] +from nose import config +from nose import core +from nose import result -def parse_suite_filter(): - """ Parses out -O or --only argument and returns the value after it as the - filter. Removes it from sys.argv in the process. """ - filter = None - if '-O' in sys.argv or '--only' in sys.argv: - for i in range(len(sys.argv)): - if sys.argv[i] in ['-O', '--only']: - if len(sys.argv) > i + 1: - # Remove -O/--only settings from sys.argv - sys.argv.pop(i) - filter = sys.argv.pop(i) - break - return filter +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 'red' + elif elapsed_time > 0.25: + return 'yellow' + else: + return 'green' + + +class NovaTestResult(result.TextTestResult): + def __init__(self, *args, **kw): + self.show_elapsed = kw.pop('show_elapsed') + result.TextTestResult.__init__(self, *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 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): + 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): + 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. + """ + 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) + + def startTest(self, test): + unittest.TestResult.startTest(self, test) + self.start_time = time.time() + current_case = test.test.__class__.__name__ + + if self.showAll: + if current_case != self._last_case: + self.stream.writeln(current_case) + self._last_case = current_case + + self.stream.write( + ' %s' % str(test.test._testMethodName).ljust(60)) + self.stream.flush() + + +class NovaTestRunner(core.TextTestRunner): + def __init__(self, *args, **kwargs): + self.show_elapsed = kwargs.pop('show_elapsed') + core.TextTestRunner.__init__(self, *args, **kwargs) + + def _makeResult(self): + return NovaTestResult(self.stream, + self.descriptions, + self.verbosity, + self.config, + show_elapsed=self.show_elapsed) + + 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 run(self, test): + result_ = core.TextTestRunner.run(self, test) + if self.show_elapsed: + self._writeSlowTests(result_) + return result_ if __name__ == '__main__': - filter = parse_suite_filter() - if filter: - TESTS = [t for t in TESTS if filter in str(t)] - if not TESTS: - print 'No test configuration by the name %s found' % filter - sys.exit(2) - #Run test suites - if len(TESTS) > 1: - directory = os.getcwd() - for test_num, test_cls in enumerate(TESTS): - try: - result = test_cls().run() - if result: - logger.error("Run returned %s for test %s. Exiting" % - (result, test_cls.__name__)) - sys.exit(result) - except Exception, e: - print "Error:", e - logger.exception(e) - sys.exit(1) - # Collect coverage from each run. They'll be combined later in .sh - if '--with-coverage' in sys.argv: - coverage_file = os.path.join(directory, ".coverage") - target_file = "%s.%s" % (coverage_file, test_cls.__name__) - try: - if os.path.exists(target_file): - logger.info("deleting %s" % target_file) - os.unlink(target_file) - if os.path.exists(coverage_file): - logger.info("Saving %s to %s" % (coverage_file, - target_file)) - os.rename(coverage_file, target_file) - except Exception, e: - logger.exception(e) - print ("Failed to move coverage file while running test" - ": %s. Error reported was: %s" % - (test_cls.__name__, e)) - sys.exit(1) - else: - for test_num, test_cls in enumerate(TESTS): - try: - result = test_cls().run() - if result: - logger.error("Run returned %s for test %s. Exiting" % - (result, test_cls.__name__)) - sys.exit(result) - except Exception, e: - print "Error:", e - logger.exception(e) - sys.exit(1) + # 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 = [] + for x in sys.argv: + if x.startswith('test_'): + pass + #argv.append('tests.%s' % x) + argv.append(x) + elif x.startswith('--hide-elapsed'): + show_elapsed = False + else: + argv.append(x) + + testdir = os.path.abspath(os.path.join("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/run_tests.sh b/run_tests.sh index aa04f47360..c55a0cd9e5 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -6,62 +6,38 @@ function usage { echo "Usage: $0 [OPTION]..." echo "Run Keystone's test suite(s)" echo "" - echo " -O, --only test_suite Only run the specified test suite. Valid values are:" - echo " UnitTests: runs unit tests" - echo " ClientTests: runs tests that start and hit an HTTP[S] server" - echo " SQLTest: runs functional tests with SQLAlchemy backend" - echo " SSLTest: runs client tests with SSL configured" - echo " LDAPTest: runs functional tests with LDAP backend" - echo " MemcacheTest: runs functional tests with memcached storing tokens" - echo " ClientWithoutHPIDMTest: runs client tests with HP-IDM extension disabled" - echo " Note: by default, run_tests will run all suites" echo " -V, --virtual-env Always use virtualenv. Install automatically if not present" echo " -N, --no-virtual-env Don't use virtualenv. Run tests in local environment" + echo " -r, --recreate-db Recreate the test database (deprecated, as this is now the default)." + echo " -n, --no-recreate-db Don't recreate the test database." echo " -x, --stop Stop running tests after the first error or failure." echo " -f, --force Force a clean re-build of the virtual environment. Useful when dependencies have been added." - echo " Note: you might need to 'sudo' this since it pip installs into the vitual environment" - echo " -P, --skip-pep8 Just run tests; skip pep8 check" echo " -p, --pep8 Just run pep8" - echo " -l, --pylint Just run pylint" - echo " -j, --json Just validate JSON" - echo " -c, --with-coverage Generate coverage report" + echo " -P, --no-pep8 Don't run pep8" + echo " -c, --coverage Generate coverage report" echo " -h, --help Print this usage message" echo " --hide-elapsed Don't print the elapsed time for each test along with slow test list" - echo " --verbose Print additional logging" - echo " --debug Enable debug logging in Keystone instances" echo "" echo "Note: with no options specified, the script will try to run the tests in a virtual environment," echo " If no virtualenv is found, the script will ask if you would like to create one. If you " echo " prefer to run tests NOT in a virtual environment, simply pass the -N option." - echo "" - echo "Note: with no options specified, the script will run the pep8 check after completing the tests." - echo " If you prefer not to run pep8, simply pass the -P option." exit } -only_run_flag=0 -only_run="" function process_option { - if [ $only_run_flag -eq 1 ]; then - only_run_flag=0 - only_run=$1 - return - else - case "$1" in - -h|--help) usage;; - -V|--virtual-env) always_venv=1; never_venv=0;; - -N|--no-virtual-env) always_venv=0; never_venv=1;; - -O|--only) only_run_flag=1;; - -f|--force) force=1;; - -P|--skip-pep8) skip_pep8=1;; - -p|--pep8) just_pep8=1;; - -l|--pylint) just_pylint=1;; - -j|--json) just_json=1;; - -c|--with-coverage) coverage=1;; - -*) addlopts="$addlopts $1";; - *) addlargs="$addlargs $1" - esac - fi + case "$1" in + -h|--help) usage;; + -V|--virtual-env) always_venv=1; never_venv=0;; + -N|--no-virtual-env) always_venv=0; never_venv=1;; + -r|--recreate-db) recreate_db=1;; + -n|--no-recreate-db) recreate_db=0;; + -f|--force) force=1;; + -p|--pep8) just_pep8=1;; + -P|--no-pep8) no_pep8=1;; + -c|--coverage) coverage=1;; + -*) noseopts="$noseopts $1";; + *) noseargs="$noseargs $1" + esac } venv=.venv @@ -69,29 +45,54 @@ with_venv=tools/with_venv.sh always_venv=0 never_venv=0 force=0 -addlargs= -addlopts= +noseargs= +noseopts= wrapper="" just_pep8=0 -skip_pep8=0 -just_pylint=0 -just_json=0 +no_pep8=0 coverage=0 +recreate_db=1 for arg in "$@"; do process_option $arg done -# If enabled, tell nose/unittest to collect coverage data +# If enabled, tell nose to collect coverage data if [ $coverage -eq 1 ]; then - addlopts="$addlopts --with-coverage --cover-package=keystone" + noseopts="$noseopts --with-coverage --cover-package=keystone" fi -if [ "x$only_run" = "x" ]; then - RUNTESTS="python run_tests.py$addlopts$addlargs" -else - RUNTESTS="python run_tests.py$addlopts$addlargs -O $only_run" -fi +function run_tests { + # Just run the test suites in current environment + ${wrapper} $NOSETESTS 2> run_tests.log + # If we get some short import error right away, print the error log directly + RESULT=$? + if [ "$RESULT" -ne "0" ]; + then + ERRSIZE=`wc -l run_tests.log | awk '{print \$1}'` + if [ "$ERRSIZE" -lt "40" ]; + then + cat run_tests.log + fi + fi + return $RESULT +} + +function run_pep8 { + echo "Running pep8 ..." + # Opt-out files from pep8 + ignore_scripts="*.sh:" + ignore_files="*eventlet-patch:*pip-requires" + ignore_dirs="*ajaxterm*" + GLOBIGNORE="$ignore_scripts:$ignore_files:$ignore_dirs" + srcfiles=`find bin -type f ! -name .*.swp` + srcfiles+=" keystone" + # Just run PEP8 in current environment + ${wrapper} pep8 --repeat --show-pep8 --show-source \ + --exclude=vcsversion.py ${srcfiles} | tee pep8.txt +} + +NOSETESTS="python run_tests.py $noseopts $noseargs" if [ $never_venv -eq 0 ] then @@ -119,51 +120,6 @@ then fi fi -function run_tests { - # Just run the test suites in current environment - ${wrapper} $RUNTESTS 2> run_tests.log - # If we get some short import error right away, print the error log directly - RESULT=$? - if [ "$RESULT" -ne "0" ]; - then - ERRSIZE=`wc -l run_tests.log | awk '{print \$1}'` - if [ "$ERRSIZE" -lt "40" ]; - then - cat run_tests.log - fi - fi - return $RESULT -} - -function run_pep8 { - echo "Running pep8 ..." - # Opt-out files from pep8 - ignore_scripts="*.sh" - ignore_files="*eventlet-patch,*pip-requires,*.log" - ignore_dirs="*ajaxterm*" - GLOBIGNORE="$ignore_scripts,$ignore_files,$ignore_dirs" - srcfiles=`find bin -type f -not -name "*.log" -not -name "*.db"` - srcfiles+=" keystone examples tools setup.py run_tests.py" - # Just run PEP8 in current environment - ${wrapper} pep8 --repeat --show-pep8 --show-source \ - --exclude=vcsversion.py,$GLOBIGNORE ${srcfiles} -} - -function run_pylint { - echo "Running pylint ..." - PYLINT_OPTIONS="--rcfile=pylintrc --output-format=parseable" - PYLINT_INCLUDE="keystone" - echo "Pylint messages count: " - pylint $PYLINT_OPTIONS $PYLINT_INCLUDE | grep 'keystone/' | wc -l - echo "Run 'pylint $PYLINT_OPTIONS $PYLINT_INCLUDE' for a full report." -} - -function validate_json { - echo "Validating JSON..." - python tools/validate_json.py -} - - # Delete old coverage data from previous runs if [ $coverage -eq 1 ]; then ${wrapper} coverage erase @@ -174,28 +130,23 @@ if [ $just_pep8 -eq 1 ]; then exit fi -if [ $just_pylint -eq 1 ]; then - run_pylint - exit +if [ $recreate_db -eq 1 ]; then + rm -f tests.sqlite fi -if [ $just_json -eq 1 ]; then - validate_json - exit -fi - - run_tests -if [ $skip_pep8 -eq 0 ]; then - # Run the pep8 check + +# NOTE(sirp): we only want to run pep8 when we're running the full-test suite, +# not when we're running tests individually. To handle this, we need to +# distinguish between options (noseopts), which begin with a '-', and +# arguments (noseargs). +if [ -z "$noseargs" ]; then + if [ $no_pep8 -eq 0 ]; then run_pep8 + fi fi -# Since we run multiple test suites, we need to execute 'coverage combine' if [ $coverage -eq 1 ]; then echo "Generating coverage report in covhtml/" - ${wrapper} coverage combine ${wrapper} coverage html -d covhtml -i - ${wrapper} coverage report --omit='/usr*,keystone/test*,.,setup.py,*egg*,/Library*,*.xml,*.tpl' fi - diff --git a/setup.py b/setup.py index 89fa3f656a..127867a8b0 100755 --- a/setup.py +++ b/setup.py @@ -1,75 +1,15 @@ -#!/usr/bin/python -# Copyright (c) 2010-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. - -from keystone import version -import os -import subprocess -import sys - from setuptools import setup, find_packages -cmdclass = {} -# If Sphinx is installed on the box running setup.py, -# enable setup.py to build the documentation, otherwise, -# just ignore it -try: - from sphinx.setup_command import BuildDoc - - class local_BuildDoc(BuildDoc): - def run(self): - base_dir = os.path.dirname(os.path.abspath(__file__)) - subprocess.Popen(["python", "generate_autodoc_index.py"], - cwd=os.path.join(base_dir, "doc")).communicate() - for builder in ['html', 'man']: - self.builder = builder - self.finalize_options() - BuildDoc.run(self) - cmdclass['build_sphinx'] = local_BuildDoc - -except: - pass - -setup( - name='keystone', - version=version.canonical_version(), - description="Authentication service - proposed for OpenStack", - license='Apache License (2.0)', - classifiers=["Programming Language :: Python"], - keywords='identity auth authentication openstack', - author='OpenStack, LLC.', - author_email='openstack@lists.launchpad.net', - url='http://www.openstack.org', - include_package_data=True, - packages=find_packages(exclude=['test', 'bin']), - scripts=['bin/keystone', 'bin/keystone-auth', 'bin/keystone-admin', - 'bin/keystone-manage', 'bin/keystone-import', - 'bin/keystone-control'], - zip_safe=False, - cmdclass=cmdclass, - tests_require=['nose', 'unittest2', 'webtest', 'mox', 'pylint', 'pep8'], - test_suite='keystone.test.runtests', - entry_points={ - 'paste.app_factory': ['main=identity:app_factory'], - 'paste.filter_factory': [ - 'extfilter=keystone.middleware.url:filter_factory', - 'remoteauth=keystone.middleware.remoteauth:remoteauth_factory', - 'tokenauth=keystone.middleware.auth_token:filter_factory', - 'swiftauth=keystone.middleware.swift_auth:filter_factory', - 's3token=keystone.middleware.s3_token:filter_factory', - ], - }, - ) +setup(name='keystone', + version='2012.1', + description="Authentication service for OpenStack", + license='Apache License (2.0)', + author='OpenStack, LLC.', + author_email='openstack@lists.launchpad.net', + url='http://www.openstack.org', + packages=find_packages(exclude=['test', 'bin']), + scripts=['bin/keystone-all', 'bin/keystone-manage'], + zip_safe=False, + install_requires=['setuptools', 'python-keystoneclient'], + ) diff --git a/tests/backend_sql.conf b/tests/backend_sql.conf new file mode 100644 index 0000000000..cb246194b1 --- /dev/null +++ b/tests/backend_sql.conf @@ -0,0 +1,15 @@ +[sql] +connection = sqlite:///bla.db +idle_timeout = 200 +min_pool_size = 5 +max_pool_size = 10 +pool_timeout = 200 + +[identity] +driver = keystone.identity.backends.sql.Identity + +[token] +driver = keystone.token.backends.sql.Token + +[ec2] +driver = keystone.contrib.ec2.backends.sql.Ec2 diff --git a/tests/default_catalog.templates b/tests/default_catalog.templates new file mode 100644 index 0000000000..c12b5c4ca7 --- /dev/null +++ b/tests/default_catalog.templates @@ -0,0 +1,12 @@ +# config for TemplatedCatalog, using camelCase because I don't want to do +# translations for keystone compat +catalog.RegionOne.identity.publicURL = http://localhost:$(public_port)s/v2.0 +catalog.RegionOne.identity.adminURL = http://localhost:$(admin_port)s/v2.0 +catalog.RegionOne.identity.internalURL = http://localhost:$(admin_port)s/v2.0 +catalog.RegionOne.identity.name = 'Identity Service' + +# fake compute service for now to help novaclient tests work +catalog.RegionOne.compute.publicURL = http://localhost:$(compute_port)s/v1.1/$(tenant_id)s +catalog.RegionOne.compute.adminURL = http://localhost:$(compute_port)s/v1.1/$(tenant_id)s +catalog.RegionOne.compute.internalURL = http://localhost:$(compute_port)s/v1.1/$(tenant_id)s +catalog.RegionOne.compute.name = 'Compute Service' diff --git a/tests/default_fixtures.py b/tests/default_fixtures.py new file mode 100644 index 0000000000..6d455bdf66 --- /dev/null +++ b/tests/default_fixtures.py @@ -0,0 +1,20 @@ +TENANTS = [ + {'id': 'bar', 'name': 'BAR'}, + {'id': 'baz', 'name': 'BAZ'}, + ] + +# NOTE(ja): a role of keystone_admin and attribute "is_admin" is done in setUp +USERS = [ + {'id': 'foo', 'name': 'FOO', 'password': 'foo2', 'tenants': ['bar',]}, + {'id': 'two', 'name': 'TWO', 'password': 'two2', 'tenants': ['baz',]}, + ] + +METADATA = [ + {'user_id': 'foo', 'tenant_id': 'bar', 'extra': 'extra'}, + {'user_id': 'two', 'tenant_id': 'baz', 'extra': 'extra'}, + ] + +ROLES = [ + {'id': 'keystone_admin', 'name': 'Keystone Admin'}, + {'id': 'useless', 'name': 'Useless'}, + ] diff --git a/tests/legacy_d5.mysql b/tests/legacy_d5.mysql new file mode 100644 index 0000000000..57b31febe5 --- /dev/null +++ b/tests/legacy_d5.mysql @@ -0,0 +1,281 @@ +-- MySQL dump 10.13 Distrib 5.1.54, for debian-linux-gnu (x86_64) +-- +-- Host: localhost Database: keystone +-- ------------------------------------------------------ +-- Server version 5.1.54-1ubuntu4 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `credentials` +-- + +DROP TABLE IF EXISTS `credentials`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `credentials` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `user_id` int(11) DEFAULT NULL, + `tenant_id` int(11) DEFAULT NULL, + `type` varchar(20) DEFAULT NULL, + `key` varchar(255) DEFAULT NULL, + `secret` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `tenant_id` (`tenant_id`), + KEY `user_id` (`user_id`) +) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `credentials` +-- + +LOCK TABLES `credentials` WRITE; +/*!40000 ALTER TABLE `credentials` DISABLE KEYS */; +INSERT INTO `credentials` VALUES (1,1,1,'EC2','admin','secrete'),(2,2,2,'EC2','demo','secrete'); +/*!40000 ALTER TABLE `credentials` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `endpoint_templates` +-- + +DROP TABLE IF EXISTS `endpoint_templates`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `endpoint_templates` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `region` varchar(255) DEFAULT NULL, + `service_id` int(11) DEFAULT NULL, + `public_url` varchar(2000) DEFAULT NULL, + `admin_url` varchar(2000) DEFAULT NULL, + `internal_url` varchar(2000) DEFAULT NULL, + `enabled` tinyint(1) DEFAULT NULL, + `is_global` tinyint(1) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `service_id` (`service_id`) +) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `endpoint_templates` +-- + +LOCK TABLES `endpoint_templates` WRITE; +/*!40000 ALTER TABLE `endpoint_templates` DISABLE KEYS */; +INSERT INTO `endpoint_templates` VALUES (1,'RegionOne',1,'http://10.4.128.10:8774/v1.1/%tenant_id%','http://10.4.128.10:8774/v1.1/%tenant_id%','http://10.4.128.10:8774/v1.1/%tenant_id%',1,1),(2,'RegionOne',2,'http://10.4.128.10:9292/v1.1/%tenant_id%','http://10.4.128.10:9292/v1.1/%tenant_id%','http://10.4.128.10:9292/v1.1/%tenant_id%',1,1),(3,'RegionOne',3,'http://10.4.128.10:5000/v2.0','http://10.4.128.10:35357/v2.0','http://10.4.128.10:5000/v2.0',1,1); +/*!40000 ALTER TABLE `endpoint_templates` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `endpoints` +-- + +DROP TABLE IF EXISTS `endpoints`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `endpoints` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `tenant_id` int(11) DEFAULT NULL, + `endpoint_template_id` int(11) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `endpoint_template_id` (`endpoint_template_id`,`tenant_id`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `endpoints` +-- + +LOCK TABLES `endpoints` WRITE; +/*!40000 ALTER TABLE `endpoints` DISABLE KEYS */; +/*!40000 ALTER TABLE `endpoints` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `roles` +-- + +DROP TABLE IF EXISTS `roles`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `roles` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(255) DEFAULT NULL, + `desc` varchar(255) DEFAULT NULL, + `service_id` int(11) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `name` (`name`,`service_id`), + KEY `service_id` (`service_id`) +) ENGINE=MyISAM AUTO_INCREMENT=5 DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `roles` +-- + +LOCK TABLES `roles` WRITE; +/*!40000 ALTER TABLE `roles` DISABLE KEYS */; +INSERT INTO `roles` VALUES (1,'Admin',NULL,NULL),(2,'Member',NULL,NULL),(3,'KeystoneAdmin',NULL,NULL),(4,'KeystoneServiceAdmin',NULL,NULL); +/*!40000 ALTER TABLE `roles` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `services` +-- + +DROP TABLE IF EXISTS `services`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `services` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(255) DEFAULT NULL, + `type` varchar(255) DEFAULT NULL, + `desc` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `name` (`name`) +) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `services` +-- + +LOCK TABLES `services` WRITE; +/*!40000 ALTER TABLE `services` DISABLE KEYS */; +INSERT INTO `services` VALUES (1,'nova','compute','Nova Compute Service'),(2,'glance','image','Glance Image Service'),(3,'keystone','identity','Keystone Identity Service'); +/*!40000 ALTER TABLE `services` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `tenants` +-- + +DROP TABLE IF EXISTS `tenants`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `tenants` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(255) DEFAULT NULL, + `desc` varchar(255) DEFAULT NULL, + `enabled` int(11) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `name` (`name`) +) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `tenants` +-- + +LOCK TABLES `tenants` WRITE; +/*!40000 ALTER TABLE `tenants` DISABLE KEYS */; +INSERT INTO `tenants` VALUES (1,'admin',NULL,1),(2,'demo',NULL,1),(3,'invisible_to_admin',NULL,1); +/*!40000 ALTER TABLE `tenants` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `token` +-- + +DROP TABLE IF EXISTS `token`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `token` ( + `id` varchar(255) NOT NULL, + `user_id` int(11) DEFAULT NULL, + `tenant_id` int(11) DEFAULT NULL, + `expires` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `id` (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `token` +-- + +LOCK TABLES `token` WRITE; +/*!40000 ALTER TABLE `token` DISABLE KEYS */; +INSERT INTO `token` VALUES ('secrete',1,1,'2015-02-05 00:00:00'); +/*!40000 ALTER TABLE `token` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `user_roles` +-- + +DROP TABLE IF EXISTS `user_roles`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `user_roles` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `user_id` int(11) DEFAULT NULL, + `role_id` int(11) DEFAULT NULL, + `tenant_id` int(11) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `user_id` (`user_id`,`role_id`,`tenant_id`), + KEY `tenant_id` (`tenant_id`), + KEY `role_id` (`role_id`) +) ENGINE=MyISAM AUTO_INCREMENT=8 DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `user_roles` +-- + +LOCK TABLES `user_roles` WRITE; +/*!40000 ALTER TABLE `user_roles` DISABLE KEYS */; +INSERT INTO `user_roles` VALUES (1,1,1,1),(2,2,2,2),(3,2,2,3),(4,1,1,2),(5,1,1,NULL),(6,1,3,NULL),(7,1,4,NULL); +/*!40000 ALTER TABLE `user_roles` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `users` +-- + +DROP TABLE IF EXISTS `users`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `users` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(255) DEFAULT NULL, + `password` varchar(255) DEFAULT NULL, + `email` varchar(255) DEFAULT NULL, + `enabled` int(11) DEFAULT NULL, + `tenant_id` int(11) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `name` (`name`), + KEY `tenant_id` (`tenant_id`) +) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `users` +-- + +LOCK TABLES `users` WRITE; +/*!40000 ALTER TABLE `users` DISABLE KEYS */; +INSERT INTO `users` VALUES (1,'admin','secrete',NULL,1,NULL),(2,'demo','secrete',NULL,1,NULL); +/*!40000 ALTER TABLE `users` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2012-02-14 0:16:40 diff --git a/tests/legacy_d5.sqlite b/tests/legacy_d5.sqlite new file mode 100644 index 0000000000..d96dbf40fe --- /dev/null +++ b/tests/legacy_d5.sqlite @@ -0,0 +1,277 @@ +begin; +-- MySQL dump 10.13 Distrib 5.1.54, for debian-linux-gnu (x86_64) +-- +-- Host: localhost Database: keystone +-- ------------------------------------------------------ +-- Server version 5.1.54-1ubuntu4 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `credentials` +-- + +DROP TABLE IF EXISTS `credentials`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `credentials` ( + `id` integer NOT NULL primary key autoincrement, + `user_id` integer NULL, + `tenant_id` integer NULL, + `type` varchar(20) NULL, + `key` varchar(255) NULL, + `secret` varchar(255) NULL +) ; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `credentials` +-- + + +/*!40000 ALTER TABLE `credentials` DISABLE KEYS */; +INSERT INTO `credentials` VALUES (1,1,1,'EC2','admin','secrete'); +INSERT INTO `credentials` VALUES (2,2,2,'EC2','demo','secrete'); +/*!40000 ALTER TABLE `credentials` ENABLE KEYS */; + + +-- +-- Table structure for table `endpoint_templates` +-- + +DROP TABLE IF EXISTS `endpoint_templates`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `endpoint_templates` ( + `id` integer NOT NULL primary key autoincrement, + `region` varchar(255) NULL, + `service_id` integer NULL, + `public_url` varchar(2000) NULL, + `admin_url` varchar(2000) NULL, + `internal_url` varchar(2000) NULL, + `enabled` integer NULL, + `is_global` integer NULL +) ; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `endpoint_templates` +-- + + +/*!40000 ALTER TABLE `endpoint_templates` DISABLE KEYS */; +INSERT INTO `endpoint_templates` VALUES (1,'RegionOne',1,'http://10.4.128.10:8774/v1.1/%tenant_id%','http://10.4.128.10:8774/v1.1/%tenant_id%','http://10.4.128.10:8774/v1.1/%tenant_id%',1,1); +INSERT INTO `endpoint_templates` VALUES (2,'RegionOne',2,'http://10.4.128.10:9292/v1.1/%tenant_id%','http://10.4.128.10:9292/v1.1/%tenant_id%','http://10.4.128.10:9292/v1.1/%tenant_id%',1,1); +INSERT INTO `endpoint_templates` VALUES (3,'RegionOne',3,'http://10.4.128.10:5000/v2.0','http://10.4.128.10:35357/v2.0','http://10.4.128.10:5000/v2.0',1,1); +/*!40000 ALTER TABLE `endpoint_templates` ENABLE KEYS */; + + +-- +-- Table structure for table `endpoints` +-- + +DROP TABLE IF EXISTS `endpoints`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `endpoints` ( + `id` integer NOT NULL primary key autoincrement, + `tenant_id` integer NULL, + `endpoint_template_id` integer NULL +) ; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `endpoints` +-- + + +/*!40000 ALTER TABLE `endpoints` DISABLE KEYS */; +/*!40000 ALTER TABLE `endpoints` ENABLE KEYS */; + + +-- +-- Table structure for table `roles` +-- + +DROP TABLE IF EXISTS `roles`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `roles` ( + `id` integer NOT NULL primary key autoincrement, + `name` varchar(255) NULL, + `desc` varchar(255) NULL, + `service_id` integer NULL +) ; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `roles` +-- + + +/*!40000 ALTER TABLE `roles` DISABLE KEYS */; +INSERT INTO `roles` VALUES (1,'Admin',NULL,NULL); +INSERT INTO `roles` VALUES (2,'Member',NULL,NULL); +INSERT INTO `roles` VALUES (3,'KeystoneAdmin',NULL,NULL); +INSERT INTO `roles` VALUES (4,'KeystoneServiceAdmin',NULL,NULL); +/*!40000 ALTER TABLE `roles` ENABLE KEYS */; + + +-- +-- Table structure for table `services` +-- + +DROP TABLE IF EXISTS `services`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `services` ( + `id` integer NOT NULL primary key autoincrement, + `name` varchar(255) NULL, + `type` varchar(255) NULL, + `desc` varchar(255) NULL +) ; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `services` +-- + + +/*!40000 ALTER TABLE `services` DISABLE KEYS */; +INSERT INTO `services` VALUES (1,'nova','compute','Nova Compute Service'); +INSERT INTO `services` VALUES (2,'glance','image','Glance Image Service'); +INSERT INTO `services` VALUES (3,'keystone','identity','Keystone Identity Service'); +/*!40000 ALTER TABLE `services` ENABLE KEYS */; + + +-- +-- Table structure for table `tenants` +-- + +DROP TABLE IF EXISTS `tenants`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `tenants` ( + `id` integer NOT NULL primary key autoincrement, + `name` varchar(255) NULL, + `desc` varchar(255) NULL, + `enabled` integer NULL +) ; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `tenants` +-- + + +/*!40000 ALTER TABLE `tenants` DISABLE KEYS */; +INSERT INTO `tenants` VALUES (1,'admin',NULL,1); +INSERT INTO `tenants` VALUES (2,'demo',NULL,1); +INSERT INTO `tenants` VALUES (3,'invisible_to_admin',NULL,1); +/*!40000 ALTER TABLE `tenants` ENABLE KEYS */; + + +-- +-- Table structure for table `token` +-- + +DROP TABLE IF EXISTS `token`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `token` ( + `id` varchar(255) NOT NULL, + `user_id` integer NULL, + `tenant_id` integer NULL, + `expires` datetime NULL +) ; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `token` +-- + + +/*!40000 ALTER TABLE `token` DISABLE KEYS */; +INSERT INTO `token` VALUES ('secrete',1,1,'2015-02-05 00:00:00'); +/*!40000 ALTER TABLE `token` ENABLE KEYS */; + + +-- +-- Table structure for table `user_roles` +-- + +DROP TABLE IF EXISTS `user_roles`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `user_roles` ( + `id` integer NOT NULL primary key autoincrement, + `user_id` integer NULL, + `role_id` integer NULL, + `tenant_id` integer NULL +) ; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `user_roles` +-- + + +/*!40000 ALTER TABLE `user_roles` DISABLE KEYS */; +INSERT INTO `user_roles` VALUES (1,1,1,1); +INSERT INTO `user_roles` VALUES (2,2,2,2); +INSERT INTO `user_roles` VALUES (3,2,2,3); +INSERT INTO `user_roles` VALUES (4,1,1,2); +INSERT INTO `user_roles` VALUES (5,1,1,NULL); +INSERT INTO `user_roles` VALUES (6,1,3,NULL); +INSERT INTO `user_roles` VALUES (7,1,4,NULL); +/*!40000 ALTER TABLE `user_roles` ENABLE KEYS */; + + +-- +-- Table structure for table `users` +-- + +DROP TABLE IF EXISTS `users`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `users` ( + `id` integer NOT NULL primary key autoincrement, + `name` varchar(255) NULL, + `password` varchar(255) NULL, + `email` varchar(255) NULL, + `enabled` integer NULL, + `tenant_id` integer NULL +) ; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `users` +-- + + +/*!40000 ALTER TABLE `users` DISABLE KEYS */; +INSERT INTO `users` VALUES (1,'admin','secrete',NULL,1,NULL); +INSERT INTO `users` VALUES (2,'demo','secrete',NULL,1,NULL); +/*!40000 ALTER TABLE `users` ENABLE KEYS */; + +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2012-02-14 0:16:40 +commit; diff --git a/tests/legacy_diablo.mysql b/tests/legacy_diablo.mysql new file mode 100644 index 0000000000..543f439f8a --- /dev/null +++ b/tests/legacy_diablo.mysql @@ -0,0 +1,281 @@ +-- MySQL dump 10.13 Distrib 5.1.58, for debian-linux-gnu (x86_64) +-- +-- Host: localhost Database: keystone +-- ------------------------------------------------------ +-- Server version 5.1.58-1ubuntu1 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `credentials` +-- + +DROP TABLE IF EXISTS `credentials`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `credentials` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `user_id` int(11) DEFAULT NULL, + `tenant_id` int(11) DEFAULT NULL, + `type` varchar(20) DEFAULT NULL, + `key` varchar(255) DEFAULT NULL, + `secret` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `tenant_id` (`tenant_id`), + KEY `user_id` (`user_id`) +) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `credentials` +-- + +LOCK TABLES `credentials` WRITE; +/*!40000 ALTER TABLE `credentials` DISABLE KEYS */; +INSERT INTO `credentials` VALUES (1,1,1,'EC2','admin','secrete'),(2,2,2,'EC2','demo','secrete'); +/*!40000 ALTER TABLE `credentials` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `endpoint_templates` +-- + +DROP TABLE IF EXISTS `endpoint_templates`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `endpoint_templates` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `region` varchar(255) DEFAULT NULL, + `service_id` int(11) DEFAULT NULL, + `public_url` varchar(2000) DEFAULT NULL, + `admin_url` varchar(2000) DEFAULT NULL, + `internal_url` varchar(2000) DEFAULT NULL, + `enabled` tinyint(1) DEFAULT NULL, + `is_global` tinyint(1) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `service_id` (`service_id`) +) ENGINE=MyISAM AUTO_INCREMENT=5 DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `endpoint_templates` +-- + +LOCK TABLES `endpoint_templates` WRITE; +/*!40000 ALTER TABLE `endpoint_templates` DISABLE KEYS */; +INSERT INTO `endpoint_templates` VALUES (1,'RegionOne',1,'http://192.168.2.10:8774/v1.1/%tenant_id%','http://192.168.2.10:8774/v1.1/%tenant_id%','http://192.168.2.10:8774/v1.1/%tenant_id%',1,1),(2,'RegionOne',2,'http://192.168.2.10:9292/v1','http://192.168.2.10:9292/v1','http://192.168.2.10:9292/v1',1,1),(3,'RegionOne',3,'http://192.168.2.10:5000/v2.0','http://192.168.2.10:35357/v2.0','http://192.168.2.10:5000/v2.0',1,1),(4,'RegionOne',4,'http://192.168.2.10:8080/v1/AUTH_%tenant_id%','http://192.168.2.10:8080/','http://192.168.2.10:8080/v1/AUTH_%tenant_id%',1,1); +/*!40000 ALTER TABLE `endpoint_templates` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `endpoints` +-- + +DROP TABLE IF EXISTS `endpoints`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `endpoints` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `tenant_id` int(11) DEFAULT NULL, + `endpoint_template_id` int(11) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `endpoint_template_id` (`endpoint_template_id`,`tenant_id`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `endpoints` +-- + +LOCK TABLES `endpoints` WRITE; +/*!40000 ALTER TABLE `endpoints` DISABLE KEYS */; +/*!40000 ALTER TABLE `endpoints` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `roles` +-- + +DROP TABLE IF EXISTS `roles`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `roles` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(255) DEFAULT NULL, + `desc` varchar(255) DEFAULT NULL, + `service_id` int(11) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `name` (`name`,`service_id`), + KEY `service_id` (`service_id`) +) ENGINE=MyISAM AUTO_INCREMENT=7 DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `roles` +-- + +LOCK TABLES `roles` WRITE; +/*!40000 ALTER TABLE `roles` DISABLE KEYS */; +INSERT INTO `roles` VALUES (1,'Admin',NULL,NULL),(2,'Member',NULL,NULL),(3,'KeystoneAdmin',NULL,NULL),(4,'KeystoneServiceAdmin',NULL,NULL),(5,'sysadmin',NULL,NULL),(6,'netadmin',NULL,NULL); +/*!40000 ALTER TABLE `roles` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `services` +-- + +DROP TABLE IF EXISTS `services`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `services` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(255) DEFAULT NULL, + `type` varchar(255) DEFAULT NULL, + `desc` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `name` (`name`) +) ENGINE=MyISAM AUTO_INCREMENT=5 DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `services` +-- + +LOCK TABLES `services` WRITE; +/*!40000 ALTER TABLE `services` DISABLE KEYS */; +INSERT INTO `services` VALUES (1,'nova','compute','Nova Compute Service'),(2,'glance','image','Glance Image Service'),(3,'keystone','identity','Keystone Identity Service'),(4,'swift','object-store','Swift Service'); +/*!40000 ALTER TABLE `services` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `tenants` +-- + +DROP TABLE IF EXISTS `tenants`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `tenants` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(255) DEFAULT NULL, + `desc` varchar(255) DEFAULT NULL, + `enabled` int(11) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `name` (`name`) +) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `tenants` +-- + +LOCK TABLES `tenants` WRITE; +/*!40000 ALTER TABLE `tenants` DISABLE KEYS */; +INSERT INTO `tenants` VALUES (1,'admin',NULL,1),(2,'demo',NULL,1),(3,'invisible_to_admin',NULL,1); +/*!40000 ALTER TABLE `tenants` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `token` +-- + +DROP TABLE IF EXISTS `token`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `token` ( + `id` varchar(255) NOT NULL, + `user_id` int(11) DEFAULT NULL, + `tenant_id` int(11) DEFAULT NULL, + `expires` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `id` (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `token` +-- + +LOCK TABLES `token` WRITE; +/*!40000 ALTER TABLE `token` DISABLE KEYS */; +INSERT INTO `token` VALUES ('secrete',1,1,'2015-02-05 00:00:00'); +/*!40000 ALTER TABLE `token` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `user_roles` +-- + +DROP TABLE IF EXISTS `user_roles`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `user_roles` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `user_id` int(11) DEFAULT NULL, + `role_id` int(11) DEFAULT NULL, + `tenant_id` int(11) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `user_id` (`user_id`,`role_id`,`tenant_id`), + KEY `tenant_id` (`tenant_id`), + KEY `role_id` (`role_id`) +) ENGINE=MyISAM AUTO_INCREMENT=10 DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `user_roles` +-- + +LOCK TABLES `user_roles` WRITE; +/*!40000 ALTER TABLE `user_roles` DISABLE KEYS */; +INSERT INTO `user_roles` VALUES (1,1,1,1),(2,2,2,2),(3,2,5,2),(4,2,6,2),(5,2,2,3),(6,1,1,2),(7,1,1,NULL),(8,1,3,NULL),(9,1,4,NULL); +/*!40000 ALTER TABLE `user_roles` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `users` +-- + +DROP TABLE IF EXISTS `users`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `users` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(255) DEFAULT NULL, + `password` varchar(255) DEFAULT NULL, + `email` varchar(255) DEFAULT NULL, + `enabled` int(11) DEFAULT NULL, + `tenant_id` int(11) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `name` (`name`), + KEY `tenant_id` (`tenant_id`) +) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `users` +-- + +LOCK TABLES `users` WRITE; +/*!40000 ALTER TABLE `users` DISABLE KEYS */; +INSERT INTO `users` VALUES (1,'admin','secrete',NULL,1,NULL),(2,'demo','secrete',NULL,1,NULL); +/*!40000 ALTER TABLE `users` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2012-02-13 17:30:03 diff --git a/tests/legacy_diablo.sqlite b/tests/legacy_diablo.sqlite new file mode 100644 index 0000000000..edf15be4c7 --- /dev/null +++ b/tests/legacy_diablo.sqlite @@ -0,0 +1,283 @@ +begin; +-- MySQL dump 10.13 Distrib 5.1.58, for debian-linux-gnu (x86_64) +-- +-- Host: localhost Database: keystone +-- ------------------------------------------------------ +-- Server version 5.1.58-1ubuntu1 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `credentials` +-- + +DROP TABLE IF EXISTS `credentials`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `credentials` ( + `id` integer NOT NULL primary key autoincrement, + `user_id` integer NULL, + `tenant_id` integer NULL, + `type` varchar(20) NULL, + `key` varchar(255) NULL, + `secret` varchar(255) NULL +) ; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `credentials` +-- + + +/*!40000 ALTER TABLE `credentials` DISABLE KEYS */; +INSERT INTO `credentials` VALUES (1,1,1,'EC2','admin','secrete'); +INSERT INTO `credentials` VALUES (2,2,2,'EC2','demo','secrete'); +/*!40000 ALTER TABLE `credentials` ENABLE KEYS */; + + +-- +-- Table structure for table `endpoint_templates` +-- + +DROP TABLE IF EXISTS `endpoint_templates`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `endpoint_templates` ( + `id` integer NOT NULL primary key autoincrement, + `region` varchar(255) NULL, + `service_id` integer NULL, + `public_url` varchar(2000) NULL, + `admin_url` varchar(2000) NULL, + `internal_url` varchar(2000) NULL, + `enabled` integer NULL, + `is_global` integer NULL +) ; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `endpoint_templates` +-- + + +/*!40000 ALTER TABLE `endpoint_templates` DISABLE KEYS */; +INSERT INTO `endpoint_templates` VALUES (1,'RegionOne',1,'http://192.168.2.10:8774/v1.1/%tenant_id%','http://192.168.2.10:8774/v1.1/%tenant_id%','http://192.168.2.10:8774/v1.1/%tenant_id%',1,1); +INSERT INTO `endpoint_templates` VALUES (2,'RegionOne',2,'http://192.168.2.10:9292/v1','http://192.168.2.10:9292/v1','http://192.168.2.10:9292/v1',1,1); +INSERT INTO `endpoint_templates` VALUES (3,'RegionOne',3,'http://192.168.2.10:5000/v2.0','http://192.168.2.10:35357/v2.0','http://192.168.2.10:5000/v2.0',1,1); +INSERT INTO `endpoint_templates` VALUES (4,'RegionOne',4,'http://192.168.2.10:8080/v1/AUTH_%tenant_id%','http://192.168.2.10:8080/','http://192.168.2.10:8080/v1/AUTH_%tenant_id%',1,1); +/*!40000 ALTER TABLE `endpoint_templates` ENABLE KEYS */; + + +-- +-- Table structure for table `endpoints` +-- + +DROP TABLE IF EXISTS `endpoints`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `endpoints` ( + `id` integer NOT NULL primary key autoincrement, + `tenant_id` integer NULL, + `endpoint_template_id` integer NULL +) ; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `endpoints` +-- + + +/*!40000 ALTER TABLE `endpoints` DISABLE KEYS */; +/*!40000 ALTER TABLE `endpoints` ENABLE KEYS */; + + +-- +-- Table structure for table `roles` +-- + +DROP TABLE IF EXISTS `roles`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `roles` ( + `id` integer NOT NULL primary key autoincrement, + `name` varchar(255) NULL, + `desc` varchar(255) NULL, + `service_id` integer NULL +) ; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `roles` +-- + + +/*!40000 ALTER TABLE `roles` DISABLE KEYS */; +INSERT INTO `roles` VALUES (1,'Admin',NULL,NULL); +INSERT INTO `roles` VALUES (2,'Member',NULL,NULL); +INSERT INTO `roles` VALUES (3,'KeystoneAdmin',NULL,NULL); +INSERT INTO `roles` VALUES (4,'KeystoneServiceAdmin',NULL,NULL); +INSERT INTO `roles` VALUES (5,'sysadmin',NULL,NULL); +INSERT INTO `roles` VALUES (6,'netadmin',NULL,NULL); +/*!40000 ALTER TABLE `roles` ENABLE KEYS */; + + +-- +-- Table structure for table `services` +-- + +DROP TABLE IF EXISTS `services`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `services` ( + `id` integer NOT NULL primary key autoincrement, + `name` varchar(255) NULL, + `type` varchar(255) NULL, + `desc` varchar(255) NULL +) ; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `services` +-- + + +/*!40000 ALTER TABLE `services` DISABLE KEYS */; +INSERT INTO `services` VALUES (1,'nova','compute','Nova Compute Service'); +INSERT INTO `services` VALUES (2,'glance','image','Glance Image Service'); +INSERT INTO `services` VALUES (3,'keystone','identity','Keystone Identity Service'); +INSERT INTO `services` VALUES (4,'swift','object-store','Swift Service'); +/*!40000 ALTER TABLE `services` ENABLE KEYS */; + + +-- +-- Table structure for table `tenants` +-- + +DROP TABLE IF EXISTS `tenants`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `tenants` ( + `id` integer NOT NULL primary key autoincrement, + `name` varchar(255) NULL, + `desc` varchar(255) NULL, + `enabled` integer NULL +) ; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `tenants` +-- + + +/*!40000 ALTER TABLE `tenants` DISABLE KEYS */; +INSERT INTO `tenants` VALUES (1,'admin',NULL,1); +INSERT INTO `tenants` VALUES (2,'demo',NULL,1); +INSERT INTO `tenants` VALUES (3,'invisible_to_admin',NULL,1); +/*!40000 ALTER TABLE `tenants` ENABLE KEYS */; + + +-- +-- Table structure for table `token` +-- + +DROP TABLE IF EXISTS `token`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `token` ( + `id` varchar(255) NOT NULL, + `user_id` integer NULL, + `tenant_id` integer NULL, + `expires` datetime NULL +) ; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `token` +-- + + +/*!40000 ALTER TABLE `token` DISABLE KEYS */; +INSERT INTO `token` VALUES ('secrete',1,1,'2015-02-05 00:00:00'); +/*!40000 ALTER TABLE `token` ENABLE KEYS */; + + +-- +-- Table structure for table `user_roles` +-- + +DROP TABLE IF EXISTS `user_roles`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `user_roles` ( + `id` integer NOT NULL primary key autoincrement, + `user_id` integer NULL, + `role_id` integer NULL, + `tenant_id` integer NULL +) ; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `user_roles` +-- + + +/*!40000 ALTER TABLE `user_roles` DISABLE KEYS */; +INSERT INTO `user_roles` VALUES (1,1,1,1); +INSERT INTO `user_roles` VALUES (2,2,2,2); +INSERT INTO `user_roles` VALUES (3,2,5,2); +INSERT INTO `user_roles` VALUES (4,2,6,2); +INSERT INTO `user_roles` VALUES (5,2,2,3); +INSERT INTO `user_roles` VALUES (6,1,1,2); +INSERT INTO `user_roles` VALUES (7,1,1,NULL); +INSERT INTO `user_roles` VALUES (8,1,3,NULL); +INSERT INTO `user_roles` VALUES (9,1,4,NULL); +/*!40000 ALTER TABLE `user_roles` ENABLE KEYS */; + + +-- +-- Table structure for table `users` +-- + +DROP TABLE IF EXISTS `users`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `users` ( + `id` integer NOT NULL primary key autoincrement, + `name` varchar(255) NULL, + `password` varchar(255) NULL, + `email` varchar(255) NULL, + `enabled` integer NULL, + `tenant_id` integer NULL +) ; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `users` +-- + + +/*!40000 ALTER TABLE `users` DISABLE KEYS */; +INSERT INTO `users` VALUES (1,'admin','secrete',NULL,1,NULL); +INSERT INTO `users` VALUES (2,'demo','secrete',NULL,1,NULL); +/*!40000 ALTER TABLE `users` ENABLE KEYS */; + +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2012-02-13 17:30:03 +commit; diff --git a/tests/legacy_essex.mysql b/tests/legacy_essex.mysql new file mode 100644 index 0000000000..457ba7e984 --- /dev/null +++ b/tests/legacy_essex.mysql @@ -0,0 +1,309 @@ +-- MySQL dump 10.13 Distrib 5.1.58, for debian-linux-gnu (x86_64) +-- +-- Host: localhost Database: keystone +-- ------------------------------------------------------ +-- Server version 5.1.58-1ubuntu1 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `credentials` +-- + +DROP TABLE IF EXISTS `credentials`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `credentials` ( + `user_id` int(11) DEFAULT NULL, + `tenant_id` int(11) DEFAULT NULL, + `secret` varchar(255) DEFAULT NULL, + `key` varchar(255) DEFAULT NULL, + `type` varchar(20) DEFAULT NULL, + `id` int(11) NOT NULL AUTO_INCREMENT, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `credentials` +-- + +LOCK TABLES `credentials` WRITE; +/*!40000 ALTER TABLE `credentials` DISABLE KEYS */; +/*!40000 ALTER TABLE `credentials` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `endpoint_templates` +-- + +DROP TABLE IF EXISTS `endpoint_templates`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `endpoint_templates` ( + `is_global` tinyint(1) DEFAULT NULL, + `region` varchar(255) DEFAULT NULL, + `public_url` varchar(2000) DEFAULT NULL, + `enabled` tinyint(1) DEFAULT NULL, + `internal_url` varchar(2000) DEFAULT NULL, + `id` int(11) NOT NULL AUTO_INCREMENT, + `service_id` int(11) DEFAULT NULL, + `admin_url` varchar(2000) DEFAULT NULL, + `version_id` varchar(20) DEFAULT NULL, + `version_list` varchar(2000) DEFAULT NULL, + `version_info` varchar(500) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM AUTO_INCREMENT=6 DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `endpoint_templates` +-- + +LOCK TABLES `endpoint_templates` WRITE; +/*!40000 ALTER TABLE `endpoint_templates` DISABLE KEYS */; +INSERT INTO `endpoint_templates` VALUES (1,'RegionOne','http://4.2.2.1:8774/v1.1/%tenant_id%',1,'http://4.2.2.1:8774/v1.1/%tenant_id%',1,1,'http://4.2.2.1:8774/v1.1/%tenant_id%',NULL,NULL,NULL),(1,'RegionOne','http://4.2.2.1:8773/services/Cloud',1,'http://4.2.2.1:8773/services/Cloud',2,2,'http://4.2.2.1:8773/services/Admin',NULL,NULL,NULL),(1,'RegionOne','http://4.2.2.1:9292/v1',1,'http://4.2.2.1:9292/v1',3,3,'http://4.2.2.1:9292/v1',NULL,NULL,NULL),(1,'RegionOne','http://4.2.2.1:5000/v2.0',1,'http://4.2.2.1:5000/v2.0',4,4,'http://4.2.2.1:35357/v2.0',NULL,NULL,NULL),(1,'RegionOne','http://4.2.2.1:8080/v1/AUTH_%tenant_id%',1,'http://4.2.2.1:8080/v1/AUTH_%tenant_id%',5,5,'http://4.2.2.1:8080/',NULL,NULL,NULL); +/*!40000 ALTER TABLE `endpoint_templates` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `endpoints` +-- + +DROP TABLE IF EXISTS `endpoints`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `endpoints` ( + `endpoint_template_id` int(11) DEFAULT NULL, + `tenant_id` int(11) DEFAULT NULL, + `id` int(11) NOT NULL AUTO_INCREMENT, + PRIMARY KEY (`id`), + UNIQUE KEY `endpoint_template_id` (`endpoint_template_id`,`tenant_id`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `endpoints` +-- + +LOCK TABLES `endpoints` WRITE; +/*!40000 ALTER TABLE `endpoints` DISABLE KEYS */; +/*!40000 ALTER TABLE `endpoints` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `migrate_version` +-- + +DROP TABLE IF EXISTS `migrate_version`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `migrate_version` ( + `repository_id` varchar(250) NOT NULL, + `repository_path` text, + `version` int(11) DEFAULT NULL, + PRIMARY KEY (`repository_id`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `migrate_version` +-- + +LOCK TABLES `migrate_version` WRITE; +/*!40000 ALTER TABLE `migrate_version` DISABLE KEYS */; +INSERT INTO `migrate_version` VALUES ('Keystone','/opt/stack/keystone/keystone/backends/sqlalchemy/migrate_repo',11); +/*!40000 ALTER TABLE `migrate_version` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `roles` +-- + +DROP TABLE IF EXISTS `roles`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `roles` ( + `service_id` int(11) DEFAULT NULL, + `desc` varchar(255) DEFAULT NULL, + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `name` (`name`,`service_id`) +) ENGINE=MyISAM AUTO_INCREMENT=7 DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `roles` +-- + +LOCK TABLES `roles` WRITE; +/*!40000 ALTER TABLE `roles` DISABLE KEYS */; +INSERT INTO `roles` VALUES (NULL,NULL,1,'admin'),(NULL,NULL,2,'Member'),(NULL,NULL,3,'KeystoneAdmin'),(NULL,NULL,4,'KeystoneServiceAdmin'),(NULL,NULL,5,'sysadmin'),(NULL,NULL,6,'netadmin'); +/*!40000 ALTER TABLE `roles` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `services` +-- + +DROP TABLE IF EXISTS `services`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `services` ( + `desc` varchar(255) DEFAULT NULL, + `type` varchar(255) DEFAULT NULL, + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(255) DEFAULT NULL, + `owner_id` int(11) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `name` (`name`), + UNIQUE KEY `name_2` (`name`) +) ENGINE=MyISAM AUTO_INCREMENT=6 DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `services` +-- + +LOCK TABLES `services` WRITE; +/*!40000 ALTER TABLE `services` DISABLE KEYS */; +INSERT INTO `services` VALUES ('Nova Compute Service','compute',1,'nova',NULL),('EC2 Compatability Layer','ec2',2,'ec2',NULL),('Glance Image Service','image',3,'glance',NULL),('Keystone Identity Service','identity',4,'keystone',NULL),('Swift Service','object-store',5,'swift',NULL); +/*!40000 ALTER TABLE `services` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `tenants` +-- + +DROP TABLE IF EXISTS `tenants`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `tenants` ( + `desc` varchar(255) DEFAULT NULL, + `enabled` tinyint(1) DEFAULT NULL, + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(255) DEFAULT NULL, + `uid` varchar(255) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `tenants_uid_key` (`uid`), + UNIQUE KEY `name` (`name`), + UNIQUE KEY `name_2` (`name`) +) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `tenants` +-- + +LOCK TABLES `tenants` WRITE; +/*!40000 ALTER TABLE `tenants` DISABLE KEYS */; +INSERT INTO `tenants` VALUES (NULL,1,1,'admin','182c1fbf7eef44eda162ff3fd30c0a76'),(NULL,1,2,'demo','b1a7ea3a884f4d0685a98cd6e682a5da'),(NULL,1,3,'invisible_to_admin','f4d1eed9bb5d4d35a5f37af934f87574'); +/*!40000 ALTER TABLE `tenants` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `tokens` +-- + +DROP TABLE IF EXISTS `tokens`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `tokens` ( + `tenant_id` int(11) DEFAULT NULL, + `expires` datetime DEFAULT NULL, + `user_id` int(11) DEFAULT NULL, + `id` varchar(255) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `id` (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `tokens` +-- + +LOCK TABLES `tokens` WRITE; +/*!40000 ALTER TABLE `tokens` DISABLE KEYS */; +INSERT INTO `tokens` VALUES (1,'2015-02-05 00:00:00',1,'123123123123123123123'); +/*!40000 ALTER TABLE `tokens` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `user_roles` +-- + +DROP TABLE IF EXISTS `user_roles`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `user_roles` ( + `tenant_id` int(11) DEFAULT NULL, + `user_id` int(11) DEFAULT NULL, + `id` int(11) NOT NULL AUTO_INCREMENT, + `role_id` int(11) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `user_id` (`user_id`,`role_id`,`tenant_id`) +) ENGINE=MyISAM AUTO_INCREMENT=10 DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `user_roles` +-- + +LOCK TABLES `user_roles` WRITE; +/*!40000 ALTER TABLE `user_roles` DISABLE KEYS */; +INSERT INTO `user_roles` VALUES (1,1,1,1),(2,2,2,2),(2,2,3,5),(2,2,4,6),(3,2,5,2),(2,1,6,1),(NULL,1,7,1),(NULL,1,8,3),(NULL,1,9,4); +/*!40000 ALTER TABLE `user_roles` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `users` +-- + +DROP TABLE IF EXISTS `users`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `users` ( + `name` varchar(255) DEFAULT NULL, + `tenant_id` int(11) DEFAULT NULL, + `enabled` tinyint(1) DEFAULT NULL, + `id` int(11) NOT NULL AUTO_INCREMENT, + `password` varchar(255) DEFAULT NULL, + `email` varchar(255) DEFAULT NULL, + `uid` varchar(255) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `users_uid_key` (`uid`), + UNIQUE KEY `name` (`name`), + UNIQUE KEY `name_2` (`name`) +) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `users` +-- + +LOCK TABLES `users` WRITE; +/*!40000 ALTER TABLE `users` DISABLE KEYS */; +INSERT INTO `users` VALUES ('admin',NULL,1,1,'$6$rounds=40000$hFXlgBSMi599197d$tmGKBpoGHNRsLB3ruK9f1wPvvtfWWuMEUzdqUAynsmmYXBK6eekyNHTzzhwXTM3mWpnaMHCI4mHPOycqmPJJc0',NULL,'c93b19ea3fa94484824213db8ac0afce'),('demo',NULL,1,2,'$6$rounds=40000$RBsX2ja9fdj2uTNQ$/wJOn510AYKW9BPFAJneVQAjm6TM0Ty11LG.u4.k4RhmoUcXNSjGKmQT6KO0SsvypMM7A.doWgt73V5rNnv5h.',NULL,'04c6697e88ff4667820903fcce05d904'); +/*!40000 ALTER TABLE `users` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2012-02-13 19:23:51 diff --git a/tests/legacy_essex.sqlite b/tests/legacy_essex.sqlite new file mode 100644 index 0000000000..33d0de20c8 --- /dev/null +++ b/tests/legacy_essex.sqlite @@ -0,0 +1,313 @@ +begin; +-- MySQL dump 10.13 Distrib 5.1.58, for debian-linux-gnu (x86_64) +-- +-- Host: localhost Database: keystone +-- ------------------------------------------------------ +-- Server version 5.1.58-1ubuntu1 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `credentials` +-- + +DROP TABLE IF EXISTS `credentials`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `credentials` ( + `user_id` integer NULL, + `tenant_id` integer NULL, + `secret` varchar(255) NULL, + `key` varchar(255) NULL, + `type` varchar(20) NULL, + `id` integer NOT NULL primary key autoincrement +) ; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `credentials` +-- + + +/*!40000 ALTER TABLE `credentials` DISABLE KEYS */; +/*!40000 ALTER TABLE `credentials` ENABLE KEYS */; + + +-- +-- Table structure for table `endpoint_templates` +-- + +DROP TABLE IF EXISTS `endpoint_templates`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `endpoint_templates` ( + `is_global` integer NULL, + `region` varchar(255) NULL, + `public_url` varchar(2000) NULL, + `enabled` integer NULL, + `internal_url` varchar(2000) NULL, + `id` integer NOT NULL primary key autoincrement, + `service_id` integer NULL, + `admin_url` varchar(2000) NULL, + `version_id` varchar(20) NULL, + `version_list` varchar(2000) NULL, + `version_info` varchar(500) NULL +) ; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `endpoint_templates` +-- + + +/*!40000 ALTER TABLE `endpoint_templates` DISABLE KEYS */; +INSERT INTO `endpoint_templates` VALUES (1,'RegionOne','http://4.2.2.1:8774/v1.1/%tenant_id%',1,'http://4.2.2.1:8774/v1.1/%tenant_id%',1,1,'http://4.2.2.1:8774/v1.1/%tenant_id%',NULL,NULL,NULL); +INSERT INTO `endpoint_templates` VALUES (1,'RegionOne','http://4.2.2.1:8773/services/Cloud',1,'http://4.2.2.1:8773/services/Cloud',2,2,'http://4.2.2.1:8773/services/Admin',NULL,NULL,NULL); +INSERT INTO `endpoint_templates` VALUES (1,'RegionOne','http://4.2.2.1:9292/v1',1,'http://4.2.2.1:9292/v1',3,3,'http://4.2.2.1:9292/v1',NULL,NULL,NULL); +INSERT INTO `endpoint_templates` VALUES (1,'RegionOne','http://4.2.2.1:5000/v2.0',1,'http://4.2.2.1:5000/v2.0',4,4,'http://4.2.2.1:35357/v2.0',NULL,NULL,NULL); +INSERT INTO `endpoint_templates` VALUES (1,'RegionOne','http://4.2.2.1:8080/v1/AUTH_%tenant_id%',1,'http://4.2.2.1:8080/v1/AUTH_%tenant_id%',5,5,'http://4.2.2.1:8080/',NULL,NULL,NULL); +/*!40000 ALTER TABLE `endpoint_templates` ENABLE KEYS */; + + +-- +-- Table structure for table `endpoints` +-- + +DROP TABLE IF EXISTS `endpoints`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `endpoints` ( + `endpoint_template_id` integer NULL, + `tenant_id` integer NULL, + `id` integer NOT NULL primary key autoincrement +) ; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `endpoints` +-- + + +/*!40000 ALTER TABLE `endpoints` DISABLE KEYS */; +/*!40000 ALTER TABLE `endpoints` ENABLE KEYS */; + + +-- +-- Table structure for table `migrate_version` +-- + +DROP TABLE IF EXISTS `migrate_version`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `migrate_version` ( + `repository_id` varchar(250) NOT NULL, + `repository_path` text, + `version` integer NULL +) ; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `migrate_version` +-- + + +/*!40000 ALTER TABLE `migrate_version` DISABLE KEYS */; +INSERT INTO `migrate_version` VALUES ('Keystone','/opt/stack/keystone/keystone/backends/sqlalchemy/migrate_repo',11); +/*!40000 ALTER TABLE `migrate_version` ENABLE KEYS */; + + +-- +-- Table structure for table `roles` +-- + +DROP TABLE IF EXISTS `roles`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `roles` ( + `service_id` integer NULL, + `desc` varchar(255) NULL, + `id` integer NOT NULL primary key autoincrement, + `name` varchar(255) NULL +) ; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `roles` +-- + + +/*!40000 ALTER TABLE `roles` DISABLE KEYS */; +INSERT INTO `roles` VALUES (NULL,NULL,1,'admin'); +INSERT INTO `roles` VALUES (NULL,NULL,2,'Member'); +INSERT INTO `roles` VALUES (NULL,NULL,3,'KeystoneAdmin'); +INSERT INTO `roles` VALUES (NULL,NULL,4,'KeystoneServiceAdmin'); +INSERT INTO `roles` VALUES (NULL,NULL,5,'sysadmin'); +INSERT INTO `roles` VALUES (NULL,NULL,6,'netadmin'); +/*!40000 ALTER TABLE `roles` ENABLE KEYS */; + + +-- +-- Table structure for table `services` +-- + +DROP TABLE IF EXISTS `services`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `services` ( + `desc` varchar(255) NULL, + `type` varchar(255) NULL, + `id` integer NOT NULL primary key autoincrement, + `name` varchar(255) NULL, + `owner_id` integer NULL +) ; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `services` +-- + + +/*!40000 ALTER TABLE `services` DISABLE KEYS */; +INSERT INTO `services` VALUES ('Nova Compute Service','compute',1,'nova',NULL); +INSERT INTO `services` VALUES ('EC2 Compatability Layer','ec2',2,'ec2',NULL); +INSERT INTO `services` VALUES ('Glance Image Service','image',3,'glance',NULL); +INSERT INTO `services` VALUES ('Keystone Identity Service','identity',4,'keystone',NULL); +INSERT INTO `services` VALUES ('Swift Service','object-store',5,'swift',NULL); +/*!40000 ALTER TABLE `services` ENABLE KEYS */; + + +-- +-- Table structure for table `tenants` +-- + +DROP TABLE IF EXISTS `tenants`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `tenants` ( + `desc` varchar(255) NULL, + `enabled` integer NULL, + `id` integer NOT NULL primary key autoincrement, + `name` varchar(255) NULL, + `uid` varchar(255) NOT NULL +) ; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `tenants` +-- + + +/*!40000 ALTER TABLE `tenants` DISABLE KEYS */; +INSERT INTO `tenants` VALUES (NULL,1,1,'admin','182c1fbf7eef44eda162ff3fd30c0a76'); +INSERT INTO `tenants` VALUES (NULL,1,2,'demo','b1a7ea3a884f4d0685a98cd6e682a5da'); +INSERT INTO `tenants` VALUES (NULL,1,3,'invisible_to_admin','f4d1eed9bb5d4d35a5f37af934f87574'); +/*!40000 ALTER TABLE `tenants` ENABLE KEYS */; + + +-- +-- Table structure for table `tokens` +-- + +DROP TABLE IF EXISTS `tokens`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `tokens` ( + `tenant_id` integer NULL, + `expires` datetime NULL, + `user_id` integer NULL, + `id` varchar(255) NOT NULL +) ; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `tokens` +-- + + +/*!40000 ALTER TABLE `tokens` DISABLE KEYS */; +INSERT INTO `tokens` VALUES (1,'2015-02-05 00:00:00',1,'123123123123123123123'); +/*!40000 ALTER TABLE `tokens` ENABLE KEYS */; + + +-- +-- Table structure for table `user_roles` +-- + +DROP TABLE IF EXISTS `user_roles`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `user_roles` ( + `tenant_id` integer NULL, + `user_id` integer NULL, + `id` integer NOT NULL primary key autoincrement, + `role_id` integer NULL +) ; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `user_roles` +-- + + +/*!40000 ALTER TABLE `user_roles` DISABLE KEYS */; +INSERT INTO `user_roles` VALUES (1,1,1,1); +INSERT INTO `user_roles` VALUES (2,2,2,2); +INSERT INTO `user_roles` VALUES (2,2,3,5); +INSERT INTO `user_roles` VALUES (2,2,4,6); +INSERT INTO `user_roles` VALUES (3,2,5,2); +INSERT INTO `user_roles` VALUES (2,1,6,1); +INSERT INTO `user_roles` VALUES (NULL,1,7,1); +INSERT INTO `user_roles` VALUES (NULL,1,8,3); +INSERT INTO `user_roles` VALUES (NULL,1,9,4); +/*!40000 ALTER TABLE `user_roles` ENABLE KEYS */; + + +-- +-- Table structure for table `users` +-- + +DROP TABLE IF EXISTS `users`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `users` ( + `name` varchar(255) NULL, + `tenant_id` integer NULL, + `enabled` integer NULL, + `id` integer NOT NULL primary key autoincrement, + `password` varchar(255) NULL, + `email` varchar(255) NULL, + `uid` varchar(255) NOT NULL +) ; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `users` +-- + + +/*!40000 ALTER TABLE `users` DISABLE KEYS */; +INSERT INTO `users` VALUES ('admin',NULL,1,1,'$6$rounds=40000$hFXlgBSMi599197d$tmGKBpoGHNRsLB3ruK9f1wPvvtfWWuMEUzdqUAynsmmYXBK6eekyNHTzzhwXTM3mWpnaMHCI4mHPOycqmPJJc0',NULL,'c93b19ea3fa94484824213db8ac0afce'); +INSERT INTO `users` VALUES ('demo',NULL,1,2,'$6$rounds=40000$RBsX2ja9fdj2uTNQ$/wJOn510AYKW9BPFAJneVQAjm6TM0Ty11LG.u4.k4RhmoUcXNSjGKmQT6KO0SsvypMM7A.doWgt73V5rNnv5h.',NULL,'04c6697e88ff4667820903fcce05d904'); +/*!40000 ALTER TABLE `users` ENABLE KEYS */; + +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2012-02-13 19:23:51 +commit; diff --git a/tests/test_backend.py b/tests/test_backend.py new file mode 100644 index 0000000000..bbc651ef9f --- /dev/null +++ b/tests/test_backend.py @@ -0,0 +1,245 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +import datetime +import uuid + +from keystone import exception + + +class IdentityTests(object): + def test_authenticate_bad_user(self): + self.assertRaises(AssertionError, + self.identity_api.authenticate, + user_id=self.user_foo['id'] + 'WRONG', + tenant_id=self.tenant_bar['id'], + password=self.user_foo['password']) + + def test_authenticate_bad_password(self): + self.assertRaises(AssertionError, + self.identity_api.authenticate, + user_id=self.user_foo['id'], + tenant_id=self.tenant_bar['id'], + password=self.user_foo['password'] + 'WRONG') + + def test_authenticate_invalid_tenant(self): + self.assertRaises(AssertionError, + self.identity_api.authenticate, + user_id=self.user_foo['id'], + tenant_id=self.tenant_bar['id'] + 'WRONG', + password=self.user_foo['password']) + + def test_authenticate_no_tenant(self): + user_ref, tenant_ref, metadata_ref = self.identity_api.authenticate( + user_id=self.user_foo['id'], + password=self.user_foo['password']) + # NOTE(termie): the password field is left in user_foo to make it easier + # to authenticate in tests, but should not be returned by + # the api + self.user_foo.pop('password') + self.assertDictEquals(user_ref, self.user_foo) + self.assert_(tenant_ref is None) + self.assert_(not metadata_ref) + + def test_authenticate(self): + user_ref, tenant_ref, metadata_ref = self.identity_api.authenticate( + user_id=self.user_foo['id'], + tenant_id=self.tenant_bar['id'], + password=self.user_foo['password']) + # NOTE(termie): the password field is left in user_foo to make it easier + # to authenticate in tests, but should not be returned by + # the api + self.user_foo.pop('password') + self.assertDictEquals(user_ref, self.user_foo) + self.assertDictEquals(tenant_ref, self.tenant_bar) + self.assertDictEquals(metadata_ref, self.metadata_foobar) + + def test_password_hashed(self): + user_ref = self.identity_api._get_user(self.user_foo['id']) + self.assertNotEqual(user_ref['password'], self.user_foo['password']) + + + def test_get_tenant_bad_tenant(self): + tenant_ref = self.identity_api.get_tenant( + tenant_id=self.tenant_bar['id'] + 'WRONG') + self.assert_(tenant_ref is None) + + def test_get_tenant(self): + tenant_ref = self.identity_api.get_tenant(tenant_id=self.tenant_bar['id']) + self.assertDictEquals(tenant_ref, self.tenant_bar) + + def test_get_tenant_by_name_bad_tenant(self): + tenant_ref = self.identity_api.get_tenant( + tenant_id=self.tenant_bar['name'] + 'WRONG') + self.assert_(tenant_ref is None) + + def test_get_tenant_by_name(self): + tenant_ref = self.identity_api.get_tenant_by_name( + tenant_name=self.tenant_bar['name']) + self.assertDictEquals(tenant_ref, self.tenant_bar) + + def test_get_user_bad_user(self): + user_ref = self.identity_api.get_user( + user_id=self.user_foo['id'] + 'WRONG') + self.assert_(user_ref is None) + + def test_get_user(self): + user_ref = self.identity_api.get_user(user_id=self.user_foo['id']) + # NOTE(termie): the password field is left in user_foo to make it easier + # to authenticate in tests, but should not be returned by + # the api + self.user_foo.pop('password') + self.assertDictEquals(user_ref, self.user_foo) + + def test_get_metadata_bad_user(self): + metadata_ref = self.identity_api.get_metadata( + user_id=self.user_foo['id'] + 'WRONG', + tenant_id=self.tenant_bar['id']) + self.assert_(metadata_ref is None) + + def test_get_metadata_bad_tenant(self): + metadata_ref = self.identity_api.get_metadata( + user_id=self.user_foo['id'], + tenant_id=self.tenant_bar['id'] + 'WRONG') + self.assert_(metadata_ref is None) + + def test_get_metadata(self): + metadata_ref = self.identity_api.get_metadata( + user_id=self.user_foo['id'], + tenant_id=self.tenant_bar['id']) + self.assertDictEquals(metadata_ref, self.metadata_foobar) + + def test_get_role(self): + role_ref = self.identity_api.get_role( + role_id=self.role_keystone_admin['id']) + self.assertDictEquals(role_ref, self.role_keystone_admin) + + def test_create_duplicate_user_id_fails(self): + user = {'id': 'fake1', + 'name': 'fake1', + 'password': 'fakepass', + 'tenants': ['bar',]} + self.identity_api.create_user('fake1', user) + user['name'] = 'fake2' + self.assertRaises(Exception, + self.identity_api.create_user, + 'fake1', + user) + + def test_create_duplicate_user_name_fails(self): + user = {'id': 'fake1', + 'name': 'fake1', + 'password': 'fakepass', + 'tenants': ['bar',]} + self.identity_api.create_user('fake1', user) + user['id'] = 'fake2' + self.assertRaises(Exception, + self.identity_api.create_user, + 'fake2', + user) + + def test_rename_duplicate_user_name_fails(self): + user1 = {'id': 'fake1', + 'name': 'fake1', + 'password': 'fakepass', + 'tenants': ['bar',]} + user2 = {'id': 'fake2', + 'name': 'fake2', + 'password': 'fakepass', + 'tenants': ['bar',]} + self.identity_api.create_user('fake1', user1) + self.identity_api.create_user('fake2', user2) + user2['name'] = 'fake1' + self.assertRaises(Exception, + self.identity_api.update_user, + 'fake2', + user2) + + def test_update_user_id_does_nothing(self): + user = {'id': 'fake1', + 'name': 'fake1', + 'password': 'fakepass', + 'tenants': ['bar',]} + self.identity_api.create_user('fake1', user) + user['id'] = 'fake2' + self.identity_api.update_user('fake1', user) + user_ref = self.identity_api.get_user('fake1') + self.assertEqual(user_ref['id'], 'fake1') + user_ref = self.identity_api.get_user('fake2') + self.assert_(user_ref is None) + + def test_create_duplicate_tenant_id_fails(self): + tenant = {'id': 'fake1', 'name': 'fake1'} + self.identity_api.create_tenant('fake1', tenant) + tenant['name'] = 'fake2' + self.assertRaises(Exception, + self.identity_api.create_tenant, + 'fake1', + tenant) + + def test_create_duplicate_tenant_name_fails(self): + tenant = {'id': 'fake1', 'name': 'fake'} + self.identity_api.create_tenant('fake1', tenant) + tenant['id'] = 'fake2' + self.assertRaises(Exception, + self.identity_api.create_tenant, + 'fake1', + tenant) + + def test_rename_duplicate_tenant_name_fails(self): + tenant1 = {'id': 'fake1', 'name': 'fake1'} + tenant2 = {'id': 'fake2', 'name': 'fake2'} + self.identity_api.create_tenant('fake1', tenant1) + self.identity_api.create_tenant('fake2', tenant2) + tenant2['name'] = 'fake1' + self.assertRaises(Exception, + self.identity_api.update_tenant, + 'fake2', + tenant2) + + def test_update_tenant_id_does_nothing(self): + tenant = {'id': 'fake1', 'name': 'fake1'} + self.identity_api.create_tenant('fake1', tenant) + tenant['id'] = 'fake2' + self.identity_api.update_tenant('fake1', tenant) + tenant_ref = self.identity_api.get_tenant('fake1') + self.assertEqual(tenant_ref['id'], 'fake1') + tenant_ref = self.identity_api.get_tenant('fake2') + self.assert_(tenant_ref is None) + + +class TokenTests(object): + def test_token_crud(self): + token_id = uuid.uuid4().hex + data = {'id': token_id, 'a': 'b'} + data_ref = self.token_api.create_token(token_id, data) + expires = data_ref.pop('expires') + self.assertTrue(isinstance(expires, datetime.datetime)) + self.assertDictEquals(data_ref, data) + + new_data_ref = self.token_api.get_token(token_id) + expires = new_data_ref.pop('expires') + self.assertTrue(isinstance(expires, datetime.datetime)) + self.assertEquals(new_data_ref, data) + + self.token_api.delete_token(token_id) + self.assertRaises(exception.TokenNotFound, + self.token_api.delete_token, token_id) + self.assertRaises(exception.TokenNotFound, + self.token_api.get_token, token_id) + + def test_expired_token(self): + token_id = uuid.uuid4().hex + expire_time = datetime.datetime.now() - datetime.timedelta(minutes=1) + data = {'id': token_id, 'a': 'b', 'expires': expire_time} + data_ref = self.token_api.create_token(token_id, data) + self.assertDictEquals(data_ref, data) + self.assertRaises(exception.TokenNotFound, + self.token_api.get_token, token_id) + + def test_null_expires_token(self): + token_id = uuid.uuid4().hex + data = {'id': token_id, 'a': 'b', 'expires': None} + data_ref = self.token_api.create_token(token_id, data) + self.assertDictEquals(data_ref, data) + new_data_ref = self.token_api.get_token(token_id) + self.assertEqual(data_ref, new_data_ref) diff --git a/tests/test_backend_kvs.py b/tests/test_backend_kvs.py new file mode 100644 index 0000000000..4175360998 --- /dev/null +++ b/tests/test_backend_kvs.py @@ -0,0 +1,44 @@ +from keystone import test +from keystone.identity.backends import kvs as identity_kvs +from keystone.token.backends import kvs as token_kvs +from keystone.catalog.backends import kvs as catalog_kvs + +import test_backend +import default_fixtures + + +class KvsIdentity(test.TestCase, test_backend.IdentityTests): + def setUp(self): + super(KvsIdentity, self).setUp() + self.identity_api = identity_kvs.Identity(db={}) + self.load_fixtures(default_fixtures) + + +class KvsToken(test.TestCase, test_backend.TokenTests): + def setUp(self): + super(KvsToken, self).setUp() + self.token_api = token_kvs.Token(db={}) + + +class KvsCatalog(test.TestCase): + def setUp(self): + super(KvsCatalog, self).setUp() + self.catalog_api = catalog_kvs.Catalog(db={}) + self._load_fixtures() + + def _load_fixtures(self): + self.catalog_foobar = self.catalog_api._create_catalog( + 'foo', 'bar', + {'RegionFoo': {'service_bar': {'foo': 'bar'}}}) + + def test_get_catalog_bad_user(self): + catalog_ref = self.catalog_api.get_catalog('foo' + 'WRONG', 'bar') + self.assert_(catalog_ref is None) + + def test_get_catalog_bad_tenant(self): + catalog_ref = self.catalog_api.get_catalog('foo', 'bar' + 'WRONG') + self.assert_(catalog_ref is None) + + def test_get_catalog(self): + catalog_ref = self.catalog_api.get_catalog('foo', 'bar') + self.assertDictEquals(catalog_ref, self.catalog_foobar) diff --git a/tests/test_backend_memcache.py b/tests/test_backend_memcache.py new file mode 100644 index 0000000000..05ef2107c8 --- /dev/null +++ b/tests/test_backend_memcache.py @@ -0,0 +1,62 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +import datetime +import time +import uuid + +import memcache + +from keystone import exception +from keystone import test +from keystone.token.backends import memcache as token_memcache + +import test_backend + + +class MemcacheClient(object): + """Replicates a tiny subset of memcached client interface.""" + + def __init__(self, *args, **kwargs): + """Ignores the passed in args.""" + self.cache = {} + + def check_key(self, key): + if not isinstance(key, str): + raise memcache.Client.MemcachedStringEncodingError() + + def get(self, key): + """Retrieves the value for a key or None.""" + self.check_key(key) + obj = self.cache.get(key) + now = time.mktime(datetime.datetime.now().timetuple()) + if obj and (obj[1] == 0 or obj[1] > now): + return obj[0] + else: + raise exception.TokenNotFound(token_id=key) + + def set(self, key, value, time=0): + """Sets the value for a key.""" + self.check_key(key) + self.cache[key] = (value, time) + return True + + def delete(self, key): + self.check_key(key) + try: + del self.cache[key] + except KeyError: + #NOTE(bcwaldon): python-memcached always returns the same value + pass + + +class MemcacheToken(test.TestCase, test_backend.TokenTests): + def setUp(self): + super(MemcacheToken, self).setUp() + fake_client = MemcacheClient() + self.token_api = token_memcache.Token(client=fake_client) + + def test_get_unicode(self): + token_id = unicode(uuid.uuid4().hex) + data = {'id': token_id, 'a': 'b'} + self.token_api.create_token(token_id, data) + self.token_api.get_token(token_id) diff --git a/tests/test_backend_sql.py b/tests/test_backend_sql.py new file mode 100644 index 0000000000..89068d7178 --- /dev/null +++ b/tests/test_backend_sql.py @@ -0,0 +1,58 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +from keystone import config +from keystone import test +from keystone.common.sql import util as sql_util +from keystone.identity.backends import sql as identity_sql +from keystone.token.backends import sql as token_sql + +import test_backend +import default_fixtures + + +CONF = config.CONF + + +class SqlIdentity(test.TestCase, test_backend.IdentityTests): + def setUp(self): + super(SqlIdentity, self).setUp() + CONF(config_files=[test.etcdir('keystone.conf'), + test.testsdir('test_overrides.conf'), + test.testsdir('backend_sql.conf')]) + sql_util.setup_test_database() + self.identity_api = identity_sql.Identity() + self.load_fixtures(default_fixtures) + + +class SqlToken(test.TestCase, test_backend.TokenTests): + def setUp(self): + super(SqlToken, self).setUp() + CONF(config_files=[test.etcdir('keystone.conf'), + test.testsdir('test_overrides.conf'), + test.testsdir('backend_sql.conf')]) + sql_util.setup_test_database() + self.token_api = token_sql.Token() + + +#class SqlCatalog(test_backend_kvs.KvsCatalog): +# def setUp(self): +# super(SqlCatalog, self).setUp() +# self.catalog_api = sql.SqlCatalog() +# self._load_fixtures() + +# def _load_fixtures(self): +# self.catalog_foobar = self.catalog_api._create_catalog( +# 'foo', 'bar', +# {'RegionFoo': {'service_bar': {'foo': 'bar'}}}) + +# def test_get_catalog_bad_user(self): +# catalog_ref = self.catalog_api.get_catalog('foo' + 'WRONG', 'bar') +# self.assert_(catalog_ref is None) + +# def test_get_catalog_bad_tenant(self): +# catalog_ref = self.catalog_api.get_catalog('foo', 'bar' + 'WRONG') +# self.assert_(catalog_ref is None) + +# def test_get_catalog(self): +# catalog_ref = self.catalog_api.get_catalog('foo', 'bar') +# self.assertDictEquals(catalog_ref, self.catalog_foobar) diff --git a/tests/test_exception.py b/tests/test_exception.py new file mode 100644 index 0000000000..2c4b948406 --- /dev/null +++ b/tests/test_exception.py @@ -0,0 +1,53 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +import uuid +import json + +from keystone.common import wsgi +from keystone import exception +from keystone import test + + +class ExceptionTestCase(test.TestCase): + def setUp(self): + pass + + def tearDown(self): + pass + + def assertValidJsonRendering(self, e): + resp = wsgi.render_exception(e) + self.assertEqual(resp.status_int, e.code) + self.assertEqual(resp.status, '%s %s' % (e.code, e.title)) + + j = json.loads(resp.body) + self.assertIsNotNone(j.get('error')) + self.assertIsNotNone(j['error'].get('code')) + self.assertIsNotNone(j['error'].get('title')) + self.assertIsNotNone(j['error'].get('message')) + self.assertNotIn('\n', j['error']['message']) + self.assertNotIn(' ', j['error']['message']) + self.assertTrue(type(j['error']['code']) is int) + + def test_validation_error(self): + target = uuid.uuid4().hex + attribute = uuid.uuid4().hex + e = exception.ValidationError(target=target, attribute=attribute) + self.assertValidJsonRendering(e) + self.assertIn(target, str(e)) + self.assertIn(attribute, str(e)) + + def test_unauthorized(self): + e = exception.Unauthorized() + self.assertValidJsonRendering(e) + + def test_forbidden(self): + action = uuid.uuid4().hex + e = exception.Forbidden(action=action) + self.assertValidJsonRendering(e) + self.assertIn(action, str(e)) + + def test_not_found(self): + target = uuid.uuid4().hex + e = exception.NotFound(target=target) + self.assertValidJsonRendering(e) + self.assertIn(target, str(e)) diff --git a/tests/test_import_legacy.py b/tests/test_import_legacy.py new file mode 100644 index 0000000000..133ebcda43 --- /dev/null +++ b/tests/test_import_legacy.py @@ -0,0 +1,99 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +import os + +import sqlite3 +#import sqlalchemy + +from keystone import config +from keystone import test +from keystone.common.sql import legacy +from keystone.common.sql import util as sql_util +from keystone.identity.backends import sql as identity_sql +from keystone.token.backends import sql as token_sql +from keystone.catalog.backends import templated as catalog_templated + + + +CONF = config.CONF + + +class ImportLegacy(test.TestCase): + def setUp(self): + super(ImportLegacy, self).setUp() + CONF(config_files=[test.etcdir('keystone.conf'), + test.testsdir('test_overrides.conf'), + test.testsdir('backend_sql.conf')]) + sql_util.setup_test_database() + self.identity_api = identity_sql.Identity() + + def setup_old_database(self, sql_dump): + sql_path = test.testsdir(sql_dump) + db_path = test.testsdir('%s.db' % sql_dump) + try: + os.unlink(db_path) + except OSError: + pass + script_str = open(sql_path).read().strip() + conn = sqlite3.connect(db_path) + conn.executescript(script_str) + conn.commit() + return db_path + + def test_import_d5(self): + db_path = self.setup_old_database('legacy_d5.sqlite') + migration = legacy.LegacyMigration('sqlite:///%s' % db_path) + migration.migrate_all() + + admin_id = '1' + user_ref = self.identity_api.get_user(admin_id) + self.assertEquals(user_ref['name'], 'admin') + self.assertEquals(user_ref['enabled'], True) + + # check password hashing + user_ref, tenant_ref, metadata_ref = self.identity_api.authenticate( + user_id=admin_id, password='secrete') + + # check catalog + self._check_catalog(migration) + + def test_import_diablo(self): + db_path = self.setup_old_database('legacy_diablo.sqlite') + migration = legacy.LegacyMigration('sqlite:///%s' % db_path) + migration.migrate_all() + + admin_id = '1' + user_ref = self.identity_api.get_user(admin_id) + self.assertEquals(user_ref['name'], 'admin') + self.assertEquals(user_ref['enabled'], True) + + # check password hashing + user_ref, tenant_ref, metadata_ref = self.identity_api.authenticate( + user_id=admin_id, password='secrete') + + # check catalog + self._check_catalog(migration) + + def test_import_essex(self): + db_path = self.setup_old_database('legacy_essex.sqlite') + migration = legacy.LegacyMigration('sqlite:///%s' % db_path) + migration.migrate_all() + + admin_id = 'c93b19ea3fa94484824213db8ac0afce' + user_ref = self.identity_api.get_user(admin_id) + self.assertEquals(user_ref['name'], 'admin') + self.assertEquals(user_ref['enabled'], True) + + # check password hashing + user_ref, tenant_ref, metadata_ref = self.identity_api.authenticate( + user_id=admin_id, password='secrete') + + # check catalog + self._check_catalog(migration) + + def _check_catalog(self, migration): + catalog_lines = migration.dump_catalog() + catalog = catalog_templated.parse_templates(catalog_lines) + self.assert_('RegionOne' in catalog) + self.assert_('compute' in catalog['RegionOne']) + self.assert_('adminUrl' in catalog['RegionOne']['compute']) diff --git a/tests/test_keystoneclient.py b/tests/test_keystoneclient.py new file mode 100644 index 0000000000..1fca22fc79 --- /dev/null +++ b/tests/test_keystoneclient.py @@ -0,0 +1,550 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +import uuid + +import nose.exc + +from keystone import test + +import default_fixtures + +OPENSTACK_REPO = 'https://review.openstack.org/p/openstack' +KEYSTONECLIENT_REPO = '%s/python-keystoneclient.git' % OPENSTACK_REPO + + +class CompatTestCase(test.TestCase): + def setUp(self): + super(CompatTestCase, self).setUp() + + revdir = test.checkout_vendor(*self.get_checkout()) + self.add_path(revdir) + self.clear_module('keystoneclient') + + self.load_backends() + self.load_fixtures(default_fixtures) + + self.public_server = self.serveapp('keystone', name='main') + self.admin_server = self.serveapp('keystone', name='admin') + + # TODO(termie): is_admin is being deprecated once the policy stuff + # is all working + # TODO(termie): add an admin user to the fixtures and use that user + # override the fixtures, for now + self.metadata_foobar = self.identity_api.update_metadata( + self.user_foo['id'], self.tenant_bar['id'], + dict(roles=['keystone_admin'], is_admin='1')) + + def tearDown(self): + self.public_server.kill() + self.admin_server.kill() + self.public_server = None + self.admin_server = None + super(CompatTestCase, self).tearDown() + + def _public_url(self): + public_port = self.public_server.socket_info['socket'][1] + return "http://localhost:%s/v2.0" % public_port + + def _admin_url(self): + admin_port = self.admin_server.socket_info['socket'][1] + return "http://localhost:%s/v2.0" % admin_port + + def _client(self, **kwargs): + from keystoneclient.v2_0 import client as ks_client + + kc = ks_client.Client(endpoint=self._admin_url(), + auth_url=self._public_url(), + **kwargs) + kc.authenticate() + # have to manually overwrite the management url after authentication + kc.management_url = self._admin_url() + return kc + + def get_client(self, user_ref=None, tenant_ref=None): + if user_ref is None: + user_ref = self.user_foo + if tenant_ref is None: + for user in default_fixtures.USERS: + if user['id'] == user_ref['id']: + tenant_id = user['tenants'][0] + else: + tenant_id = tenant_ref['id'] + + return self._client(username=user_ref['name'], + password=user_ref['password'], + tenant_id=tenant_id) + + +class KeystoneClientTests(object): + """Tests for all versions of keystoneclient.""" + + def test_authenticate_tenant_name_and_tenants(self): + client = self.get_client() + tenants = client.tenants.list() + self.assertEquals(tenants[0].id, self.tenant_bar['id']) + + def test_authenticate_tenant_id_and_tenants(self): + client = self._client(username=self.user_foo['name'], + password=self.user_foo['password'], + tenant_id='bar') + tenants = client.tenants.list() + self.assertEquals(tenants[0].id, self.tenant_bar['id']) + + def test_authenticate_invalid_tenant_id(self): + from keystoneclient import exceptions as client_exceptions + self.assertRaises(client_exceptions.AuthorizationFailure, + self._client, + username=self.user_foo['name'], + password=self.user_foo['password'], + tenant_id='baz') + + def test_authenticate_token_no_tenant(self): + client = self.get_client() + token = client.auth_token + token_client = self._client(token=token) + tenants = token_client.tenants.list() + self.assertEquals(tenants[0].id, self.tenant_bar['id']) + + def test_authenticate_token_tenant_id(self): + client = self.get_client() + token = client.auth_token + token_client = self._client(token=token, tenant_id='bar') + tenants = token_client.tenants.list() + self.assertEquals(tenants[0].id, self.tenant_bar['id']) + + def test_authenticate_token_invalid_tenant_id(self): + from keystoneclient import exceptions as client_exceptions + client = self.get_client() + token = client.auth_token + self.assertRaises(client_exceptions.AuthorizationFailure, + self._client, token=token, tenant_id='baz') + + def test_authenticate_token_tenant_name(self): + client = self.get_client() + token = client.auth_token + token_client = self._client(token=token, tenant_name='BAR') + tenants = token_client.tenants.list() + self.assertEquals(tenants[0].id, self.tenant_bar['id']) + self.assertEquals(tenants[0].id, self.tenant_bar['id']) + + def test_authenticate_and_delete_token(self): + from keystoneclient import exceptions as client_exceptions + + client = self.get_client() + token = client.auth_token + token_client = self._client(token=token) + tenants = token_client.tenants.list() + self.assertEquals(tenants[0].id, self.tenant_bar['id']) + + client.tokens.delete(token_client.auth_token) + + self.assertRaises(client_exceptions.Unauthorized, + token_client.tenants.list) + + def test_authenticate_no_password(self): + from keystoneclient import exceptions as client_exceptions + + user_ref = self.user_foo.copy() + user_ref['password'] = None + self.assertRaises(client_exceptions.AuthorizationFailure, + self.get_client, + user_ref) + + def test_authenticate_no_username(self): + from keystoneclient import exceptions as client_exceptions + + user_ref = self.user_foo.copy() + user_ref['name'] = None + self.assertRaises(client_exceptions.AuthorizationFailure, + self.get_client, + user_ref) + + # TODO(termie): I'm not really sure that this is testing much + def test_endpoints(self): + client = self.get_client() + token = client.auth_token + endpoints = client.tokens.endpoints(token=token) + + # FIXME(ja): this test should require the "keystone:admin" roled + # (probably the role set via --keystone_admin_role flag) + # FIXME(ja): add a test that admin endpoint is only sent to admin user + # FIXME(ja): add a test that admin endpoint returns unauthorized if not + # admin + def test_tenant_create_update_and_delete(self): + from keystoneclient import exceptions as client_exceptions + + test_tenant = 'new_tenant' + client = self.get_client() + tenant = client.tenants.create(tenant_name=test_tenant, + description="My new tenant!", + enabled=True) + self.assertEquals(tenant.name, test_tenant) + + tenant = client.tenants.get(tenant_id=tenant.id) + self.assertEquals(tenant.name, test_tenant) + + # TODO(devcamcar): update gives 404. why? + tenant = client.tenants.update(tenant_id=tenant.id, + tenant_name='new_tenant2', + enabled=False, + description='new description') + self.assertEquals(tenant.name, 'new_tenant2') + self.assertFalse(tenant.enabled) + self.assertEquals(tenant.description, 'new description') + + client.tenants.delete(tenant=tenant.id) + self.assertRaises(client_exceptions.NotFound, client.tenants.get, + tenant.id) + + def test_tenant_list(self): + client = self.get_client() + tenants = client.tenants.list() + self.assertEquals(len(tenants), 1) + + def test_invalid_password(self): + from keystoneclient import exceptions as client_exceptions + + good_client = self._client(username=self.user_foo['name'], + password=self.user_foo['password']) + good_client.tenants.list() + + self.assertRaises(client_exceptions.AuthorizationFailure, + self._client, + username=self.user_foo['name'], + password='invalid') + + def test_user_create_update_delete(self): + from keystoneclient import exceptions as client_exceptions + + test_username = 'new_user' + client = self.get_client() + user = client.users.create(name=test_username, + password='password', + email='user1@test.com') + self.assertEquals(user.name, test_username) + + user = client.users.get(user=user.id) + self.assertEquals(user.name, test_username) + + user = client.users.update_email(user=user, email='user2@test.com') + self.assertEquals(user.email, 'user2@test.com') + + # NOTE(termie): update_enabled doesn't return anything, probably a bug + client.users.update_enabled(user=user, enabled=False) + user = client.users.get(user.id) + self.assertFalse(user.enabled) + + self.assertRaises(client_exceptions.AuthorizationFailure, + self._client, + username=test_username, + password='password') + client.users.update_enabled(user, True) + + user = client.users.update_password(user=user, password='password2') + + self._client(username=test_username, + password='password2') + + user = client.users.update_tenant(user=user, tenant='bar') + # TODO(ja): once keystonelight supports default tenant + # when you login without specifying tenant, the + # token should be scoped to tenant 'bar' + + client.users.delete(user.id) + self.assertRaises(client_exceptions.NotFound, client.users.get, + user.id) + + def test_user_list(self): + client = self.get_client() + users = client.users.list() + self.assertTrue(len(users) > 0) + user = users[0] + self.assertRaises(AttributeError, lambda: user.password) + + def test_user_get(self): + client = self.get_client() + user = client.users.get(user=self.user_foo['id']) + self.assertRaises(AttributeError, lambda: user.password) + + def test_role_get(self): + client = self.get_client() + role = client.roles.get(role='keystone_admin') + self.assertEquals(role.id, 'keystone_admin') + + def test_role_create_and_delete(self): + from keystoneclient import exceptions as client_exceptions + + test_role = 'new_role' + client = self.get_client() + role = client.roles.create(name=test_role) + self.assertEquals(role.name, test_role) + + role = client.roles.get(role=role.id) + self.assertEquals(role.name, test_role) + + client.roles.delete(role=role.id) + + self.assertRaises(client_exceptions.NotFound, client.roles.get, + role=role.id) + + def test_role_list(self): + client = self.get_client() + roles = client.roles.list() + # TODO(devcamcar): This assert should be more specific. + self.assertTrue(len(roles) > 0) + + def test_ec2_credential_crud(self): + client = self.get_client() + creds = client.ec2.list(user_id=self.user_foo['id']) + self.assertEquals(creds, []) + + cred = client.ec2.create(user_id=self.user_foo['id'], + tenant_id=self.tenant_bar['id']) + creds = client.ec2.list(user_id=self.user_foo['id']) + self.assertEquals(creds, [cred]) + + got = client.ec2.get(user_id=self.user_foo['id'], access=cred.access) + self.assertEquals(cred, got) + + client.ec2.delete(user_id=self.user_foo['id'], access=cred.access) + creds = client.ec2.list(user_id=self.user_foo['id']) + self.assertEquals(creds, []) + + def test_ec2_credentials_list_user_forbidden(self): + from keystoneclient import exceptions as client_exceptions + + two = self.get_client(self.user_two) + self.assertRaises(client_exceptions.Forbidden, two.ec2.list, + user_id=self.user_foo['id']) + + def test_ec2_credentials_get_user_forbidden(self): + from keystoneclient import exceptions as client_exceptions + + foo = self.get_client() + cred = foo.ec2.create(user_id=self.user_foo['id'], + tenant_id=self.tenant_bar['id']) + + two = self.get_client(self.user_two) + self.assertRaises(client_exceptions.Forbidden, two.ec2.get, + user_id=self.user_foo['id'], access=cred.access) + + foo.ec2.delete(user_id=self.user_foo['id'], access=cred.access) + + def test_ec2_credentials_delete_user_forbidden(self): + from keystoneclient import exceptions as client_exceptions + + foo = self.get_client() + cred = foo.ec2.create(user_id=self.user_foo['id'], + tenant_id=self.tenant_bar['id']) + + two = self.get_client(self.user_two) + self.assertRaises(client_exceptions.Forbidden, two.ec2.delete, + user_id=self.user_foo['id'], access=cred.access) + + foo.ec2.delete(user_id=self.user_foo['id'], access=cred.access) + + def test_service_create_and_delete(self): + from keystoneclient import exceptions as client_exceptions + + test_service = 'new_service' + client = self.get_client() + service = client.services.create(name=test_service, + service_type='test', + description='test') + self.assertEquals(service.name, test_service) + + service = client.services.get(id=service.id) + self.assertEquals(service.name, test_service) + + client.services.delete(id=service.id) + self.assertRaises(client_exceptions.NotFound, client.services.get, + id=service.id) + + def test_service_list(self): + client = self.get_client() + test_service = 'new_service' + service = client.services.create(name=test_service, + service_type='test', + description='test') + services = client.services.list() + # TODO(devcamcar): This assert should be more specific. + self.assertTrue(len(services) > 0) + + def test_admin_requires_adminness(self): + from keystoneclient import exceptions as client_exceptions + # FIXME(ja): this should be Unauthorized + exception = client_exceptions.ClientException + + two = self.get_client(self.user_two) # non-admin user + + # USER CRUD + self.assertRaises(exception, + two.users.list) + self.assertRaises(exception, + two.users.get, + user=self.user_two['id']) + self.assertRaises(exception, + two.users.create, + name='oops', + password='password', + email='oops@test.com') + self.assertRaises(exception, + two.users.delete, + user=self.user_foo['id']) + + # TENANT CRUD + # NOTE(ja): tenants.list is different since /tenants fulfills the + # two different tasks: return list of all tenants & return + # list of tenants the current user is a member of... + # which means if you are admin getting the list + # of tenants for admin user is annoying? + tenants = two.tenants.list() + self.assertTrue(len(tenants) == 1) + self.assertTrue(tenants[0].id == self.tenant_baz['id']) + self.assertRaises(exception, + two.tenants.get, + tenant_id=self.tenant_bar['id']) + self.assertRaises(exception, + two.tenants.create, + tenant_name='oops', + description="shouldn't work!", + enabled=True) + self.assertRaises(exception, + two.tenants.delete, + tenant=self.tenant_baz['id']) + + # ROLE CRUD + self.assertRaises(exception, + two.roles.get, + role='keystone_admin') + self.assertRaises(exception, + two.roles.list) + self.assertRaises(exception, + two.roles.create, + name='oops') + self.assertRaises(exception, + two.roles.delete, + role='keystone_admin') + + # TODO(ja): MEMBERSHIP CRUD + # TODO(ja): determine what else todo + + +class KcMasterTestCase(CompatTestCase, KeystoneClientTests): + def get_checkout(self): + return KEYSTONECLIENT_REPO, 'master' + + def test_tenant_add_and_remove_user(self): + client = self.get_client() + client.roles.add_user_role(tenant=self.tenant_baz['id'], + user=self.user_foo['id'], + role=self.role_useless['id']) + tenant_refs = client.tenants.list() + self.assert_(self.tenant_baz['id'] in + [x.id for x in tenant_refs]) + + client.roles.remove_user_role(tenant=self.tenant_baz['id'], + user=self.user_foo['id'], + role=self.role_useless['id']) + + tenant_refs = client.tenants.list() + self.assert_(self.tenant_baz['id'] not in + [x.id for x in tenant_refs]) + + def test_tenant_list_marker(self): + client = self.get_client() + + # Add two arbitrary tenants to user for testing purposes + for i in range(2): + tenant_id = uuid.uuid4().hex + tenant = {'name': 'tenant-%s' % tenant_id, 'id': tenant_id} + self.identity_api.create_tenant(tenant_id, tenant) + self.identity_api.add_user_to_tenant(tenant_id, self.user_foo['id']) + + tenants = client.tenants.list() + self.assertEqual(len(tenants), 3) + + tenants_marker = client.tenants.list(marker=tenants[0].id) + self.assertEqual(len(tenants_marker), 2) + self.assertEqual(tenants[1].name, tenants_marker[0].name) + self.assertEqual(tenants[2].name, tenants_marker[1].name) + + def test_tenant_list_marker_not_found(self): + from keystoneclient import exceptions as client_exceptions + + client = self.get_client() + self.assertRaises(client_exceptions.BadRequest, + client.tenants.list, marker=uuid.uuid4().hex) + + def test_tenant_list_limit(self): + client = self.get_client() + + # Add two arbitrary tenants to user for testing purposes + for i in range(2): + tenant_id = uuid.uuid4().hex + tenant = {'name': 'tenant-%s' % tenant_id, 'id': tenant_id} + self.identity_api.create_tenant(tenant_id, tenant) + self.identity_api.add_user_to_tenant(tenant_id, self.user_foo['id']) + + tenants = client.tenants.list() + self.assertEqual(len(tenants), 3) + + tenants_limited = client.tenants.list(limit=2) + self.assertEqual(len(tenants_limited), 2) + self.assertEqual(tenants[0].name, tenants_limited[0].name) + self.assertEqual(tenants[1].name, tenants_limited[1].name) + + def test_tenant_list_limit_bad_value(self): + from keystoneclient import exceptions as client_exceptions + + client = self.get_client() + self.assertRaises(client_exceptions.BadRequest, + client.tenants.list, limit='a') + self.assertRaises(client_exceptions.BadRequest, + client.tenants.list, limit=-1) + + def test_roles_get_by_user(self): + client = self.get_client() + roles = client.roles.roles_for_user(user=self.user_foo['id'], + tenant=self.tenant_bar['id']) + self.assertTrue(len(roles) > 0) + + +class KcEssex3TestCase(CompatTestCase, KeystoneClientTests): + def get_checkout(self): + return KEYSTONECLIENT_REPO, 'essex-3' + + def test_tenant_add_and_remove_user(self): + client = self.get_client() + client.roles.add_user_to_tenant(tenant_id=self.tenant_baz['id'], + user_id=self.user_foo['id'], + role_id=self.role_useless['id']) + tenant_refs = client.tenants.list() + self.assert_(self.tenant_baz['id'] in + [x.id for x in tenant_refs]) + + # get the "role_refs" so we get the proper id, this is how the clients + # do it + roleref_refs = client.roles.get_user_role_refs( + user_id=self.user_foo['id']) + for roleref_ref in roleref_refs: + if (roleref_ref.roleId == self.role_useless['id'] + and roleref_ref.tenantId == self.tenant_baz['id']): + # use python's scope fall through to leave roleref_ref set + break + + client.roles.remove_user_from_tenant(tenant_id=self.tenant_baz['id'], + user_id=self.user_foo['id'], + role_id=roleref_ref.id) + + tenant_refs = client.tenants.list() + self.assert_(self.tenant_baz['id'] not in + [x.id for x in tenant_refs]) + + def test_roles_get_by_user(self): + client = self.get_client() + roles = client.roles.get_user_role_refs(user_id='foo') + self.assertTrue(len(roles) > 0) + + def test_authenticate_and_delete_token(self): + raise nose.exc.SkipTest('N/A') diff --git a/tests/test_keystoneclient_sql.py b/tests/test_keystoneclient_sql.py new file mode 100644 index 0000000000..0d8ef52dfa --- /dev/null +++ b/tests/test_keystoneclient_sql.py @@ -0,0 +1,17 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +from keystone import config +from keystone import test +from keystone.common.sql import util as sql_util + +import test_keystoneclient + + +CONF = config.CONF + + +class KcMasterSqlTestCase(test_keystoneclient.KcMasterTestCase): + def config(self): + CONF(config_files=[test.etcdir('keystone.conf'), + test.testsdir('test_overrides.conf'), + test.testsdir('backend_sql.conf')]) + sql_util.setup_test_database() diff --git a/tests/test_middleware.py b/tests/test_middleware.py new file mode 100644 index 0000000000..f985850561 --- /dev/null +++ b/tests/test_middleware.py @@ -0,0 +1,85 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +import webob + +from keystone import config +from keystone import middleware +from keystone import test + + +CONF = config.CONF + + +def make_request(**kwargs): + method = kwargs.pop('method', 'GET') + body = kwargs.pop('body', None) + req = webob.Request.blank('/', **kwargs) + req.method = method + if body is not None: + req.body = body + return req + + +class TokenAuthMiddlewareTest(test.TestCase): + def test_request(self): + req = make_request() + req.headers[middleware.AUTH_TOKEN_HEADER] = 'MAGIC' + middleware.TokenAuthMiddleware(None).process_request(req) + context = req.environ[middleware.CONTEXT_ENV] + self.assertEqual(context['token_id'], 'MAGIC') + + +class AdminTokenAuthMiddlewareTest(test.TestCase): + def test_request_admin(self): + req = make_request() + req.headers[middleware.AUTH_TOKEN_HEADER] = CONF.admin_token + middleware.AdminTokenAuthMiddleware(None).process_request(req) + context = req.environ[middleware.CONTEXT_ENV] + self.assertTrue(context['is_admin']) + + def test_request_non_admin(self): + req = make_request() + req.headers[middleware.AUTH_TOKEN_HEADER] = 'NOT-ADMIN' + middleware.AdminTokenAuthMiddleware(None).process_request(req) + context = req.environ[middleware.CONTEXT_ENV] + self.assertFalse(context['is_admin']) + + +class PostParamsMiddlewareTest(test.TestCase): + def test_request_with_params(self): + req = make_request(body="arg1=one", method='POST') + middleware.PostParamsMiddleware(None).process_request(req) + params = req.environ[middleware.PARAMS_ENV] + self.assertEqual(params, {"arg1": "one"}) + + +class JsonBodyMiddlewareTest(test.TestCase): + def test_request_with_params(self): + req = make_request(body='{"arg1": "one", "arg2": ["a"]}', + content_type='application/json', + method='POST') + middleware.JsonBodyMiddleware(None).process_request(req) + params = req.environ[middleware.PARAMS_ENV] + self.assertEqual(params, {"arg1": "one", "arg2": ["a"]}) + + def test_malformed_json(self): + req = make_request(body='{"arg1": "on', + content_type='application/json', + method='POST') + _middleware = middleware.JsonBodyMiddleware(None) + self.assertRaises(webob.exc.HTTPBadRequest, + _middleware.process_request, req) + + def test_no_content_type(self): + req = make_request(body='{"arg1": "one", "arg2": ["a"]}', method='POST') + middleware.JsonBodyMiddleware(None).process_request(req) + params = req.environ[middleware.PARAMS_ENV] + self.assertEqual(params, {"arg1": "one", "arg2": ["a"]}) + + def test_unrecognized_content_type(self): + req = make_request(body='{"arg1": "one", "arg2": ["a"]}', + content_type='text/plain', + method='POST') + middleware.JsonBodyMiddleware(None).process_request(req) + params = req.environ.get(middleware.PARAMS_ENV, {}) + self.assertEqual(params, {}) diff --git a/tests/test_overrides.conf b/tests/test_overrides.conf new file mode 100644 index 0000000000..501293c6e7 --- /dev/null +++ b/tests/test_overrides.conf @@ -0,0 +1,5 @@ +[DEFAULT] +crypt_strength = 10 + +[catalog] +template_file = default_catalog.templates diff --git a/tests/test_utils.py b/tests/test_utils.py new file mode 100644 index 0000000000..7409e30c65 --- /dev/null +++ b/tests/test_utils.py @@ -0,0 +1,49 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2012 Justin Santa Barbara +# 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 datetime + +from keystone import test +from keystone.common import utils + + +class UtilsTestCase(test.TestCase): + def test_hash(self): + password = 'right' + wrong = 'wrongwrong' # Two wrongs don't make a right + hashed = utils.hash_password(password) + self.assertTrue(utils.check_password(password, hashed)) + self.assertFalse(utils.check_password(wrong, hashed)) + + def test_hash_edge_cases(self): + hashed = utils.hash_password('secret') + self.assertFalse(utils.check_password('', hashed)) + self.assertFalse(utils.check_password(None, hashed)) + + def test_hash_unicode(self): + password = u'Comment \xe7a va' + wrong = 'Comment ?a va' + hashed = utils.hash_password(password) + self.assertTrue(utils.check_password(password, hashed)) + self.assertFalse(utils.check_password(wrong, hashed)) + + def test_isotime(self): + dt = datetime.datetime(year=1987, month=10, day=13, + hour=1, minute=2, second=3) + output = utils.isotime(dt) + expected = '1987-10-13T01:02:03Z' + self.assertEqual(output, expected) diff --git a/tests/test_versions.py b/tests/test_versions.py new file mode 100644 index 0000000000..6a08865498 --- /dev/null +++ b/tests/test_versions.py @@ -0,0 +1,107 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# 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. + +import json + +from keystone import test +from keystone import config + + +CONF = config.CONF + + +class VersionTestCase(test.TestCase): + def setUp(self): + super(VersionTestCase, self).setUp() + self.load_backends() + self.public_app = self.loadapp('keystone', 'main') + self.admin_app = self.loadapp('keystone', 'admin') + + self.public_server = self.serveapp('keystone', name='main') + self.admin_server = self.serveapp('keystone', name='admin') + + def test_public_versions(self): + client = self.client(self.public_app) + resp = client.get('/') + data = json.loads(resp.body) + expected = { + "versions": { + "values": [{ + "id": "v2.0", + "status": "beta", + "updated": "2011-11-19T00:00:00Z", + "links": [{ + "rel": "self", + "href": ("http://localhost:%s/v2.0/" % + CONF.public_port), + }, { + "rel": "describedby", + "type": "text/html", + "href": "http://docs.openstack.org/api/openstack-" + "identity-service/2.0/content/" + }, { + "rel": "describedby", + "type": "application/pdf", + "href": "http://docs.openstack.org/api/openstack-" + "identity-service/2.0/identity-dev-guide-" + "2.0.pdf" + }], + "media-types": [{ + "base": "application/json", + "type": "application/vnd.openstack.identity-v2.0" + "+json" + }] + }] + } + } + self.assertEqual(data, expected) + + def test_admin_versions(self): + client = self.client(self.admin_app) + resp = client.get('/') + data = json.loads(resp.body) + expected = { + "versions": { + "values": [{ + "id": "v2.0", + "status": "beta", + "updated": "2011-11-19T00:00:00Z", + "links": [{ + "rel": "self", + "href": ("http://localhost:%s/v2.0/" % + CONF.admin_port), + }, { + "rel": "describedby", + "type": "text/html", + "href": "http://docs.openstack.org/api/openstack-" + "identity-service/2.0/content/" + }, { + "rel": "describedby", + "type": "application/pdf", + "href": "http://docs.openstack.org/api/openstack-" + "identity-service/2.0/identity-dev-guide-" + "2.0.pdf" + }], + "media-types": [{ + "base": "application/json", + "type": "application/vnd.openstack.identity-v2.0" + "+json" + }] + }] + } + } + self.assertEqual(data, expected) diff --git a/tests/test_wsgi.py b/tests/test_wsgi.py new file mode 100644 index 0000000000..aa2c01223e --- /dev/null +++ b/tests/test_wsgi.py @@ -0,0 +1,36 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +import json + +import webob + +from keystone import test +from keystone.common import wsgi + + +class ApplicationTest(test.TestCase): + def _make_request(self, url='/'): + req = webob.Request.blank(url) + args = {'action': 'index', 'controller': None} + req.environ['wsgiorg.routing_args'] = [None, args] + return req + + def test_response_content_type(self): + class FakeApp(wsgi.Application): + def index(self, context): + return {'a': 'b'} + + app = FakeApp() + req = self._make_request() + resp = req.get_response(app) + self.assertEqual(resp.content_type, 'application/json') + + def test_query_string_available(self): + class FakeApp(wsgi.Application): + def index(self, context): + return context['query_string'] + + app = FakeApp() + req = self._make_request(url='/?1=2') + resp = req.get_response(app) + self.assertEqual(json.loads(resp.body), {'1': '2'}) diff --git a/tools/convert_to_sqlite.sh b/tools/convert_to_sqlite.sh new file mode 100755 index 0000000000..feb3202f25 --- /dev/null +++ b/tools/convert_to_sqlite.sh @@ -0,0 +1,71 @@ +#!/bin/sh + +# ================================================================ +# +# Convert a mysql database dump into something sqlite3 understands. +# +# Adapted from +# http://stackoverflow.com/questions/489277/script-to-convert-mysql-dump-sql-file-into-format-that-can-be-imported-into-sqlit +# +# (c) 2010 Martin Czygan +# +# ================================================================ + +if [ "$#" -lt 1 ]; then + echo "Usage: $0 " + exit +fi + +SRC=$1 +DST=$1.sqlite3.sql +DB=$1.sqlite3.db +ERR=$1.sqlite3.err + +cat $SRC | +grep -v ' KEY "' | +grep -v ' KEY `' | +grep -v ' UNIQUE KEY "' | +grep -v ' UNIQUE KEY `' | +grep -v ' PRIMARY KEY ' | + +sed 's/ENGINE=MyISAM/ /g' | +sed 's/DEFAULT/ /g' | +sed 's/CHARSET=[a-zA-Z0-9]*/ /g' | +sed 's/AUTO_INCREMENT=[0-9]*/ /g' | + +sed 's/\\r\\n/\\n/g' | +sed 's/\\"/"/g' | +sed '/^SET/d' | +sed 's/ unsigned / /g' | +sed 's/ auto_increment/ primary key autoincrement/g' | +sed 's/ AUTO_INCREMENT/ primary key autoincrement/g' | +sed 's/ smallint([0-9]*) / integer /g' | +sed 's/ tinyint([0-9]*) / integer /g' | +sed 's/ int([0-9]*) / integer /g' | +sed 's/ character set [^ ]* / /g' | +sed 's/ enum([^)]*) / varchar(255) /g' | +sed 's/ on update [^,]*//g' | +sed 's/UNLOCK TABLES;//g' | +sed 's/LOCK TABLES [^;]*;//g' | +perl -e 'local $/;$_=<>;s/,\n\)/\n\)/gs;print "begin;\n";print;print "commit;\n"' | +perl -pe ' + if (/^(INSERT.+?)\(/) { + $a=$1; + s/\\'\''/'\'\''/g; + s/\\n/\n/g; + s/\),\(/\);\n$a\(/g; + } + ' > $DST + +cat $DST | sqlite3 $DB > $ERR + +ERRORS=`cat $ERR | wc -l` + +if [ "$ERRORS" -eq "0" ]; then + echo "Conversion completed without error. Your db is ready under: $DB" + echo "\$ sqlite3 $DB" + rm -f $ERR +else + echo "There were errors during conversion. \ + Please review $ERR and $DST for details." +fi diff --git a/tools/generate_authors.sh b/tools/generate_authors.sh deleted file mode 100755 index c41f07988e..0000000000 --- a/tools/generate_authors.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -git shortlog -se | cut -c8- diff --git a/tools/install_venv.py b/tools/install_venv.py index 9f1a900a8c..11f682b63d 100644 --- a/tools/install_venv.py +++ b/tools/install_venv.py @@ -4,7 +4,7 @@ # Administrator of the National Aeronautics and Space Administration. # All Rights Reserved. # -# Copyright 2010 OpenStack LLC. +# 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 @@ -19,7 +19,7 @@ # under the License. """ -Installation script for Keystone's development virtualenv +virtualenv installation script """ import os @@ -30,13 +30,19 @@ import sys ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) VENV = os.path.join(ROOT, '.venv') PIP_REQUIRES = os.path.join(ROOT, 'tools', 'pip-requires') +PY_VERSION = "python%s.%s" % (sys.version_info[0], sys.version_info[1]) def die(message, *args): - print >> sys.stderr, message % args + print >>sys.stderr, message % args sys.exit(1) +def check_python_version(): + if sys.version_info < (2, 6): + die("Need Python Version >= 2.6") + + def run_command(cmd, redirect_output=True, check_exit_code=True): """ Runs a command in an out-of-process shell, returning the @@ -55,84 +61,68 @@ def run_command(cmd, redirect_output=True, check_exit_code=True): HAS_EASY_INSTALL = bool(run_command(['which', 'easy_install'], - check_exit_code=False).strip()) + check_exit_code=False).strip()) HAS_VIRTUALENV = bool(run_command(['which', 'virtualenv'], - check_exit_code=False).strip()) + check_exit_code=False).strip()) def check_dependencies(): """Make sure virtualenv is in the path.""" + print 'Checking for virtualenv...' if not HAS_VIRTUALENV: print 'not found.' # Try installing it via easy_install... if HAS_EASY_INSTALL: print 'Installing virtualenv via easy_install...', - if not run_command(['which', 'easy_install']): - die('ERROR: virtualenv not found.\n\n' - 'Keystone development requires virtualenv, please install' - ' it using your favorite package management tool') + if not (run_command(['which', 'easy_install']) and + run_command(['easy_install', 'virtualenv'])): + die('ERROR: virtualenv not found.\n\nNova development' + ' requires virtualenv, please install it using your' + ' favorite package management tool') print 'done.' print 'done.' def create_virtualenv(venv=VENV): - """ - Creates the virtual environment and installs PIP only into the + """Creates the virtual environment and installs PIP only into the virtual environment """ print 'Creating venv...', run_command(['virtualenv', '-q', '--no-site-packages', VENV]) print 'done.' print 'Installing pip in virtualenv...', - if not run_command(['tools/with_venv.sh', 'easy_install', - 'pip>1.0']).strip(): + if not run_command(['tools/with_venv.sh', 'easy_install', 'pip']).strip(): die("Failed to install pip.") print 'done.' def install_dependencies(venv=VENV): print 'Installing dependencies with pip (this can take a while)...' - - # Install greenlet by hand - just listing it in the requires file does not - # get it in stalled in the right order - venv_tool = 'tools/with_venv.sh' - run_command([venv_tool, 'pip', 'install', '-E', venv, '-r', PIP_REQUIRES], - redirect_output=False) - - # Tell the virtual env how to "import keystone" - - for version in ['python2.7', 'python2.6']: - pth = os.path.join(venv, "lib", version, "site-packages") - if os.path.exists(pth): - pthfile = os.path.join(pth, "keystone.pth") - f = open(pthfile, 'w') - f.write("%s\n" % ROOT) + run_command(['tools/with_venv.sh', 'pip', 'install', '-E', venv, '-r', + PIP_REQUIRES], redirect_output=False) def print_help(): help = """ - Keystone development environment setup is complete. + Virtual environment configuration complete. - Keystone development uses virtualenv to track and manage Python dependencies - while in development and testing. + To activate the virtualenv for the extent of your current shell + session you can run: - To activate the Keystone virtualenv for the extent of your current shell - session you can run: + $ source %s/bin/activate - $ source .venv/bin/activate + Or, if you prefer, you can run commands in the virtualenv on a case by case + basis by running: - Or, if you prefer, you can run commands in the virtualenv on a case by case - basis by running: + $ tools/with_venv.sh - $ tools/with_venv.sh - - Also, make test will automatically use the virtualenv. - """ + """ % VENV print help def main(argv): + check_python_version() check_dependencies() create_virtualenv() install_dependencies() diff --git a/tools/pip-requires b/tools/pip-requires index 0ec6d99c02..1ff098cd54 100644 --- a/tools/pip-requires +++ b/tools/pip-requires @@ -1,36 +1,33 @@ -# NOTE: You may need to install additional binary packages prior to using 'pip install' -# See the Contributor Documentation for more information +# keystonelight dependencies +pam==0.1.4 +WebOb==1.0.8 +eventlet +PasteDeploy +paste +routes +pyCLI +sqlalchemy +sqlalchemy-migrate +passlib -# Production -httplib2 # handles additional HTTP features such as SSL, HEAD/PUT/DELETE, etc -eventlet # scalable networking lib -paste # wsgi framework -pastedeploy # loads & configures wsgi apps -pastescript # command line frontend -webob # wsgi framework -Routes # URL matching / controller routing -sqlalchemy # core backend -sqlalchemy-migrate # database migrations -lxml # xml library providing ElementTree API -passlib # password hashing -argparse # cli support -prettytable # cli table printing -python-dateutil==1.5 # for date parsing - -# Optional backend: LDAP -python-ldap # authenticate against an existing LDAP server +# for python-novaclient +prettytable # Optional backend: Memcache python-memcached # increases performance of token validation calls # Development -Sphinx # required to build documentation +Sphinx>=1.1.2 # required to build documentation coverage # computes code coverage percentages # Testing nose # for test discovery and console feedback +nosexcover unittest2 # backport of unittest lib in python 2.7 webtest # test wsgi apps without starting an http server pylint # static code analysis -pep8 # checks for PEP8 code style compliance +pep8==0.6.1 # checks for PEP8 code style compliance mox # mock object framework + +-e git+https://review.openstack.org/p/openstack/python-keystoneclient.git#egg=python-keystoneclient +-e git+https://review.openstack.org/p/openstack-dev/openstack-nose.git#egg=openstack.nose_plugin diff --git a/tools/pip-requires-test b/tools/pip-requires-test new file mode 100644 index 0000000000..873712ecd3 --- /dev/null +++ b/tools/pip-requires-test @@ -0,0 +1,24 @@ +# keystonelight dependencies +pam==0.1.4 +WebOb==1.0.8 +eventlet +PasteDeploy +paste +routes +pyCLI +sqlalchemy +sqlalchemy-migrate +passlib +python-memcached + +# keystonelight testing dependencies +nose +nosexcover + +# for python-keystoneclient +httplib2 +pep8 +-e git+https://github.com/openstack/python-keystoneclient.git#egg=python-keystoneclient + +# for python-novaclient +prettytable diff --git a/tools/rfc.sh b/tools/rfc.sh deleted file mode 100755 index ed287e8b43..0000000000 --- a/tools/rfc.sh +++ /dev/null @@ -1,136 +0,0 @@ -#!/bin/sh -e -# Copyright (c) 2010-2011 Gluster, Inc. -# This initial version of this file was taken from the source tree -# of GlusterFS. It was not directly attributed, but is assumed to be -# Copyright (c) 2010-2011 Gluster, Inc and release GPLv3 -# Subsequent modifications are Copyright (c) 2011 OpenStack, LLC. -# -# GlusterFS is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published -# by the Free Software Foundation; either version 3 of the License, -# or (at your option) any later version. -# -# GlusterFS is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see -# . - - -branch="master"; - -set_hooks_commit_msg() -{ - top_dir=`git rev-parse --show-toplevel` - f="${top_dir}/.git/hooks/commit-msg"; - u="https://review.openstack.org/tools/hooks/commit-msg"; - - if [ -x "$f" ]; then - return; - fi - - curl -o $f $u || wget -O $f $u; - - chmod +x $f; - - GIT_EDITOR=true git commit --amend -} - -add_remote() -{ - username=$1 - project=$2 - - echo "No remote set, testing ssh://$username@review.openstack.org:29418" - if ssh -p29418 -o StrictHostKeyChecking=no $username@review.openstack.org gerrit ls-projects >/dev/null 2>&1 - then - echo "$username@review.openstack.org:29418 worked." - echo "Creating a git remote called gerrit that maps to:" - echo " ssh://$username@review.openstack.org:29418/$project" - git remote add gerrit ssh://$username@review.openstack.org:29418/$project - return 0 - fi - return 1 -} - -check_remote() -{ - if ! git remote | grep gerrit >/dev/null 2>&1 - then - origin_project=`git remote show origin | grep 'Fetch URL' | perl -nle '@fields = split(m|[:/]|); $len = $#fields; print $fields[$len-1], "/", $fields[$len];'` - if add_remote $USERNAME $origin_project - then - return 0 - else - echo "Your local name doesn't work on Gerrit." - echo -n "Enter Gerrit username (same as launchpad): " - read gerrit_user - if add_remote $gerrit_user $origin_project - then - return 0 - else - echo "Can't infer where gerrit is - please set a remote named" - echo "gerrit manually and then try again." - echo - echo "For more information, please see:" - echo "\thttp://wiki.openstack.org/GerritWorkflow" - exit 1 - fi - fi - fi -} - -rebase_changes() -{ - git fetch; - - GIT_EDITOR=true git rebase -i origin/$branch || exit $?; -} - - -assert_diverge() -{ - if ! git diff origin/$branch..HEAD | grep -q . - then - echo "No changes between the current branch and origin/$branch." - exit 1 - fi -} - - -main() -{ - set_hooks_commit_msg; - - check_remote; - - rebase_changes; - - assert_diverge; - - bug=$(git show --format='%s %b' | perl -nle 'if (/\b([Bb]ug|[Ll][Pp])\s*[#:]?\s*(\d+)/) {print "$2"; exit}') - - bp=$(git show --format='%s %b' | perl -nle 'if (/\b([Bb]lue[Pp]rint|[Bb][Pp])\s*[#:]?\s*([0-9a-zA-Z-_]+)/) {print "$2"; exit}') - - if [ "$DRY_RUN" = 1 ]; then - drier='echo -e Please use the following command to send your commits to review:\n\n' - else - drier= - fi - - local_branch=`git branch | grep -Ei "\* (.*)" | cut -f2 -d' '` - if [ -z "$bug" ]; then - if [ -z "$bp" ]; then - $drier git push gerrit HEAD:refs/for/$branch/$local_branch; - else - $drier git push gerrit HEAD:refs/for/$branch/bp/$bp; - fi - else - $drier git push gerrit HEAD:refs/for/$branch/bug/$bug; - fi -} - -main "$@" diff --git a/tools/validate_json.py b/tools/validate_json.py deleted file mode 100644 index a33a08e079..0000000000 --- a/tools/validate_json.py +++ /dev/null @@ -1,128 +0,0 @@ -""" -Searches the given path for JSON files, and validates their contents. - -Optionally, replaces valid JSON files with their pretty-printed -counterparts. -""" - -import argparse -import collections -import errno -import json -import logging -import os -import re - - -# Configure logging -logging.basicConfig(format='%(levelname)s: %(message)s') - -# Configure commandlineability -parser = argparse.ArgumentParser(description=__doc__) -parser.add_argument('-p', type=str, required=False, default='.', - help='the path to search for JSON files', dest='path') -parser.add_argument('-r', type=str, required=False, default='.json$', - help='the regular expression to match filenames against ' \ - '(not absolute paths)', dest='regexp') -parser.add_argument('-b', required=False, action='store_true', - default=False, help='beautify valid JSON (replaces file contents)', - dest='beautify') -args = parser.parse_args() - - -def main(): - files = find_matching_files(args.path, args.regexp) - - results = True - for path in files: - results &= validate_json(path) - - # Invert our test results to produce a status code - exit(not results) - - -def beautify(path, ordered_dict): - """Replaces the given file with pretty-printed JSON""" - # convert dict to str, preserving original key order - new_contents = json.dumps(ordered_dict, sort_keys=False, indent=2) - - # json.dump adds a trailing space - new_contents = new_contents.replace(' \n', '\n') - - # json.dumps doesn't include a newline, for obvious reasons - new_contents += "\n" - - return replace_file(path, new_contents) - - -def validate_json(path): - """Open a file and validate it's contents as JSON""" - try: - contents = read_file(path) - - if contents is False: - logging.warning('Insufficient permissions to open: %s' % path) - return False - except: - logging.warning('Unable to open: %s' % path) - return False - - try: - ordered_dict = json.loads(contents, - object_pairs_hook=collections.OrderedDict) - except: - logging.error('Unable to parse: %s' % path) - return False - - if args.beautify: - beautify(path, ordered_dict) - - return True - - -def find_matching_files(path, pattern): - """Search the given path for files matching the given pattern""" - - regex = re.compile(pattern) - - json_files = [] - for root, dirs, files in os.walk(path): - for name in files: - if regex.search(name): - full_name = os.path.join(root, name) - json_files.append(full_name) - return json_files - - -def read_file(path): - """Attempt to read a file safely - - Returns the contents of the file as a string on success, False otherwise""" - try: - fp = open(path) - except IOError as e: - if e.errno == errno.EACCES: - # permission error - return False - raise - else: - with fp: - return fp.read() - - -def replace_file(path, new_contents): - """Overwrite the file at the given path with the new contents - - Returns True on success, False otherwise.""" - try: - f = open(path, 'w') - f.write(new_contents) - f.close() - except: - logging.error('Unable to write: %s' % f) - return False - return True - - -if __name__ == "__main__": - main() diff --git a/tox.ini b/tox.ini index c81dd64d8a..8779ba73f1 100644 --- a/tox.ini +++ b/tox.ini @@ -1,14 +1,21 @@ [tox] -envlist = py26,py27 +envlist = py26,py27,pep8 [testenv] -deps = -r{toxinidir}/tools/pip-requires -commands = /bin/bash run_tests.sh -N +deps = -r{toxinidir}/tools/pip-requires-test +commands = nosetests [testenv:pep8] deps = pep8 -commands = /bin/bash run_tests.sh -N --pep8 +commands = pep8 --exclude=vcsversion.py,*.pyc --repeat --show-source bin keystone setup.py -[testenv:coverage] -deps = pep8 -commands = /bin/bash run_tests.sh -N --with-coverage +[testenv:hudson] +downloadcache = ~/cache/pip + +[testenv:jenkins26] +basepython = python2.6 +deps = file://{toxinidir}/.cache.bundle + +[testenv:jenkins27] +basepython = python2.7 +deps = file://{toxinidir}/.cache.bundle