python-novaclient-2.17.0/0000775000175400017540000000000012306433034016416 5ustar jenkinsjenkins00000000000000python-novaclient-2.17.0/HACKING.rst0000664000175400017540000000267412306432770020233 0ustar jenkinsjenkins00000000000000Nova Client Style Commandments ============================== - Step 1: Read the OpenStack Style Commandments http://docs.openstack.org/developer/hacking - Step 2: Read on Nova Client Specific Commandments --------------------------------- None so far Text encoding ------------- - All text within python code should be of type 'unicode'. WRONG: >>> s = 'foo' >>> s 'foo' >>> type(s) RIGHT: >>> u = u'foo' >>> u u'foo' >>> type(u) - Transitions between internal unicode and external strings should always be immediately and explicitly encoded or decoded. - All external text that is not explicitly encoded (database storage, commandline arguments, etc.) should be presumed to be encoded as utf-8. WRONG: mystring = infile.readline() myreturnstring = do_some_magic_with(mystring) outfile.write(myreturnstring) RIGHT: mystring = infile.readline() mytext = s.decode('utf-8') returntext = do_some_magic_with(mytext) returnstring = returntext.encode('utf-8') outfile.write(returnstring) Running Tests ------------- The testing system is based on a combination of tox and testr. If you just want to run the whole suite, run `tox` and all will be fine. However, if you'd like to dig in a bit more, you might want to learn some things about testr itself. A basic walkthrough for OpenStack can be found at http://wiki.openstack.org/testr python-novaclient-2.17.0/CONTRIBUTING.rst0000664000175400017540000000104712306432770021067 0ustar jenkinsjenkins00000000000000If you would like to contribute to the development of OpenStack, you must follow the steps in the "If you're a developer, start here" section of this page: http://wiki.openstack.org/HowToContribute Once those steps have been completed, changes to OpenStack should be submitted for review via the Gerrit tool, following the workflow documented at: http://wiki.openstack.org/GerritWorkflow Pull requests submitted through GitHub will be ignored. Bugs should be filed on Launchpad, not GitHub: https://bugs.launchpad.net/python-novaclient python-novaclient-2.17.0/.mailmap0000664000175400017540000000261512306432770020051 0ustar jenkinsjenkins00000000000000Antony Messerli root Chris Behrens comstud Joe Gordon Johannes Erdfelt jerdfelt Andy Smith termie hwbi Nikolay Sokolov Nokolay Sokolov Nikolay Sokolov Nokolay Sokolov zhangguoqing python-novaclient-2.17.0/test-requirements.txt0000664000175400017540000000023512306432770022665 0ustar jenkinsjenkins00000000000000hacking>=0.8.0,<0.9 coverage>=3.6 discover fixtures>=0.3.14 keyring>=1.6.1,<2.0,>=2.1 mock>=1.0 sphinx>=1.1.2,<1.2 testrepository>=0.0.18 testtools>=0.9.34 python-novaclient-2.17.0/.coveragerc0000664000175400017540000000014512306432770020545 0ustar jenkinsjenkins00000000000000[run] branch = True source = novaclient omit = novaclient/openstack/* [report] ignore-errors = True python-novaclient-2.17.0/ChangeLog0000664000175400017540000012226412306433034020177 0ustar jenkinsjenkins00000000000000CHANGES ======= 2.17.0 ------ * Add os-server-external-events support * Add classifiers for specific versions of Python * oslo sync apiclient and cliutils * Fix in in novaclient, to avoid excessive conns * Revert "'name' should as be optional param on aggregate-update" * oslo-sync of low hanging fruit * Updated from global requirements * 'name' should as be optional param on aggregate-update 2.16.0 ------ * Fix typo in novaclient * Remove usage of module py3kcompat * Updated from global requirements * Invalid client version message unclear * Remove None for dict.get() * Replace assertEqual(None, *) with assertIsNone in tests * Fix i18n messages in novaclient, part II * Update broken command line reference link * Fix spelling miss of password_func variable * Fix copy/paste errors in print messages * Remove invalid parameter of quota-update * Remove tox locale overrides * Fix python 3.3 unit test job * Adds support for the get_rdp_console API * Fixed polling after boot in shell * Update my mailmap * Fix Serivce class AttributeError * [UT] Fixed floating_ip_pools fake return to expected one * [UT] Removed duplicate key from dict in fake baremetal_node * Fixed multi validation and wrong fail calls in unit tests * Fixed super constructor call for TestResponse class * Flavor ExtraSpecs containing '/' cannot be deleted * Removed undefined method in install_env.py file * Fix i18n messages in novaclient, part I * Adds ability to boot a server via the Nova V3 API * Removes unsupported volume commands from V3 API support * Reuse Resource from oslo * Updates nova client to use the latest oslo files * Using common methods from oslo cliutils * Add tests for boot method of v3 shell * Replace basestring by six.string_types * Removes use of timeutils.set_time_override * Fix logic for "nova flavor-show 0#" * Sync with global requirements * Don't call CS if a token + URL are provided * Sync cliutils from oslo * Sync apiclient from oslo * Fix QuotaClassSet and their tests * assertTrue(isinstance) replace by assertIsInstance * Remove the coverage extension code * shell: refactor boot to use _print_server * Don't slugify() None names * Adds volume support for the V3 API * Fixes ambiguous cli output between "None" and NoneType * Support list deleted servers for admin * Using floating-ip-{associate|disassociate} * Removes vim configuration headers * Adds quota usage support for the V3 API * Fix tab-completion of --flags under OS X * Remove class_name parameter from quota_class * Ensure that the diagnostics are user friendly * Code cleanup: use oslo's to_slug() instead of slugify() * Added v3 interfaces in reference doc * Enable pep8 check for config.py in doc * Generate interfaces reference doc * Ensure that nova client prints dictionaries and arrays correctly * Replace some utils.bool_from_str with strutils * Allow empty response in service-list * Nova aggregate-details should be more human friendly * Removed duplicated import * Adding additional tests for novaclient ssh * Fix "device" as the optional para on volume-attach * Adds simple tenant usage support for the Nova V3 API * Adds keypairs support for the Nova V3 API * Adds certificates support for Nova V3 API * Adds aggregates support for Nova V3 API * Adds hypervisor support for Nova V3 API * Adds services support for Nova V3 API * Adds second part of quotas support for Nova V3 API * Adds first part of quotas support for Nova V3 API * Adds availability zone support for Nova V3 API * Adds basic servers support for the Nova V3 API * add support for nova ssh user@host * remove duplicate six import * Allow multiple volume delete from cli like Cinder * Fixed autodoc can't import/find class error * Expose the rebuild preserve-ephemeral extension * Stop using deprecated keyring backends * Adds images support for Nova V3 API * Remove commands not supported by Nova V3 API * Adds agent support for Nova V3 API * Adds flavor access support for Nova V3 API * Adds flavor support for Nova V3 API * Enables H403 pep8 rules * Allow graceful shutdown on Ctrl+C * Enables H306 pep8 rules * Enables E711,E721,E712 pep8 rules * Updates tox.ini to use new features * Updated from global requirements * Remove the release.rst file * Fix docstring on novaclient * add support for server set metadata item * Fix incorrect help message on flavor_access action * Fix inappropriate comment for delete FloatingIP * Enable hacking check for Apache 2.0 license * Sets default service type for Nova V3 API * Fix the inappropriate comment for flavor * Adds a --show option to the image-create subcommand * Updates .gitignore * Allows users to retrieve ciphered VM passwords * Fix inappropriate comment for flavor create api * Fix typo in novaclient * Removes unnecessary pass * Updated from global requirements * Discrepancy between README.rst and nova help * nova security-group-* should support uuid as input * Change "project" to "project_id" in cloudpipe-create * Fix single H234 Bug to make Hacking 0.8 pass * Flatten hypervisor-show dictionary for printing * Revert "Nova aggregate-details should be more human friendly" * Update mailmap for Joe Gordon * Print security groups as a human readable list * Adds locking to completion caches * Nova aggregate-details should be more human friendly * Make 'nova ssh' automatically fall back to private address * Quote URL in curl output to handle query params * Add --insecure to curl output if required * Apply six for metaclass * Updated from global requirements * Remove deprecated NOVA_RAX_AUTH * Print dicts in alphabetical order * Make os-cache retry on an invalid token * Document and make OS_CACHE work * Revert "Add-in some re-auth logic when using os_cache" * Align mocking pattern for test case * py33: use six.StringIO() to mock stdout/stderr * py33: sort the files parameters of "--files" * py33: sort hosts while treeize AvailabilityZone * py33: unify the input of request to json format * py33: align the order of parameters for urlencode() * py33: sort dict for test_add_floating_ip_to_fixed * py33: iteration order of dict is unpredictable * Updated from global requirements * py33: 'str' does not support the buffer interface * assertEquals is deprecated, use assertEqual * py33: align the order of parameters for urlencode() * Add shelve/unshelve/shelve-offload command * py33: uuid verification in find_resource() * py33: don't encode security_group * Add-in some re-auth logic when using os_cache * if we have a valid auth token, use it instead of generating a new one * py33: safe_encode() returns bytes in Python 3 * py33: unknown encoding: base64 Edit * Fix AttributeError in Keypair._add_details() * Fixed several test failures on Python3 * Make nova CLI use term "server" where possible * py33: dict.keys() is not a list in python3 * Corrected several usage of keys() for Python 3 * py33: 'dict_keys' object does not support indexing * Corrected usage of len(filter(...)) * Update pbr usage * Clean up a little cruft * Novaclient shell list command should support a minimal server list 2.15.0 ------ * Add v3 HostManager * Create v3 tests directory * Fix the print order of quota-show * assertEquals is deprecated, use assertEqual * Small bugfix for client v3 * Modify --num-instances flag description to clarify limit upper bound * Add a block device for the image when using BDMv2 * python3: Compatibility for iteritems differences * python3: Fix traceback while running unit tests * python3: Fix Traceback while running unit tests * Unittests added for client v1_1 * Python3: Fix traceback while running unit tests * Python3: Use six.StringIO for io.Bytes() * Update oslo from oslo-incubator * Add delete method to Flavor class * New syntax to boot from a block device mapping * Allow name argument to flavor-access-add * Add support for os-assisted-volume-snapshots * Suport instance list pagination in novaclient, Part I * Add interface for listing security groups of an instance * Added support for running the tests under PyPy with tox * python3: Fix imports for py2/py3 * Upgrade to Hacking 0.7 * Sync py3kcompat from oslo * Update mailmap * Update mailmap * Added 'nova migration-list' command * Fix and gate on H501, no locals for string formatting * Update oslo * Allow name argument to flavor-access-add * python3: Fix traceback while running tests * Fix the help messages to specify image/flavor name * Clean up inaccurate docstrings of server list() method * Remove old references * Enable v3 api code * Begin adding v3 api support * change 'Host' object's 'host' attribute to 'host_name' * Updated from global requirements * Do not restrict flavor to only ID and integers * Fix typo and grammar in docstring only 2.14.1 ------ * remove requests version max 2.14.0 ------ * Sync with global requirements * Add support for swap_volume * FakeClient: fix the arguments of a string format * Support programmatic use of disk config extension * Check whether the security group id is integer * Fixing host-action on V2 * Add user quota client API support * Fix net-id metavar for interface-attach * make findall in novaclient/base.py more efficient * Fix the help text process and the generated wrong help * Remove python 2.4 and python 2.5 support * Enable force_delete and restore instance via novaclient * Add name argument to aggregate commands * Add name argument to hypervisor commands * recognize 429 as an rate limiting status * Fix backwards-incompatible API change (method signature) * Fix and enable gating on H402 * Add AgregatesManager.get() * Skip setting volume_size if not given * Fix interface-list got none mac address * Remove uncessary code related to nova start/stop * make v2_auth and plugin_auth explictly return their results * Sync install_venv_common from oslo * Clean up and make HACKING.rst point to openstack-dev/hacking * CLI for disable service reason * Allow tenant ID for authentication * Adds zsh completion * Bring stdout/stderr capturing in line w/ nova * Fixup trivial License Header mismatch * Remove Diablo compatibility options * python3: Fix print statements * python3: Compatibility for iteritems differences * python3: Fix unicode compatibility python2/python3 * Return Customer's Quota Usage through Admin API * Discard possibly expired token before re-authenticating * Support force update quota * Update help for --nic opt and make net-id or port-id required * Adds support for ExtendedFloatingIps APi extension * Remove explicit distribute depend * Cells Support * Set default value of flavorid to "auto" * Migrate each instances of a host to another * Set/Delete metadata on all instances of a host * The 'nova keypair-show key_name' command added * Use Python 3.x compatible except: construct * Delete a quota through admin api * Exit w/ valid code when no servers are deleted * Evacuate each instance from one host to another * python3: Introduce py33 to tox.ini * Start using Hacking and PyFlakes * Add update method of security group name and description * Fix shell tests for older prettytable versions * Provide nova CLI man page * Improve error messages for invalid --nic / --file * 100% test coverage for security groups and rules * Add MethodNotAllowed and Conflict exception classes * Move tests into the novaclient package * Add CONTRIBUTING file * Rename requires files to standard names * Code cleanup in advance of flake8 * Migrate to flake8 * Revert "Support force update quota" * Only add logging handlers if there currently aren't any * Convert to more modern openstack-common.conf format * Cleanup unused local variables * Reuse oslo for is_uuid_like() implementation * Synchronize code from oslo * Migrate to pbr * Cleanup nova subcommands for security groups and rules * Make ManagerWithFind abstract and fix its descendants * Cleanup some flavor commands * Fix the default parameter in print_list * Fix for --bridge-interface being ignore by nova network-create * Add setuptools_git-*.egg to .gitignore * Expose retry_after attribute of OverLimit exception * Adds extended status fields to nova list * Clean up exceptions.from_response * Allow deleting multiple images from shell * Synchronize code from oslo * Add 'flavor-list --all' admin switch * Fix nova instance-action-list output field and order * Make list flavor show extra specs optional * Use HTTP keep-alive feature in HTTPClient class * Cleanup unused import * Make --vlan option work in network-create in VLAN mode * Support force update quota * make sure .get() also updates _info * Add coverage-reset command to reset Nova coverage data * Fixing shell command 'service-disable' description * Correct a unit test failure that crept into trunk * Fix problem with nova --version * Make "multi_host" True when it is set to 'T' in network_create * Fix IBM copyright strings * Allow for bypass_url when using proxy_token 2.13.0 ------ * Fix mispelt x-auth-token header * Remove actions command from servers * do not ignore --os-cache * Improve authentication plugins management * Skip security groups w/ no protocol * catch NoKeyringDaemonError from gnomekeyring * Ensure shell tests use isolated env variables set * Update to latest openstack.common.setup * setuptools: remove data_files section * Use correct filter name for listing of instances 2.12.0 ------ * Don't check build/ for pep8 violations * Add support for retrieving instance-actions info * Split commands properly for bash completion test * Remove extraneous output during testing * Use setuptools-git to include files from the repo * Update tools/pip-requires for prettytable * Fix keypair-delete help documents * Add support for the new fixed_ip quota * Set up debug level on root logger * Remove unused import * Fix Copyright Headers from LLC to Foundation * Removes tenant IDs checking for nova quota operations * Make os-services API extensions consistent with Nova * Revert API changes in "Unify Manager._update behaviour" * Use keyring for testing * Show Tenant_ID for secgroup-list with all-tenant * Additional "Unify Manager._update behaviour" cleanup * Add wrap option to nova credentials for humans * Check if tenant flag is uuid_like for all quota operations * Fix nova boot --num-instances option checking * Fix typo in error message * Extend test coverage for v1_1/shell.py * Decodes input and encodes output * Fixed bug with password prompt, added tests * Make ip_protocol parameter in security groups rules case insensitive * Fixes the output of 'os-evacuate' command * Update the docstring of cloudpipe-configure command * Accept 201 status code on POST * Fix how tests were failing due to missing attributes * Missing import for gnomekeyring * A minimum of Python3 fixes so that installation works without errors/warnings * Allows admins to reset-network of an instance * Remove prov_vlan_id from baremetal * Add support for os-attach-interfaces * Added limit to image-list in a preparatory step toward addressing bug 1001345 * Extend test coverage (shell, fping) 2.11.1 ------ * Issue when gnomekeyring is present but not the current backend * Avoid doing a deep copy on the availability zone manager * Allow extensions to provide a name when discovered on the python path * Fix IOError with gnomekeyring.find_network_password_sync * Expand and improve baremetal API * Fix nova availability-zone-list for admin users * Make availability_zone in aggregate_create optional * Corrects 2nd argument type for logging 2.11.0 ------ * Add format options to 'nova coverage-report' * Update to requests >= 0.8 * Mask permissions on private key files * Fix run_tests.sh --coverage * Support showing extra fields in server list * management_url not set by authenticate method * Update .coveragerc * Show the summary or details of all availability zones of a region * Upgrade to pep8 1.3.3 * Fixed 7 pep8 errors * Live migration with an auto selection of dest * Add help about the id 'auto' for flavor-create * Fix default format of 'nova coverage-report' * Add usage command to show usage for single tenant * Store tenant_id from keystone and use for quotas * Show the details of the added bare-metal resource * Fix the usage of password, keyrings, and tokens * Added homedir path expansion for keypair-add * Migrate from nose to testr * _get_secgroup returns first group even if multiple groups match * Fix bash completion on osx * Check tenant_id's format in "nova quota-update" * ClientExceptions should include url and method * Adds baremetal nova API support * RateLimit does not have method attribute * make print_dict split strings with newlines into multiple rows * Allow for image selection using the metadata properties * Add support for get_spice_console RPC API * Ensure list output function can support non-sorting printing * Allow request timeout to be specified * Implement get password for novaclient * Adds tenant network support to the client * Update functionality of coverage extension * Fix a couple of broken shell tests * Update hosts API action calls (startup etc.) * When logging request include request data * Add support for instance evacuate * Fix the help text of add-fixed-ip * Move from untitest2 to testtools * Update README.rst * Unify Manager._update behaviour * Fix some usage messages of 'nova volume-*' * add num_instances option for nova boot * Use requests module for HTTP/HTTPS * Fix find for alphanumeic flavor id/name * Make --tenant a required arg for quota-show * Add support for the coverage extension * Specify some arguments by name * Makes the OS_NO_CACHE env variable work again * Add optional argument to include reservations in os-used-limits * Add nova client support for nova-manage agent command * Adds --os-cache to replace old --no-cache * Adds support for security group/rules quotas * Adds nova client support for nova-manage network command * add host-update help info param * Fix argument checking method for 'nova list --flavor' command * Fix a wrong substition for '-h' in bash completion * Fixed nics param ignored when bdm is specified * Adds support for key_pairs quota * Adds support for injected_file_path_bytes quota * Adds nova client support for nova-manage floating command 2.10.0 ------ * Remove unnecessary casts in flavor create * Validate that rxtx_factor is a float * Adds nova client support for nova-manage vpn command * Fix aggregate command help messages * Add nova client support for nova-manage account scrub command * Adds nova client support for nova-manage fixed command * Implement fping calls in nova client * Expand help message for 'migrate' to explain how the new host is selected * Improved quota output * Boot from volume without image supplied * Added --extra-opts to the nova ssh command * Cleans up the flavor creation code. Fixes bug 1080891 * Adding support to filter instances by tenant from the admin api * Make sure create_image returns result * make tenant id optional for quota-defaults and quota-show * fix hypervisor-servers for hypervisors without servers * discover extensions via entry points * show help when calling without arguments * Add nova client support for nova-manage service command * Updated the help text for nova list command * Fixes setup compatibility issue on Windows * include projectid in the cache key * Fixes utils.findresource checking for integer * Allows deletion of multiple servers through CLI * Add ability of nova client to display availability zones when listing hosts * Validate that boolean parameters are boolean * Auto-Assign Flavor ID * Pull in latest openstack-common changes and fix a minor PEP8 issue * Add OpenStack trove classifier for PyPI * Exception handling for 'nova flavor-create' arguments * Add support for backup instance * Add simple os-api extension cli extension * Raises Exception on improper Auth Configuration * Do not prefer ALL_TENANTS environment variable to command line arguments * Encode user data to utf-8 when creating a server * Add --all-tenants option to volume-list 2.9.0 ----- * Show volume and snapshot data on create * Fixes setup compatibility issue on Windows * allow empty network list to be requested * Work around httplib2 tunnelling bug * Add support for all-tenants search opt to secgroup-list * expose os-networks extension to CLI * Add support for Unicode secgroup names * Support flavor extra specs in nova client * Optionally faster 'nova show' * Makes handling of nic args more robust * Show instances built from deleted snapshots * Add ConnectionRefused exception 2.8.0 ----- * Fix usage-list date range to use UTC time * Show POST in debug with curl * Fixes doc string and string formatting * Add the image_id arg to volume create * Make region case insensitive * Fix PEP8 issues * Add -X to DELETE and PUT in debug mode * Implement project specific flavors API, client bindings * Add missing port-id usage info * Change '_' to '-' in options * Adding --version option * Added -nic port-id= support * Implement network calls in nova client * Add nosehtmloutput as a test dependency * split req and response logging this allows capture of timestamps prior to and after request for timing also did some pep8 1.3 cleanup while I was in there * Add availability_zone support for volume creation * Adds support for autogenerated device on attach * Allow resources to use any field as 'name' * gitignore ChangeLog and add to MANIFEST.in * Allow different auth providers via plugin system * Better handling of stale tokens (no more 401's) * change image list and network list data to be sorted by name rather than UUID * Relex prettytable depend to match glanceclient 2.7.0 ----- * Add call to get hypervisor statistics * Fix image-create --poll race-condition * set admin password during instance creation * Clarify usage of --insecure flag * Fix resize polling * Add support for modification of instance Security Group * Add support for hypervisor-uptime * Install test-requires in development venv * 'endpoints' and 'credentials' work with token caching * This should fix a problem with overly aggressive token caching * Flavor-list sort by ID numerically * Bring back the output from client.http_log() 2.6.10 ------ * Add hypervisor information extension * More friendly keyring support when token caching is off * Whoops, the last changes to keyring introduced some problems with v1.1 auth tests * Auth token caching on by default. --no_cache to disable. Better bypass support too * Add host-list command * Indicate unused variables and other misc. house cleaning * don't bash-complete the '-h' option * Add read_versioninfo method * Turn multiple hints with the same key into a list * Cleanup of setup.py usage of openstack-common * Implement post-tag versioning numbering * Small doc cleanup round * Update Contributing blurb in the docs * Update for blueprint general-host-aggregates 2.6.1 ----- * Admin action to reset states * Filter out v1.0 endpoints * option to bypass managment endpoint and timings support * Removes NOVACLIENT_DEBUG from client code * Fix spelling errors in aggregates section * Move docs to doc * Lock prettytable dep at v0.6 * Removed generate_authors.sh since it's no longer used * nova show cmd displays unique flavor and image id * Use openstack-common for AUTHORS generation * Add .tox to .gitignore * Add start and stop to server actions * Adds flavor-show support * doc: fix and clarify the --meta option help * Lock pep8 at v1.1 * Turn on verbose test output folsom-1 -------- * Align tox.ini with standards * make nova bash-complete faster and more accurate * refactored --service_name to only work with compute calls and added --volume_service_name for volume calls * removed int requirement for volume_id on snaps * Updated to new prettytable api. Fixes bug 995818 * Allow server name to be specified for actions and diagnostics * Don't force volume id to int and allow search by name * Fix LP #990667 - Keypair __repr__ referencesuuid * really output the description of an exception * Limit hint/nic parsing to one split on '=' * update README.rst,add args "service_type" when getting endpoints * Rename NOVA_VERSION to OS_COMPUTE_API_VERSION * Raise exception on all 4xx and 5xx responses * Update unittests to be Python 2.6 compatible * Display the request id on error response * Make '--help' argument more useful * Fixed the subcommand error message for nova shell * Request ID when name is ambiguous * Set resources as loaded on get * Miscellaneous code cleanup * add packages using find_packages() * set 'compute' as default endpoint bug fix for #970808 * Add -i/--identity option to 'nova ssh' * Improve 'nova ssh' error message * Fix spelling of curent in list sec groups * Set up the log handler only once * Remove serverId lookup in volume attachments * Handle server_id and serverId in volume list * Added cloudpipe support. Fixes bug 962286 * Proposed HACKING guidelines for string encoding * Add missing tools and tox.ini to tarball * Fixes bug #959262 - Prevent a failure to create the cache directory from causing an exception * Improve the error message from the nova shell * Adds NOVACLIENT_INSECURE option * Implement quota classes * Open Folsom essex-rc1 --------- * Adding Console Log to CLI * Change CLIAuth arg names * Add suport for instance locking/unlocking * Add --poll for instance snapshots * Add human-friendly ID support * Fixes lp#948685 proxy_token and proxy_tenant_id behavior * Separate UUID caches for different endpoints * Remove trailing whitespaces in regular file * Adds --ipv6 and --port to ssh convience command * Add --poll for long running actions essex-4 ------- * Add support for volume types * Makes novaclient use the volumes endpoint * Fix for backward compatibility with stable/diablo flavors * Add support for ephemeral_gb to novaclient * allow '=' inside value of --meta=key=value * bug 932408: python-novaclient miss OSAPI host operations * Add ssh convenience command * Allow UUID_CACHE_DIR overriding via env variable * Removes zones * Fixes bug 925644: move dotfiles into dir * add support for --config-drive 'boot' command * shell: Hook --debug up to more stuff * Properly handle KeyErrors * adding credentials and endpoints output for debugging * Fixes bug 924588: Remove proto-keystone client from novaclient * Fix bug 904364: Consistiently handle trailing '/' on URLs * Adding describe-resource subcommand * Add Accept: applicaton/json header to all service requests. Fixes bug 904436 * Blueprint cli-auth: common cli args * Add --all_tenants option to 'nova list' * Adding live migration subcommand * Handle Ambiguous Endpoints Correctly * Implementing Scheduling Hints * Remove non-working --key_path argument on boot * Fix datetime issue with usage_data * blueprint host-aggregates: client bindings * moves the "help" in the usage information of a wrong command to the correct position essex-3 ------- * Implementing client for new x509 support in nova * Add flavor create/delete support * Add a 'usage' module and 'usage-list' cli command * Implement virtual interfaces servers api * Print adminPass when rescuing an instance * do not require NOVA_VERSION in env, default to 1.1 * Match create_image on server object and manager * Catch novaclient up with renaming and other nova changes * Add server.get_vnc_console functionality to python-novaclient * Fix bad api call, 'migrate' is an action * Adding rebuild/resize hooks * Implementing Floating Ip Pools * Get ImpLoader from ImpImporter for Py2.6 * Discover extensions via Python's import paths * PEP8 python-novaclient cleanup * show 409 responses * Added command-line interfaces for the floating ip DNS api to nova * Fix Quota ant SecurityGroup resources refreshing * Clean FloatingIPDNS resource * Install a good version of pip in the venv * Add tox.ini file * Add missing returns and remove superfluous ones * Fix typo in endpoint_name help string * Add the python api for floating IP DNS * Abstract Client building into novaclient.client * Remove unused imports and fix NameError on exc * Improve the test framework to handle urls with args * Simplifying get_console_output client interface * Removing cache-busting query param (fresh) * Adding return statement to get_console_output * python-novaclient missing pep8 in pip-requires * utils.find_resource fixes + fix for volumes * Add list() method to ManagerWithFind * Extensions can now modify resources * more work towards standardize config * Allow to not specify image if block_device_mapping is set * Adding support for the os-getConsoleOutput server action * Add 'discover' command for Keystone discovery and version listing * User friendly help message * Do no depends on argparse for Python >= 2.7 * standardize environmental settings for cli auth * Removed v1.0 support * Making contrib a Python package * Adding extension framework * Fix typo in README * Accept 1 and 2 as version choices * Add support for RAX authentication * Align run_tests.sh with nova essex-2 ------- * Switch versioning to common Nova versioning * Fix PEP8 error * Add MANIFEST.in and setup.cfg back * Adding 'absolute-limits' and 'rate-limits' * Fixing all remaining pep8 errors * Clean up image-list cli command * Clean up image-show * Updated README.rst * Converting rxtx_cap and rxtx_quota to rxtx_factor * Gracefully handle failure to cache UUID's. Bug #897885 * Change 'zone_blob' key to 'blob' in create server. bug 893183 * Fix spacing errors in authentication exceptions * Adding UUID cache for bash autocompletion * Revert api_key change in novaclient Client argument * Adds bash completion support and cleans up setup.py * Rewriting admin-only calls as server actions * Add rfc.sh * Add .gitreview config file for gerrit * pep8 * fix tests * trunk merge * Add support for specifying VIF networks while booting * Use a try/finally to ensure state restoration * Follow redirects when calling out to Keystone * Modified as per code-review comments: - Renaned snapshot to volume-snapshot - Created a new file for volume snapshots * few missing references to api_key * tests working * started * added --endpoint_name support * Add back display of adminPass to boot * Boot now works with limited info returned from server * fixed missing line continuation characters in shell.py * PEP8 cleanups of utils, and the v1_?/shell.py files * minor pep8 tweaks * corrected argument order and replaced tabs with spaces * resolved merge conflict * added a space after url * Added the option --insecure. This disables SSL certificate validation * Updated the novaclient shell to display the parent server id that the image came from * Fixed description for block_device_mapping parameter * minor fixes * Added support for boot from volume (or snapshot) * version update * minor tweaks and long overdue pep8 * new service catalog semantics * Added support for listing/creating/deleting snapshots of nova volumes. Also implemented the supporting CLI commands. Requires the OS API extension, 'os-snapshots' * Updated volume-create command to accept an optional attribute, snapshot_id. This enables the user to create a volume from a snapshot * Fixes #133 -- Keystone Client fetches correct service type and endpoint * fix tests * typo * merged and fixed pshkitin's keypair work * doc improvements * Added support to specify more boot options * Updated volume attach/detach commands to accept server name (in addition to server id). Code review comments: https://github.com/rackspace/python-novaclient/pull/125/files#r169829 * Booting server with specific key is implemented * Added commands to work with keypairs * make description consistent * remove extra space * add ability to create source group rules * don't expose ids to end user * work on formatting for secgroup rules * display floating ip on create * Add CLI for security groups and rules * raise exception if floating_ip is not found in floating-ip-delete * Add cli for floating ips * Added support to specify more boot options * Don't filter endpoints when filter_value is non-truthy * Added the following CLI commands to access nova volumes: volume-attach Attach a volume to a server. volume-create Add a new volume. volume-delete Remove a volume. volume-detach Detach a volume from a server. volume-list List all the volumes. volume-show Show details about a volume * now uses tenantName vs. tenantId to auth * version bump * removed unicode casts * cleaned up exception handling * new service catalog implementation * change auth cred format for keystone * Added methods to get, attach and detach volumes to/from running instances * Added support to access nova-volume api (v1.1 extension) - Only the basic functionality (create, delete, list) is implemented * add todo to update doc strings so that they reflect extension/optional-ness * update doc strings * add key_name to servers.create * Make sure flavor is a type of int * removed debugging * token support * fixed unknown service * properly uses keystone admin endpoint for token lookup * proxy token support - no tests * readme fix * service catalog with multiple endpoints per service * Add ability to force debugging via os environ * merge fixup * version bump * readme * service catalog as auth parameter * service name support * Extend lazy loading support to Weighting * Fix unittests breakage in test_shell * Fix #109 (nova show name not working) * Add userdata support * Remove extra NOVA_PROJECT_ID * Fix unittests breakage from merge 3507905 * Add 'meta' command to allow set/delete of metadata items on servers. Added ability to run multiple assert_called tests from one test function * add build, dist, python-novaclient.egg-info to .gitignore * fixes odd __get_attr__ behavior in 2.6.5 * conflict fixed * catch misssing id * Add body in debugging * Fix test installation exclude * Add support for image metadata to be viewed, added, updated, and deleted * Bump the release version * fixed up zone-add * Reducing v1_1.base to just booting manager * tests working * in progress - adding zone name * ensure we have auth_url and project_id for !1.0 * Updated error message as suggested by bcwaldon * take auth token param * Do not assume default for image and flavor * expanding on concept of 'loaded' * limiting resource lazyloading to a single query * Fix extra # char as noticed by jk0 * Add piston service_catalog * Add anotherjesse keystone here * Fix loop properly * Make sure we can do a get on the base class * Client changes for username and password in zone add * fix for chmouel's comment, and tweaks to tests * support for floating_ips + D4 * make __repr__ more useful with default behavior, rather than juse displaying id * a few tweaks to get the client talking to nova * more cleanup * progress on security groups * updating version * updating for new rebuild format * adding tests * cleaning up find_resource method to support str/int ids, uuids, and integer-like names * Fix #85 * fixing the shell tests * Fixed 1.0 and unit tests * Added support for 1.0 and added unit tests * Updated rescue/unrescue to use public API * removing extra space * updating quotas and tests with the format which recently landed in nova * fixing up a few pep8 issues, and pointing client to the new endpoint * Properly make image_id a requirement to be int * Make sure the image id is an integer * really fixed * accidentally deleted a comment when fixing conflict * pep8 issues * update readme to talk about keystone with CLI and use 1.1 api * Switch API path to match http://bazaar.launchpad.net/~tpatil/nova/os-security-groups/revision/1369 * Fix API path * fix display_name references that should have been instance_name * removed fixed_ip from v1.1 shell. Use --ip instead. Fixed up rest of other search options from last commit * start add of --image, --flavor, --status, and --host options to 'list' command. also fix up differences with --name and --display_name compared to how nova implementation turned out * Security groups cleanups * Added redirect tests, changed wrong status in test_authenticate_success * Added self.auth_url updating, WrongResponse exception * add note about keystone / auth 2.0 * Clean up id handling and pass basic tests * Add security group rules * Eradicate TABs, make tests run * missed a conflict * merge master * Initial security groups code * adding unittest * removing extra newline * adding email to .mailmap * catching authorization failure (x-server-management-url KeyError) * bring up-to-date with lp:~cloudbuilders/nova/os-keypairs * keypair api * add license headers * add support for quotas * pep8, again * Recursion handling * Added .mailmap file for AUTHORS * Updated authors and fixed tools/generate_authors.sh * Fixes copyright notice and adds script to gen AUTHORS * keypair api * pep8 * Status code 305 fix, ClientExceptions if we can not handle response * whitespace cleanups * pep8 cleanups after the rebase * Adds run_tests.sh and virtualenv support * pep8 in tests * pep8 in novaclient * Add Hacking and Authors to bring this into accordance with OpenStack coding standards * redirect * Redirection handling * cleaning up boot output; upping version * Added documentation for NOVA_VERSION * Make it possible to authenticate against keystone * Removed the bodies again * Corrected docs * off by one * Missed a conflict 2.6.0 ----- * manual merge * Accidently had a reference to ipgroup still * Merged v1.0 functionality into v1.1 so we don't lose any features by...upgrading? * Fix for failing tests because boot response now requests additional information * formatting updates * novaclient -> nova in some documentation as per feedback * Removed unneeded print * Change create-image back to image-create, and increased version to 2.6.0 * Updated --version to default to NOVA_VERSION, quick fix * Updated --version to default to NOVA_VERSION * osc -> novaclient * Cleaned up v1.0 and v1.1 test setup to remove globals and encapsulate custom asserts. Still duplicate code, but closer to being able to remove. Now tests set up OpenStackClient much closer to how users will do it, minus the stubbing of the client * Wrong client was getting loaded * Grrrr, bad import * Tests now run correctly for v1.1 and v1.0 * Updated the default version back to 1.0, as there are some quirks with 1.1 * Tests working again...merged in some work we did earlier * Split everything down the middle into v1_0 and v1_1, including tests * bumping version and updating README * updating server backup action; pep8 fixes * removed server dump after add/remove fixed-ip * version bump * fixed public private ip list * added various search options to list command. will need a version bump as i changed the 'list' api that nova uses. after version bump, my search nova branch will need pip-requires updated to match * docs * added add/remove fixed_ip actions to servers * Clarify description so usage doesn't imply name is the only valid value * Added support for request timeouts * Added migration functionality * Refactored backup rotation * Review feedback * Fixed unit tests * Implemented backup with rotation * for creating 'x' instances, min_count > max_count check was reversed make max/min_instances a little more sane by making them 'int' types fix issue where only specifying --min_instances didn't work * Due to how novaclient works, it tends to do a 'get' first on whatever ID you pass on command line. Then it does the real command, re-using the ID found in the 'get' call, instead of the initial ID that you specified (which may have been a UUID) * Cleaned up the query_string generation for 'nova list' Made --recurse_zones not need an '=argument' * Added --recurse_zones option to 'list' Added --fixed_ip option to 'list' to find a particular instance by IP Fixed issue with 'show' when --recurse_zones=1 and specifying UUID * fixup * release note update * tests working again for weight_scale/weight_offset * fixed up tests after trunk merge and bumped version * version bump * fixed project_id tests * Merged my 'create-num-instances' branch which adds support for --min_instances and --max_instances to zone-boot * version bumped * trunk merge * Typo fix * Added the missing files * Added a method to create instances on behalf of an account via the admin API methods for openstack * changed docs about using project id * bumped version # after project_id update * Don't restrict ids to int * fix errors * support for project id header * Now that I understand how to build extensions, I understand how this extension will be built, and can fully implement add_fixed_ip() and remove_fixed_ip() * Add the basic calls for add_fixed_ip/remove_fixed_ip * defaults back to no detail * tweaks * griddynamics better logging * reservation_id optional parameter added to GET /servers (aka 'list') * works properly with zone-boot * Improving tests * Added parameter detailed to list * zone select support and version bump to 2.4.3 * fix to reviewer comment: add check if logging disabled * improve perfomance on string concat in logging * add logging for http request-response * zone select * add undetailed flavor list * zone_blob support added to server.create * fixed flavor-list columns * added support for missing flavors/images * up'ed version to suit pypi distribution update * fixed software license * version 2.4 * Added Jacob Kaplan-Moss copyright notices on older/untouched files * renamed to novaclient and fixed flavor tests * missing docstring quote * tweaked release notes * removed copyright/license notices from files not significantly changed * renamed cmdline tool from novatools to nova. Changed version to 2.1. Changed license to Apache. Added copyright notices. Cleaner exception reporting in non-debug scnario * fixed setup * longer zone list * Added full flavor detail support * zone info works * get this zone status * removed Username from zone info * NOVA_TOOLS_* -> NOVA_*, --debug * removed zone name, renamed auth_url to api_url, added username/password * zone shell cmds & tests added * zone tests pass * zone tests pass * tests pass again * zones * Renamed all CloudServers to OpenStack and python-cloudservers to python-novatools * installer fixup * README update and rename cloudservers to novatools * Starting on child zone support * Initial commit from fork python-novaclient-2.17.0/doc/0000775000175400017540000000000012306433034017163 5ustar jenkinsjenkins00000000000000python-novaclient-2.17.0/doc/.gitignore0000664000175400017540000000002312306432770021154 0ustar jenkinsjenkins00000000000000build/ source/ref/ python-novaclient-2.17.0/doc/Makefile0000664000175400017540000000616012306432770020634 0ustar jenkinsjenkins00000000000000# 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 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)/* 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/python-novaclient.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/python-novaclient.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." python-novaclient-2.17.0/doc/source/0000775000175400017540000000000012306433034020463 5ustar jenkinsjenkins00000000000000python-novaclient-2.17.0/doc/source/shell.rst0000664000175400017540000000260512306432770022335 0ustar jenkinsjenkins00000000000000The :program:`nova` shell utility ========================================= .. program:: nova .. highlight:: bash The :program:`nova` shell utility interacts with OpenStack Nova API from the command line. It supports the entirety of the OpenStack Nova API. First, you'll need an OpenStack Nova account and an API key. You get this by using the `nova-manage` command in OpenStack Nova. You'll need to provide :program:`nova` with your OpenStack username and API key. You can do this with the :option:`--os-username`, :option:`--os-password` and :option:`--os-tenant-id` options, but it's easier to just set them as environment variables by setting two environment variables: .. envvar:: OS_USERNAME Your OpenStack Nova username. .. envvar:: OS_PASSWORD Your password. .. envvar:: OS_TENANT_NAME Project for work. .. envvar:: OS_AUTH_URL The OpenStack API server URL. .. envvar:: OS_COMPUTE_API_VERSION The OpenStack API version. For example, in Bash you'd use:: export OS_USERNAME=yourname export OS_PASSWORD=yadayadayada export OS_TENANT_NAME=myproject export OS_AUTH_URL=http://... export OS_COMPUTE_API_VERSION=1.1 From there, all shell commands take the form:: nova [arguments...] Run :program:`nova help` to get a full list of all possible commands, and run :program:`nova help ` to get detailed help for that command. python-novaclient-2.17.0/doc/source/conf.py0000664000175400017540000002250212306432770021771 0ustar jenkinsjenkins00000000000000# -*- coding: utf-8 -*- # 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-novaclient documentation build configuration file, created by # sphinx-quickstart on Sun Dec 6 14:19:25 2009. # # 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. # # All configuration values have a default; values that are commented out # serve to show the default. import sys # 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.append(os.path.abspath('.')) import os BASE_DIR = os.path.dirname(os.path.abspath(__file__)) ROOT = os.path.abspath(os.path.join(BASE_DIR, "..", "..")) sys.path.insert(0, ROOT) sys.path.insert(0, BASE_DIR) def gen_ref(ver, title, names): refdir = os.path.join(BASE_DIR, "ref") pkg = "novaclient" if ver: pkg = "%s.%s" % (pkg, ver) refdir = os.path.join(refdir, ver) if not os.path.exists(refdir): os.makedirs(refdir) idxpath = os.path.join(refdir, "index.rst") with open(idxpath, "w") as idx: idx.write(("%(title)s\n" "%(signs)s\n" "\n" ".. toctree::\n" " :maxdepth: 1\n" "\n") % {"title": title, "signs": "=" * len(title)}) for name in names: idx.write(" %s\n" % name) rstpath = os.path.join(refdir, "%s.rst" % name) with open(rstpath, "w") as rst: rst.write(("%(title)s\n" "%(signs)s\n" "\n" ".. automodule:: %(pkg)s.%(name)s\n" " :members:\n" " :undoc-members:\n" " :show-inheritance:\n" " :noindex:\n") % {"title": name.capitalize(), "signs": "=" * len(name), "pkg": pkg, "name": name}) gen_ref(None, "Exceptions", ["exceptions"]) gen_ref("v1_1", "Version 1.1, Version 2 API Reference", ["flavors", "images", "servers", "hosts", "agents", "aggregates", "availability_zones", "certs", "fixed_ips", "floating_ip_pools", "floating_ips", "hypervisors", "keypairs", "limits", "networks", "quota_classes", "quotas", "security_group_rules", "security_groups", "services", "virtual_interfaces", "volume_snapshots", "volumes", "volume_types"]) gen_ref("v3", "Version 3 API Reference", ["flavors", "hosts", "agents", "aggregates", "availability_zones", "certs", "hypervisors", "images", "keypairs", "quotas", "quotas_classes", "servers", "services"]) # -- 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. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8' # The master toctree document. master_doc = 'index' # General information about the project. project = 'python-novaclient' copyright = 'OpenStack Contributors' # 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. version = '2.13' # The full version, including alpha/beta/rc tags. release = '2.13.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # 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 directories, relative to source directory, that shouldn't be searched # for source files. exclude_trees = [] # 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. add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # Grouping the document tree for man pages. # List of tuples 'sourcefile', 'target', u'title', u'Authors name', 'manual' man_pages = [ ('man/nova', 'nova', 'OpenStack Nova command line client', ['OpenStack Contributors'], 1), ] # -- 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'. html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # 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'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_use_modindex = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = 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 = '' # Output file base name for HTML help builder. htmlhelp_basename = 'python-novaclientdoc' # -- Options for LaTeX output ------------------------------------------------- # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]) # . latex_documents = [ ('index', 'python-novaclient.tex', 'python-novaclient Documentation', 'Rackspace - based on work by Jacob Kaplan-Moss', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_use_modindex = True # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {'http://docs.python.org/': None} python-novaclient-2.17.0/doc/source/index.rst0000664000175400017540000000253712306432770022341 0ustar jenkinsjenkins00000000000000Python bindings to the OpenStack Nova API ================================================== This is a client for OpenStack Nova API. There's :doc:`a Python API ` (the :mod:`novaclient` module), and a :doc:`command-line script ` (installed as :program:`nova`). Each implements the entire OpenStack Nova API. You'll need credentials for an OpenStack cloud that implements the Compute API, such as TryStack, HP, or Rackspace, in order to use the nova client. .. seealso:: You may want to read the `OpenStack Compute Developer Guide`__ -- the overview, at least -- to get an idea of the concepts. By understanding the concepts this library should make more sense. __ http://docs.openstack.org/api/openstack-compute/2/content/ Contents: .. toctree:: :maxdepth: 2 shell api ref/index ref/v1_1/index ref/v3/index releases Contributing ============ Code is hosted at `git.openstack.org`_. Submit bugs to the Nova project on `Launchpad`_. Submit code to the openstack/python-novaclient project using `Gerrit`_. .. _git.openstack.org: https://git.openstack.org/cgit/openstack/python-novaclient .. _Launchpad: https://launchpad.net/nova .. _Gerrit: http://wiki.openstack.org/GerritWorkflow Run tests with ``python setup.py test``. Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` python-novaclient-2.17.0/doc/source/man/0000775000175400017540000000000012306433034021236 5ustar jenkinsjenkins00000000000000python-novaclient-2.17.0/doc/source/man/nova.rst0000664000175400017540000000273212306432770022745 0ustar jenkinsjenkins00000000000000==== nova ==== SYNOPSIS ======== `nova` [options] [command-options] `nova help` `nova help` DESCRIPTION =========== `nova` is a command line client for controlling OpenStack Nova, the cloud computing fabric controller. It implements 100% of the Nova API, allowing management of instances, images, quotas and much more. Before you can issue commands with `nova`, you must ensure that your environment contains the necessary variables so that you can prove to the CLI who you are and what credentials you have to issue the commands. See `Getting Credentials for a CLI` section of `OpenStack CLI Guide` for more info. See `OpenStack Nova CLI Guide` for a full-fledged guide. OPTIONS ======= To get a list of available commands and options run:: nova help To get usage and options of a command run:: nova help EXAMPLES ======== Get information about boot command:: nova help boot List available images:: nova image-list List available flavors:: nova flavor-list Launch an instance:: nova boot myserver --image some-image --flavor 2 View instance information:: nova show myserver List instances:: nova list Terminate an instance:: nova delete myserver SEE ALSO ======== OpenStack Nova CLI Guide: http://docs.openstack.org/cli-reference/content/novaclient_commands.html BUGS ==== Nova client is hosted in Launchpad so you can view current bugs at https://bugs.launchpad.net/python-novaclient/. python-novaclient-2.17.0/doc/source/api.rst0000664000175400017540000000174612306432770022004 0ustar jenkinsjenkins00000000000000The :mod:`novaclient` Python API ================================== .. module:: novaclient :synopsis: A client for the OpenStack Nova API. .. currentmodule:: novaclient Usage ----- First create a client instance with your credentials:: >>> from novaclient.client import Client >>> nova = Client(VERSION, USERNAME, PASSWORD, PROJECT_ID, AUTH_URL) Here ``VERSION`` can be: ``1.1``, ``2`` and ``3``. Then call methods on its managers:: >>> nova.servers.list() [] >>> nova.flavors.list() [, , , , , , ] >>> fl = nova.flavors.find(ram=512) >>> nova.servers.create("my-server", flavor=fl) Reference --------- For more information, see the reference: .. toctree:: :maxdepth: 2 ref/index ref/v1_1/index ref/v3/index python-novaclient-2.17.0/novaclient/0000775000175400017540000000000012306433034020560 5ustar jenkinsjenkins00000000000000python-novaclient-2.17.0/novaclient/v3/0000775000175400017540000000000012306433034021110 5ustar jenkinsjenkins00000000000000python-novaclient-2.17.0/novaclient/v3/hypervisors.py0000664000175400017540000000272512306432770024073 0ustar jenkinsjenkins00000000000000# Copyright 2013 IBM Corp # 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. """ Hypervisors interface """ from six.moves.urllib import parse from novaclient.v1_1 import hypervisors class Hypervisor(hypervisors.Hypervisor): pass class HypervisorManager(hypervisors.HypervisorManager): resource_class = Hypervisor def search(self, hypervisor_match): """ Get a list of matching hypervisors. :param servers: If True, server information is also retrieved. """ url = ('/os-hypervisors/search?query=%s' % parse.quote(hypervisor_match, safe='')) return self._list(url, 'hypervisors') def servers(self, hypervisor): """ Get servers for a specific hypervisor :param hypervisor: ID of hypervisor to get list of servers for. """ return self._get('/os-hypervisors/%s/servers' % hypervisor, 'hypervisor') python-novaclient-2.17.0/novaclient/v3/servers.py0000664000175400017540000010265012306432770023165 0ustar jenkinsjenkins00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 OpenStack Foundation # 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. """ Server interface. """ import base64 import six from six.moves.urllib import parse from novaclient import base from novaclient import crypto from novaclient.openstack.common import strutils REBOOT_SOFT, REBOOT_HARD = 'SOFT', 'HARD' class Server(base.Resource): HUMAN_ID = True def __repr__(self): return "" % self.name def delete(self): """ Delete (i.e. shut down and delete the image) this server. """ self.manager.delete(self) def update(self, name=None): """ Update the name or the password for this server. :param name: Update the server's name. :param password: Update the root password. """ self.manager.update(self, name=name) def get_console_output(self, length=None): """ Get text console log output from Server. :param length: The number of lines you would like to retrieve (as int) """ return self.manager.get_console_output(self, length) def get_vnc_console(self, console_type): """ Get vnc console for a Server. :param console_type: Type of console ('novnc' or 'xvpvnc') """ return self.manager.get_vnc_console(self, console_type) def get_spice_console(self, console_type): """ Get spice console for a Server. :param console_type: Type of console ('spice-html5') """ return self.manager.get_spice_console(self, console_type) def get_password(self, private_key): """ Get password for a Server. :param private_key: Path to private key file for decryption """ return self.manager.get_password(self, private_key) def clear_password(self): """ Get password for a Server. """ return self.manager.clear_password(self) def add_fixed_ip(self, network_id): """ Add an IP address on a network. :param network_id: The ID of the network the IP should be on. """ self.manager.add_fixed_ip(self, network_id) def remove_floating_ip(self, address): """ Remove floating IP from an instance :param address: The ip address or FloatingIP to remove """ self.manager.remove_floating_ip(self, address) def stop(self): """ Stop -- Stop the running server. """ self.manager.stop(self) def force_delete(self): """ Force delete -- Force delete a server. """ self.manager.force_delete(self) def restore(self): """ Restore -- Restore a server in 'soft-deleted' state. """ self.manager.restore(self) def start(self): """ Start -- Start the paused server. """ self.manager.start(self) def pause(self): """ Pause -- Pause the running server. """ self.manager.pause(self) def unpause(self): """ Unpause -- Unpause the paused server. """ self.manager.unpause(self) def lock(self): """ Lock -- Lock the instance from certain operations. """ self.manager.lock(self) def unlock(self): """ Unlock -- Remove instance lock. """ self.manager.unlock(self) def suspend(self): """ Suspend -- Suspend the running server. """ self.manager.suspend(self) def resume(self): """ Resume -- Resume the suspended server. """ self.manager.resume(self) def rescue(self): """ Rescue -- Rescue the problematic server. """ return self.manager.rescue(self) def unrescue(self): """ Unrescue -- Unrescue the rescued server. """ self.manager.unrescue(self) def shelve(self): """ Shelve -- Shelve the server. """ self.manager.shelve(self) def shelve_offload(self): """ Shelve_offload -- Remove a shelved server from the compute node. """ self.manager.shelve_offload(self) def unshelve(self): """ Unshelve -- Unshelve the server. """ self.manager.unshelve(self) def diagnostics(self): """Diagnostics -- Retrieve server diagnostics.""" return self.manager.diagnostics(self) def migrate(self): """ Migrate a server to a new host. """ self.manager.migrate(self) def remove_fixed_ip(self, address): """ Remove an IP address. :param address: The IP address to remove. """ self.manager.remove_fixed_ip(self, address) def change_password(self, password): """ Update the password for a server. """ self.manager.change_password(self, password) def reboot(self, reboot_type=REBOOT_SOFT): """ Reboot the server. :param reboot_type: either :data:`REBOOT_SOFT` for a software-level reboot, or `REBOOT_HARD` for a virtual power cycle hard reboot. """ self.manager.reboot(self, reboot_type) def rebuild(self, image, password=None, **kwargs): """ Rebuild -- shut down and then re-image -- this server. :param image: the :class:`Image` (or its ID) to re-image with. :param password: string to set as password on the rebuilt server. """ return self.manager.rebuild(self, image, password=password, **kwargs) def resize(self, flavor, **kwargs): """ Resize the server's resources. :param flavor: the :class:`Flavor` (or its ID) to resize to. Until a resize event is confirmed with :meth:`confirm_resize`, the old server will be kept around and you'll be able to roll back to the old flavor quickly with :meth:`revert_resize`. All resizes are automatically confirmed after 24 hours. """ self.manager.resize(self, flavor, **kwargs) def create_image(self, image_name, metadata=None): """ Create an image based on this server. :param image_name: The name to assign the newly create image. :param metadata: Metadata to assign to the image. """ return self.manager.create_image(self, image_name, metadata) def backup(self, backup_name, backup_type, rotation): """ Backup a server instance. :param backup_name: Name of the backup image :param backup_type: The backup type, like 'daily' or 'weekly' :param rotation: Int parameter representing how many backups to keep around. """ self.manager.backup(self, backup_name, backup_type, rotation) def confirm_resize(self): """ Confirm that the resize worked, thus removing the original server. """ self.manager.confirm_resize(self) def revert_resize(self): """ Revert a previous resize, switching back to the old server. """ self.manager.revert_resize(self) @property def networks(self): """ Generate a simplified list of addresses """ networks = {} try: for network_label, address_list in self.addresses.items(): networks[network_label] = [a['addr'] for a in address_list] return networks except Exception: return {} def live_migrate(self, host=None, block_migration=False, disk_over_commit=False): """ Migrates a running instance to a new machine. """ self.manager.live_migrate(self, host, block_migration, disk_over_commit) def reset_state(self, state='error'): """ Reset the state of an instance to active or error. """ self.manager.reset_state(self, state) def reset_network(self): """ Reset network of an instance. """ self.manager.reset_network(self) def evacuate(self, host, on_shared_storage, password=None): """ Evacuate an instance from failed host to specified host. :param host: Name of the target host :param on_shared_storage: Specifies whether instance files located on shared storage :param password: string to set as password on the evacuated server. """ return self.manager.evacuate(self, host, on_shared_storage, password) def interface_list(self): """ List interfaces attached to an instance. """ return self.manager.interface_list(self) def interface_attach(self, port_id, net_id, fixed_ip): """ Attach a network interface to an instance. """ return self.manager.interface_attach(self, port_id, net_id, fixed_ip) def interface_detach(self, port_id): """ Detach a network interface from an instance. """ return self.manager.interface_detach(self, port_id) class ServerManager(base.BootingManagerWithFind): resource_class = Server def _boot(self, resource_url, response_key, name, image, flavor, meta=None, userdata=None, reservation_id=None, return_raw=False, min_count=None, max_count=None, security_groups=None, key_name=None, availability_zone=None, block_device_mapping=None, block_device_mapping_v2=None, nics=None, scheduler_hints=None, config_drive=None, admin_pass=None, **kwargs): """ Create (boot) a new server. :param name: Something to name the server. :param image: The :class:`Image` to boot with. :param flavor: The :class:`Flavor` to boot onto. :param meta: A dict of arbitrary key/value metadata to store for this server. A maximum of five entries is allowed, and both keys and values must be 255 characters or less. :param reservation_id: a UUID for the set of servers being requested. :param return_raw: If True, don't try to coearse the result into a Resource object. :param security_groups: list of security group names :param key_name: (optional extension) name of keypair to inject into the instance :param availability_zone: Name of the availability zone for instance placement. :param block_device_mapping: A dict of block device mappings for this server. :param block_device_mapping_v2: A dict of block device mappings V2 for this server. :param nics: (optional extension) an ordered list of nics to be added to this server, with information about connected networks, fixed ips, etc. :param scheduler_hints: (optional extension) arbitrary key-value pairs specified by the client to help boot an instance. :param config_drive: (optional extension) value for config drive either boolean, or volume-id :param admin_pass: admin password for the server. """ body = {"server": { "name": name, "image_ref": str(base.getid(image)) if image else '', "flavor_ref": str(base.getid(flavor)), }} if userdata: if hasattr(userdata, 'read'): userdata = userdata.read() if six.PY3: userdata = userdata.encode("utf-8") else: userdata = strutils.safe_encode(userdata) body["server"][ "os-user-data:user_data"] = base64.b64encode(userdata) if meta: body["server"]["metadata"] = meta if reservation_id: body["server"][ "os-multiple-create:reservation_id"] = reservation_id if key_name: body["server"]["key_name"] = key_name if scheduler_hints: body["server"][ "os-scheduler-hints:scheduler_hints"] = scheduler_hints if config_drive: body["server"]["os-config-drive:config_drive"] = config_drive if admin_pass: body["server"]["admin_password"] = admin_pass if not min_count: min_count = 1 if not max_count: max_count = min_count body["server"]["os-multiple-create:min_count"] = min_count body["server"]["os-multiple-create:max_count"] = max_count if security_groups: body["server"]["os-security-groups:security_groups"] = \ [{'name': sg} for sg in security_groups] if availability_zone: body["server"][ "os-availability-zone:availability_zone"] = availability_zone # Block device mappings are passed as a list of dictionaries if block_device_mapping: bdm_param = 'os-block-device-mapping:block_device_mapping' body['server'][bdm_param] = \ self._parse_block_device_mapping(block_device_mapping) elif block_device_mapping_v2: # Append the image to the list only if we have new style BDMs if image: bdm_dict = {'uuid': image.id, 'source_type': 'image', 'destination_type': 'local', 'boot_index': 0, 'delete_on_termination': True} block_device_mapping_v2.insert(0, bdm_dict) body['server'][bdm_param] = block_device_mapping_v2 if nics is not None: # NOTE(tr3buchet): nics can be an empty list all_net_data = [] for nic_info in nics: net_data = {} # if value is empty string, do not send value in body if nic_info.get('net-id'): net_data['uuid'] = nic_info['net-id'] if nic_info.get('v4-fixed-ip'): net_data['fixed_ip'] = nic_info['v4-fixed-ip'] if nic_info.get('port-id'): net_data['port'] = nic_info['port-id'] all_net_data.append(net_data) body['server']['networks'] = all_net_data return self._create(resource_url, body, response_key, return_raw=return_raw, **kwargs) def get(self, server): """ Get a server. :param server: ID of the :class:`Server` to get. :rtype: :class:`Server` """ return self._get("/servers/%s" % base.getid(server), "server") def list(self, detailed=True, search_opts=None, marker=None, limit=None): """ Get a list of servers. :param detailed: Whether to return detailed server info (optional). :param search_opts: Search options to filter out servers (optional). :param marker: Begin returning servers that appear later in the server list than that represented by this server id (optional). :param limit: Maximum number of servers to return (optional). :rtype: list of :class:`Server` """ if search_opts is None: search_opts = {} qparams = {} for opt, val in six.iteritems(search_opts): if val: qparams[opt] = val if marker: qparams['marker'] = marker if limit: qparams['limit'] = limit # Transform the dict to a sequence of two-element tuples in fixed # order, then the encoded string will be consistent in Python 2&3. if qparams: new_qparams = sorted(qparams.items(), key=lambda x: x[0]) query_string = "?%s" % parse.urlencode(new_qparams) else: query_string = "" detail = "" if detailed: detail = "/detail" return self._list("/servers%s%s" % (detail, query_string), "servers") def add_fixed_ip(self, server, network_id): """ Add an IP address on a network. :param server: The :class:`Server` (or its ID) to add an IP to. :param network_id: The ID of the network the IP should be on. """ self._action('add_fixed_ip', server, {'network_id': network_id}) def remove_fixed_ip(self, server, address): """ Remove an IP address. :param server: The :class:`Server` (or its ID) to add an IP to. :param address: The IP address to remove. """ self._action('remove_fixed_ip', server, {'address': address}) def get_vnc_console(self, server, console_type): """ Get a vnc console for an instance :param server: The :class:`Server` (or its ID) to add an IP to. :param console_type: Type of vnc console to get ('novnc' or 'xvpvnc') """ return self._action('get_vnc_console', server, {'type': console_type})[1] def get_spice_console(self, server, console_type): """ Get a spice console for an instance :param server: The :class:`Server` (or its ID) to add an IP to. :param console_type: Type of spice console to get ('spice-html5') """ return self._action('get_spice_console', server, {'type': console_type})[1] def get_password(self, server, private_key): """ Get password for an instance Requires that openssl is installed and in the path :param server: The :class:`Server` (or its ID) to add an IP to. :param private_key: The private key to decrypt password """ _resp, body = self.api.client.get("/servers/%s/os-server-password" % base.getid(server)) if body and body.get('password'): try: return crypto.decrypt_password(private_key, body['password']) except Exception as exc: return '%sFailed to decrypt:\n%s' % (exc, body['password']) return '' def clear_password(self, server): """ Clear password for an instance :param server: The :class:`Server` (or its ID) to add an IP to. """ return self._delete("/servers/%s/os-server-password" % base.getid(server)) def stop(self, server): """ Stop the server. """ return self._action('stop', server, None) def force_delete(self, server): """ Force delete the server. """ return self._action('force_delete', server, None) def restore(self, server): """ Restore soft-deleted server. """ return self._action('restore', server, None) def start(self, server): """ Start the server. """ self._action('start', server, None) def pause(self, server): """ Pause the server. """ self._action('pause', server, None) def unpause(self, server): """ Unpause the server. """ self._action('unpause', server, None) def lock(self, server): """ Lock the server. """ self._action('lock', server, None) def unlock(self, server): """ Unlock the server. """ self._action('unlock', server, None) def suspend(self, server): """ Suspend the server. """ self._action('suspend', server, None) def resume(self, server): """ Resume the server. """ self._action('resume', server, None) def rescue(self, server): """ Rescue the server. """ return self._action('rescue', server, None) def unrescue(self, server): """ Unrescue the server. """ self._action('unrescue', server, None) def shelve(self, server): """ Shelve the server. """ self._action('shelve', server, None) def shelve_offload(self, server): """ Remove a shelved instance from the compute node. """ self._action('shelve_offload', server, None) def unshelve(self, server): """ Unshelve the server. """ self._action('unshelve', server, None) def diagnostics(self, server): """Retrieve server diagnostics.""" return self.api.client.get("/servers/%s/os-server-diagnostics" % base.getid(server)) def create(self, name, image, flavor, meta=None, files=None, reservation_id=None, min_count=None, max_count=None, security_groups=None, userdata=None, key_name=None, availability_zone=None, block_device_mapping=None, block_device_mapping_v2=None, nics=None, scheduler_hints=None, config_drive=None, **kwargs): # TODO(anthony): indicate in doc string if param is an extension # and/or optional """ Create (boot) a new server. :param name: Something to name the server. :param image: The :class:`Image` to boot with. :param flavor: The :class:`Flavor` to boot onto. :param meta: A dict of arbitrary key/value metadata to store for this server. A maximum of five entries is allowed, and both keys and values must be 255 characters or less. :param files: A dict of files to overrwrite on the server upon boot. Keys are file names (i.e. ``/etc/passwd``) and values are the file contents (either as a string or as a file-like object). A maximum of five entries is allowed, and each file must be 10k or less. :param userdata: user data to pass to be exposed by the metadata server this can be a file type object as well or a string. :param reservation_id: a UUID for the set of servers being requested. :param key_name: (optional extension) name of previously created keypair to inject into the instance. :param availability_zone: Name of the availability zone for instance placement. :param block_device_mapping: (optional extension) A dict of block device mappings for this server. :param block_device_mapping_v2: (optional extension) A dict of block device mappings for this server. :param nics: (optional extension) an ordered list of nics to be added to this server, with information about connected networks, fixed ips, port etc. :param scheduler_hints: (optional extension) arbitrary key-value pairs specified by the client to help boot an instance :param config_drive: (optional extension) value for config drive either boolean, or volume-id """ if not min_count: min_count = 1 if not max_count: max_count = min_count if min_count > max_count: min_count = max_count boot_args = [name, image, flavor] boot_kwargs = dict( meta=meta, files=files, userdata=userdata, reservation_id=reservation_id, min_count=min_count, max_count=max_count, security_groups=security_groups, key_name=key_name, availability_zone=availability_zone, scheduler_hints=scheduler_hints, config_drive=config_drive, **kwargs) if block_device_mapping: boot_kwargs['block_device_mapping'] = block_device_mapping elif block_device_mapping_v2: boot_kwargs['block_device_mapping_v2'] = block_device_mapping_v2 resource_url = "/servers" if nics: boot_kwargs['nics'] = nics response_key = "server" return self._boot(resource_url, response_key, *boot_args, **boot_kwargs) def update(self, server, name=None): """ Update the name or the password for a server. :param server: The :class:`Server` (or its ID) to update. :param name: Update the server's name. """ if name is None: return body = { "server": { "name": name, }, } return self._update("/servers/%s" % base.getid(server), body, "server") def change_password(self, server, password): """ Update the password for a server. """ self._action("change_password", server, {"admin_password": password}) def delete(self, server): """ Delete (i.e. shut down and delete the image) this server. """ self._delete("/servers/%s" % base.getid(server)) def reboot(self, server, reboot_type=REBOOT_SOFT): """ Reboot a server. :param server: The :class:`Server` (or its ID) to share onto. :param reboot_type: either :data:`REBOOT_SOFT` for a software-level reboot, or `REBOOT_HARD` for a virtual power cycle hard reboot. """ self._action('reboot', server, {'type': reboot_type}) def rebuild(self, server, image, password=None, **kwargs): """ Rebuild -- shut down and then re-image -- a server. :param server: The :class:`Server` (or its ID) to share onto. :param image: the :class:`Image` (or its ID) to re-image with. :param password: string to set as password on the rebuilt server. """ body = {'image_ref': base.getid(image)} if password is not None: body['admin_password'] = password _resp, body = self._action('rebuild', server, body, **kwargs) return Server(self, body['server']) def migrate(self, server): """ Migrate a server to a new host. :param server: The :class:`Server` (or its ID). """ self._action('migrate', server) def resize(self, server, flavor, **kwargs): """ Resize a server's resources. :param server: The :class:`Server` (or its ID) to share onto. :param flavor: the :class:`Flavor` (or its ID) to resize to. Until a resize event is confirmed with :meth:`confirm_resize`, the old server will be kept around and you'll be able to roll back to the old flavor quickly with :meth:`revert_resize`. All resizes are automatically confirmed after 24 hours. """ info = {'flavor_ref': base.getid(flavor)} self._action('resize', server, info=info, **kwargs) def confirm_resize(self, server): """ Confirm that the resize worked, thus removing the original server. :param server: The :class:`Server` (or its ID) to share onto. """ self._action('confirm_resize', server) def revert_resize(self, server): """ Revert a previous resize, switching back to the old server. :param server: The :class:`Server` (or its ID) to share onto. """ self._action('revert_resize', server) def create_image(self, server, image_name, metadata=None): """ Snapshot a server. :param server: The :class:`Server` (or its ID) to share onto. :param image_name: Name to give the snapshot image :param meta: Metadata to give newly-created image entity """ body = {'name': image_name, 'metadata': metadata or {}} resp = self._action('create_image', server, body)[0] location = resp.headers['location'] image_uuid = location.split('/')[-1] return image_uuid def backup(self, server, backup_name, backup_type, rotation): """ Backup a server instance. :param server: The :class:`Server` (or its ID) to share onto. :param backup_name: Name of the backup image :param backup_type: The backup type, like 'daily' or 'weekly' :param rotation: Int parameter representing how many backups to keep around. """ body = {'name': backup_name, 'backup_type': backup_type, 'rotation': rotation} self._action('create_backup', server, body) def set_meta(self, server, metadata): """ Set a servers metadata :param server: The :class:`Server` to add metadata to :param metadata: A dict of metadata to add to the server """ body = {'metadata': metadata} return self._create("/servers/%s/metadata" % base.getid(server), body, "metadata") def get_console_output(self, server, length=None): """ Get text console log output from Server. :param server: The :class:`Server` (or its ID) whose console output you would like to retrieve. :param length: The number of tail loglines you would like to retrieve. """ return self._action('get_console_output', server, {'length': length})[1]['output'] def delete_meta(self, server, keys): """ Delete metadata from an server :param server: The :class:`Server` to add metadata to :param keys: A list of metadata keys to delete from the server """ for k in keys: self._delete("/servers/%s/metadata/%s" % (base.getid(server), k)) def live_migrate(self, server, host, block_migration, disk_over_commit): """ Migrates a running instance to a new machine. :param server: instance id which comes from nova list. :param host: destination host name. :param block_migration: if True, do block_migration. :param disk_over_commit: if True, Allow overcommit. """ self._action('migrate_live', server, {'host': host, 'block_migration': block_migration, 'disk_over_commit': disk_over_commit}) def reset_state(self, server, state='error'): """ Reset the state of an instance to active or error. :param server: ID of the instance to reset the state of. :param state: Desired state; either 'active' or 'error'. Defaults to 'error'. """ self._action('reset_state', server, dict(state=state)) def reset_network(self, server): """ Reset network of an instance. """ self._action('reset_network', server) def evacuate(self, server, host, on_shared_storage, password=None): """ Evacuate a server instance. :param server: The :class:`Server` (or its ID) to share onto. :param host: Name of the target host. :param on_shared_storage: Specifies whether instance files located on shared storage :param password: string to set as password on the evacuated server. """ body = { 'host': host, 'on_shared_storage': on_shared_storage, } if password is not None: body['admin_password'] = password return self._action('evacuate', server, body) def interface_list(self, server): """ List attached network interfaces :param server: The :class:`Server` (or its ID) to query. """ return self._list('/servers/%s/os-attach-interfaces' % base.getid(server), 'interface_attachments') def interface_attach(self, server, port_id, net_id, fixed_ip): """ Attach a network_interface to an instance. :param server: The :class:`Server` (or its ID) to attach to. :param port_id: The port to attach. """ body = {'interface_attachment': {}} if port_id: body['interface_attachment']['port_id'] = port_id if net_id: body['interface_attachment']['net_id'] = net_id if fixed_ip: body['interface_attachment']['fixed_ips'] = [ {'ip_address': fixed_ip}] return self._create('/servers/%s/os-attach-interfaces' % base.getid(server), body, 'interface_attachment') def interface_detach(self, server, port_id): """ Detach a network_interface from an instance. :param server: The :class:`Server` (or its ID) to detach from. :param port_id: The port to detach. """ self._delete('/servers/%s/os-attach-interfaces/%s' % (base.getid(server), port_id)) def _action(self, action, server, info=None, **kwargs): """ Perform a server "action" -- reboot/rebuild/resize/etc. """ body = {action: info} self.run_hooks('modify_body_for_action', body, **kwargs) url = '/servers/%s/action' % base.getid(server) return self.api.client.post(url, body=body) python-novaclient-2.17.0/novaclient/v3/flavors.py0000664000175400017540000000605112306432770023146 0ustar jenkinsjenkins00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2013 IBM Corp. # # 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. """ Flavor interface. """ from novaclient import base from novaclient import utils from novaclient.v1_1 import flavors class Flavor(base.Resource): """ A flavor is an available hardware configuration for a server. """ HUMAN_ID = True def __repr__(self): return "" % self.name @property def is_public(self): """ Provide a user-friendly accessor to flavor-access:is_public """ return self._info.get("flavor-access:is_public", 'N/A') def get_keys(self): """ Get extra specs from a flavor. :param flavor: The :class:`Flavor` to get extra specs from """ _resp, body = self.manager.api.client.get( "/flavors/%s/flavor-extra-specs" % base.getid(self)) return body["extra_specs"] def set_keys(self, metadata): """ Set extra specs on a flavor. :param flavor: The :class:`Flavor` to set extra spec on :param metadata: A dict of key/value pairs to be set """ utils.validate_flavor_metadata_keys(metadata.keys()) body = {'extra_specs': metadata} return self.manager._create( "/flavors/%s/flavor-extra-specs" % base.getid(self), body, "extra_specs", return_raw=True) def unset_keys(self, keys): """ Unset extra specs on a flavor. :param flavor: The :class:`Flavor` to unset extra spec on :param keys: A list of keys to be unset """ for k in keys: return self.manager._delete( "/flavors/%s/flavor-extra-specs/%s" % ( base.getid(self), k)) def delete(self): """ Delete this flavor. """ self.manager.delete(self) class FlavorManager(flavors.FlavorManager): resource_class = Flavor def _build_body(self, name, ram, vcpus, disk, id, swap, ephemeral, rxtx_factor, is_public): return { "flavor": { "name": name, "ram": ram, "vcpus": vcpus, "disk": disk, "id": id, "swap": swap, "ephemeral": ephemeral, "rxtx_factor": rxtx_factor, "flavor-access:is_public": is_public, } } python-novaclient-2.17.0/novaclient/v3/availability_zones.py0000664000175400017540000000203412306432770025357 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # Copyright 2013 IBM Corp. # 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. """ Availability Zone interface. """ from novaclient.v1_1 import availability_zones class AvailabilityZone(availability_zones.AvailabilityZone): pass class AvailabilityZoneManager(availability_zones.AvailabilityZoneManager): """ Manage :class:`AvailabilityZone` resources. """ resource_class = AvailabilityZone return_parameter_name = 'availability_zone_info' python-novaclient-2.17.0/novaclient/v3/images.py0000664000175400017540000000635412306432770022745 0ustar jenkinsjenkins00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2013 IBM Corp. # # 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. """ Image interface. """ from six.moves.urllib import parse from novaclient import base from novaclient.openstack.common import strutils class Image(base.Resource): """ An image is a collection of files used to create or rebuild a server. """ HUMAN_ID = True def __repr__(self): return "" % self.name def delete(self): """ Delete this image. """ self.manager.delete(self) class ImageManager(base.ManagerWithFind): """ Manage :class:`Image` resources. """ resource_class = Image # NOTE(cyeoh): Eventually we'll want novaclient to be smart # enough to do version discovery, but for now we just request # the v1 image API image_api_prefix = '/v1' def _image_meta_from_headers(self, headers): meta = {'properties': {}} safe_decode = strutils.safe_decode for key, value in headers.items(): value = safe_decode(value, incoming='utf-8') if key.startswith('x-image-meta-property-'): _key = safe_decode(key[22:], incoming='utf-8') meta['properties'][_key] = value elif key.startswith('x-image-meta-'): _key = safe_decode(key[13:], incoming='utf-8') meta[_key] = value for key in ['is_public', 'protected', 'deleted']: if key in meta: meta[key] = strutils.bool_from_string(meta[key]) return self._format_image_meta_for_user(meta) @staticmethod def _format_image_meta_for_user(meta): for key in ['size', 'min_ram', 'min_disk']: if key in meta: try: meta[key] = int(meta[key]) except ValueError: pass return meta def get(self, image): """ Get an image. :param image: The ID of the image to get. :rtype: :class:`Image` """ url = "%s/images/%s" % (self.image_api_prefix, base.getid(image)) resp, _ = self.api.client._cs_request(url, 'HEAD') foo = self._image_meta_from_headers(resp.headers) return Image(self, foo) def list(self, detailed=True, limit=None): """ Get a list of all images. :rtype: list of :class:`Image` :param limit: maximum number of images to return. """ params = {} detail = '' if detailed: detail = '/detail' if limit: params['limit'] = int(limit) query = '?%s' % parse.urlencode(params) if params else '' return self._list('/v1/images%s%s' % (detail, query), 'images') python-novaclient-2.17.0/novaclient/v3/flavor_access.py0000664000175400017540000000304412306432770024303 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # Copyright 2013 IBM Corp. # 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. """Flavor access interface.""" from novaclient import base from novaclient.v1_1 import flavor_access class FlavorAccess(flavor_access.FlavorAccess): pass class FlavorAccessManager(flavor_access.FlavorAccessManager): """ Manage :class:`FlavorAccess` resources. """ resource_class = FlavorAccess def _list_by_flavor(self, flavor): return self._list('/flavors/%s/flavor-access' % base.getid(flavor), 'flavor_access') def add_tenant_access(self, flavor, tenant): """Add a tenant to the given flavor access list.""" info = {'tenant_id': tenant} return self._action('add_tenant_access', flavor, info) def remove_tenant_access(self, flavor, tenant): """Remove a tenant from the given flavor access list.""" info = {'tenant_id': tenant} return self._action('remove_tenant_access', flavor, info) python-novaclient-2.17.0/novaclient/v3/quotas.py0000664000175400017540000000261412306432770023007 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # 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 novaclient.v1_1 import quotas class QuotaSet(quotas.QuotaSet): pass class QuotaSetManager(quotas.QuotaSetManager): resource_class = QuotaSet def get(self, tenant_id, user_id=None, detail=False): if detail: detail_string = '/detail' else: detail_string = '' if hasattr(tenant_id, 'tenant_id'): tenant_id = tenant_id.tenant_id if user_id: url = '/os-quota-sets/%s%s?user_id=%s' % (tenant_id, detail_string, user_id) else: url = '/os-quota-sets/%s%s' % (tenant_id, detail_string) return self._get(url, "quota_set") def _update_body(self, tenant_id, **kwargs): return {'quota_set': kwargs} python-novaclient-2.17.0/novaclient/v3/aggregates.py0000664000175400017540000000150712306432770023604 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # 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. """Aggregate interface.""" from novaclient.v1_1 import aggregates class Aggregate(aggregates.Aggregate): pass class AggregateManager(aggregates.AggregateManager): resource_class = Aggregate python-novaclient-2.17.0/novaclient/v3/shell.py0000664000175400017540000027767312306432770022625 0ustar jenkinsjenkins00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 OpenStack Foundation # Copyright 2013 IBM Corp. # 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 __future__ import print_function import argparse import copy import datetime import getpass import locale import os import sys import time import six from novaclient import exceptions from novaclient.openstack.common import strutils from novaclient.openstack.common import timeutils from novaclient.openstack.common import uuidutils from novaclient import utils from novaclient.v3 import availability_zones from novaclient.v3 import servers def _key_value_pairing(text): try: (k, v) = text.split('=', 1) return (k, v) except ValueError: msg = "%r is not in the format of key=value" % text raise argparse.ArgumentTypeError(msg) def _match_image(cs, wanted_properties): image_list = cs.images.list() images_matched = [] match = set(wanted_properties) for img in image_list: try: if match == match.intersection(set(img.metadata.items())): images_matched.append(img) except AttributeError: pass return images_matched def _boot(cs, args, reservation_id=None, min_count=None, max_count=None): """Boot a new server.""" if min_count is None: min_count = 1 if max_count is None: max_count = min_count if min_count > max_count: raise exceptions.CommandError("min_instances should be <= " "max_instances") if not min_count or not max_count: raise exceptions.CommandError("min_instances nor max_instances should" "be 0") if args.image: image = _find_image(cs.image_cs, args.image) else: image = None if not image and args.image_with: images = _match_image(cs.image_cs, args.image_with) if images: # TODO(harlowja): log a warning that we # are selecting the first of many? image = images[0] if not image and not args.block_device_mapping: raise exceptions.CommandError("you need to specify an Image ID " "or a block device mapping " "or provide a set of properties to match" " against an image") if not args.flavor: raise exceptions.CommandError("you need to specify a Flavor ID ") if args.num_instances is not None: if args.num_instances <= 1: raise exceptions.CommandError("num_instances should be > 1") max_count = args.num_instances flavor = _find_flavor(cs, args.flavor) meta = dict(v.split('=', 1) for v in args.meta) files = {} for f in args.files: try: dst, src = f.split('=', 1) files[dst] = open(src) except IOError as e: raise exceptions.CommandError("Can't open '%s': %s" % (src, e)) except ValueError as e: raise exceptions.CommandError("Invalid file argument '%s'. File " "arguments must be of the form '--file '" % f) # use the os-keypair extension key_name = None if args.key_name is not None: key_name = args.key_name if args.user_data: try: userdata = open(args.user_data) except IOError as e: raise exceptions.CommandError("Can't open '%s': %s" % (args.user_data, e)) else: userdata = None if args.availability_zone: availability_zone = args.availability_zone else: availability_zone = None if args.security_groups: security_groups = args.security_groups.split(',') else: security_groups = None block_device_mapping = {} for bdm in args.block_device_mapping: device_name, mapping = bdm.split('=', 1) block_device_mapping[device_name] = mapping nics = [] for nic_str in args.nics: err_msg = ("Invalid nic argument '%s'. Nic arguments must be of the " "form --nic , with at minimum net-id or port-id " "specified." % nic_str) nic_info = {"net-id": "", "v4-fixed-ip": "", "port-id": ""} for kv_str in nic_str.split(","): try: k, v = kv_str.split("=", 1) except ValueError as e: raise exceptions.CommandError(err_msg) if k in nic_info: nic_info[k] = v else: raise exceptions.CommandError(err_msg) if not nic_info['net-id'] and not nic_info['port-id']: raise exceptions.CommandError(err_msg) nics.append(nic_info) hints = {} if args.scheduler_hints: for hint in args.scheduler_hints: key, _sep, value = hint.partition('=') # NOTE(vish): multiple copies of the same hint will # result in a list of values if key in hints: if isinstance(hints[key], six.string_types): hints[key] = [hints[key]] hints[key] += [value] else: hints[key] = value boot_args = [args.name, image, flavor] if str(args.config_drive).lower() in ("true", "1"): config_drive = True elif str(args.config_drive).lower() in ("false", "0", "", "none"): config_drive = None else: config_drive = args.config_drive boot_kwargs = dict( meta=meta, files=files, key_name=key_name, reservation_id=reservation_id, min_count=min_count, max_count=max_count, userdata=userdata, availability_zone=availability_zone, security_groups=security_groups, block_device_mapping=block_device_mapping, nics=nics, scheduler_hints=hints, config_drive=config_drive) return boot_args, boot_kwargs @utils.arg('--flavor', default=None, metavar='', help="Flavor ID (see 'nova flavor-list').") @utils.arg('--image', default=None, metavar='', help="Image ID (see 'nova image-list'). ") @utils.arg('--image-with', default=[], type=_key_value_pairing, action='append', metavar='', help="Image metadata property (see 'nova image-show'). ") @utils.arg('--num-instances', default=None, type=int, metavar='', help="boot multiple servers at a time (limited by quota).") @utils.arg('--meta', metavar="", action='append', default=[], help="Record arbitrary key/value metadata to /meta.js " "on the new server. Can be specified multiple times.") @utils.arg('--file', metavar="", action='append', dest='files', default=[], help="Store arbitrary files from locally to " "on the new server. You may store up to 5 files.") @utils.arg('--key-name', metavar='', help="Key name of keypair that should be created earlier with \ the command keypair-add") @utils.arg('--key_name', help=argparse.SUPPRESS) @utils.arg('name', metavar='', help='Name for the new server') @utils.arg('--user-data', default=None, metavar='', help="user data file to pass to be exposed by the metadata server.") @utils.arg('--user_data', help=argparse.SUPPRESS) @utils.arg('--availability-zone', default=None, metavar='', help="The availability zone for server placement.") @utils.arg('--availability_zone', help=argparse.SUPPRESS) @utils.arg('--security-groups', default=None, metavar='', help="Comma separated list of security group names.") @utils.arg('--security_groups', help=argparse.SUPPRESS) @utils.arg('--block-device-mapping', metavar="", action='append', default=[], help="Block device mapping in the format " "=:::.") @utils.arg('--block_device_mapping', action='append', help=argparse.SUPPRESS) @utils.arg('--hint', action='append', dest='scheduler_hints', default=[], metavar='', help="Send arbitrary key/value pairs to the scheduler for custom use.") @utils.arg('--nic', metavar="", action='append', dest='nics', default=[], help="Create a NIC on the server. " "Specify option multiple times to create multiple NICs. " "net-id: attach NIC to network with this UUID " "(required if no port-id), " "v4-fixed-ip: IPv4 fixed address for NIC (optional), " "port-id: attach NIC to port with this UUID " "(required if no net-id)") @utils.arg('--config-drive', metavar="", dest='config_drive', default=False, help="Enable config drive") @utils.arg('--poll', dest='poll', action="store_true", default=False, help='Blocks while server builds so progress can be reported.') def do_boot(cs, args): """Boot a new server.""" boot_args, boot_kwargs = _boot(cs, args) extra_boot_kwargs = utils.get_resource_manager_extra_kwargs(do_boot, args) boot_kwargs.update(extra_boot_kwargs) server = cs.servers.create(*boot_args, **boot_kwargs) _print_server(cs, args, server) if args.poll: _poll_for_status(cs.servers.get, server.id, 'building', ['active']) def _poll_for_status(poll_fn, obj_id, action, final_ok_states, poll_period=5, show_progress=True, status_field="status", silent=False): """Block while an action is being performed, periodically printing progress. """ def print_progress(progress): if show_progress: msg = ('\rServer %(action)s... %(progress)s%% complete' % dict(action=action, progress=progress)) else: msg = '\rServer %(action)s...' % dict(action=action) sys.stdout.write(msg) sys.stdout.flush() if not silent: print while True: obj = poll_fn(obj_id) status = getattr(obj, status_field) if status: status = status.lower() progress = getattr(obj, 'progress', None) or 0 if status in final_ok_states: if not silent: print_progress(100) print("\nFinished") break elif status == "error": if not silent: print("\nError %s server" % action) break if not silent: print_progress(progress) time.sleep(poll_period) def _translate_keys(collection, convert): for item in collection: keys = item.__dict__.keys() for from_key, to_key in convert: if from_key in keys and to_key not in keys: setattr(item, to_key, item._info[from_key]) def _translate_extended_states(collection): power_states = [ 'NOSTATE', # 0x00 'Running', # 0x01 '', # 0x02 'Paused', # 0x03 'Shutdown', # 0x04 '', # 0x05 'Crashed', # 0x06 'Suspended' # 0x07 ] for item in collection: try: setattr(item, 'power_state', power_states[getattr(item, 'power_state')] ) except AttributeError: setattr(item, 'power_state', "N/A") try: getattr(item, 'task_state') except AttributeError: setattr(item, 'task_state', "N/A") def _translate_flavor_keys(collection): _translate_keys(collection, [('ram', 'memory_mb')]) def _print_flavor_extra_specs(flavor): try: return flavor.get_keys() except exceptions.NotFound: return "N/A" def _print_flavor_list(flavors, show_extra_specs=False): _translate_flavor_keys(flavors) headers = [ 'ID', 'Name', 'Memory_MB', 'Disk', 'Ephemeral', 'Swap', 'VCPUs', 'RXTX_Factor', 'Is_Public', ] if show_extra_specs: formatters = {'extra_specs': _print_flavor_extra_specs} headers.append('extra_specs') else: formatters = {} utils.print_list(flavors, headers, formatters) @utils.arg('--extra-specs', dest='extra_specs', action='store_true', default=False, help='Get extra-specs of each flavor.') @utils.arg('--all', dest='all', action='store_true', default=False, help='Display all flavors (Admin only).') def do_flavor_list(cs, args): """Print a list of available 'flavors' (sizes of servers).""" if args.all: flavors = cs.flavors.list(is_public=None) else: flavors = cs.flavors.list() _print_flavor_list(flavors, args.extra_specs) @utils.arg('flavor', metavar='', help="Name or ID of the flavor to delete") def do_flavor_delete(cs, args): """Delete a specific flavor""" flavorid = _find_flavor(cs, args.flavor) cs.flavors.delete(flavorid) _print_flavor_list([flavorid]) @utils.arg('flavor', metavar='', help="Name or ID of flavor") def do_flavor_show(cs, args): """Show details about the given flavor.""" flavor = _find_flavor(cs, args.flavor) _print_flavor(flavor) @utils.arg('name', metavar='', help="Name of the new flavor") @utils.arg('id', metavar='', help="Unique ID (integer or UUID) for the new flavor." " If specifying 'auto', a UUID will be generated as id") @utils.arg('ram', metavar='', help="Memory size in MB") @utils.arg('disk', metavar='', help="Disk size in GB") @utils.arg('--ephemeral', metavar='', help="Ephemeral space size in GB (default 0)", default=0) @utils.arg('vcpus', metavar='', help="Number of vcpus") @utils.arg('--swap', metavar='', help="Swap space size in MB (default 0)", default=0) @utils.arg('--rxtx-factor', metavar='', help="RX/TX factor (default 1)", default=1.0) @utils.arg('--is-public', metavar='', help="Make flavor accessible to the public (default true)", type=lambda v: strutils.bool_from_string(v, True), default=True) def do_flavor_create(cs, args): """Create a new flavor""" f = cs.flavors.create(args.name, args.ram, args.vcpus, args.disk, args.id, args.ephemeral, args.swap, args.rxtx_factor, args.is_public) _print_flavor_list([f]) @utils.arg('flavor', metavar='', help="Name or ID of flavor") @utils.arg('action', metavar='', choices=['set', 'unset'], help="Actions: 'set' or 'unset'") @utils.arg('metadata', metavar='', nargs='+', action='append', default=[], help='Extra_specs to set/unset (only key is necessary on unset)') def do_flavor_key(cs, args): """Set or unset extra_spec for a flavor.""" flavor = _find_flavor(cs, args.flavor) keypair = _extract_metadata(args) if args.action == 'set': flavor.set_keys(keypair) elif args.action == 'unset': flavor.unset_keys(keypair.keys()) @utils.arg('--flavor', metavar='', help="Filter results by flavor name or ID.") @utils.arg('--tenant', metavar='', help='Filter results by tenant ID.') def do_flavor_access_list(cs, args): """Print access information about the given flavor.""" if args.flavor and args.tenant: raise exceptions.CommandError("Unable to filter results by " "both --flavor and --tenant.") elif args.flavor: flavor = _find_flavor(cs, args.flavor) if flavor.is_public: raise exceptions.CommandError("Failed to get access list " "for public flavor type.") kwargs = {'flavor': flavor} elif args.tenant: kwargs = {'tenant': args.tenant} else: raise exceptions.CommandError("Unable to get all access lists. " "Specify --flavor or --tenant") try: access_list = cs.flavor_access.list(**kwargs) except NotImplementedError as e: raise exceptions.CommandError("%s" % str(e)) columns = ['Flavor_ID', 'Tenant_ID'] utils.print_list(access_list, columns) @utils.arg('flavor', metavar='', help="Flavor name or ID to add access for the given tenant.") @utils.arg('tenant', metavar='', help='Tenant ID to add flavor access for.') def do_flavor_access_add(cs, args): """Add flavor access for the given tenant.""" flavor = _find_flavor(cs, args.flavor) access_list = cs.flavor_access.add_tenant_access(flavor, args.tenant) columns = ['Flavor_ID', 'Tenant_ID'] utils.print_list(access_list, columns) @utils.arg('flavor', metavar='', help="Flavor name or ID to remove access for the given tenant.") @utils.arg('tenant', metavar='', help='Tenant ID to remove flavor access for.') def do_flavor_access_remove(cs, args): """Remove flavor access for the given tenant.""" flavor = _find_flavor(cs, args.flavor) access_list = cs.flavor_access.remove_tenant_access(flavor, args.tenant) columns = ['Flavor_ID', 'Tenant_ID'] utils.print_list(access_list, columns) @utils.arg('project_id', metavar='', help='The ID of the project.') def do_scrub(cs, args): """Delete data associated with the project.""" networks_list = cs.networks.list() networks_list = [network for network in networks_list if getattr(network, 'project_id', '') == args.project_id] search_opts = {'all_tenants': 1} groups = cs.security_groups.list(search_opts) groups = [group for group in groups if group.tenant_id == args.project_id] for network in networks_list: cs.networks.disassociate(network) for group in groups: cs.security_groups.delete(group) def do_network_list(cs, _args): """Print a list of available networks.""" network_list = cs.networks.list() columns = ['ID', 'Label', 'Cidr'] utils.print_list(network_list, columns) @utils.arg('network', metavar='', help="uuid or label of network") def do_network_show(cs, args): """Show details about the given network.""" network = utils.find_resource(cs.networks, args.network) utils.print_dict(network._info) @utils.arg('--host-only', dest='host_only', metavar='<0|1>', nargs='?', type=int, const=1, default=0) @utils.arg('--project-only', dest='project_only', metavar='<0|1>', nargs='?', type=int, const=1, default=0) @utils.arg('network', metavar='', help="uuid of network") def do_network_disassociate(cs, args): """Disassociate host and/or project from the given network.""" if args.host_only: cs.networks.disassociate(args.network, True, False) elif args.project_only: cs.networks.disassociate(args.network, False, True) else: cs.networks.disassociate(args.network, True, True) @utils.arg('network', metavar='', help="uuid of network") @utils.arg('host', metavar='', help="Name of host") def do_network_associate_host(cs, args): """Associate host with network.""" cs.networks.associate_host(args.network, args.host) @utils.arg('network', metavar='', help="uuid of network") def do_network_associate_project(cs, args): """Associate project with network.""" cs.networks.associate_project(args.network) def _filter_network_create_options(args): valid_args = ['label', 'cidr', 'vlan_start', 'vpn_start', 'cidr_v6', 'gateway', 'gateway_v6', 'bridge', 'bridge_interface', 'multi_host', 'dns1', 'dns2', 'uuid', 'fixed_cidr', 'project_id', 'priority'] kwargs = {} for k, v in args.__dict__.items(): if k in valid_args and v is not None: kwargs[k] = v return kwargs @utils.arg('label', metavar='', help="Label for network") @utils.arg('--fixed-range-v4', dest='cidr', metavar='', help="IPv4 subnet (ex: 10.0.0.0/8)") @utils.arg('--fixed-range-v6', dest="cidr_v6", help='IPv6 subnet (ex: fe80::/64') @utils.arg('--vlan', dest='vlan_start', metavar='', help="vlan id") @utils.arg('--vpn', dest='vpn_start', metavar='', help="vpn start") @utils.arg('--gateway', dest="gateway", help='gateway') @utils.arg('--gateway-v6', dest="gateway_v6", help='ipv6 gateway') @utils.arg('--bridge', dest="bridge", metavar='', help='VIFs on this network are connected to this bridge') @utils.arg('--bridge-interface', dest="bridge_interface", metavar='', help='the bridge is connected to this interface') @utils.arg('--multi-host', dest="multi_host", metavar="<'T'|'F'>", help='Multi host') @utils.arg('--dns1', dest="dns1", metavar="", help='First DNS') @utils.arg('--dns2', dest="dns2", metavar="", help='Second DNS') @utils.arg('--uuid', dest="uuid", metavar="", help='Network UUID') @utils.arg('--fixed-cidr', dest="fixed_cidr", metavar='', help='IPv4 subnet for fixed IPS (ex: 10.20.0.0/16)') @utils.arg('--project-id', dest="project_id", metavar="", help='Project id') @utils.arg('--priority', dest="priority", metavar="", help='Network interface priority') def do_network_create(cs, args): """Create a network.""" if not (args.cidr or args.cidr_v6): raise exceptions.CommandError( "Must specify eith fixed_range_v4 or fixed_range_v6") kwargs = _filter_network_create_options(args) if args.multi_host is not None: kwargs['multi_host'] = bool(args.multi_host == 'T' or strutils.bool_from_string(args.multi_host)) cs.networks.create(**kwargs) @utils.arg('--limit', dest="limit", metavar="", help='number of images to return per request') @utils.service_type('image') def do_image_list(cs, _args): """Print a list of available images to boot from.""" limit = _args.limit image_list = cs.images.list(limit=limit) def parse_server_name(image): try: return image.server['id'] except (AttributeError, KeyError): return '' fmts = {'Server': parse_server_name} utils.print_list(image_list, ['ID', 'Name', 'Status', 'Server'], fmts, sortby_index=1) @utils.arg('image', metavar='', help="Name or ID of image") @utils.arg('action', metavar='', choices=['set', 'delete'], help="Actions: 'set' or 'delete'") @utils.arg('metadata', metavar='', nargs='+', action='append', default=[], help='Metadata to add/update or delete (only key is necessary on delete)') def do_image_meta(cs, args): """Set or Delete metadata on an image.""" image = _find_image(cs, args.image) metadata = _extract_metadata(args) if args.action == 'set': cs.images.set_meta(image, metadata) elif args.action == 'delete': cs.images.delete_meta(image, metadata.keys()) def _extract_metadata(args): metadata = {} for metadatum in args.metadata[0]: # Can only pass the key in on 'delete' # So this doesn't have to have '=' if metadatum.find('=') > -1: (key, value) = metadatum.split('=', 1) else: key = metadatum value = None metadata[key] = value return metadata def _print_image(image): info = image._info.copy() # try to replace a server entity to just an id server = info.pop('server', None) try: info['server'] = server['id'] except (KeyError, TypeError): pass # break up metadata and display each on its own row properties = info.pop('properties', {}) try: for key, value in properties.items(): _key = 'Property %s' % key info[_key] = value except AttributeError: pass utils.print_dict(info) def _print_flavor(flavor): info = flavor._info.copy() # ignore links, we don't need to present those info.pop('links') info.update({"extra_specs": _print_flavor_extra_specs(flavor)}) utils.print_dict(info) @utils.arg('image', metavar='', help="Name or ID of image") @utils.service_type('image') def do_image_show(cs, args): """Show details about the given image.""" image = _find_image(cs, args.image) _print_image(image) @utils.arg('image', metavar='', nargs='+', help='Name or ID of image(s).') def do_image_delete(cs, args): """Delete specified image(s).""" for image in args.image: try: _find_image(cs, image).delete() except Exception as e: print("Delete for image %s failed: %s" % (image, e)) @utils.arg('--reservation-id', dest='reservation_id', metavar='', default=None, help='Only return servers that match reservation-id.') @utils.arg('--reservation_id', help=argparse.SUPPRESS) @utils.arg('--ip', dest='ip', metavar='', default=None, help='Search with regular expression match by IP address (Admin only).') @utils.arg('--ip6', dest='ip6', metavar='', default=None, help='Search with regular expression match by IPv6 address (Admin only).') @utils.arg('--name', dest='name', metavar='', default=None, help='Search with regular expression match by name') @utils.arg('--instance-name', dest='instance_name', metavar='', default=None, help='Search with regular expression match by server name (Admin only).') @utils.arg('--instance_name', help=argparse.SUPPRESS) @utils.arg('--status', dest='status', metavar='', default=None, help='Search by server status') @utils.arg('--flavor', dest='flavor', metavar='', default=None, help='Search by flavor name or ID') @utils.arg('--image', dest='image', metavar='', default=None, help='Search by image name or ID') @utils.arg('--host', dest='host', metavar='', default=None, help='Search servers by hostname to which they are assigned ' '(Admin only).') @utils.arg('--all-tenants', dest='all_tenants', metavar='<0|1>', nargs='?', type=int, const=1, default=int(strutils.bool_from_string( os.environ.get("ALL_TENANTS", 'false'), True)), help='Display information from all tenants (Admin only).') @utils.arg('--all_tenants', nargs='?', type=int, const=1, help=argparse.SUPPRESS) @utils.arg('--tenant', #nova db searches by project_id dest='tenant', metavar='', nargs='?', help='Display information from single tenant (Admin only).') @utils.arg('--fields', default=None, metavar='', help='Comma-separated list of fields to display. ' 'Use the show command to see which fields are available.') @utils.arg('--deleted', dest='deleted', action="store_true", default=False, help='Only display deleted servers (Admin only).') @utils.arg('--minimal', dest='minimal', action="store_true", default=False, help='Get only uuid and name.') def do_list(cs, args): """List active servers.""" imageid = None flavorid = None if args.image: imageid = _find_image(cs, args.image).id if args.flavor: flavorid = _find_flavor(cs, args.flavor).id search_opts = { 'all_tenants': args.all_tenants, 'reservation_id': args.reservation_id, 'ip': args.ip, 'ip6': args.ip6, 'name': args.name, 'image': imageid, 'flavor': flavorid, 'status': args.status, 'tenant_id': args.tenant, 'host': args.host, 'deleted': args.deleted, 'instance_name': args.instance_name} filters = {'flavor': lambda f: f['id'], 'security_groups': utils._format_security_groups} formatters = {} field_titles = [] if args.fields: for field in args.fields.split(','): field_title, formatter = utils._make_field_formatter(field, filters) field_titles.append(field_title) formatters[field_title] = formatter id_col = 'ID' detailed = not args.minimal servers = cs.servers.list(detailed=detailed, search_opts=search_opts) convert = [('os-extended-server-attributes:hypervisor_hostname', 'host'), ('os-extended-status:task_state', 'task_state'), ('os-extended-server-attributes:instance_name', 'instance_name'), ('os-extended-status:power_state', 'power_state')] _translate_keys(servers, convert) _translate_extended_states(servers) if args.minimal: columns = [ id_col, 'Name'] elif field_titles: columns = [id_col] + field_titles else: columns = [ id_col, 'Name', 'Status', 'Task State', 'Power State', 'Networks' ] formatters['Networks'] = utils._format_servers_list_networks utils.print_list(servers, columns, formatters, sortby_index=1) @utils.arg('--hard', dest='reboot_type', action='store_const', const=servers.REBOOT_HARD, default=servers.REBOOT_SOFT, help='Perform a hard reboot (instead of a soft one).') @utils.arg('server', metavar='', help='Name or ID of server.') @utils.arg('--poll', dest='poll', action="store_true", default=False, help='Blocks while server is rebooting.') def do_reboot(cs, args): """Reboot a server.""" server = _find_server(cs, args.server) server.reboot(args.reboot_type) if args.poll: _poll_for_status(cs.servers.get, server.id, 'rebooting', ['active'], show_progress=False) @utils.arg('server', metavar='', help='Name or ID of server.') @utils.arg('image', metavar='', help="Name or ID of new image.") @utils.arg('--rebuild-password', dest='rebuild_password', metavar='', default=False, help="Set the provided password on the rebuild server.") @utils.arg('--rebuild_password', help=argparse.SUPPRESS) @utils.arg('--poll', dest='poll', action="store_true", default=False, help='Blocks while server rebuilds so progress can be reported.') @utils.arg('--minimal', dest='minimal', action="store_true", default=False, help='Skips flavor/image lookups when showing servers') def do_rebuild(cs, args): """Shutdown, re-image, and re-boot a server.""" server = _find_server(cs, args.server) image = _find_image(cs, args.image) if args.rebuild_password is not False: _password = args.rebuild_password else: _password = None kwargs = utils.get_resource_manager_extra_kwargs(do_rebuild, args) server.rebuild(image, _password, **kwargs) _print_server(cs, args) if args.poll: _poll_for_status(cs.servers.get, server.id, 'rebuilding', ['active']) @utils.arg('server', metavar='', help='Name (old name) or ID of server.') @utils.arg('name', metavar='', help='New name for the server.') def do_rename(cs, args): """Rename a server.""" _find_server(cs, args.server).update(name=args.name) @utils.arg('server', metavar='', help='Name or ID of server.') @utils.arg('flavor', metavar='', help="Name or ID of new flavor.") @utils.arg('--poll', dest='poll', action="store_true", default=False, help='Blocks while server resizes so progress can be reported.') def do_resize(cs, args): """Resize a server.""" server = _find_server(cs, args.server) flavor = _find_flavor(cs, args.flavor) kwargs = utils.get_resource_manager_extra_kwargs(do_resize, args) server.resize(flavor, **kwargs) if args.poll: _poll_for_status(cs.servers.get, server.id, 'resizing', ['active', 'verify_resize']) @utils.arg('server', metavar='', help='Name or ID of server.') def do_resize_confirm(cs, args): """Confirm a previous resize.""" _find_server(cs, args.server).confirm_resize() @utils.arg('server', metavar='', help='Name or ID of server.') def do_resize_revert(cs, args): """Revert a previous resize (and return to the previous VM).""" _find_server(cs, args.server).revert_resize() @utils.arg('server', metavar='', help='Name or ID of server.') @utils.arg('--poll', dest='poll', action="store_true", default=False, help='Blocks while server migrates so progress can be reported.') def do_migrate(cs, args): """Migrate a server. The new host will be selected by the scheduler.""" server = _find_server(cs, args.server) server.migrate() if args.poll: _poll_for_status(cs.servers.get, server.id, 'migrating', ['active', 'verify_resize']) @utils.arg('server', metavar='', help='Name or ID of server.') def do_pause(cs, args): """Pause a server.""" _find_server(cs, args.server).pause() @utils.arg('server', metavar='', help='Name or ID of server.') def do_unpause(cs, args): """Unpause a server.""" _find_server(cs, args.server).unpause() @utils.arg('server', metavar='', help='Name or ID of server.') def do_stop(cs, args): """Stop a server.""" _find_server(cs, args.server).stop() @utils.arg('server', metavar='', help='Name or ID of server.') def do_start(cs, args): """Start a server.""" _find_server(cs, args.server).start() @utils.arg('server', metavar='', help='Name or ID of server.') def do_lock(cs, args): """Lock a server.""" _find_server(cs, args.server).lock() @utils.arg('server', metavar='', help='Name or ID of server.') def do_unlock(cs, args): """Unlock a server.""" _find_server(cs, args.server).unlock() @utils.arg('server', metavar='', help='Name or ID of server.') def do_suspend(cs, args): """Suspend a server.""" _find_server(cs, args.server).suspend() @utils.arg('server', metavar='', help='Name or ID of server.') def do_resume(cs, args): """Resume a server.""" _find_server(cs, args.server).resume() @utils.arg('server', metavar='', help='Name or ID of server.') def do_rescue(cs, args): """Rescue a server.""" utils.print_dict(_find_server(cs, args.server).rescue()[1]) @utils.arg('server', metavar='', help='Name or ID of server.') def do_unrescue(cs, args): """Unrescue a server.""" _find_server(cs, args.server).unrescue() @utils.arg('server', metavar='', help='Name or ID of server.') def do_diagnostics(cs, args): """Retrieve server diagnostics.""" server = _find_server(cs, args.server) utils.print_dict(cs.servers.diagnostics(server)[1], wrap=80) @utils.arg('server', metavar='', help='Name or ID of server.') def do_root_password(cs, args): """ Change the root password for a server. """ server = _find_server(cs, args.server) p1 = getpass.getpass('New password: ') p2 = getpass.getpass('Again: ') if p1 != p2: raise exceptions.CommandError("Passwords do not match.") server.change_password(p1) @utils.arg('server', metavar='', help='Name or ID of server.') @utils.arg('name', metavar='', help='Name of snapshot.') @utils.arg('--show', dest='show', action="store_true", default=False, help='Print image info.') @utils.arg('--poll', dest='poll', action="store_true", default=False, help='Blocks while server snapshots so progress can be reported.') def do_image_create(cs, args): """Create a new image by taking a snapshot of a running server.""" server = _find_server(cs, args.server) image_uuid = cs.servers.create_image(server, args.name) if args.poll: _poll_for_status(cs.images.get, image_uuid, 'snapshotting', ['active']) # NOTE(sirp): A race-condition exists between when the image finishes # uploading and when the servers's `task_state` is cleared. To account # for this, we need to poll a second time to ensure the `task_state` is # cleared before returning, ensuring that a snapshot taken immediately # after this function returns will succeed. # # A better long-term solution will be to separate 'snapshotting' and # 'image-uploading' in Nova and clear the task-state once the VM # snapshot is complete but before the upload begins. task_state_field = "OS-EXT-STS:task_state" if hasattr(server, task_state_field): _poll_for_status(cs.servers.get, server.id, 'image_snapshot', [None], status_field=task_state_field, show_progress=False, silent=True) if args.show: _print_image(cs.images.get(image_uuid)) @utils.arg('server', metavar='', help='Name or ID of server.') @utils.arg('name', metavar='', help='Name of the backup image.') @utils.arg('backup_type', metavar='', help='The backup type, like "daily" or "weekly".') @utils.arg('rotation', metavar='', help='Int parameter representing how many backups to keep around.') def do_backup(cs, args): """Backup a server by creating a 'backup' type snapshot.""" _find_server(cs, args.server).backup(args.name, args.backup_type, args.rotation) @utils.arg('server', metavar='', help="Name or ID of server") @utils.arg('action', metavar='', choices=['set', 'delete'], help="Actions: 'set' or 'delete'") @utils.arg('metadata', metavar='', nargs='+', action='append', default=[], help='Metadata to set or delete (only key is necessary on delete)') def do_meta(cs, args): """Set or Delete metadata on a server.""" server = _find_server(cs, args.server) metadata = _extract_metadata(args) if args.action == 'set': cs.servers.set_meta(server, metadata) elif args.action == 'delete': cs.servers.delete_meta(server, metadata.keys()) def _print_server(cs, args, server=None): # By default when searching via name we will do a # findall(name=blah) and due a REST /details which is not the same # as a .get() and doesn't get the information about flavors and # images. This fix it as we redo the call with the id which does a # .get() to get all informations. if not server: server = _find_server(cs, args.server) minimal = getattr(args, "minimal", False) networks = server.networks info = server._info.copy() for network_label, address_list in networks.items(): info['%s network' % network_label] = ', '.join(address_list) flavor = info.get('flavor', {}) flavor_id = flavor.get('id', '') if minimal: info['flavor'] = flavor_id else: info['flavor'] = '%s (%s)' % (_find_flavor(cs, flavor_id).name, flavor_id) image = info.get('image', {}) if image: image_id = image.get('id', '') if minimal: info['image'] = image_id else: try: info['image'] = '%s (%s)' % (_find_image(cs, image_id).name, image_id) except Exception: info['image'] = '%s (%s)' % ("Image not found", image_id) else: # Booted from volume info['image'] = "Attempt to boot from volume - no image supplied" info.pop('links', None) info.pop('addresses', None) utils.print_dict(info) @utils.arg('--minimal', dest='minimal', action="store_true", default=False, help='Skips flavor/image lookups when showing servers') @utils.arg('server', metavar='', help='Name or ID of server.') def do_show(cs, args): """Show details about the given server.""" _print_server(cs, args) @utils.arg('server', metavar='', nargs='+', help='Name or ID of server(s).') def do_delete(cs, args): """Immediately shut down and delete specified server(s).""" failure_count = 0 for server in args.server: try: _find_server(cs, server).delete() except Exception as e: failure_count += 1 print(e) if failure_count == len(args.server): raise exceptions.CommandError("Unable to delete any of the specified " "servers.") def _find_server(cs, server): """Get a server by name or ID.""" return utils.find_resource(cs.servers, server) def _find_image(cs, image): """Get an image by name or ID.""" return utils.find_resource(cs.images, image) def _find_flavor(cs, flavor): """Get a flavor by name, ID, or RAM size.""" try: return utils.find_resource(cs.flavors, flavor) except exceptions.NotFound: return cs.flavors.find(ram=flavor) @utils.arg('server', metavar='', help='Name or ID of server.') @utils.arg('network_id', metavar='', help='Network ID.') def do_add_fixed_ip(cs, args): """Add new IP address on a network to server.""" server = _find_server(cs, args.server) server.add_fixed_ip(args.network_id) @utils.arg('server', metavar='', help='Name or ID of server.') @utils.arg('address', metavar='
', help='IP Address.') def do_remove_fixed_ip(cs, args): """Remove an IP address from a server.""" server = _find_server(cs, args.server) server.remove_fixed_ip(args.address) def _translate_availability_zone_keys(collection): _translate_keys(collection, [('zone_name', 'name'), ('zone_state', 'status')]) @utils.arg('server', metavar='', help='Name or ID of server.') @utils.arg('volume', metavar='', help='ID of the volume to attach.') @utils.arg('device', metavar='', default=None, nargs='?', help='Name of the device e.g. /dev/vdb. ' 'Use "auto" for autoassign (if supported)') def do_volume_attach(cs, args): """Attach a volume to a server.""" if args.device == 'auto': args.device = None volume = cs.volumes.attach_server_volume(_find_server(cs, args.server).id, args.volume, args.device) @utils.arg('server', metavar='', help='Name or ID of server.') @utils.arg('attachment_id', metavar='', help='Attachment ID of the volume.') @utils.arg('new_volume', metavar='', help='ID of the volume to attach.') def do_volume_update(cs, args): """Update volume attachment.""" volume = cs.volumes.update_server_volume(_find_server(cs, args.server).id, args.attachment_id, args.new_volume) @utils.arg('server', metavar='', help='Name or ID of server.') @utils.arg('attachment_id', metavar='', help='Attachment ID of the volume.') def do_volume_detach(cs, args): """Detach a volume from a server.""" cs.volumes.delete_server_volume(_find_server(cs, args.server).id, args.attachment_id) @utils.arg('server', metavar='', help='Name or ID of server.') @utils.arg('console_type', metavar='', help='Type of vnc console ("novnc" or "xvpvnc").') def do_get_vnc_console(cs, args): """Get a vnc console to a server.""" server = _find_server(cs, args.server) data = server.get_vnc_console(args.console_type) class VNCConsole: def __init__(self, console_dict): self.type = console_dict['type'] self.url = console_dict['url'] utils.print_list([VNCConsole(data['console'])], ['Type', 'Url']) @utils.arg('server', metavar='', help='Name or ID of server.') @utils.arg('console_type', metavar='', help='Type of spice console ("spice-html5").') def do_get_spice_console(cs, args): """Get a spice console to a server.""" server = _find_server(cs, args.server) data = server.get_spice_console(args.console_type) class SPICEConsole: def __init__(self, console_dict): self.type = console_dict['type'] self.url = console_dict['url'] utils.print_list([SPICEConsole(data['console'])], ['Type', 'Url']) @utils.arg('server', metavar='', help='Name or ID of server.') @utils.arg('console_type', metavar='', help='Type of rdp console ("rdp-html5").') def do_get_rdp_console(cs, args): """Get a rdp console to a server.""" server = _find_server(cs, args.server) data = server.get_rdp_console(args.console_type) class RDPConsole: def __init__(self, console_dict): self.type = console_dict['type'] self.url = console_dict['url'] utils.print_list([RDPConsole(data['console'])], ['Type', 'Url']) @utils.arg('server', metavar='', help='Name or ID of server.') @utils.arg('private_key', metavar='', help='Private key (used locally to decrypt password) (Optional). ' 'When specified, the command displays the clear (decrypted) VM ' 'password. When not specified, the ciphered VM password is ' 'displayed.', nargs='?', default=None) def do_get_password(cs, args): """Get password for a server.""" server = _find_server(cs, args.server) data = server.get_password(args.private_key) print(data) @utils.arg('server', metavar='', help='Name or ID of server.') def do_clear_password(cs, args): """Clear password for a server.""" server = _find_server(cs, args.server) server.clear_password() def _print_floating_ip_list(floating_ips): utils.print_list(floating_ips, ['Ip', 'Server Id', 'Fixed Ip', 'Pool']) @utils.arg('server', metavar='', help='Name or ID of server.') @utils.arg('--length', metavar='', default=None, help='Length in lines to tail.') def do_console_log(cs, args): """Get console log output of a server.""" server = _find_server(cs, args.server) data = server.get_console_output(length=args.length) print(data) @utils.arg('server', metavar='', help='Name or ID of server.') @utils.arg('address', metavar='
', help='IP Address.') @utils.arg('--fixed-address', metavar='', default=None, help='Fixed IP Address to associate with.') def do_add_floating_ip(cs, args): """Add a floating IP address to a server.""" server = _find_server(cs, args.server) server.add_floating_ip(args.address, args.fixed_address) @utils.arg('server', metavar='', help='Name or ID of server.') @utils.arg('address', metavar='
', help='IP Address.') def do_remove_floating_ip(cs, args): """Remove a floating IP address from a server.""" server = _find_server(cs, args.server) server.remove_floating_ip(args.address) @utils.arg('server', metavar='', help='Name or ID of server.') @utils.arg('secgroup', metavar='', help='Name of Security Group.') def do_add_secgroup(cs, args): """Add a Security Group to a server.""" server = _find_server(cs, args.server) server.add_security_group(args.secgroup) @utils.arg('server', metavar='', help='Name or ID of server.') @utils.arg('secgroup', metavar='', help='Name of Security Group.') def do_remove_secgroup(cs, args): """Remove a Security Group from a server.""" server = _find_server(cs, args.server) server.remove_security_group(args.secgroup) @utils.arg('pool', metavar='', help='Name of Floating IP Pool. (Optional)', nargs='?', default=None) def do_floating_ip_create(cs, args): """Allocate a floating IP for the current tenant.""" _print_floating_ip_list([cs.floating_ips.create(pool=args.pool)]) @utils.arg('address', metavar='
', help='IP of Floating Ip.') def do_floating_ip_delete(cs, args): """De-allocate a floating IP.""" floating_ips = cs.floating_ips.list() for floating_ip in floating_ips: if floating_ip.ip == args.address: return cs.floating_ips.delete(floating_ip.id) raise exceptions.CommandError("Floating ip %s not found." % args.address) def do_floating_ip_list(cs, _args): """List floating ips for this tenant.""" _print_floating_ip_list(cs.floating_ips.list()) def do_floating_ip_pool_list(cs, _args): """List all floating ip pools.""" utils.print_list(cs.floating_ip_pools.list(), ['name']) @utils.arg('--host', dest='host', metavar='', default=None, help='Filter by host') def do_floating_ip_bulk_list(cs, args): """List all floating ips.""" utils.print_list(cs.floating_ips_bulk.list(args.host), ['project_id', 'address', 'instance_uuid', 'pool', 'interface']) @utils.arg('ip_range', metavar='', help='Address range to create') @utils.arg('--pool', dest='pool', metavar='', default=None, help='Pool for new Floating IPs') @utils.arg('--interface', metavar='', default=None, help='Interface for new Floating IPs') def do_floating_ip_bulk_create(cs, args): """Bulk create floating ips by range.""" cs.floating_ips_bulk.create(args.ip_range, args.pool, args.interface) @utils.arg('ip_range', metavar='', help='Address range to delete') def do_floating_ip_bulk_delete(cs, args): """Bulk delete floating ips by range.""" cs.floating_ips_bulk.delete(args.ip_range) def _print_dns_list(dns_entries): utils.print_list(dns_entries, ['ip', 'name', 'domain']) def _print_domain_list(domain_entries): utils.print_list(domain_entries, ['domain', 'scope', 'project', 'availability_zone']) def do_dns_domains(cs, args): """Print a list of available dns domains.""" domains = cs.dns_domains.domains() _print_domain_list(domains) @utils.arg('domain', metavar='', help='DNS domain') @utils.arg('--ip', metavar='', help='ip address', default=None) @utils.arg('--name', metavar='', help='DNS name', default=None) def do_dns_list(cs, args): """List current DNS entries for domain and ip or domain and name.""" if not (args.ip or args.name): raise exceptions.CommandError( "You must specify either --ip or --name") if args.name: entry = cs.dns_entries.get(args.domain, args.name) _print_dns_list([entry]) else: entries = cs.dns_entries.get_for_ip(args.domain, ip=args.ip) _print_dns_list(entries) @utils.arg('ip', metavar='', help='ip address') @utils.arg('name', metavar='', help='DNS name') @utils.arg('domain', metavar='', help='DNS domain') @utils.arg('--type', metavar='', help='dns type (e.g. "A")', default='A') def do_dns_create(cs, args): """Create a DNS entry for domain, name and ip.""" cs.dns_entries.create(args.domain, args.name, args.ip, args.type) @utils.arg('domain', metavar='', help='DNS domain') @utils.arg('name', metavar='', help='DNS name') def do_dns_delete(cs, args): """Delete the specified DNS entry.""" cs.dns_entries.delete(args.domain, args.name) @utils.arg('domain', metavar='', help='DNS domain') def do_dns_delete_domain(cs, args): """Delete the specified DNS domain.""" cs.dns_domains.delete(args.domain) @utils.arg('domain', metavar='', help='DNS domain') @utils.arg('--availability-zone', metavar='', default=None, help='Limit access to this domain to servers ' 'in the specified availability zone.') @utils.arg('--availability_zone', help=argparse.SUPPRESS) def do_dns_create_private_domain(cs, args): """Create the specified DNS domain.""" cs.dns_domains.create_private(args.domain, args.availability_zone) @utils.arg('domain', metavar='', help='DNS domain') @utils.arg('--project', metavar='', help='Limit access to this domain to users ' 'of the specified project.', default=None) def do_dns_create_public_domain(cs, args): """Create the specified DNS domain.""" cs.dns_domains.create_public(args.domain, args.project) def _print_secgroup_rules(rules): class FormattedRule: def __init__(self, obj): items = (obj if isinstance(obj, dict) else obj._info).items() for k, v in items: if k == 'ip_range': v = v.get('cidr') elif k == 'group': k = 'source_group' v = v.get('name') if v is None: v = '' setattr(self, k, v) rules = [FormattedRule(rule) for rule in rules] utils.print_list(rules, ['IP Protocol', 'From Port', 'To Port', 'IP Range', 'Source Group']) def _print_secgroups(secgroups): utils.print_list(secgroups, ['Id', 'Name', 'Description']) def _get_secgroup(cs, secgroup): # Check secgroup is an ID if uuidutils.is_uuid_like(strutils.safe_encode(secgroup)): try: return cs.security_groups.get(secgroup) except exceptions.NotFound: pass # Check secgroup as a name match_found = False for s in cs.security_groups.list(): encoding = (locale.getpreferredencoding() or sys.stdin.encoding or 'UTF-8') s.name = s.name.encode(encoding) if secgroup == s.name: if match_found is not False: msg = ("Multiple security group matches found for name" " '%s', use an ID to be more specific." % secgroup) raise exceptions.NoUniqueMatch(msg) match_found = s if match_found is False: raise exceptions.CommandError("Secgroup ID or name '%s' not found." % secgroup) return match_found @utils.arg('secgroup', metavar='', help='ID or name of security group.') @utils.arg('ip_proto', metavar='', help='IP protocol (icmp, tcp, udp).') @utils.arg('from_port', metavar='', help='Port at start of range.') @utils.arg('to_port', metavar='', help='Port at end of range.') @utils.arg('cidr', metavar='', help='CIDR for address range.') def do_secgroup_add_rule(cs, args): """Add a rule to a security group.""" secgroup = _get_secgroup(cs, args.secgroup) rule = cs.security_group_rules.create(secgroup.id, args.ip_proto, args.from_port, args.to_port, args.cidr) _print_secgroup_rules([rule]) @utils.arg('secgroup', metavar='', help='ID or name of security group.') @utils.arg('ip_proto', metavar='', help='IP protocol (icmp, tcp, udp).') @utils.arg('from_port', metavar='', help='Port at start of range.') @utils.arg('to_port', metavar='', help='Port at end of range.') @utils.arg('cidr', metavar='', help='CIDR for address range.') def do_secgroup_delete_rule(cs, args): """Delete a rule from a security group.""" secgroup = _get_secgroup(cs, args.secgroup) for rule in secgroup.rules: if (rule['ip_protocol'] and rule['ip_protocol'].upper() == args.ip_proto.upper() and rule['from_port'] == int(args.from_port) and rule['to_port'] == int(args.to_port) and rule['ip_range']['cidr'] == args.cidr): _print_secgroup_rules([rule]) return cs.security_group_rules.delete(rule['id']) raise exceptions.CommandError("Rule not found") @utils.arg('name', metavar='', help='Name of security group.') @utils.arg('description', metavar='', help='Description of security group.') def do_secgroup_create(cs, args): """Create a security group.""" secgroup = cs.security_groups.create(args.name, args.description) _print_secgroups([secgroup]) @utils.arg('secgroup', metavar='', help='ID or name of security group.') @utils.arg('name', metavar='', help='Name of security group.') @utils.arg('description', metavar='', help='Description of security group.') def do_secgroup_update(cs, args): """Update a security group.""" sg = _get_secgroup(cs, args.secgroup) secgroup = cs.security_groups.update(sg, args.name, args.description) _print_secgroups([secgroup]) @utils.arg('secgroup', metavar='', help='ID or name of security group.') def do_secgroup_delete(cs, args): """Delete a security group.""" secgroup = _get_secgroup(cs, args.secgroup) cs.security_groups.delete(secgroup) _print_secgroups([secgroup]) @utils.arg('--all-tenants', dest='all_tenants', metavar='<0|1>', nargs='?', type=int, const=1, default=int(strutils.bool_from_string( os.environ.get("ALL_TENANTS", 'false'), True)), help='Display information from all tenants (Admin only).') @utils.arg('--all_tenants', nargs='?', type=int, const=1, help=argparse.SUPPRESS) def do_secgroup_list(cs, args): """List security groups for the current tenant.""" search_opts = {'all_tenants': args.all_tenants} columns = ['Id', 'Name', 'Description'] if args.all_tenants: columns.append('Tenant_ID') groups = cs.security_groups.list(search_opts=search_opts) utils.print_list(groups, columns) @utils.arg('secgroup', metavar='', help='ID or name of security group.') def do_secgroup_list_rules(cs, args): """List rules for a security group.""" secgroup = _get_secgroup(cs, args.secgroup) _print_secgroup_rules(secgroup.rules) @utils.arg('secgroup', metavar='', help='ID or name of security group.') @utils.arg('source_group', metavar='', help='ID or name of source group.') @utils.arg('ip_proto', metavar='', help='IP protocol (icmp, tcp, udp).') @utils.arg('from_port', metavar='', help='Port at start of range.') @utils.arg('to_port', metavar='', help='Port at end of range.') def do_secgroup_add_group_rule(cs, args): """Add a source group rule to a security group.""" secgroup = _get_secgroup(cs, args.secgroup) source_group = _get_secgroup(cs, args.source_group) params = {} params['group_id'] = source_group.id if args.ip_proto or args.from_port or args.to_port: if not (args.ip_proto and args.from_port and args.to_port): raise exceptions.CommandError("ip_proto, from_port, and to_port" " must be specified together") params['ip_protocol'] = args.ip_proto.upper() params['from_port'] = args.from_port params['to_port'] = args.to_port rule = cs.security_group_rules.create(secgroup.id, **params) _print_secgroup_rules([rule]) @utils.arg('secgroup', metavar='', help='ID or name of security group.') @utils.arg('source_group', metavar='', help='ID or name of source group.') @utils.arg('ip_proto', metavar='', help='IP protocol (icmp, tcp, udp).') @utils.arg('from_port', metavar='', help='Port at start of range.') @utils.arg('to_port', metavar='', help='Port at end of range.') def do_secgroup_delete_group_rule(cs, args): """Delete a source group rule from a security group.""" secgroup = _get_secgroup(cs, args.secgroup) source_group = _get_secgroup(cs, args.source_group) params = {} params['group_name'] = source_group.name if args.ip_proto or args.from_port or args.to_port: if not (args.ip_proto and args.from_port and args.to_port): raise exceptions.CommandError("ip_proto, from_port, and to_port" " must be specified together") params['ip_protocol'] = args.ip_proto.upper() params['from_port'] = int(args.from_port) params['to_port'] = int(args.to_port) for rule in secgroup.rules: if (rule.get('ip_protocol').upper() == params.get( 'ip_protocol').upper() and rule.get('from_port') == params.get('from_port') and rule.get('to_port') == params.get('to_port') and rule.get('group', {}).get('name') == params.get('group_name')): return cs.security_group_rules.delete(rule['id']) raise exceptions.CommandError("Rule not found") @utils.arg('name', metavar='', help='Name of key.') @utils.arg('--pub-key', metavar='', default=None, help='Path to a public ssh key.') @utils.arg('--pub_key', help=argparse.SUPPRESS) def do_keypair_add(cs, args): """Create a new key pair for use with servers.""" name = args.name pub_key = args.pub_key if pub_key: try: with open(os.path.expanduser(pub_key)) as f: pub_key = f.read() except IOError as e: raise exceptions.CommandError("Can't open or read '%s': %s" % (pub_key, e)) keypair = cs.keypairs.create(name, pub_key) if not pub_key: private_key = keypair.private_key print(private_key) @utils.arg('name', metavar='', help='Keypair name to delete.') def do_keypair_delete(cs, args): """Delete keypair given by its name.""" name = args.name cs.keypairs.delete(name) def do_keypair_list(cs, args): """Print a list of keypairs for a user""" keypairs = cs.keypairs.list() columns = ['Name', 'Fingerprint'] utils.print_list(keypairs, columns) def _print_keypair(keypair): kp = keypair._info.copy() pk = kp.pop('public_key') utils.print_dict(kp) print("Public key: %s" % pk) @utils.arg('keypair', metavar='', help="Name or ID of keypair") def do_keypair_show(cs, args): """Show details about the given keypair.""" keypair = cs.keypairs.get(args.keypair) _print_keypair(keypair) @utils.arg('--start', metavar='', help='Usage range start date ex 2012-01-20 (default: 4 weeks ago)', default=None) @utils.arg('--end', metavar='', help='Usage range end date, ex 2012-01-20 (default: tomorrow) ', default=None) def do_usage_list(cs, args): """List usage data for all tenants.""" dateformat = "%Y-%m-%d" rows = ["Tenant ID", "Servers", "RAM MB-Hours", "CPU Hours", "Disk GB-Hours"] now = timeutils.utcnow() if args.start: start = datetime.datetime.strptime(args.start, dateformat) else: start = now - datetime.timedelta(weeks=4) if args.end: end = datetime.datetime.strptime(args.end, dateformat) else: end = now + datetime.timedelta(days=1) def simplify_usage(u): simplerows = map(lambda x: x.lower().replace(" ", "_"), rows) setattr(u, simplerows[0], u.tenant_id) setattr(u, simplerows[1], "%d" % len(u.server_usages)) setattr(u, simplerows[2], "%.2f" % u.total_memory_mb_usage) setattr(u, simplerows[3], "%.2f" % u.total_vcpus_usage) setattr(u, simplerows[4], "%.2f" % u.total_local_gb_usage) usage_list = cs.usage.list(start, end, detailed=True) print("Usage from %s to %s:" % (start.strftime(dateformat), end.strftime(dateformat))) for usage in usage_list: simplify_usage(usage) utils.print_list(usage_list, rows) @utils.arg('--start', metavar='', help='Usage range start date ex 2012-01-20 (default: 4 weeks ago)', default=None) @utils.arg('--end', metavar='', help='Usage range end date, ex 2012-01-20 (default: tomorrow) ', default=None) @utils.arg('--tenant', metavar='', default=None, help='UUID or name of tenant to get usage for.') def do_usage(cs, args): """Show usage data for a single tenant.""" dateformat = "%Y-%m-%d" rows = ["Servers", "RAM MB-Hours", "CPU Hours", "Disk GB-Hours"] now = timeutils.utcnow() if args.start: start = datetime.datetime.strptime(args.start, dateformat) else: start = now - datetime.timedelta(weeks=4) if args.end: end = datetime.datetime.strptime(args.end, dateformat) else: end = now + datetime.timedelta(days=1) def simplify_usage(u): simplerows = map(lambda x: x.lower().replace(" ", "_"), rows) setattr(u, simplerows[0], "%d" % len(u.server_usages)) setattr(u, simplerows[1], "%.2f" % u.total_memory_mb_usage) setattr(u, simplerows[2], "%.2f" % u.total_vcpus_usage) setattr(u, simplerows[3], "%.2f" % u.total_local_gb_usage) if args.tenant: usage = cs.usage.get(args.tenant, start, end) else: usage = cs.usage.get(cs.client.tenant_id, start, end) print("Usage from %s to %s:" % (start.strftime(dateformat), end.strftime(dateformat))) if getattr(usage, 'total_vcpus_usage', None): simplify_usage(usage) utils.print_list([usage], rows) else: print('None') @utils.arg('pk_filename', metavar='', nargs='?', default='pk.pem', help='Filename for the private key [Default: pk.pem]') @utils.arg('cert_filename', metavar='', nargs='?', default='cert.pem', help='Filename for the X.509 certificate [Default: cert.pem]') def do_x509_create_cert(cs, args): """Create x509 cert for a user in tenant.""" if os.path.exists(args.pk_filename): raise exceptions.CommandError("Unable to write privatekey - %s exists." % args.pk_filename) if os.path.exists(args.cert_filename): raise exceptions.CommandError("Unable to write x509 cert - %s exists." % args.cert_filename) certs = cs.certs.create() try: old_umask = os.umask(0o377) with open(args.pk_filename, 'w') as private_key: private_key.write(certs.private_key) print("Wrote private key to %s" % args.pk_filename) finally: os.umask(old_umask) with open(args.cert_filename, 'w') as cert: cert.write(certs.data) print("Wrote x509 certificate to %s" % args.cert_filename) @utils.arg('filename', metavar='', nargs='?', default='cacert.pem', help='Filename to write the x509 root cert.') def do_x509_get_root_cert(cs, args): """Fetch the x509 root cert.""" if os.path.exists(args.filename): raise exceptions.CommandError("Unable to write x509 root cert - \ %s exists." % args.filename) with open(args.filename, 'w') as cert: cacert = cs.certs.get() cert.write(cacert.data) print("Wrote x509 root cert to %s" % args.filename) @utils.arg('--hypervisor', metavar='', default=None, help='type of hypervisor.') def do_agent_list(cs, args): """List all builds.""" result = cs.agents.list(args.hypervisor) columns = ["Agent_id", "Hypervisor", "OS", "Architecture", "Version", 'Md5hash', 'Url'] utils.print_list(result, columns) @utils.arg('os', metavar='', help='type of os.') @utils.arg('architecture', metavar='', help='type of architecture') @utils.arg('version', metavar='', help='version') @utils.arg('url', metavar='', help='url') @utils.arg('md5hash', metavar='', help='md5 hash') @utils.arg('hypervisor', metavar='', default='xen', help='type of hypervisor.') def do_agent_create(cs, args): """Create new agent build.""" result = cs.agents.create(args.os, args.architecture, args.version, args.url, args.md5hash, args.hypervisor) utils.print_dict(result._info.copy()) @utils.arg('id', metavar='', help='id of the agent-build') def do_agent_delete(cs, args): """Delete existing agent build.""" cs.agents.delete(args.id) @utils.arg('id', metavar='', help='id of the agent-build') @utils.arg('version', metavar='', help='version') @utils.arg('url', metavar='', help='url') @utils.arg('md5hash', metavar='', help='md5hash') def do_agent_modify(cs, args): """Modify existing agent build.""" result = cs.agents.update(args.id, args.version, args.url, args.md5hash) utils.print_dict(result._info) def _find_aggregate(cs, aggregate): """Get a aggregate by name or ID.""" return utils.find_resource(cs.aggregates, aggregate) def do_aggregate_list(cs, args): """Print a list of all aggregates.""" aggregates = cs.aggregates.list() columns = ['Id', 'Name', 'Availability Zone'] utils.print_list(aggregates, columns) @utils.arg('name', metavar='', help='Name of aggregate.') @utils.arg('availability_zone', metavar='', default=None, nargs='?', help='The availability zone of the aggregate (optional).') def do_aggregate_create(cs, args): """Create a new aggregate with the specified details.""" aggregate = cs.aggregates.create(args.name, args.availability_zone) _print_aggregate_details(aggregate) @utils.arg('aggregate', metavar='', help='Name or ID of aggregate to delete.') def do_aggregate_delete(cs, args): """Delete the aggregate.""" aggregate = _find_aggregate(cs, args.aggregate) cs.aggregates.delete(aggregate) print("Aggregate %s has been successfully deleted." % aggregate.id) @utils.arg('aggregate', metavar='', help='Name or ID of aggregate to update.') @utils.arg('name', metavar='', help='Name of aggregate.') @utils.arg('availability_zone', metavar='', nargs='?', default=None, help='The availability zone of the aggregate.') def do_aggregate_update(cs, args): """Update the aggregate's name and optionally availability zone.""" aggregate = _find_aggregate(cs, args.aggregate) updates = {"name": args.name} if args.availability_zone: updates["availability_zone"] = args.availability_zone aggregate = cs.aggregates.update(aggregate.id, updates) print("Aggregate %s has been successfully updated." % aggregate.id) _print_aggregate_details(aggregate) @utils.arg('aggregate', metavar='', help='Name or ID of aggregate to update.') @utils.arg('metadata', metavar='', nargs='+', action='append', default=[], help='Metadata to add/update to aggregate') def do_aggregate_set_metadata(cs, args): """Update the metadata associated with the aggregate.""" aggregate = _find_aggregate(cs, args.aggregate) metadata = _extract_metadata(args) aggregate = cs.aggregates.set_metadata(aggregate.id, metadata) print("Metadata has been successfully updated for aggregate %s." % aggregate.id) _print_aggregate_details(aggregate) @utils.arg('aggregate', metavar='', help='Name or ID of aggregate.') @utils.arg('host', metavar='', help='The host to add to the aggregate.') def do_aggregate_add_host(cs, args): """Add the host to the specified aggregate.""" aggregate = _find_aggregate(cs, args.aggregate) aggregate = cs.aggregates.add_host(aggregate.id, args.host) print("Host %s has been successfully added for aggregate %s " % (args.host, aggregate.id)) _print_aggregate_details(aggregate) @utils.arg('aggregate', metavar='', help='Name or ID of aggregate.') @utils.arg('host', metavar='', help='The host to remove from the aggregate.') def do_aggregate_remove_host(cs, args): """Remove the specified host from the specified aggregate.""" aggregate = _find_aggregate(cs, args.aggregate) aggregate = cs.aggregates.remove_host(aggregate.id, args.host) print("Host %s has been successfully removed from aggregate %s " % (args.host, aggregate.id)) _print_aggregate_details(aggregate) @utils.arg('aggregate', metavar='', help='Name or ID of aggregate.') def do_aggregate_details(cs, args): """Show details of the specified aggregate.""" aggregate = _find_aggregate(cs, args.aggregate) _print_aggregate_details(aggregate) def _print_aggregate_details(aggregate): columns = ['Id', 'Name', 'Availability Zone', 'Hosts', 'Metadata'] def parser_metadata(fields): return utils.pretty_choice_dict(getattr(fields, 'metadata', {}) or {}) def parser_hosts(fields): return utils.pretty_choice_list(getattr(fields, 'hosts', [])) formatters = { 'Metadata': parser_metadata, 'Hosts': parser_hosts, } utils.print_list([aggregate], columns, formatters=formatters) @utils.arg('server', metavar='', help='Name or ID of server.') @utils.arg('host', metavar='', default=None, nargs='?', help='destination host name.') @utils.arg('--block-migrate', action='store_true', dest='block_migrate', default=False, help='True in case of block_migration.\ (Default=False:live_migration)') @utils.arg('--block_migrate', action='store_true', help=argparse.SUPPRESS) @utils.arg('--disk-over-commit', action='store_true', dest='disk_over_commit', default=False, help='Allow overcommit.(Default=False)') @utils.arg('--disk_over_commit', action='store_true', help=argparse.SUPPRESS) def do_live_migration(cs, args): """Migrate running server to a new machine.""" _find_server(cs, args.server).live_migrate(args.host, args.block_migrate, args.disk_over_commit) @utils.arg('server', metavar='', help='Name or ID of server.') @utils.arg('--active', action='store_const', dest='state', default='error', const='active', help='Request the server be reset to "active" state instead ' 'of "error" state (the default).') def do_reset_state(cs, args): """Reset the state of a server.""" _find_server(cs, args.server).reset_state(args.state) @utils.arg('server', metavar='', help='Name or ID of server.') def do_reset_network(cs, args): """Reset network of a server.""" _find_server(cs, args.server).reset_network() @utils.arg('--host', metavar='', default=None, help='Name of host.') @utils.arg('--binary', metavar='', default=None, help='Service binary.') def do_service_list(cs, args): """Show a list of all running services. Filter by host & binary.""" result = cs.services.list(host=args.host, binary=args.binary) columns = ["Binary", "Host", "Zone", "Status", "State", "Updated_at"] # NOTE(sulo): we check if the response has disabled_reason # so as not to add the column when the extended ext is not enabled. if hasattr(result[0], 'disabled_reason'): columns.append("Disabled Reason") utils.print_list(result, columns) @utils.arg('host', metavar='', help='Name of host.') @utils.arg('binary', metavar='', help='Service binary.') def do_service_enable(cs, args): """Enable the service.""" result = cs.services.enable(args.host, args.binary) utils.print_list([result], ['Host', 'Binary', 'Status']) @utils.arg('host', metavar='', help='Name of host.') @utils.arg('binary', metavar='', help='Service binary.') @utils.arg('--reason', metavar='', help='Reason for disabling service.') def do_service_disable(cs, args): """Disable the service.""" if args.reason: result = cs.services.disable_log_reason(args.host, args.binary, args.reason) utils.print_list([result], ['Host', 'Binary', 'Status', 'Disabled Reason']) else: result = cs.services.disable(args.host, args.binary) utils.print_list([result], ['Host', 'Binary', 'Status']) @utils.arg('fixed_ip', metavar='', help='Fixed IP Address.') def do_fixed_ip_get(cs, args): """Retrieve info on a fixed ip.""" result = cs.fixed_ips.get(args.fixed_ip) utils.print_list([result], ['address', 'cidr', 'hostname', 'host']) @utils.arg('fixed_ip', metavar='', help='Fixed IP Address.') def do_fixed_ip_reserve(cs, args): """Reserve a fixed IP.""" cs.fixed_ips.reserve(args.fixed_ip) @utils.arg('fixed_ip', metavar='', help='Fixed IP Address.') def do_fixed_ip_unreserve(cs, args): """Unreserve a fixed IP.""" cs.fixed_ips.unreserve(args.fixed_ip) @utils.arg('host', metavar='', help='Name of host.') def do_host_describe(cs, args): """Describe a specific host.""" result = cs.hosts.get(args.host) columns = ["HOST", "PROJECT", "cpu", "memory_mb", "disk_gb"] utils.print_list(result, columns) @utils.arg('--zone', metavar='', default=None, help='Filters the list, returning only those ' 'hosts in the availability zone .') def do_host_list(cs, args): """List all hosts by service.""" columns = ["host_name", "service", "zone"] result = cs.hosts.list(args.zone) utils.print_list(result, columns) @utils.arg('host', metavar='', help='Name of host.') @utils.arg('--status', metavar='', default=None, dest='status', help='Either enable or disable a host.') @utils.arg('--maintenance', metavar='', default=None, dest='maintenance', help='Either put or resume host to/from maintenance.') def do_host_update(cs, args): """Update host settings.""" updates = {} columns = ["HOST"] if args.status: updates['status'] = args.status columns.append("status") if args.maintenance: updates['maintenance_mode'] = args.maintenance columns.append("maintenance_mode") result = cs.hosts.update(args.host, updates) utils.print_list([result], columns) @utils.arg('host', metavar='', help='Name of host.') @utils.arg('--action', metavar='', dest='action', choices=['startup', 'shutdown', 'reboot'], help='A power action: startup, reboot, or shutdown.') def do_host_action(cs, args): """Perform a power action on a host.""" result = cs.hosts.host_action(args.host, args.action) utils.print_list([result], ['HOST', 'power_action']) def _find_hypervisor(cs, hypervisor): """Get a hypervisor by name or ID.""" return utils.find_resource(cs.hypervisors, hypervisor) @utils.arg('--matching', metavar='', default=None, help='List hypervisors matching the given .') def do_hypervisor_list(cs, args): """List hypervisors.""" columns = ['ID', 'Hypervisor hostname'] if args.matching: utils.print_list(cs.hypervisors.search(args.matching), columns) else: # Since we're not outputting detail data, choose # detailed=False for server-side efficiency utils.print_list(cs.hypervisors.list(False), columns) @utils.arg('hostname', metavar='', help='The hypervisor hostname (or pattern) to search for.') def do_hypervisor_servers(cs, args): """List servers belonging to specific hypervisors.""" # Get a list of hypervisors first hypers = cs.hypervisors.search(args.hostname) class InstanceOnHyper(object): def __init__(self, **kwargs): self.__dict__.update(kwargs) # Massage the result into a list to be displayed servers = [] for hyper in hypers: # Get a list of servers for each hypervisor hyper_host = hyper.hypervisor_hostname hyper_id = hyper.id hyper_servers = cs.hypervisors.servers(hyper_id) if hasattr(hyper_servers, 'servers'): print(hyper_servers.servers) servers.extend([InstanceOnHyper(id=serv['id'], name=serv['name'], hypervisor_hostname=hyper_host, hypervisor_id=hyper_id) for serv in hyper_servers.servers]) # Output the data utils.print_list(servers, ['ID', 'Name', 'Hypervisor ID', 'Hypervisor Hostname']) @utils.arg('hypervisor', metavar='', help='Name or ID of the hypervisor to show the details of.') def do_hypervisor_show(cs, args): """Display the details of the specified hypervisor.""" hyper = _find_hypervisor(cs, args.hypervisor) # Build up the dict info = hyper._info.copy() info['service_id'] = info['service']['id'] info['service_host'] = info['service']['host'] del info['service'] utils.print_dict(info) @utils.arg('hypervisor', metavar='', help='Name or ID of the hypervisor to show the uptime of.') def do_hypervisor_uptime(cs, args): """Display the uptime of the specified hypervisor.""" hyper = _find_hypervisor(cs, args.hypervisor) hyper = cs.hypervisors.uptime(hyper) # Output the uptime information utils.print_dict(hyper._info.copy()) def do_hypervisor_stats(cs, args): """Get hypervisor statistics over all compute nodes.""" stats = cs.hypervisors.statistics() utils.print_dict(stats._info.copy()) def ensure_service_catalog_present(cs): if not hasattr(cs.client, 'service_catalog'): # Turn off token caching and re-auth cs.client.unauthenticate() cs.client.use_token_cache(False) cs.client.authenticate() def do_endpoints(cs, _args): """Discover endpoints that get returned from the authenticate services.""" ensure_service_catalog_present(cs) catalog = cs.client.service_catalog.catalog for e in catalog['access']['serviceCatalog']: utils.print_dict(e['endpoints'][0], e['name']) @utils.arg('--wrap', dest='wrap', metavar='', default=64, help='wrap PKI tokens to a specified length, or 0 to disable') def do_credentials(cs, _args): """Show user credentials returned from auth.""" ensure_service_catalog_present(cs) catalog = cs.client.service_catalog.catalog utils.print_dict(catalog['access']['user'], "User Credentials", wrap=int(_args.wrap)) utils.print_dict(catalog['access']['token'], "Token", wrap=int(_args.wrap)) @utils.arg('server', metavar='', help='Name or ID of server.') @utils.arg('--port', dest='port', action='store', type=int, default=22, help='Optional flag to indicate which port to use for ssh. ' '(Default=22)') @utils.arg('--private', dest='private', action='store_true', default=False, help='Optional flag to indicate whether to use private address ' 'attached to a server. (Default=False)') @utils.arg('--ipv6', dest='ipv6', action='store_true', default=False, help='Optional flag to indicate whether to use an IPv6 address ' 'attached to a server. (Defaults to IPv4 address)') @utils.arg('--login', metavar='', help='Login to use.', default="root") @utils.arg('-i', '--identity', dest='identity', help='Private key file, same as the -i option to the ssh command.', default='') @utils.arg('--extra-opts', dest='extra', help='Extra options to pass to ssh. see: man ssh', default='') def do_ssh(cs, args): """SSH into a server.""" if '@' in args.server: user, server = args.server.split('@', 1) args.login = user args.server = server addresses = _find_server(cs, args.server).addresses address_type = "private" if args.private else "public" version = 6 if args.ipv6 else 4 if address_type not in addresses: print("ERROR: No %s addresses found for '%s'." % (address_type, args.server)) return ip_address = None for address in addresses[address_type]: if address['version'] == version: ip_address = address['addr'] break identity = '-i %s' % args.identity if len(args.identity) else '' if ip_address: os.system("ssh -%d -p%d %s %s@%s %s" % (version, args.port, identity, args.login, ip_address, args.extra)) else: pretty_version = "IPv%d" % version print("ERROR: No %s %s address found." % (address_type, pretty_version)) return _quota_resources = ['instances', 'cores', 'ram', 'fixed_ips', 'metadata_items', 'key_pairs'] def _quota_show(quotas): quota_dict = {} for resource in _quota_resources: try: quota_dict[resource] = getattr(quotas, resource) except AttributeError: pass utils.print_dict(quota_dict) def _quota_usage(quotas): class QuotaObj(object): def __init__(self, resource, quota_dict): setattr(self, 'resource', resource) for (k, v) in six.iteritems(quota_dict): setattr(self, k, v) quota_list = [] for resource in _quota_resources: try: quota_list.append(QuotaObj(resource, getattr(quotas, resource))) except AttributeError: pass utils.print_list(quota_list, ['resource', 'in use', 'limit'], sortby_index=0) def _quota_update(manager, identifier, args): updates = {} for resource in _quota_resources: val = getattr(args, resource, None) if val is not None: updates[resource] = val if updates: # default value of force is None to make sure this client # will be compatibile with old nova server manager.update(identifier, **updates) @utils.arg('--tenant', metavar='', default=None, help='ID of tenant to list the quotas for.') def do_quota_show(cs, args): """List the quotas for a tenant.""" if not args.tenant: _quota_show(cs.quotas.get(cs.client.tenant_id)) else: _quota_show(cs.quotas.get(args.tenant)) @utils.arg('--tenant', metavar='', default=None, help='ID of tenant to list the quotas for.') @utils.arg('--user', metavar='', default=None, help='ID of user to list the quotas for.') def do_quota_usage(cs, args): """List the quotas for a tenant.""" tenant = args.tenant or cs.client.tenant_id _quota_usage(cs.quotas.get(tenant, user_id=args.user, detail=True)) @utils.arg('--tenant', metavar='', default=None, help='ID of tenant to list the default quotas for.') def do_quota_defaults(cs, args): """List the default quotas for a tenant.""" if not args.tenant: _quota_show(cs.quotas.defaults(cs.client.tenant_id)) else: _quota_show(cs.quotas.defaults(args.tenant)) @utils.arg('tenant', metavar='', help='ID of tenant to set the quotas for.') @utils.arg('--instances', metavar='', type=int, default=None, help='New value for the "instances" quota.') @utils.arg('--cores', metavar='', type=int, default=None, help='New value for the "cores" quota.') @utils.arg('--ram', metavar='', type=int, default=None, help='New value for the "ram" quota.') @utils.arg('--fixed-ips', metavar='', type=int, default=None, help='New value for the "fixed-ips" quota.') @utils.arg('--metadata-items', metavar='', type=int, default=None, help='New value for the "metadata-items" quota.') @utils.arg('--metadata_items', type=int, help=argparse.SUPPRESS) @utils.arg('--key-pairs', metavar='', type=int, default=None, help='New value for the "key-pairs" quota.') @utils.arg('--force', dest='force', action="store_true", default=None, help='Whether force update the quota even if the already used' ' and reserved exceeds the new quota') def do_quota_update(cs, args): """Update the quotas for a tenant.""" _quota_update(cs.quotas, args.tenant, args) @utils.arg('--tenant', metavar='', help='ID of tenant to delete quota for.') def do_quota_delete(cs, args): """Delete quota for a tenant so their quota will revert back to default.""" cs.quotas.delete(args.tenant) @utils.arg('class_name', metavar='', help='Name of quota class to list the quotas for.') def do_quota_class_show(cs, args): """List the quotas for a quota class.""" _quota_show(cs.quota_classes.get(args.class_name)) @utils.arg('class_name', metavar='', help='Name of quota class to set the quotas for.') @utils.arg('--instances', metavar='', type=int, default=None, help='New value for the "instances" quota.') @utils.arg('--cores', metavar='', type=int, default=None, help='New value for the "cores" quota.') @utils.arg('--ram', metavar='', type=int, default=None, help='New value for the "ram" quota.') @utils.arg('--metadata-items', metavar='', type=int, default=None, help='New value for the "metadata-items" quota.') @utils.arg('--metadata_items', type=int, help=argparse.SUPPRESS) @utils.arg('--key-pairs', metavar='', type=int, default=None, help='New value for the "key-pairs" quota.') def do_quota_class_update(cs, args): """Update the quotas for a quota class.""" _quota_update(cs.quota_classes, args.class_name, args) @utils.arg('server', metavar='', help='Name or ID of server.') @utils.arg('host', metavar='', help='Name or ID of target host.') @utils.arg('--password', dest='password', metavar='', default=None, help="Set the provided password on the evacuated server. Not applicable " "with on-shared-storage flag") @utils.arg('--on-shared-storage', dest='on_shared_storage', action="store_true", default=False, help='Specifies whether server files are located on shared storage') def do_evacuate(cs, args): """Evacuate server from failed host to specified one.""" server = _find_server(cs, args.server) res = server.evacuate(args.host, args.on_shared_storage, args.password)[1] if type(res) is dict: utils.print_dict(res) def _print_interfaces(interfaces): columns = ['Port State', 'Port ID', 'Net ID', 'IP addresses', 'MAC Addr'] class FormattedInterface(object): def __init__(self, interface): for col in columns: key = col.lower().replace(" ", "_") if hasattr(interface, key): setattr(self, key, getattr(interface, key)) self.ip_addresses = ",".join([fip['ip_address'] for fip in interface.fixed_ips]) utils.print_list([FormattedInterface(i) for i in interfaces], columns) @utils.arg('server', metavar='', help='Name or ID of server.') def do_interface_list(cs, args): """List interfaces attached to a server.""" server = _find_server(cs, args.server) res = server.interface_list() if type(res) is list: _print_interfaces(res) @utils.arg('server', metavar='', help='Name or ID of server.') @utils.arg('--port-id', metavar='', help='Port ID.', dest="port_id") @utils.arg('--net-id', metavar='', help='Network ID', default=None, dest="net_id") @utils.arg('--fixed-ip', metavar='', help='Requested fixed IP.', default=None, dest="fixed_ip") def do_interface_attach(cs, args): """Attach a network interface to a server.""" server = _find_server(cs, args.server) res = server.interface_attach(args.port_id, args.net_id, args.fixed_ip) if type(res) is dict: utils.print_dict(res) @utils.arg('server', metavar='', help='Name or ID of server.') @utils.arg('port_id', metavar='', help='Port ID.') def do_interface_detach(cs, args): """Detach a network interface from a server.""" server = _find_server(cs, args.server) res = server.interface_detach(args.port_id) if type(res) is dict: utils.print_dict(res) def _treeizeAvailabilityZone(zone): """Build a tree view for availability zones.""" AvailabilityZone = availability_zones.AvailabilityZone az = AvailabilityZone(zone.manager, copy.deepcopy(zone._info), zone._loaded) result = [] # Zone tree view item az.zone_name = zone.zone_name az.zone_state = ('available' if zone.zone_state['available'] else 'not available') az._info['zone_name'] = az.zone_name az._info['zone_state'] = az.zone_state result.append(az) if zone.hosts is not None: zone_hosts = sorted(zone.hosts.items(), key=lambda x: x[0]) for (host, services) in zone_hosts: # Host tree view item az = AvailabilityZone(zone.manager, copy.deepcopy(zone._info), zone._loaded) az.zone_name = '|- %s' % host az.zone_state = '' az._info['zone_name'] = az.zone_name az._info['zone_state'] = az.zone_state result.append(az) for (svc, state) in services.items(): # Service tree view item az = AvailabilityZone(zone.manager, copy.deepcopy(zone._info), zone._loaded) az.zone_name = '| |- %s' % svc az.zone_state = '%s %s %s' % ( 'enabled' if state['active'] else 'disabled', ':-)' if state['available'] else 'XXX', state['updated_at']) az._info['zone_name'] = az.zone_name az._info['zone_state'] = az.zone_state result.append(az) return result def do_availability_zone_list(cs, _args): """List all the availability zones.""" try: availability_zones = cs.availability_zones.list() except exceptions.Forbidden as e: # policy doesn't allow probably try: availability_zones = cs.availability_zones.list(detailed=False) except Exception: raise e result = [] for zone in availability_zones: result += _treeizeAvailabilityZone(zone) _translate_availability_zone_keys(result) utils.print_list(result, ['Name', 'Status'], sortby_index=None) python-novaclient-2.17.0/novaclient/v3/certs.py0000664000175400017540000000152212306432770022610 0ustar jenkinsjenkins00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 OpenStack Foundation # 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. """ Certificate interface. """ from novaclient.v1_1 import certs class Certificate(certs.Certificate): pass class CertificateManager(certs.CertificateManager): pass python-novaclient-2.17.0/novaclient/v3/usage.py0000664000175400017540000000135612306432770022601 0ustar jenkinsjenkins00000000000000# Copyright 2013 IBM Corp. # # 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. """ Usage interface. """ from novaclient.v1_1 import usage class Usage(usage.Usage): pass class UsageManager(usage.UsageManager): pass python-novaclient-2.17.0/novaclient/v3/volumes.py0000664000175400017540000000460312306432770023165 0ustar jenkinsjenkins00000000000000# Copyright 2013 IBM Corp. # # 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. """ Volume interface """ from novaclient import base class VolumeManager(base.Manager): """ Manage :class:`Volume` resources. """ def attach_server_volume(self, server, volume_id, device): """ Attach a volume identified by the volume ID to the given server ID :param server: The server (or it's ID) :param volume_id: The ID of the volume to attach. :param device: The device name :rtype: :class:`Volume` """ body = {'volume_id': volume_id, 'device': device} return self._action('attach', server, body) def update_server_volume(self, server, old_volume_id, new_volume_id): """ Update the volume identified by the attachment ID, that is attached to the given server ID :param server_id: The server (or it's ID) :param old_volume_id: The ID of the attachment :param new_volume_id: The ID of the new volume to attach :rtype: :class:`Volume` """ body = {'new_volume_id': new_volume_id, 'old_volume_id': old_volume_id} return self._action('swap_volume_attachment', server, body) def delete_server_volume(self, server, volume_id): """ Detach a volume identified by the attachment ID from the given server :param server_id: The ID of the server :param volume_id: The ID of the attachment """ return self._action('detach', server, {'volume_id': volume_id}) def _action(self, action, server, info=None, **kwargs): """ Perform a server "action" -- reboot/rebuild/resize/etc. """ body = {action: info} self.run_hooks('modify_body_for_action', body, **kwargs) url = '/servers/%s/action' % base.getid(server) return self.api.client.post(url, body=body) python-novaclient-2.17.0/novaclient/v3/agents.py0000664000175400017540000000175212306432770022756 0ustar jenkinsjenkins00000000000000# Copyright 2012 IBM Corp. # 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. """ agent interface """ from novaclient.v1_1 import agents class Agent(agents.Agent): pass class AgentsManager(agents.AgentsManager): resource_class = Agent def _build_update_body(self, version, url, md5hash): return {'agent': { 'version': version, 'url': url, 'md5hash': md5hash}} python-novaclient-2.17.0/novaclient/v3/keypairs.py0000664000175400017540000000146412306432770023324 0ustar jenkinsjenkins00000000000000# Copyright 2013 IBM Corp. # # 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. """ Keypair interface """ from novaclient.v1_1 import keypairs class Keypair(keypairs.Keypair): pass class KeypairManager(keypairs.KeypairManager): resource_class = Keypair keypair_prefix = "keypairs" python-novaclient-2.17.0/novaclient/v3/quota_classes.py0000664000175400017540000000144612306432770024343 0ustar jenkinsjenkins00000000000000# Copyright IBM Corp. 2013 # # 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 novaclient.v1_1 import quota_classes class QuotaClassSet(quota_classes.QuotaClassSet): pass class QuotaClassSetManager(quota_classes.QuotaClassSetManager): resource_class = QuotaClassSet python-novaclient-2.17.0/novaclient/v3/client.py0000664000175400017540000001256312306432770022755 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # Copyright 2013 IBM Corp. # # 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 novaclient import client from novaclient.v3 import agents from novaclient.v3 import aggregates from novaclient.v3 import availability_zones from novaclient.v3 import certs from novaclient.v3 import flavor_access from novaclient.v3 import flavors from novaclient.v3 import hosts from novaclient.v3 import hypervisors from novaclient.v3 import images from novaclient.v3 import keypairs from novaclient.v3 import quota_classes from novaclient.v3 import quotas from novaclient.v3 import servers from novaclient.v3 import services from novaclient.v3 import usage from novaclient.v3 import volumes class Client(object): """ Top-level object to access the OpenStack Compute API. Create an instance with your creds:: >>> client = Client(USERNAME, PASSWORD, PROJECT_ID, AUTH_URL) Then call methods on its managers:: >>> client.servers.list() ... >>> client.flavors.list() ... """ # FIXME(jesse): project_id isn't required to authenticate def __init__(self, username, password, project_id, auth_url=None, insecure=False, timeout=None, proxy_tenant_id=None, proxy_token=None, region_name=None, endpoint_type='publicURL', extensions=None, service_type='computev3', service_name=None, volume_service_name=None, timings=False, bypass_url=None, os_cache=False, no_cache=True, http_log_debug=False, auth_system='keystone', auth_plugin=None, auth_token=None, cacert=None, tenant_id=None): self.projectid = project_id self.tenant_id = tenant_id self.os_cache = os_cache or not no_cache #TODO(bnemec): Add back in v3 extensions self.agents = agents.AgentsManager(self) self.aggregates = aggregates.AggregateManager(self) self.availability_zones = \ availability_zones.AvailabilityZoneManager(self) self.certs = certs.CertificateManager(self) self.hosts = hosts.HostManager(self) self.flavors = flavors.FlavorManager(self) self.flavor_access = flavor_access.FlavorAccessManager(self) self.hypervisors = hypervisors.HypervisorManager(self) self.images = images.ImageManager(self) self.keypairs = keypairs.KeypairManager(self) self.quotas = quotas.QuotaSetManager(self) self.quota_classes = quota_classes.QuotaClassSetManager(self) self.servers = servers.ServerManager(self) self.services = services.ServiceManager(self) self.usage = usage.UsageManager(self) self.volumes = volumes.VolumeManager(self) # Add in any extensions... if extensions: for extension in extensions: if extension.manager_class: setattr(self, extension.name, extension.manager_class(self)) self.client = client.HTTPClient(username, password, projectid=project_id, tenant_id=tenant_id, auth_url=auth_url, insecure=insecure, timeout=timeout, auth_system=auth_system, auth_plugin=auth_plugin, auth_token=auth_token, proxy_token=proxy_token, proxy_tenant_id=proxy_tenant_id, region_name=region_name, endpoint_type=endpoint_type, service_type=service_type, service_name=service_name, volume_service_name=volume_service_name, timings=timings, bypass_url=bypass_url, os_cache=os_cache, http_log_debug=http_log_debug, cacert=cacert) def set_management_url(self, url): self.client.set_management_url(url) def get_timings(self): return self.client.get_timings() def reset_timings(self): self.client.reset_timings() def authenticate(self): """ Authenticate against the server. Normally this is called automatically when you first access the API, but you can call this method to force authentication right now. Returns on success; raises :exc:`exceptions.Unauthorized` if the credentials are wrong. """ self.client.authenticate() python-novaclient-2.17.0/novaclient/v3/__init__.py0000664000175400017540000000126312306432770023231 0ustar jenkinsjenkins00000000000000# Copyright (c) 2012 OpenStack Foundation # # 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 novaclient.v3.client import Client # noqa python-novaclient-2.17.0/novaclient/v3/hosts.py0000664000175400017540000000231612306432770022632 0ustar jenkinsjenkins00000000000000# Copyright 2013 OpenStack Foundation # # 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. """ V3 API versions of the Hosts interface. Inherits from the 1.1 code because a lot of the functionality is shared. """ from novaclient.v1_1 import hosts Host = hosts.Host class HostManager(hosts.HostManager): def update(self, host, values): """Update status or maintenance mode for the host.""" body = dict(host=values) return self._update("/os-hosts/%s" % host, body, response_key='host') def host_action(self, host, action): """Perform an action on a host.""" url = '/os-hosts/{0}/{1}'.format(host, action) return self._get(url, response_key='host') python-novaclient-2.17.0/novaclient/v3/services.py0000664000175400017540000000205712306432770023317 0ustar jenkinsjenkins00000000000000# Copyright 2013 IBM Corp. # # 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 interface """ from novaclient.v1_1 import services class Service(services.Service): pass class ServiceManager(services.ServiceManager): resource_class = Service def _update_body(self, host, binary, disabled_reason=None): body = {"service": {"host": host, "binary": binary}} if disabled_reason is not None: body["service"]["disabled_reason"] = disabled_reason return body python-novaclient-2.17.0/novaclient/auth_plugin.py0000664000175400017540000001101612306432770023456 0ustar jenkinsjenkins00000000000000# Copyright 2013 OpenStack Foundation # Copyright 2013 Spanish National Research Council. # 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 pkg_resources import six from novaclient import exceptions from novaclient.openstack.common.gettextutils import _ from novaclient import utils logger = logging.getLogger(__name__) _discovered_plugins = {} def discover_auth_systems(): """Discover the available auth-systems. This won't take into account the old style auth-systems. """ ep_name = 'openstack.client.auth_plugin' for ep in pkg_resources.iter_entry_points(ep_name): try: auth_plugin = ep.load() except (ImportError, pkg_resources.UnknownExtra, AttributeError) as e: logger.debug(_("ERROR: Cannot load auth plugin %s") % ep.name) logger.debug(e, exc_info=1) else: _discovered_plugins[ep.name] = auth_plugin def load_auth_system_opts(parser): """Load options needed by the available auth-systems into a parser. This function will try to populate the parser with options from the available plugins. """ for name, auth_plugin in six.iteritems(_discovered_plugins): add_opts_fn = getattr(auth_plugin, "add_opts", None) if add_opts_fn: group = parser.add_argument_group("Auth-system '%s' options" % name) add_opts_fn(group) def load_plugin(auth_system): if auth_system in _discovered_plugins: return _discovered_plugins[auth_system]() # NOTE(aloga): If we arrive here, the plugin will be an old-style one, # so we have to create a fake AuthPlugin for it. return DeprecatedAuthPlugin(auth_system) class BaseAuthPlugin(object): """Base class for authentication plugins. An authentication plugin needs to override at least the authenticate method to be a valid plugin. """ def __init__(self): self.opts = {} def get_auth_url(self): """Return the auth url for the plugin (if any).""" return None @staticmethod def add_opts(parser): """Populate and return the parser with the options for this plugin. If the plugin does not need any options, it should return the same parser untouched. """ return parser def parse_opts(self, args): """Parse the actual auth-system options if any. This method is expected to populate the attribute self.opts with a dict containing the options and values needed to make authentication. If the dict is empty, the client should assume that it needs the same options as the 'keystone' auth system (i.e. os_username and os_password). Returns the self.opts dict. """ return self.opts def authenticate(self, cls, auth_url): """Authenticate using plugin defined method.""" raise exceptions.AuthSystemNotFound(self.auth_system) class DeprecatedAuthPlugin(object): """Class to mimic the AuthPlugin class for deprecated auth systems. Old auth systems only define two entry points: openstack.client.auth_url and openstack.client.authenticate. This class will load those entry points into a class similar to a valid AuthPlugin. """ def __init__(self, auth_system): self.auth_system = auth_system def authenticate(cls, auth_url): raise exceptions.AuthSystemNotFound(self.auth_system) self.opts = {} self.get_auth_url = lambda: None self.authenticate = authenticate self._load_endpoints() def _load_endpoints(self): ep_name = 'openstack.client.auth_url' fn = utils._load_entry_point(ep_name, name=self.auth_system) if fn: self.get_auth_url = fn ep_name = 'openstack.client.authenticate' fn = utils._load_entry_point(ep_name, name=self.auth_system) if fn: self.authenticate = fn def parse_opts(self, args): return self.opts python-novaclient-2.17.0/novaclient/v1_1/0000775000175400017540000000000012306433034021326 5ustar jenkinsjenkins00000000000000python-novaclient-2.17.0/novaclient/v1_1/floating_ips_bulk.py0000664000175400017540000000354512306432770025410 0ustar jenkinsjenkins00000000000000# Copyright 2012 IBM Corp. # 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. """ Bulk Floating IPs interface """ from novaclient import base class FloatingIP(base.Resource): def __repr__(self): return "" % self.address class FloatingIPBulkManager(base.ManagerWithFind): resource_class = FloatingIP def list(self, host=None): """ List all floating IPs """ if host is None: return self._list('/os-floating-ips-bulk', 'floating_ip_info') else: return self._list('/os-floating-ips-bulk/%s' % host, 'floating_ip_info') def create(self, ip_range, pool=None, interface=None): """ Create floating IPs by range """ body = {"floating_ips_bulk_create": {'ip_range': ip_range}} if pool is not None: body['floating_ips_bulk_create']['pool'] = pool if interface is not None: body['floating_ips_bulk_create']['interface'] = interface return self._create('/os-floating-ips-bulk', body, 'floating_ips_bulk_create') def delete(self, ip_range): """ Delete floating IPs by range """ body = {"ip_range": ip_range} return self._update('/os-floating-ips-bulk/delete', body) python-novaclient-2.17.0/novaclient/v1_1/fixed_ips.py0000664000175400017540000000322312306432770023660 0ustar jenkinsjenkins00000000000000# Copyright 2012 IBM Corp. # 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. """ Fixed IPs interface. """ from novaclient import base class FixedIP(base.Resource): def __repr__(self): return "" % self.address class FixedIPsManager(base.Manager): resource_class = FixedIP def get(self, fixed_ip): """ Show information for a Fixed IP :param fixed_ip: Fixed IP address to get info for """ return self._get('/os-fixed-ips/%s' % base.getid(fixed_ip), "fixed_ip") def reserve(self, fixed_ip): """Reserve a Fixed IP :param fixed_ip: Fixed IP address to reserve """ body = {"reserve": None} self.api.client.post('/os-fixed-ips/%s/action' % base.getid(fixed_ip), body=body) def unreserve(self, fixed_ip): """Unreserve a Fixed IP :param fixed_ip: Fixed IP address to unreserve """ body = {"unreserve": None} self.api.client.post('/os-fixed-ips/%s/action' % base.getid(fixed_ip), body=body) python-novaclient-2.17.0/novaclient/v1_1/hypervisors.py0000664000175400017540000000426312306432770024310 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # 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. """ Hypervisors interface (1.1 extension). """ from six.moves.urllib import parse from novaclient import base class Hypervisor(base.Resource): NAME_ATTR = 'hypervisor_hostname' def __repr__(self): return "" % self.id class HypervisorManager(base.ManagerWithFind): resource_class = Hypervisor def list(self, detailed=True): """ Get a list of hypervisors. """ detail = "" if detailed: detail = "/detail" return self._list('/os-hypervisors%s' % detail, 'hypervisors') def search(self, hypervisor_match, servers=False): """ Get a list of matching hypervisors. :param servers: If True, server information is also retrieved. """ target = 'servers' if servers else 'search' url = ('/os-hypervisors/%s/%s' % (parse.quote(hypervisor_match, safe=''), target)) return self._list(url, 'hypervisors') def get(self, hypervisor): """ Get a specific hypervisor. """ return self._get("/os-hypervisors/%s" % base.getid(hypervisor), "hypervisor") def uptime(self, hypervisor): """ Get the uptime for a specific hypervisor. """ return self._get("/os-hypervisors/%s/uptime" % base.getid(hypervisor), "hypervisor") def statistics(self): """ Get hypervisor statistics over all compute nodes. """ return self._get("/os-hypervisors/statistics", "hypervisor_statistics") python-novaclient-2.17.0/novaclient/v1_1/floating_ip_dns.py0000664000175400017540000001117312306432770025050 0ustar jenkinsjenkins00000000000000# Copyright 2011 Andrew Bogott for The Wikimedia Foundation # 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 six.moves.urllib import parse from novaclient import base def _quote_domain(domain): """Special quoting rule for placing domain names on a url line. Domain names tend to have .'s in them. Urllib doesn't quote dots, but Routes tends to choke on them, so we need an extra level of by-hand quoting here. """ return parse.quote(domain.replace('.', '%2E')) class FloatingIPDNSDomain(base.Resource): def delete(self): self.manager.delete(self.domain) def create(self): if self.scope == 'public': self.manager.create_public(self.domain, self.project) else: self.manager.create_private(self.domain, self.availability_zone) def get(self): entries = self.manager.domains() for entry in entries: if entry.get('domain') == self.domain: return entry return None class FloatingIPDNSDomainManager(base.Manager): resource_class = FloatingIPDNSDomain def domains(self): """Return the list of available dns domains.""" return self._list("/os-floating-ip-dns", "domain_entries") def create_private(self, fqdomain, availability_zone): """Add or modify a private DNS domain.""" body = {'domain_entry': {'scope': 'private', 'availability_zone': availability_zone}} return self._update('/os-floating-ip-dns/%s' % _quote_domain(fqdomain), body, 'domain_entry') def create_public(self, fqdomain, project): """Add or modify a public DNS domain.""" body = {'domain_entry': {'scope': 'public', 'project': project}} return self._update('/os-floating-ip-dns/%s' % _quote_domain(fqdomain), body, 'domain_entry') def delete(self, fqdomain): """Delete the specified domain.""" self._delete("/os-floating-ip-dns/%s" % _quote_domain(fqdomain)) class FloatingIPDNSEntry(base.Resource): def delete(self): self.manager.delete(self.name, self.domain) def create(self): self.manager.create(self.domain, self.name, self.ip, self.dns_type) def get(self): return self.manager.get(self.domain, self.name) class FloatingIPDNSEntryManager(base.Manager): resource_class = FloatingIPDNSEntry def get(self, domain, name): """Return a list of entries for the given domain and ip or name.""" return self._get("/os-floating-ip-dns/%s/entries/%s" % (_quote_domain(domain), name), "dns_entry") def get_for_ip(self, domain, ip): """Return a list of entries for the given domain and ip or name.""" qparams = {'ip': ip} params = "?%s" % parse.urlencode(qparams) return self._list("/os-floating-ip-dns/%s/entries%s" % (_quote_domain(domain), params), "dns_entries") def create(self, domain, name, ip, dns_type): """Add a new DNS entry.""" body = {'dns_entry': {'ip': ip, 'dns_type': dns_type}} return self._update("/os-floating-ip-dns/%s/entries/%s" % (_quote_domain(domain), name), body, "dns_entry") def modify_ip(self, domain, name, ip): """Add a new DNS entry.""" body = {'dns_entry': {'ip': ip, 'dns_type': 'A'}} return self._update("/os-floating-ip-dns/%s/entries/%s" % (_quote_domain(domain), name), body, "dns_entry") def delete(self, domain, name): """Delete entry specified by name and domain.""" self._delete("/os-floating-ip-dns/%s/entries/%s" % (_quote_domain(domain), name)) python-novaclient-2.17.0/novaclient/v1_1/volume_snapshots.py0000664000175400017540000000526112306432770025323 0ustar jenkinsjenkins00000000000000# Copyright 2011 Denali Systems, Inc. # 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. """ Volume snapshot interface (1.1 extension). """ from novaclient import base class Snapshot(base.Resource): """ A Snapshot is a point-in-time snapshot of an openstack volume. """ NAME_ATTR = 'display_name' def __repr__(self): return "" % self.id def delete(self): """ Delete this snapshot. """ self.manager.delete(self) class SnapshotManager(base.ManagerWithFind): """ Manage :class:`Snapshot` resources. """ resource_class = Snapshot def create(self, volume_id, force=False, display_name=None, display_description=None): """ Create a snapshot of the given volume. :param volume_id: The ID of the volume to snapshot. :param force: If force is True, create a snapshot even if the volume is attached to an instance. Default is False. :param display_name: Name of the snapshot :param display_description: Description of the snapshot :rtype: :class:`Snapshot` """ body = {'snapshot': {'volume_id': volume_id, 'force': force, 'display_name': display_name, 'display_description': display_description}} return self._create('/snapshots', body, 'snapshot') def get(self, snapshot_id): """ Get a snapshot. :param snapshot_id: The ID of the snapshot to get. :rtype: :class:`Snapshot` """ return self._get("/snapshots/%s" % snapshot_id, "snapshot") def list(self, detailed=True): """ Get a list of all snapshots. :rtype: list of :class:`Snapshot` """ if detailed is True: return self._list("/snapshots/detail", "snapshots") else: return self._list("/snapshots", "snapshots") def delete(self, snapshot): """ Delete a snapshot. :param snapshot: The :class:`Snapshot` to delete. """ self._delete("/snapshots/%s" % base.getid(snapshot)) python-novaclient-2.17.0/novaclient/v1_1/volume_types.py0000664000175400017540000000371212306432770024444 0ustar jenkinsjenkins00000000000000# Copyright (c) 2011 Rackspace US, 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. """ Volume Type interface. """ from novaclient import base class VolumeType(base.Resource): """ A Volume Type is the type of volume to be created """ def __repr__(self): return "" % self.name class VolumeTypeManager(base.ManagerWithFind): """ Manage :class:`VolumeType` resources. """ resource_class = VolumeType def list(self): """ Get a list of all volume types. :rtype: list of :class:`VolumeType`. """ return self._list("/types", "volume_types") def get(self, volume_type): """ Get a specific volume type. :param volume_type: The ID of the :class:`VolumeType` to get. :rtype: :class:`VolumeType` """ return self._get("/types/%s" % base.getid(volume_type), "volume_type") def delete(self, volume_type): """ Delete a specific volume_type. :param volume_type: The ID of the :class:`VolumeType` to get. """ self._delete("/types/%s" % base.getid(volume_type)) def create(self, name): """ Create a volume type. :param name: Descriptive name of the volume type :rtype: :class:`VolumeType` """ body = { "volume_type": { "name": name, } } return self._create("/types", body, "volume_type") python-novaclient-2.17.0/novaclient/v1_1/servers.py0000664000175400017540000012045112306432770023402 0ustar jenkinsjenkins00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 OpenStack Foundation # 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. """ Server interface. """ import base64 import six from six.moves.urllib import parse from novaclient import base from novaclient import crypto from novaclient.openstack.common import strutils from novaclient.v1_1.security_groups import SecurityGroup REBOOT_SOFT, REBOOT_HARD = 'SOFT', 'HARD' class Server(base.Resource): HUMAN_ID = True def __repr__(self): return "" % self.name def delete(self): """ Delete (i.e. shut down and delete the image) this server. """ self.manager.delete(self) def update(self, name=None): """ Update the name for this server. :param name: Update the server's name. """ self.manager.update(self, name=name) def get_console_output(self, length=None): """ Get text console log output from Server. :param length: The number of lines you would like to retrieve (as int) """ return self.manager.get_console_output(self, length) def get_vnc_console(self, console_type): """ Get vnc console for a Server. :param console_type: Type of console ('novnc' or 'xvpvnc') """ return self.manager.get_vnc_console(self, console_type) def get_spice_console(self, console_type): """ Get spice console for a Server. :param console_type: Type of console ('spice-html5') """ return self.manager.get_spice_console(self, console_type) def get_rdp_console(self, console_type): """ Get rdp console for a Server. :param console_type: Type of console ('rdp-html5') """ return self.manager.get_rdp_console(self, console_type) def get_password(self, private_key=None): """ Get password for a Server. Returns the clear password of an instance if private_key is provided, returns the ciphered password otherwise. :param private_key: Path to private key file for decryption (optional) """ return self.manager.get_password(self, private_key) def clear_password(self): """ Get password for a Server. """ return self.manager.clear_password(self) def add_fixed_ip(self, network_id): """ Add an IP address on a network. :param network_id: The ID of the network the IP should be on. """ self.manager.add_fixed_ip(self, network_id) def add_floating_ip(self, address, fixed_address=None): """ Add floating IP to an instance :param address: The ip address or FloatingIP to add to the instance :param fixed_address: The fixedIP address the FloatingIP is to be associated with (optional) """ self.manager.add_floating_ip(self, address, fixed_address) def remove_floating_ip(self, address): """ Remove floating IP from an instance :param address: The ip address or FloatingIP to remove """ self.manager.remove_floating_ip(self, address) def stop(self): """ Stop -- Stop the running server. """ self.manager.stop(self) def force_delete(self): """ Force delete -- Force delete a server. """ self.manager.force_delete(self) def restore(self): """ Restore -- Restore a server in 'soft-deleted' state. """ self.manager.restore(self) def start(self): """ Start -- Start the paused server. """ self.manager.start(self) def pause(self): """ Pause -- Pause the running server. """ self.manager.pause(self) def unpause(self): """ Unpause -- Unpause the paused server. """ self.manager.unpause(self) def lock(self): """ Lock -- Lock the instance from certain operations. """ self.manager.lock(self) def unlock(self): """ Unlock -- Remove instance lock. """ self.manager.unlock(self) def suspend(self): """ Suspend -- Suspend the running server. """ self.manager.suspend(self) def resume(self): """ Resume -- Resume the suspended server. """ self.manager.resume(self) def rescue(self): """ Rescue -- Rescue the problematic server. """ return self.manager.rescue(self) def unrescue(self): """ Unrescue -- Unrescue the rescued server. """ self.manager.unrescue(self) def shelve(self): """ Shelve -- Shelve the server. """ self.manager.shelve(self) def shelve_offload(self): """ Shelve_offload -- Remove a shelved server from the compute node. """ self.manager.shelve_offload(self) def unshelve(self): """ Unshelve -- Unshelve the server. """ self.manager.unshelve(self) def diagnostics(self): """Diagnostics -- Retrieve server diagnostics.""" return self.manager.diagnostics(self) def migrate(self): """ Migrate a server to a new host. """ self.manager.migrate(self) def remove_fixed_ip(self, address): """ Remove an IP address. :param address: The IP address to remove. """ self.manager.remove_fixed_ip(self, address) def change_password(self, password): """ Update the password for a server. """ self.manager.change_password(self, password) def reboot(self, reboot_type=REBOOT_SOFT): """ Reboot the server. :param reboot_type: either :data:`REBOOT_SOFT` for a software-level reboot, or `REBOOT_HARD` for a virtual power cycle hard reboot. """ self.manager.reboot(self, reboot_type) def rebuild(self, image, password=None, preserve_ephemeral=False, **kwargs): """ Rebuild -- shut down and then re-image -- this server. :param image: the :class:`Image` (or its ID) to re-image with. :param password: string to set as password on the rebuilt server. :param preserve_ephemeral: If True, request that any ephemeral device be preserved when rebuilding the instance. Defaults to False. """ return self.manager.rebuild(self, image, password=password, preserve_ephemeral=preserve_ephemeral, **kwargs) def resize(self, flavor, **kwargs): """ Resize the server's resources. :param flavor: the :class:`Flavor` (or its ID) to resize to. Until a resize event is confirmed with :meth:`confirm_resize`, the old server will be kept around and you'll be able to roll back to the old flavor quickly with :meth:`revert_resize`. All resizes are automatically confirmed after 24 hours. """ self.manager.resize(self, flavor, **kwargs) def create_image(self, image_name, metadata=None): """ Create an image based on this server. :param image_name: The name to assign the newly create image. :param metadata: Metadata to assign to the image. """ return self.manager.create_image(self, image_name, metadata) def backup(self, backup_name, backup_type, rotation): """ Backup a server instance. :param backup_name: Name of the backup image :param backup_type: The backup type, like 'daily' or 'weekly' :param rotation: Int parameter representing how many backups to keep around. """ self.manager.backup(self, backup_name, backup_type, rotation) def confirm_resize(self): """ Confirm that the resize worked, thus removing the original server. """ self.manager.confirm_resize(self) def revert_resize(self): """ Revert a previous resize, switching back to the old server. """ self.manager.revert_resize(self) @property def networks(self): """ Generate a simplified list of addresses """ networks = {} try: for network_label, address_list in self.addresses.items(): networks[network_label] = [a['addr'] for a in address_list] return networks except Exception: return {} def live_migrate(self, host=None, block_migration=False, disk_over_commit=False): """ Migrates a running instance to a new machine. """ self.manager.live_migrate(self, host, block_migration, disk_over_commit) def reset_state(self, state='error'): """ Reset the state of an instance to active or error. """ self.manager.reset_state(self, state) def reset_network(self): """ Reset network of an instance. """ self.manager.reset_network(self) def add_security_group(self, security_group): """ Add a security group to an instance. """ self.manager.add_security_group(self, security_group) def remove_security_group(self, security_group): """ Remove a security group from an instance. """ self.manager.remove_security_group(self, security_group) def list_security_group(self): """ List security group(s) of an instance. """ return self.manager.list_security_group(self) def evacuate(self, host, on_shared_storage, password=None): """ Evacuate an instance from failed host to specified host. :param host: Name of the target host :param on_shared_storage: Specifies whether instance files located on shared storage :param password: string to set as password on the evacuated server. """ return self.manager.evacuate(self, host, on_shared_storage, password) def interface_list(self): """ List interfaces attached to an instance. """ return self.manager.interface_list(self) def interface_attach(self, port_id, net_id, fixed_ip): """ Attach a network interface to an instance. """ return self.manager.interface_attach(self, port_id, net_id, fixed_ip) def interface_detach(self, port_id): """ Detach a network interface from an instance. """ return self.manager.interface_detach(self, port_id) class ServerManager(base.BootingManagerWithFind): resource_class = Server def _boot(self, resource_url, response_key, name, image, flavor, meta=None, files=None, userdata=None, reservation_id=None, return_raw=False, min_count=None, max_count=None, security_groups=None, key_name=None, availability_zone=None, block_device_mapping=None, block_device_mapping_v2=None, nics=None, scheduler_hints=None, config_drive=None, admin_pass=None, disk_config=None, **kwargs): """ Create (boot) a new server. :param name: Something to name the server. :param image: The :class:`Image` to boot with. :param flavor: The :class:`Flavor` to boot onto. :param meta: A dict of arbitrary key/value metadata to store for this server. A maximum of five entries is allowed, and both keys and values must be 255 characters or less. :param files: A dict of files to overwrite on the server upon boot. Keys are file names (i.e. ``/etc/passwd``) and values are the file contents (either as a string or as a file-like object). A maximum of five entries is allowed, and each file must be 10k or less. :param reservation_id: a UUID for the set of servers being requested. :param return_raw: If True, don't try to coearse the result into a Resource object. :param security_groups: list of security group names :param key_name: (optional extension) name of keypair to inject into the instance :param availability_zone: Name of the availability zone for instance placement. :param block_device_mapping: A dict of block device mappings for this server. :param block_device_mapping_v2: A dict of block device mappings V2 for this server. :param nics: (optional extension) an ordered list of nics to be added to this server, with information about connected networks, fixed ips, etc. :param scheduler_hints: (optional extension) arbitrary key-value pairs specified by the client to help boot an instance. :param config_drive: (optional extension) value for config drive either boolean, or volume-id :param admin_pass: admin password for the server. :param disk_config: (optional extension) control how the disk is partitioned when the server is created. """ body = {"server": { "name": name, "imageRef": str(base.getid(image)) if image else '', "flavorRef": str(base.getid(flavor)), }} if userdata: if hasattr(userdata, 'read'): userdata = userdata.read() if six.PY3: userdata = userdata.encode("utf-8") else: userdata = strutils.safe_encode(userdata) body["server"]["user_data"] = base64.b64encode(userdata) if meta: body["server"]["metadata"] = meta if reservation_id: body["server"]["reservation_id"] = reservation_id if key_name: body["server"]["key_name"] = key_name if scheduler_hints: body['os:scheduler_hints'] = scheduler_hints if config_drive: body["server"]["config_drive"] = config_drive if admin_pass: body["server"]["adminPass"] = admin_pass if not min_count: min_count = 1 if not max_count: max_count = min_count body["server"]["min_count"] = min_count body["server"]["max_count"] = max_count if security_groups: body["server"]["security_groups"] =\ [{'name': sg} for sg in security_groups] # Files are a slight bit tricky. They're passed in a "personality" # list to the POST. Each item is a dict giving a file name and the # base64-encoded contents of the file. We want to allow passing # either an open file *or* some contents as files here. if files: personality = body['server']['personality'] = [] for filepath, file_or_string in sorted(files.items(), key=lambda x: x[0]): if hasattr(file_or_string, 'read'): data = file_or_string.read() else: data = file_or_string personality.append({ 'path': filepath, 'contents': base64.b64encode(data.encode('utf-8')), }) if availability_zone: body["server"]["availability_zone"] = availability_zone # Block device mappings are passed as a list of dictionaries if block_device_mapping: body['server']['block_device_mapping'] = \ self._parse_block_device_mapping(block_device_mapping) elif block_device_mapping_v2: # Append the image to the list only if we have new style BDMs if image: bdm_dict = {'uuid': image.id, 'source_type': 'image', 'destination_type': 'local', 'boot_index': 0, 'delete_on_termination': True} block_device_mapping_v2.insert(0, bdm_dict) body['server']['block_device_mapping_v2'] = block_device_mapping_v2 if nics is not None: # NOTE(tr3buchet): nics can be an empty list all_net_data = [] for nic_info in nics: net_data = {} # if value is empty string, do not send value in body if nic_info.get('net-id'): net_data['uuid'] = nic_info['net-id'] if nic_info.get('v4-fixed-ip'): net_data['fixed_ip'] = nic_info['v4-fixed-ip'] if nic_info.get('port-id'): net_data['port'] = nic_info['port-id'] all_net_data.append(net_data) body['server']['networks'] = all_net_data if disk_config is not None: body['server']['OS-DCF:diskConfig'] = disk_config return self._create(resource_url, body, response_key, return_raw=return_raw, **kwargs) def get(self, server): """ Get a server. :param server: ID of the :class:`Server` to get. :rtype: :class:`Server` """ return self._get("/servers/%s" % base.getid(server), "server") def list(self, detailed=True, search_opts=None, marker=None, limit=None): """ Get a list of servers. :param detailed: Whether to return detailed server info (optional). :param search_opts: Search options to filter out servers (optional). :param marker: Begin returning servers that appear later in the server list than that represented by this server id (optional). :param limit: Maximum number of servers to return (optional). :rtype: list of :class:`Server` """ if search_opts is None: search_opts = {} qparams = {} for opt, val in six.iteritems(search_opts): if val: qparams[opt] = val if marker: qparams['marker'] = marker if limit: qparams['limit'] = limit # Transform the dict to a sequence of two-element tuples in fixed # order, then the encoded string will be consistent in Python 2&3. if qparams: new_qparams = sorted(qparams.items(), key=lambda x: x[0]) query_string = "?%s" % parse.urlencode(new_qparams) else: query_string = "" detail = "" if detailed: detail = "/detail" return self._list("/servers%s%s" % (detail, query_string), "servers") def add_fixed_ip(self, server, network_id): """ Add an IP address on a network. :param server: The :class:`Server` (or its ID) to add an IP to. :param network_id: The ID of the network the IP should be on. """ self._action('addFixedIp', server, {'networkId': network_id}) def remove_fixed_ip(self, server, address): """ Remove an IP address. :param server: The :class:`Server` (or its ID) to add an IP to. :param address: The IP address to remove. """ self._action('removeFixedIp', server, {'address': address}) def add_floating_ip(self, server, address, fixed_address=None): """ Add a floating ip to an instance :param server: The :class:`Server` (or its ID) to add an IP to. :param address: The FloatingIP or string floating address to add. :param fixed_address: The FixedIP the floatingIP should be associated with (optional) """ address = address.ip if hasattr(address, 'ip') else address if fixed_address: if hasattr(fixed_address, 'ip'): fixed_address = fixed_address.ip self._action('addFloatingIp', server, {'address': address, 'fixed_address': fixed_address}) else: self._action('addFloatingIp', server, {'address': address}) def remove_floating_ip(self, server, address): """ Remove a floating IP address. :param server: The :class:`Server` (or its ID) to remove an IP from. :param address: The FloatingIP or string floating address to remove. """ address = address.ip if hasattr(address, 'ip') else address self._action('removeFloatingIp', server, {'address': address}) def get_vnc_console(self, server, console_type): """ Get a vnc console for an instance :param server: The :class:`Server` (or its ID) to add an IP to. :param console_type: Type of vnc console to get ('novnc' or 'xvpvnc') """ return self._action('os-getVNCConsole', server, {'type': console_type})[1] def get_spice_console(self, server, console_type): """ Get a spice console for an instance :param server: The :class:`Server` (or its ID) to add an IP to. :param console_type: Type of spice console to get ('spice-html5') """ return self._action('os-getSPICEConsole', server, {'type': console_type})[1] def get_rdp_console(self, server, console_type): """ Get a rdp console for an instance :param server: The :class:`Server` (or its ID) to add an IP to. :param console_type: Type of rdp console to get ('rdp-html5') """ return self._action('os-getRDPConsole', server, {'type': console_type})[1] def get_password(self, server, private_key=None): """ Get password for an instance Returns the clear password of an instance if private_key is provided, returns the ciphered password otherwise. Requires that openssl is installed and in the path :param server: The :class:`Server` (or its ID) to add an IP to. :param private_key: The private key to decrypt password (optional) """ _resp, body = self.api.client.get("/servers/%s/os-server-password" % base.getid(server)) ciphered_pw = body.get('password', '') if body else '' if private_key and ciphered_pw: try: return crypto.decrypt_password(private_key, ciphered_pw) except Exception as exc: return '%sFailed to decrypt:\n%s' % (exc, ciphered_pw) return ciphered_pw def clear_password(self, server): """ Clear password for an instance :param server: The :class:`Server` (or its ID) to add an IP to. """ return self._delete("/servers/%s/os-server-password" % base.getid(server)) def stop(self, server): """ Stop the server. """ return self._action('os-stop', server, None) def force_delete(self, server): """ Force delete the server. """ return self._action('forceDelete', server, None) def restore(self, server): """ Restore soft-deleted server. """ return self._action('restore', server, None) def start(self, server): """ Start the server. """ self._action('os-start', server, None) def pause(self, server): """ Pause the server. """ self._action('pause', server, None) def unpause(self, server): """ Unpause the server. """ self._action('unpause', server, None) def lock(self, server): """ Lock the server. """ self._action('lock', server, None) def unlock(self, server): """ Unlock the server. """ self._action('unlock', server, None) def suspend(self, server): """ Suspend the server. """ self._action('suspend', server, None) def resume(self, server): """ Resume the server. """ self._action('resume', server, None) def rescue(self, server): """ Rescue the server. """ return self._action('rescue', server, None) def unrescue(self, server): """ Unrescue the server. """ self._action('unrescue', server, None) def shelve(self, server): """ Shelve the server. """ self._action('shelve', server, None) def shelve_offload(self, server): """ Remove a shelved instance from the compute node. """ self._action('shelveOffload', server, None) def unshelve(self, server): """ Unshelve the server. """ self._action('unshelve', server, None) def diagnostics(self, server): """Retrieve server diagnostics.""" return self.api.client.get("/servers/%s/diagnostics" % base.getid(server)) def create(self, name, image, flavor, meta=None, files=None, reservation_id=None, min_count=None, max_count=None, security_groups=None, userdata=None, key_name=None, availability_zone=None, block_device_mapping=None, block_device_mapping_v2=None, nics=None, scheduler_hints=None, config_drive=None, disk_config=None, **kwargs): # TODO(anthony): indicate in doc string if param is an extension # and/or optional """ Create (boot) a new server. :param name: Something to name the server. :param image: The :class:`Image` to boot with. :param flavor: The :class:`Flavor` to boot onto. :param meta: A dict of arbitrary key/value metadata to store for this server. A maximum of five entries is allowed, and both keys and values must be 255 characters or less. :param files: A dict of files to overrwrite on the server upon boot. Keys are file names (i.e. ``/etc/passwd``) and values are the file contents (either as a string or as a file-like object). A maximum of five entries is allowed, and each file must be 10k or less. :param userdata: user data to pass to be exposed by the metadata server this can be a file type object as well or a string. :param reservation_id: a UUID for the set of servers being requested. :param key_name: (optional extension) name of previously created keypair to inject into the instance. :param availability_zone: Name of the availability zone for instance placement. :param block_device_mapping: (optional extension) A dict of block device mappings for this server. :param block_device_mapping_v2: (optional extension) A dict of block device mappings for this server. :param nics: (optional extension) an ordered list of nics to be added to this server, with information about connected networks, fixed ips, port etc. :param scheduler_hints: (optional extension) arbitrary key-value pairs specified by the client to help boot an instance :param config_drive: (optional extension) value for config drive either boolean, or volume-id :param disk_config: (optional extension) control how the disk is partitioned when the server is created. possible values are 'AUTO' or 'MANUAL'. """ if not min_count: min_count = 1 if not max_count: max_count = min_count if min_count > max_count: min_count = max_count boot_args = [name, image, flavor] boot_kwargs = dict( meta=meta, files=files, userdata=userdata, reservation_id=reservation_id, min_count=min_count, max_count=max_count, security_groups=security_groups, key_name=key_name, availability_zone=availability_zone, scheduler_hints=scheduler_hints, config_drive=config_drive, disk_config=disk_config, **kwargs) if block_device_mapping: resource_url = "/os-volumes_boot" boot_kwargs['block_device_mapping'] = block_device_mapping elif block_device_mapping_v2: resource_url = "/os-volumes_boot" boot_kwargs['block_device_mapping_v2'] = block_device_mapping_v2 else: resource_url = "/servers" if nics: boot_kwargs['nics'] = nics response_key = "server" return self._boot(resource_url, response_key, *boot_args, **boot_kwargs) def update(self, server, name=None): """ Update the name or the password for a server. :param server: The :class:`Server` (or its ID) to update. :param name: Update the server's name. """ if name is None: return body = { "server": { "name": name, }, } return self._update("/servers/%s" % base.getid(server), body, "server") def change_password(self, server, password): """ Update the password for a server. """ self._action("changePassword", server, {"adminPass": password}) def delete(self, server): """ Delete (i.e. shut down and delete the image) this server. """ self._delete("/servers/%s" % base.getid(server)) def reboot(self, server, reboot_type=REBOOT_SOFT): """ Reboot a server. :param server: The :class:`Server` (or its ID) to share onto. :param reboot_type: either :data:`REBOOT_SOFT` for a software-level reboot, or `REBOOT_HARD` for a virtual power cycle hard reboot. """ self._action('reboot', server, {'type': reboot_type}) def rebuild(self, server, image, password=None, disk_config=None, preserve_ephemeral=False, **kwargs): """ Rebuild -- shut down and then re-image -- a server. :param server: The :class:`Server` (or its ID) to share onto. :param image: the :class:`Image` (or its ID) to re-image with. :param password: string to set as password on the rebuilt server. :param disk_config: partitioning mode to use on the rebuilt server. Valid values are 'AUTO' or 'MANUAL' :param preserve_ephemeral: If True, request that any ephemeral device be preserved when rebuilding the instance. Defaults to False. """ body = {'imageRef': base.getid(image)} if password is not None: body['adminPass'] = password if disk_config is not None: body['OS-DCF:diskConfig'] = disk_config if preserve_ephemeral is not False: body['preserve_ephemeral'] = True _resp, body = self._action('rebuild', server, body, **kwargs) return Server(self, body['server']) def migrate(self, server): """ Migrate a server to a new host. :param server: The :class:`Server` (or its ID). """ self._action('migrate', server) def resize(self, server, flavor, disk_config=None, **kwargs): """ Resize a server's resources. :param server: The :class:`Server` (or its ID) to share onto. :param flavor: the :class:`Flavor` (or its ID) to resize to. :param disk_config: partitioning mode to use on the rebuilt server. Valid values are 'AUTO' or 'MANUAL' Until a resize event is confirmed with :meth:`confirm_resize`, the old server will be kept around and you'll be able to roll back to the old flavor quickly with :meth:`revert_resize`. All resizes are automatically confirmed after 24 hours. """ info = {'flavorRef': base.getid(flavor)} if disk_config is not None: info['OS-DCF:diskConfig'] = disk_config self._action('resize', server, info=info, **kwargs) def confirm_resize(self, server): """ Confirm that the resize worked, thus removing the original server. :param server: The :class:`Server` (or its ID) to share onto. """ self._action('confirmResize', server) def revert_resize(self, server): """ Revert a previous resize, switching back to the old server. :param server: The :class:`Server` (or its ID) to share onto. """ self._action('revertResize', server) def create_image(self, server, image_name, metadata=None): """ Snapshot a server. :param server: The :class:`Server` (or its ID) to share onto. :param image_name: Name to give the snapshot image :param meta: Metadata to give newly-created image entity """ body = {'name': image_name, 'metadata': metadata or {}} resp = self._action('createImage', server, body)[0] location = resp.headers['location'] image_uuid = location.split('/')[-1] return image_uuid def backup(self, server, backup_name, backup_type, rotation): """ Backup a server instance. :param server: The :class:`Server` (or its ID) to share onto. :param backup_name: Name of the backup image :param backup_type: The backup type, like 'daily' or 'weekly' :param rotation: Int parameter representing how many backups to keep around. """ body = {'name': backup_name, 'backup_type': backup_type, 'rotation': rotation} self._action('createBackup', server, body) def set_meta(self, server, metadata): """ Set a servers metadata :param server: The :class:`Server` to add metadata to :param metadata: A dict of metadata to add to the server """ body = {'metadata': metadata} return self._create("/servers/%s/metadata" % base.getid(server), body, "metadata") def set_meta_item(self, server, key, value): """ Updates an item of server metadata :param server: The :class:`Server` to add metadata to :param key: metadata key to update :param value: string value """ body = {'meta': {key: value}} return self._update("/servers/%s/metadata/%s" % (base.getid(server), key), body) def get_console_output(self, server, length=None): """ Get text console log output from Server. :param server: The :class:`Server` (or its ID) whose console output you would like to retrieve. :param length: The number of tail loglines you would like to retrieve. """ return self._action('os-getConsoleOutput', server, {'length': length})[1]['output'] def delete_meta(self, server, keys): """ Delete metadata from an server :param server: The :class:`Server` to add metadata to :param keys: A list of metadata keys to delete from the server """ for k in keys: self._delete("/servers/%s/metadata/%s" % (base.getid(server), k)) def live_migrate(self, server, host, block_migration, disk_over_commit): """ Migrates a running instance to a new machine. :param server: instance id which comes from nova list. :param host: destination host name. :param block_migration: if True, do block_migration. :param disk_over_commit: if True, Allow overcommit. """ self._action('os-migrateLive', server, {'host': host, 'block_migration': block_migration, 'disk_over_commit': disk_over_commit}) def reset_state(self, server, state='error'): """ Reset the state of an instance to active or error. :param server: ID of the instance to reset the state of. :param state: Desired state; either 'active' or 'error'. Defaults to 'error'. """ self._action('os-resetState', server, dict(state=state)) def reset_network(self, server): """ Reset network of an instance. """ self._action('resetNetwork', server) def add_security_group(self, server, security_group): """ Add a Security Group to an instance :param server: ID of the instance. :param security_group: The name of security group to add. """ self._action('addSecurityGroup', server, {'name': security_group}) def remove_security_group(self, server, security_group): """ Add a Security Group to an instance :param server: ID of the instance. :param security_group: The name of security group to remove. """ self._action('removeSecurityGroup', server, {'name': security_group}) def list_security_group(self, server): """ List Security Group(s) of an instance :param server: ID of the instance. """ return self._list('/servers/%s/os-security-groups' % base.getid(server), 'security_groups', SecurityGroup) def evacuate(self, server, host, on_shared_storage, password=None): """ Evacuate a server instance. :param server: The :class:`Server` (or its ID) to share onto. :param host: Name of the target host. :param on_shared_storage: Specifies whether instance files located on shared storage :param password: string to set as password on the evacuated server. """ body = { 'host': host, 'onSharedStorage': on_shared_storage, } if password is not None: body['adminPass'] = password return self._action('evacuate', server, body) def interface_list(self, server): """ List attached network interfaces :param server: The :class:`Server` (or its ID) to query. """ return self._list('/servers/%s/os-interface' % base.getid(server), 'interfaceAttachments') def interface_attach(self, server, port_id, net_id, fixed_ip): """ Attach a network_interface to an instance. :param server: The :class:`Server` (or its ID) to attach to. :param port_id: The port to attach. """ body = {'interfaceAttachment': {}} if port_id: body['interfaceAttachment']['port_id'] = port_id if net_id: body['interfaceAttachment']['net_id'] = net_id if fixed_ip: body['interfaceAttachment']['fixed_ips'] = [ {'ip_address': fixed_ip}] return self._create('/servers/%s/os-interface' % base.getid(server), body, 'interfaceAttachment') def interface_detach(self, server, port_id): """ Detach a network_interface from an instance. :param server: The :class:`Server` (or its ID) to detach from. :param port_id: The port to detach. """ self._delete('/servers/%s/os-interface/%s' % (base.getid(server), port_id)) def _action(self, action, server, info=None, **kwargs): """ Perform a server "action" -- reboot/rebuild/resize/etc. """ body = {action: info} self.run_hooks('modify_body_for_action', body, **kwargs) url = '/servers/%s/action' % base.getid(server) return self.api.client.post(url, body=body) python-novaclient-2.17.0/novaclient/v1_1/flavors.py0000664000175400017540000001522512306432770023367 0ustar jenkinsjenkins00000000000000# Copyright 2010 Jacob Kaplan-Moss # # 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. """ Flavor interface. """ from six.moves.urllib import parse from novaclient import base from novaclient import exceptions from novaclient.openstack.common.gettextutils import _ from novaclient.openstack.common import strutils from novaclient import utils class Flavor(base.Resource): """ A flavor is an available hardware configuration for a server. """ HUMAN_ID = True def __repr__(self): return "" % self.name @property def ephemeral(self): """ Provide a user-friendly accessor to OS-FLV-EXT-DATA:ephemeral """ return self._info.get("OS-FLV-EXT-DATA:ephemeral", 'N/A') @property def is_public(self): """ Provide a user-friendly accessor to os-flavor-access:is_public """ return self._info.get("os-flavor-access:is_public", 'N/A') def get_keys(self): """ Get extra specs from a flavor. :param flavor: The :class:`Flavor` to get extra specs from """ _resp, body = self.manager.api.client.get( "/flavors/%s/os-extra_specs" % base.getid(self)) return body["extra_specs"] def set_keys(self, metadata): """ Set extra specs on a flavor. :param flavor: The :class:`Flavor` to set extra spec on :param metadata: A dict of key/value pairs to be set """ utils.validate_flavor_metadata_keys(metadata.keys()) body = {'extra_specs': metadata} return self.manager._create( "/flavors/%s/os-extra_specs" % base.getid(self), body, "extra_specs", return_raw=True) def unset_keys(self, keys): """ Unset extra specs on a flavor. :param flavor: The :class:`Flavor` to unset extra spec on :param keys: A list of keys to be unset """ for k in keys: return self.manager._delete( "/flavors/%s/os-extra_specs/%s" % ( base.getid(self), k)) def delete(self): """ Delete this flavor. """ self.manager.delete(self) class FlavorManager(base.ManagerWithFind): """ Manage :class:`Flavor` resources. """ resource_class = Flavor is_alphanum_id_allowed = True def list(self, detailed=True, is_public=True): """ Get a list of all flavors. :rtype: list of :class:`Flavor`. """ qparams = {} # is_public is ternary - None means give all flavors. # By default Nova assumes True and gives admins public flavors # and flavors from their own projects only. if not is_public: qparams['is_public'] = is_public query_string = "?%s" % parse.urlencode(qparams) if qparams else "" detail = "" if detailed: detail = "/detail" return self._list("/flavors%s%s" % (detail, query_string), "flavors") def get(self, flavor): """ Get a specific flavor. :param flavor: The ID of the :class:`Flavor` to get. :rtype: :class:`Flavor` """ return self._get("/flavors/%s" % base.getid(flavor), "flavor") def delete(self, flavor): """ Delete a specific flavor. :param flavor: The ID of the :class:`Flavor` to get. """ self._delete("/flavors/%s" % base.getid(flavor)) def _build_body(self, name, ram, vcpus, disk, id, swap, ephemeral, rxtx_factor, is_public): return { "flavor": { "name": name, "ram": ram, "vcpus": vcpus, "disk": disk, "id": id, "swap": swap, "OS-FLV-EXT-DATA:ephemeral": ephemeral, "rxtx_factor": rxtx_factor, "os-flavor-access:is_public": is_public, } } def create(self, name, ram, vcpus, disk, flavorid="auto", ephemeral=0, swap=0, rxtx_factor=1.0, is_public=True): """ Create a flavor. :param name: Descriptive name of the flavor :param ram: Memory in MB for the flavor :param vcpus: Number of VCPUs for the flavor :param disk: Size of local disk in GB :param flavorid: ID for the flavor (optional). You can use the reserved value ``"auto"`` to have Nova generate a UUID for the flavor in cases where you cannot simply pass ``None``. :param swap: Swap space in MB :param rxtx_factor: RX/TX factor :rtype: :class:`Flavor` """ try: ram = int(ram) except (TypeError, ValueError): raise exceptions.CommandError(_("Ram must be an integer.")) try: vcpus = int(vcpus) except (TypeError, ValueError): raise exceptions.CommandError(_("VCPUs must be an integer.")) try: disk = int(disk) except (TypeError, ValueError): raise exceptions.CommandError(_("Disk must be an integer.")) if flavorid == "auto": flavorid = None try: swap = int(swap) except (TypeError, ValueError): raise exceptions.CommandError(_("Swap must be an integer.")) try: ephemeral = int(ephemeral) except (TypeError, ValueError): raise exceptions.CommandError(_("Ephemeral must be an integer.")) try: rxtx_factor = float(rxtx_factor) except (TypeError, ValueError): raise exceptions.CommandError(_("rxtx_factor must be a float.")) try: is_public = strutils.bool_from_string(is_public, True) except Exception: raise exceptions.CommandError(_("is_public must be a boolean.")) body = self._build_body(name, ram, vcpus, disk, flavorid, swap, ephemeral, rxtx_factor, is_public) return self._create("/flavors", body, "flavor") python-novaclient-2.17.0/novaclient/v1_1/availability_zones.py0000664000175400017540000000306412306432770025601 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # Copyright 2013 IBM Corp. # 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. """ Availability Zone interface (1.1 extension). """ from novaclient import base class AvailabilityZone(base.Resource): """ An availability zone object. """ NAME_ATTR = 'display_name' def __repr__(self): return "" % self.zoneName class AvailabilityZoneManager(base.ManagerWithFind): """ Manage :class:`AvailabilityZone` resources. """ resource_class = AvailabilityZone return_parameter_name = "availabilityZoneInfo" def list(self, detailed=True): """ Get a list of all availability zones. :rtype: list of :class:`AvailabilityZone` """ if detailed is True: return self._list("/os-availability-zone/detail", self.return_parameter_name) else: return self._list("/os-availability-zone", self.return_parameter_name) python-novaclient-2.17.0/novaclient/v1_1/images.py0000664000175400017540000000545512306432770023164 0ustar jenkinsjenkins00000000000000# Copyright 2010 Jacob Kaplan-Moss # # 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. """ Image interface. """ from six.moves.urllib import parse from novaclient import base class Image(base.Resource): """ An image is a collection of files used to create or rebuild a server. """ HUMAN_ID = True def __repr__(self): return "" % self.name def delete(self): """ Delete this image. """ self.manager.delete(self) class ImageManager(base.ManagerWithFind): """ Manage :class:`Image` resources. """ resource_class = Image def get(self, image): """ Get an image. :param image: The ID of the image to get. :rtype: :class:`Image` """ return self._get("/images/%s" % base.getid(image), "image") def list(self, detailed=True, limit=None): """ Get a list of all images. :rtype: list of :class:`Image` :param limit: maximum number of images to return. """ params = {} detail = '' if detailed: detail = '/detail' if limit: params['limit'] = int(limit) query = '?%s' % parse.urlencode(params) if params else '' return self._list('/images%s%s' % (detail, query), 'images') def delete(self, image): """ Delete an image. It should go without saying that you can't delete an image that you didn't create. :param image: The :class:`Image` (or its ID) to delete. """ self._delete("/images/%s" % base.getid(image)) def set_meta(self, image, metadata): """ Set an images metadata :param image: The :class:`Image` to add metadata to :param metadata: A dict of metadata to add to the image """ body = {'metadata': metadata} return self._create("/images/%s/metadata" % base.getid(image), body, "metadata") def delete_meta(self, image, keys): """ Delete metadata from an image :param image: The :class:`Image` to delete metadata :param keys: A list of metadata keys to delete from the image """ for k in keys: self._delete("/images/%s/metadata/%s" % (base.getid(image), k)) python-novaclient-2.17.0/novaclient/v1_1/floating_ips.py0000664000175400017540000000331712306432770024370 0ustar jenkinsjenkins00000000000000# Copyright (c) 2011 X.commerce, a business unit of eBay Inc. # Copyright 2011 OpenStack Foundation # 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 novaclient import base class FloatingIP(base.Resource): def delete(self): """ Delete this floating ip """ self.manager.delete(self) class FloatingIPManager(base.ManagerWithFind): resource_class = FloatingIP def list(self): """ List floating ips for a tenant """ return self._list("/os-floating-ips", "floating_ips") def create(self, pool=None): """ Create (allocate) a floating ip for a tenant """ return self._create("/os-floating-ips", {'pool': pool}, "floating_ip") def delete(self, floating_ip): """ Delete (deallocate) a floating ip for a tenant :param floating_ip: The floating ip address to delete. """ self._delete("/os-floating-ips/%s" % base.getid(floating_ip)) def get(self, floating_ip): """ Retrieve a floating ip """ return self._get("/os-floating-ips/%s" % base.getid(floating_ip), "floating_ip") python-novaclient-2.17.0/novaclient/v1_1/floating_ip_pools.py0000664000175400017540000000205012306432770025412 0ustar jenkinsjenkins00000000000000# Copyright (c) 2011 X.commerce, a business unit of eBay Inc. # # 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 novaclient import base class FloatingIPPool(base.Resource): def __repr__(self): return "" % self.name class FloatingIPPoolManager(base.ManagerWithFind): resource_class = FloatingIPPool def list(self): """ Retrieve a list of all floating ip pools. """ return self._list('/os-floating-ip-pools', 'floating_ip_pools') python-novaclient-2.17.0/novaclient/v1_1/flavor_access.py0000664000175400017540000000507112306432770024523 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # 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. """Flavor access interface.""" from novaclient import base from novaclient.openstack.common.gettextutils import _ class FlavorAccess(base.Resource): def __repr__(self): return "" % self.name class FlavorAccessManager(base.ManagerWithFind): """ Manage :class:`FlavorAccess` resources. """ resource_class = FlavorAccess def list(self, **kwargs): if kwargs.get('flavor'): return self._list_by_flavor(kwargs['flavor']) elif kwargs.get('tenant'): return self._list_by_tenant(kwargs['tenant']) else: raise NotImplementedError(_('Unknown list options.')) def _list_by_flavor(self, flavor): return self._list('/flavors/%s/os-flavor-access' % base.getid(flavor), 'flavor_access') def _list_by_tenant(self, tenant): """Print flavor list shared with the given tenant.""" # TODO(uni): need to figure out a proper URI for list_by_tenant # since current API already provided current tenant_id information raise NotImplementedError(_('Sorry, query by tenant not supported.')) def add_tenant_access(self, flavor, tenant): """Add a tenant to the given flavor access list.""" info = {'tenant': tenant} return self._action('addTenantAccess', flavor, info) def remove_tenant_access(self, flavor, tenant): """Remove a tenant from the given flavor access list.""" info = {'tenant': tenant} return self._action('removeTenantAccess', flavor, info) def _action(self, action, flavor, info, **kwargs): """Perform a flavor action.""" body = {action: info} self.run_hooks('modify_body_for_action', body, **kwargs) url = '/flavors/%s/action' % base.getid(flavor) _resp, body = self.api.client.post(url, body=body) return [self.resource_class(self, res) for res in body['flavor_access']] python-novaclient-2.17.0/novaclient/v1_1/quotas.py0000664000175400017540000000455012306432770023226 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # 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 novaclient import base class QuotaSet(base.Resource): @property def id(self): """QuotaSet does not have a 'id' attribute but base.Resource needs it to self-refresh and QuotaSet is indexed by tenant_id. """ return self.tenant_id def update(self, *args, **kwargs): return self.manager.update(self.tenant_id, *args, **kwargs) class QuotaSetManager(base.Manager): resource_class = QuotaSet def get(self, tenant_id, user_id=None): if hasattr(tenant_id, 'tenant_id'): tenant_id = tenant_id.tenant_id if user_id: url = '/os-quota-sets/%s?user_id=%s' % (tenant_id, user_id) else: url = '/os-quota-sets/%s' % tenant_id return self._get(url, "quota_set") def _update_body(self, tenant_id, **kwargs): kwargs['tenant_id'] = tenant_id return {'quota_set': kwargs} def update(self, tenant_id, **kwargs): user_id = kwargs.pop('user_id', None) body = self._update_body(tenant_id, **kwargs) for key in list(body['quota_set']): if body['quota_set'][key] is None: body['quota_set'].pop(key) if user_id: url = '/os-quota-sets/%s?user_id=%s' % (tenant_id, user_id) else: url = '/os-quota-sets/%s' % tenant_id return self._update(url, body, 'quota_set') def defaults(self, tenant_id): return self._get('/os-quota-sets/%s/defaults' % tenant_id, 'quota_set') def delete(self, tenant_id, user_id=None): if user_id: url = '/os-quota-sets/%s?user_id=%s' % (tenant_id, user_id) else: url = '/os-quota-sets/%s' % tenant_id self._delete(url) python-novaclient-2.17.0/novaclient/v1_1/cloudpipe.py0000664000175400017540000000354612306432770023702 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # 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. """Cloudpipe interface.""" from novaclient import base class Cloudpipe(base.Resource): """A cloudpipe instance is a VPN attached to a proejct's VLAN.""" def __repr__(self): return "" % self.project_id def delete(self): self.manager.delete(self) class CloudpipeManager(base.ManagerWithFind): resource_class = Cloudpipe def create(self, project): """ Launch a cloudpipe instance. :param project: UUID of the project (tenant) for the cloudpipe """ body = {'cloudpipe': {'project_id': project}} return self._create('/os-cloudpipe', body, 'instance_id', return_raw=True) def list(self): """ Get a list of cloudpipe instances. """ return self._list('/os-cloudpipe', 'cloudpipes') def update(self, address, port): """ Update VPN address and port for all networks associated with the project defined by authentication :param address: IP address :param port: Port number """ body = {'configure_project': {'vpn_ip': address, 'vpn_port': port}} self._update("/os-cloudpipe/configure-project", body) python-novaclient-2.17.0/novaclient/v1_1/fping.py0000664000175400017540000000332712306432770023016 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # 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. """ Fping interface. """ from novaclient import base class Fping(base.Resource): """ A server to fping. """ HUMAN_ID = True def __repr__(self): return "" % self.id class FpingManager(base.ManagerWithFind): """ Manage :class:`Fping` resources. """ resource_class = Fping def list(self, all_tenants=False, include=[], exclude=[]): """ Fping all servers. :rtype: list of :class:`Fping`. """ params = [] if all_tenants: params.append("all_tenants=1") if include: params.append("include=%s" % ",".join(include)) elif exclude: params.append("exclude=%s" % ",".join(exclude)) uri = "/os-fping" if params: uri = "%s?%s" % (uri, "&".join(params)) return self._list(uri, "servers") def get(self, server): """ Fping a specific server. :param network: ID of the server to fping. :rtype: :class:`Fping` """ return self._get("/os-fping/%s" % base.getid(server), "server") python-novaclient-2.17.0/novaclient/v1_1/aggregates.py0000664000175400017540000000665712306432770024035 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # 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. """Aggregate interface.""" from novaclient import base class Aggregate(base.Resource): """An aggregates is a collection of compute hosts.""" def __repr__(self): return "" % self.id def update(self, values): """Update the name and/or availability zone.""" return self.manager.update(self, values) def add_host(self, host): return self.manager.add_host(self, host) def remove_host(self, host): return self.manager.remove_host(self, host) def set_metadata(self, metadata): return self.manager.set_metadata(self, metadata) def delete(self): self.manager.delete(self) class AggregateManager(base.ManagerWithFind): resource_class = Aggregate def list(self): """Get a list of os-aggregates.""" return self._list('/os-aggregates', 'aggregates') def create(self, name, availability_zone): """Create a new aggregate.""" body = {'aggregate': {'name': name, 'availability_zone': availability_zone}} return self._create('/os-aggregates', body, 'aggregate') def get(self, aggregate): """Get details of the specified aggregate.""" return self._get('/os-aggregates/%s' % (base.getid(aggregate)), "aggregate") # NOTE:(dtroyer): utils.find_resource() uses manager.get() but we need to # keep the API backward compatible def get_details(self, aggregate): """Get details of the specified aggregate.""" return self.get(aggregate) def update(self, aggregate, values): """Update the name and/or availability zone.""" body = {'aggregate': values} return self._update("/os-aggregates/%s" % base.getid(aggregate), body, "aggregate") def add_host(self, aggregate, host): """Add a host into the Host Aggregate.""" body = {'add_host': {'host': host}} return self._create("/os-aggregates/%s/action" % base.getid(aggregate), body, "aggregate") def remove_host(self, aggregate, host): """Remove a host from the Host Aggregate.""" body = {'remove_host': {'host': host}} return self._create("/os-aggregates/%s/action" % base.getid(aggregate), body, "aggregate") def set_metadata(self, aggregate, metadata): """Set a aggregate metadata, replacing the existing metadata.""" body = {'set_metadata': {'metadata': metadata}} return self._create("/os-aggregates/%s/action" % base.getid(aggregate), body, "aggregate") def delete(self, aggregate): """Delete the specified aggregates.""" self._delete('/os-aggregates/%s' % (base.getid(aggregate))) python-novaclient-2.17.0/novaclient/v1_1/security_groups.py0000664000175400017540000000567012306432770025164 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # 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. """ Security group interface (1.1 extension). """ import six from six.moves.urllib import parse from novaclient import base class SecurityGroup(base.Resource): def __str__(self): return str(self.id) def delete(self): self.manager.delete(self) def update(self): self.manager.update(self) class SecurityGroupManager(base.ManagerWithFind): resource_class = SecurityGroup def create(self, name, description): """ Create a security group :param name: name for the security group to create :param description: description of the security group :rtype: the security group object """ body = {"security_group": {"name": name, 'description': description}} return self._create('/os-security-groups', body, 'security_group') def update(self, group, name, description): """ Update a security group :param group: The security group to update (group or ID) :param name: name for the security group to update :param description: description for the security group to update :rtype: the security group object """ body = {"security_group": {"name": name, 'description': description}} return self._update('/os-security-groups/%s' % base.getid(group), body, 'security_group') def delete(self, group): """ Delete a security group :param group: The security group to delete (group or ID) :rtype: None """ self._delete('/os-security-groups/%s' % base.getid(group)) def get(self, group_id): """ Get a security group :param group_id: The security group to get by ID :rtype: :class:`SecurityGroup` """ return self._get('/os-security-groups/%s' % group_id, 'security_group') def list(self, search_opts=None): """ Get a list of all security_groups :rtype: list of :class:`SecurityGroup` """ search_opts = search_opts or {} qparams = dict((k, v) for (k, v) in six.iteritems(search_opts) if v) query_string = '?%s' % parse.urlencode(qparams) if qparams else '' return self._list('/os-security-groups%s' % query_string, 'security_groups') python-novaclient-2.17.0/novaclient/v1_1/shell.py0000664000175400017540000034735312306432771023035 0ustar jenkinsjenkins00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 OpenStack Foundation # Copyright 2013 IBM Corp. # 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 __future__ import print_function import argparse import copy import datetime import getpass import locale import os import sys import time import six from novaclient import exceptions from novaclient.openstack.common.gettextutils import _ from novaclient.openstack.common import strutils from novaclient.openstack.common import timeutils from novaclient.openstack.common import uuidutils from novaclient import utils from novaclient.v1_1 import availability_zones from novaclient.v1_1 import quotas from novaclient.v1_1 import servers CLIENT_BDM2_KEYS = { 'id': 'uuid', 'source': 'source_type', 'dest': 'destination_type', 'bus': 'disk_bus', 'device': 'device_name', 'size': 'volume_size', 'format': 'guest_format', 'bootindex': 'boot_index', 'type': 'device_type', 'shutdown': 'delete_on_termination', } def _key_value_pairing(text): try: (k, v) = text.split('=', 1) return (k, v) except ValueError: msg = "%r is not in the format of key=value" % text raise argparse.ArgumentTypeError(msg) def _match_image(cs, wanted_properties): image_list = cs.images.list() images_matched = [] match = set(wanted_properties) for img in image_list: try: if match == match.intersection(set(img.metadata.items())): images_matched.append(img) except AttributeError: pass return images_matched def _parse_block_device_mapping_v2(args, image): bdm = [] if args.boot_volume: bdm_dict = {'uuid': args.boot_volume, 'source_type': 'volume', 'destination_type': 'volume', 'boot_index': 0, 'delete_on_termination': False} bdm.append(bdm_dict) if args.snapshot: bdm_dict = {'uuid': args.snapshot, 'source_type': 'snapshot', 'destination_type': 'volume', 'boot_index': 0, 'delete_on_termination': False} bdm.append(bdm_dict) for device_spec in args.block_device: spec_dict = dict(v.split('=') for v in device_spec.split(',')) bdm_dict = {} for key, value in six.iteritems(spec_dict): bdm_dict[CLIENT_BDM2_KEYS[key]] = value # Convert the delete_on_termination to a boolean or set it to true by # default for local block devices when not specified. if 'delete_on_termination' in bdm_dict: action = bdm_dict['delete_on_termination'] bdm_dict['delete_on_termination'] = (action == 'remove') elif bdm_dict.get('destination_type') == 'local': bdm_dict['delete_on_termination'] = True bdm.append(bdm_dict) for ephemeral_spec in args.ephemeral: bdm_dict = {'source_type': 'blank', 'destination_type': 'local', 'boot_index': -1, 'delete_on_termination': True} eph_dict = dict(v.split('=') for v in ephemeral_spec.split(',')) if 'size' in eph_dict: bdm_dict['volume_size'] = eph_dict['size'] if 'format' in eph_dict: bdm_dict['guest_format'] = eph_dict['format'] bdm.append(bdm_dict) if args.swap: bdm_dict = {'source_type': 'blank', 'destination_type': 'local', 'boot_index': -1, 'delete_on_termination': True, 'guest_format': 'swap', 'volume_size': args.swap} bdm.append(bdm_dict) return bdm def _boot(cs, args, reservation_id=None, min_count=None, max_count=None): """Boot a new server.""" if min_count is None: min_count = 1 if max_count is None: max_count = min_count if min_count > max_count: raise exceptions.CommandError(_("min_instances should be <= " "max_instances")) if not min_count or not max_count: raise exceptions.CommandError(_("min_instances nor max_instances " "should be 0")) if args.image: image = _find_image(cs, args.image) else: image = None if not image and args.image_with: images = _match_image(cs, args.image_with) if images: # TODO(harlowja): log a warning that we # are selecting the first of many? image = images[0] if not args.flavor: raise exceptions.CommandError(_("you need to specify a Flavor ID ")) if args.num_instances is not None: if args.num_instances <= 1: raise exceptions.CommandError(_("num_instances should be > 1")) max_count = args.num_instances flavor = _find_flavor(cs, args.flavor) meta = dict(v.split('=', 1) for v in args.meta) files = {} for f in args.files: try: dst, src = f.split('=', 1) files[dst] = open(src) except IOError as e: raise exceptions.CommandError(_("Can't open '%(src)s': %(exc)s") % {'src': src, 'exc': e}) except ValueError as e: raise exceptions.CommandError(_("Invalid file argument '%s'. " "File arguments must be of the " "form '--file " "'") % f) # use the os-keypair extension key_name = None if args.key_name is not None: key_name = args.key_name if args.user_data: try: userdata = open(args.user_data) except IOError as e: raise exceptions.CommandError(_("Can't open '%(user_data)s': " "%(exc)s") % {'user_data': args.user_data, 'exc': e}) else: userdata = None if args.availability_zone: availability_zone = args.availability_zone else: availability_zone = None if args.security_groups: security_groups = args.security_groups.split(',') else: security_groups = None block_device_mapping = {} for bdm in args.block_device_mapping: device_name, mapping = bdm.split('=', 1) block_device_mapping[device_name] = mapping block_device_mapping_v2 = _parse_block_device_mapping_v2(args, image) n_boot_args = len(list(filter( bool, (image, args.boot_volume, args.snapshot)))) have_bdm = block_device_mapping_v2 or block_device_mapping # Fail if more than one boot devices are present # or if there is no device to boot from. if n_boot_args > 1 or n_boot_args == 0 and not have_bdm: raise exceptions.CommandError( _("you need to specify at least one source ID (Image, Snapshot, " "or Volume), a block device mapping or provide a set of " "properties to match against an image")) if block_device_mapping and block_device_mapping_v2: raise exceptions.CommandError( _("you can't mix old block devices (--block-device-mapping) " "with the new ones (--block-device, --boot-volume, --snapshot, " "--ephemeral, --swap)")) nics = [] for nic_str in args.nics: err_msg = (_("Invalid nic argument '%s'. Nic arguments must be of " "the form --nic , with at minimum net-id or port-id " "specified.") % nic_str) nic_info = {"net-id": "", "v4-fixed-ip": "", "port-id": ""} for kv_str in nic_str.split(","): try: k, v = kv_str.split("=", 1) except ValueError as e: raise exceptions.CommandError(err_msg) if k in nic_info: nic_info[k] = v else: raise exceptions.CommandError(err_msg) if not nic_info['net-id'] and not nic_info['port-id']: raise exceptions.CommandError(err_msg) nics.append(nic_info) hints = {} if args.scheduler_hints: for hint in args.scheduler_hints: key, _sep, value = hint.partition('=') # NOTE(vish): multiple copies of the same hint will # result in a list of values if key in hints: if isinstance(hints[key], six.string_types): hints[key] = [hints[key]] hints[key] += [value] else: hints[key] = value boot_args = [args.name, image, flavor] if str(args.config_drive).lower() in ("true", "1"): config_drive = True elif str(args.config_drive).lower() in ("false", "0", "", "none"): config_drive = None else: config_drive = args.config_drive boot_kwargs = dict( meta=meta, files=files, key_name=key_name, reservation_id=reservation_id, min_count=min_count, max_count=max_count, userdata=userdata, availability_zone=availability_zone, security_groups=security_groups, block_device_mapping=block_device_mapping, block_device_mapping_v2=block_device_mapping_v2, nics=nics, scheduler_hints=hints, config_drive=config_drive) return boot_args, boot_kwargs @utils.arg('--flavor', default=None, metavar='', help=_("Name or ID of flavor (see 'nova flavor-list').")) @utils.arg('--image', default=None, metavar='', help=_("Name or ID of image (see 'nova image-list'). ")) @utils.arg('--image-with', default=[], type=_key_value_pairing, action='append', metavar='', help=_("Image metadata property (see 'nova image-show'). ")) @utils.arg('--boot-volume', default=None, metavar="", help=_("Volume ID to boot from.")) @utils.arg('--snapshot', default=None, metavar="", help=_("Snapshot ID to boot from (will create a volume).")) @utils.arg('--num-instances', default=None, type=int, metavar='', help=_("boot multiple servers at a time (limited by quota).")) @utils.arg('--meta', metavar="", action='append', default=[], help=_("Record arbitrary key/value metadata to /meta.js " "on the new server. Can be specified multiple times.")) @utils.arg('--file', metavar="", action='append', dest='files', default=[], help=_("Store arbitrary files from locally to " "on the new server. You may store up to 5 files.")) @utils.arg('--key-name', metavar='', help=_("Key name of keypair that should be created earlier with \ the command keypair-add")) @utils.arg('--key_name', help=argparse.SUPPRESS) @utils.arg('name', metavar='', help=_('Name for the new server')) @utils.arg('--user-data', default=None, metavar='', help=_("user data file to pass to be exposed by the metadata server.")) @utils.arg('--user_data', help=argparse.SUPPRESS) @utils.arg('--availability-zone', default=None, metavar='', help=_("The availability zone for server placement.")) @utils.arg('--availability_zone', help=argparse.SUPPRESS) @utils.arg('--security-groups', default=None, metavar='', help=_("Comma separated list of security group names.")) @utils.arg('--security_groups', help=argparse.SUPPRESS) @utils.arg('--block-device-mapping', metavar="", action='append', default=[], help=_("Block device mapping in the format " "=:::.")) @utils.arg('--block_device_mapping', action='append', help=argparse.SUPPRESS) @utils.arg('--block-device', metavar="key1=value1[,key2=value2...]", action='append', default=[], help=_("Block device mapping with the keys: " "id=image_id, snapshot_id or volume_id, " "source=source type (image, snapshot, volume or blank), " "dest=destination type of the block device (volume or local), " "bus=device's bus, " "device=name of the device (e.g. vda, xda, ...), " "size=size of the block device in GB, " "format=device will be formatted (e.g. swap, ext3, ntfs, ...), " "bootindex=integer used for ordering the boot disks, " "type=device type (e.g. disk, cdrom, ...) and " "shutdown=shutdown behaviour (either preserve or remove).")) @utils.arg('--swap', metavar="", default=None, help=_("Create and attach a local swap block device of MB.")) @utils.arg('--ephemeral', metavar="size=[,format=]", action='append', default=[], help=_("Create and attach a local ephemeral block device of GB " "and format it to .")) @utils.arg('--hint', action='append', dest='scheduler_hints', default=[], metavar='', help=_("Send arbitrary key/value pairs to the scheduler for custom " "use.")) @utils.arg('--nic', metavar="", action='append', dest='nics', default=[], help=_("Create a NIC on the server. " "Specify option multiple times to create multiple NICs. " "net-id: attach NIC to network with this UUID " "(required if no port-id), " "v4-fixed-ip: IPv4 fixed address for NIC (optional), " "port-id: attach NIC to port with this UUID " "(required if no net-id)")) @utils.arg('--config-drive', metavar="", dest='config_drive', default=False, help=_("Enable config drive")) @utils.arg('--poll', dest='poll', action="store_true", default=False, help=_('Blocks while server builds so progress can be reported.')) def do_boot(cs, args): """Boot a new server.""" boot_args, boot_kwargs = _boot(cs, args) extra_boot_kwargs = utils.get_resource_manager_extra_kwargs(do_boot, args) boot_kwargs.update(extra_boot_kwargs) server = cs.servers.create(*boot_args, **boot_kwargs) _print_server(cs, args, server) if args.poll: _poll_for_status(cs.servers.get, server.id, 'building', ['active']) def do_cloudpipe_list(cs, _args): """Print a list of all cloudpipe instances.""" cloudpipes = cs.cloudpipe.list() columns = ['Project Id', "Public IP", "Public Port", "Internal IP"] utils.print_list(cloudpipes, columns) @utils.arg('project', metavar='', help=_('UUID of the project to create the cloudpipe for.')) def do_cloudpipe_create(cs, args): """Create a cloudpipe instance for the given project.""" cs.cloudpipe.create(args.project) @utils.arg('address', metavar='', help=_('New IP Address.')) @utils.arg('port', metavar='', help='New Port.') def do_cloudpipe_configure(cs, args): """Update the VPN IP/port of a cloudpipe instance.""" cs.cloudpipe.update(args.address, args.port) def _poll_for_status(poll_fn, obj_id, action, final_ok_states, poll_period=5, show_progress=True, status_field="status", silent=False): """Block while an action is being performed, periodically printing progress. """ def print_progress(progress): if show_progress: msg = (_('\rServer %(action)s... %(progress)s%% complete') % dict(action=action, progress=progress)) else: msg = _('\rServer %(action)s...') % dict(action=action) sys.stdout.write(msg) sys.stdout.flush() if not silent: print while True: obj = poll_fn(obj_id) status = getattr(obj, status_field) if status: status = status.lower() progress = getattr(obj, 'progress', None) or 0 if status in final_ok_states: if not silent: print_progress(100) print(_("\nFinished")) break elif status == "error": if not silent: print(_("\nError %s server") % action) break if not silent: print_progress(progress) time.sleep(poll_period) def _translate_keys(collection, convert): for item in collection: keys = item.__dict__.keys() for from_key, to_key in convert: if from_key in keys and to_key not in keys: setattr(item, to_key, item._info[from_key]) def _translate_extended_states(collection): power_states = [ 'NOSTATE', # 0x00 'Running', # 0x01 '', # 0x02 'Paused', # 0x03 'Shutdown', # 0x04 '', # 0x05 'Crashed', # 0x06 'Suspended' # 0x07 ] for item in collection: try: setattr(item, 'power_state', power_states[getattr(item, 'power_state')] ) except AttributeError: setattr(item, 'power_state', "N/A") try: getattr(item, 'task_state') except AttributeError: setattr(item, 'task_state', "N/A") def _translate_flavor_keys(collection): _translate_keys(collection, [('ram', 'memory_mb')]) def _print_flavor_extra_specs(flavor): try: return flavor.get_keys() except exceptions.NotFound: return "N/A" def _print_flavor_list(flavors, show_extra_specs=False): _translate_flavor_keys(flavors) headers = [ 'ID', 'Name', 'Memory_MB', 'Disk', 'Ephemeral', 'Swap', 'VCPUs', 'RXTX_Factor', 'Is_Public', ] if show_extra_specs: formatters = {'extra_specs': _print_flavor_extra_specs} headers.append('extra_specs') else: formatters = {} utils.print_list(flavors, headers, formatters) @utils.arg('--extra-specs', dest='extra_specs', action='store_true', default=False, help=_('Get extra-specs of each flavor.')) @utils.arg('--all', dest='all', action='store_true', default=False, help=_('Display all flavors (Admin only).')) def do_flavor_list(cs, args): """Print a list of available 'flavors' (sizes of servers).""" if args.all: flavors = cs.flavors.list(is_public=None) else: flavors = cs.flavors.list() _print_flavor_list(flavors, args.extra_specs) @utils.arg('flavor', metavar='', help=_("Name or ID of the flavor to delete")) def do_flavor_delete(cs, args): """Delete a specific flavor""" flavorid = _find_flavor(cs, args.flavor) cs.flavors.delete(flavorid) _print_flavor_list([flavorid]) @utils.arg('flavor', metavar='', help=_("Name or ID of flavor")) def do_flavor_show(cs, args): """Show details about the given flavor.""" flavor = _find_flavor(cs, args.flavor) _print_flavor(flavor) @utils.arg('name', metavar='', help=_("Name of the new flavor")) @utils.arg('id', metavar='', help=_("Unique ID (integer or UUID) for the new flavor." " If specifying 'auto', a UUID will be generated as id")) @utils.arg('ram', metavar='', help=_("Memory size in MB")) @utils.arg('disk', metavar='', help=_("Disk size in GB")) @utils.arg('--ephemeral', metavar='', help=_("Ephemeral space size in GB (default 0)"), default=0) @utils.arg('vcpus', metavar='', help=_("Number of vcpus")) @utils.arg('--swap', metavar='', help=_("Swap space size in MB (default 0)"), default=0) @utils.arg('--rxtx-factor', metavar='', help=_("RX/TX factor (default 1)"), default=1.0) @utils.arg('--is-public', metavar='', help=_("Make flavor accessible to the public (default true)"), type=lambda v: strutils.bool_from_string(v, True), default=True) def do_flavor_create(cs, args): """Create a new flavor""" f = cs.flavors.create(args.name, args.ram, args.vcpus, args.disk, args.id, args.ephemeral, args.swap, args.rxtx_factor, args.is_public) _print_flavor_list([f]) @utils.arg('flavor', metavar='', help=_("Name or ID of flavor")) @utils.arg('action', metavar='', choices=['set', 'unset'], help=_("Actions: 'set' or 'unset'")) @utils.arg('metadata', metavar='', nargs='+', action='append', default=[], help=_('Extra_specs to set/unset (only key is necessary on unset)')) def do_flavor_key(cs, args): """Set or unset extra_spec for a flavor.""" flavor = _find_flavor(cs, args.flavor) keypair = _extract_metadata(args) if args.action == 'set': flavor.set_keys(keypair) elif args.action == 'unset': flavor.unset_keys(keypair.keys()) @utils.arg('--flavor', metavar='', help=_("Filter results by flavor name or ID.")) @utils.arg('--tenant', metavar='', help=_('Filter results by tenant ID.')) def do_flavor_access_list(cs, args): """Print access information about the given flavor.""" if args.flavor and args.tenant: raise exceptions.CommandError(_("Unable to filter results by " "both --flavor and --tenant.")) elif args.flavor: flavor = _find_flavor(cs, args.flavor) if flavor.is_public: raise exceptions.CommandError(_("Failed to get access list " "for public flavor type.")) kwargs = {'flavor': flavor} elif args.tenant: kwargs = {'tenant': args.tenant} else: raise exceptions.CommandError(_("Unable to get all access lists. " "Specify --flavor or --tenant")) try: access_list = cs.flavor_access.list(**kwargs) except NotImplementedError as e: raise exceptions.CommandError("%s" % str(e)) columns = ['Flavor_ID', 'Tenant_ID'] utils.print_list(access_list, columns) @utils.arg('flavor', metavar='', help=_("Flavor name or ID to add access for the given tenant.")) @utils.arg('tenant', metavar='', help=_('Tenant ID to add flavor access for.')) def do_flavor_access_add(cs, args): """Add flavor access for the given tenant.""" flavor = _find_flavor_for_admin(cs, args.flavor) access_list = cs.flavor_access.add_tenant_access(flavor, args.tenant) columns = ['Flavor_ID', 'Tenant_ID'] utils.print_list(access_list, columns) @utils.arg('flavor', metavar='', help=_("Flavor name or ID to remove access for the given tenant.")) @utils.arg('tenant', metavar='', help=_('Tenant ID to remove flavor access for.')) def do_flavor_access_remove(cs, args): """Remove flavor access for the given tenant.""" flavor = _find_flavor_for_admin(cs, args.flavor) access_list = cs.flavor_access.remove_tenant_access(flavor, args.tenant) columns = ['Flavor_ID', 'Tenant_ID'] utils.print_list(access_list, columns) @utils.arg('project_id', metavar='', help=_('The ID of the project.')) def do_scrub(cs, args): """Delete data associated with the project.""" networks_list = cs.networks.list() networks_list = [network for network in networks_list if getattr(network, 'project_id', '') == args.project_id] search_opts = {'all_tenants': 1} groups = cs.security_groups.list(search_opts) groups = [group for group in groups if group.tenant_id == args.project_id] for network in networks_list: cs.networks.disassociate(network) for group in groups: cs.security_groups.delete(group) def do_network_list(cs, _args): """Print a list of available networks.""" network_list = cs.networks.list() columns = ['ID', 'Label', 'Cidr'] utils.print_list(network_list, columns) @utils.arg('network', metavar='', help=_("uuid or label of network")) def do_network_show(cs, args): """Show details about the given network.""" network = utils.find_resource(cs.networks, args.network) utils.print_dict(network._info) @utils.arg('--host-only', dest='host_only', metavar='<0|1>', nargs='?', type=int, const=1, default=0) @utils.arg('--project-only', dest='project_only', metavar='<0|1>', nargs='?', type=int, const=1, default=0) @utils.arg('network', metavar='', help="uuid of network") def do_network_disassociate(cs, args): """Disassociate host and/or project from the given network.""" if args.host_only: cs.networks.disassociate(args.network, True, False) elif args.project_only: cs.networks.disassociate(args.network, False, True) else: cs.networks.disassociate(args.network, True, True) @utils.arg('network', metavar='', help="uuid of network") @utils.arg('host', metavar='', help="Name of host") def do_network_associate_host(cs, args): """Associate host with network.""" cs.networks.associate_host(args.network, args.host) @utils.arg('network', metavar='', help="uuid of network") def do_network_associate_project(cs, args): """Associate project with network.""" cs.networks.associate_project(args.network) def _filter_network_create_options(args): valid_args = ['label', 'cidr', 'vlan_start', 'vpn_start', 'cidr_v6', 'gateway', 'gateway_v6', 'bridge', 'bridge_interface', 'multi_host', 'dns1', 'dns2', 'uuid', 'fixed_cidr', 'project_id', 'priority'] kwargs = {} for k, v in args.__dict__.items(): if k in valid_args and v is not None: kwargs[k] = v return kwargs @utils.arg('label', metavar='', help=_("Label for network")) @utils.arg('--fixed-range-v4', dest='cidr', metavar='', help=_("IPv4 subnet (ex: 10.0.0.0/8)")) @utils.arg('--fixed-range-v6', dest="cidr_v6", help=_('IPv6 subnet (ex: fe80::/64')) @utils.arg('--vlan', dest='vlan_start', metavar='', help=_("vlan id")) @utils.arg('--vpn', dest='vpn_start', metavar='', help=_("vpn start")) @utils.arg('--gateway', dest="gateway", help=_('gateway')) @utils.arg('--gateway-v6', dest="gateway_v6", help=_('ipv6 gateway')) @utils.arg('--bridge', dest="bridge", metavar='', help=_('VIFs on this network are connected to this bridge')) @utils.arg('--bridge-interface', dest="bridge_interface", metavar='', help=_('the bridge is connected to this interface')) @utils.arg('--multi-host', dest="multi_host", metavar="<'T'|'F'>", help=_('Multi host')) @utils.arg('--dns1', dest="dns1", metavar="", help='First DNS') @utils.arg('--dns2', dest="dns2", metavar="", help=_('Second DNS')) @utils.arg('--uuid', dest="uuid", metavar="", help=_('Network UUID')) @utils.arg('--fixed-cidr', dest="fixed_cidr", metavar='', help=_('IPv4 subnet for fixed IPS (ex: 10.20.0.0/16)')) @utils.arg('--project-id', dest="project_id", metavar="", help=_('Project id')) @utils.arg('--priority', dest="priority", metavar="", help=_('Network interface priority')) def do_network_create(cs, args): """Create a network.""" if not (args.cidr or args.cidr_v6): raise exceptions.CommandError( _("Must specify eith fixed_range_v4 or fixed_range_v6")) kwargs = _filter_network_create_options(args) if args.multi_host is not None: kwargs['multi_host'] = bool(args.multi_host == 'T' or strutils.bool_from_string(args.multi_host)) cs.networks.create(**kwargs) @utils.arg('--limit', dest="limit", metavar="", help=_('number of images to return per request')) def do_image_list(cs, _args): """Print a list of available images to boot from.""" limit = _args.limit image_list = cs.images.list(limit=limit) def parse_server_name(image): try: return image.server['id'] except (AttributeError, KeyError): return '' fmts = {'Server': parse_server_name} utils.print_list(image_list, ['ID', 'Name', 'Status', 'Server'], fmts, sortby_index=1) @utils.arg('image', metavar='', help=_("Name or ID of image")) @utils.arg('action', metavar='', choices=['set', 'delete'], help=_("Actions: 'set' or 'delete'")) @utils.arg('metadata', metavar='', nargs='+', action='append', default=[], help=_('Metadata to add/update or delete (only key is necessary on ' 'delete)')) def do_image_meta(cs, args): """Set or Delete metadata on an image.""" image = _find_image(cs, args.image) metadata = _extract_metadata(args) if args.action == 'set': cs.images.set_meta(image, metadata) elif args.action == 'delete': cs.images.delete_meta(image, metadata.keys()) def _extract_metadata(args): metadata = {} for metadatum in args.metadata[0]: # Can only pass the key in on 'delete' # So this doesn't have to have '=' if metadatum.find('=') > -1: (key, value) = metadatum.split('=', 1) else: key = metadatum value = None metadata[key] = value return metadata def _print_image(image): info = image._info.copy() # ignore links, we don't need to present those info.pop('links') # try to replace a server entity to just an id server = info.pop('server', None) try: info['server'] = server['id'] except (KeyError, TypeError): pass # break up metadata and display each on its own row metadata = info.pop('metadata', {}) try: for key, value in metadata.items(): _key = 'metadata %s' % key info[_key] = value except AttributeError: pass utils.print_dict(info) def _print_flavor(flavor): info = flavor._info.copy() # ignore links, we don't need to present those info.pop('links') info.update({"extra_specs": _print_flavor_extra_specs(flavor)}) utils.print_dict(info) @utils.arg('image', metavar='', help=_("Name or ID of image")) def do_image_show(cs, args): """Show details about the given image.""" image = _find_image(cs, args.image) _print_image(image) @utils.arg('image', metavar='', nargs='+', help=_('Name or ID of image(s).')) def do_image_delete(cs, args): """Delete specified image(s).""" for image in args.image: try: _find_image(cs, image).delete() except Exception as e: print(_("Delete for image %(image)s failed: %(e)s") % {'image': image, 'e': e}) @utils.arg('--reservation-id', dest='reservation_id', metavar='', default=None, help=_('Only return servers that match reservation-id.')) @utils.arg('--reservation_id', help=argparse.SUPPRESS) @utils.arg('--ip', dest='ip', metavar='', default=None, help=_('Search with regular expression match by IP address (Admin only).')) @utils.arg('--ip6', dest='ip6', metavar='', default=None, help=_('Search with regular expression match by IPv6 address ' '(Admin only).')) @utils.arg('--name', dest='name', metavar='', default=None, help=_('Search with regular expression match by name')) @utils.arg('--instance-name', dest='instance_name', metavar='', default=None, help=_('Search with regular expression match by server name ' '(Admin only).')) @utils.arg('--instance_name', help=argparse.SUPPRESS) @utils.arg('--status', dest='status', metavar='', default=None, help=_('Search by server status')) @utils.arg('--flavor', dest='flavor', metavar='', default=None, help=_('Search by flavor name or ID')) @utils.arg('--image', dest='image', metavar='', default=None, help=_('Search by image name or ID')) @utils.arg('--host', dest='host', metavar='', default=None, help=_('Search servers by hostname to which they are assigned (Admin ' 'only).')) @utils.arg('--all-tenants', dest='all_tenants', metavar='<0|1>', nargs='?', type=int, const=1, default=int(strutils.bool_from_string( os.environ.get("ALL_TENANTS", 'false'), True)), help=_('Display information from all tenants (Admin only).')) @utils.arg('--all_tenants', nargs='?', type=int, const=1, help=argparse.SUPPRESS) @utils.arg('--tenant', #nova db searches by project_id dest='tenant', metavar='', nargs='?', help=_('Display information from single tenant (Admin only).')) @utils.arg('--deleted', dest='deleted', action="store_true", default=False, help='Only display deleted servers (Admin only).') @utils.arg('--fields', default=None, metavar='', help=_('Comma-separated list of fields to display. ' 'Use the show command to see which fields are available.')) @utils.arg('--minimal', dest='minimal', action="store_true", default=False, help=_('Get only uuid and name.')) def do_list(cs, args): """List active servers.""" imageid = None flavorid = None if args.image: imageid = _find_image(cs, args.image).id if args.flavor: flavorid = _find_flavor(cs, args.flavor).id search_opts = { 'all_tenants': args.all_tenants, 'reservation_id': args.reservation_id, 'ip': args.ip, 'ip6': args.ip6, 'name': args.name, 'image': imageid, 'flavor': flavorid, 'status': args.status, 'tenant_id': args.tenant, 'host': args.host, 'deleted': args.deleted, 'instance_name': args.instance_name} filters = {'flavor': lambda f: f['id'], 'security_groups': utils._format_security_groups} formatters = {} field_titles = [] if args.fields: for field in args.fields.split(','): field_title, formatter = utils._make_field_formatter(field, filters) field_titles.append(field_title) formatters[field_title] = formatter id_col = 'ID' detailed = not args.minimal servers = cs.servers.list(detailed=detailed, search_opts=search_opts) convert = [('OS-EXT-SRV-ATTR:host', 'host'), ('OS-EXT-STS:task_state', 'task_state'), ('OS-EXT-SRV-ATTR:instance_name', 'instance_name'), ('OS-EXT-STS:power_state', 'power_state'), ('hostId', 'host_id')] _translate_keys(servers, convert) _translate_extended_states(servers) if args.minimal: columns = [ id_col, 'Name'] elif field_titles: columns = [id_col] + field_titles else: columns = [ id_col, 'Name', 'Status', 'Task State', 'Power State', 'Networks' ] formatters['Networks'] = utils._format_servers_list_networks utils.print_list(servers, columns, formatters, sortby_index=1) @utils.arg('--hard', dest='reboot_type', action='store_const', const=servers.REBOOT_HARD, default=servers.REBOOT_SOFT, help=_('Perform a hard reboot (instead of a soft one).')) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg('--poll', dest='poll', action="store_true", default=False, help=_('Blocks while server is rebooting.')) def do_reboot(cs, args): """Reboot a server.""" server = _find_server(cs, args.server) server.reboot(args.reboot_type) if args.poll: _poll_for_status(cs.servers.get, server.id, 'rebooting', ['active'], show_progress=False) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg('image', metavar='', help=_("Name or ID of new image.")) @utils.arg('--rebuild-password', dest='rebuild_password', metavar='', default=False, help=_("Set the provided password on the rebuild server.")) @utils.arg('--rebuild_password', help=argparse.SUPPRESS) @utils.arg('--poll', dest='poll', action="store_true", default=False, help=_('Blocks while server rebuilds so progress can be reported.')) @utils.arg('--minimal', dest='minimal', action="store_true", default=False, help=_('Skips flavor/image lookups when showing servers')) @utils.arg('--preserve-ephemeral', action="store_true", default=False, help='Preserve the default ephemeral storage partition on rebuild.') def do_rebuild(cs, args): """Shutdown, re-image, and re-boot a server.""" server = _find_server(cs, args.server) image = _find_image(cs, args.image) if args.rebuild_password is not False: _password = args.rebuild_password else: _password = None kwargs = utils.get_resource_manager_extra_kwargs(do_rebuild, args) kwargs['preserve_ephemeral'] = args.preserve_ephemeral server.rebuild(image, _password, **kwargs) _print_server(cs, args) if args.poll: _poll_for_status(cs.servers.get, server.id, 'rebuilding', ['active']) @utils.arg('server', metavar='', help=_('Name (old name) or ID of server.')) @utils.arg('name', metavar='', help=_('New name for the server.')) def do_rename(cs, args): """Rename a server.""" _find_server(cs, args.server).update(name=args.name) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg('flavor', metavar='', help=_("Name or ID of new flavor.")) @utils.arg('--poll', dest='poll', action="store_true", default=False, help=_('Blocks while servers resizes so progress can be reported.')) def do_resize(cs, args): """Resize a server.""" server = _find_server(cs, args.server) flavor = _find_flavor(cs, args.flavor) kwargs = utils.get_resource_manager_extra_kwargs(do_resize, args) server.resize(flavor, **kwargs) if args.poll: _poll_for_status(cs.servers.get, server.id, 'resizing', ['active', 'verify_resize']) @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_resize_confirm(cs, args): """Confirm a previous resize.""" _find_server(cs, args.server).confirm_resize() @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_resize_revert(cs, args): """Revert a previous resize (and return to the previous VM).""" _find_server(cs, args.server).revert_resize() @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg('--poll', dest='poll', action="store_true", default=False, help=_('Blocks while server migrates so progress can be reported.')) def do_migrate(cs, args): """Migrate a server. The new host will be selected by the scheduler.""" server = _find_server(cs, args.server) server.migrate() if args.poll: _poll_for_status(cs.servers.get, server.id, 'migrating', ['active', 'verify_resize']) @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_pause(cs, args): """Pause a server.""" _find_server(cs, args.server).pause() @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_unpause(cs, args): """Unpause a server.""" _find_server(cs, args.server).unpause() @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_stop(cs, args): """Stop a server.""" _find_server(cs, args.server).stop() @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_start(cs, args): """Start a server.""" _find_server(cs, args.server).start() @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_lock(cs, args): """Lock a server.""" _find_server(cs, args.server).lock() @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_unlock(cs, args): """Unlock a server.""" _find_server(cs, args.server).unlock() @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_suspend(cs, args): """Suspend a server.""" _find_server(cs, args.server).suspend() @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_resume(cs, args): """Resume a server.""" _find_server(cs, args.server).resume() @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_rescue(cs, args): """Rescue a server.""" utils.print_dict(_find_server(cs, args.server).rescue()[1]) @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_unrescue(cs, args): """Unrescue a server.""" _find_server(cs, args.server).unrescue() @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_shelve(cs, args): """Shelve a server.""" _find_server(cs, args.server).shelve() @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_shelve_offload(cs, args): """Remove a shelved server from the compute node.""" _find_server(cs, args.server).shelve_offload() @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_unshelve(cs, args): """Unshelve a server.""" _find_server(cs, args.server).unshelve() @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_diagnostics(cs, args): """Retrieve server diagnostics.""" server = _find_server(cs, args.server) utils.print_dict(cs.servers.diagnostics(server)[1], wrap=80) @utils.arg('server', metavar='', help=_('Name or ID of a server for which the network cache should ' 'be refreshed from neutron (Admin only).')) def do_refresh_network(cs, args): """Refresh server network information.""" server = _find_server(cs, args.server) cs.server_external_events.create([{'server_uuid': server.id, 'name': 'network-changed'}]) @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_root_password(cs, args): """ Change the root password for a server. """ server = _find_server(cs, args.server) p1 = getpass.getpass('New password: ') p2 = getpass.getpass('Again: ') if p1 != p2: raise exceptions.CommandError(_("Passwords do not match.")) server.change_password(p1) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg('name', metavar='', help=_('Name of snapshot.')) @utils.arg('--show', dest='show', action="store_true", default=False, help=_('Print image info.')) @utils.arg('--poll', dest='poll', action="store_true", default=False, help=_('Blocks while server snapshots so progress can be reported.')) def do_image_create(cs, args): """Create a new image by taking a snapshot of a running server.""" server = _find_server(cs, args.server) image_uuid = cs.servers.create_image(server, args.name) if args.poll: _poll_for_status(cs.images.get, image_uuid, 'snapshotting', ['active']) # NOTE(sirp): A race-condition exists between when the image finishes # uploading and when the servers's `task_state` is cleared. To account # for this, we need to poll a second time to ensure the `task_state` is # cleared before returning, ensuring that a snapshot taken immediately # after this function returns will succeed. # # A better long-term solution will be to separate 'snapshotting' and # 'image-uploading' in Nova and clear the task-state once the VM # snapshot is complete but before the upload begins. task_state_field = "OS-EXT-STS:task_state" if hasattr(server, task_state_field): _poll_for_status(cs.servers.get, server.id, 'image_snapshot', [None], status_field=task_state_field, show_progress=False, silent=True) if args.show: _print_image(cs.images.get(image_uuid)) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg('name', metavar='', help=_('Name of the backup image.')) @utils.arg('backup_type', metavar='', help=_('The backup type, like "daily" or "weekly".')) @utils.arg('rotation', metavar='', help=_('Int parameter representing how many backups to keep ' 'around.')) def do_backup(cs, args): """Backup a server by creating a 'backup' type snapshot.""" _find_server(cs, args.server).backup(args.name, args.backup_type, args.rotation) @utils.arg('server', metavar='', help=_("Name or ID of server")) @utils.arg('action', metavar='', choices=['set', 'delete'], help=_("Actions: 'set' or 'delete'")) @utils.arg('metadata', metavar='', nargs='+', action='append', default=[], help=_('Metadata to set or delete (only key is necessary on delete)')) def do_meta(cs, args): """Set or Delete metadata on a server.""" server = _find_server(cs, args.server) metadata = _extract_metadata(args) if args.action == 'set': cs.servers.set_meta(server, metadata) elif args.action == 'delete': cs.servers.delete_meta(server, sorted(metadata.keys(), reverse=True)) def _print_server(cs, args, server=None): # By default when searching via name we will do a # findall(name=blah) and due a REST /details which is not the same # as a .get() and doesn't get the information about flavors and # images. This fix it as we redo the call with the id which does a # .get() to get all informations. if not server: server = _find_server(cs, args.server) minimal = getattr(args, "minimal", False) networks = server.networks info = server._info.copy() for network_label, address_list in networks.items(): info['%s network' % network_label] = ', '.join(address_list) flavor = info.get('flavor', {}) flavor_id = flavor.get('id', '') if minimal: info['flavor'] = flavor_id else: info['flavor'] = '%s (%s)' % (_find_flavor(cs, flavor_id).name, flavor_id) if 'security_groups' in info: info['security_groups'] = \ ', '.join(group['name'] for group in info['security_groups']) image = info.get('image', {}) if image: image_id = image.get('id', '') if minimal: info['image'] = image_id else: try: info['image'] = '%s (%s)' % (_find_image(cs, image_id).name, image_id) except Exception: info['image'] = '%s (%s)' % (_("Image not found"), image_id) else: # Booted from volume info['image'] = _("Attempt to boot from volume - no image supplied") info.pop('links', None) info.pop('addresses', None) utils.print_dict(info) @utils.arg('--minimal', dest='minimal', action="store_true", default=False, help=_('Skips flavor/image lookups when showing servers')) @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_show(cs, args): """Show details about the given server.""" _print_server(cs, args) @utils.arg('server', metavar='', nargs='+', help=_('Name or ID of server(s).')) def do_delete(cs, args): """Immediately shut down and delete specified server(s).""" failure_count = 0 for server in args.server: try: _find_server(cs, server).delete() except Exception as e: failure_count += 1 print(e) if failure_count == len(args.server): raise exceptions.CommandError(_("Unable to delete any of the " "specified servers.")) def _find_server(cs, server): """Get a server by name or ID.""" return utils.find_resource(cs.servers, server) def _find_image(cs, image): """Get an image by name or ID.""" return utils.find_resource(cs.images, image) def _find_flavor_for_admin(cs, flavor): """Get a flavor for administrator by name, ID, or RAM size.""" try: return utils.find_resource(cs.flavors, flavor, is_public=None) except exceptions.NotFound: return cs.flavors.find(ram=flavor) def _find_flavor(cs, flavor): """Get a flavor by name, ID, or RAM size.""" try: return utils.find_resource(cs.flavors, flavor) except exceptions.NotFound: return cs.flavors.find(ram=flavor) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg('network_id', metavar='', help='Network ID.') def do_add_fixed_ip(cs, args): """Add new IP address on a network to server.""" server = _find_server(cs, args.server) server.add_fixed_ip(args.network_id) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg('address', metavar='
', help=_('IP Address.')) def do_remove_fixed_ip(cs, args): """Remove an IP address from a server.""" server = _find_server(cs, args.server) server.remove_fixed_ip(args.address) def _find_volume(cs, volume): """Get a volume by name or ID.""" return utils.find_resource(cs.volumes, volume) def _find_volume_snapshot(cs, snapshot): """Get a volume snapshot by name or ID.""" return utils.find_resource(cs.volume_snapshots, snapshot) def _print_volume(volume): utils.print_dict(volume._info) def _print_volume_snapshot(snapshot): utils.print_dict(snapshot._info) def _translate_volume_keys(collection): _translate_keys(collection, [('displayName', 'display_name'), ('volumeType', 'volume_type')]) def _translate_volume_snapshot_keys(collection): _translate_keys(collection, [('displayName', 'display_name'), ('volumeId', 'volume_id')]) def _translate_availability_zone_keys(collection): _translate_keys(collection, [('zoneName', 'name'), ('zoneState', 'status')]) @utils.arg('--all-tenants', dest='all_tenants', metavar='<0|1>', nargs='?', type=int, const=1, default=int(strutils.bool_from_string( os.environ.get("ALL_TENANTS", 'false'), True)), help=_('Display information from all tenants (Admin only).')) @utils.arg('--all_tenants', nargs='?', type=int, const=1, help=argparse.SUPPRESS) @utils.service_type('volume') def do_volume_list(cs, args): """List all the volumes.""" search_opts = {'all_tenants': args.all_tenants} volumes = cs.volumes.list(search_opts=search_opts) _translate_volume_keys(volumes) # Create a list of servers to which the volume is attached for vol in volumes: servers = [s.get('server_id') for s in vol.attachments] setattr(vol, 'attached_to', ','.join(map(str, servers))) utils.print_list(volumes, ['ID', 'Status', 'Display Name', 'Size', 'Volume Type', 'Attached to']) @utils.arg('volume', metavar='', help=_('Name or ID of the volume.')) @utils.service_type('volume') def do_volume_show(cs, args): """Show details about a volume.""" volume = _find_volume(cs, args.volume) _print_volume(volume) @utils.arg('size', metavar='', type=int, help=_('Size of volume in GB')) @utils.arg('--snapshot-id', metavar='', default=None, help=_('Optional snapshot id to create the volume from. (Default=None)')) @utils.arg('--snapshot_id', help=argparse.SUPPRESS) @utils.arg('--image-id', metavar='', help=_('Optional image id to create the volume from. (Default=None)'), default=None) @utils.arg('--display-name', metavar='', default=None, help=_('Optional volume name. (Default=None)')) @utils.arg('--display_name', help=argparse.SUPPRESS) @utils.arg('--display-description', metavar='', default=None, help=_('Optional volume description. (Default=None)')) @utils.arg('--display_description', help=argparse.SUPPRESS) @utils.arg('--volume-type', metavar='', default=None, help=_('Optional volume type. (Default=None)')) @utils.arg('--volume_type', help=argparse.SUPPRESS) @utils.arg('--availability-zone', metavar='', help=_('Optional Availability Zone for volume. (Default=None)'), default=None) @utils.service_type('volume') def do_volume_create(cs, args): """Add a new volume.""" volume = cs.volumes.create(args.size, args.snapshot_id, args.display_name, args.display_description, args.volume_type, args.availability_zone, imageRef=args.image_id) _print_volume(volume) @utils.arg('volume', metavar='', nargs='+', help=_('Name or ID of the volume(s) to delete.')) @utils.service_type('volume') def do_volume_delete(cs, args): """Remove volume(s).""" for volume in args.volume: try: _find_volume(cs, volume).delete() except Exception as e: print(_("Delete for volume %(volume)s failed: %(e)s") % {'volume': volume, 'e': e}) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg('volume', metavar='', help=_('ID of the volume to attach.')) @utils.arg('device', metavar='', default=None, nargs='?', help=_('Name of the device e.g. /dev/vdb. ' 'Use "auto" for autoassign (if supported)')) def do_volume_attach(cs, args): """Attach a volume to a server.""" if args.device == 'auto': args.device = None volume = cs.volumes.create_server_volume(_find_server(cs, args.server).id, args.volume, args.device) _print_volume(volume) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg('attachment_id', metavar='', help=_('Attachment ID of the volume.')) @utils.arg('new_volume', metavar='', help=_('ID of the volume to attach.')) def do_volume_update(cs, args): """Update volume attachment.""" volume = cs.volumes.update_server_volume(_find_server(cs, args.server).id, args.attachment_id, args.new_volume) _print_volume(volume) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg('attachment_id', metavar='', help=_('Attachment ID of the volume.')) def do_volume_detach(cs, args): """Detach a volume from a server.""" cs.volumes.delete_server_volume(_find_server(cs, args.server).id, args.attachment_id) @utils.service_type('volume') def do_volume_snapshot_list(cs, _args): """List all the snapshots.""" snapshots = cs.volume_snapshots.list() _translate_volume_snapshot_keys(snapshots) utils.print_list(snapshots, ['ID', 'Volume ID', 'Status', 'Display Name', 'Size']) @utils.arg('snapshot', metavar='', help=_('Name or ID of the snapshot.')) @utils.service_type('volume') def do_volume_snapshot_show(cs, args): """Show details about a snapshot.""" snapshot = _find_volume_snapshot(cs, args.snapshot) _print_volume_snapshot(snapshot) @utils.arg('volume_id', metavar='', help=_('ID of the volume to snapshot')) @utils.arg('--force', metavar='', help=_('Optional flag to indicate whether to snapshot a volume even if ' 'its attached to a server. (Default=False)'), default=False) @utils.arg('--display-name', metavar='', default=None, help=_('Optional snapshot name. (Default=None)')) @utils.arg('--display_name', help=argparse.SUPPRESS) @utils.arg('--display-description', metavar='', default=None, help=_('Optional snapshot description. (Default=None)')) @utils.arg('--display_description', help=argparse.SUPPRESS) @utils.service_type('volume') def do_volume_snapshot_create(cs, args): """Add a new snapshot.""" snapshot = cs.volume_snapshots.create(args.volume_id, args.force, args.display_name, args.display_description) _print_volume_snapshot(snapshot) @utils.arg('snapshot', metavar='', help=_('Name or ID of the snapshot to delete.')) @utils.service_type('volume') def do_volume_snapshot_delete(cs, args): """Remove a snapshot.""" snapshot = _find_volume_snapshot(cs, args.snapshot) snapshot.delete() def _print_volume_type_list(vtypes): utils.print_list(vtypes, ['ID', 'Name']) @utils.service_type('volume') def do_volume_type_list(cs, args): """Print a list of available 'volume types'.""" vtypes = cs.volume_types.list() _print_volume_type_list(vtypes) @utils.arg('name', metavar='', help=_("Name of the new flavor")) @utils.service_type('volume') def do_volume_type_create(cs, args): """Create a new volume type.""" vtype = cs.volume_types.create(args.name) _print_volume_type_list([vtype]) @utils.arg('id', metavar='', help=_("Unique ID of the volume type to delete")) @utils.service_type('volume') def do_volume_type_delete(cs, args): """Delete a specific flavor""" cs.volume_types.delete(args.id) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg('console_type', metavar='', help=_('Type of vnc console ("novnc" or "xvpvnc").')) def do_get_vnc_console(cs, args): """Get a vnc console to a server.""" server = _find_server(cs, args.server) data = server.get_vnc_console(args.console_type) class VNCConsole: def __init__(self, console_dict): self.type = console_dict['type'] self.url = console_dict['url'] utils.print_list([VNCConsole(data['console'])], ['Type', 'Url']) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg('console_type', metavar='', help=_('Type of spice console ("spice-html5").')) def do_get_spice_console(cs, args): """Get a spice console to a server.""" server = _find_server(cs, args.server) data = server.get_spice_console(args.console_type) class SPICEConsole: def __init__(self, console_dict): self.type = console_dict['type'] self.url = console_dict['url'] utils.print_list([SPICEConsole(data['console'])], ['Type', 'Url']) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg('console_type', metavar='', help='Type of rdp console ("rdp-html5").') def do_get_rdp_console(cs, args): """Get a rdp console to a server.""" server = _find_server(cs, args.server) data = server.get_rdp_console(args.console_type) class RDPConsole: def __init__(self, console_dict): self.type = console_dict['type'] self.url = console_dict['url'] utils.print_list([RDPConsole(data['console'])], ['Type', 'Url']) @utils.arg('server', metavar='', help='Name or ID of server.') @utils.arg('private_key', metavar='', help=_('Private key (used locally to decrypt password) (Optional). ' 'When specified, the command displays the clear (decrypted) VM ' 'password. When not specified, the ciphered VM password is ' 'displayed.'), nargs='?', default=None) def do_get_password(cs, args): """Get password for a server.""" server = _find_server(cs, args.server) data = server.get_password(args.private_key) print(data) @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_clear_password(cs, args): """Clear password for a server.""" server = _find_server(cs, args.server) server.clear_password() def _print_floating_ip_list(floating_ips): utils.print_list(floating_ips, ['Ip', 'Server Id', 'Fixed Ip', 'Pool']) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg('--length', metavar='', default=None, help=_('Length in lines to tail.')) def do_console_log(cs, args): """Get console log output of a server.""" server = _find_server(cs, args.server) data = server.get_console_output(length=args.length) print(data) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg('address', metavar='
', help=_('IP Address.')) @utils.arg('--fixed-address', metavar='', default=None, help=_('Fixed IP Address to associate with.')) def do_add_floating_ip(cs, args): """DEPRECATED, use floating-ip-associate instead.""" _associate_floating_ip(cs, args) @utils.arg('server', metavar='', help='Name or ID of server.') @utils.arg('address', metavar='
', help='IP Address.') @utils.arg('--fixed-address', metavar='', default=None, help='Fixed IP Address to associate with.') def do_floating_ip_associate(cs, args): """Associate a floating IP address to a server.""" _associate_floating_ip(cs, args) def _associate_floating_ip(cs, args): server = _find_server(cs, args.server) server.add_floating_ip(args.address, args.fixed_address) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg('address', metavar='
', help=_('IP Address.')) def do_remove_floating_ip(cs, args): """DEPRECATED, use floating-ip-disassociate instead.""" _disassociate_floating_ip(cs, args) @utils.arg('server', metavar='', help='Name or ID of server.') @utils.arg('address', metavar='
', help='IP Address.') def do_floating_ip_disassociate(cs, args): """Disassociate a floating IP address from a server.""" _disassociate_floating_ip(cs, args) def _disassociate_floating_ip(cs, args): server = _find_server(cs, args.server) server.remove_floating_ip(args.address) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg('secgroup', metavar='', help=_('Name of Security Group.')) def do_add_secgroup(cs, args): """Add a Security Group to a server.""" server = _find_server(cs, args.server) server.add_security_group(args.secgroup) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg('secgroup', metavar='', help=_('Name of Security Group.')) def do_remove_secgroup(cs, args): """Remove a Security Group from a server.""" server = _find_server(cs, args.server) server.remove_security_group(args.secgroup) @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_list_secgroup(cs, args): """List Security Group(s) of a server.""" server = _find_server(cs, args.server) groups = server.list_security_group() _print_secgroups(groups) @utils.arg('pool', metavar='', help=_('Name of Floating IP Pool. (Optional)'), nargs='?', default=None) def do_floating_ip_create(cs, args): """Allocate a floating IP for the current tenant.""" _print_floating_ip_list([cs.floating_ips.create(pool=args.pool)]) @utils.arg('address', metavar='
', help=_('IP of Floating Ip.')) def do_floating_ip_delete(cs, args): """De-allocate a floating IP.""" floating_ips = cs.floating_ips.list() for floating_ip in floating_ips: if floating_ip.ip == args.address: return cs.floating_ips.delete(floating_ip.id) raise exceptions.CommandError(_("Floating ip %s not found.") % args.address) def do_floating_ip_list(cs, _args): """List floating ips for this tenant.""" _print_floating_ip_list(cs.floating_ips.list()) def do_floating_ip_pool_list(cs, _args): """List all floating ip pools.""" utils.print_list(cs.floating_ip_pools.list(), ['name']) @utils.arg('--host', dest='host', metavar='', default=None, help=_('Filter by host')) def do_floating_ip_bulk_list(cs, args): """List all floating ips.""" utils.print_list(cs.floating_ips_bulk.list(args.host), ['project_id', 'address', 'instance_uuid', 'pool', 'interface']) @utils.arg('ip_range', metavar='', help=_('Address range to create')) @utils.arg('--pool', dest='pool', metavar='', default=None, help=_('Pool for new Floating IPs')) @utils.arg('--interface', metavar='', default=None, help=_('Interface for new Floating IPs')) def do_floating_ip_bulk_create(cs, args): """Bulk create floating ips by range.""" cs.floating_ips_bulk.create(args.ip_range, args.pool, args.interface) @utils.arg('ip_range', metavar='', help=_('Address range to delete')) def do_floating_ip_bulk_delete(cs, args): """Bulk delete floating ips by range.""" cs.floating_ips_bulk.delete(args.ip_range) def _print_dns_list(dns_entries): utils.print_list(dns_entries, ['ip', 'name', 'domain']) def _print_domain_list(domain_entries): utils.print_list(domain_entries, ['domain', 'scope', 'project', 'availability_zone']) def do_dns_domains(cs, args): """Print a list of available dns domains.""" domains = cs.dns_domains.domains() _print_domain_list(domains) @utils.arg('domain', metavar='', help=_('DNS domain')) @utils.arg('--ip', metavar='', help=_('ip address'), default=None) @utils.arg('--name', metavar='', help=_('DNS name'), default=None) def do_dns_list(cs, args): """List current DNS entries for domain and ip or domain and name.""" if not (args.ip or args.name): raise exceptions.CommandError( _("You must specify either --ip or --name")) if args.name: entry = cs.dns_entries.get(args.domain, args.name) _print_dns_list([entry]) else: entries = cs.dns_entries.get_for_ip(args.domain, ip=args.ip) _print_dns_list(entries) @utils.arg('ip', metavar='', help=_('ip address')) @utils.arg('name', metavar='', help=_('DNS name')) @utils.arg('domain', metavar='', help=_('DNS domain')) @utils.arg('--type', metavar='', help=_('dns type (e.g. "A")'), default='A') def do_dns_create(cs, args): """Create a DNS entry for domain, name and ip.""" cs.dns_entries.create(args.domain, args.name, args.ip, args.type) @utils.arg('domain', metavar='', help=_('DNS domain')) @utils.arg('name', metavar='', help=_('DNS name')) def do_dns_delete(cs, args): """Delete the specified DNS entry.""" cs.dns_entries.delete(args.domain, args.name) @utils.arg('domain', metavar='', help=_('DNS domain')) def do_dns_delete_domain(cs, args): """Delete the specified DNS domain.""" cs.dns_domains.delete(args.domain) @utils.arg('domain', metavar='', help=_('DNS domain')) @utils.arg('--availability-zone', metavar='', default=None, help=_('Limit access to this domain to servers ' 'in the specified availability zone.')) @utils.arg('--availability_zone', help=argparse.SUPPRESS) def do_dns_create_private_domain(cs, args): """Create the specified DNS domain.""" cs.dns_domains.create_private(args.domain, args.availability_zone) @utils.arg('domain', metavar='', help=_('DNS domain')) @utils.arg('--project', metavar='', help=_('Limit access to this domain to users ' 'of the specified project.'), default=None) def do_dns_create_public_domain(cs, args): """Create the specified DNS domain.""" cs.dns_domains.create_public(args.domain, args.project) def _print_secgroup_rules(rules): class FormattedRule: def __init__(self, obj): items = (obj if isinstance(obj, dict) else obj._info).items() for k, v in items: if k == 'ip_range': v = v.get('cidr') elif k == 'group': k = 'source_group' v = v.get('name') if v is None: v = '' setattr(self, k, v) rules = [FormattedRule(rule) for rule in rules] utils.print_list(rules, ['IP Protocol', 'From Port', 'To Port', 'IP Range', 'Source Group']) def _print_secgroups(secgroups): utils.print_list(secgroups, ['Id', 'Name', 'Description']) def _get_secgroup(cs, secgroup): # Check secgroup is an ID (nova-network) or UUID (neutron) if (utils.is_integer_like(strutils.safe_encode(secgroup)) or uuidutils.is_uuid_like(secgroup)): try: return cs.security_groups.get(secgroup) except exceptions.NotFound: pass # Check secgroup as a name match_found = False for s in cs.security_groups.list(): encoding = (locale.getpreferredencoding() or sys.stdin.encoding or 'UTF-8') if not six.PY3: s.name = s.name.encode(encoding) if secgroup == s.name: if match_found is not False: msg = (_("Multiple security group matches found for name '%s'" ", use an ID to be more specific.") % secgroup) raise exceptions.NoUniqueMatch(msg) match_found = s if match_found is False: raise exceptions.CommandError(_("Secgroup ID or name '%s' not found.") % secgroup) return match_found @utils.arg('secgroup', metavar='', help=_('ID or name of security group.')) @utils.arg('ip_proto', metavar='', help=_('IP protocol (icmp, tcp, udp).')) @utils.arg('from_port', metavar='', help=_('Port at start of range.')) @utils.arg('to_port', metavar='', help=_('Port at end of range.')) @utils.arg('cidr', metavar='', help=_('CIDR for address range.')) def do_secgroup_add_rule(cs, args): """Add a rule to a security group.""" secgroup = _get_secgroup(cs, args.secgroup) rule = cs.security_group_rules.create(secgroup.id, args.ip_proto, args.from_port, args.to_port, args.cidr) _print_secgroup_rules([rule]) @utils.arg('secgroup', metavar='', help=_('ID or name of security group.')) @utils.arg('ip_proto', metavar='', help=_('IP protocol (icmp, tcp, udp).')) @utils.arg('from_port', metavar='', help=_('Port at start of range.')) @utils.arg('to_port', metavar='', help=_('Port at end of range.')) @utils.arg('cidr', metavar='', help=_('CIDR for address range.')) def do_secgroup_delete_rule(cs, args): """Delete a rule from a security group.""" secgroup = _get_secgroup(cs, args.secgroup) for rule in secgroup.rules: if (rule['ip_protocol'] and rule['ip_protocol'].upper() == args.ip_proto.upper() and rule['from_port'] == int(args.from_port) and rule['to_port'] == int(args.to_port) and rule['ip_range']['cidr'] == args.cidr): _print_secgroup_rules([rule]) return cs.security_group_rules.delete(rule['id']) raise exceptions.CommandError(_("Rule not found")) @utils.arg('name', metavar='', help=_('Name of security group.')) @utils.arg('description', metavar='', help=_('Description of security group.')) def do_secgroup_create(cs, args): """Create a security group.""" secgroup = cs.security_groups.create(args.name, args.description) _print_secgroups([secgroup]) @utils.arg('secgroup', metavar='', help=_('ID or name of security group.')) @utils.arg('name', metavar='', help=_('Name of security group.')) @utils.arg('description', metavar='', help=_('Description of security group.')) def do_secgroup_update(cs, args): """Update a security group.""" sg = _get_secgroup(cs, args.secgroup) secgroup = cs.security_groups.update(sg, args.name, args.description) _print_secgroups([secgroup]) @utils.arg('secgroup', metavar='', help=_('ID or name of security group.')) def do_secgroup_delete(cs, args): """Delete a security group.""" secgroup = _get_secgroup(cs, args.secgroup) cs.security_groups.delete(secgroup) _print_secgroups([secgroup]) @utils.arg('--all-tenants', dest='all_tenants', metavar='<0|1>', nargs='?', type=int, const=1, default=int(strutils.bool_from_string( os.environ.get("ALL_TENANTS", 'false'), True)), help=_('Display information from all tenants (Admin only).')) @utils.arg('--all_tenants', nargs='?', type=int, const=1, help=argparse.SUPPRESS) def do_secgroup_list(cs, args): """List security groups for the current tenant.""" search_opts = {'all_tenants': args.all_tenants} columns = ['Id', 'Name', 'Description'] if args.all_tenants: columns.append('Tenant_ID') groups = cs.security_groups.list(search_opts=search_opts) utils.print_list(groups, columns) @utils.arg('secgroup', metavar='', help=_('ID or name of security group.')) def do_secgroup_list_rules(cs, args): """List rules for a security group.""" secgroup = _get_secgroup(cs, args.secgroup) _print_secgroup_rules(secgroup.rules) @utils.arg('secgroup', metavar='', help=_('ID or name of security group.')) @utils.arg('source_group', metavar='', help=_('ID or name of source group.')) @utils.arg('ip_proto', metavar='', help=_('IP protocol (icmp, tcp, udp).')) @utils.arg('from_port', metavar='', help=_('Port at start of range.')) @utils.arg('to_port', metavar='', help=_('Port at end of range.')) def do_secgroup_add_group_rule(cs, args): """Add a source group rule to a security group.""" secgroup = _get_secgroup(cs, args.secgroup) source_group = _get_secgroup(cs, args.source_group) params = {} params['group_id'] = source_group.id if args.ip_proto or args.from_port or args.to_port: if not (args.ip_proto and args.from_port and args.to_port): raise exceptions.CommandError(_("ip_proto, from_port, and to_port" " must be specified together")) params['ip_protocol'] = args.ip_proto.upper() params['from_port'] = args.from_port params['to_port'] = args.to_port rule = cs.security_group_rules.create(secgroup.id, **params) _print_secgroup_rules([rule]) @utils.arg('secgroup', metavar='', help=_('ID or name of security group.')) @utils.arg('source_group', metavar='', help=_('ID or name of source group.')) @utils.arg('ip_proto', metavar='', help=_('IP protocol (icmp, tcp, udp).')) @utils.arg('from_port', metavar='', help=_('Port at start of range.')) @utils.arg('to_port', metavar='', help=_('Port at end of range.')) def do_secgroup_delete_group_rule(cs, args): """Delete a source group rule from a security group.""" secgroup = _get_secgroup(cs, args.secgroup) source_group = _get_secgroup(cs, args.source_group) params = {} params['group_name'] = source_group.name if args.ip_proto or args.from_port or args.to_port: if not (args.ip_proto and args.from_port and args.to_port): raise exceptions.CommandError(_("ip_proto, from_port, and to_port" " must be specified together")) params['ip_protocol'] = args.ip_proto.upper() params['from_port'] = int(args.from_port) params['to_port'] = int(args.to_port) for rule in secgroup.rules: if (rule.get('ip_protocol').upper() == params.get( 'ip_protocol').upper() and rule.get('from_port') == params.get('from_port') and rule.get('to_port') == params.get('to_port') and rule.get('group', {}).get('name') == params.get('group_name')): return cs.security_group_rules.delete(rule['id']) raise exceptions.CommandError(_("Rule not found")) @utils.arg('name', metavar='', help=_('Name of key.')) @utils.arg('--pub-key', metavar='', default=None, help=_('Path to a public ssh key.')) @utils.arg('--pub_key', help=argparse.SUPPRESS) def do_keypair_add(cs, args): """Create a new key pair for use with servers.""" name = args.name pub_key = args.pub_key if pub_key: try: with open(os.path.expanduser(pub_key)) as f: pub_key = f.read() except IOError as e: raise exceptions.CommandError(_("Can't open or read '%(key)s': " "%(exc)s") % {'key': pub_key, 'exc': e}) keypair = cs.keypairs.create(name, pub_key) if not pub_key: private_key = keypair.private_key print(private_key) @utils.arg('name', metavar='', help=_('Keypair name to delete.')) def do_keypair_delete(cs, args): """Delete keypair given by its name.""" name = args.name cs.keypairs.delete(name) def do_keypair_list(cs, args): """Print a list of keypairs for a user""" keypairs = cs.keypairs.list() columns = ['Name', 'Fingerprint'] utils.print_list(keypairs, columns) def _print_keypair(keypair): kp = keypair._info.copy() pk = kp.pop('public_key') utils.print_dict(kp) print(_("Public key: %s") % pk) @utils.arg('keypair', metavar='', help=_("Name or ID of keypair")) def do_keypair_show(cs, args): """Show details about the given keypair.""" keypair = cs.keypairs.get(args.keypair) _print_keypair(keypair) @utils.arg('--tenant', #nova db searches by project_id dest='tenant', metavar='', nargs='?', help=_('Display information from single tenant (Admin only).')) @utils.arg('--reserved', dest='reserved', action='store_true', default=False, help=_('Include reservations count.')) def do_absolute_limits(cs, args): """Print a list of absolute limits for a user""" limits = cs.limits.get(args.reserved, args.tenant).absolute columns = ['Name', 'Value'] utils.print_list(limits, columns) def do_rate_limits(cs, args): """Print a list of rate limits for a user""" limits = cs.limits.get().rate columns = ['Verb', 'URI', 'Value', 'Remain', 'Unit', 'Next_Available'] utils.print_list(limits, columns) @utils.arg('--start', metavar='', help=_('Usage range start date ex 2012-01-20 (default: 4 weeks ' 'ago)'), default=None) @utils.arg('--end', metavar='', help=_('Usage range end date, ex 2012-01-20 (default: tomorrow)'), default=None) def do_usage_list(cs, args): """List usage data for all tenants.""" dateformat = "%Y-%m-%d" rows = ["Tenant ID", "Servers", "RAM MB-Hours", "CPU Hours", "Disk GB-Hours"] now = timeutils.utcnow() if args.start: start = datetime.datetime.strptime(args.start, dateformat) else: start = now - datetime.timedelta(weeks=4) if args.end: end = datetime.datetime.strptime(args.end, dateformat) else: end = now + datetime.timedelta(days=1) def simplify_usage(u): simplerows = [x.lower().replace(" ", "_") for x in rows] setattr(u, simplerows[0], u.tenant_id) setattr(u, simplerows[1], "%d" % len(u.server_usages)) setattr(u, simplerows[2], "%.2f" % u.total_memory_mb_usage) setattr(u, simplerows[3], "%.2f" % u.total_vcpus_usage) setattr(u, simplerows[4], "%.2f" % u.total_local_gb_usage) usage_list = cs.usage.list(start, end, detailed=True) print(_("Usage from %(start)s to %(end)s:") % {'start': start.strftime(dateformat), 'end': end.strftime(dateformat)}) for usage in usage_list: simplify_usage(usage) utils.print_list(usage_list, rows) @utils.arg('--start', metavar='', help=_('Usage range start date ex 2012-01-20 (default: 4 weeks ' 'ago)'), default=None) @utils.arg('--end', metavar='', help=_('Usage range end date, ex 2012-01-20 (default: tomorrow)'), default=None) @utils.arg('--tenant', metavar='', default=None, help=_('UUID or name of tenant to get usage for.')) def do_usage(cs, args): """Show usage data for a single tenant.""" dateformat = "%Y-%m-%d" rows = ["Servers", "RAM MB-Hours", "CPU Hours", "Disk GB-Hours"] now = timeutils.utcnow() if args.start: start = datetime.datetime.strptime(args.start, dateformat) else: start = now - datetime.timedelta(weeks=4) if args.end: end = datetime.datetime.strptime(args.end, dateformat) else: end = now + datetime.timedelta(days=1) def simplify_usage(u): simplerows = [x.lower().replace(" ", "_") for x in rows] setattr(u, simplerows[0], "%d" % len(u.server_usages)) setattr(u, simplerows[1], "%.2f" % u.total_memory_mb_usage) setattr(u, simplerows[2], "%.2f" % u.total_vcpus_usage) setattr(u, simplerows[3], "%.2f" % u.total_local_gb_usage) if args.tenant: usage = cs.usage.get(args.tenant, start, end) else: usage = cs.usage.get(cs.client.tenant_id, start, end) print(_("Usage from %(start)s to %(end)s:") % {'start': start.strftime(dateformat), 'end': end.strftime(dateformat)}) if getattr(usage, 'total_vcpus_usage', None): simplify_usage(usage) utils.print_list([usage], rows) else: print(_('None')) @utils.arg('pk_filename', metavar='', nargs='?', default='pk.pem', help=_('Filename for the private key [Default: pk.pem]')) @utils.arg('cert_filename', metavar='', nargs='?', default='cert.pem', help=_('Filename for the X.509 certificate [Default: cert.pem]')) def do_x509_create_cert(cs, args): """Create x509 cert for a user in tenant.""" if os.path.exists(args.pk_filename): raise exceptions.CommandError(_("Unable to write privatekey - %s " "exists.") % args.pk_filename) if os.path.exists(args.cert_filename): raise exceptions.CommandError(_("Unable to write x509 cert - %s " "exists.") % args.cert_filename) certs = cs.certs.create() try: old_umask = os.umask(0o377) with open(args.pk_filename, 'w') as private_key: private_key.write(certs.private_key) print(_("Wrote private key to %s") % args.pk_filename) finally: os.umask(old_umask) with open(args.cert_filename, 'w') as cert: cert.write(certs.data) print(_("Wrote x509 certificate to %s") % args.cert_filename) @utils.arg('filename', metavar='', nargs='?', default='cacert.pem', help=_('Filename to write the x509 root cert.')) def do_x509_get_root_cert(cs, args): """Fetch the x509 root cert.""" if os.path.exists(args.filename): raise exceptions.CommandError(_("Unable to write x509 root cert - \ %s exists.") % args.filename) with open(args.filename, 'w') as cert: cacert = cs.certs.get() cert.write(cacert.data) print(_("Wrote x509 root cert to %s") % args.filename) @utils.arg('--hypervisor', metavar='', default=None, help=_('type of hypervisor.')) def do_agent_list(cs, args): """List all builds.""" result = cs.agents.list(args.hypervisor) columns = ["Agent_id", "Hypervisor", "OS", "Architecture", "Version", 'Md5hash', 'Url'] utils.print_list(result, columns) @utils.arg('os', metavar='', help=_('type of os.')) @utils.arg('architecture', metavar='', help=_('type of architecture')) @utils.arg('version', metavar='', help=_('version')) @utils.arg('url', metavar='', help=_('url')) @utils.arg('md5hash', metavar='', help=_('md5 hash')) @utils.arg('hypervisor', metavar='', default='xen', help=_('type of hypervisor.')) def do_agent_create(cs, args): """Create new agent build.""" result = cs.agents.create(args.os, args.architecture, args.version, args.url, args.md5hash, args.hypervisor) utils.print_dict(result._info.copy()) @utils.arg('id', metavar='', help=_('id of the agent-build')) def do_agent_delete(cs, args): """Delete existing agent build.""" cs.agents.delete(args.id) @utils.arg('id', metavar='', help=_('id of the agent-build')) @utils.arg('version', metavar='', help=_('version')) @utils.arg('url', metavar='', help=_('url')) @utils.arg('md5hash', metavar='', help=_('md5hash')) def do_agent_modify(cs, args): """Modify existing agent build.""" result = cs.agents.update(args.id, args.version, args.url, args.md5hash) utils.print_dict(result._info) def _find_aggregate(cs, aggregate): """Get a aggregate by name or ID.""" return utils.find_resource(cs.aggregates, aggregate) def do_aggregate_list(cs, args): """Print a list of all aggregates.""" aggregates = cs.aggregates.list() columns = ['Id', 'Name', 'Availability Zone'] utils.print_list(aggregates, columns) @utils.arg('name', metavar='', help=_('Name of aggregate.')) @utils.arg('availability_zone', metavar='', default=None, nargs='?', help=_('The availability zone of the aggregate (optional).')) def do_aggregate_create(cs, args): """Create a new aggregate with the specified details.""" aggregate = cs.aggregates.create(args.name, args.availability_zone) _print_aggregate_details(aggregate) @utils.arg('aggregate', metavar='', help=_('Name or ID of aggregate to delete.')) def do_aggregate_delete(cs, args): """Delete the aggregate.""" aggregate = _find_aggregate(cs, args.aggregate) cs.aggregates.delete(aggregate) print(_("Aggregate %s has been successfully deleted.") % aggregate.id) @utils.arg('aggregate', metavar='', help=_('Name or ID of aggregate to update.')) @utils.arg('name', metavar='', help=_('Name of aggregate.')) @utils.arg('availability_zone', metavar='', nargs='?', default=None, help=_('The availability zone of the aggregate.')) def do_aggregate_update(cs, args): """Update the aggregate's name and optionally availability zone.""" aggregate = _find_aggregate(cs, args.aggregate) updates = {"name": args.name} if args.availability_zone: updates["availability_zone"] = args.availability_zone aggregate = cs.aggregates.update(aggregate.id, updates) print(_("Aggregate %s has been successfully updated.") % aggregate.id) _print_aggregate_details(aggregate) @utils.arg('aggregate', metavar='', help=_('Name or ID of aggregate to update.')) @utils.arg('metadata', metavar='', nargs='+', action='append', default=[], help=_('Metadata to add/update to aggregate')) def do_aggregate_set_metadata(cs, args): """Update the metadata associated with the aggregate.""" aggregate = _find_aggregate(cs, args.aggregate) metadata = _extract_metadata(args) aggregate = cs.aggregates.set_metadata(aggregate.id, metadata) print(_("Metadata has been successfully updated for aggregate %s.") % aggregate.id) _print_aggregate_details(aggregate) @utils.arg('aggregate', metavar='', help=_('Name or ID of aggregate.')) @utils.arg('host', metavar='', help=_('The host to add to the aggregate.')) def do_aggregate_add_host(cs, args): """Add the host to the specified aggregate.""" aggregate = _find_aggregate(cs, args.aggregate) aggregate = cs.aggregates.add_host(aggregate.id, args.host) print(_("Host %(host)s has been successfully added for aggregate " "%(aggregate_id)s ") % {'host': args.host, 'aggregate_id': aggregate.id}) _print_aggregate_details(aggregate) @utils.arg('aggregate', metavar='', help=_('Name or ID of aggregate.')) @utils.arg('host', metavar='', help=_('The host to remove from the aggregate.')) def do_aggregate_remove_host(cs, args): """Remove the specified host from the specified aggregate.""" aggregate = _find_aggregate(cs, args.aggregate) aggregate = cs.aggregates.remove_host(aggregate.id, args.host) print(_("Host %(host)s has been successfully removed from aggregate " "%(aggregate_id)s ") % {'host': args.host, 'aggregate_id': aggregate.id}) _print_aggregate_details(aggregate) @utils.arg('aggregate', metavar='', help=_('Name or ID of aggregate.')) def do_aggregate_details(cs, args): """Show details of the specified aggregate.""" aggregate = _find_aggregate(cs, args.aggregate) _print_aggregate_details(aggregate) def _print_aggregate_details(aggregate): columns = ['Id', 'Name', 'Availability Zone', 'Hosts', 'Metadata'] def parser_metadata(fields): return utils.pretty_choice_dict(getattr(fields, 'metadata', {}) or {}) def parser_hosts(fields): return utils.pretty_choice_list(getattr(fields, 'hosts', [])) formatters = { 'Metadata': parser_metadata, 'Hosts': parser_hosts, } utils.print_list([aggregate], columns, formatters=formatters) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg('host', metavar='', default=None, nargs='?', help=_('destination host name.')) @utils.arg('--block-migrate', action='store_true', dest='block_migrate', default=False, help=_('True in case of block_migration. (Default=False:live_migration)')) @utils.arg('--block_migrate', action='store_true', help=argparse.SUPPRESS) @utils.arg('--disk-over-commit', action='store_true', dest='disk_over_commit', default=False, help=_('Allow overcommit.(Default=False)')) @utils.arg('--disk_over_commit', action='store_true', help=argparse.SUPPRESS) def do_live_migration(cs, args): """Migrate running server to a new machine.""" _find_server(cs, args.server).live_migrate(args.host, args.block_migrate, args.disk_over_commit) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg('--active', action='store_const', dest='state', default='error', const='active', help=_('Request the server be reset to "active" state instead ' 'of "error" state (the default).')) def do_reset_state(cs, args): """Reset the state of a server.""" _find_server(cs, args.server).reset_state(args.state) @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_reset_network(cs, args): """Reset network of a server.""" _find_server(cs, args.server).reset_network() @utils.arg('--host', metavar='', default=None, help=_('Name of host.')) @utils.arg('--binary', metavar='', default=None, help=_('Service binary.')) def do_service_list(cs, args): """Show a list of all running services. Filter by host & binary.""" result = cs.services.list(host=args.host, binary=args.binary) columns = ["Binary", "Host", "Zone", "Status", "State", "Updated_at"] # NOTE(sulo): we check if the response has disabled_reason # so as not to add the column when the extended ext is not enabled. if result and hasattr(result[0], 'disabled_reason'): columns.append("Disabled Reason") utils.print_list(result, columns) @utils.arg('host', metavar='', help=_('Name of host.')) @utils.arg('binary', metavar='', help=_('Service binary.')) def do_service_enable(cs, args): """Enable the service.""" result = cs.services.enable(args.host, args.binary) utils.print_list([result], ['Host', 'Binary', 'Status']) @utils.arg('host', metavar='', help=_('Name of host.')) @utils.arg('binary', metavar='', help=_('Service binary.')) @utils.arg('--reason', metavar='', help=_('Reason for disabling service.')) def do_service_disable(cs, args): """Disable the service.""" if args.reason: result = cs.services.disable_log_reason(args.host, args.binary, args.reason) utils.print_list([result], ['Host', 'Binary', 'Status', 'Disabled Reason']) else: result = cs.services.disable(args.host, args.binary) utils.print_list([result], ['Host', 'Binary', 'Status']) @utils.arg('fixed_ip', metavar='', help=_('Fixed IP Address.')) def do_fixed_ip_get(cs, args): """Retrieve info on a fixed ip.""" result = cs.fixed_ips.get(args.fixed_ip) utils.print_list([result], ['address', 'cidr', 'hostname', 'host']) @utils.arg('fixed_ip', metavar='', help=_('Fixed IP Address.')) def do_fixed_ip_reserve(cs, args): """Reserve a fixed IP.""" cs.fixed_ips.reserve(args.fixed_ip) @utils.arg('fixed_ip', metavar='', help=_('Fixed IP Address.')) def do_fixed_ip_unreserve(cs, args): """Unreserve a fixed IP.""" cs.fixed_ips.unreserve(args.fixed_ip) @utils.arg('host', metavar='', help=_('Name of host.')) def do_host_describe(cs, args): """Describe a specific host.""" result = cs.hosts.get(args.host) columns = ["HOST", "PROJECT", "cpu", "memory_mb", "disk_gb"] utils.print_list(result, columns) @utils.arg('--zone', metavar='', default=None, help=_('Filters the list, returning only those ' 'hosts in the availability zone .')) def do_host_list(cs, args): """List all hosts by service.""" columns = ["host_name", "service", "zone"] result = cs.hosts.list(args.zone) utils.print_list(result, columns) @utils.arg('host', metavar='', help='Name of host.') @utils.arg('--status', metavar='', default=None, dest='status', help=_('Either enable or disable a host.')) @utils.arg('--maintenance', metavar='', default=None, dest='maintenance', help=_('Either put or resume host to/from maintenance.')) def do_host_update(cs, args): """Update host settings.""" updates = {} columns = ["HOST"] if args.status: updates['status'] = args.status columns.append("status") if args.maintenance: updates['maintenance_mode'] = args.maintenance columns.append("maintenance_mode") result = cs.hosts.update(args.host, updates) utils.print_list([result], columns) @utils.arg('host', metavar='', help='Name of host.') @utils.arg('--action', metavar='', dest='action', choices=['startup', 'shutdown', 'reboot'], help=_('A power action: startup, reboot, or shutdown.')) def do_host_action(cs, args): """Perform a power action on a host.""" result = cs.hosts.host_action(args.host, args.action) utils.print_list([result], ['HOST', 'power_action']) def _find_hypervisor(cs, hypervisor): """Get a hypervisor by name or ID.""" return utils.find_resource(cs.hypervisors, hypervisor) @utils.arg('--matching', metavar='', default=None, help=_('List hypervisors matching the given .')) def do_hypervisor_list(cs, args): """List hypervisors.""" columns = ['ID', 'Hypervisor hostname'] if args.matching: utils.print_list(cs.hypervisors.search(args.matching), columns) else: # Since we're not outputting detail data, choose # detailed=False for server-side efficiency utils.print_list(cs.hypervisors.list(False), columns) @utils.arg('hostname', metavar='', help=_('The hypervisor hostname (or pattern) to search for.')) def do_hypervisor_servers(cs, args): """List servers belonging to specific hypervisors.""" hypers = cs.hypervisors.search(args.hostname, servers=True) class InstanceOnHyper(object): def __init__(self, **kwargs): self.__dict__.update(kwargs) # Massage the result into a list to be displayed instances = [] for hyper in hypers: hyper_host = hyper.hypervisor_hostname hyper_id = hyper.id if hasattr(hyper, 'servers'): instances.extend([InstanceOnHyper(id=serv['uuid'], name=serv['name'], hypervisor_hostname=hyper_host, hypervisor_id=hyper_id) for serv in hyper.servers]) # Output the data utils.print_list(instances, ['ID', 'Name', 'Hypervisor ID', 'Hypervisor Hostname']) @utils.arg('hypervisor', metavar='', help=_('Name or ID of the hypervisor to show the details of.')) def do_hypervisor_show(cs, args): """Display the details of the specified hypervisor.""" hyper = _find_hypervisor(cs, args.hypervisor) utils.print_dict(utils.flatten_dict(hyper._info)) @utils.arg('hypervisor', metavar='', help=_('Name or ID of the hypervisor to show the uptime of.')) def do_hypervisor_uptime(cs, args): """Display the uptime of the specified hypervisor.""" hyper = _find_hypervisor(cs, args.hypervisor) hyper = cs.hypervisors.uptime(hyper) # Output the uptime information utils.print_dict(hyper._info.copy()) def do_hypervisor_stats(cs, args): """Get hypervisor statistics over all compute nodes.""" stats = cs.hypervisors.statistics() utils.print_dict(stats._info.copy()) def ensure_service_catalog_present(cs): if not hasattr(cs.client, 'service_catalog'): # Turn off token caching and re-auth cs.client.unauthenticate() cs.client.use_token_cache(False) cs.client.authenticate() def do_endpoints(cs, _args): """Discover endpoints that get returned from the authenticate services.""" ensure_service_catalog_present(cs) catalog = cs.client.service_catalog.catalog for e in catalog['access']['serviceCatalog']: utils.print_dict(e['endpoints'][0], e['name']) @utils.arg('--wrap', dest='wrap', metavar='', default=64, help=_('wrap PKI tokens to a specified length, or 0 to disable')) def do_credentials(cs, _args): """Show user credentials returned from auth.""" ensure_service_catalog_present(cs) catalog = cs.client.service_catalog.catalog utils.print_dict(catalog['access']['user'], "User Credentials", wrap=int(_args.wrap)) utils.print_dict(catalog['access']['token'], "Token", wrap=int(_args.wrap)) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg('--port', dest='port', action='store', type=int, default=22, help=_('Optional flag to indicate which port to use for ssh. ' '(Default=22)')) @utils.arg('--private', dest='private', action='store_true', default=False, help=_('Optional flag to indicate whether to only use private address ' 'attached to an instance. (Default=False). If no public address is ' 'found try private address')) @utils.arg('--ipv6', dest='ipv6', action='store_true', default=False, help=_('Optional flag to indicate whether to use an IPv6 address ' 'attached to a server. (Defaults to IPv4 address)')) @utils.arg('--login', metavar='', help=_('Login to use.'), default="root") @utils.arg('-i', '--identity', dest='identity', help=_('Private key file, same as the -i option to the ssh command.'), default='') @utils.arg('--extra-opts', dest='extra', help=_('Extra options to pass to ssh. see: man ssh'), default='') def do_ssh(cs, args): """SSH into a server.""" if '@' in args.server: user, server = args.server.split('@', 1) args.login = user args.server = server addresses = _find_server(cs, args.server).addresses address_type = "private" if args.private else "public" version = 6 if args.ipv6 else 4 if (address_type == "public" and address_type not in addresses and "private" in addresses): address_type = "private" if address_type not in addresses: print(_("ERROR: No %(addr_type)s addresses found for '%(server)s'.") % {'addr_type': address_type, 'server': args.server}) return ip_address = None for address in addresses[address_type]: if address['version'] == version: ip_address = address['addr'] break identity = '-i %s' % args.identity if len(args.identity) else '' if ip_address: os.system("ssh -%d -p%d %s %s@%s %s" % (version, args.port, identity, args.login, ip_address, args.extra)) else: pretty_version = "IPv%d" % version print(_("ERROR: No %(addr_type)s %(pretty_version)s address found.") % {'addr_type': address_type, 'pretty_version': pretty_version}) return _quota_resources = ['instances', 'cores', 'ram', 'floating_ips', 'fixed_ips', 'metadata_items', 'injected_files', 'injected_file_content_bytes', 'injected_file_path_bytes', 'key_pairs', 'security_groups', 'security_group_rules'] def _quota_show(quotas): class FormattedQuota(object): def __init__(self, key, value): setattr(self, 'quota', key) setattr(self, 'limit', value) quota_list = [] for resource in _quota_resources: try: quota = FormattedQuota(resource, getattr(quotas, resource)) quota_list.append(quota) except AttributeError: pass columns = ['Quota', 'Limit'] utils.print_list(quota_list, columns) def _quota_update(manager, identifier, args): updates = {} for resource in _quota_resources: val = getattr(args, resource, None) if val is not None: updates[resource] = val if updates: # default value of force is None to make sure this client # will be compatibile with old nova server force_update = getattr(args, 'force', None) user_id = getattr(args, 'user', None) if isinstance(manager, quotas.QuotaSetManager): manager.update(identifier, force=force_update, user_id=user_id, **updates) else: manager.update(identifier, **updates) @utils.arg('--tenant', metavar='', default=None, help=_('ID of tenant to list the quotas for.')) @utils.arg('--user', metavar='', default=None, help=_('ID of user to list the quotas for.')) def do_quota_show(cs, args): """List the quotas for a tenant/user.""" if not args.tenant: _quota_show(cs.quotas.get(cs.client.tenant_id, user_id=args.user)) else: _quota_show(cs.quotas.get(args.tenant, user_id=args.user)) @utils.arg('--tenant', metavar='', default=None, help=_('ID of tenant to list the default quotas for.')) def do_quota_defaults(cs, args): """List the default quotas for a tenant.""" if not args.tenant: _quota_show(cs.quotas.defaults(cs.client.tenant_id)) else: _quota_show(cs.quotas.defaults(args.tenant)) @utils.arg('tenant', metavar='', help=_('ID of tenant to set the quotas for.')) @utils.arg('--user', metavar='', default=None, help=_('ID of user to set the quotas for.')) @utils.arg('--instances', metavar='', type=int, default=None, help=_('New value for the "instances" quota.')) @utils.arg('--cores', metavar='', type=int, default=None, help=_('New value for the "cores" quota.')) @utils.arg('--ram', metavar='', type=int, default=None, help=_('New value for the "ram" quota.')) @utils.arg('--floating-ips', metavar='', type=int, default=None, help=_('New value for the "floating-ips" quota.')) @utils.arg('--floating_ips', type=int, help=argparse.SUPPRESS) @utils.arg('--fixed-ips', metavar='', type=int, default=None, help=_('New value for the "fixed-ips" quota.')) @utils.arg('--metadata-items', metavar='', type=int, default=None, help=_('New value for the "metadata-items" quota.')) @utils.arg('--metadata_items', type=int, help=argparse.SUPPRESS) @utils.arg('--injected-files', metavar='', type=int, default=None, help=_('New value for the "injected-files" quota.')) @utils.arg('--injected_files', type=int, help=argparse.SUPPRESS) @utils.arg('--injected-file-content-bytes', metavar='', type=int, default=None, help=_('New value for the "injected-file-content-bytes" quota.')) @utils.arg('--injected_file_content_bytes', type=int, help=argparse.SUPPRESS) @utils.arg('--injected-file-path-bytes', metavar='', type=int, default=None, help=_('New value for the "injected-file-path-bytes" quota.')) @utils.arg('--key-pairs', metavar='', type=int, default=None, help=_('New value for the "key-pairs" quota.')) @utils.arg('--security-groups', metavar='', type=int, default=None, help=_('New value for the "security-groups" quota.')) @utils.arg('--security-group-rules', metavar='', type=int, default=None, help=_('New value for the "security-group-rules" quota.')) @utils.arg('--force', dest='force', action="store_true", default=None, help=_('Whether force update the quota even if the already used' ' and reserved exceeds the new quota')) def do_quota_update(cs, args): """Update the quotas for a tenant/user.""" _quota_update(cs.quotas, args.tenant, args) @utils.arg('--tenant', metavar='', help=_('ID of tenant to delete quota for.')) @utils.arg('--user', metavar='', help=_('ID of user to delete quota for.')) def do_quota_delete(cs, args): """Delete quota for a tenant/user so their quota will Revert back to default. """ cs.quotas.delete(args.tenant, user_id=args.user) @utils.arg('class_name', metavar='', help=_('Name of quota class to list the quotas for.')) def do_quota_class_show(cs, args): """List the quotas for a quota class.""" _quota_show(cs.quota_classes.get(args.class_name)) @utils.arg('class_name', metavar='', help=_('Name of quota class to set the quotas for.')) @utils.arg('--instances', metavar='', type=int, default=None, help=_('New value for the "instances" quota.')) @utils.arg('--cores', metavar='', type=int, default=None, help=_('New value for the "cores" quota.')) @utils.arg('--ram', metavar='', type=int, default=None, help=_('New value for the "ram" quota.')) @utils.arg('--floating-ips', metavar='', type=int, default=None, help=_('New value for the "floating-ips" quota.')) @utils.arg('--floating_ips', type=int, help=argparse.SUPPRESS) @utils.arg('--metadata-items', metavar='', type=int, default=None, help=_('New value for the "metadata-items" quota.')) @utils.arg('--metadata_items', type=int, help=argparse.SUPPRESS) @utils.arg('--injected-files', metavar='', type=int, default=None, help=_('New value for the "injected-files" quota.')) @utils.arg('--injected_files', type=int, help=argparse.SUPPRESS) @utils.arg('--injected-file-content-bytes', metavar='', type=int, default=None, help=_('New value for the "injected-file-content-bytes" quota.')) @utils.arg('--injected_file_content_bytes', type=int, help=argparse.SUPPRESS) @utils.arg('--injected-file-path-bytes', metavar='', type=int, default=None, help=_('New value for the "injected-file-path-bytes" quota.')) @utils.arg('--key-pairs', metavar='', type=int, default=None, help=_('New value for the "key-pairs" quota.')) @utils.arg('--security-groups', metavar='', type=int, default=None, help=_('New value for the "security-groups" quota.')) @utils.arg('--security-group-rules', metavar='', type=int, default=None, help=_('New value for the "security-group-rules" quota.')) def do_quota_class_update(cs, args): """Update the quotas for a quota class.""" _quota_update(cs.quota_classes, args.class_name, args) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg('host', metavar='', help=_('Name or ID of target host.')) @utils.arg('--password', dest='password', metavar='', default=None, help=_("Set the provided password on the evacuated server. Not applicable " "with on-shared-storage flag")) @utils.arg('--on-shared-storage', dest='on_shared_storage', action="store_true", default=False, help=_('Specifies whether server files are located on shared storage')) def do_evacuate(cs, args): """Evacuate server from failed host to specified one.""" server = _find_server(cs, args.server) res = server.evacuate(args.host, args.on_shared_storage, args.password)[1] if type(res) is dict: utils.print_dict(res) def _print_interfaces(interfaces): columns = ['Port State', 'Port ID', 'Net ID', 'IP addresses', 'MAC Addr'] class FormattedInterface(object): def __init__(self, interface): for col in columns: key = col.lower().replace(" ", "_") if hasattr(interface, key): setattr(self, key, getattr(interface, key)) self.ip_addresses = ",".join([fip['ip_address'] for fip in interface.fixed_ips]) utils.print_list([FormattedInterface(i) for i in interfaces], columns) @utils.arg('server', metavar='', help=_('Name or ID of server.')) def do_interface_list(cs, args): """List interfaces attached to a server.""" server = _find_server(cs, args.server) res = server.interface_list() if type(res) is list: _print_interfaces(res) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg('--port-id', metavar='', help=_('Port ID.'), dest="port_id") @utils.arg('--net-id', metavar='', help=_('Network ID'), default=None, dest="net_id") @utils.arg('--fixed-ip', metavar='', help=_('Requested fixed IP.'), default=None, dest="fixed_ip") def do_interface_attach(cs, args): """Attach a network interface to a server.""" server = _find_server(cs, args.server) res = server.interface_attach(args.port_id, args.net_id, args.fixed_ip) if type(res) is dict: utils.print_dict(res) @utils.arg('server', metavar='', help=_('Name or ID of server.')) @utils.arg('port_id', metavar='', help=_('Port ID.')) def do_interface_detach(cs, args): """Detach a network interface from a server.""" server = _find_server(cs, args.server) res = server.interface_detach(args.port_id) if type(res) is dict: utils.print_dict(res) def _treeizeAvailabilityZone(zone): """Build a tree view for availability zones.""" AvailabilityZone = availability_zones.AvailabilityZone az = AvailabilityZone(zone.manager, copy.deepcopy(zone._info), zone._loaded) result = [] # Zone tree view item az.zoneName = zone.zoneName az.zoneState = ('available' if zone.zoneState['available'] else 'not available') az._info['zoneName'] = az.zoneName az._info['zoneState'] = az.zoneState result.append(az) if zone.hosts is not None: zone_hosts = sorted(zone.hosts.items(), key=lambda x: x[0]) for (host, services) in zone_hosts: # Host tree view item az = AvailabilityZone(zone.manager, copy.deepcopy(zone._info), zone._loaded) az.zoneName = '|- %s' % host az.zoneState = '' az._info['zoneName'] = az.zoneName az._info['zoneState'] = az.zoneState result.append(az) for (svc, state) in services.items(): # Service tree view item az = AvailabilityZone(zone.manager, copy.deepcopy(zone._info), zone._loaded) az.zoneName = '| |- %s' % svc az.zoneState = '%s %s %s' % ( 'enabled' if state['active'] else 'disabled', ':-)' if state['available'] else 'XXX', state['updated_at']) az._info['zoneName'] = az.zoneName az._info['zoneState'] = az.zoneState result.append(az) return result @utils.service_type('compute') def do_availability_zone_list(cs, _args): """List all the availability zones.""" try: availability_zones = cs.availability_zones.list() except exceptions.Forbidden as e: # policy doesn't allow probably try: availability_zones = cs.availability_zones.list(detailed=False) except Exception: raise e result = [] for zone in availability_zones: result += _treeizeAvailabilityZone(zone) _translate_availability_zone_keys(result) utils.print_list(result, ['Name', 'Status'], sortby_index=None) python-novaclient-2.17.0/novaclient/v1_1/limits.py0000664000175400017540000000602412306432770023211 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # # 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 six.moves.urllib import parse from novaclient import base class Limits(base.Resource): """A collection of RateLimit and AbsoluteLimit objects.""" def __repr__(self): return "" @property def absolute(self): for (name, value) in self._info['absolute'].items(): yield AbsoluteLimit(name, value) @property def rate(self): for group in self._info['rate']: uri = group['uri'] regex = group['regex'] for rate in group['limit']: yield RateLimit(rate['verb'], uri, regex, rate['value'], rate['remaining'], rate['unit'], rate['next-available']) class RateLimit(object): """Data model that represents a flattened view of a single rate limit.""" def __init__(self, verb, uri, regex, value, remain, unit, next_available): self.verb = verb self.uri = uri self.regex = regex self.value = value self.remain = remain self.unit = unit self.next_available = next_available def __eq__(self, other): return self.uri == other.uri \ and self.regex == other.regex \ and self.value == other.value \ and self.verb == other.verb \ and self.remain == other.remain \ and self.unit == other.unit \ and self.next_available == other.next_available def __repr__(self): return "" % (self.verb, self.uri) class AbsoluteLimit(object): """Data model that represents a single absolute limit.""" def __init__(self, name, value): self.name = name self.value = value def __eq__(self, other): return self.value == other.value and self.name == other.name def __repr__(self): return "" % (self.name) class LimitsManager(base.Manager): """Manager object used to interact with limits resource.""" resource_class = Limits def get(self, reserved=False, tenant_id=None): """ Get a specific extension. :rtype: :class:`Limits` """ opts = {} if reserved: opts['reserved'] = 1 if tenant_id: opts['tenant_id'] = tenant_id query_string = "?%s" % parse.urlencode(opts) if opts else "" return self._get("/limits%s" % query_string, "limits") python-novaclient-2.17.0/novaclient/v1_1/certs.py0000664000175400017540000000262312306432770023031 0ustar jenkinsjenkins00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 OpenStack Foundation # 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. """ Certificate interface. """ from novaclient import base class Certificate(base.Resource): def __repr__(self): return "" % \ (len(self.private_key) if self.private_key else 0, len(self.data)) class CertificateManager(base.Manager): """ Manage :class:`Certificate` resources. """ resource_class = Certificate def create(self): """ Create a x509 certificates for a user in tenant. """ return self._create('/os-certificates', {}, 'certificate') def get(self): """ Get root certificate. """ return self._get("/os-certificates/root", 'certificate') python-novaclient-2.17.0/novaclient/v1_1/usage.py0000664000175400017540000000377312306432770023024 0ustar jenkinsjenkins00000000000000# # 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. """ Usage interface. """ from novaclient import base class Usage(base.Resource): """ Usage contains information about a tenant's physical resource usage """ def __repr__(self): return "" class UsageManager(base.ManagerWithFind): """ Manage :class:`Usage` resources. """ resource_class = Usage def list(self, start, end, detailed=False): """ Get usage for all tenants :param start: :class:`datetime.datetime` Start date :param end: :class:`datetime.datetime` End date :param detailed: Whether to include information about each instance whose usage is part of the report :rtype: list of :class:`Usage`. """ return self._list( "/os-simple-tenant-usage?start=%s&end=%s&detailed=%s" % (start.isoformat(), end.isoformat(), int(bool(detailed))), "tenant_usages") def get(self, tenant_id, start, end): """ Get usage for a specific tenant. :param tenant_id: Tenant ID to fetch usage for :param start: :class:`datetime.datetime` Start date :param end: :class:`datetime.datetime` End date :rtype: :class:`Usage` """ return self._get("/os-simple-tenant-usage/%s?start=%s&end=%s" % (tenant_id, start.isoformat(), end.isoformat()), "tenant_usage") python-novaclient-2.17.0/novaclient/v1_1/networks.py0000664000175400017540000001102212306432770023556 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # 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. """ Network interface. """ from novaclient import base from novaclient import exceptions from novaclient.openstack.common.gettextutils import _ class Network(base.Resource): """ A network. """ HUMAN_ID = False NAME_ATTR = "label" def __repr__(self): return "" % self.label def delete(self): self.manager.delete(self) class NetworkManager(base.ManagerWithFind): """ Manage :class:`Network` resources. """ resource_class = Network def list(self): """ Get a list of all networks. :rtype: list of :class:`Network`. """ return self._list("/os-networks", "networks") def get(self, network): """ Get a specific network. :param network: The ID of the :class:`Network` to get. :rtype: :class:`Network` """ return self._get("/os-networks/%s" % base.getid(network), "network") def delete(self, network): """ Delete a specific network. :param network: The ID of the :class:`Network` to delete. """ self._delete("/os-networks/%s" % base.getid(network)) def create(self, **kwargs): """ Create (allocate) a network. The following parameters are optional except for label; cidr or cidr_v6 must be specified, too. :param label: str :param bridge: str :param bridge_interface: str :param cidr: str :param cidr_v6: str :param dns1: str :param dns2: str :param fixed_cidr: str :param gateway: str :param gateway_v6: str :param multi_host: str :param priority: str :param project_id: str :param vlan_start: int :param vpn_start: int :rtype: list of :class:`Network` """ body = {"network": kwargs} return self._create('/os-networks', body, 'network') def disassociate(self, network, disassociate_host=True, disassociate_project=True): """ Disassociate a specific network from project and/or host. :param network: The ID of the :class:`Network`. :param disassociate_host: Whether to disassociate the host :param disassociate_project: Whether to disassociate the project """ if disassociate_host and disassociate_project: body = {"disassociate": None} elif disassociate_project: body = {"disassociate_project": None} elif disassociate_host: body = {"disassociate_host": None} else: raise exceptions.CommandError( _("Must disassociate either host or project or both")) self.api.client.post("/os-networks/%s/action" % base.getid(network), body=body) def associate_host(self, network, host): """ Associate a specific network with a host. :param network: The ID of the :class:`Network`. :param host: The name of the host to associate the network with """ self.api.client.post("/os-networks/%s/action" % base.getid(network), body={"associate_host": host}) def associate_project(self, network): """ Associate a specific network with a project. The project is defined by the project authenticated against :param network: The ID of the :class:`Network`. """ self.api.client.post("/os-networks/add", body={"id": network}) def add(self, network=None): """ Associates the current project with a network. Network can be chosen automatically or provided explicitly. :param network: The ID of the :class:`Network` to associate (optional). """ self.api.client.post( "/os-networks/add", body={"id": base.getid(network) if network else None}) python-novaclient-2.17.0/novaclient/v1_1/volumes.py0000664000175400017540000001301612306432770023401 0ustar jenkinsjenkins00000000000000# Copyright 2011 Denali Systems, Inc. # 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. """ Volume interface (1.1 extension). """ import six from six.moves.urllib import parse from novaclient import base class Volume(base.Resource): """ A volume is an extra block level storage to the OpenStack instances. """ NAME_ATTR = 'display_name' def __repr__(self): return "" % self.id def delete(self): """ Delete this volume. """ self.manager.delete(self) class VolumeManager(base.ManagerWithFind): """ Manage :class:`Volume` resources. """ resource_class = Volume def create(self, size, snapshot_id=None, display_name=None, display_description=None, volume_type=None, availability_zone=None, imageRef=None): """ Create a volume. :param size: Size of volume in GB :param snapshot_id: ID of the snapshot :param display_name: Name of the volume :param display_description: Description of the volume :param volume_type: Type of volume :param availability_zone: Availability Zone for volume :rtype: :class:`Volume` :param imageRef: reference to an image stored in glance """ body = {'volume': {'size': size, 'snapshot_id': snapshot_id, 'display_name': display_name, 'display_description': display_description, 'volume_type': volume_type, 'availability_zone': availability_zone, 'imageRef': imageRef}} return self._create('/volumes', body, 'volume') def get(self, volume_id): """ Get a volume. :param volume_id: The ID of the volume to delete. :rtype: :class:`Volume` """ return self._get("/volumes/%s" % volume_id, "volume") def list(self, detailed=True, search_opts=None): """ Get a list of all volumes. :rtype: list of :class:`Volume` """ search_opts = search_opts or {} qparams = dict((k, v) for (k, v) in six.iteritems(search_opts) if v) query_string = '?%s' % parse.urlencode(qparams) if qparams else '' if detailed is True: return self._list("/volumes/detail%s" % query_string, "volumes") else: return self._list("/volumes%s" % query_string, "volumes") def delete(self, volume): """ Delete a volume. :param volume: The :class:`Volume` to delete. """ self._delete("/volumes/%s" % base.getid(volume)) def create_server_volume(self, server_id, volume_id, device): """ Attach a volume identified by the volume ID to the given server ID :param server_id: The ID of the server :param volume_id: The ID of the volume to attach. :param device: The device name :rtype: :class:`Volume` """ body = {'volumeAttachment': {'volumeId': volume_id, 'device': device}} return self._create("/servers/%s/os-volume_attachments" % server_id, body, "volumeAttachment") def update_server_volume(self, server_id, attachment_id, new_volume_id): """ Update the volume identified by the attachment ID, that is attached to the given server ID :param server_id: The ID of the server :param attachment_id: The ID of the attachment :param new_volume_id: The ID of the new volume to attach :rtype: :class:`Volume` """ body = {'volumeAttachment': {'volumeId': new_volume_id}} return self._update("/servers/%s/os-volume_attachments/%s" % (server_id, attachment_id,), body, "volumeAttachment") def get_server_volume(self, server_id, attachment_id): """ Get the volume identified by the attachment ID, that is attached to the given server ID :param server_id: The ID of the server :param attachment_id: The ID of the attachment :rtype: :class:`Volume` """ return self._get("/servers/%s/os-volume_attachments/%s" % (server_id, attachment_id,), "volumeAttachment") def get_server_volumes(self, server_id): """ Get a list of all the attached volumes for the given server ID :param server_id: The ID of the server :rtype: list of :class:`Volume` """ return self._list("/servers/%s/os-volume_attachments" % server_id, "volumeAttachments") def delete_server_volume(self, server_id, attachment_id): """ Detach a volume identified by the attachment ID from the given server :param server_id: The ID of the server :param attachment_id: The ID of the attachment """ self._delete("/servers/%s/os-volume_attachments/%s" % (server_id, attachment_id,)) python-novaclient-2.17.0/novaclient/v1_1/agents.py0000664000175400017540000000434312306432770023173 0ustar jenkinsjenkins00000000000000# Copyright 2012 IBM Corp. # 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. """ agent interface """ from novaclient import base class Agent(base.Resource): def __repr__(self): return "" % self.agent def _add_details(self, info): dico = 'resource' in info and info['resource'] or info for (k, v) in dico.items(): setattr(self, k, v) class AgentsManager(base.ManagerWithFind): resource_class = Agent def list(self, hypervisor=None): """List all agent builds.""" url = "/os-agents" if hypervisor: url = "/os-agents?hypervisor=%s" % hypervisor return self._list(url, "agents") def _build_update_body(self, version, url, md5hash): return {'para': { 'version': version, 'url': url, 'md5hash': md5hash}} def update(self, id, version, url, md5hash): """Update an existing agent build.""" body = self._build_update_body(version, url, md5hash) return self._update('/os-agents/%s' % id, body, 'agent') def create(self, os, architecture, version, url, md5hash, hypervisor): """Create a new agent build.""" body = {'agent': { 'hypervisor': hypervisor, 'os': os, 'architecture': architecture, 'version': version, 'url': url, 'md5hash': md5hash}} return self._create('/os-agents', body, 'agent') def delete(self, id): """Deletes an existing agent build.""" self._delete('/os-agents/%s' % id) python-novaclient-2.17.0/novaclient/v1_1/keypairs.py0000664000175400017540000000544012306432770023540 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # 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. """ Keypair interface (1.1 extension). """ from novaclient import base class Keypair(base.Resource): """ A keypair is a ssh key that can be injected into a server on launch. """ def __repr__(self): return "" % self.id def _add_details(self, info): dico = 'keypair' in info and \ info['keypair'] or info for (k, v) in dico.items(): # NOTE(rpodolyaka): keypair name allows us to uniquely identify # a specific keypair, while its id attribute # is nothing more than an implementation # detail. We can safely omit the id attribute # here to ensure setattr() won't raise # AttributeError trying to set read-only # property id if k != 'id': setattr(self, k, v) @property def id(self): return self.name def delete(self): self.manager.delete(self) class KeypairManager(base.ManagerWithFind): resource_class = Keypair keypair_prefix = "os-keypairs" def get(self, keypair): """ Get a keypair. :param keypair: The ID of the keypair to get. :rtype: :class:`Keypair` """ return self._get("/%s/%s" % (self.keypair_prefix, base.getid(keypair)), "keypair") def create(self, name, public_key=None): """ Create a keypair :param name: name for the keypair to create :param public_key: existing public key to import """ body = {'keypair': {'name': name}} if public_key: body['keypair']['public_key'] = public_key return self._create('/%s' % self.keypair_prefix, body, 'keypair') def delete(self, key): """ Delete a keypair :param key: The :class:`Keypair` (or its ID) to delete. """ self._delete('/%s/%s' % (self.keypair_prefix, base.getid(key))) def list(self): """ Get a list of keypairs. """ return self._list('/%s' % self.keypair_prefix, 'keypairs') python-novaclient-2.17.0/novaclient/v1_1/contrib/0000775000175400017540000000000012306433034022766 5ustar jenkinsjenkins00000000000000python-novaclient-2.17.0/novaclient/v1_1/contrib/list_extensions.py0000664000175400017540000000260112306432770026577 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # 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 novaclient import base from novaclient import utils class ListExtResource(base.Resource): @property def summary(self): descr = self.description.strip() if not descr: return '??' lines = descr.split("\n") if len(lines) == 1: return lines[0] else: return lines[0] + "..." class ListExtManager(base.Manager): resource_class = ListExtResource def show_all(self): return self._list("/extensions", 'extensions') def do_list_extensions(client, _args): """ List all the os-api extensions that are available. """ extensions = client.list_extensions.show_all() fields = ["Name", "Summary", "Alias", "Updated"] utils.print_list(extensions, fields) python-novaclient-2.17.0/novaclient/v1_1/contrib/assisted_volume_snapshots.py0000664000175400017540000000275212306432770030664 0ustar jenkinsjenkins00000000000000# Copyright (C) 2013, 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. """ Assisted volume snapshots - to be used by Cinder and not end users. """ import json from novaclient import base class Snapshot(base.Resource): def __repr__(self): return "" % self.id def delete(self): """ Delete this snapshot. """ self.manager.delete(self) class AssistedSnapshotManager(base.Manager): resource_class = Snapshot def create(self, volume_id, create_info): body = {'snapshot': {'volume_id': volume_id, 'create_info': create_info}} return self._create('/os-assisted-volume-snapshots', body, 'snapshot') def delete(self, snapshot, delete_info): self._delete("/os-assisted-volume-snapshots/%s?delete_info=%s" % ( base.getid(snapshot), json.dumps(delete_info))) manager_class = AssistedSnapshotManager name = 'assisted_volume_snapshots' python-novaclient-2.17.0/novaclient/v1_1/contrib/deferred_delete.py0000664000175400017540000000176212306432770026456 0ustar jenkinsjenkins00000000000000# Copyright 2013 OpenStack Foundation # # 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 novaclient import utils @utils.arg('server', metavar='', help='Name or ID of server.') def do_force_delete(cs, args): """Force delete a server.""" utils.find_resource(cs.servers, args.server).force_delete() @utils.arg('server', metavar='', help='Name or ID of server.') def do_restore(cs, args): """Restore a soft-deleted server.""" utils.find_resource(cs.servers, args.server).restore() python-novaclient-2.17.0/novaclient/v1_1/contrib/migrations.py0000664000175400017540000000556212306432770025532 0ustar jenkinsjenkins00000000000000# 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. """ migration interface """ from six.moves.urllib import parse from novaclient import base from novaclient.openstack.common.gettextutils import _ from novaclient import utils class Migration(base.Resource): def __repr__(self): return "" % self.id class MigrationManager(base.ManagerWithFind): resource_class = Migration def list(self, host=None, status=None, cell_name=None): """ Get a list of migrations. :param host: (optional) filter migrations by host name. :param status: (optional) filter migrations by status. :param cell_name: (optional) filter migrations for a cell. """ opts = {} if host: opts['host'] = host if status: opts['status'] = status if cell_name: opts['cell_name'] = cell_name # Transform the dict to a sequence of two-element tuples in fixed # order, then the encoded string will be consistent in Python 2&3. new_opts = sorted(opts.items(), key=lambda x: x[0]) query_string = "?%s" % parse.urlencode(new_opts) if new_opts else "" return self._list("/os-migrations%s" % query_string, "migrations") @utils.arg('--host', dest='host', metavar='', help=_('Fetch migrations for the given host.')) @utils.arg('--status', dest='status', metavar='', help=_('Fetch migrations for the given status.')) @utils.arg('--cell_name', dest='cell_name', metavar='', help=_('Fetch migrations for the given cell_name.')) def do_migration_list(cs, args): """Print a list of migrations.""" _print_migrations(cs.migrations.list(args.host, args.status, args.cell_name)) def _print_migrations(migrations): fields = ['Source Node', 'Dest Node', 'Source Compute', 'Dest Compute', 'Dest Host', 'Status', 'Instance UUID', 'Old Flavor', 'New Flavor', 'Created At', 'Updated At'] def old_flavor(migration): return migration.old_instance_type_id def new_flavor(migration): return migration.new_instance_type_id formatters = {'Old Flavor': old_flavor, 'New Flavor': new_flavor} utils.print_list(migrations, fields, formatters) python-novaclient-2.17.0/novaclient/v1_1/contrib/cells.py0000664000175400017540000000453012306432770024452 0ustar jenkinsjenkins00000000000000# Copyright 2013 Rackspace Hosting # 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 novaclient import base from novaclient.openstack.common.gettextutils import _ from novaclient import utils class Cell(base.Resource): def __repr__(self): return "" % self.name class CellsManager(base.Manager): resource_class = Cell def get(self, cell_name): """ Get a cell. :param cell: Name of the :class:`Cell` to get. :rtype: :class:`Cell` """ return self._get("/os-cells/%s" % cell_name, "cell") def capacities(self, cell_name=None): """ Get capacities for a cell. :param cell: Name of the :class:`Cell` to get capacities for. :rtype: :class:`Cell` """ path = ["%s/capacities" % cell_name, "capacities"][cell_name is None] return self._get("/os-cells/%s" % path, "cell") @utils.arg('cell', metavar='', help=_('Name of the cell.')) def do_cell_show(cs, args): """Show details of a given cell.""" cell = cs.cells.get(args.cell) utils.print_dict(cell._info) @utils.arg('--cell', metavar='', help=_("Name of the cell to get the capacities."), default=None) def do_cell_capacities(cs, args): """Get cell capacities for all cells or a given cell.""" cell = cs.cells.capacities(args.cell) print(_("Ram Available: %s MB") % cell.capacities['ram_free']['total_mb']) utils.print_dict(cell.capacities['ram_free']['units_by_mb'], dict_property='Ram(MB)', dict_value="Units") print(_("\nDisk Available: %s MB") % cell.capacities['disk_free']['total_mb']) utils.print_dict(cell.capacities['disk_free']['units_by_mb'], dict_property='Disk(MB)', dict_value="Units") python-novaclient-2.17.0/novaclient/v1_1/contrib/metadata_extensions.py0000664000175400017540000000332612306432770027411 0ustar jenkinsjenkins00000000000000# Copyright 2013 Rackspace Hosting # 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 novaclient.openstack.common.gettextutils import _ from novaclient import utils from novaclient.v1_1 import shell @utils.arg('host', metavar='', help=_('Name of host.')) @utils.arg('action', metavar='', choices=['set', 'delete'], help=_("Actions: 'set' or 'delete'")) @utils.arg('metadata', metavar='', nargs='+', action='append', default=[], help=_('Metadata to set or delete (only key is necessary on ' 'delete)')) def do_host_meta(cs, args): """Set or Delete metadata on all instances of a host.""" hypervisors = cs.hypervisors.search(args.host, servers=True) for hyper in hypervisors: metadata = shell._extract_metadata(args) if hasattr(hyper, 'servers'): for server in hyper.servers: if args.action == 'set': cs.servers.set_meta(server['uuid'], metadata) elif args.action == 'delete': cs.servers.delete_meta(server['uuid'], metadata.keys()) python-novaclient-2.17.0/novaclient/v1_1/contrib/host_evacuate.py0000664000175400017540000000432112306432770026200 0ustar jenkinsjenkins00000000000000# Copyright 2013 Rackspace Hosting # 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 novaclient import base from novaclient.openstack.common.gettextutils import _ from novaclient import utils class EvacuateHostResponse(base.Resource): pass def _server_evacuate(cs, server, args): success = True error_message = "" try: cs.servers.evacuate(server['uuid'], args.target_host, args.on_shared_storage) except Exception as e: success = False error_message = _("Error while evacuating instance: %s") % e return EvacuateHostResponse(base.Manager, {"server_uuid": server['uuid'], "evacuate_accepted": success, "error_message": error_message}) @utils.arg('host', metavar='', help='Name of host.') @utils.arg('--target_host', metavar='', default=None, help=_('Name of target host.')) @utils.arg('--on-shared-storage', dest='on_shared_storage', action="store_true", default=False, help=_('Specifies whether all instances files are on shared ' ' storage')) def do_host_evacuate(cs, args): """Evacuate all instances from failed host to specified one.""" hypervisors = cs.hypervisors.search(args.host, servers=True) response = [] for hyper in hypervisors: if hasattr(hyper, 'servers'): for server in hyper.servers: response.append(_server_evacuate(cs, server, args)) utils.print_list(response, ["Server UUID", "Evacuate Accepted", "Error Message"]) python-novaclient-2.17.0/novaclient/v1_1/contrib/host_servers_migrate.py0000664000175400017540000000350312306432770027605 0ustar jenkinsjenkins00000000000000# Copyright 2013 Rackspace Hosting # 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 novaclient import base from novaclient.openstack.common.gettextutils import _ from novaclient import utils class HostServersMigrateResponse(base.Resource): pass def _server_migrate(cs, server): success = True error_message = "" try: cs.servers.migrate(server['uuid']) except Exception as e: success = False error_message = _("Error while migrating instance: %s") % e return HostServersMigrateResponse(base.Manager, {"server_uuid": server['uuid'], "migration_accepted": success, "error_message": error_message}) @utils.arg('host', metavar='', help='Name of host.') def do_host_servers_migrate(cs, args): """Migrate all instances of the specified host to other available hosts.""" hypervisors = cs.hypervisors.search(args.host, servers=True) response = [] for hyper in hypervisors: if hasattr(hyper, 'servers'): for server in hyper.servers: response.append(_server_migrate(cs, server)) utils.print_list(response, ["Server UUID", "Migration Accepted", "Error Message"]) python-novaclient-2.17.0/novaclient/v1_1/contrib/tenant_networks.py0000664000175400017540000000445012306432770026576 0ustar jenkinsjenkins00000000000000# Copyright 2013 OpenStack Foundation # # 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 novaclient import base from novaclient.openstack.common.gettextutils import _ from novaclient import utils class TenantNetwork(base.Resource): def delete(self): self.manager.delete(network=self) class TenantNetworkManager(base.ManagerWithFind): resource_class = base.Resource def list(self): return self._list('/os-tenant-networks', 'networks') def get(self, network): return self._get('/os-tenant-networks/%s' % base.getid(network), 'network') def delete(self, network): self._delete('/os-tenant-networks/%s' % base.getid(network)) def create(self, label, cidr): body = {'network': {'label': label, 'cidr': cidr}} return self._create('/os-tenant-networks', body, 'network') @utils.arg('network_id', metavar='', help='ID of network') def do_net(cs, args): """ Show a network """ network = cs.tenant_networks.get(args.network_id) utils.print_dict(network._info) def do_net_list(cs, args): """ List networks """ networks = cs.tenant_networks.list() utils.print_list(networks, ['ID', 'Label', 'CIDR']) @utils.arg('label', metavar='', help=_('Network label (ex. my_new_network)')) @utils.arg('cidr', metavar='', help=_('IP block to allocate from (ex. 172.16.0.0/24 or ' '2001:DB8::/64)')) def do_net_create(cs, args): """ Create a network """ network = cs.tenant_networks.create(args.label, args.cidr) utils.print_dict(network._info) @utils.arg('network_id', metavar='', help='ID of network') def do_net_delete(cs, args): """ Delete a network """ cs.tenant_networks.delete(args.network_id) python-novaclient-2.17.0/novaclient/v1_1/contrib/baremetal.py0000664000175400017540000002327512306432770025313 0ustar jenkinsjenkins00000000000000# Copyright 2013 Hewlett-Packard Development Company, L.P. # 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. """ Baremetal interface (v2 extension). """ from novaclient import base from novaclient.openstack.common.gettextutils import _ from novaclient import utils class BareMetalNode(base.Resource): """ A baremetal node (typically a physical server or an empty VM). """ def __repr__(self): return "" % self.id class BareMetalNodeInterface(base.Resource): """ An interface belonging to a baremetal node. """ def __repr__(self): return "" % self.id class BareMetalNodeManager(base.ManagerWithFind): """ Manage :class:`BareMetalNode` resources. """ resource_class = BareMetalNode def create(self, service_host, cpus, memory_mb, local_gb, prov_mac_address, pm_address=None, pm_user=None, pm_password=None, terminal_port=None): """ Create a baremetal node. :param service_host: Name of controlling compute host :param cpus: Number of CPUs in the node :param memory_mb: Megabytes of RAM in the node :param local_gb: Gigabytes of local storage in the node :param pm_address: Power management IP for the node :param pm_user: Username for the node's power management :param pm_password: Password for the node's power management :param prov_mac_address: MAC address to provision the node :param terminal_port: ShellInABox port :rtype: :class:`BareMetalNode` """ body = {'node': {'service_host': service_host, 'cpus': cpus, 'memory_mb': memory_mb, 'local_gb': local_gb, 'pm_address': pm_address, 'pm_user': pm_user, 'pm_password': pm_password, 'prov_mac_address': prov_mac_address, 'terminal_port': terminal_port}} return self._create('/os-baremetal-nodes', body, 'node') def delete(self, node): """ Delete a baremetal node. :param node: The :class:`BareMetalNode` to delete. """ self._delete('/os-baremetal-nodes/%s' % base.getid(node)) def get(self, node_id): """ Get a baremetal node. :param node_id: The ID of the node to delete. :rtype: :class:`BareMetalNode` """ return self._get("/os-baremetal-nodes/%s" % node_id, 'node') def list(self): """ Get a list of all baremetal nodes. :rtype: list of :class:`BareMetalNode` """ return self._list('/os-baremetal-nodes', 'nodes') def add_interface(self, node_id, address, datapath_id=0, port_no=0): """ Add an interface to a baremetal node. :param node_id: The ID of the node to modify. :param address: The MAC address to add. :param datapath_id: Datapath ID of OpenFlow switch for the interface :param port_no: OpenFlow port number for the interface :rtype: :class:`BareMetalNodeInterface` """ body = {'add_interface': {'address': address, 'datapath_id': datapath_id, 'port_no': port_no}} url = '/os-baremetal-nodes/%s/action' % node_id _resp, body = self.api.client.post(url, body=body) return BareMetalNodeInterface(self, body['interface']) def remove_interface(self, node_id, address): """ Remove an interface from a baremetal node. :param node_id: The ID of the node to modify. :param address: The MAC address to remove. :rtype: bool """ req_body = {'remove_interface': {'address': address}} url = '/os-baremetal-nodes/%s/action' % node_id self.api.client.post(url, body=req_body) def list_interfaces(self, node_id): """ List the interfaces on a baremetal node. :param node_id: The ID of the node to list. :rtype: list """ interfaces = [] node = self._get("/os-baremetal-nodes/%s" % node_id, 'node') for interface in node.interfaces: interface_object = BareMetalNodeInterface(self, interface) interfaces.append(interface_object) return interfaces @utils.arg('service_host', metavar='', help=_('Name of nova compute host which will control this baremetal ' 'node')) @utils.arg('cpus', metavar='', type=int, help=_('Number of CPUs in the node')) @utils.arg('memory_mb', metavar='', type=int, help=_('Megabytes of RAM in the node')) @utils.arg('local_gb', metavar='', type=int, help=_('Gigabytes of local storage in the node')) @utils.arg('prov_mac_address', metavar='', help=_('MAC address to provision the node')) @utils.arg('--pm_address', default=None, metavar='', help=_('Power management IP for the node')) @utils.arg('--pm_user', default=None, metavar='', help=_('Username for the node\'s power management')) @utils.arg('--pm_password', default=None, metavar='', help=_('Password for the node\'s power management')) @utils.arg('--terminal_port', default=None, metavar='', type=int, help=_('ShellInABox port?')) def do_baremetal_node_create(cs, args): """Create a baremetal node.""" node = cs.baremetal.create(args.service_host, args.cpus, args.memory_mb, args.local_gb, args.prov_mac_address, pm_address=args.pm_address, pm_user=args.pm_user, pm_password=args.pm_password, terminal_port=args.terminal_port) _print_baremetal_resource(node) @utils.arg('node', metavar='', help='ID of the node to delete.') def do_baremetal_node_delete(cs, args): """Remove a baremetal node and any associated interfaces.""" node = _find_baremetal_node(cs, args.node) cs.baremetal.delete(node) def _translate_baremetal_node_keys(collection): convert = [('service_host', 'host'), ('local_gb', 'disk_gb'), ('prov_mac_address', 'mac_address'), ('pm_address', 'pm_address'), ('pm_user', 'pm_username'), ('pm_password', 'pm_password'), ('terminal_port', 'terminal_port'), ] for item in collection: keys = item.__dict__.keys() for from_key, to_key in convert: if from_key in keys and to_key not in keys: setattr(item, to_key, item._info[from_key]) def _print_baremetal_nodes_list(nodes): """Print the list of baremetal nodes.""" _translate_baremetal_node_keys(nodes) utils.print_list(nodes, [ 'ID', 'Host', 'CPUs', 'Memory_MB', 'Disk_GB', 'MAC Address', 'PM Address', 'PM Username', 'PM Password', 'Terminal Port', ]) def do_baremetal_node_list(cs, _args): """Print list of available baremetal nodes.""" nodes = cs.baremetal.list() _print_baremetal_nodes_list(nodes) def _find_baremetal_node(cs, node): """Get a node by ID.""" return utils.find_resource(cs.baremetal, node) def _print_baremetal_resource(resource): """Print details of a baremetal resource.""" info = resource._info.copy() utils.print_dict(info) def _print_baremetal_node_interfaces(interfaces): """Print interfaces of a baremetal node.""" utils.print_list(interfaces, [ 'ID', 'Datapath_ID', 'Port_No', 'Address', ]) @utils.arg('node', metavar='', help="ID of node") def do_baremetal_node_show(cs, args): """Show information about a baremetal node.""" node = _find_baremetal_node(cs, args.node) _print_baremetal_resource(node) @utils.arg('node', metavar='', help=_("ID of node")) @utils.arg('address', metavar='
', help=_("MAC address of interface")) @utils.arg('--datapath_id', default=0, metavar='', help=_("OpenFlow Datapath ID of interface")) @utils.arg('--port_no', default=0, metavar='', help=_("OpenFlow port number of interface")) def do_baremetal_interface_add(cs, args): """Add a network interface to a baremetal node.""" bmif = cs.baremetal.add_interface(args.node, args.address, args.datapath_id, args.port_no) _print_baremetal_resource(bmif) @utils.arg('node', metavar='', help=_("ID of node")) @utils.arg('address', metavar='
', help=_("MAC address of interface")) def do_baremetal_interface_remove(cs, args): """Remove a network interface from a baremetal node.""" cs.baremetal.remove_interface(args.node, args.address) @utils.arg('node', metavar='', help="ID of node") def do_baremetal_interface_list(cs, args): """List network interfaces associated with a baremetal node.""" interfaces = cs.baremetal.list_interfaces(args.node) _print_baremetal_node_interfaces(interfaces) python-novaclient-2.17.0/novaclient/v1_1/contrib/server_external_events.py0000664000175400017540000000250512306432771030145 0ustar jenkinsjenkins00000000000000# Copyright (C) 2014, 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. """ External event triggering for servers, not to be used by users. """ from novaclient import base class Event(base.Resource): def __repr__(self): return "" % self.name class ServerExternalEventManager(base.Manager): resource_class = Event def create(self, events): """Create one or more server events. :param:events: A list of dictionaries containing 'server_uuid', 'name', 'status', and 'tag' (which may be absent) """ body = {'events': events} return self._create('/os-server-external-events', body, 'events', return_raw=True) manager_class = ServerExternalEventManager name = 'server_external_events' python-novaclient-2.17.0/novaclient/v1_1/contrib/__init__.py0000664000175400017540000000000012306432770025073 0ustar jenkinsjenkins00000000000000python-novaclient-2.17.0/novaclient/v1_1/contrib/instance_action.py0000664000175400017540000000447212306432770026516 0ustar jenkinsjenkins00000000000000# Copyright 2013 Rackspace Hosting # 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 pprint from novaclient import base from novaclient.openstack.common.gettextutils import _ from novaclient import utils class InstanceActionManager(base.ManagerWithFind): resource_class = base.Resource def get(self, server, request_id): """ Get details of an action performed on an instance. :param request_id: The request_id of the action to get. """ return self._get("/servers/%s/os-instance-actions/%s" % (base.getid(server), request_id), 'instanceAction') def list(self, server): """ Get a list of actions performed on an server. """ return self._list('/servers/%s/os-instance-actions' % base.getid(server), 'instanceActions') @utils.arg('server', metavar='', help=_('Name or UUID of the server to show an action for.')) @utils.arg('request_id', metavar='', help=_('Request ID of the action to get.')) def do_instance_action(cs, args): """Show an action.""" server = utils.find_resource(cs.servers, args.server) action_resource = cs.instance_action.get(server, args.request_id) action = action_resource._info if 'events' in action: action['events'] = pprint.pformat(action['events']) utils.print_dict(action) @utils.arg('server', metavar='', help=_('Name or UUID of the server to list actions for.')) def do_instance_action_list(cs, args): """List actions on a server.""" server = utils.find_resource(cs.servers, args.server) actions = cs.instance_action.list(server) utils.print_list(actions, ['Action', 'Request_ID', 'Message', 'Start_Time'], sortby_index=3) python-novaclient-2.17.0/novaclient/v1_1/security_group_rules.py0000664000175400017540000000530212306432770026203 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # 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. """ Security group rules interface (1.1 extension). """ from novaclient import base from novaclient import exceptions from novaclient.openstack.common.gettextutils import _ class SecurityGroupRule(base.Resource): def __str__(self): return str(self.id) def delete(self): self.manager.delete(self) class SecurityGroupRuleManager(base.Manager): resource_class = SecurityGroupRule def create(self, parent_group_id, ip_protocol=None, from_port=None, to_port=None, cidr=None, group_id=None): """ Create a security group rule :param ip_protocol: IP protocol, one of 'tcp', 'udp' or 'icmp' :param from_port: Source port :param to_port: Destination port :param cidr: Destination IP address(es) in CIDR notation :param group_id: Security group id (int) :param parent_group_id: Parent security group id (int) """ try: from_port = int(from_port) except (TypeError, ValueError): raise exceptions.CommandError(_("From port must be an integer.")) try: to_port = int(to_port) except (TypeError, ValueError): raise exceptions.CommandError(_("To port must be an integer.")) if ip_protocol.upper() not in ['TCP', 'UDP', 'ICMP']: raise exceptions.CommandError(_("Ip protocol must be 'tcp', 'udp'" ", or 'icmp'.")) body = {"security_group_rule": { "ip_protocol": ip_protocol, "from_port": from_port, "to_port": to_port, "cidr": cidr, "group_id": group_id, "parent_group_id": parent_group_id}} return self._create('/os-security-group-rules', body, 'security_group_rule') def delete(self, rule): """ Delete a security group rule :param rule: The security group rule to delete (ID or Class) """ self._delete('/os-security-group-rules/%s' % base.getid(rule)) python-novaclient-2.17.0/novaclient/v1_1/quota_classes.py0000664000175400017540000000273412306432770024562 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # 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 novaclient import base class QuotaClassSet(base.Resource): def update(self, *args, **kwargs): return self.manager.update(self.id, *args, **kwargs) class QuotaClassSetManager(base.Manager): resource_class = QuotaClassSet def get(self, class_name): return self._get("/os-quota-class-sets/%s" % (class_name), "quota_class_set") def _update_body(self, **kwargs): return {'quota_class_set': kwargs} def update(self, class_name, **kwargs): body = self._update_body(**kwargs) for key in list(body['quota_class_set']): if body['quota_class_set'][key] is None: body['quota_class_set'].pop(key) return self._update('/os-quota-class-sets/%s' % (class_name), body, 'quota_class_set') python-novaclient-2.17.0/novaclient/v1_1/client.py0000664000175400017540000001616012306432770023170 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # Copyright 2013 IBM Corp. # # 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 novaclient import client from novaclient.v1_1 import agents from novaclient.v1_1 import aggregates from novaclient.v1_1 import availability_zones from novaclient.v1_1 import certs from novaclient.v1_1 import cloudpipe from novaclient.v1_1 import fixed_ips from novaclient.v1_1 import flavor_access from novaclient.v1_1 import flavors from novaclient.v1_1 import floating_ip_dns from novaclient.v1_1 import floating_ip_pools from novaclient.v1_1 import floating_ips from novaclient.v1_1 import floating_ips_bulk from novaclient.v1_1 import fping from novaclient.v1_1 import hosts from novaclient.v1_1 import hypervisors from novaclient.v1_1 import images from novaclient.v1_1 import keypairs from novaclient.v1_1 import limits from novaclient.v1_1 import networks from novaclient.v1_1 import quota_classes from novaclient.v1_1 import quotas from novaclient.v1_1 import security_group_rules from novaclient.v1_1 import security_groups from novaclient.v1_1 import servers from novaclient.v1_1 import services from novaclient.v1_1 import usage from novaclient.v1_1 import virtual_interfaces from novaclient.v1_1 import volume_snapshots from novaclient.v1_1 import volume_types from novaclient.v1_1 import volumes class Client(object): """ Top-level object to access the OpenStack Compute API. Create an instance with your creds:: >>> client = Client(USERNAME, PASSWORD, PROJECT_ID, AUTH_URL) Then call methods on its managers:: >>> client.servers.list() ... >>> client.flavors.list() ... """ # FIXME(jesse): project_id isn't required to authenticate def __init__(self, username, api_key, project_id, auth_url=None, insecure=False, timeout=None, proxy_tenant_id=None, proxy_token=None, region_name=None, endpoint_type='publicURL', extensions=None, service_type='compute', service_name=None, volume_service_name=None, timings=False, bypass_url=None, os_cache=False, no_cache=True, http_log_debug=False, auth_system='keystone', auth_plugin=None, auth_token=None, cacert=None, tenant_id=None): # FIXME(comstud): Rename the api_key argument above when we # know it's not being used as keyword argument password = api_key self.projectid = project_id self.tenant_id = tenant_id self.flavors = flavors.FlavorManager(self) self.flavor_access = flavor_access.FlavorAccessManager(self) self.images = images.ImageManager(self) self.limits = limits.LimitsManager(self) self.servers = servers.ServerManager(self) # extensions self.agents = agents.AgentsManager(self) self.dns_domains = floating_ip_dns.FloatingIPDNSDomainManager(self) self.dns_entries = floating_ip_dns.FloatingIPDNSEntryManager(self) self.cloudpipe = cloudpipe.CloudpipeManager(self) self.certs = certs.CertificateManager(self) self.floating_ips = floating_ips.FloatingIPManager(self) self.floating_ip_pools = floating_ip_pools.FloatingIPPoolManager(self) self.fping = fping.FpingManager(self) self.volumes = volumes.VolumeManager(self) self.volume_snapshots = volume_snapshots.SnapshotManager(self) self.volume_types = volume_types.VolumeTypeManager(self) self.keypairs = keypairs.KeypairManager(self) self.networks = networks.NetworkManager(self) self.quota_classes = quota_classes.QuotaClassSetManager(self) self.quotas = quotas.QuotaSetManager(self) self.security_groups = security_groups.SecurityGroupManager(self) self.security_group_rules = \ security_group_rules.SecurityGroupRuleManager(self) self.usage = usage.UsageManager(self) self.virtual_interfaces = \ virtual_interfaces.VirtualInterfaceManager(self) self.aggregates = aggregates.AggregateManager(self) self.hosts = hosts.HostManager(self) self.hypervisors = hypervisors.HypervisorManager(self) self.services = services.ServiceManager(self) self.fixed_ips = fixed_ips.FixedIPsManager(self) self.floating_ips_bulk = floating_ips_bulk.FloatingIPBulkManager(self) self.os_cache = os_cache or not no_cache self.availability_zones = \ availability_zones.AvailabilityZoneManager(self) # Add in any extensions... if extensions: for extension in extensions: if extension.manager_class: setattr(self, extension.name, extension.manager_class(self)) self.client = client.HTTPClient(username, password, projectid=project_id, tenant_id=tenant_id, auth_url=auth_url, auth_token=auth_token, insecure=insecure, timeout=timeout, auth_system=auth_system, auth_plugin=auth_plugin, proxy_token=proxy_token, proxy_tenant_id=proxy_tenant_id, region_name=region_name, endpoint_type=endpoint_type, service_type=service_type, service_name=service_name, volume_service_name=volume_service_name, timings=timings, bypass_url=bypass_url, os_cache=self.os_cache, http_log_debug=http_log_debug, cacert=cacert) def set_management_url(self, url): self.client.set_management_url(url) def get_timings(self): return self.client.get_timings() def reset_timings(self): self.client.reset_timings() def authenticate(self): """ Authenticate against the server. Normally this is called automatically when you first access the API, but you can call this method to force authentication right now. Returns on success; raises :exc:`exceptions.Unauthorized` if the credentials are wrong. """ self.client.authenticate() python-novaclient-2.17.0/novaclient/v1_1/virtual_interfaces.py0000664000175400017540000000201712306432770025577 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # 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. """ Virtual Interfaces (1.1 extension). """ from novaclient import base class VirtualInterface(base.Resource): def __repr__(self): pass class VirtualInterfaceManager(base.ManagerWithFind): resource_class = VirtualInterface def list(self, instance_id): return self._list('/servers/%s/os-virtual-interfaces' % instance_id, 'virtual_interfaces') python-novaclient-2.17.0/novaclient/v1_1/__init__.py0000664000175400017540000000126512306432770023451 0ustar jenkinsjenkins00000000000000# Copyright (c) 2012 OpenStack Foundation # # 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 novaclient.v1_1.client import Client # noqa python-novaclient-2.17.0/novaclient/v1_1/hosts.py0000664000175400017540000000406312306432770023051 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # 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. """ host interface (1.1 extension). """ from novaclient import base class Host(base.Resource): def __repr__(self): return "" % self.host_name def _add_details(self, info): dico = 'resource' in info and info['resource'] or info for (k, v) in dico.items(): setattr(self, k, v) def update(self, values): return self.manager.update(self.host, values) def startup(self): return self.manager.host_action(self.host, 'startup') def shutdown(self): return self.manager.host_action(self.host, 'shutdown') def reboot(self): return self.manager.host_action(self.host, 'reboot') class HostManager(base.ManagerWithFind): resource_class = Host def get(self, host): """ Describes cpu/memory/hdd info for host. :param host: destination host name. """ return self._list("/os-hosts/%s" % host, "host") def update(self, host, values): """Update status or maintenance mode for the host.""" return self._update("/os-hosts/%s" % host, values) def host_action(self, host, action): """Perform an action on a host.""" url = '/os-hosts/{0}/{1}'.format(host, action) return self.api.client.get(url) def list(self, zone=None): url = '/os-hosts' if zone: url = '/os-hosts?zone=%s' % zone return self._list(url, "hosts") list_all = list python-novaclient-2.17.0/novaclient/v1_1/services.py0000664000175400017540000000454212306432770023536 0ustar jenkinsjenkins00000000000000# Copyright 2012 IBM Corp. # 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 interface """ from novaclient import base class Service(base.Resource): def __repr__(self): return "" % self.binary def _add_details(self, info): dico = 'resource' in info and info['resource'] or info for (k, v) in dico.items(): setattr(self, k, v) class ServiceManager(base.ManagerWithFind): resource_class = Service def list(self, host=None, binary=None): """ Describes cpu/memory/hdd info for host. :param host: destination host name. """ url = "/os-services" filters = [] if host: filters.append("host=%s" % host) if binary: filters.append("binary=%s" % binary) if filters: url = "%s?%s" % (url, "&".join(filters)) return self._list(url, "services") def _update_body(self, host, binary, disabled_reason=None): body = {"host": host, "binary": binary} if disabled_reason is not None: body["disabled_reason"] = disabled_reason return body def enable(self, host, binary): """Enable the service specified by hostname and binary.""" body = self._update_body(host, binary) return self._update("/os-services/enable", body, "service") def disable(self, host, binary): """Disable the service specified by hostname and binary.""" body = self._update_body(host, binary) return self._update("/os-services/disable", body, "service") def disable_log_reason(self, host, binary, reason): """Disable the service with reason.""" body = self._update_body(host, binary, reason) return self._update("/os-services/disable-log-reason", body, "service") python-novaclient-2.17.0/novaclient/tests/0000775000175400017540000000000012306433034021722 5ustar jenkinsjenkins00000000000000python-novaclient-2.17.0/novaclient/tests/test_shell.py0000664000175400017540000002223112306432770024450 0ustar jenkinsjenkins00000000000000# # 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 prettytable import re import six import sys from distutils.version import StrictVersion import fixtures import mock from testtools import matchers import novaclient.client from novaclient import exceptions import novaclient.shell from novaclient.tests import utils FAKE_ENV = {'OS_USERNAME': 'username', 'OS_PASSWORD': 'password', 'OS_TENANT_NAME': 'tenant_name', 'OS_AUTH_URL': 'http://no.where'} FAKE_ENV2 = {'OS_USERNAME': 'username', 'OS_PASSWORD': 'password', 'OS_TENANT_ID': 'tenant_id', 'OS_AUTH_URL': 'http://no.where'} class ShellTest(utils.TestCase): def make_env(self, exclude=None, fake_env=FAKE_ENV): env = dict((k, v) for k, v in fake_env.items() if k != exclude) self.useFixture(fixtures.MonkeyPatch('os.environ', env)) def setUp(self): super(ShellTest, self).setUp() self.useFixture(fixtures.MonkeyPatch( 'novaclient.client.get_client_class', mock.MagicMock)) self.nc_util = mock.patch( 'novaclient.openstack.common.cliutils.isunauthenticated').start() self.nc_util.return_value = False def shell(self, argstr, exitcodes=(0,)): orig = sys.stdout orig_stderr = sys.stderr try: sys.stdout = six.StringIO() sys.stderr = six.StringIO() _shell = novaclient.shell.OpenStackComputeShell() _shell.main(argstr.split()) except SystemExit: exc_type, exc_value, exc_traceback = sys.exc_info() self.assertIn(exc_value.code, exitcodes) finally: stdout = sys.stdout.getvalue() sys.stdout.close() sys.stdout = orig stderr = sys.stderr.getvalue() sys.stderr.close() sys.stderr = orig_stderr return (stdout, stderr) def test_help_unknown_command(self): self.assertRaises(exceptions.CommandError, self.shell, 'help foofoo') def test_invalid_timeout(self): for f in [0, -1, -10]: cmd_text = '--timeout %s' % (f) stdout, stderr = self.shell(cmd_text, exitcodes=[0, 2]) required = [ 'argument --timeout: %s must be greater than 0' % (f), ] for r in required: self.assertIn(r, stderr) def test_help(self): required = [ '.*?^usage: ', '.*?^\s+root-password\s+Change the root password', '.*?^See "nova help COMMAND" for help on a specific command', ] stdout, stderr = self.shell('help') for r in required: self.assertThat((stdout + stderr), matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE)) def test_help_on_subcommand(self): required = [ '.*?^usage: nova root-password', '.*?^Change the root password', '.*?^Positional arguments:', ] stdout, stderr = self.shell('help root-password') for r in required: self.assertThat((stdout + stderr), matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE)) def test_help_no_options(self): required = [ '.*?^usage: ', '.*?^\s+root-password\s+Change the root password', '.*?^See "nova help COMMAND" for help on a specific command', ] stdout, stderr = self.shell('') for r in required: self.assertThat((stdout + stderr), matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE)) def test_bash_completion(self): stdout, stderr = self.shell('bash-completion') # just check we have some output required = [ '.*--matching', '.*--wrap', '.*help', '.*secgroup-delete-rule', '.*--priority'] for r in required: self.assertThat((stdout + stderr), matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE)) def test_no_username(self): required = ('You must provide a username' ' via either --os-username or env[OS_USERNAME]',) self.make_env(exclude='OS_USERNAME') try: self.shell('list') except exceptions.CommandError as message: self.assertEqual(required, message.args) else: self.fail('CommandError not raised') def test_no_tenant_name(self): required = ('You must provide a tenant name or tenant id' ' via --os-tenant-name, --os-tenant-id,' ' env[OS_TENANT_NAME] or env[OS_TENANT_ID]',) self.make_env(exclude='OS_TENANT_NAME') try: self.shell('list') except exceptions.CommandError as message: self.assertEqual(required, message.args) else: self.fail('CommandError not raised') def test_no_tenant_id(self): required = ('You must provide a tenant name or tenant id' ' via --os-tenant-name, --os-tenant-id,' ' env[OS_TENANT_NAME] or env[OS_TENANT_ID]',) self.make_env(exclude='OS_TENANT_ID', fake_env=FAKE_ENV2) try: self.shell('list') except exceptions.CommandError as message: self.assertEqual(required, message.args) else: self.fail('CommandError not raised') def test_no_auth_url(self): required = ('You must provide an auth url' ' via either --os-auth-url or env[OS_AUTH_URL] or' ' specify an auth_system which defines a default url' ' with --os-auth-system or env[OS_AUTH_SYSTEM]',) self.make_env(exclude='OS_AUTH_URL') try: self.shell('list') except exceptions.CommandError as message: self.assertEqual(required, message.args) else: self.fail('CommandError not raised') @mock.patch('sys.stdin', side_effect=mock.MagicMock) @mock.patch('getpass.getpass', return_value='password') def test_password(self, mock_getpass, mock_stdin): mock_stdin.encoding = "utf-8" # default output of empty tables differs depending between prettytable # versions if (hasattr(prettytable, '__version__') and StrictVersion(prettytable.__version__) < StrictVersion('0.7.2')): ex = '\n' else: ex = ( '+----+------+--------+------------+-------------+----------+\n' '| ID | Name | Status | Task State | Power State | Networks |\n' '+----+------+--------+------------+-------------+----------+\n' '+----+------+--------+------------+-------------+----------+\n' ) self.make_env(exclude='OS_PASSWORD') stdout, stderr = self.shell('list') self.assertEqual((stdout + stderr), ex) @mock.patch('sys.stdin', side_effect=mock.MagicMock) @mock.patch('getpass.getpass', side_effect=EOFError) def test_no_password(self, mock_getpass, mock_stdin): required = ('Expecting a password provided' ' via either --os-password, env[OS_PASSWORD],' ' or prompted response',) self.make_env(exclude='OS_PASSWORD') try: self.shell('list') except exceptions.CommandError as message: self.assertEqual(required, message.args) else: self.fail('CommandError not raised') def _test_service_type(self, version, service_type, mock_client): if version is None: cmd = 'list' else: cmd = '--os-compute-api-version %s list' % version self.make_env() self.shell(cmd) _, client_kwargs = mock_client.call_args_list[0] self.assertEqual(service_type, client_kwargs['service_type']) @mock.patch('novaclient.client.Client') def test_default_service_type(self, mock_client): self._test_service_type(None, 'compute', mock_client) @mock.patch('novaclient.client.Client') def test_v1_1_service_type(self, mock_client): self._test_service_type('1.1', 'compute', mock_client) @mock.patch('novaclient.client.Client') def test_v2_service_type(self, mock_client): self._test_service_type('2', 'compute', mock_client) @mock.patch('novaclient.client.Client') def test_v3_service_type(self, mock_client): self._test_service_type('3', 'computev3', mock_client) @mock.patch('novaclient.client.Client') def test_v_unknown_service_type(self, mock_client): self._test_service_type('unknown', 'compute', mock_client) python-novaclient-2.17.0/novaclient/tests/v3/0000775000175400017540000000000012306433034022252 5ustar jenkinsjenkins00000000000000python-novaclient-2.17.0/novaclient/tests/v3/test_shell.py0000664000175400017540000004415512306432770025011 0ustar jenkinsjenkins00000000000000# Copyright 2013 Cloudwatt # Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 OpenStack Foundation # Copyright 2012 IBM Corp. # 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 os import fixtures import mock import six from novaclient import exceptions import novaclient.shell from novaclient.tests import utils from novaclient.tests.v3 import fakes class ShellFixture(fixtures.Fixture): def setUp(self): super(ShellFixture, self).setUp() self.shell = novaclient.shell.OpenStackComputeShell() def tearDown(self): # For some method like test_image_meta_bad_action we are # testing a SystemExit to be thrown and object self.shell has # no time to get instantatiated which is OK in this case, so # we make sure the method is there before launching it. if hasattr(self.shell, 'cs'): self.shell.cs.clear_callstack() super(ShellFixture, self).tearDown() class ShellTest(utils.TestCase): FAKE_ENV = { 'NOVA_USERNAME': 'username', 'NOVA_PASSWORD': 'password', 'NOVA_PROJECT_ID': 'project_id', 'OS_COMPUTE_API_VERSION': '3', 'NOVA_URL': 'http://no.where', } def setUp(self): """Run before each test.""" super(ShellTest, self).setUp() for var in self.FAKE_ENV: self.useFixture(fixtures.EnvironmentVariable(var, self.FAKE_ENV[var])) self.shell = self.useFixture(ShellFixture()).shell self.useFixture(fixtures.MonkeyPatch( 'novaclient.client.get_client_class', lambda *_: fakes.FakeClient)) @mock.patch('sys.stdout', new_callable=six.StringIO) def run_command(self, cmd, mock_stdout): if isinstance(cmd, list): self.shell.main(cmd) else: self.shell.main(cmd.split()) return mock_stdout.getvalue() def assert_called(self, method, url, body=None, **kwargs): return self.shell.cs.assert_called(method, url, body, **kwargs) def assert_called_anytime(self, method, url, body=None): return self.shell.cs.assert_called_anytime(method, url, body) def test_list_deleted(self): self.run_command('list --deleted') self.assert_called('GET', '/servers/detail?deleted=True') def test_aggregate_list(self): self.run_command('aggregate-list') self.assert_called('GET', '/os-aggregates') def test_aggregate_create(self): self.run_command('aggregate-create test_name nova1') body = {"aggregate": {"name": "test_name", "availability_zone": "nova1"}} self.assert_called('POST', '/os-aggregates', body, pos=-2) self.assert_called('GET', '/os-aggregates/1', pos=-1) def test_aggregate_delete_by_id(self): self.run_command('aggregate-delete 1') self.assert_called('DELETE', '/os-aggregates/1') def test_aggregate_delete_by_name(self): self.run_command('aggregate-delete test') self.assert_called('DELETE', '/os-aggregates/1') def test_aggregate_update_by_id(self): self.run_command('aggregate-update 1 new_name') body = {"aggregate": {"name": "new_name"}} self.assert_called('PUT', '/os-aggregates/1', body, pos=-2) self.assert_called('GET', '/os-aggregates/1', pos=-1) def test_aggregate_update_by_name(self): self.run_command('aggregate-update test new_name') body = {"aggregate": {"name": "new_name"}} self.assert_called('PUT', '/os-aggregates/1', body, pos=-2) self.assert_called('GET', '/os-aggregates/1', pos=-1) def test_aggregate_update_with_availability_zone_by_id(self): self.run_command('aggregate-update 1 foo new_zone') body = {"aggregate": {"name": "foo", "availability_zone": "new_zone"}} self.assert_called('PUT', '/os-aggregates/1', body, pos=-2) self.assert_called('GET', '/os-aggregates/1', pos=-1) def test_aggregate_update_with_availability_zone_by_name(self): self.run_command('aggregate-update test foo new_zone') body = {"aggregate": {"name": "foo", "availability_zone": "new_zone"}} self.assert_called('PUT', '/os-aggregates/1', body, pos=-2) self.assert_called('GET', '/os-aggregates/1', pos=-1) def test_aggregate_set_metadata_by_id(self): self.run_command('aggregate-set-metadata 1 foo=bar delete_key') body = {"set_metadata": {"metadata": {"foo": "bar", "delete_key": None}}} self.assert_called('POST', '/os-aggregates/1/action', body, pos=-2) self.assert_called('GET', '/os-aggregates/1', pos=-1) def test_aggregate_set_metadata_by_name(self): self.run_command('aggregate-set-metadata test foo=bar delete_key') body = {"set_metadata": {"metadata": {"foo": "bar", "delete_key": None}}} self.assert_called('POST', '/os-aggregates/1/action', body, pos=-2) self.assert_called('GET', '/os-aggregates/1', pos=-1) def test_aggregate_add_host_by_id(self): self.run_command('aggregate-add-host 1 host1') body = {"add_host": {"host": "host1"}} self.assert_called('POST', '/os-aggregates/1/action', body, pos=-2) self.assert_called('GET', '/os-aggregates/1', pos=-1) def test_aggregate_add_host_by_name(self): self.run_command('aggregate-add-host test host1') body = {"add_host": {"host": "host1"}} self.assert_called('POST', '/os-aggregates/1/action', body, pos=-2) self.assert_called('GET', '/os-aggregates/1', pos=-1) def test_aggregate_remove_host_by_id(self): self.run_command('aggregate-remove-host 1 host1') body = {"remove_host": {"host": "host1"}} self.assert_called('POST', '/os-aggregates/1/action', body, pos=-2) self.assert_called('GET', '/os-aggregates/1', pos=-1) def test_aggregate_remove_host_by_name(self): self.run_command('aggregate-remove-host test host1') body = {"remove_host": {"host": "host1"}} self.assert_called('POST', '/os-aggregates/1/action', body, pos=-2) self.assert_called('GET', '/os-aggregates/1', pos=-1) def test_aggregate_details_by_id(self): self.run_command('aggregate-details 1') self.assert_called('GET', '/os-aggregates/1') def test_aggregate_details_by_name(self): self.run_command('aggregate-details test') self.assert_called('GET', '/os-aggregates') def test_boot(self): self.run_command('boot --flavor 1 --image 1 some-server') self.assert_called_anytime( 'POST', '/servers', {'server': { 'flavor_ref': '1', 'name': 'some-server', 'image_ref': '1', 'os-multiple-create:min_count': 1, 'os-multiple-create:max_count': 1, }}, ) def test_boot_multiple(self): self.run_command('boot --flavor 1 --image 1' ' --num-instances 3 some-server') self.assert_called_anytime( 'POST', '/servers', {'server': { 'flavor_ref': '1', 'name': 'some-server', 'image_ref': '1', 'os-multiple-create:min_count': 1, 'os-multiple-create:max_count': 3, }}, ) def test_boot_image_with(self): self.run_command("boot --flavor 1" " --image-with test_key=test_value some-server") self.assert_called_anytime( 'POST', '/servers', {'server': { 'flavor_ref': '1', 'name': 'some-server', 'image_ref': '1', 'os-multiple-create:min_count': 1, 'os-multiple-create:max_count': 1, }}, ) def test_boot_key(self): self.run_command('boot --flavor 1 --image 1 --key_name 1 some-server') self.assert_called_anytime( 'POST', '/servers', {'server': { 'flavor_ref': '1', 'name': 'some-server', 'image_ref': '1', 'key_name': '1', 'os-multiple-create:min_count': 1, 'os-multiple-create:max_count': 1, }}, ) def test_boot_user_data(self): file_text = 'text' with mock.patch('novaclient.v3.shell.open', create=True) as mock_open: mock_open.return_value = file_text testfile = 'some_dir/some_file.txt' self.run_command('boot --flavor 1 --image 1 --user_data %s ' 'some-server' % testfile) mock_open.assert_called_once_with(testfile) self.assert_called_anytime( 'POST', '/servers', {'server': { 'flavor_ref': '1', 'name': 'some-server', 'image_ref': '1', 'os-multiple-create:min_count': 1, 'os-multiple-create:max_count': 1, 'os-user-data:user_data': base64.b64encode( file_text.encode('utf-8')) }}, ) def test_boot_avzone(self): self.run_command( 'boot --flavor 1 --image 1 --availability-zone avzone ' 'some-server') self.assert_called_anytime( 'POST', '/servers', {'server': { 'flavor_ref': '1', 'name': 'some-server', 'image_ref': '1', 'os-availability-zone:availability_zone': 'avzone', 'os-multiple-create:min_count': 1, 'os-multiple-create:max_count': 1 }}, ) def test_boot_secgroup(self): self.run_command( 'boot --flavor 1 --image 1 --security-groups secgroup1,' 'secgroup2 some-server') self.assert_called_anytime( 'POST', '/servers', {'server': { 'os-security-groups:security_groups': [{'name': 'secgroup1'}, {'name': 'secgroup2'}], 'flavor_ref': '1', 'name': 'some-server', 'image_ref': '1', 'os-multiple-create:min_count': 1, 'os-multiple-create:max_count': 1, }}, ) def test_boot_config_drive(self): self.run_command( 'boot --flavor 1 --image 1 --config-drive 1 some-server') self.assert_called_anytime( 'POST', '/servers', {'server': { 'flavor_ref': '1', 'name': 'some-server', 'image_ref': '1', 'os-multiple-create:min_count': 1, 'os-multiple-create:max_count': 1, 'os-config-drive:config_drive': True }}, ) def test_boot_config_drive_custom(self): self.run_command( 'boot --flavor 1 --image 1 --config-drive /dev/hda some-server') self.assert_called_anytime( 'POST', '/servers', {'server': { 'flavor_ref': '1', 'name': 'some-server', 'image_ref': '1', 'os-multiple-create:min_count': 1, 'os-multiple-create:max_count': 1, 'os-config-drive:config_drive': '/dev/hda' }}, ) def test_boot_invalid_user_data(self): invalid_file = os.path.join(os.path.dirname(__file__), 'no_such_file') cmd = ('boot some-server --flavor 1 --image 1' ' --user_data %s' % invalid_file) self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_boot_no_image_no_bdms(self): cmd = 'boot --flavor 1 some-server' self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_boot_no_flavor(self): cmd = 'boot --image 1 some-server' self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_boot_no_image_bdms(self): self.run_command( 'boot --flavor 1 --block_device_mapping vda=blah:::0 some-server' ) self.assert_called_anytime( 'POST', '/servers', {'server': { 'flavor_ref': '1', 'name': 'some-server', 'os-block-device-mapping:block_device_mapping': [ { 'volume_id': 'blah', 'delete_on_termination': '0', 'device_name': 'vda' } ], 'image_ref': '', 'os-multiple-create:min_count': 1, 'os-multiple-create:max_count': 1, }}, ) def test_boot_image_bdms(self): self.run_command( 'boot --flavor 1 --image 1 --block-device id=fake-id,' 'source=volume,dest=volume,device=vda,size=1,format=ext4,' 'type=disk,shutdown=preserve some-server' ) self.assert_called_anytime( 'POST', '/servers', {'server': { 'flavor_ref': '1', 'name': 'some-server', 'os-block-device-mapping:block_device_mapping': [ {'device_name': 'id', 'volume_id': 'fake-id,source=volume,dest=volume,device=vda,size=1,' 'format=ext4,type=disk,shutdown=preserve'}], 'image_ref': '1', 'os-multiple-create:min_count': 1, 'os-multiple-create:max_count': 1, }}, ) def test_boot_metadata(self): self.run_command('boot --image 1 --flavor 1 --meta foo=bar=pants' ' --meta spam=eggs some-server ') self.assert_called_anytime( 'POST', '/servers', {'server': { 'flavor_ref': '1', 'name': 'some-server', 'image_ref': '1', 'metadata': {'foo': 'bar=pants', 'spam': 'eggs'}, 'os-multiple-create:min_count': 1, 'os-multiple-create:max_count': 1, }}, ) def test_boot_hints(self): self.run_command('boot --image 1 --flavor 1 ' '--hint a=b1=c1 --hint a2=b2=c2 --hint a=b0=c0 ' 'some-server') self.assert_called_anytime( 'POST', '/servers', { 'server': { 'flavor_ref': '1', 'name': 'some-server', 'image_ref': '1', 'os-multiple-create:min_count': 1, 'os-multiple-create:max_count': 1, 'os-scheduler-hints:scheduler_hints': { 'a': ['b1=c1', 'b0=c0'], 'a2': 'b2=c2'}, }, }, ) def test_boot_nics(self): cmd = ('boot --image 1 --flavor 1 ' '--nic net-id=a=c,v4-fixed-ip=10.0.0.1 some-server') self.run_command(cmd) self.assert_called_anytime( 'POST', '/servers', { 'server': { 'flavor_ref': '1', 'name': 'some-server', 'image_ref': '1', 'os-multiple-create:min_count': 1, 'os-multiple-create:max_count': 1, 'networks': [ {'uuid': 'a=c', 'fixed_ip': '10.0.0.1'}, ], }, }, ) def tets_boot_nics_no_value(self): cmd = ('boot --image 1 --flavor 1 ' '--nic net-id some-server') self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_boot_nics_random_key(self): cmd = ('boot --image 1 --flavor 1 ' '--nic net-id=a=c,v4-fixed-ip=10.0.0.1,foo=bar some-server') self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_boot_nics_no_netid_or_portid(self): cmd = ('boot --image 1 --flavor 1 ' '--nic v4-fixed-ip=10.0.0.1 some-server') self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_boot_num_instances(self): self.run_command('boot --image 1 --flavor 1 --num-instances 3 server') self.assert_called_anytime( 'POST', '/servers', { 'server': { 'flavor_ref': '1', 'name': 'server', 'image_ref': '1', 'os-multiple-create:min_count': 1, 'os-multiple-create:max_count': 3, } }) def test_boot_invalid_num_instances(self): cmd = 'boot --image 1 --flavor 1 --num-instances 1 server' self.assertRaises(exceptions.CommandError, self.run_command, cmd) cmd = 'boot --image 1 --flavor 1 --num-instances 0 server' self.assertRaises(exceptions.CommandError, self.run_command, cmd) @mock.patch('novaclient.v3.shell._poll_for_status') def test_boot_with_poll(self, poll_method): self.run_command('boot --flavor 1 --image 1 some-server --poll') self.assert_called_anytime( 'POST', '/servers', {'server': { 'flavor_ref': '1', 'name': 'some-server', 'image_ref': '1', 'os-multiple-create:min_count': 1, 'os-multiple-create:max_count': 1, }}, ) self.assertEqual(poll_method.call_count, 1) poll_method.assert_has_calls( [mock.call(self.shell.cs.servers.get, 1234, 'building', ['active'])]) python-novaclient-2.17.0/novaclient/tests/v3/test_quotas.py0000664000175400017540000000327512306432770025214 0ustar jenkinsjenkins00000000000000# Copyright IBM Corp. 2013 # # 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 novaclient.tests.v1_1 import test_quotas from novaclient.tests.v3 import fakes class QuotaSetsTest(test_quotas.QuotaSetsTest): def setUp(self): super(QuotaSetsTest, self).setUp() self.cs = self._get_fake_client() def _get_fake_client(self): return fakes.FakeClient() def test_force_update_quota(self): q = self.cs.quotas.get('97f4c221bff44578b0300df4ef119353') q.update(cores=2, force=True) self.cs.assert_called( 'PUT', '/os-quota-sets/97f4c221bff44578b0300df4ef119353', {'quota_set': {'force': True, 'cores': 2}}) def test_tenant_quotas_get_detail(self): tenant_id = 'test' self.cs.quotas.get(tenant_id, detail=True) self.cs.assert_called('GET', '/os-quota-sets/%s/detail' % tenant_id) def test_user_quotas_get_detail(self): tenant_id = 'test' user_id = 'fake_user' self.cs.quotas.get(tenant_id, user_id=user_id, detail=True) url = '/os-quota-sets/%s/detail?user_id=%s' % (tenant_id, user_id) self.cs.assert_called('GET', url) python-novaclient-2.17.0/novaclient/tests/v3/test_availability_zone.py0000664000175400017540000000261012306432770027375 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # Copyright 2013 IBM Corp. # 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 novaclient.tests.v1_1 import test_availability_zone from novaclient.tests.v3 import fakes from novaclient.v3 import availability_zones class AvailabilityZoneTest(test_availability_zone.AvailabilityZoneTest): from novaclient.v3 import shell # noqa def setUp(self): super(AvailabilityZoneTest, self).setUp() self.cs = self._get_fake_client() self.availability_zone_type = self._get_availability_zone_type() def _assertZone(self, zone, name, status): self.assertEqual(zone.zone_name, name) self.assertEqual(zone.zone_state, status) def _get_fake_client(self): return fakes.FakeClient() def _get_availability_zone_type(self): return availability_zones.AvailabilityZone python-novaclient-2.17.0/novaclient/tests/v3/test_hypervisors.py0000664000175400017540000000346212306432770026273 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # 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 novaclient.tests.v1_1 import test_hypervisors from novaclient.tests.v3 import fakes class HypervisorsTest(test_hypervisors.HypervisorsTest): def setUp(self): super(HypervisorsTest, self).setUp() self.cs = self._get_fake_client() def _get_fake_client(self): return fakes.FakeClient() def test_hypervisor_search(self): expected = [ dict(id=1234, hypervisor_hostname='hyper1'), dict(id=5678, hypervisor_hostname='hyper2'), ] result = self.cs.hypervisors.search('hyper') self.cs.assert_called('GET', '/os-hypervisors/search?query=hyper') for idx, hyper in enumerate(result): self.compare_to_expected(expected[idx], hyper) def test_hypervisor_servers(self): expected = dict(id=1234, hypervisor_hostname='hyper1', servers=[ dict(name='inst1', uuid='uuid1'), dict(name='inst2', uuid='uuid2')]) result = self.cs.hypervisors.servers('1234') self.cs.assert_called('GET', '/os-hypervisors/1234/servers') self.compare_to_expected(expected, result) python-novaclient-2.17.0/novaclient/tests/v3/test_servers.py0000664000175400017540000003426012306432770025367 0ustar jenkinsjenkins00000000000000# -*- coding: utf-8 -*- # Copyright 2013 IBM Corp. # # 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 six from novaclient import exceptions from novaclient.tests import utils from novaclient.tests.v3 import fakes from novaclient.v3 import servers cs = fakes.FakeClient() class ServersTest(utils.TestCase): def test_list_servers(self): sl = cs.servers.list() cs.assert_called('GET', '/servers/detail') for s in sl: self.assertIsInstance(s, servers.Server) def test_list_servers_undetailed(self): sl = cs.servers.list(detailed=False) cs.assert_called('GET', '/servers') for s in sl: self.assertIsInstance(s, servers.Server) def test_list_servers_with_marker_limit(self): sl = cs.servers.list(marker=1234, limit=2) cs.assert_called('GET', '/servers/detail?limit=2&marker=1234') for s in sl: self.assertIsInstance(s, servers.Server) def test_get_server_details(self): s = cs.servers.get(1234) cs.assert_called('GET', '/servers/1234') self.assertIsInstance(s, servers.Server) self.assertEqual(s.id, 1234) self.assertEqual(s.status, 'BUILD') def test_get_server_promote_details(self): s1 = cs.servers.list(detailed=False)[0] s2 = cs.servers.list(detailed=True)[0] self.assertNotEqual(s1._info, s2._info) s1.get() self.assertEqual(s1._info, s2._info) def test_create_server(self): s = cs.servers.create( name="My server", image=1, flavor=1, meta={'foo': 'bar'}, userdata="hello moto", key_name="fakekey", files={ '/etc/passwd': 'some data', # a file '/tmp/foo.txt': six.StringIO('data'), # a stream } ) cs.assert_called('POST', '/servers') self.assertIsInstance(s, servers.Server) def test_create_server_userdata_file_object(self): s = cs.servers.create( name="My server", image=1, flavor=1, meta={'foo': 'bar'}, userdata=six.StringIO('hello moto'), files={ '/etc/passwd': 'some data', # a file '/tmp/foo.txt': six.StringIO('data'), # a stream }, ) cs.assert_called('POST', '/servers') self.assertIsInstance(s, servers.Server) def test_create_server_userdata_unicode(self): s = cs.servers.create( name="My server", image=1, flavor=1, meta={'foo': 'bar'}, userdata=six.u('こんにちは'), key_name="fakekey", files={ '/etc/passwd': 'some data', # a file '/tmp/foo.txt': six.StringIO('data'), # a stream }, ) cs.assert_called('POST', '/servers') self.assertIsInstance(s, servers.Server) def test_create_server_userdata_utf8(self): s = cs.servers.create( name="My server", image=1, flavor=1, meta={'foo': 'bar'}, userdata='こんにちは', key_name="fakekey", files={ '/etc/passwd': 'some data', # a file '/tmp/foo.txt': six.StringIO('data'), # a stream }, ) cs.assert_called('POST', '/servers') self.assertIsInstance(s, servers.Server) def test_update_server(self): s = cs.servers.get(1234) # Update via instance s.update(name='hi') cs.assert_called('PUT', '/servers/1234') s.update(name='hi') cs.assert_called('PUT', '/servers/1234') # Silly, but not an error s.update() # Update via manager cs.servers.update(s, name='hi') cs.assert_called('PUT', '/servers/1234') def test_delete_server(self): s = cs.servers.get(1234) s.delete() cs.assert_called('DELETE', '/servers/1234') cs.servers.delete(1234) cs.assert_called('DELETE', '/servers/1234') cs.servers.delete(s) cs.assert_called('DELETE', '/servers/1234') def test_delete_server_meta(self): s = cs.servers.delete_meta(1234, ['test_key']) cs.assert_called('DELETE', '/servers/1234/metadata/test_key') def test_set_server_meta(self): s = cs.servers.set_meta(1234, {'test_key': 'test_value'}) reval = cs.assert_called('POST', '/servers/1234/metadata', {'metadata': {'test_key': 'test_value'}}) def test_find(self): server = cs.servers.find(name='sample-server') cs.assert_called('GET', '/servers', pos=-2) cs.assert_called('GET', '/servers/1234', pos=-1) self.assertEqual(server.name, 'sample-server') self.assertRaises(exceptions.NoUniqueMatch, cs.servers.find, flavor={"id": 1, "name": "256 MB Server"}) sl = cs.servers.findall(flavor={"id": 1, "name": "256 MB Server"}) self.assertEqual([s.id for s in sl], [1234, 5678, 9012]) def test_reboot_server(self): s = cs.servers.get(1234) s.reboot() cs.assert_called('POST', '/servers/1234/action') cs.servers.reboot(s, reboot_type='HARD') cs.assert_called('POST', '/servers/1234/action') def test_rebuild_server(self): s = cs.servers.get(1234) s.rebuild(image=1) cs.assert_called('POST', '/servers/1234/action') cs.servers.rebuild(s, image=1) cs.assert_called('POST', '/servers/1234/action') s.rebuild(image=1, password='5678') cs.assert_called('POST', '/servers/1234/action') cs.servers.rebuild(s, image=1, password='5678') cs.assert_called('POST', '/servers/1234/action') def test_resize_server(self): s = cs.servers.get(1234) s.resize(flavor=1) cs.assert_called('POST', '/servers/1234/action') cs.servers.resize(s, flavor=1) cs.assert_called('POST', '/servers/1234/action') def test_confirm_resized_server(self): s = cs.servers.get(1234) s.confirm_resize() cs.assert_called('POST', '/servers/1234/action') cs.servers.confirm_resize(s) cs.assert_called('POST', '/servers/1234/action') def test_revert_resized_server(self): s = cs.servers.get(1234) s.revert_resize() cs.assert_called('POST', '/servers/1234/action') cs.servers.revert_resize(s) cs.assert_called('POST', '/servers/1234/action') def test_migrate_server(self): s = cs.servers.get(1234) s.migrate() cs.assert_called('POST', '/servers/1234/action') cs.servers.migrate(s) cs.assert_called('POST', '/servers/1234/action') def test_add_fixed_ip(self): s = cs.servers.get(1234) s.add_fixed_ip(1) cs.assert_called('POST', '/servers/1234/action') cs.servers.add_fixed_ip(s, 1) cs.assert_called('POST', '/servers/1234/action') def test_remove_fixed_ip(self): s = cs.servers.get(1234) s.remove_fixed_ip('10.0.0.1') cs.assert_called('POST', '/servers/1234/action') cs.servers.remove_fixed_ip(s, '10.0.0.1') cs.assert_called('POST', '/servers/1234/action') def test_stop(self): s = cs.servers.get(1234) s.stop() cs.assert_called('POST', '/servers/1234/action') cs.servers.stop(s) cs.assert_called('POST', '/servers/1234/action') def test_force_delete(self): s = cs.servers.get(1234) s.force_delete() cs.assert_called('POST', '/servers/1234/action') cs.servers.force_delete(s) cs.assert_called('POST', '/servers/1234/action') def test_restore(self): s = cs.servers.get(1234) s.restore() cs.assert_called('POST', '/servers/1234/action') cs.servers.restore(s) cs.assert_called('POST', '/servers/1234/action') def test_start(self): s = cs.servers.get(1234) s.start() cs.assert_called('POST', '/servers/1234/action') cs.servers.start(s) cs.assert_called('POST', '/servers/1234/action') def test_rescue(self): s = cs.servers.get(1234) s.rescue() cs.assert_called('POST', '/servers/1234/action') cs.servers.rescue(s) cs.assert_called('POST', '/servers/1234/action') def test_unrescue(self): s = cs.servers.get(1234) s.unrescue() cs.assert_called('POST', '/servers/1234/action') cs.servers.unrescue(s) cs.assert_called('POST', '/servers/1234/action') def test_lock(self): s = cs.servers.get(1234) s.lock() cs.assert_called('POST', '/servers/1234/action') cs.servers.lock(s) cs.assert_called('POST', '/servers/1234/action') def test_unlock(self): s = cs.servers.get(1234) s.unlock() cs.assert_called('POST', '/servers/1234/action') cs.servers.unlock(s) cs.assert_called('POST', '/servers/1234/action') def test_backup(self): s = cs.servers.get(1234) s.backup('back1', 'daily', 1) cs.assert_called('POST', '/servers/1234/action') cs.servers.backup(s, 'back1', 'daily', 2) cs.assert_called('POST', '/servers/1234/action') def test_get_console_output_without_length(self): success = 'foo' s = cs.servers.get(1234) s.get_console_output() self.assertEqual(s.get_console_output(), success) cs.assert_called('POST', '/servers/1234/action') cs.servers.get_console_output(s) self.assertEqual(cs.servers.get_console_output(s), success) cs.assert_called('POST', '/servers/1234/action') def test_get_console_output_with_length(self): success = 'foo' s = cs.servers.get(1234) s.get_console_output(length=50) self.assertEqual(s.get_console_output(length=50), success) cs.assert_called('POST', '/servers/1234/action') cs.servers.get_console_output(s, length=50) self.assertEqual(cs.servers.get_console_output(s, length=50), success) cs.assert_called('POST', '/servers/1234/action') def test_get_password(self): s = cs.servers.get(1234) self.assertEqual(s.get_password('/foo/id_rsa'), '') cs.assert_called('GET', '/servers/1234/os-server-password') def test_clear_password(self): s = cs.servers.get(1234) s.clear_password() cs.assert_called('DELETE', '/servers/1234/os-server-password') def test_get_server_diagnostics(self): s = cs.servers.get(1234) diagnostics = s.diagnostics() self.assertTrue(diagnostics is not None) cs.assert_called('GET', '/servers/1234/os-server-diagnostics') diagnostics_from_manager = cs.servers.diagnostics(1234) self.assertTrue(diagnostics_from_manager is not None) cs.assert_called('GET', '/servers/1234/os-server-diagnostics') self.assertEqual(diagnostics, diagnostics_from_manager) def test_get_vnc_console(self): s = cs.servers.get(1234) s.get_vnc_console('fake') cs.assert_called('POST', '/servers/1234/action') cs.servers.get_vnc_console(s, 'fake') cs.assert_called('POST', '/servers/1234/action') def test_get_spice_console(self): s = cs.servers.get(1234) s.get_spice_console('fake') cs.assert_called('POST', '/servers/1234/action') cs.servers.get_spice_console(s, 'fake') cs.assert_called('POST', '/servers/1234/action') def test_create_image(self): s = cs.servers.get(1234) s.create_image('123') cs.assert_called('POST', '/servers/1234/action') s.create_image('123', {}) cs.assert_called('POST', '/servers/1234/action') cs.servers.create_image(s, '123') cs.assert_called('POST', '/servers/1234/action') cs.servers.create_image(s, '123', {}) def test_live_migrate_server(self): s = cs.servers.get(1234) s.live_migrate(host='hostname', block_migration=False, disk_over_commit=False) cs.assert_called('POST', '/servers/1234/action') cs.servers.live_migrate(s, host='hostname', block_migration=False, disk_over_commit=False) cs.assert_called('POST', '/servers/1234/action') def test_reset_state(self): s = cs.servers.get(1234) s.reset_state('newstate') cs.assert_called('POST', '/servers/1234/action') cs.servers.reset_state(s, 'newstate') cs.assert_called('POST', '/servers/1234/action') def test_reset_network(self): s = cs.servers.get(1234) s.reset_network() cs.assert_called('POST', '/servers/1234/action') cs.servers.reset_network(s) cs.assert_called('POST', '/servers/1234/action') def test_evacuate(self): s = cs.servers.get(1234) s.evacuate('fake_target_host', 'True') cs.assert_called('POST', '/servers/1234/action') cs.servers.evacuate(s, 'fake_target_host', 'False', 'NewAdminPassword') cs.assert_called('POST', '/servers/1234/action') def test_interface_list(self): s = cs.servers.get(1234) s.interface_list() cs.assert_called('GET', '/servers/1234/os-attach-interfaces') def test_interface_attach(self): s = cs.servers.get(1234) s.interface_attach(None, None, None) cs.assert_called('POST', '/servers/1234/os-attach-interfaces') def test_interface_detach(self): s = cs.servers.get(1234) s.interface_detach('port-id') cs.assert_called('DELETE', '/servers/1234/os-attach-interfaces/port-id') python-novaclient-2.17.0/novaclient/tests/v3/test_volumes.py0000664000175400017540000000317612306432770025372 0ustar jenkinsjenkins00000000000000# Copyright 2013 IBM Corp. # 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 novaclient.tests import utils from novaclient.tests.v3 import fakes class VolumesTest(utils.TestCase): def setUp(self): super(VolumesTest, self).setUp() self.cs = self._get_fake_client() def _get_fake_client(self): return fakes.FakeClient() def test_attach_server_volume(self): v = self.cs.volumes.attach_server_volume( server=1234, volume_id='15e59938-07d5-11e1-90e3-e3dffe0c5983', device='/dev/vdb' ) self.cs.assert_called('POST', '/servers/1234/action') def test_update_server_volume(self): vol_id = '15e59938-07d5-11e1-90e3-e3dffe0c5983' v = self.cs.volumes.update_server_volume( server=1234, old_volume_id='Work', new_volume_id=vol_id ) self.cs.assert_called('POST', '/servers/1234/action') def test_delete_server_volume(self): self.cs.volumes.delete_server_volume(1234, 'Work') self.cs.assert_called('POST', '/servers/1234/action') python-novaclient-2.17.0/novaclient/tests/v3/test_hosts.py0000664000175400017540000000554312306432770025040 0ustar jenkinsjenkins00000000000000# Copyright 2013 OpenStack Foundation # # 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 novaclient.tests import utils from novaclient.tests.v3 import fakes from novaclient.v3 import hosts cs = fakes.FakeClient() class HostsTest(utils.TestCase): def test_describe_resource(self): hs = cs.hosts.get('host') cs.assert_called('GET', '/os-hosts/host') for h in hs: self.assertIsInstance(h, hosts.Host) def test_list_host(self): hs = cs.hosts.list() cs.assert_called('GET', '/os-hosts') for h in hs: self.assertIsInstance(h, hosts.Host) self.assertEqual(h.zone, 'nova1') def test_list_host_with_zone(self): hs = cs.hosts.list('nova') cs.assert_called('GET', '/os-hosts?zone=nova') for h in hs: self.assertIsInstance(h, hosts.Host) self.assertEqual(h.zone, 'nova') def test_update_enable(self): host = cs.hosts.get('sample_host')[0] values = {"status": "enabled"} result = host.update(values) cs.assert_called('PUT', '/os-hosts/sample_host', {"host": values}) self.assertIsInstance(result, hosts.Host) def test_update_maintenance(self): host = cs.hosts.get('sample_host')[0] values = {"maintenance_mode": "enable"} result = host.update(values) cs.assert_called('PUT', '/os-hosts/sample_host', {"host": values}) self.assertIsInstance(result, hosts.Host) def test_update_both(self): host = cs.hosts.get('sample_host')[0] values = {"status": "enabled", "maintenance_mode": "enable"} result = host.update(values) cs.assert_called('PUT', '/os-hosts/sample_host', {"host": values}) self.assertIsInstance(result, hosts.Host) def test_host_startup(self): host = cs.hosts.get('sample_host')[0] result = host.startup() cs.assert_called( 'GET', '/os-hosts/sample_host/startup') def test_host_reboot(self): host = cs.hosts.get('sample_host')[0] result = host.reboot() cs.assert_called( 'GET', '/os-hosts/sample_host/reboot') def test_host_shutdown(self): host = cs.hosts.get('sample_host')[0] result = host.shutdown() cs.assert_called( 'GET', '/os-hosts/sample_host/shutdown') python-novaclient-2.17.0/novaclient/tests/v3/test_flavor_access.py0000664000175400017540000000376612306432770026517 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # Copyright 2013 IBM Corp. # 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 novaclient.tests import utils from novaclient.tests.v3 import fakes from novaclient.v3 import flavor_access cs = fakes.FakeClient() class FlavorAccessTest(utils.TestCase): def test_list_access_by_flavor_private(self): kwargs = {'flavor': cs.flavors.get(2)} r = cs.flavor_access.list(**kwargs) cs.assert_called('GET', '/flavors/2/flavor-access') for access_list in r: self.assertIsInstance(access_list, flavor_access.FlavorAccess) def test_add_tenant_access(self): flavor = cs.flavors.get(2) tenant = 'proj2' r = cs.flavor_access.add_tenant_access(flavor, tenant) body = { "add_tenant_access": { "tenant_id": "proj2" } } cs.assert_called('POST', '/flavors/2/action', body) for a in r: self.assertIsInstance(a, flavor_access.FlavorAccess) def test_remove_tenant_access(self): flavor = cs.flavors.get(2) tenant = 'proj2' r = cs.flavor_access.remove_tenant_access(flavor, tenant) body = { "remove_tenant_access": { "tenant_id": "proj2" } } cs.assert_called('POST', '/flavors/2/action', body) for a in r: self.assertIsInstance(a, flavor_access.FlavorAccess) python-novaclient-2.17.0/novaclient/tests/v3/test_aggregates.py0000664000175400017540000000207612306432770026007 0ustar jenkinsjenkins00000000000000# Copyright 2013 IBM Corp. # # 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 novaclient.tests.v1_1 import test_aggregates from novaclient.tests.v3 import fakes from novaclient.v3 import aggregates class AggregatesTest(test_aggregates.AggregatesTest): def setUp(self): super(AggregatesTest, self).setUp() self.cs = self._get_fake_client() self.aggregate_type = self._get_aggregate_type() def _get_fake_client(self): return fakes.FakeClient() def _get_aggregate_type(self): return aggregates.Aggregate python-novaclient-2.17.0/novaclient/tests/v3/test_quota_classes.py0000664000175400017540000000165212306432770026543 0ustar jenkinsjenkins00000000000000# Copyright IBM Corp. 2013 # # 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 novaclient.tests.v1_1 import test_quota_classes from novaclient.tests.v3 import fakes class QuotaClassSetsTest(test_quota_classes.QuotaClassSetsTest): def setUp(self): super(QuotaClassSetsTest, self).setUp() self.cs = self._get_fake_client() def _get_fake_client(self): return fakes.FakeClient() python-novaclient-2.17.0/novaclient/tests/v3/test_images.py0000664000175400017540000000357712306432770025152 0ustar jenkinsjenkins00000000000000# Copyright 2013 IBM Corp. # # 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 novaclient.tests import utils from novaclient.tests.v3 import fakes from novaclient.v3 import images cs = fakes.FakeClient() class ImagesTest(utils.TestCase): def test_list_images(self): il = cs.images.list() cs.assert_called('GET', '/v1/images/detail') for i in il: self.assertIsInstance(i, images.Image) def test_list_images_undetailed(self): il = cs.images.list(detailed=False) cs.assert_called('GET', '/v1/images') for i in il: self.assertIsInstance(i, images.Image) def test_list_images_with_limit(self): il = cs.images.list(limit=4) cs.assert_called('GET', '/v1/images/detail?limit=4') def test_get_image_details(self): i = cs.images.get(1) cs.assert_called('HEAD', '/v1/images/1') self.assertIsInstance(i, images.Image) self.assertEqual(i.id, '1') self.assertEqual(i.name, 'CentOS 5.2') def test_find(self): i = cs.images.find(name="CentOS 5.2") self.assertEqual(i.id, '1') cs.assert_called('GET', '/v1/images', pos=-2) cs.assert_called('HEAD', '/v1/images/1', pos=-1) iml = cs.images.findall(status='SAVING') self.assertEqual(len(iml), 1) self.assertEqual(iml[0].name, 'My Server Backup') python-novaclient-2.17.0/novaclient/tests/v3/fakes.py0000664000175400017540000003247012306432770023731 0ustar jenkinsjenkins00000000000000# Copyright (c) 2011 X.commerce, a business unit of eBay Inc. # Copyright 2011 OpenStack Foundation # Copyright 2013 IBM Corp. # # 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 datetime import datetime from novaclient.openstack.common import strutils from novaclient.tests import fakes from novaclient.tests.v1_1 import fakes as fakes_v1_1 from novaclient.v3 import client class FakeClient(fakes.FakeClient, client.Client): def __init__(self, *args, **kwargs): client.Client.__init__(self, 'username', 'password', 'project_id', 'auth_url', extensions=kwargs.get('extensions')) self.client = FakeHTTPClient(**kwargs) class FakeHTTPClient(fakes_v1_1.FakeHTTPClient): # # Hosts # def put_os_hosts_sample_host_1(self, body, **kw): return (200, {}, {'host': {'host': 'sample-host_1', 'status': 'enabled'}}) def put_os_hosts_sample_host_2(self, body, **kw): return (200, {}, {'host': {'host': 'sample-host_2', 'maintenance_mode': 'on_maintenance'}}) def put_os_hosts_sample_host_3(self, body, **kw): return (200, {}, {'host': {'host': 'sample-host_3', 'status': 'enabled', 'maintenance_mode': 'on_maintenance'}}) def get_os_hosts_sample_host_reboot(self, **kw): return (200, {}, {'host': {'host': 'sample_host', 'power_action': 'reboot'}}) def get_os_hosts_sample_host_startup(self, **kw): return (200, {}, {'host': {'host': 'sample_host', 'power_action': 'startup'}}) def get_os_hosts_sample_host_shutdown(self, **kw): return (200, {}, {'host': {'host': 'sample_host', 'power_action': 'shutdown'}}) # # Flavors # post_flavors_1_flavor_extra_specs = ( fakes_v1_1.FakeHTTPClient.post_flavors_1_os_extra_specs) post_flavors_4_flavor_extra_specs = ( fakes_v1_1.FakeHTTPClient.post_flavors_4_os_extra_specs) delete_flavors_1_flavor_extra_specs_k1 = ( fakes_v1_1.FakeHTTPClient.delete_flavors_1_os_extra_specs_k1) def get_flavors_detail(self, **kw): flavors = {'flavors': [ {'id': 1, 'name': '256 MB Server', 'ram': 256, 'disk': 10, 'ephemeral': 10, 'flavor-access:is_public': True, 'links': {}}, {'id': 2, 'name': '512 MB Server', 'ram': 512, 'disk': 20, 'ephemeral': 20, 'flavor-access:is_public': False, 'links': {}}, {'id': 4, 'name': '1024 MB Server', 'ram': 1024, 'disk': 10, 'ephemeral': 10, 'flavor-access:is_public': True, 'links': {}}, {'id': 'aa1', 'name': '128 MB Server', 'ram': 128, 'disk': 0, 'ephemeral': 0, 'flavor-access:is_public': True, 'links': {}} ]} if 'is_public' not in kw: filter_is_public = True else: if kw['is_public'].lower() == 'none': filter_is_public = None else: filter_is_public = strutils.bool_from_string(kw['is_public'], True) if filter_is_public is not None: if filter_is_public: flavors['flavors'] = [ v for v in flavors['flavors'] if v['flavor-access:is_public'] ] else: flavors['flavors'] = [ v for v in flavors['flavors'] if not v['flavor-access:is_public'] ] return (200, {}, flavors) # # Flavor access # get_flavors_2_flavor_access = ( fakes_v1_1.FakeHTTPClient.get_flavors_2_os_flavor_access) # # Images # get_v1_images_detail = fakes_v1_1.FakeHTTPClient.get_images_detail get_v1_images = fakes_v1_1.FakeHTTPClient.get_images def head_v1_images_1(self, **kw): headers = { 'x-image-meta-id': '1', 'x-image-meta-name': 'CentOS 5.2', 'x-image-meta-updated': '2010-10-10T12:00:00Z', 'x-image-meta-created': '2010-10-10T12:00:00Z', 'x-image-meta-status': 'ACTIVE', 'x-image-meta-property-test_key': 'test_value'} return 200, headers, '' # # Servers # get_servers_1234_os_server_diagnostics = ( fakes_v1_1.FakeHTTPClient.get_servers_1234_diagnostics) delete_servers_1234_os_attach_interfaces_port_id = ( fakes_v1_1.FakeHTTPClient.delete_servers_1234_os_interface_port_id) def get_servers_1234_os_attach_interfaces(self, **kw): return (200, {}, {"interface_attachments": [ {"port_state": "ACTIVE", "net_id": "net-id-1", "port_id": "port-id-1", "mac_address": "aa:bb:cc:dd:ee:ff", "fixed_ips": [{"ip_address": "1.2.3.4"}], }, {"port_state": "ACTIVE", "net_id": "net-id-1", "port_id": "port-id-1", "mac_address": "aa:bb:cc:dd:ee:ff", "fixed_ips": [{"ip_address": "1.2.3.4"}], }]}) def post_servers_1234_os_attach_interfaces(self, **kw): return (200, {}, {'interface_attachment': {}}) def post_servers(self, body, **kw): assert set(body.keys()) <= set(['server']) fakes.assert_has_keys(body['server'], required=['name', 'image_ref', 'flavor_ref'], optional=['metadata', 'personality', 'os-scheduler-hints:scheduler_hints']) return (202, {}, self.get_servers_1234()[2]) # # Server Actions # def post_servers_1234_action(self, body, **kw): _headers = None resp = 202 body_is_none_list = [ 'revert_resize', 'migrate', 'stop', 'start', 'force_delete', 'restore', 'pause', 'unpause', 'lock', 'unlock', 'unrescue', 'resume', 'suspend', 'lock', 'unlock', 'shelve', 'shelve_offload', 'unshelve', 'reset_network', 'rescue', 'confirm_resize'] body_return_map = { 'rescue': {'admin_password': 'RescuePassword'}, 'get_console_output': {'output': 'foo'}, 'rebuild': self.get_servers_1234()[2], } body_param_check_exists = { 'rebuild': 'image_ref', 'resize': 'flavor_ref'} body_params_check_exact = { 'reboot': ['type'], 'add_fixed_ip': ['network_id'], 'evacuate': ['host', 'on_shared_storage'], 'remove_fixed_ip': ['address'], 'change_password': ['admin_password'], 'get_console_output': ['length'], 'get_vnc_console': ['type'], 'get_spice_console': ['type'], 'reset_state': ['state'], 'create_image': ['name', 'metadata'], 'migrate_live': ['host', 'block_migration', 'disk_over_commit'], 'create_backup': ['name', 'backup_type', 'rotation'], 'attach': ['volume_id', 'device'], 'detach': ['volume_id'], 'swap_volume_attachment': ['old_volume_id', 'new_volume_id']} assert len(body.keys()) == 1 action = list(body)[0] _body = body_return_map.get(action) if action in body_is_none_list: assert body[action] is None if action in body_param_check_exists: assert body_param_check_exists[action] in body[action] if action == 'evacuate': body[action].pop('admin_password', None) if action in body_params_check_exact: assert set(body[action]) == set(body_params_check_exact[action]) if action == 'reboot': assert body[action]['type'] in ['HARD', 'SOFT'] elif action == 'confirm_resize': # This one method returns a different response code resp = 204 elif action == 'create_image': _headers = dict(location="http://blah/images/456") if action not in set.union(set(body_is_none_list), set(body_params_check_exact.keys()), set(body_param_check_exists.keys())): raise AssertionError("Unexpected server action: %s" % action) return (resp, _headers, _body) # # Server password # def get_servers_1234_os_server_password(self, **kw): return (200, {}, {'password': ''}) def delete_servers_1234_os_server_password(self, **kw): return (202, {}, None) # # Availability Zones # def get_os_availability_zone(self, **kw): return (200, {}, {"availability_zone_info": [ {"zone_name": "zone-1", "zone_state": {"available": True}, "hosts": None}, {"zone_name": "zone-2", "zone_state": {"available": False}, "hosts": None}]}) def get_os_availability_zone_detail(self, **kw): return (200, {}, {"availability_zone_info": [ {"zone_name": "zone-1", "zone_state": {"available": True}, "hosts": { "fake_host-1": { "nova-compute": {"active": True, "available": True, "updated_at": datetime(2012, 12, 26, 14, 45, 25, 0)}}}}, {"zone_name": "internal", "zone_state": {"available": True}, "hosts": { "fake_host-1": { "nova-sched": { "active": True, "available": True, "updated_at": datetime(2012, 12, 26, 14, 45, 25, 0)}}, "fake_host-2": { "nova-network": { "active": True, "available": False, "updated_at": datetime(2012, 12, 26, 14, 45, 24, 0)}}}}, {"zone_name": "zone-2", "zone_state": {"available": False}, "hosts": None}]}) # # Quotas # def put_os_quota_sets_97f4c221bff44578b0300df4ef119353(self, body, **kw): assert list(body) == ['quota_set'] return (200, {}, {'quota_set': { 'tenant_id': '97f4c221bff44578b0300df4ef119353', 'metadata_items': [], 'injected_file_content_bytes': 1, 'injected_file_path_bytes': 1, 'ram': 1, 'floating_ips': 1, 'instances': 1, 'injected_files': 1, 'cores': 1, 'keypairs': 1, 'security_groups': 1, 'security_group_rules': 1}}) def get_os_quota_sets_test_detail(self, **kw): return (200, {}, {'quota_set': { 'cores': {'reserved': 0, 'in_use': 0, 'limit': 10}, 'instances': {'reserved': 0, 'in_use': 4, 'limit': 50}, 'ram': {'reserved': 0, 'in_use': 1024, 'limit': 51200}}}) # # Hypervisors # def get_os_hypervisors_search(self, **kw): return (200, {}, {'hypervisors': [ {'id': 1234, 'hypervisor_hostname': 'hyper1'}, {'id': 5678, 'hypervisor_hostname': 'hyper2'} ]}) def get_os_hypervisors_1234_servers(self, **kw): return (200, {}, {'hypervisor': {'id': 1234, 'hypervisor_hostname': 'hyper1', 'servers': [ {'name': 'inst1', 'uuid': 'uuid1'}, {'name': 'inst2', 'uuid': 'uuid2'} ]}, }) # # Keypairs # get_keypairs_test = fakes_v1_1.FakeHTTPClient.get_os_keypairs_test get_keypairs = fakes_v1_1.FakeHTTPClient.get_os_keypairs delete_keypairs_test = fakes_v1_1.FakeHTTPClient.delete_os_keypairs_test post_keypairs = fakes_v1_1.FakeHTTPClient.post_os_keypairs python-novaclient-2.17.0/novaclient/tests/v3/test_agents.py0000664000175400017540000000212512306432770025152 0ustar jenkinsjenkins00000000000000# Copyright 2012 IBM Corp. # 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 novaclient.tests.v1_1 import test_agents from novaclient.tests.v3 import fakes from novaclient.v3 import agents class AgentsTest(test_agents.AgentsTest): def _build_example_update_body(self): return {"agent": { "url": "/yyy/yyyy/yyyy", "version": "8.0", "md5hash": "add6bb58e139be103324d04d82d8f546"}} def _get_fake_client(self): return fakes.FakeClient() def _get_agent_type(self): return agents.Agent python-novaclient-2.17.0/novaclient/tests/v3/test_services.py0000664000175400017540000000243112306432770025514 0ustar jenkinsjenkins00000000000000# Copyright 2012 IBM Corp. # 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 novaclient.tests import utils from novaclient.tests.v3 import fakes from novaclient.v3 import services class ServicesTest(utils.TestCase): def setUp(self): super(ServicesTest, self).setUp() self.cs = self._get_fake_client() self.service_type = self._get_service_type() def _get_fake_client(self): return fakes.FakeClient() def _get_service_type(self): return services.Service def _update_body(self, host, binary, disabled_reason=None): body = {"host": host, "binary": binary} if disabled_reason is not None: body["disabled_reason"] = disabled_reason return body python-novaclient-2.17.0/novaclient/tests/v3/test_flavors.py0000664000175400017540000000443412306432770025352 0ustar jenkinsjenkins00000000000000# Copyright (c) 2013, OpenStack # Copyright 2013 IBM Corp. # # 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 novaclient.tests.v1_1 import test_flavors from novaclient.tests.v3 import fakes from novaclient.v3 import flavors class FlavorsTest(test_flavors.FlavorsTest): def _get_fake_client(self): return fakes.FakeClient() def _get_flavor_type(self): return flavors.Flavor def _create_body(self, name, ram, vcpus, disk, ephemeral, id, swap, rxtx_factor, is_public): return { "flavor": { "name": name, "ram": ram, "vcpus": vcpus, "disk": disk, "ephemeral": ephemeral, "id": id, "swap": swap, "rxtx_factor": rxtx_factor, "flavor-access:is_public": is_public, } } def test_set_keys(self): f = self.cs.flavors.get(1) f.set_keys({'k1': 'v1'}) self.cs.assert_called('POST', '/flavors/1/flavor-extra-specs', {"extra_specs": {'k1': 'v1'}}) def test_set_with_valid_keys(self): valid_keys = ['key4', 'month.price', 'I-Am:AK-ey.44-', 'key with spaces and _'] f = self.cs.flavors.get(4) for key in valid_keys: f.set_keys({key: 'v4'}) self.cs.assert_called('POST', '/flavors/4/flavor-extra-specs', {"extra_specs": {key: 'v4'}}) def test_unset_keys(self): f = self.cs.flavors.get(1) f.unset_keys(['k1']) self.cs.assert_called('DELETE', '/flavors/1/flavor-extra-specs/k1') def test_get_flavor_details_diablo(self): # Don't need for V3 API to work against diablo pass python-novaclient-2.17.0/novaclient/tests/v3/test_keypairs.py0000664000175400017540000000215512306432770025523 0ustar jenkinsjenkins00000000000000# Copyright 2013 IBM Corp. # # 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 novaclient.tests.v1_1 import test_keypairs from novaclient.tests.v3 import fakes from novaclient.v3 import keypairs class KeypairsTest(test_keypairs.KeypairsTest): def setUp(self): super(KeypairsTest, self).setUp() self.cs = self._get_fake_client() self.keypair_type = self._get_keypair_type() self.keypair_prefix = keypairs.KeypairManager.keypair_prefix def _get_fake_client(self): return fakes.FakeClient() def _get_keypair_type(self): return keypairs.Keypair python-novaclient-2.17.0/novaclient/tests/v3/test_certs.py0000664000175400017540000000176512306432770025022 0ustar jenkinsjenkins00000000000000# # 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 novaclient.tests.v1_1 import fakes from novaclient.tests.v1_1 import test_certs from novaclient.v3 import certs class CertsTest(test_certs.CertsTest): def setUp(self): super(CertsTest, self).setUp() self.cs = self._get_fake_client() self.cert_type = self._get_cert_type() def _get_fake_client(self): return fakes.FakeClient() def _get_cert_type(self): return certs.Certificate python-novaclient-2.17.0/novaclient/tests/v3/test_usage.py0000664000175400017540000000202012306432770024767 0ustar jenkinsjenkins00000000000000# Copyright 2013 IBM Corp. # # 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 novaclient.tests.v1_1 import fakes from novaclient.tests.v1_1 import test_usage from novaclient.v3 import usage class UsageTest(test_usage.UsageTest): def setUp(self): super(UsageTest, self).setUp() self.cs = self._get_fake_client() self.usage_type = self._get_usage_type() def _get_fake_client(self): return fakes.FakeClient() def _get_usage_type(self): return usage.Usage python-novaclient-2.17.0/novaclient/tests/v3/__init__.py0000664000175400017540000000000012306432770024357 0ustar jenkinsjenkins00000000000000python-novaclient-2.17.0/novaclient/tests/idfake.pem0000664000175400017540000000321312306432770023655 0ustar jenkinsjenkins00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEA9QstF/7prDY7a9La7GS9TpMX+MWWXQgK6pHRLakDFp1WX1Q3 Vly7rWitaZUGirUPMm181oJXBwkKlAxFD7hKjyHYaSswNszPYIAsVkc1+AO5epXz g9kUBNtfg44Pg72UecwLrZ8JpmNZpJlKQOx6vF+yi7JmHrrIf6il/grIGUPzoT2L yReimpyPoBrGtXhJYaCJ/XbKg1idRZiQdmwh1F/OmZWn9p0wunnsv08a0+qIywuw WhG9/Zy9fjnEByfusS6gI0GIxDRL4RWzOqphd3PZzunwIBgEKFhgiki9+2DgcRVO 9I5wnDvfwQREJRZWh1uJa5ZTcfPa1EzZryVeOQIDAQABAoIBABxO3Te/cBk/7p9n LXlPrfrszUEk+ljm+/PbQpIGy1+Kb5b1sKrebaP7ysS+vZG6lvXZZimVxx398mXm APhu7tYYL9r+bUR3ZqGcTQLumRJ8w6mgtxANPN3Oxfr5p1stxIBJjTPSgpfhNFLq joRvjUJDv+mZg2ibZVwyDHMLpdAdKp+3XMdyTLZcH9esqwii+natix7rHd1RuF85 L1dfpxjkItwhgHsfdYS++5X3fRByFOhQ+Nhabh/kPQbQMcteRn1bN6zeCWBSglNb Ka/ZrXb6ApRUc22Ji62mNO2ZPPekLJeCHk2h2E7ezYX+sGDNvvd/jHVDJJ20FjD1 Z9KXuK0CgYEA/2vniy9yWd925QQtWbmrxgy6yj89feMH/LTv4qP298rGZ2nqxsyd 9pdBdb4NMsi4HmV5PG1hp3VRNBHl53DNh5eqzT8WEXnIF+sbrIU3KzrCVAx1kZTl +OWKA6aVUsvvO3y85SOvInnsV+IsOGmU4/WBSjYoe39Bo7mq/YuZB9MCgYEA9ZlB KBm6PjFdHQGNgedXahWzRcwC+ALCYqequPYqJolNzhrK4Uc2sWPSGdnldcHZ4XCQ wbfCxUSwrMpA1oyuIQ0U4aowmOw5DjIueBWI8XBYEVRBlwvJwbXpBZ/DspGzTUDx MBrrEwEaMadQvxhRnAzhp0rQAepatcz6Fgb1JkMCgYBMwDLiew5kfSav6JJsDMPW DksurNQgeNEUmZYfx19V1EPMHWKj/CZXS9oqtEIpCXFyCNHmW4PlmvYcrGgmJJpN 7UAwzo0mES8UKNy2+Yy7W7u7H8dQSKrWILtZH3xtVcR8Xp4wSIm+1V40hkz9YpSP 71y7XQzLF1E1DnyYFZOVawKBgAFrmHfd5jjT2kD/sEzPBK9lXrsJmf7LLUqaw578 NXQxmRSXDRNOcR+Hf0CNBQmwTE1EdGHaaTLw2cC2Drfu6lbgl31SmaNYwl+1pJUn MrqKtseq4BI6jDkljypsKRqQQyQwOvTXQwLCH9+nowzn3Bj17hwkj51jOJESlWOp OKO3AoGBALm+jjqyqX7gSnqK3FAumB8mlhv3yI1Wr1ctwe18mKfKbz17HxXRu9pF K/6e7WMCA1p+jhoE8gj1h2WBcH0nV2qt8Ye8gJBbCi4dhI08o4AfrIV47oZx1RlO qYcA1U9lyaODY5SL8+6PHOy5J/aYtuA+wvfEnWiCIdKQrhWetcn3 -----END RSA PRIVATE KEY----- python-novaclient-2.17.0/novaclient/tests/v1_1/0000775000175400017540000000000012306433034022470 5ustar jenkinsjenkins00000000000000python-novaclient-2.17.0/novaclient/tests/v1_1/test_shell.py0000664000175400017540000024056312306432771025231 0ustar jenkinsjenkins00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 OpenStack Foundation # Copyright 2012 IBM Corp. # 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 datetime import os import fixtures import mock import six import novaclient.client from novaclient import exceptions import novaclient.shell from novaclient.tests import utils from novaclient.tests.v1_1 import fakes import novaclient.v1_1.shell class ShellFixture(fixtures.Fixture): def setUp(self): super(ShellFixture, self).setUp() self.shell = novaclient.shell.OpenStackComputeShell() def tearDown(self): # For some method like test_image_meta_bad_action we are # testing a SystemExit to be thrown and object self.shell has # no time to get instantatiated which is OK in this case, so # we make sure the method is there before launching it. if hasattr(self.shell, 'cs'): self.shell.cs.clear_callstack() super(ShellFixture, self).tearDown() class ShellTest(utils.TestCase): FAKE_ENV = { 'NOVA_USERNAME': 'username', 'NOVA_PASSWORD': 'password', 'NOVA_PROJECT_ID': 'project_id', 'OS_COMPUTE_API_VERSION': '1.1', 'NOVA_URL': 'http://no.where', } def setUp(self): """Run before each test.""" super(ShellTest, self).setUp() for var in self.FAKE_ENV: self.useFixture(fixtures.EnvironmentVariable(var, self.FAKE_ENV[var])) self.shell = self.useFixture(ShellFixture()).shell self.useFixture(fixtures.MonkeyPatch( 'novaclient.client.get_client_class', lambda *_: fakes.FakeClient)) @mock.patch('sys.stdout', new_callable=six.StringIO) def run_command(self, cmd, mock_stdout): if isinstance(cmd, list): self.shell.main(cmd) else: self.shell.main(cmd.split()) return mock_stdout.getvalue() def assert_called(self, method, url, body=None, **kwargs): return self.shell.cs.assert_called(method, url, body, **kwargs) def assert_called_anytime(self, method, url, body=None): return self.shell.cs.assert_called_anytime(method, url, body) def test_agents_list_with_hypervisor(self): self.run_command('agent-list --hypervisor xen') self.assert_called('GET', '/os-agents?hypervisor=xen') def test_agents_create(self): self.run_command('agent-create win x86 7.0 ' '/xxx/xxx/xxx ' 'add6bb58e139be103324d04d82d8f546 ' 'kvm') self.assert_called( 'POST', '/os-agents', {'agent': { 'hypervisor': 'kvm', 'os': 'win', 'architecture': 'x86', 'version': '7.0', 'url': '/xxx/xxx/xxx', 'md5hash': 'add6bb58e139be103324d04d82d8f546'}}) def test_agents_delete(self): self.run_command('agent-delete 1') self.assert_called('DELETE', '/os-agents/1') def test_agents_modify(self): self.run_command('agent-modify 1 8.0 /yyy/yyyy/yyyy ' 'add6bb58e139be103324d04d82d8f546') self.assert_called('PUT', '/os-agents/1', {"para": { "url": "/yyy/yyyy/yyyy", "version": "8.0", "md5hash": "add6bb58e139be103324d04d82d8f546"}}) def test_boot(self): self.run_command('boot --flavor 1 --image 1 some-server') self.assert_called_anytime( 'POST', '/servers', {'server': { 'flavorRef': '1', 'name': 'some-server', 'imageRef': '1', 'min_count': 1, 'max_count': 1, }}, ) def test_boot_multiple(self): self.run_command('boot --flavor 1 --image 1' ' --num-instances 3 some-server') self.assert_called_anytime( 'POST', '/servers', {'server': { 'flavorRef': '1', 'name': 'some-server', 'imageRef': '1', 'min_count': 1, 'max_count': 3, }}, ) def test_boot_image_with(self): self.run_command("boot --flavor 1" " --image-with test_key=test_value some-server") self.assert_called_anytime( 'POST', '/servers', {'server': { 'flavorRef': '1', 'name': 'some-server', 'imageRef': '1', 'min_count': 1, 'max_count': 1, }}, ) def test_boot_key(self): self.run_command('boot --flavor 1 --image 1 --key_name 1 some-server') self.assert_called_anytime( 'POST', '/servers', {'server': { 'flavorRef': '1', 'name': 'some-server', 'imageRef': '1', 'key_name': '1', 'min_count': 1, 'max_count': 1, }}, ) def test_boot_user_data(self): testfile = os.path.join(os.path.dirname(__file__), 'testfile.txt') data = open(testfile).read() expected_file_data = base64.b64encode(data.encode('utf-8')) self.run_command( 'boot --flavor 1 --image 1 --user_data %s some-server' % testfile) self.assert_called_anytime( 'POST', '/servers', {'server': { 'flavorRef': '1', 'name': 'some-server', 'imageRef': '1', 'min_count': 1, 'max_count': 1, 'user_data': expected_file_data }}, ) def test_boot_avzone(self): self.run_command( 'boot --flavor 1 --image 1 --availability-zone avzone ' 'some-server') self.assert_called_anytime( 'POST', '/servers', {'server': { 'flavorRef': '1', 'name': 'some-server', 'imageRef': '1', 'availability_zone': 'avzone', 'min_count': 1, 'max_count': 1 }}, ) def test_boot_secgroup(self): self.run_command( 'boot --flavor 1 --image 1 --security-groups secgroup1,' 'secgroup2 some-server') self.assert_called_anytime( 'POST', '/servers', {'server': { 'security_groups': [{'name': 'secgroup1'}, {'name': 'secgroup2'}], 'flavorRef': '1', 'name': 'some-server', 'imageRef': '1', 'min_count': 1, 'max_count': 1, }}, ) def test_boot_config_drive(self): self.run_command( 'boot --flavor 1 --image 1 --config-drive 1 some-server') self.assert_called_anytime( 'POST', '/servers', {'server': { 'flavorRef': '1', 'name': 'some-server', 'imageRef': '1', 'min_count': 1, 'max_count': 1, 'config_drive': True }}, ) def test_boot_config_drive_custom(self): self.run_command( 'boot --flavor 1 --image 1 --config-drive /dev/hda some-server') self.assert_called_anytime( 'POST', '/servers', {'server': { 'flavorRef': '1', 'name': 'some-server', 'imageRef': '1', 'min_count': 1, 'max_count': 1, 'config_drive': '/dev/hda' }}, ) def test_boot_invalid_user_data(self): invalid_file = os.path.join(os.path.dirname(__file__), 'no_such_file') cmd = ('boot some-server --flavor 1 --image 1' ' --user_data %s' % invalid_file) self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_boot_no_image_no_bdms(self): cmd = 'boot --flavor 1 some-server' self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_boot_no_flavor(self): cmd = 'boot --image 1 some-server' self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_boot_no_image_bdms(self): self.run_command( 'boot --flavor 1 --block_device_mapping vda=blah:::0 some-server' ) self.assert_called_anytime( 'POST', '/os-volumes_boot', {'server': { 'flavorRef': '1', 'name': 'some-server', 'block_device_mapping': [ { 'volume_id': 'blah', 'delete_on_termination': '0', 'device_name': 'vda' } ], 'imageRef': '', 'min_count': 1, 'max_count': 1, }}, ) def test_boot_image_bdms_v2(self): self.run_command( 'boot --flavor 1 --image 1 --block-device id=fake-id,' 'source=volume,dest=volume,device=vda,size=1,format=ext4,' 'type=disk,shutdown=preserve some-server' ) self.assert_called_anytime( 'POST', '/os-volumes_boot', {'server': { 'flavorRef': '1', 'name': 'some-server', 'block_device_mapping_v2': [ { 'uuid': 1, 'source_type': 'image', 'destination_type': 'local', 'boot_index': 0, 'delete_on_termination': True, }, { 'uuid': 'fake-id', 'source_type': 'volume', 'destination_type': 'volume', 'device_name': 'vda', 'volume_size': '1', 'guest_format': 'ext4', 'device_type': 'disk', 'delete_on_termination': False, }, ], 'imageRef': '1', 'min_count': 1, 'max_count': 1, }}, ) def test_boot_no_image_bdms_v2(self): self.run_command( 'boot --flavor 1 --block-device id=fake-id,source=volume,' 'dest=volume,bus=virtio,device=vda,size=1,format=ext4,bootindex=0,' 'type=disk,shutdown=preserve some-server' ) self.assert_called_anytime( 'POST', '/os-volumes_boot', {'server': { 'flavorRef': '1', 'name': 'some-server', 'block_device_mapping_v2': [ { 'uuid': 'fake-id', 'source_type': 'volume', 'destination_type': 'volume', 'disk_bus': 'virtio', 'device_name': 'vda', 'volume_size': '1', 'guest_format': 'ext4', 'boot_index': '0', 'device_type': 'disk', 'delete_on_termination': False, } ], 'imageRef': '', 'min_count': 1, 'max_count': 1, }}, ) cmd = 'boot --flavor 1 --boot-volume fake-id some-server' self.run_command(cmd) self.assert_called_anytime( 'POST', '/os-volumes_boot', {'server': { 'flavorRef': '1', 'name': 'some-server', 'block_device_mapping_v2': [ { 'uuid': 'fake-id', 'source_type': 'volume', 'destination_type': 'volume', 'boot_index': 0, 'delete_on_termination': False, } ], 'imageRef': '', 'min_count': 1, 'max_count': 1, }}, ) cmd = 'boot --flavor 1 --snapshot fake-id some-server' self.run_command(cmd) self.assert_called_anytime( 'POST', '/os-volumes_boot', {'server': { 'flavorRef': '1', 'name': 'some-server', 'block_device_mapping_v2': [ { 'uuid': 'fake-id', 'source_type': 'snapshot', 'destination_type': 'volume', 'boot_index': 0, 'delete_on_termination': False, } ], 'imageRef': '', 'min_count': 1, 'max_count': 1, }}, ) self.run_command('boot --flavor 1 --swap 1 some-server') self.assert_called_anytime( 'POST', '/os-volumes_boot', {'server': { 'flavorRef': '1', 'name': 'some-server', 'block_device_mapping_v2': [ { 'source_type': 'blank', 'destination_type': 'local', 'boot_index': -1, 'guest_format': 'swap', 'volume_size': '1', 'delete_on_termination': True, } ], 'imageRef': '', 'min_count': 1, 'max_count': 1, }}, ) self.run_command( 'boot --flavor 1 --ephemeral size=1,format=ext4 some-server' ) self.assert_called_anytime( 'POST', '/os-volumes_boot', {'server': { 'flavorRef': '1', 'name': 'some-server', 'block_device_mapping_v2': [ { 'source_type': 'blank', 'destination_type': 'local', 'boot_index': -1, 'guest_format': 'ext4', 'volume_size': '1', 'delete_on_termination': True, } ], 'imageRef': '', 'min_count': 1, 'max_count': 1, }}, ) def test_boot_metadata(self): self.run_command('boot --image 1 --flavor 1 --meta foo=bar=pants' ' --meta spam=eggs some-server ') self.assert_called_anytime( 'POST', '/servers', {'server': { 'flavorRef': '1', 'name': 'some-server', 'imageRef': '1', 'metadata': {'foo': 'bar=pants', 'spam': 'eggs'}, 'min_count': 1, 'max_count': 1, }}, ) def test_boot_hints(self): self.run_command('boot --image 1 --flavor 1 ' '--hint a=b0=c0 --hint a=b1=c1 some-server ') self.assert_called_anytime( 'POST', '/servers', { 'server': { 'flavorRef': '1', 'name': 'some-server', 'imageRef': '1', 'min_count': 1, 'max_count': 1, }, 'os:scheduler_hints': {'a': ['b0=c0', 'b1=c1']}, }, ) def test_boot_nics(self): cmd = ('boot --image 1 --flavor 1 ' '--nic net-id=a=c,v4-fixed-ip=10.0.0.1 some-server') self.run_command(cmd) self.assert_called_anytime( 'POST', '/servers', { 'server': { 'flavorRef': '1', 'name': 'some-server', 'imageRef': '1', 'min_count': 1, 'max_count': 1, 'networks': [ {'uuid': 'a=c', 'fixed_ip': '10.0.0.1'}, ], }, }, ) def tets_boot_nics_no_value(self): cmd = ('boot --image 1 --flavor 1 ' '--nic net-id some-server') self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_boot_nics_random_key(self): cmd = ('boot --image 1 --flavor 1 ' '--nic net-id=a=c,v4-fixed-ip=10.0.0.1,foo=bar some-server') self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_boot_nics_no_netid_or_portid(self): cmd = ('boot --image 1 --flavor 1 ' '--nic v4-fixed-ip=10.0.0.1 some-server') self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_boot_files(self): testfile = os.path.join(os.path.dirname(__file__), 'testfile.txt') data = open(testfile).read() expected_file_data = base64.b64encode(data.encode('utf-8')) cmd = ('boot some-server --flavor 1 --image 1' ' --file /tmp/foo=%s --file /tmp/bar=%s') self.run_command(cmd % (testfile, testfile)) self.assert_called_anytime( 'POST', '/servers', {'server': { 'flavorRef': '1', 'name': 'some-server', 'imageRef': '1', 'min_count': 1, 'max_count': 1, 'personality': [ {'path': '/tmp/bar', 'contents': expected_file_data}, {'path': '/tmp/foo', 'contents': expected_file_data}, ] }}, ) def test_boot_invalid_files(self): invalid_file = os.path.join(os.path.dirname(__file__), 'asdfasdfasdfasdf') cmd = ('boot some-server --flavor 1 --image 1' ' --file /foo=%s' % invalid_file) self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_boot_num_instances(self): self.run_command('boot --image 1 --flavor 1 --num-instances 3 server') self.assert_called_anytime( 'POST', '/servers', { 'server': { 'flavorRef': '1', 'name': 'server', 'imageRef': '1', 'min_count': 1, 'max_count': 3, } }) def test_boot_invalid_num_instances(self): cmd = 'boot --image 1 --flavor 1 --num-instances 1 server' self.assertRaises(exceptions.CommandError, self.run_command, cmd) cmd = 'boot --image 1 --flavor 1 --num-instances 0 server' self.assertRaises(exceptions.CommandError, self.run_command, cmd) @mock.patch('novaclient.v1_1.shell._poll_for_status') def test_boot_with_poll(self, poll_method): self.run_command('boot --flavor 1 --image 1 some-server --poll') self.assert_called_anytime( 'POST', '/servers', {'server': { 'flavorRef': '1', 'name': 'some-server', 'imageRef': '1', 'min_count': 1, 'max_count': 1, }}, ) self.assertEqual(poll_method.call_count, 1) poll_method.assert_has_calls( [mock.call(self.shell.cs.servers.get, 1234, 'building', ['active'])]) def test_flavor_list(self): self.run_command('flavor-list') self.assert_called_anytime('GET', '/flavors/detail') def test_flavor_list_with_extra_specs(self): self.run_command('flavor-list --extra-specs') self.assert_called('GET', '/flavors/aa1/os-extra_specs') self.assert_called_anytime('GET', '/flavors/detail') def test_flavor_list_with_all(self): self.run_command('flavor-list --all') self.assert_called('GET', '/flavors/detail?is_public=None') def test_flavor_show(self): self.run_command('flavor-show 1') self.assert_called_anytime('GET', '/flavors/1') def test_flavor_show_with_alphanum_id(self): self.run_command('flavor-show aa1') self.assert_called_anytime('GET', '/flavors/aa1') def test_flavor_key_set(self): self.run_command('flavor-key 1 set k1=v1') self.assert_called('POST', '/flavors/1/os-extra_specs', {'extra_specs': {'k1': 'v1'}}) def test_flavor_key_unset(self): self.run_command('flavor-key 1 unset k1') self.assert_called('DELETE', '/flavors/1/os-extra_specs/k1') def test_flavor_access_list_flavor(self): self.run_command('flavor-access-list --flavor 2') self.assert_called('GET', '/flavors/2/os-flavor-access') # FIXME: flavor-access-list is not implemented yet # def test_flavor_access_list_tenant(self): # self.run_command('flavor-access-list --tenant proj2') # self.assert_called('GET', '/flavors/2/os-flavor-access') def test_flavor_access_list_bad_filter(self): cmd = 'flavor-access-list --flavor 2 --tenant proj2' self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_flavor_access_list_no_filter(self): cmd = 'flavor-access-list' self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_flavor_access_add_by_id(self): self.run_command('flavor-access-add 2 proj2') self.assert_called('POST', '/flavors/2/action', {'addTenantAccess': {'tenant': 'proj2'}}) def test_flavor_access_add_by_name(self): self.run_command(['flavor-access-add', '512 MB Server', 'proj2']) self.assert_called('POST', '/flavors/2/action', {'addTenantAccess': {'tenant': 'proj2'}}) def test_flavor_access_remove_by_id(self): self.run_command('flavor-access-remove 2 proj2') self.assert_called('POST', '/flavors/2/action', {'removeTenantAccess': {'tenant': 'proj2'}}) def test_flavor_access_remove_by_name(self): self.run_command(['flavor-access-remove', '512 MB Server', 'proj2']) self.assert_called('POST', '/flavors/2/action', {'removeTenantAccess': {'tenant': 'proj2'}}) def test_image_show(self): self.run_command('image-show 1') self.assert_called('GET', '/images/1') def test_image_meta_set(self): self.run_command('image-meta 1 set test_key=test_value') self.assert_called('POST', '/images/1/metadata', {'metadata': {'test_key': 'test_value'}}) def test_image_meta_del(self): self.run_command('image-meta 1 delete test_key=test_value') self.assert_called('DELETE', '/images/1/metadata/test_key') @mock.patch('sys.stdout', six.StringIO()) @mock.patch('sys.stderr', six.StringIO()) def test_image_meta_bad_action(self): self.assertRaises(SystemExit, self.run_command, 'image-meta 1 BAD_ACTION test_key=test_value') def test_image_list(self): self.run_command('image-list') self.assert_called('GET', '/images/detail') def test_create_image(self): self.run_command('image-create sample-server mysnapshot') self.assert_called( 'POST', '/servers/1234/action', {'createImage': {'name': 'mysnapshot', 'metadata': {}}}, ) def test_create_image_show(self): output = self.run_command('image-create ' 'sample-server mysnapshot --show') self.assert_called_anytime( 'POST', '/servers/1234/action', {'createImage': {'name': 'mysnapshot', 'metadata': {}}}, ) self.assertIn('My Server Backup', output) self.assertIn('SAVING', output) def test_image_delete(self): self.run_command('image-delete 1') self.assert_called('DELETE', '/images/1') def test_image_delete_multiple(self): self.run_command('image-delete 1 2') self.assert_called('DELETE', '/images/1', pos=-3) self.assert_called('DELETE', '/images/2', pos=-1) def test_list(self): self.run_command('list') self.assert_called('GET', '/servers/detail') def test_list_minimal(self): self.run_command('list --minimal') self.assert_called('GET', '/servers') def test_list_deleted(self): self.run_command('list --deleted') self.assert_called('GET', '/servers/detail?deleted=True') def test_list_with_images(self): self.run_command('list --image 1') self.assert_called('GET', '/servers/detail?image=1') def test_list_with_flavors(self): self.run_command('list --flavor 1') self.assert_called('GET', '/servers/detail?flavor=1') def test_list_fields(self): output = self.run_command('list --fields ' 'host,security_groups,OS-EXT-MOD:some_thing') self.assert_called('GET', '/servers/detail') self.assertIn('computenode1', output) self.assertIn('securitygroup1', output) self.assertIn('OS-EXT-MOD: Some Thing', output) self.assertIn('mod_some_thing_value', output) def test_reboot(self): self.run_command('reboot sample-server') self.assert_called('POST', '/servers/1234/action', {'reboot': {'type': 'SOFT'}}) self.run_command('reboot sample-server --hard') self.assert_called('POST', '/servers/1234/action', {'reboot': {'type': 'HARD'}}) def test_rebuild(self): self.run_command('rebuild sample-server 1') self.assert_called('GET', '/servers', pos=-8) self.assert_called('GET', '/servers/1234', pos=-7) self.assert_called('GET', '/images/1', pos=-6) self.assert_called('POST', '/servers/1234/action', {'rebuild': {'imageRef': 1}}, pos=-5) self.assert_called('GET', '/flavors/1', pos=-2) self.assert_called('GET', '/images/2') self.run_command('rebuild sample-server 1 --rebuild-password asdf') self.assert_called('GET', '/servers', pos=-8) self.assert_called('GET', '/servers/1234', pos=-7) self.assert_called('GET', '/images/1', pos=-6) self.assert_called('POST', '/servers/1234/action', {'rebuild': {'imageRef': 1, 'adminPass': 'asdf'}}, pos=-5) self.assert_called('GET', '/flavors/1', pos=-2) self.assert_called('GET', '/images/2') def test_rebuild_preserve_ephemeral(self): self.run_command('rebuild sample-server 1 --preserve-ephemeral') self.assert_called('GET', '/servers', pos=-8) self.assert_called('GET', '/servers/1234', pos=-7) self.assert_called('GET', '/images/1', pos=-6) self.assert_called('POST', '/servers/1234/action', {'rebuild': {'imageRef': 1, 'preserve_ephemeral': True}}, pos=-5) self.assert_called('GET', '/flavors/1', pos=-2) self.assert_called('GET', '/images/2') self.run_command('rebuild sample-server 1 --rebuild-password asdf') self.assert_called('GET', '/servers', pos=-8) self.assert_called('GET', '/servers/1234', pos=-7) self.assert_called('GET', '/images/1', pos=-6) self.assert_called('POST', '/servers/1234/action', {'rebuild': {'imageRef': 1, 'adminPass': 'asdf'}}, pos=-5) self.assert_called('GET', '/flavors/1', pos=-2) self.assert_called('GET', '/images/2') def test_start(self): self.run_command('start sample-server') self.assert_called('POST', '/servers/1234/action', {'os-start': None}) def test_stop(self): self.run_command('stop sample-server') self.assert_called('POST', '/servers/1234/action', {'os-stop': None}) def test_pause(self): self.run_command('pause sample-server') self.assert_called('POST', '/servers/1234/action', {'pause': None}) def test_unpause(self): self.run_command('unpause sample-server') self.assert_called('POST', '/servers/1234/action', {'unpause': None}) def test_lock(self): self.run_command('lock sample-server') self.assert_called('POST', '/servers/1234/action', {'lock': None}) def test_unlock(self): self.run_command('unlock sample-server') self.assert_called('POST', '/servers/1234/action', {'unlock': None}) def test_suspend(self): self.run_command('suspend sample-server') self.assert_called('POST', '/servers/1234/action', {'suspend': None}) def test_resume(self): self.run_command('resume sample-server') self.assert_called('POST', '/servers/1234/action', {'resume': None}) def test_rescue(self): self.run_command('rescue sample-server') self.assert_called('POST', '/servers/1234/action', {'rescue': None}) def test_unrescue(self): self.run_command('unrescue sample-server') self.assert_called('POST', '/servers/1234/action', {'unrescue': None}) def test_shelve(self): self.run_command('shelve sample-server') self.assert_called('POST', '/servers/1234/action', {'shelve': None}) def test_shelve_offload(self): self.run_command('shelve-offload sample-server') self.assert_called('POST', '/servers/1234/action', {'shelveOffload': None}) def test_unshelve(self): self.run_command('unshelve sample-server') self.assert_called('POST', '/servers/1234/action', {'unshelve': None}) def test_migrate(self): self.run_command('migrate sample-server') self.assert_called('POST', '/servers/1234/action', {'migrate': None}) def test_rename(self): self.run_command('rename sample-server newname') self.assert_called('PUT', '/servers/1234', {'server': {'name': 'newname'}}) def test_resize(self): self.run_command('resize sample-server 1') self.assert_called('POST', '/servers/1234/action', {'resize': {'flavorRef': 1}}) def test_resize_confirm(self): self.run_command('resize-confirm sample-server') self.assert_called('POST', '/servers/1234/action', {'confirmResize': None}) def test_resize_revert(self): self.run_command('resize-revert sample-server') self.assert_called('POST', '/servers/1234/action', {'revertResize': None}) @mock.patch('getpass.getpass', mock.Mock(return_value='p')) def test_root_password(self): self.run_command('root-password sample-server') self.assert_called('POST', '/servers/1234/action', {'changePassword': {'adminPass': 'p'}}) def test_scrub(self): self.run_command('scrub 4ffc664c198e435e9853f2538fbcd7a7') self.assert_called('GET', '/os-networks', pos=-4) self.assert_called('GET', '/os-security-groups?all_tenants=1', pos=-3) self.assert_called('POST', '/os-networks/1/action', {"disassociate": None}, pos=-2) self.assert_called('DELETE', '/os-security-groups/1') def test_show(self): self.run_command('show 1234') self.assert_called('GET', '/servers/1234', pos=-3) self.assert_called('GET', '/flavors/1', pos=-2) self.assert_called('GET', '/images/2') def test_show_no_image(self): self.run_command('show 9012') self.assert_called('GET', '/servers/9012', pos=-2) self.assert_called('GET', '/flavors/1', pos=-1) def test_show_bad_id(self): self.assertRaises(exceptions.CommandError, self.run_command, 'show xxx') @mock.patch('novaclient.v1_1.shell.utils.print_dict') def test_print_server(self, mock_print_dict): self.run_command('show 5678') args, kwargs = mock_print_dict.call_args parsed_server = args[0] self.assertEqual('securitygroup1, securitygroup2', parsed_server['security_groups']) def test_delete(self): self.run_command('delete 1234') self.assert_called('DELETE', '/servers/1234') self.run_command('delete sample-server') self.assert_called('DELETE', '/servers/1234') def test_force_delete(self): self.run_command('force-delete 1234') self.assert_called('POST', '/servers/1234/action', {'forceDelete': None}) self.run_command('force-delete sample-server') self.assert_called('POST', '/servers/1234/action', {'forceDelete': None}) def test_restore(self): self.run_command('restore 1234') self.assert_called('POST', '/servers/1234/action', {'restore': None}) self.run_command('restore sample-server') self.assert_called('POST', '/servers/1234/action', {'restore': None}) def test_delete_two_with_two_existent(self): self.run_command('delete 1234 5678') self.assert_called('DELETE', '/servers/1234', pos=-3) self.assert_called('DELETE', '/servers/5678', pos=-1) self.run_command('delete sample-server sample-server2') self.assert_called('GET', '/servers', pos=-6) self.assert_called('GET', '/servers/1234', pos=-5) self.assert_called('DELETE', '/servers/1234', pos=-4) self.assert_called('GET', '/servers', pos=-3) self.assert_called('GET', '/servers/5678', pos=-2) self.assert_called('DELETE', '/servers/5678', pos=-1) def test_delete_two_with_one_nonexistent(self): self.run_command('delete 1234 123456789') self.assert_called_anytime('DELETE', '/servers/1234') self.run_command('delete sample-server nonexistentserver') self.assert_called_anytime('DELETE', '/servers/1234') def test_delete_one_with_one_nonexistent(self): cmd = 'delete 123456789' self.assertRaises(exceptions.CommandError, self.run_command, cmd) cmd = 'delete nonexistent-server1' self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_delete_two_with_two_nonexistent(self): cmd = 'delete 123456789 987654321' self.assertRaises(exceptions.CommandError, self.run_command, cmd) cmd = 'delete nonexistent-server1 nonexistent-server2' self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_diagnostics(self): self.run_command('diagnostics 1234') self.assert_called('GET', '/servers/1234/diagnostics') self.run_command('diagnostics sample-server') self.assert_called('GET', '/servers/1234/diagnostics') def test_refresh_network(self): self.run_command('refresh-network 1234') self.assert_called('POST', '/os-server-external-events', {'events': [{'name': 'network-changed', 'server_uuid': 1234}]}) def test_set_meta_set(self): self.run_command('meta 1234 set key1=val1 key2=val2') self.assert_called('POST', '/servers/1234/metadata', {'metadata': {'key1': 'val1', 'key2': 'val2'}}) def test_set_meta_delete_dict(self): self.run_command('meta 1234 delete key1=val1 key2=val2') self.assert_called('DELETE', '/servers/1234/metadata/key1') self.assert_called('DELETE', '/servers/1234/metadata/key2', pos=-2) def test_set_meta_delete_keys(self): self.run_command('meta 1234 delete key1 key2') self.assert_called('DELETE', '/servers/1234/metadata/key1') self.assert_called('DELETE', '/servers/1234/metadata/key2', pos=-2) def test_set_host_meta(self): self.run_command('host-meta hyper set key1=val1 key2=val2') self.assert_called('GET', '/os-hypervisors/hyper/servers', pos=0) self.assert_called('POST', '/servers/uuid1/metadata', {'metadata': {'key1': 'val1', 'key2': 'val2'}}, pos=1) self.assert_called('POST', '/servers/uuid2/metadata', {'metadata': {'key1': 'val1', 'key2': 'val2'}}, pos=2) self.assert_called('POST', '/servers/uuid3/metadata', {'metadata': {'key1': 'val1', 'key2': 'val2'}}, pos=3) self.assert_called('POST', '/servers/uuid4/metadata', {'metadata': {'key1': 'val1', 'key2': 'val2'}}, pos=4) def test_set_host_meta_with_no_servers(self): self.run_command('host-meta hyper_no_servers set key1=val1 key2=val2') self.assert_called('GET', '/os-hypervisors/hyper_no_servers/servers') def test_delete_host_meta(self): self.run_command('host-meta hyper delete key1') self.assert_called('GET', '/os-hypervisors/hyper/servers', pos=0) self.assert_called('DELETE', '/servers/uuid1/metadata/key1', pos=1) self.assert_called('DELETE', '/servers/uuid2/metadata/key1', pos=2) def test_dns_create(self): self.run_command('dns-create 192.168.1.1 testname testdomain') self.assert_called('PUT', '/os-floating-ip-dns/testdomain/entries/testname') self.run_command('dns-create 192.168.1.1 testname testdomain --type A') self.assert_called('PUT', '/os-floating-ip-dns/testdomain/entries/testname') def test_dns_create_public_domain(self): self.run_command('dns-create-public-domain testdomain ' '--project test_project') self.assert_called('PUT', '/os-floating-ip-dns/testdomain') def test_dns_create_private_domain(self): self.run_command('dns-create-private-domain testdomain ' '--availability-zone av_zone') self.assert_called('PUT', '/os-floating-ip-dns/testdomain') def test_dns_delete(self): self.run_command('dns-delete testdomain testname') self.assert_called('DELETE', '/os-floating-ip-dns/testdomain/entries/testname') def test_dns_delete_domain(self): self.run_command('dns-delete-domain testdomain') self.assert_called('DELETE', '/os-floating-ip-dns/testdomain') def test_dns_list(self): self.run_command('dns-list testdomain --ip 192.168.1.1') self.assert_called('GET', '/os-floating-ip-dns/testdomain/entries?' 'ip=192.168.1.1') self.run_command('dns-list testdomain --name testname') self.assert_called('GET', '/os-floating-ip-dns/testdomain/entries/testname') def test_dns_domains(self): self.run_command('dns-domains') self.assert_called('GET', '/os-floating-ip-dns') def test_floating_ip_list(self): self.run_command('floating-ip-list') self.assert_called('GET', '/os-floating-ips') def test_floating_ip_create(self): self.run_command('floating-ip-create') self.assert_called('GET', '/os-floating-ips/1') def test_floating_ip_delete(self): self.run_command('floating-ip-delete 11.0.0.1') self.assert_called('DELETE', '/os-floating-ips/1') def test_floating_ip_bulk_list(self): self.run_command('floating-ip-bulk-list') self.assert_called('GET', '/os-floating-ips-bulk') def test_floating_ip_bulk_create(self): self.run_command('floating-ip-bulk-create 10.0.0.1/24') self.assert_called('POST', '/os-floating-ips-bulk', {'floating_ips_bulk_create': {'ip_range': '10.0.0.1/24'}}) def test_floating_ip_bulk_create_host_and_interface(self): self.run_command('floating-ip-bulk-create 10.0.0.1/24 --pool testPool' ' --interface ethX') self.assert_called('POST', '/os-floating-ips-bulk', {'floating_ips_bulk_create': {'ip_range': '10.0.0.1/24', 'pool': 'testPool', 'interface': 'ethX'}}) def test_floating_ip_bulk_delete(self): self.run_command('floating-ip-bulk-delete 10.0.0.1/24') self.assert_called('PUT', '/os-floating-ips-bulk/delete', {'ip_range': '10.0.0.1/24'}) def test_server_floating_ip_add(self): self.run_command('add-floating-ip sample-server 11.0.0.1') self.assert_called('POST', '/servers/1234/action', {'addFloatingIp': {'address': '11.0.0.1'}}) def test_server_floating_ip_remove(self): self.run_command('remove-floating-ip sample-server 11.0.0.1') self.assert_called('POST', '/servers/1234/action', {'removeFloatingIp': {'address': '11.0.0.1'}}) def test_server_floating_ip_associate(self): self.run_command('floating-ip-associate sample-server 11.0.0.1') self.assert_called('POST', '/servers/1234/action', {'addFloatingIp': {'address': '11.0.0.1'}}) def test_server_floating_ip_disassociate(self): self.run_command('floating-ip-disassociate sample-server 11.0.0.1') self.assert_called('POST', '/servers/1234/action', {'removeFloatingIp': {'address': '11.0.0.1'}}) def test_usage_list(self): self.run_command('usage-list --start 2000-01-20 --end 2005-02-01') self.assert_called('GET', '/os-simple-tenant-usage?' + 'start=2000-01-20T00:00:00&' + 'end=2005-02-01T00:00:00&' + 'detailed=1') @mock.patch('novaclient.openstack.common.timeutils.utcnow') def test_usage_list_no_args(self, mock_utcnow): mock_utcnow.return_value = datetime.datetime(2005, 2, 1, 0, 0) self.run_command('usage-list') self.assert_called('GET', '/os-simple-tenant-usage?' + 'start=2005-01-04T00:00:00&' + 'end=2005-02-02T00:00:00&' + 'detailed=1') def test_usage(self): self.run_command('usage --start 2000-01-20 --end 2005-02-01 ' '--tenant test') self.assert_called('GET', '/os-simple-tenant-usage/test?' + 'start=2000-01-20T00:00:00&' + 'end=2005-02-01T00:00:00') def test_usage_no_tenant(self): self.run_command('usage --start 2000-01-20 --end 2005-02-01') self.assert_called('GET', '/os-simple-tenant-usage/tenant_id?' + 'start=2000-01-20T00:00:00&' + 'end=2005-02-01T00:00:00') def test_flavor_delete(self): self.run_command("flavor-delete 2") self.assert_called('DELETE', '/flavors/2') def test_flavor_create(self): self.run_command("flavor-create flavorcreate " "1234 512 10 1 --swap 1024 --ephemeral 10 " "--is-public true") self.assert_called('POST', '/flavors', pos=-2) self.assert_called('GET', '/flavors/1', pos=-1) def test_aggregate_list(self): self.run_command('aggregate-list') self.assert_called('GET', '/os-aggregates') def test_aggregate_create(self): self.run_command('aggregate-create test_name nova1') body = {"aggregate": {"name": "test_name", "availability_zone": "nova1"}} self.assert_called('POST', '/os-aggregates', body, pos=-2) self.assert_called('GET', '/os-aggregates/1', pos=-1) def test_aggregate_delete_by_id(self): self.run_command('aggregate-delete 1') self.assert_called('DELETE', '/os-aggregates/1') def test_aggregate_delete_by_name(self): self.run_command('aggregate-delete test') self.assert_called('DELETE', '/os-aggregates/1') def test_aggregate_update_by_id(self): self.run_command('aggregate-update 1 new_name') body = {"aggregate": {"name": "new_name"}} self.assert_called('PUT', '/os-aggregates/1', body, pos=-2) self.assert_called('GET', '/os-aggregates/1', pos=-1) def test_aggregate_update_by_name(self): self.run_command('aggregate-update test new_name') body = {"aggregate": {"name": "new_name"}} self.assert_called('PUT', '/os-aggregates/1', body, pos=-2) self.assert_called('GET', '/os-aggregates/1', pos=-1) def test_aggregate_update_with_availability_zone_by_id(self): self.run_command('aggregate-update 1 foo new_zone') body = {"aggregate": {"name": "foo", "availability_zone": "new_zone"}} self.assert_called('PUT', '/os-aggregates/1', body, pos=-2) self.assert_called('GET', '/os-aggregates/1', pos=-1) def test_aggregate_update_with_availability_zone_by_name(self): self.run_command('aggregate-update test foo new_zone') body = {"aggregate": {"name": "foo", "availability_zone": "new_zone"}} self.assert_called('PUT', '/os-aggregates/1', body, pos=-2) self.assert_called('GET', '/os-aggregates/1', pos=-1) def test_aggregate_set_metadata_by_id(self): self.run_command('aggregate-set-metadata 1 foo=bar delete_key') body = {"set_metadata": {"metadata": {"foo": "bar", "delete_key": None}}} self.assert_called('POST', '/os-aggregates/1/action', body, pos=-2) self.assert_called('GET', '/os-aggregates/1', pos=-1) def test_aggregate_set_metadata_by_name(self): self.run_command('aggregate-set-metadata test foo=bar delete_key') body = {"set_metadata": {"metadata": {"foo": "bar", "delete_key": None}}} self.assert_called('POST', '/os-aggregates/1/action', body, pos=-2) self.assert_called('GET', '/os-aggregates/1', pos=-1) def test_aggregate_add_host_by_id(self): self.run_command('aggregate-add-host 1 host1') body = {"add_host": {"host": "host1"}} self.assert_called('POST', '/os-aggregates/1/action', body, pos=-2) self.assert_called('GET', '/os-aggregates/1', pos=-1) def test_aggregate_add_host_by_name(self): self.run_command('aggregate-add-host test host1') body = {"add_host": {"host": "host1"}} self.assert_called('POST', '/os-aggregates/1/action', body, pos=-2) self.assert_called('GET', '/os-aggregates/1', pos=-1) def test_aggregate_remove_host_by_id(self): self.run_command('aggregate-remove-host 1 host1') body = {"remove_host": {"host": "host1"}} self.assert_called('POST', '/os-aggregates/1/action', body, pos=-2) self.assert_called('GET', '/os-aggregates/1', pos=-1) def test_aggregate_remove_host_by_name(self): self.run_command('aggregate-remove-host test host1') body = {"remove_host": {"host": "host1"}} self.assert_called('POST', '/os-aggregates/1/action', body, pos=-2) self.assert_called('GET', '/os-aggregates/1', pos=-1) def test_aggregate_details_by_id(self): self.run_command('aggregate-details 1') self.assert_called('GET', '/os-aggregates/1') def test_aggregate_details_by_name(self): self.run_command('aggregate-details test') self.assert_called('GET', '/os-aggregates') def test_live_migration(self): self.run_command('live-migration sample-server hostname') self.assert_called('POST', '/servers/1234/action', {'os-migrateLive': {'host': 'hostname', 'block_migration': False, 'disk_over_commit': False}}) self.run_command('live-migration sample-server hostname' ' --block-migrate') self.assert_called('POST', '/servers/1234/action', {'os-migrateLive': {'host': 'hostname', 'block_migration': True, 'disk_over_commit': False}}) self.run_command('live-migration sample-server hostname' ' --block-migrate --disk-over-commit') self.assert_called('POST', '/servers/1234/action', {'os-migrateLive': {'host': 'hostname', 'block_migration': True, 'disk_over_commit': True}}) def test_reset_state(self): self.run_command('reset-state sample-server') self.assert_called('POST', '/servers/1234/action', {'os-resetState': {'state': 'error'}}) self.run_command('reset-state sample-server --active') self.assert_called('POST', '/servers/1234/action', {'os-resetState': {'state': 'active'}}) def test_reset_network(self): self.run_command('reset-network sample-server') self.assert_called('POST', '/servers/1234/action', {'resetNetwork': None}) def test_services_list(self): self.run_command('service-list') self.assert_called('GET', '/os-services') def test_services_list_with_host(self): self.run_command('service-list --host host1') self.assert_called('GET', '/os-services?host=host1') def test_services_list_with_binary(self): self.run_command('service-list --binary nova-cert') self.assert_called('GET', '/os-services?binary=nova-cert') def test_services_list_with_host_binary(self): self.run_command('service-list --host host1 --binary nova-cert') self.assert_called('GET', '/os-services?host=host1&binary=nova-cert') def test_services_enable(self): self.run_command('service-enable host1 nova-cert') body = {'host': 'host1', 'binary': 'nova-cert'} self.assert_called('PUT', '/os-services/enable', body) def test_services_disable(self): self.run_command('service-disable host1 nova-cert') body = {'host': 'host1', 'binary': 'nova-cert'} self.assert_called('PUT', '/os-services/disable', body) def test_services_disable_with_reason(self): self.run_command('service-disable host1 nova-cert --reason no_reason') body = {'host': 'host1', 'binary': 'nova-cert', 'disabled_reason': 'no_reason'} self.assert_called('PUT', '/os-services/disable-log-reason', body) def test_fixed_ips_get(self): self.run_command('fixed-ip-get 192.168.1.1') self.assert_called('GET', '/os-fixed-ips/192.168.1.1') def test_fixed_ips_reserve(self): self.run_command('fixed-ip-reserve 192.168.1.1') body = {'reserve': None} self.assert_called('POST', '/os-fixed-ips/192.168.1.1/action', body) def test_fixed_ips_unreserve(self): self.run_command('fixed-ip-unreserve 192.168.1.1') body = {'unreserve': None} self.assert_called('POST', '/os-fixed-ips/192.168.1.1/action', body) def test_host_list(self): self.run_command('host-list') self.assert_called('GET', '/os-hosts') def test_host_list_with_zone(self): self.run_command('host-list --zone nova') self.assert_called('GET', '/os-hosts?zone=nova') def test_host_update_status(self): self.run_command('host-update sample-host_1 --status enabled') body = {'status': 'enabled'} self.assert_called('PUT', '/os-hosts/sample-host_1', body) def test_host_update_maintenance(self): self.run_command('host-update sample-host_2 --maintenance enable') body = {'maintenance_mode': 'enable'} self.assert_called('PUT', '/os-hosts/sample-host_2', body) def test_host_update_multiple_settings(self): self.run_command('host-update sample-host_3 ' '--status disabled --maintenance enable') body = {'status': 'disabled', 'maintenance_mode': 'enable'} self.assert_called('PUT', '/os-hosts/sample-host_3', body) def test_host_startup(self): self.run_command('host-action sample-host --action startup') self.assert_called( 'GET', '/os-hosts/sample-host/startup') def test_host_shutdown(self): self.run_command('host-action sample-host --action shutdown') self.assert_called( 'GET', '/os-hosts/sample-host/shutdown') def test_host_reboot(self): self.run_command('host-action sample-host --action reboot') self.assert_called( 'GET', '/os-hosts/sample-host/reboot') def test_host_evacuate(self): self.run_command('host-evacuate hyper --target target_hyper') self.assert_called('GET', '/os-hypervisors/hyper/servers', pos=0) self.assert_called('POST', '/servers/uuid1/action', {'evacuate': {'host': 'target_hyper', 'onSharedStorage': False}}, pos=1) self.assert_called('POST', '/servers/uuid2/action', {'evacuate': {'host': 'target_hyper', 'onSharedStorage': False}}, pos=2) self.assert_called('POST', '/servers/uuid3/action', {'evacuate': {'host': 'target_hyper', 'onSharedStorage': False}}, pos=3) self.assert_called('POST', '/servers/uuid4/action', {'evacuate': {'host': 'target_hyper', 'onSharedStorage': False}}, pos=4) def test_host_evacuate_with_shared_storage(self): self.run_command( 'host-evacuate --on-shared-storage hyper --target target_hyper') self.assert_called('GET', '/os-hypervisors/hyper/servers', pos=0) self.assert_called('POST', '/servers/uuid1/action', {'evacuate': {'host': 'target_hyper', 'onSharedStorage': True}}, pos=1) self.assert_called('POST', '/servers/uuid2/action', {'evacuate': {'host': 'target_hyper', 'onSharedStorage': True}}, pos=2) self.assert_called('POST', '/servers/uuid3/action', {'evacuate': {'host': 'target_hyper', 'onSharedStorage': True}}, pos=3) self.assert_called('POST', '/servers/uuid4/action', {'evacuate': {'host': 'target_hyper', 'onSharedStorage': True}}, pos=4) def test_host_evacuate_with_no_target_host(self): self.run_command('host-evacuate --on-shared-storage hyper') self.assert_called('GET', '/os-hypervisors/hyper/servers', pos=0) self.assert_called('POST', '/servers/uuid1/action', {'evacuate': {'host': None, 'onSharedStorage': True}}, pos=1) self.assert_called('POST', '/servers/uuid2/action', {'evacuate': {'host': None, 'onSharedStorage': True}}, pos=2) self.assert_called('POST', '/servers/uuid3/action', {'evacuate': {'host': None, 'onSharedStorage': True}}, pos=3) self.assert_called('POST', '/servers/uuid4/action', {'evacuate': {'host': None, 'onSharedStorage': True}}, pos=4) def test_host_servers_migrate(self): self.run_command('host-servers-migrate hyper') self.assert_called('GET', '/os-hypervisors/hyper/servers', pos=0) self.assert_called('POST', '/servers/uuid1/action', {'migrate': None}, pos=1) self.assert_called('POST', '/servers/uuid2/action', {'migrate': None}, pos=2) self.assert_called('POST', '/servers/uuid3/action', {'migrate': None}, pos=3) self.assert_called('POST', '/servers/uuid4/action', {'migrate': None}, pos=4) def test_hypervisor_list(self): self.run_command('hypervisor-list') self.assert_called('GET', '/os-hypervisors') def test_hypervisor_list_matching(self): self.run_command('hypervisor-list --matching hyper') self.assert_called('GET', '/os-hypervisors/hyper/search') def test_hypervisor_servers(self): self.run_command('hypervisor-servers hyper') self.assert_called('GET', '/os-hypervisors/hyper/servers') def test_hypervisor_show_by_id(self): self.run_command('hypervisor-show 1234') self.assert_called('GET', '/os-hypervisors/1234') def test_hypervisor_show_by_name(self): self.run_command('hypervisor-show hyper1') self.assert_called('GET', '/os-hypervisors/detail') def test_hypervisor_uptime_by_id(self): self.run_command('hypervisor-uptime 1234') self.assert_called('GET', '/os-hypervisors/1234/uptime') def test_hypervisor_uptime_by_name(self): self.run_command('hypervisor-uptime hyper1') self.assert_called('GET', '/os-hypervisors/1234/uptime') def test_hypervisor_stats(self): self.run_command('hypervisor-stats') self.assert_called('GET', '/os-hypervisors/statistics') def test_quota_show(self): self.run_command('quota-show --tenant ' '97f4c221bff44578b0300df4ef119353') self.assert_called('GET', '/os-quota-sets/97f4c221bff44578b0300df4ef119353') def test_user_quota_show(self): self.run_command('quota-show --tenant ' '97f4c221bff44578b0300df4ef119353 --user u1') self.assert_called('GET', '/os-quota-sets/97f4c221bff44578b0300df4ef119353?user_id=u1') def test_quota_show_no_tenant(self): self.run_command('quota-show') self.assert_called('GET', '/os-quota-sets/tenant_id') def test_quota_defaults(self): self.run_command('quota-defaults --tenant ' '97f4c221bff44578b0300df4ef119353') self.assert_called('GET', '/os-quota-sets/97f4c221bff44578b0300df4ef119353/defaults') def test_quota_defaults_no_tenant(self): self.run_command('quota-defaults') self.assert_called('GET', '/os-quota-sets/tenant_id/defaults') def test_quota_update(self): self.run_command( 'quota-update 97f4c221bff44578b0300df4ef119353' ' --instances=5') self.assert_called( 'PUT', '/os-quota-sets/97f4c221bff44578b0300df4ef119353', {'quota_set': {'instances': 5, 'tenant_id': '97f4c221bff44578b0300df4ef119353'}}) def test_user_quota_update(self): self.run_command( 'quota-update 97f4c221bff44578b0300df4ef119353' ' --user=u1' ' --instances=5') self.assert_called( 'PUT', '/os-quota-sets/97f4c221bff44578b0300df4ef119353?user_id=u1', {'quota_set': {'instances': 5, 'tenant_id': '97f4c221bff44578b0300df4ef119353'}}) def test_quota_force_update(self): self.run_command( 'quota-update 97f4c221bff44578b0300df4ef119353' ' --instances=5 --force') self.assert_called( 'PUT', '/os-quota-sets/97f4c221bff44578b0300df4ef119353', {'quota_set': {'force': True, 'instances': 5, 'tenant_id': '97f4c221bff44578b0300df4ef119353'}}) def test_quota_update_fixed_ip(self): self.run_command( 'quota-update 97f4c221bff44578b0300df4ef119353' ' --fixed-ips=5') self.assert_called( 'PUT', '/os-quota-sets/97f4c221bff44578b0300df4ef119353', {'quota_set': {'fixed_ips': 5, 'tenant_id': '97f4c221bff44578b0300df4ef119353'}}) def test_quota_delete(self): self.run_command('quota-delete --tenant ' '97f4c221bff44578b0300df4ef119353') self.assert_called('DELETE', '/os-quota-sets/97f4c221bff44578b0300df4ef119353') def test_user_quota_delete(self): self.run_command('quota-delete --tenant ' '97f4c221bff44578b0300df4ef119353 ' '--user u1') self.assert_called('DELETE', '/os-quota-sets/97f4c221bff44578b0300df4ef119353?user_id=u1') def test_quota_class_show(self): self.run_command('quota-class-show test') self.assert_called('GET', '/os-quota-class-sets/test') def test_quota_class_update(self): self.run_command('quota-class-update 97f4c221bff44578b0300df4ef119353' ' --instances=5') self.assert_called('PUT', '/os-quota-class-sets/97f4c221bff44578b0300' 'df4ef119353') def test_network_list(self): self.run_command('network-list') self.assert_called('GET', '/os-networks') def test_network_show(self): self.run_command('network-show 1') self.assert_called('GET', '/os-networks/1') def test_cloudpipe_list(self): self.run_command('cloudpipe-list') self.assert_called('GET', '/os-cloudpipe') def test_cloudpipe_create(self): self.run_command('cloudpipe-create myproject') body = {'cloudpipe': {'project_id': "myproject"}} self.assert_called('POST', '/os-cloudpipe', body) def test_cloudpipe_configure(self): self.run_command('cloudpipe-configure 192.168.1.1 1234') body = {'configure_project': {'vpn_ip': "192.168.1.1", 'vpn_port': '1234'}} self.assert_called('PUT', '/os-cloudpipe/configure-project', body) def test_network_associate_host(self): self.run_command('network-associate-host 1 testHost') body = {'associate_host': 'testHost'} self.assert_called('POST', '/os-networks/1/action', body) def test_network_associate_project(self): self.run_command('network-associate-project 1') body = {'id': "1"} self.assert_called('POST', '/os-networks/add', body) def test_network_disassociate_host(self): self.run_command('network-disassociate --host-only 1 2') body = {'disassociate_host': None} self.assert_called('POST', '/os-networks/2/action', body) def test_network_disassociate_project(self): self.run_command('network-disassociate --project-only 1 2') body = {'disassociate_project': None} self.assert_called('POST', '/os-networks/2/action', body) def test_network_create_v4(self): self.run_command('network-create --fixed-range-v4 10.0.1.0/24' ' --dns1 10.0.1.254 new_network') body = {'network': {'cidr': '10.0.1.0/24', 'label': 'new_network', 'dns1': '10.0.1.254'}} self.assert_called('POST', '/os-networks', body) def test_network_create_v6(self): self.run_command('network-create --fixed-range-v6 2001::/64' ' new_network') body = {'network': {'cidr_v6': '2001::/64', 'label': 'new_network'}} self.assert_called('POST', '/os-networks', body) def test_network_create_invalid(self): cmd = 'network-create 10.0.1.0' self.assertRaises(exceptions.CommandError, self.run_command, cmd) def test_network_create_multi_host(self): self.run_command('network-create --fixed-range-v4 192.168.0.0/24' ' --multi-host=T new_network') body = {'network': {'cidr': '192.168.0.0/24', 'label': 'new_network', 'multi_host': True}} self.assert_called('POST', '/os-networks', body) self.run_command('network-create --fixed-range-v4 192.168.0.0/24' ' --multi-host=True new_network') body = {'network': {'cidr': '192.168.0.0/24', 'label': 'new_network', 'multi_host': True}} self.assert_called('POST', '/os-networks', body) self.run_command('network-create --fixed-range-v4 192.168.0.0/24' ' --multi-host=1 new_network') body = {'network': {'cidr': '192.168.0.0/24', 'label': 'new_network', 'multi_host': True}} self.assert_called('POST', '/os-networks', body) self.run_command('network-create --fixed-range-v4 192.168.1.0/24' ' --multi-host=F new_network') body = {'network': {'cidr': '192.168.1.0/24', 'label': 'new_network', 'multi_host': False}} self.assert_called('POST', '/os-networks', body) def test_network_create_vlan(self): self.run_command('network-create --fixed-range-v4 192.168.0.0/24' ' --vlan=200 new_network') body = {'network': {'cidr': '192.168.0.0/24', 'label': 'new_network', 'vlan_start': '200'}} self.assert_called('POST', '/os-networks', body) def test_add_fixed_ip(self): self.run_command('add-fixed-ip sample-server 1') self.assert_called('POST', '/servers/1234/action', {'addFixedIp': {'networkId': '1'}}) def test_remove_fixed_ip(self): self.run_command('remove-fixed-ip sample-server 10.0.0.10') self.assert_called('POST', '/servers/1234/action', {'removeFixedIp': {'address': '10.0.0.10'}}) def test_backup(self): self.run_command('backup sample-server back1 daily 1') self.assert_called('POST', '/servers/1234/action', {'createBackup': {'name': 'back1', 'backup_type': 'daily', 'rotation': '1'}}) self.run_command('backup 1234 back1 daily 1') self.assert_called('POST', '/servers/1234/action', {'createBackup': {'name': 'back1', 'backup_type': 'daily', 'rotation': '1'}}) def test_absolute_limits(self): self.run_command('absolute-limits') self.assert_called('GET', '/limits') self.run_command('absolute-limits --reserved') self.assert_called('GET', '/limits?reserved=1') self.run_command('absolute-limits --tenant 1234') self.assert_called('GET', '/limits?tenant_id=1234') def test_evacuate(self): self.run_command('evacuate sample-server new_host') self.assert_called('POST', '/servers/1234/action', {'evacuate': {'host': 'new_host', 'onSharedStorage': False}}) self.run_command('evacuate sample-server new_host ' '--password NewAdminPass') self.assert_called('POST', '/servers/1234/action', {'evacuate': {'host': 'new_host', 'onSharedStorage': False, 'adminPass': 'NewAdminPass'}}) self.run_command('evacuate sample-server new_host') self.assert_called('POST', '/servers/1234/action', {'evacuate': {'host': 'new_host', 'onSharedStorage': False}}) self.run_command('evacuate sample-server new_host ' '--on-shared-storage') self.assert_called('POST', '/servers/1234/action', {'evacuate': {'host': 'new_host', 'onSharedStorage': True}}) def test_get_password(self): self.run_command('get-password sample-server /foo/id_rsa') self.assert_called('GET', '/servers/1234/os-server-password') def test_get_password_without_key(self): self.run_command('get-password sample-server') self.assert_called('GET', '/servers/1234/os-server-password') def test_clear_password(self): self.run_command('clear-password sample-server') self.assert_called('DELETE', '/servers/1234/os-server-password') def test_availability_zone_list(self): self.run_command('availability-zone-list') self.assert_called('GET', '/os-availability-zone/detail') def test_security_group_create(self): self.run_command('secgroup-create test FAKE_SECURITY_GROUP') self.assert_called('POST', '/os-security-groups', {'security_group': {'name': 'test', 'description': 'FAKE_SECURITY_GROUP'}}) def test_security_group_update(self): self.run_command('secgroup-update test te FAKE_SECURITY_GROUP') self.assert_called('PUT', '/os-security-groups/1', {'security_group': {'name': 'te', 'description': 'FAKE_SECURITY_GROUP'}}) def test_security_group_list(self): self.run_command('secgroup-list') self.assert_called('GET', '/os-security-groups') def test_security_group_add_rule(self): self.run_command('secgroup-add-rule test tcp 22 22 10.0.0.0/8') self.assert_called('POST', '/os-security-group-rules', {'security_group_rule': {'from_port': 22, 'ip_protocol': 'tcp', 'to_port': 22, 'parent_group_id': 1, 'cidr': '10.0.0.0/8', 'group_id': None}}) def test_security_group_delete_rule(self): self.run_command('secgroup-delete-rule test TCP 22 22 10.0.0.0/8') self.assert_called('DELETE', '/os-security-group-rules/11') def test_security_group_delete_rule_protocol_case(self): self.run_command('secgroup-delete-rule test tcp 22 22 10.0.0.0/8') self.assert_called('DELETE', '/os-security-group-rules/11') def test_security_group_add_group_rule(self): self.run_command('secgroup-add-group-rule test test2 tcp 22 22') self.assert_called('POST', '/os-security-group-rules', {'security_group_rule': {'from_port': 22, 'ip_protocol': 'TCP', 'to_port': 22, 'parent_group_id': 1, 'cidr': None, 'group_id': 2}}) def test_security_group_delete_group_rule(self): self.run_command('secgroup-delete-group-rule test test2 TCP 222 222') self.assert_called('DELETE', '/os-security-group-rules/12') def test_security_group_delete_group_rule_protocol_case(self): self.run_command('secgroup-delete-group-rule test test2 tcp 222 222') self.assert_called('DELETE', '/os-security-group-rules/12') def test_security_group_list_rules(self): self.run_command('secgroup-list-rules test') self.assert_called('GET', '/os-security-groups') def test_security_group_list_all_tenants(self): self.run_command('secgroup-list --all-tenants 1') self.assert_called('GET', '/os-security-groups?all_tenants=1') def test_security_group_delete(self): self.run_command('secgroup-delete test') self.assert_called('DELETE', '/os-security-groups/1') def test_server_security_group_add(self): self.run_command('add-secgroup sample-server testgroup') self.assert_called('POST', '/servers/1234/action', {'addSecurityGroup': {'name': 'testgroup'}}) def test_server_security_group_remove(self): self.run_command('remove-secgroup sample-server testgroup') self.assert_called('POST', '/servers/1234/action', {'removeSecurityGroup': {'name': 'testgroup'}}) def test_server_security_group_list(self): self.run_command('list-secgroup 1234') self.assert_called('GET', '/servers/1234/os-security-groups') def test_interface_list(self): self.run_command('interface-list 1234') self.assert_called('GET', '/servers/1234/os-interface') def test_interface_attach(self): self.run_command('interface-attach --port-id port_id 1234') self.assert_called('POST', '/servers/1234/os-interface', {'interfaceAttachment': {'port_id': 'port_id'}}) def test_interface_detach(self): self.run_command('interface-detach 1234 port_id') self.assert_called('DELETE', '/servers/1234/os-interface/port_id') def test_volume_list(self): self.run_command('volume-list') self.assert_called('GET', '/volumes/detail') def test_volume_show(self): self.run_command('volume-show Work') self.assert_called('GET', '/volumes', pos=-2) self.assert_called( 'GET', '/volumes/15e59938-07d5-11e1-90e3-e3dffe0c5983', pos=-1 ) def test_volume_create(self): self.run_command('volume-create 2 --display-name Work') self.assert_called('POST', '/volumes', {'volume': {'display_name': 'Work', 'imageRef': None, 'availability_zone': None, 'volume_type': None, 'display_description': None, 'snapshot_id': None, 'size': 2}}) def test_volume_delete(self): self.run_command('volume-delete Work') self.assert_called('DELETE', '/volumes/15e59938-07d5-11e1-90e3-e3dffe0c5983') def test_volume_delete_multiple(self): self.run_command('volume-delete Work Work2') self.assert_called('DELETE', '/volumes/15e59938-07d5-11e1-90e3-e3dffe0c5983', pos=-5) self.assert_called('DELETE', '/volumes/15e59938-07d5-11e1-90e3-ee32ba30feaa', pos=-1) def test_volume_attach(self): self.run_command('volume-attach sample-server Work /dev/vdb') self.assert_called('POST', '/servers/1234/os-volume_attachments', {'volumeAttachment': {'device': '/dev/vdb', 'volumeId': 'Work'}}) def test_volume_attach_without_device(self): self.run_command('volume-attach sample-server Work') self.assert_called('POST', '/servers/1234/os-volume_attachments', {'volumeAttachment': {'device': None, 'volumeId': 'Work'}}) def test_volume_update(self): self.run_command('volume-update sample-server Work Work') self.assert_called('PUT', '/servers/1234/os-volume_attachments/Work', {'volumeAttachment': {'volumeId': 'Work'}}) def test_volume_detach(self): self.run_command('volume-detach sample-server Work') self.assert_called('DELETE', '/servers/1234/os-volume_attachments/Work') def test_instance_action_list(self): self.run_command('instance-action-list sample-server') self.assert_called('GET', '/servers/1234/os-instance-actions') def test_instance_action_get(self): self.run_command('instance-action sample-server req-abcde12345') self.assert_called('GET', '/servers/1234/os-instance-actions/req-abcde12345') def test_cell_show(self): self.run_command('cell-show child_cell') self.assert_called('GET', '/os-cells/child_cell') def test_cell_capacities_with_cell_name(self): self.run_command('cell-capacities --cell child_cell') self.assert_called('GET', '/os-cells/child_cell/capacities') def test_cell_capacities_without_cell_name(self): self.run_command('cell-capacities') self.assert_called('GET', '/os-cells/capacities') def test_migration_list(self): self.run_command('migration-list') self.assert_called('GET', '/os-migrations') def test_migration_list_with_filters(self): self.run_command('migration-list --host host1 --cell_name child1 ' '--status finished') self.assert_called('GET', '/os-migrations?cell_name=child1&host=host1' '&status=finished') @mock.patch('novaclient.v1_1.shell._find_server') @mock.patch('os.system') def test_ssh(self, mock_system, mock_find_server): class FakeResources(object): addresses = { "private": [{'version': 4, 'addr': "1.1.1.1"}, {'version': 6, 'addr': "2607:f0d0:1002::4"}], "public": [{'version': 4, 'addr': "2.2.2.2"}, {'version': 6, 'addr': "7612:a1b2:2004::6"}] } mock_find_server.return_value = FakeResources() self.run_command("ssh --login bob server") mock_system.assert_called_with("ssh -4 -p22 bob@2.2.2.2 ") self.run_command("ssh alice@server") mock_system.assert_called_with("ssh -4 -p22 alice@2.2.2.2 ") self.run_command("ssh --port 202 server") mock_system.assert_called_with("ssh -4 -p202 root@2.2.2.2 ") self.run_command("ssh --private server") mock_system.assert_called_with("ssh -4 -p22 root@1.1.1.1 ") self.run_command("ssh -i ~/my_rsa_key server --private") mock_system.assert_called_with("ssh -4 -p22 -i ~/my_rsa_key " "root@1.1.1.1 ") self.run_command("ssh --extra-opts -1 server") mock_system.assert_called_with("ssh -4 -p22 root@2.2.2.2 -1") self.run_command("ssh --ipv6 --login carol server") mock_system.assert_called_with("ssh -6 -p22 carol@7612:a1b2:2004::6 ") self.run_command("ssh --ipv6 dan@server") mock_system.assert_called_with("ssh -6 -p22 dan@7612:a1b2:2004::6 ") self.run_command("ssh --ipv6 --port 2022 server") mock_system.assert_called_with("ssh -6 -p2022 " "root@7612:a1b2:2004::6 ") self.run_command("ssh --ipv6 --private server") mock_system.assert_called_with("ssh -6 -p22 root@2607:f0d0:1002::4 ") self.run_command("ssh --ipv6 --identity /home/me/my_dsa_key " "--private server") mock_system.assert_called_with("ssh -6 -p22 -i /home/me/my_dsa_key " "root@2607:f0d0:1002::4 ") self.run_command("ssh --ipv6 --private --extra-opts -1 server") mock_system.assert_called_with("ssh -6 -p22 " "root@2607:f0d0:1002::4 -1") class GetSecgroupTest(utils.TestCase): def test_with_integer(self): cs = mock.Mock(**{ 'security_groups.get.return_value': 'sec_group', 'security_groups.list.return_value': [], }) result = novaclient.v1_1.shell._get_secgroup(cs, '1') self.assertEqual(result, 'sec_group') cs.security_groups.get.assert_called_once_with('1') def test_with_uuid(self): cs = mock.Mock(**{ 'security_groups.get.return_value': 'sec_group', 'security_groups.list.return_value': [], }) result = novaclient.v1_1.shell._get_secgroup( cs, 'c0c32459-dc5f-44dc-9a0a-473b28bac831') self.assertEqual(result, 'sec_group') cs.security_groups.get.assert_called_once_with( 'c0c32459-dc5f-44dc-9a0a-473b28bac831') def test_with_an_nonexisting_name(self): cs = mock.Mock(**{ 'security_groups.get.return_value': 'sec_group', 'security_groups.list.return_value': [], }) self.assertRaises(exceptions.CommandError, novaclient.v1_1.shell._get_secgroup, cs, 'abc') python-novaclient-2.17.0/novaclient/tests/v1_1/test_fping.py0000664000175400017540000000404012306432770025210 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # 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 novaclient.tests import utils from novaclient.tests.v1_1 import fakes from novaclient.v1_1 import fping cs = fakes.FakeClient() class FpingTest(utils.TestCase): def test_fping_repr(self): r = cs.fping.get(1) self.assertEqual(repr(r), "") def test_list_fpings(self): fl = cs.fping.list() cs.assert_called('GET', '/os-fping') for f in fl: self.assertIsInstance(f, fping.Fping) self.assertEqual(f.project_id, "fake-project") self.assertEqual(f.alive, True) def test_list_fpings_all_tenants(self): fl = cs.fping.list(all_tenants=True) for f in fl: self.assertIsInstance(f, fping.Fping) cs.assert_called('GET', '/os-fping?all_tenants=1') def test_list_fpings_exclude(self): fl = cs.fping.list(exclude=['1']) for f in fl: self.assertIsInstance(f, fping.Fping) cs.assert_called('GET', '/os-fping?exclude=1') def test_list_fpings_include(self): fl = cs.fping.list(include=['1']) for f in fl: self.assertIsInstance(f, fping.Fping) cs.assert_called('GET', '/os-fping?include=1') def test_get_fping(self): f = cs.fping.get(1) cs.assert_called('GET', '/os-fping/1') self.assertIsInstance(f, fping.Fping) self.assertEqual(f.project_id, "fake-project") self.assertEqual(f.alive, True) python-novaclient-2.17.0/novaclient/tests/v1_1/test_networks.py0000664000175400017540000000673712306432770026000 0ustar jenkinsjenkins00000000000000# # 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 novaclient.tests import utils from novaclient.tests.v1_1 import fakes from novaclient.v1_1 import networks cs = fakes.FakeClient() class NetworksTest(utils.TestCase): def test_list_networks(self): fl = cs.networks.list() cs.assert_called('GET', '/os-networks') [self.assertIsInstance(f, networks.Network) for f in fl] def test_get_network(self): f = cs.networks.get(1) cs.assert_called('GET', '/os-networks/1') self.assertIsInstance(f, networks.Network) def test_delete(self): cs.networks.delete('networkdelete') cs.assert_called('DELETE', '/os-networks/networkdelete') def test_create(self): f = cs.networks.create(label='foo') cs.assert_called('POST', '/os-networks', {'network': {'label': 'foo'}}) self.assertIsInstance(f, networks.Network) def test_create_allparams(self): params = { 'label': 'bar', 'bridge': 'br0', 'bridge_interface': 'int0', 'cidr': '192.0.2.0/24', 'cidr_v6': '2001:DB8::/32', 'dns1': '1.1.1.1', 'dns2': '1.1.1.2', 'fixed_cidr': '198.51.100.0/24', 'gateway': '192.0.2.1', 'gateway_v6': '2001:DB8::1', 'multi_host': 'T', 'priority': '1', 'project_id': '1', 'vlan_start': 1, 'vpn_start': 1 } f = cs.networks.create(**params) cs.assert_called('POST', '/os-networks', {'network': params}) self.assertIsInstance(f, networks.Network) def test_associate_project(self): cs.networks.associate_project('networktest') cs.assert_called('POST', '/os-networks/add', {'id': 'networktest'}) def test_associate_host(self): cs.networks.associate_host('networktest', 'testHost') cs.assert_called('POST', '/os-networks/networktest/action', {'associate_host': 'testHost'}) def test_disassociate(self): cs.networks.disassociate('networkdisassociate') cs.assert_called('POST', '/os-networks/networkdisassociate/action', {'disassociate': None}) def test_disassociate_host_only(self): cs.networks.disassociate('networkdisassociate', True, False) cs.assert_called('POST', '/os-networks/networkdisassociate/action', {'disassociate_host': None}) def test_disassociate_project(self): cs.networks.disassociate('networkdisassociate', False, True) cs.assert_called('POST', '/os-networks/networkdisassociate/action', {'disassociate_project': None}) def test_add(self): cs.networks.add('networkadd') cs.assert_called('POST', '/os-networks/add', {'id': 'networkadd'}) python-novaclient-2.17.0/novaclient/tests/v1_1/test_quotas.py0000664000175400017540000000457212306432770025433 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # 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 novaclient.tests import utils from novaclient.tests.v1_1 import fakes class QuotaSetsTest(utils.TestCase): def setUp(self): super(QuotaSetsTest, self).setUp() self.cs = self._get_fake_client() def _get_fake_client(self): return fakes.FakeClient() def test_tenant_quotas_get(self): tenant_id = 'test' self.cs.quotas.get(tenant_id) self.cs.assert_called('GET', '/os-quota-sets/%s' % tenant_id) def test_user_quotas_get(self): tenant_id = 'test' user_id = 'fake_user' self.cs.quotas.get(tenant_id, user_id=user_id) url = '/os-quota-sets/%s?user_id=%s' % (tenant_id, user_id) self.cs.assert_called('GET', url) def test_tenant_quotas_defaults(self): tenant_id = '97f4c221bff44578b0300df4ef119353' self.cs.quotas.defaults(tenant_id) self.cs.assert_called('GET', '/os-quota-sets/%s/defaults' % tenant_id) def test_force_update_quota(self): q = self.cs.quotas.get('97f4c221bff44578b0300df4ef119353') q.update(cores=2, force=True) self.cs.assert_called( 'PUT', '/os-quota-sets/97f4c221bff44578b0300df4ef119353', {'quota_set': {'force': True, 'cores': 2, 'tenant_id': '97f4c221bff44578b0300df4ef119353'}}) def test_quotas_delete(self): tenant_id = 'test' self.cs.quotas.delete(tenant_id) self.cs.assert_called('DELETE', '/os-quota-sets/%s' % tenant_id) def test_user_quotas_delete(self): tenant_id = 'test' user_id = 'fake_user' self.cs.quotas.delete(tenant_id, user_id=user_id) url = '/os-quota-sets/%s?user_id=%s' % (tenant_id, user_id) self.cs.assert_called('DELETE', url) python-novaclient-2.17.0/novaclient/tests/v1_1/test_security_groups.py0000664000175400017540000000553312306432770027363 0ustar jenkinsjenkins00000000000000# # 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 novaclient.tests import utils from novaclient.tests.v1_1 import fakes from novaclient.v1_1 import security_groups cs = fakes.FakeClient() class SecurityGroupsTest(utils.TestCase): def _do_test_list_security_groups(self, search_opts, path): sgs = cs.security_groups.list(search_opts=search_opts) cs.assert_called('GET', path) for sg in sgs: self.assertIsInstance(sg, security_groups.SecurityGroup) def test_list_security_groups_all_tenants_on(self): self._do_test_list_security_groups( None, '/os-security-groups') def test_list_security_groups_all_tenants_on(self): self._do_test_list_security_groups( {'all_tenants': 1}, '/os-security-groups?all_tenants=1') def test_list_security_groups_all_tenants_off(self): self._do_test_list_security_groups( {'all_tenants': 0}, '/os-security-groups') def test_get_security_groups(self): sg = cs.security_groups.get(1) cs.assert_called('GET', '/os-security-groups/1') self.assertIsInstance(sg, security_groups.SecurityGroup) self.assertEqual('1', str(sg)) def test_delete_security_group(self): sg = cs.security_groups.list()[0] sg.delete() cs.assert_called('DELETE', '/os-security-groups/1') cs.security_groups.delete(1) cs.assert_called('DELETE', '/os-security-groups/1') cs.security_groups.delete(sg) cs.assert_called('DELETE', '/os-security-groups/1') def test_create_security_group(self): sg = cs.security_groups.create("foo", "foo barr") cs.assert_called('POST', '/os-security-groups') self.assertIsInstance(sg, security_groups.SecurityGroup) def test_update_security_group(self): sg = cs.security_groups.list()[0] secgroup = cs.security_groups.update(sg, "update", "update") cs.assert_called('PUT', '/os-security-groups/1') self.assertIsInstance(secgroup, security_groups.SecurityGroup) def test_refresh_security_group(self): sg = cs.security_groups.get(1) sg2 = cs.security_groups.get(1) self.assertEqual(sg.name, sg2.name) sg2.name = "should be test" self.assertNotEqual(sg.name, sg2.name) sg2.get() self.assertEqual(sg.name, sg2.name) python-novaclient-2.17.0/novaclient/tests/v1_1/testfile.txt0000664000175400017540000000000512306432770025051 0ustar jenkinsjenkins00000000000000BLAH python-novaclient-2.17.0/novaclient/tests/v1_1/test_availability_zone.py0000664000175400017540000000724712306432770027626 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # Copyright 2013 IBM Corp. # 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 six from novaclient.tests import utils from novaclient.tests.v1_1 import fakes from novaclient.v1_1 import availability_zones class AvailabilityZoneTest(utils.TestCase): # NOTE(cyeoh): import shell here so the V3 version of # this class can inherit off the v3 version of shell from novaclient.v1_1 import shell # noqa def setUp(self): super(AvailabilityZoneTest, self).setUp() self.cs = self._get_fake_client() self.availability_zone_type = self._get_availability_zone_type() def _get_fake_client(self): return fakes.FakeClient() def _get_availability_zone_type(self): return availability_zones.AvailabilityZone def _assertZone(self, zone, name, status): self.assertEqual(zone.zoneName, name) self.assertEqual(zone.zoneState, status) def test_list_availability_zone(self): zones = self.cs.availability_zones.list(detailed=False) self.cs.assert_called('GET', '/os-availability-zone') for zone in zones: self.assertIsInstance(zone, self.availability_zone_type) self.assertEqual(2, len(zones)) l0 = [six.u('zone-1'), six.u('available')] l1 = [six.u('zone-2'), six.u('not available')] z0 = self.shell._treeizeAvailabilityZone(zones[0]) z1 = self.shell._treeizeAvailabilityZone(zones[1]) self.assertEqual((len(z0), len(z1)), (1, 1)) self._assertZone(z0[0], l0[0], l0[1]) self._assertZone(z1[0], l1[0], l1[1]) def test_detail_availability_zone(self): zones = self.cs.availability_zones.list(detailed=True) self.cs.assert_called('GET', '/os-availability-zone/detail') for zone in zones: self.assertIsInstance(zone, self.availability_zone_type) self.assertEqual(3, len(zones)) l0 = [six.u('zone-1'), six.u('available')] l1 = [six.u('|- fake_host-1'), six.u('')] l2 = [six.u('| |- nova-compute'), six.u('enabled :-) 2012-12-26 14:45:25')] l3 = [six.u('internal'), six.u('available')] l4 = [six.u('|- fake_host-1'), six.u('')] l5 = [six.u('| |- nova-sched'), six.u('enabled :-) 2012-12-26 14:45:25')] l6 = [six.u('|- fake_host-2'), six.u('')] l7 = [six.u('| |- nova-network'), six.u('enabled XXX 2012-12-26 14:45:24')] l8 = [six.u('zone-2'), six.u('not available')] z0 = self.shell._treeizeAvailabilityZone(zones[0]) z1 = self.shell._treeizeAvailabilityZone(zones[1]) z2 = self.shell._treeizeAvailabilityZone(zones[2]) self.assertEqual((len(z0), len(z1), len(z2)), (3, 5, 1)) self._assertZone(z0[0], l0[0], l0[1]) self._assertZone(z0[1], l1[0], l1[1]) self._assertZone(z0[2], l2[0], l2[1]) self._assertZone(z1[0], l3[0], l3[1]) self._assertZone(z1[1], l4[0], l4[1]) self._assertZone(z1[2], l5[0], l5[1]) self._assertZone(z1[3], l6[0], l6[1]) self._assertZone(z1[4], l7[0], l7[1]) self._assertZone(z2[0], l8[0], l8[1]) python-novaclient-2.17.0/novaclient/tests/v1_1/test_fixed_ips.py0000664000175400017540000000277012306432770026067 0ustar jenkinsjenkins00000000000000# Copyright 2012 IBM Corp. # 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 novaclient.tests import utils from novaclient.tests.v1_1 import fakes cs = fakes.FakeClient() class FixedIpsTest(utils.TestCase): def test_get_fixed_ip(self): info = cs.fixed_ips.get(fixed_ip='192.168.1.1') cs.assert_called('GET', '/os-fixed-ips/192.168.1.1') self.assertEqual(info.cidr, '192.168.1.0/24') self.assertEqual(info.address, '192.168.1.1') self.assertEqual(info.hostname, 'foo') self.assertEqual(info.host, 'bar') def test_reserve_fixed_ip(self): body = {"reserve": None} res = cs.fixed_ips.reserve(fixed_ip='192.168.1.1') cs.assert_called('POST', '/os-fixed-ips/192.168.1.1/action', body) def test_unreserve_fixed_ip(self): body = {"unreserve": None} res = cs.fixed_ips.unreserve(fixed_ip='192.168.1.1') cs.assert_called('POST', '/os-fixed-ips/192.168.1.1/action', body) python-novaclient-2.17.0/novaclient/tests/v1_1/test_hypervisors.py0000664000175400017540000001336612306432770026515 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # 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 novaclient.tests import utils from novaclient.tests.v1_1 import fakes class HypervisorsTest(utils.TestCase): def setUp(self): super(HypervisorsTest, self).setUp() self.cs = self._get_fake_client() def _get_fake_client(self): return fakes.FakeClient() def compare_to_expected(self, expected, hyper): for key, value in expected.items(): self.assertEqual(getattr(hyper, key), value) def test_hypervisor_index(self): expected = [ dict(id=1234, hypervisor_hostname='hyper1'), dict(id=5678, hypervisor_hostname='hyper2'), ] result = self.cs.hypervisors.list(False) self.cs.assert_called('GET', '/os-hypervisors') for idx, hyper in enumerate(result): self.compare_to_expected(expected[idx], hyper) def test_hypervisor_detail(self): expected = [ dict(id=1234, service=dict(id=1, host='compute1'), vcpus=4, memory_mb=10 * 1024, local_gb=250, vcpus_used=2, memory_mb_used=5 * 1024, local_gb_used=125, hypervisor_type="xen", hypervisor_version=3, hypervisor_hostname="hyper1", free_ram_mb=5 * 1024, free_disk_gb=125, current_workload=2, running_vms=2, cpu_info='cpu_info', disk_available_least=100), dict(id=2, service=dict(id=2, host="compute2"), vcpus=4, memory_mb=10 * 1024, local_gb=250, vcpus_used=2, memory_mb_used=5 * 1024, local_gb_used=125, hypervisor_type="xen", hypervisor_version=3, hypervisor_hostname="hyper2", free_ram_mb=5 * 1024, free_disk_gb=125, current_workload=2, running_vms=2, cpu_info='cpu_info', disk_available_least=100)] result = self.cs.hypervisors.list() self.cs.assert_called('GET', '/os-hypervisors/detail') for idx, hyper in enumerate(result): self.compare_to_expected(expected[idx], hyper) def test_hypervisor_search(self): expected = [ dict(id=1234, hypervisor_hostname='hyper1'), dict(id=5678, hypervisor_hostname='hyper2'), ] result = self.cs.hypervisors.search('hyper') self.cs.assert_called('GET', '/os-hypervisors/hyper/search') for idx, hyper in enumerate(result): self.compare_to_expected(expected[idx], hyper) def test_hypervisor_servers(self): expected = [ dict(id=1234, hypervisor_hostname='hyper1', servers=[ dict(name='inst1', uuid='uuid1'), dict(name='inst2', uuid='uuid2')]), dict(id=5678, hypervisor_hostname='hyper2', servers=[ dict(name='inst3', uuid='uuid3'), dict(name='inst4', uuid='uuid4')]), ] result = self.cs.hypervisors.search('hyper', True) self.cs.assert_called('GET', '/os-hypervisors/hyper/servers') for idx, hyper in enumerate(result): self.compare_to_expected(expected[idx], hyper) def test_hypervisor_get(self): expected = dict( id=1234, service=dict(id=1, host='compute1'), vcpus=4, memory_mb=10 * 1024, local_gb=250, vcpus_used=2, memory_mb_used=5 * 1024, local_gb_used=125, hypervisor_type="xen", hypervisor_version=3, hypervisor_hostname="hyper1", free_ram_mb=5 * 1024, free_disk_gb=125, current_workload=2, running_vms=2, cpu_info='cpu_info', disk_available_least=100) result = self.cs.hypervisors.get(1234) self.cs.assert_called('GET', '/os-hypervisors/1234') self.compare_to_expected(expected, result) def test_hypervisor_uptime(self): expected = dict( id=1234, hypervisor_hostname="hyper1", uptime="fake uptime") result = self.cs.hypervisors.uptime(1234) self.cs.assert_called('GET', '/os-hypervisors/1234/uptime') self.compare_to_expected(expected, result) def test_hypervisor_statistics(self): expected = dict( count=2, vcpus=8, memory_mb=20 * 1024, local_gb=500, vcpus_used=4, memory_mb_used=10 * 1024, local_gb_used=250, free_ram_mb=10 * 1024, free_disk_gb=250, current_workload=4, running_vms=4, disk_available_least=200, ) result = self.cs.hypervisors.statistics() self.cs.assert_called('GET', '/os-hypervisors/statistics') self.compare_to_expected(expected, result) python-novaclient-2.17.0/novaclient/tests/v1_1/test_servers.py0000664000175400017540000005267412306432770025616 0ustar jenkinsjenkins00000000000000# -*- coding: utf-8 -*- # # 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 mock import six from novaclient import exceptions from novaclient.tests import utils from novaclient.tests.v1_1 import fakes from novaclient.v1_1 import servers cs = fakes.FakeClient() class ServersTest(utils.TestCase): def test_list_servers(self): sl = cs.servers.list() cs.assert_called('GET', '/servers/detail') [self.assertIsInstance(s, servers.Server) for s in sl] def test_list_servers_undetailed(self): sl = cs.servers.list(detailed=False) cs.assert_called('GET', '/servers') [self.assertIsInstance(s, servers.Server) for s in sl] def test_list_servers_with_marker_limit(self): sl = cs.servers.list(marker=1234, limit=2) cs.assert_called('GET', '/servers/detail?limit=2&marker=1234') for s in sl: self.assertIsInstance(s, servers.Server) def test_get_server_details(self): s = cs.servers.get(1234) cs.assert_called('GET', '/servers/1234') self.assertIsInstance(s, servers.Server) self.assertEqual(s.id, 1234) self.assertEqual(s.status, 'BUILD') def test_get_server_promote_details(self): s1 = cs.servers.list(detailed=False)[0] s2 = cs.servers.list(detailed=True)[0] self.assertNotEqual(s1._info, s2._info) s1.get() self.assertEqual(s1._info, s2._info) def test_create_server(self): s = cs.servers.create( name="My server", image=1, flavor=1, meta={'foo': 'bar'}, userdata="hello moto", key_name="fakekey", files={ '/etc/passwd': 'some data', # a file '/tmp/foo.txt': six.StringIO('data'), # a stream } ) cs.assert_called('POST', '/servers') self.assertIsInstance(s, servers.Server) def test_create_server_boot_from_volume_with_nics(self): old_boot = cs.servers._boot nics = [{'net-id': '11111111-1111-1111-1111-111111111111', 'v4-fixed-ip': '10.0.0.7'}] bdm = {"volume_size": "1", "volume_id": "11111111-1111-1111-1111-111111111111", "delete_on_termination": "0", "device_name": "vda"} def wrapped_boot(url, key, *boot_args, **boot_kwargs): self.assertEqual(boot_kwargs['block_device_mapping'], bdm) self.assertEqual(boot_kwargs['nics'], nics) return old_boot(url, key, *boot_args, **boot_kwargs) @mock.patch.object(cs.servers, '_boot', wrapped_boot) def test_create_server_from_volume(): s = cs.servers.create( name="My server", image=1, flavor=1, meta={'foo': 'bar'}, userdata="hello moto", key_name="fakekey", block_device_mapping=bdm, nics=nics ) cs.assert_called('POST', '/os-volumes_boot') self.assertIsInstance(s, servers.Server) test_create_server_from_volume() def test_create_server_userdata_file_object(self): s = cs.servers.create( name="My server", image=1, flavor=1, meta={'foo': 'bar'}, userdata=six.StringIO('hello moto'), files={ '/etc/passwd': 'some data', # a file '/tmp/foo.txt': six.StringIO('data'), # a stream }, ) cs.assert_called('POST', '/servers') self.assertIsInstance(s, servers.Server) def test_create_server_userdata_unicode(self): s = cs.servers.create( name="My server", image=1, flavor=1, meta={'foo': 'bar'}, userdata=six.u('こんにちは'), key_name="fakekey", files={ '/etc/passwd': 'some data', # a file '/tmp/foo.txt': six.StringIO('data'), # a stream }, ) cs.assert_called('POST', '/servers') self.assertIsInstance(s, servers.Server) def test_create_server_userdata_utf8(self): s = cs.servers.create( name="My server", image=1, flavor=1, meta={'foo': 'bar'}, userdata='こんにちは', key_name="fakekey", files={ '/etc/passwd': 'some data', # a file '/tmp/foo.txt': six.StringIO('data'), # a stream }, ) cs.assert_called('POST', '/servers') self.assertIsInstance(s, servers.Server) def _create_disk_config(self, disk_config): s = cs.servers.create( name="My server", image=1, flavor=1, disk_config=disk_config ) cs.assert_called('POST', '/servers') self.assertIsInstance(s, servers.Server) # verify disk config param was used in the request: last_request = cs.client.callstack[-1] body = last_request[-1] server = body['server'] self.assertTrue('OS-DCF:diskConfig' in server) self.assertEqual(disk_config, server['OS-DCF:diskConfig']) def test_create_server_disk_config_auto(self): self._create_disk_config('AUTO') def test_create_server_disk_config_manual(self): self._create_disk_config('MANUAL') def test_update_server(self): s = cs.servers.get(1234) # Update via instance s.update(name='hi') cs.assert_called('PUT', '/servers/1234') s.update(name='hi') cs.assert_called('PUT', '/servers/1234') # Silly, but not an error s.update() # Update via manager cs.servers.update(s, name='hi') cs.assert_called('PUT', '/servers/1234') def test_delete_server(self): s = cs.servers.get(1234) s.delete() cs.assert_called('DELETE', '/servers/1234') cs.servers.delete(1234) cs.assert_called('DELETE', '/servers/1234') cs.servers.delete(s) cs.assert_called('DELETE', '/servers/1234') def test_delete_server_meta(self): s = cs.servers.delete_meta(1234, ['test_key']) cs.assert_called('DELETE', '/servers/1234/metadata/test_key') def test_set_server_meta(self): s = cs.servers.set_meta(1234, {'test_key': 'test_value'}) reval = cs.assert_called('POST', '/servers/1234/metadata', {'metadata': {'test_key': 'test_value'}}) def test_set_server_meta_item(self): s = cs.servers.set_meta_item(1234, 'test_key', 'test_value') reval = cs.assert_called('PUT', '/servers/1234/metadata/test_key', {'meta': {'test_key': 'test_value'}}) def test_find(self): server = cs.servers.find(name='sample-server') cs.assert_called('GET', '/servers', pos=-2) cs.assert_called('GET', '/servers/1234', pos=-1) self.assertEqual(server.name, 'sample-server') self.assertRaises(exceptions.NoUniqueMatch, cs.servers.find, flavor={"id": 1, "name": "256 MB Server"}) sl = cs.servers.findall(flavor={"id": 1, "name": "256 MB Server"}) self.assertEqual([s.id for s in sl], [1234, 5678, 9012]) def test_reboot_server(self): s = cs.servers.get(1234) s.reboot() cs.assert_called('POST', '/servers/1234/action') cs.servers.reboot(s, reboot_type='HARD') cs.assert_called('POST', '/servers/1234/action') def test_rebuild_server(self): s = cs.servers.get(1234) s.rebuild(image=1) cs.assert_called('POST', '/servers/1234/action') cs.servers.rebuild(s, image=1) cs.assert_called('POST', '/servers/1234/action') s.rebuild(image=1, password='5678') cs.assert_called('POST', '/servers/1234/action') cs.servers.rebuild(s, image=1, password='5678') cs.assert_called('POST', '/servers/1234/action') def _rebuild_resize_disk_config(self, disk_config, operation="rebuild"): s = cs.servers.get(1234) if operation == "rebuild": s.rebuild(image=1, disk_config=disk_config) elif operation == "resize": s.resize(flavor=1, disk_config=disk_config) cs.assert_called('POST', '/servers/1234/action') # verify disk config param was used in the request: last_request = cs.client.callstack[-1] body = last_request[-1] d = body[operation] self.assertTrue('OS-DCF:diskConfig' in d) self.assertEqual(disk_config, d['OS-DCF:diskConfig']) def test_rebuild_server_disk_config_auto(self): self._rebuild_resize_disk_config('AUTO') def test_rebuild_server_disk_config_manual(self): self._rebuild_resize_disk_config('MANUAL') def test_rebuild_server_preserve_ephemeral(self): s = cs.servers.get(1234) s.rebuild(image=1, preserve_ephemeral=True) cs.assert_called('POST', '/servers/1234/action') body = cs.client.callstack[-1][-1] d = body['rebuild'] self.assertIn('preserve_ephemeral', d) self.assertEqual(d['preserve_ephemeral'], True) def test_resize_server(self): s = cs.servers.get(1234) s.resize(flavor=1) cs.assert_called('POST', '/servers/1234/action') cs.servers.resize(s, flavor=1) cs.assert_called('POST', '/servers/1234/action') def test_resize_server_disk_config_auto(self): self._rebuild_resize_disk_config('AUTO', 'resize') def test_resize_server_disk_config_manual(self): self._rebuild_resize_disk_config('MANUAL', 'resize') def test_confirm_resized_server(self): s = cs.servers.get(1234) s.confirm_resize() cs.assert_called('POST', '/servers/1234/action') cs.servers.confirm_resize(s) cs.assert_called('POST', '/servers/1234/action') def test_revert_resized_server(self): s = cs.servers.get(1234) s.revert_resize() cs.assert_called('POST', '/servers/1234/action') cs.servers.revert_resize(s) cs.assert_called('POST', '/servers/1234/action') def test_migrate_server(self): s = cs.servers.get(1234) s.migrate() cs.assert_called('POST', '/servers/1234/action') cs.servers.migrate(s) cs.assert_called('POST', '/servers/1234/action') def test_add_fixed_ip(self): s = cs.servers.get(1234) s.add_fixed_ip(1) cs.assert_called('POST', '/servers/1234/action') cs.servers.add_fixed_ip(s, 1) cs.assert_called('POST', '/servers/1234/action') def test_remove_fixed_ip(self): s = cs.servers.get(1234) s.remove_fixed_ip('10.0.0.1') cs.assert_called('POST', '/servers/1234/action') cs.servers.remove_fixed_ip(s, '10.0.0.1') cs.assert_called('POST', '/servers/1234/action') def test_add_floating_ip(self): s = cs.servers.get(1234) s.add_floating_ip('11.0.0.1') cs.assert_called('POST', '/servers/1234/action') cs.servers.add_floating_ip(s, '11.0.0.1') cs.assert_called('POST', '/servers/1234/action') f = cs.floating_ips.list()[0] cs.servers.add_floating_ip(s, f) cs.assert_called('POST', '/servers/1234/action') s.add_floating_ip(f) cs.assert_called('POST', '/servers/1234/action') def test_add_floating_ip_to_fixed(self): s = cs.servers.get(1234) s.add_floating_ip('11.0.0.1', fixed_address='12.0.0.1') cs.assert_called('POST', '/servers/1234/action') cs.servers.add_floating_ip(s, '11.0.0.1', fixed_address='12.0.0.1') cs.assert_called('POST', '/servers/1234/action') f = cs.floating_ips.list()[0] cs.servers.add_floating_ip(s, f) cs.assert_called('POST', '/servers/1234/action') s.add_floating_ip(f) cs.assert_called('POST', '/servers/1234/action') def test_remove_floating_ip(self): s = cs.servers.get(1234) s.remove_floating_ip('11.0.0.1') cs.assert_called('POST', '/servers/1234/action') cs.servers.remove_floating_ip(s, '11.0.0.1') cs.assert_called('POST', '/servers/1234/action') f = cs.floating_ips.list()[0] cs.servers.remove_floating_ip(s, f) cs.assert_called('POST', '/servers/1234/action') s.remove_floating_ip(f) cs.assert_called('POST', '/servers/1234/action') def test_stop(self): s = cs.servers.get(1234) s.stop() cs.assert_called('POST', '/servers/1234/action') cs.servers.stop(s) cs.assert_called('POST', '/servers/1234/action') def test_force_delete(self): s = cs.servers.get(1234) s.force_delete() cs.assert_called('POST', '/servers/1234/action') cs.servers.force_delete(s) cs.assert_called('POST', '/servers/1234/action') def test_restore(self): s = cs.servers.get(1234) s.restore() cs.assert_called('POST', '/servers/1234/action') cs.servers.restore(s) cs.assert_called('POST', '/servers/1234/action') def test_start(self): s = cs.servers.get(1234) s.start() cs.assert_called('POST', '/servers/1234/action') cs.servers.start(s) cs.assert_called('POST', '/servers/1234/action') def test_rescue(self): s = cs.servers.get(1234) s.rescue() cs.assert_called('POST', '/servers/1234/action') cs.servers.rescue(s) cs.assert_called('POST', '/servers/1234/action') def test_unrescue(self): s = cs.servers.get(1234) s.unrescue() cs.assert_called('POST', '/servers/1234/action') cs.servers.unrescue(s) cs.assert_called('POST', '/servers/1234/action') def test_lock(self): s = cs.servers.get(1234) s.lock() cs.assert_called('POST', '/servers/1234/action') cs.servers.lock(s) cs.assert_called('POST', '/servers/1234/action') def test_unlock(self): s = cs.servers.get(1234) s.unlock() cs.assert_called('POST', '/servers/1234/action') cs.servers.unlock(s) cs.assert_called('POST', '/servers/1234/action') def test_backup(self): s = cs.servers.get(1234) s.backup('back1', 'daily', 1) cs.assert_called('POST', '/servers/1234/action') cs.servers.backup(s, 'back1', 'daily', 2) cs.assert_called('POST', '/servers/1234/action') def test_get_console_output_without_length(self): success = 'foo' s = cs.servers.get(1234) s.get_console_output() self.assertEqual(s.get_console_output(), success) cs.assert_called('POST', '/servers/1234/action') cs.servers.get_console_output(s) self.assertEqual(cs.servers.get_console_output(s), success) cs.assert_called('POST', '/servers/1234/action') def test_get_console_output_with_length(self): success = 'foo' s = cs.servers.get(1234) s.get_console_output(length=50) self.assertEqual(s.get_console_output(length=50), success) cs.assert_called('POST', '/servers/1234/action') cs.servers.get_console_output(s, length=50) self.assertEqual(cs.servers.get_console_output(s, length=50), success) cs.assert_called('POST', '/servers/1234/action') # Testing password methods with the following password and key # # Clear password: FooBar123 # # RSA Private Key: novaclient/tests/idfake.pem # # Encrypted password # OIuEuQttO8Rk93BcKlwHQsziDAnkAm/V6V8VPToA8ZeUaUBWwS0gwo2K6Y61Z96r # qG447iRz0uTEEYq3RAYJk1mh3mMIRVl27t8MtIecR5ggVVbz1S9AwXJQypDKl0ho # QFvhCBcMWPohyGewDJOhDbtuN1IoFI9G55ZvFwCm5y7m7B2aVcoLeIsJZE4PLsIw # /y5a6Z3/AoJZYGG7IH5WN88UROU3B9JZGFB2qtPLQTOvDMZLUhoPRIJeHiVSlo1N # tI2/++UsXVg3ow6ItqCJGgdNuGG5JB+bslDHWPxROpesEIHdczk46HCpHQN8f1sk # Hi/fmZZNQQqj1Ijq0caOIw== def test_get_password(self): s = cs.servers.get(1234) self.assertEqual(s.get_password('novaclient/tests/idfake.pem'), b'FooBar123') cs.assert_called('GET', '/servers/1234/os-server-password') def test_get_password_without_key(self): s = cs.servers.get(1234) self.assertEqual(s.get_password(), 'OIuEuQttO8Rk93BcKlwHQsziDAnkAm/V6V8VPToA8ZeUaUBWwS0gwo2K6Y61Z96r' 'qG447iRz0uTEEYq3RAYJk1mh3mMIRVl27t8MtIecR5ggVVbz1S9AwXJQypDKl0ho' 'QFvhCBcMWPohyGewDJOhDbtuN1IoFI9G55ZvFwCm5y7m7B2aVcoLeIsJZE4PLsIw' '/y5a6Z3/AoJZYGG7IH5WN88UROU3B9JZGFB2qtPLQTOvDMZLUhoPRIJeHiVSlo1N' 'tI2/++UsXVg3ow6ItqCJGgdNuGG5JB+bslDHWPxROpesEIHdczk46HCpHQN8f1sk' 'Hi/fmZZNQQqj1Ijq0caOIw==') cs.assert_called('GET', '/servers/1234/os-server-password') def test_clear_password(self): s = cs.servers.get(1234) s.clear_password() cs.assert_called('DELETE', '/servers/1234/os-server-password') def test_get_server_diagnostics(self): s = cs.servers.get(1234) diagnostics = s.diagnostics() self.assertTrue(diagnostics is not None) cs.assert_called('GET', '/servers/1234/diagnostics') diagnostics_from_manager = cs.servers.diagnostics(1234) self.assertTrue(diagnostics_from_manager is not None) cs.assert_called('GET', '/servers/1234/diagnostics') self.assertEqual(diagnostics, diagnostics_from_manager) def test_get_vnc_console(self): s = cs.servers.get(1234) s.get_vnc_console('fake') cs.assert_called('POST', '/servers/1234/action') cs.servers.get_vnc_console(s, 'fake') cs.assert_called('POST', '/servers/1234/action') def test_get_spice_console(self): s = cs.servers.get(1234) s.get_spice_console('fake') cs.assert_called('POST', '/servers/1234/action') cs.servers.get_spice_console(s, 'fake') cs.assert_called('POST', '/servers/1234/action') def test_get_rdp_console(self): s = cs.servers.get(1234) s.get_rdp_console('fake') cs.assert_called('POST', '/servers/1234/action') cs.servers.get_rdp_console(s, 'fake') cs.assert_called('POST', '/servers/1234/action') def test_create_image(self): s = cs.servers.get(1234) s.create_image('123') cs.assert_called('POST', '/servers/1234/action') s.create_image('123', {}) cs.assert_called('POST', '/servers/1234/action') cs.servers.create_image(s, '123') cs.assert_called('POST', '/servers/1234/action') cs.servers.create_image(s, '123', {}) def test_live_migrate_server(self): s = cs.servers.get(1234) s.live_migrate(host='hostname', block_migration=False, disk_over_commit=False) cs.assert_called('POST', '/servers/1234/action') cs.servers.live_migrate(s, host='hostname', block_migration=False, disk_over_commit=False) cs.assert_called('POST', '/servers/1234/action') def test_reset_state(self): s = cs.servers.get(1234) s.reset_state('newstate') cs.assert_called('POST', '/servers/1234/action') cs.servers.reset_state(s, 'newstate') cs.assert_called('POST', '/servers/1234/action') def test_reset_network(self): s = cs.servers.get(1234) s.reset_network() cs.assert_called('POST', '/servers/1234/action') cs.servers.reset_network(s) cs.assert_called('POST', '/servers/1234/action') def test_add_security_group(self): s = cs.servers.get(1234) s.add_security_group('newsg') cs.assert_called('POST', '/servers/1234/action') cs.servers.add_security_group(s, 'newsg') cs.assert_called('POST', '/servers/1234/action') def test_remove_security_group(self): s = cs.servers.get(1234) s.remove_security_group('oldsg') cs.assert_called('POST', '/servers/1234/action') cs.servers.remove_security_group(s, 'oldsg') cs.assert_called('POST', '/servers/1234/action') def test_list_security_group(self): s = cs.servers.get(1234) s.list_security_group() cs.assert_called('GET', '/servers/1234/os-security-groups') def test_evacuate(self): s = cs.servers.get(1234) s.evacuate('fake_target_host', 'True') cs.assert_called('POST', '/servers/1234/action') cs.servers.evacuate(s, 'fake_target_host', 'False', 'NewAdminPassword') cs.assert_called('POST', '/servers/1234/action') def test_interface_list(self): s = cs.servers.get(1234) s.interface_list() cs.assert_called('GET', '/servers/1234/os-interface') def test_interface_attach(self): s = cs.servers.get(1234) s.interface_attach(None, None, None) cs.assert_called('POST', '/servers/1234/os-interface') def test_interface_detach(self): s = cs.servers.get(1234) s.interface_detach('port-id') cs.assert_called('DELETE', '/servers/1234/os-interface/port-id') python-novaclient-2.17.0/novaclient/tests/v1_1/test_volumes.py0000664000175400017540000000656212306432770025612 0ustar jenkinsjenkins00000000000000# Copyright 2013 IBM Corp. # 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 novaclient.tests import utils from novaclient.tests.v1_1 import fakes from novaclient.v1_1 import volumes cs = fakes.FakeClient() class VolumesTest(utils.TestCase): def test_list_servers(self): vl = cs.volumes.list() cs.assert_called('GET', '/volumes/detail') [self.assertIsInstance(v, volumes.Volume) for v in vl] def test_list_volumes_undetailed(self): vl = cs.volumes.list(detailed=False) cs.assert_called('GET', '/volumes') [self.assertIsInstance(v, volumes.Volume) for v in vl] def test_get_volume_details(self): vol_id = '15e59938-07d5-11e1-90e3-e3dffe0c5983' v = cs.volumes.get(vol_id) cs.assert_called('GET', '/volumes/%s' % vol_id) self.assertIsInstance(v, volumes.Volume) self.assertEqual(v.id, vol_id) def test_create_volume(self): v = cs.volumes.create( size=2, display_name="My volume", display_description="My volume desc", ) cs.assert_called('POST', '/volumes') self.assertIsInstance(v, volumes.Volume) def test_delete_volume(self): vol_id = '15e59938-07d5-11e1-90e3-e3dffe0c5983' v = cs.volumes.get(vol_id) v.delete() cs.assert_called('DELETE', '/volumes/%s' % vol_id) cs.volumes.delete(vol_id) cs.assert_called('DELETE', '/volumes/%s' % vol_id) cs.volumes.delete(v) cs.assert_called('DELETE', '/volumes/%s' % vol_id) def test_create_server_volume(self): v = cs.volumes.create_server_volume( server_id=1234, volume_id='15e59938-07d5-11e1-90e3-e3dffe0c5983', device='/dev/vdb' ) cs.assert_called('POST', '/servers/1234/os-volume_attachments') self.assertIsInstance(v, volumes.Volume) def test_update_server_volume(self): vol_id = '15e59938-07d5-11e1-90e3-e3dffe0c5983' v = cs.volumes.update_server_volume( server_id=1234, attachment_id='Work', new_volume_id=vol_id ) cs.assert_called('PUT', '/servers/1234/os-volume_attachments/Work') self.assertIsInstance(v, volumes.Volume) def test_get_server_volume(self): v = cs.volumes.get_server_volume(1234, 'Work') cs.assert_called('GET', '/servers/1234/os-volume_attachments/Work') self.assertIsInstance(v, volumes.Volume) def test_list_server_volumes(self): vl = cs.volumes.get_server_volumes(1234) cs.assert_called('GET', '/servers/1234/os-volume_attachments') [self.assertIsInstance(v, volumes.Volume) for v in vl] def test_delete_server_volume(self): cs.volumes.delete_server_volume(1234, 'Work') cs.assert_called('DELETE', '/servers/1234/os-volume_attachments/Work') python-novaclient-2.17.0/novaclient/tests/v1_1/test_floating_ip_dns.py0000664000175400017540000000620312306432770027247 0ustar jenkinsjenkins00000000000000# # 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 novaclient.tests import utils from novaclient.tests.v1_1 import fakes from novaclient.v1_1 import floating_ip_dns cs = fakes.FakeClient() class FloatingIPDNSDomainTest(utils.TestCase): testdomain = "testdomain" def test_dns_domains(self): domainlist = cs.dns_domains.domains() self.assertEqual(len(domainlist), 2) for entry in domainlist: self.assertIsInstance(entry, floating_ip_dns.FloatingIPDNSDomain) self.assertEqual(domainlist[1].domain, 'example.com') def test_create_private_domain(self): cs.dns_domains.create_private(self.testdomain, 'test_avzone') cs.assert_called('PUT', '/os-floating-ip-dns/%s' % self.testdomain) def test_create_public_domain(self): cs.dns_domains.create_public(self.testdomain, 'test_project') cs.assert_called('PUT', '/os-floating-ip-dns/%s' % self.testdomain) def test_delete_domain(self): cs.dns_domains.delete(self.testdomain) cs.assert_called('DELETE', '/os-floating-ip-dns/%s' % self.testdomain) class FloatingIPDNSEntryTest(utils.TestCase): testname = "testname" testip = "1.2.3.4" testdomain = "testdomain" testtype = "A" def test_get_dns_entries_by_ip(self): entries = cs.dns_entries.get_for_ip(self.testdomain, ip=self.testip) self.assertEqual(len(entries), 2) for entry in entries: self.assertIsInstance(entry, floating_ip_dns.FloatingIPDNSEntry) self.assertEqual(entries[1].dns_entry['name'], 'host2') self.assertEqual(entries[1].dns_entry['ip'], self.testip) def test_get_dns_entry_by_name(self): entry = cs.dns_entries.get(self.testdomain, self.testname) self.assertIsInstance(entry, floating_ip_dns.FloatingIPDNSEntry) self.assertEqual(entry.name, self.testname) def test_create_entry(self): cs.dns_entries.create(self.testdomain, self.testname, self.testip, self.testtype) cs.assert_called('PUT', '/os-floating-ip-dns/%s/entries/%s' % (self.testdomain, self.testname)) def test_delete_entry(self): cs.dns_entries.delete(self.testdomain, self.testname) cs.assert_called('DELETE', '/os-floating-ip-dns/%s/entries/%s' % (self.testdomain, self.testname)) python-novaclient-2.17.0/novaclient/tests/v1_1/test_hosts.py0000664000175400017540000000542612306432770025256 0ustar jenkinsjenkins00000000000000# # 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 novaclient.tests import utils from novaclient.tests.v1_1 import fakes from novaclient.v1_1 import hosts cs = fakes.FakeClient() class HostsTest(utils.TestCase): def test_describe_resource(self): hs = cs.hosts.get('host') cs.assert_called('GET', '/os-hosts/host') [self.assertIsInstance(h, hosts.Host) for h in hs] def test_list_host(self): hs = cs.hosts.list() cs.assert_called('GET', '/os-hosts') [self.assertIsInstance(h, hosts.Host) for h in hs] [self.assertEqual(h.zone, 'nova1') for h in hs] def test_list_host_with_zone(self): hs = cs.hosts.list('nova') cs.assert_called('GET', '/os-hosts?zone=nova') [self.assertIsInstance(h, hosts.Host) for h in hs] [self.assertEqual(h.zone, 'nova') for h in hs] def test_update_enable(self): host = cs.hosts.get('sample_host')[0] values = {"status": "enabled"} result = host.update(values) cs.assert_called('PUT', '/os-hosts/sample_host', values) self.assertIsInstance(result, hosts.Host) def test_update_maintenance(self): host = cs.hosts.get('sample_host')[0] values = {"maintenance_mode": "enable"} result = host.update(values) cs.assert_called('PUT', '/os-hosts/sample_host', values) self.assertIsInstance(result, hosts.Host) def test_update_both(self): host = cs.hosts.get('sample_host')[0] values = {"status": "enabled", "maintenance_mode": "enable"} result = host.update(values) cs.assert_called('PUT', '/os-hosts/sample_host', values) self.assertIsInstance(result, hosts.Host) def test_host_startup(self): host = cs.hosts.get('sample_host')[0] result = host.startup() cs.assert_called( 'GET', '/os-hosts/sample_host/startup') def test_host_reboot(self): host = cs.hosts.get('sample_host')[0] result = host.reboot() cs.assert_called( 'GET', '/os-hosts/sample_host/reboot') def test_host_shutdown(self): host = cs.hosts.get('sample_host')[0] result = host.shutdown() cs.assert_called( 'GET', '/os-hosts/sample_host/shutdown') python-novaclient-2.17.0/novaclient/tests/v1_1/test_flavor_access.py0000664000175400017540000000360112306432770026721 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # 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 novaclient.tests import utils from novaclient.tests.v1_1 import fakes from novaclient.v1_1 import flavor_access cs = fakes.FakeClient() class FlavorAccessTest(utils.TestCase): def test_list_access_by_flavor_private(self): kwargs = {'flavor': cs.flavors.get(2)} r = cs.flavor_access.list(**kwargs) cs.assert_called('GET', '/flavors/2/os-flavor-access') [self.assertIsInstance(a, flavor_access.FlavorAccess) for a in r] def test_add_tenant_access(self): flavor = cs.flavors.get(2) tenant = 'proj2' r = cs.flavor_access.add_tenant_access(flavor, tenant) body = { "addTenantAccess": { "tenant": "proj2" } } cs.assert_called('POST', '/flavors/2/action', body) [self.assertIsInstance(a, flavor_access.FlavorAccess) for a in r] def test_remove_tenant_access(self): flavor = cs.flavors.get(2) tenant = 'proj2' r = cs.flavor_access.remove_tenant_access(flavor, tenant) body = { "removeTenantAccess": { "tenant": "proj2" } } cs.assert_called('POST', '/flavors/2/action', body) [self.assertIsInstance(a, flavor_access.FlavorAccess) for a in r] python-novaclient-2.17.0/novaclient/tests/v1_1/test_limits.py0000664000175400017540000000577312306432770025424 0ustar jenkinsjenkins00000000000000# # 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 novaclient.tests import utils from novaclient.tests.v1_1 import fakes from novaclient.v1_1 import limits cs = fakes.FakeClient() class LimitsTest(utils.TestCase): def test_get_limits(self): obj = cs.limits.get() cs.assert_called('GET', '/limits') self.assertIsInstance(obj, limits.Limits) def test_get_limits_for_a_tenant(self): obj = cs.limits.get(tenant_id=1234) cs.assert_called('GET', '/limits?tenant_id=1234') self.assertIsInstance(obj, limits.Limits) def test_absolute_limits(self): obj = cs.limits.get() expected = ( limits.AbsoluteLimit("maxTotalRAMSize", 51200), limits.AbsoluteLimit("maxServerMeta", 5), limits.AbsoluteLimit("maxImageMeta", 5), limits.AbsoluteLimit("maxPersonality", 5), limits.AbsoluteLimit("maxPersonalitySize", 10240), ) abs_limits = list(obj.absolute) self.assertEqual(len(abs_limits), len(expected)) for limit in abs_limits: self.assertTrue(limit in expected) def test_absolute_limits_reserved(self): obj = cs.limits.get(reserved=True) expected = ( limits.AbsoluteLimit("maxTotalRAMSize", 51200), limits.AbsoluteLimit("maxServerMeta", 5), limits.AbsoluteLimit("maxImageMeta", 5), limits.AbsoluteLimit("maxPersonality", 5), limits.AbsoluteLimit("maxPersonalitySize", 10240), ) cs.assert_called('GET', '/limits?reserved=1') abs_limits = list(obj.absolute) self.assertEqual(len(abs_limits), len(expected)) for limit in abs_limits: self.assertTrue(limit in expected) def test_rate_limits(self): obj = cs.limits.get() expected = ( limits.RateLimit('POST', '*', '.*', 10, 2, 'MINUTE', '2011-12-15T22:42:45Z'), limits.RateLimit('PUT', '*', '.*', 10, 2, 'MINUTE', '2011-12-15T22:42:45Z'), limits.RateLimit('DELETE', '*', '.*', 100, 100, 'MINUTE', '2011-12-15T22:42:45Z'), limits.RateLimit('POST', '*/servers', '^/servers', 25, 24, 'DAY', '2011-12-15T22:42:45Z'), ) rate_limits = list(obj.rate) self.assertEqual(len(rate_limits), len(expected)) for limit in rate_limits: self.assertTrue(limit in expected) python-novaclient-2.17.0/novaclient/tests/v1_1/test_aggregates.py0000664000175400017540000001333212306432770026222 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # 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 novaclient.tests import utils from novaclient.tests.v1_1 import fakes from novaclient.v1_1 import aggregates class AggregatesTest(utils.TestCase): def setUp(self): super(AggregatesTest, self).setUp() self.cs = self._get_fake_client() self.aggregate_type = self._get_aggregate_type() def _get_fake_client(self): return fakes.FakeClient() def _get_aggregate_type(self): return aggregates.Aggregate def test_list_aggregates(self): result = self.cs.aggregates.list() self.cs.assert_called('GET', '/os-aggregates') for aggregate in result: self.assertIsInstance(aggregate, aggregates.Aggregate) def test_create_aggregate(self): body = {"aggregate": {"name": "test", "availability_zone": "nova1"}} aggregate = self.cs.aggregates.create("test", "nova1") self.cs.assert_called('POST', '/os-aggregates', body) self.assertIsInstance(aggregate, aggregates.Aggregate) def test_get(self): aggregate = self.cs.aggregates.get("1") self.cs.assert_called('GET', '/os-aggregates/1') self.assertIsInstance(aggregate, aggregates.Aggregate) aggregate2 = self.cs.aggregates.get(aggregate) self.cs.assert_called('GET', '/os-aggregates/1') self.assertIsInstance(aggregate2, aggregates.Aggregate) def test_get_details(self): aggregate = self.cs.aggregates.get_details("1") self.cs.assert_called('GET', '/os-aggregates/1') self.assertIsInstance(aggregate, aggregates.Aggregate) aggregate2 = self.cs.aggregates.get_details(aggregate) self.cs.assert_called('GET', '/os-aggregates/1') self.assertIsInstance(aggregate2, aggregates.Aggregate) def test_update(self): aggregate = self.cs.aggregates.get("1") values = {"name": "foo"} body = {"aggregate": values} result1 = aggregate.update(values) self.cs.assert_called('PUT', '/os-aggregates/1', body) self.assertIsInstance(result1, aggregates.Aggregate) result2 = self.cs.aggregates.update(2, values) self.cs.assert_called('PUT', '/os-aggregates/2', body) self.assertIsInstance(result2, aggregates.Aggregate) def test_update_with_availability_zone(self): aggregate = self.cs.aggregates.get("1") values = {"name": "foo", "availability_zone": "new_zone"} body = {"aggregate": values} result3 = self.cs.aggregates.update(aggregate, values) self.cs.assert_called('PUT', '/os-aggregates/1', body) self.assertIsInstance(result3, aggregates.Aggregate) def test_add_host(self): aggregate = self.cs.aggregates.get("1") host = "host1" body = {"add_host": {"host": "host1"}} result1 = aggregate.add_host(host) self.cs.assert_called('POST', '/os-aggregates/1/action', body) self.assertIsInstance(result1, aggregates.Aggregate) result2 = self.cs.aggregates.add_host("2", host) self.cs.assert_called('POST', '/os-aggregates/2/action', body) self.assertIsInstance(result2, aggregates.Aggregate) result3 = self.cs.aggregates.add_host(aggregate, host) self.cs.assert_called('POST', '/os-aggregates/1/action', body) self.assertIsInstance(result3, aggregates.Aggregate) def test_remove_host(self): aggregate = self.cs.aggregates.get("1") host = "host1" body = {"remove_host": {"host": "host1"}} result1 = aggregate.remove_host(host) self.cs.assert_called('POST', '/os-aggregates/1/action', body) self.assertIsInstance(result1, aggregates.Aggregate) result2 = self.cs.aggregates.remove_host("2", host) self.cs.assert_called('POST', '/os-aggregates/2/action', body) self.assertIsInstance(result2, aggregates.Aggregate) result3 = self.cs.aggregates.remove_host(aggregate, host) self.cs.assert_called('POST', '/os-aggregates/1/action', body) self.assertIsInstance(result3, aggregates.Aggregate) def test_set_metadata(self): aggregate = self.cs.aggregates.get("1") metadata = {"foo": "bar"} body = {"set_metadata": {"metadata": metadata}} result1 = aggregate.set_metadata(metadata) self.cs.assert_called('POST', '/os-aggregates/1/action', body) self.assertIsInstance(result1, aggregates.Aggregate) result2 = self.cs.aggregates.set_metadata(2, metadata) self.cs.assert_called('POST', '/os-aggregates/2/action', body) self.assertIsInstance(result2, aggregates.Aggregate) result3 = self.cs.aggregates.set_metadata(aggregate, metadata) self.cs.assert_called('POST', '/os-aggregates/1/action', body) self.assertIsInstance(result3, aggregates.Aggregate) def test_delete_aggregate(self): aggregate = self.cs.aggregates.list()[0] aggregate.delete() self.cs.assert_called('DELETE', '/os-aggregates/1') self.cs.aggregates.delete('1') self.cs.assert_called('DELETE', '/os-aggregates/1') self.cs.aggregates.delete(aggregate) self.cs.assert_called('DELETE', '/os-aggregates/1') python-novaclient-2.17.0/novaclient/tests/v1_1/test_quota_classes.py0000664000175400017540000000260512306432770026760 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # 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 novaclient.tests import utils from novaclient.tests.v1_1 import fakes cs = fakes.FakeClient() class QuotaClassSetsTest(utils.TestCase): def test_class_quotas_get(self): class_name = 'test' cs.quota_classes.get(class_name) cs.assert_called('GET', '/os-quota-class-sets/%s' % class_name) def test_update_quota(self): q = cs.quota_classes.get('test') q.update(cores=2) cs.assert_called('PUT', '/os-quota-class-sets/test') def test_refresh_quota(self): q = cs.quota_classes.get('test') q2 = cs.quota_classes.get('test') self.assertEqual(q.cores, q2.cores) q2.cores = 0 self.assertNotEqual(q.cores, q2.cores) q2.get() self.assertEqual(q.cores, q2.cores) python-novaclient-2.17.0/novaclient/tests/v1_1/test_security_group_rules.py0000664000175400017540000000576312306432770030417 0ustar jenkinsjenkins00000000000000# # 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 novaclient import exceptions from novaclient.tests import utils from novaclient.tests.v1_1 import fakes from novaclient.v1_1 import security_group_rules cs = fakes.FakeClient() class SecurityGroupRulesTest(utils.TestCase): def test_delete_security_group_rule(self): cs.security_group_rules.delete(1) cs.assert_called('DELETE', '/os-security-group-rules/1') def test_create_security_group_rule(self): sg = cs.security_group_rules.create(1, "tcp", 1, 65535, "10.0.0.0/16") body = { "security_group_rule": { "ip_protocol": "tcp", "from_port": 1, "to_port": 65535, "cidr": "10.0.0.0/16", "group_id": None, "parent_group_id": 1, } } cs.assert_called('POST', '/os-security-group-rules', body) self.assertTrue(isinstance(sg, security_group_rules.SecurityGroupRule)) def test_create_security_group_group_rule(self): sg = cs.security_group_rules.create(1, "tcp", 1, 65535, "10.0.0.0/16", 101) body = { "security_group_rule": { "ip_protocol": "tcp", "from_port": 1, "to_port": 65535, "cidr": "10.0.0.0/16", "group_id": 101, "parent_group_id": 1, } } cs.assert_called('POST', '/os-security-group-rules', body) self.assertTrue(isinstance(sg, security_group_rules.SecurityGroupRule)) def test_invalid_parameters_create(self): self.assertRaises(exceptions.CommandError, cs.security_group_rules.create, 1, "invalid_ip_protocol", 1, 65535, "10.0.0.0/16", 101) self.assertRaises(exceptions.CommandError, cs.security_group_rules.create, 1, "tcp", "invalid_from_port", 65535, "10.0.0.0/16", 101) self.assertRaises(exceptions.CommandError, cs.security_group_rules.create, 1, "tcp", 1, "invalid_to_port", "10.0.0.0/16", 101) def test_security_group_rule_str(self): sg = cs.security_group_rules.create(1, "tcp", 1, 65535, "10.0.0.0/16") self.assertEqual('1', str(sg)) def test_security_group_rule_del(self): sg = cs.security_group_rules.create(1, "tcp", 1, 65535, "10.0.0.0/16") sg.delete() cs.assert_called('DELETE', '/os-security-group-rules/1') python-novaclient-2.17.0/novaclient/tests/v1_1/test_images.py0000664000175400017540000000443112306432770025356 0ustar jenkinsjenkins00000000000000# # 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 novaclient.tests import utils from novaclient.tests.v1_1 import fakes from novaclient.v1_1 import images cs = fakes.FakeClient() class ImagesTest(utils.TestCase): def test_list_images(self): il = cs.images.list() cs.assert_called('GET', '/images/detail') [self.assertIsInstance(i, images.Image) for i in il] def test_list_images_undetailed(self): il = cs.images.list(detailed=False) cs.assert_called('GET', '/images') [self.assertIsInstance(i, images.Image) for i in il] def test_list_images_with_limit(self): il = cs.images.list(limit=4) cs.assert_called('GET', '/images/detail?limit=4') def test_get_image_details(self): i = cs.images.get(1) cs.assert_called('GET', '/images/1') self.assertIsInstance(i, images.Image) self.assertEqual(i.id, 1) self.assertEqual(i.name, 'CentOS 5.2') def test_delete_image(self): cs.images.delete(1) cs.assert_called('DELETE', '/images/1') def test_delete_meta(self): cs.images.delete_meta(1, {'test_key': 'test_value'}) cs.assert_called('DELETE', '/images/1/metadata/test_key') def test_set_meta(self): cs.images.set_meta(1, {'test_key': 'test_value'}) cs.assert_called('POST', '/images/1/metadata', {"metadata": {'test_key': 'test_value'}}) def test_find(self): i = cs.images.find(name="CentOS 5.2") self.assertEqual(i.id, 1) cs.assert_called('GET', '/images', pos=-2) cs.assert_called('GET', '/images/1', pos=-1) iml = cs.images.findall(status='SAVING') self.assertEqual(len(iml), 1) self.assertEqual(iml[0].name, 'My Server Backup') python-novaclient-2.17.0/novaclient/tests/v1_1/fakes.py0000664000175400017540000022310412306432771024144 0ustar jenkinsjenkins00000000000000# Copyright (c) 2011 X.commerce, a business unit of eBay Inc. # Copyright 2011 OpenStack Foundation # Copyright 2013 IBM Corp. # # 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 datetime import datetime import six from six.moves.urllib import parse from novaclient import client as base_client from novaclient import exceptions from novaclient.openstack.common import strutils from novaclient.tests import fakes from novaclient.tests import utils from novaclient.v1_1 import client class FakeClient(fakes.FakeClient, client.Client): def __init__(self, *args, **kwargs): client.Client.__init__(self, 'username', 'password', 'project_id', 'auth_url', extensions=kwargs.get('extensions')) self.client = FakeHTTPClient(**kwargs) class FakeHTTPClient(base_client.HTTPClient): def __init__(self, **kwargs): self.username = 'username' self.password = 'password' self.auth_url = 'auth_url' self.tenant_id = 'tenant_id' self.callstack = [] self.projectid = 'projectid' self.user = 'user' self.region_name = 'region_name' self.endpoint_type = 'endpoint_type' self.service_type = 'service_type' self.service_name = 'service_name' self.volume_service_name = 'volume_service_name' self.timings = 'timings' self.bypass_url = 'bypass_url' self.os_cache = 'os_cache' self.http_log_debug = 'http_log_debug' def _cs_request(self, url, method, **kwargs): # Check that certain things are called correctly if method in ['GET', 'DELETE']: assert 'body' not in kwargs elif method == 'PUT': assert 'body' in kwargs # Call the method args = parse.parse_qsl(parse.urlparse(url)[4]) kwargs.update(args) munged_url = url.rsplit('?', 1)[0] munged_url = munged_url.strip('/').replace('/', '_').replace('.', '_') munged_url = munged_url.replace('-', '_') munged_url = munged_url.replace(' ', '_') callback = "%s_%s" % (method.lower(), munged_url) if not hasattr(self, callback): raise AssertionError('Called unknown API method: %s %s, ' 'expected fakes method name: %s' % (method, url, callback)) # Note the call self.callstack.append((method, url, kwargs.get('body'))) status, headers, body = getattr(self, callback)(**kwargs) r = utils.TestResponse({ "status_code": status, "text": body, "headers": headers, }) return r, body # # agents # def get_os_agents(self, **kw): hypervisor = kw.get('hypervisor', 'kvm') return (200, {}, {'agents': [{'hypervisor': hypervisor, 'os': 'win', 'architecture': 'x86', 'version': '7.0', 'url': 'xxx://xxxx/xxx/xxx', 'md5hash': 'add6bb58e139be103324d04d82d8f545', 'id': 1}, {'hypervisor': hypervisor, 'os': 'linux', 'architecture': 'x86', 'version': '16.0', 'url': 'xxx://xxxx/xxx/xxx1', 'md5hash': 'add6bb58e139be103324d04d82d8f546', 'id': 2}, ]}) def post_os_agents(self, body): return (200, {}, {'agent': { 'url': '/xxx/xxx/xxx', 'hypervisor': body['agent']['hypervisor'], 'md5hash': 'add6bb58e139be103324d04d82d8f546', 'version': '7.0', 'architecture': 'x86', 'os': 'win', 'id': 1}}) def delete_os_agents_1(self, **kw): return (202, {}, None) def put_os_agents_1(self, body, **kw): return (200, {}, {"agent": { "url": "/yyy/yyyy/yyyy", "version": "8.0", "md5hash": "add6bb58e139be103324d04d82d8f546", 'id': 1}}) # # List all extensions # def get_extensions(self, **kw): exts = [ { "alias": "NMN", "description": "Multiple network support", "links": [], "name": "Multinic", "namespace": ("http://docs.openstack.org/" "compute/ext/multinic/api/v1.1"), "updated": "2011-06-09T00:00:00+00:00" }, { "alias": "OS-DCF", "description": "Disk Management Extension", "links": [], "name": "DiskConfig", "namespace": ("http://docs.openstack.org/" "compute/ext/disk_config/api/v1.1"), "updated": "2011-09-27T00:00:00+00:00" }, { "alias": "OS-EXT-SRV-ATTR", "description": "Extended Server Attributes support.", "links": [], "name": "ExtendedServerAttributes", "namespace": ("http://docs.openstack.org/" "compute/ext/extended_status/api/v1.1"), "updated": "2011-11-03T00:00:00+00:00" }, { "alias": "OS-EXT-STS", "description": "Extended Status support", "links": [], "name": "ExtendedStatus", "namespace": ("http://docs.openstack.org/" "compute/ext/extended_status/api/v1.1"), "updated": "2011-11-03T00:00:00+00:00" }, ] return (200, {}, { "extensions": exts, }) # # Limits # def get_limits(self, **kw): return (200, {}, {"limits": { "rate": [ { "uri": "*", "regex": ".*", "limit": [ { "value": 10, "verb": "POST", "remaining": 2, "unit": "MINUTE", "next-available": "2011-12-15T22:42:45Z" }, { "value": 10, "verb": "PUT", "remaining": 2, "unit": "MINUTE", "next-available": "2011-12-15T22:42:45Z" }, { "value": 100, "verb": "DELETE", "remaining": 100, "unit": "MINUTE", "next-available": "2011-12-15T22:42:45Z" } ] }, { "uri": "*/servers", "regex": "^/servers", "limit": [ { "verb": "POST", "value": 25, "remaining": 24, "unit": "DAY", "next-available": "2011-12-15T22:42:45Z" } ] } ], "absolute": { "maxTotalRAMSize": 51200, "maxServerMeta": 5, "maxImageMeta": 5, "maxPersonality": 5, "maxPersonalitySize": 10240 }, }, }) # # Servers # def get_servers(self, **kw): return (200, {}, {"servers": [ {'id': 1234, 'name': 'sample-server'}, {'id': 5678, 'name': 'sample-server2'} ]}) def get_servers_detail(self, **kw): return (200, {}, {"servers": [ { "id": 1234, "name": "sample-server", "image": { "id": 2, "name": "sample image", }, "flavor": { "id": 1, "name": "256 MB Server", }, "hostId": "e4d909c290d0fb1ca068ffaddf22cbd0", "status": "BUILD", "progress": 60, "addresses": { "public": [{ "version": 4, "addr": "1.2.3.4", }, { "version": 4, "addr": "5.6.7.8", }], "private": [{ "version": 4, "addr": "10.11.12.13", }], }, "metadata": { "Server Label": "Web Head 1", "Image Version": "2.1" }, "OS-EXT-SRV-ATTR:host": "computenode1", "security_groups": [{ 'id': 1, 'name': 'securitygroup1', 'description': 'FAKE_SECURITY_GROUP', 'tenant_id': '4ffc664c198e435e9853f2538fbcd7a7' }], "OS-EXT-MOD:some_thing": "mod_some_thing_value", }, { "id": 5678, "name": "sample-server2", "image": { "id": 2, "name": "sample image", }, "flavor": { "id": 1, "name": "256 MB Server", }, "hostId": "9e107d9d372bb6826bd81d3542a419d6", "status": "ACTIVE", "addresses": { "public": [{ "version": 4, "addr": "4.5.6.7", }, { "version": 4, "addr": "5.6.9.8", }], "private": [{ "version": 4, "addr": "10.13.12.13", }], }, "metadata": { "Server Label": "DB 1" }, "OS-EXT-SRV-ATTR:host": "computenode2", "security_groups": [{ 'id': 1, 'name': 'securitygroup1', 'description': 'FAKE_SECURITY_GROUP', 'tenant_id': '4ffc664c198e435e9853f2538fbcd7a7' }, { 'id': 2, 'name': 'securitygroup2', 'description': 'ANOTHER_FAKE_SECURITY_GROUP', 'tenant_id': '4ffc664c198e435e9853f2538fbcd7a7' }], }, { "id": 9012, "name": "sample-server3", "image": "", "flavor": { "id": 1, "name": "256 MB Server", }, "hostId": "9e107d9d372bb6826bd81d3542a419d6", "status": "ACTIVE", "addresses": { "public": [{ "version": 4, "addr": "4.5.6.7", }, { "version": 4, "addr": "5.6.9.8", }], "private": [{ "version": 4, "addr": "10.13.12.13", }], }, "metadata": { "Server Label": "DB 1" } } ]}) def post_servers(self, body, **kw): assert set(body.keys()) <= set(['server', 'os:scheduler_hints']) fakes.assert_has_keys(body['server'], required=['name', 'imageRef', 'flavorRef'], optional=['metadata', 'personality']) if 'personality' in body['server']: for pfile in body['server']['personality']: fakes.assert_has_keys(pfile, required=['path', 'contents']) return (202, {}, self.get_servers_1234()[2]) def post_os_volumes_boot(self, body, **kw): assert set(body.keys()) <= set(['server', 'os:scheduler_hints']) fakes.assert_has_keys(body['server'], required=['name', 'flavorRef'], optional=['imageRef']) # Require one, and only one, of the keys for bdm if 'block_device_mapping' not in body['server']: if 'block_device_mapping_v2' not in body['server']: raise AssertionError( "missing required keys: 'block_device_mapping'" ) elif 'block_device_mapping_v2' in body['server']: raise AssertionError("found extra keys: 'block_device_mapping'") return (202, {}, self.get_servers_9012()[2]) def get_servers_1234(self, **kw): r = {'server': self.get_servers_detail()[2]['servers'][0]} return (200, {}, r) def get_servers_5678(self, **kw): r = {'server': self.get_servers_detail()[2]['servers'][1]} return (200, {}, r) def get_servers_9012(self, **kw): r = {'server': self.get_servers_detail()[2]['servers'][2]} return (200, {}, r) def put_servers_1234(self, body, **kw): assert list(body) == ['server'] fakes.assert_has_keys(body['server'], optional=['name', 'adminPass']) return (204, {}, body) def delete_servers_1234(self, **kw): return (202, {}, None) def delete_servers_5678(self, **kw): return (202, {}, None) def delete_servers_1234_metadata_test_key(self, **kw): return (204, {}, None) def delete_servers_1234_metadata_key1(self, **kw): return (204, {}, None) def delete_servers_1234_metadata_key2(self, **kw): return (204, {}, None) def post_servers_1234_metadata(self, **kw): return (204, {}, {'metadata': {'test_key': 'test_value'}}) def put_servers_1234_metadata_test_key(self, **kw): return (200, {}, {'meta': {'test_key': 'test_value'}}) def get_servers_1234_diagnostics(self, **kw): return (200, {}, {'data': 'Fake diagnostics'}) def post_servers_uuid1_metadata(self, **kw): return (204, {}, {'metadata': {'key1': 'val1'}}) def post_servers_uuid2_metadata(self, **kw): return (204, {}, {'metadata': {'key1': 'val1'}}) def post_servers_uuid3_metadata(self, **kw): return (204, {}, {'metadata': {'key1': 'val1'}}) def post_servers_uuid4_metadata(self, **kw): return (204, {}, {'metadata': {'key1': 'val1'}}) def delete_servers_uuid1_metadata_key1(self, **kw): return (200, {}, {'data': 'Fake diagnostics'}) def delete_servers_uuid2_metadata_key1(self, **kw): return (200, {}, {'data': 'Fake diagnostics'}) def delete_servers_uuid3_metadata_key1(self, **kw): return (200, {}, {'data': 'Fake diagnostics'}) def delete_servers_uuid4_metadata_key1(self, **kw): return (200, {}, {'data': 'Fake diagnostics'}) def get_servers_1234_os_security_groups(self, **kw): return (200, {}, { "security_groups": [{ 'id': 1, 'name': 'securitygroup1', 'description': 'FAKE_SECURITY_GROUP', 'tenant_id': '4ffc664c198e435e9853f2538fbcd7a7', 'rules': []}] }) # # Server Addresses # def get_servers_1234_ips(self, **kw): return (200, {}, {'addresses': self.get_servers_1234()[1]['server']['addresses']}) def get_servers_1234_ips_public(self, **kw): return (200, {}, {'public': self.get_servers_1234_ips()[1]['addresses']['public']}) def get_servers_1234_ips_private(self, **kw): return (200, {}, {'private': self.get_servers_1234_ips()[1]['addresses']['private']}) def delete_servers_1234_ips_public_1_2_3_4(self, **kw): return (202, {}, None) # # Server password # # Testing with the following password and key # # Clear password: FooBar123 # # RSA Private Key: novaclient/tests/idfake.pem # # Encrypted password # OIuEuQttO8Rk93BcKlwHQsziDAnkAm/V6V8VPToA8ZeUaUBWwS0gwo2K6Y61Z96r # qG447iRz0uTEEYq3RAYJk1mh3mMIRVl27t8MtIecR5ggVVbz1S9AwXJQypDKl0ho # QFvhCBcMWPohyGewDJOhDbtuN1IoFI9G55ZvFwCm5y7m7B2aVcoLeIsJZE4PLsIw # /y5a6Z3/AoJZYGG7IH5WN88UROU3B9JZGFB2qtPLQTOvDMZLUhoPRIJeHiVSlo1N # tI2/++UsXVg3ow6ItqCJGgdNuGG5JB+bslDHWPxROpesEIHdczk46HCpHQN8f1sk # Hi/fmZZNQQqj1Ijq0caOIw== def get_servers_1234_os_server_password(self, **kw): return (200, {}, {'password': 'OIuEuQttO8Rk93BcKlwHQsziDAnkAm/V6V8VPToA8ZeUaUBWwS0gwo2K6Y61Z96r' 'qG447iRz0uTEEYq3RAYJk1mh3mMIRVl27t8MtIecR5ggVVbz1S9AwXJQypDKl0ho' 'QFvhCBcMWPohyGewDJOhDbtuN1IoFI9G55ZvFwCm5y7m7B2aVcoLeIsJZE4PLsIw' '/y5a6Z3/AoJZYGG7IH5WN88UROU3B9JZGFB2qtPLQTOvDMZLUhoPRIJeHiVSlo1N' 'tI2/++UsXVg3ow6ItqCJGgdNuGG5JB+bslDHWPxROpesEIHdczk46HCpHQN8f1sk' 'Hi/fmZZNQQqj1Ijq0caOIw=='}) def delete_servers_1234_os_server_password(self, **kw): return (202, {}, None) # # Server actions # def post_servers_1234_action(self, body, **kw): _headers = None _body = None resp = 202 assert len(body.keys()) == 1 action = list(body)[0] if action == 'reboot': assert list(body[action]) == ['type'] assert body[action]['type'] in ['HARD', 'SOFT'] elif action == 'rebuild': keys = list(body[action]) if 'adminPass' in keys: keys.remove('adminPass') assert 'imageRef' in keys _body = self.get_servers_1234()[2] elif action == 'resize': keys = body[action].keys() assert 'flavorRef' in keys elif action == 'confirmResize': assert body[action] is None # This one method returns a different response code return (204, {}, None) elif action == 'revertResize': assert body[action] is None elif action == 'migrate': assert body[action] is None elif action == 'os-stop': assert body[action] is None elif action == 'os-start': assert body[action] is None elif action == 'forceDelete': assert body[action] is None elif action == 'restore': assert body[action] is None elif action == 'pause': assert body[action] is None elif action == 'unpause': assert body[action] is None elif action == 'lock': assert body[action] is None elif action == 'unlock': assert body[action] is None elif action == 'rescue': assert body[action] is None _body = {'Password': 'RescuePassword'} elif action == 'unrescue': assert body[action] is None elif action == 'resume': assert body[action] is None elif action == 'suspend': assert body[action] is None elif action == 'lock': assert body[action] is None elif action == 'unlock': assert body[action] is None elif action == 'shelve': assert body[action] is None elif action == 'shelveOffload': assert body[action] is None elif action == 'unshelve': assert body[action] is None elif action == 'addFixedIp': assert list(body[action]) == ['networkId'] elif action == 'removeFixedIp': assert list(body[action]) == ['address'] elif action == 'addFloatingIp': assert (list(body[action]) == ['address'] or sorted(list(body[action])) == ['address', 'fixed_address']) elif action == 'removeFloatingIp': assert list(body[action]) == ['address'] elif action == 'createImage': assert set(body[action].keys()) == set(['name', 'metadata']) _headers = dict(location="http://blah/images/456") elif action == 'changePassword': assert list(body[action]) == ['adminPass'] elif action == 'os-getConsoleOutput': assert list(body[action]) == ['length'] return (202, {}, {'output': 'foo'}) elif action == 'os-getVNCConsole': assert list(body[action]) == ['type'] elif action == 'os-getSPICEConsole': assert list(body[action]) == ['type'] elif action == 'os-getRDPConsole': assert list(body[action]) == ['type'] elif action == 'os-migrateLive': assert set(body[action].keys()) == set(['host', 'block_migration', 'disk_over_commit']) elif action == 'os-resetState': assert list(body[action]) == ['state'] elif action == 'resetNetwork': assert body[action] is None elif action == 'addSecurityGroup': assert list(body[action]) == ['name'] elif action == 'removeSecurityGroup': assert list(body[action]) == ['name'] elif action == 'createBackup': assert set(body[action]) == set(['name', 'backup_type', 'rotation']) elif action == 'evacuate': keys = list(body[action]) if 'adminPass' in keys: keys.remove('adminPass') assert set(keys) == set(['host', 'onSharedStorage']) else: raise AssertionError("Unexpected server action: %s" % action) return (resp, _headers, _body) # # Cloudpipe # def get_os_cloudpipe(self, **kw): return ( 200, {}, {'cloudpipes': [{'project_id': 1}]} ) def post_os_cloudpipe(self, **ks): return ( 202, {}, {'instance_id': '9d5824aa-20e6-4b9f-b967-76a699fc51fd'} ) def put_os_cloudpipe_configure_project(self, **kw): return (202, {}, None) # # Flavors # def get_flavors(self, **kw): status, header, flavors = self.get_flavors_detail(**kw) for flavor in flavors['flavors']: for k in list(flavor): if k not in ['id', 'name']: del flavor[k] return (200, {}, flavors) def get_flavors_detail(self, **kw): flavors = {'flavors': [ {'id': 1, 'name': '256 MB Server', 'ram': 256, 'disk': 10, 'OS-FLV-EXT-DATA:ephemeral': 10, 'os-flavor-access:is_public': True, 'links': {}}, {'id': 2, 'name': '512 MB Server', 'ram': 512, 'disk': 20, 'OS-FLV-EXT-DATA:ephemeral': 20, 'os-flavor-access:is_public': False, 'links': {}}, {'id': 4, 'name': '1024 MB Server', 'ram': 1024, 'disk': 10, 'OS-FLV-EXT-DATA:ephemeral': 10, 'os-flavor-access:is_public': True, 'links': {}}, {'id': 'aa1', 'name': '128 MB Server', 'ram': 128, 'disk': 0, 'OS-FLV-EXT-DATA:ephemeral': 0, 'os-flavor-access:is_public': True, 'links': {}} ]} if 'is_public' not in kw: filter_is_public = True else: if kw['is_public'].lower() == 'none': filter_is_public = None else: filter_is_public = strutils.bool_from_string(kw['is_public'], True) if filter_is_public is not None: if filter_is_public: flavors['flavors'] = [ v for v in flavors['flavors'] if v['os-flavor-access:is_public'] ] else: flavors['flavors'] = [ v for v in flavors['flavors'] if not v['os-flavor-access:is_public'] ] return (200, {}, flavors) def get_flavors_1(self, **kw): return ( 200, {}, {'flavor': self.get_flavors_detail(is_public='None')[2]['flavors'][0]} ) def get_flavors_2(self, **kw): return ( 200, {}, {'flavor': self.get_flavors_detail(is_public='None')[2]['flavors'][1]} ) def get_flavors_3(self, **kw): # Diablo has no ephemeral return ( 200, {}, {'flavor': { 'id': 3, 'name': '256 MB Server', 'ram': 256, 'disk': 10, }}, ) def get_flavors_512_MB_Server(self, **kw): raise exceptions.NotFound('404') def get_flavors_aa1(self, **kw): # Aplhanumeric flavor id are allowed. return ( 200, {}, {'flavor': self.get_flavors_detail(is_public='None')[2]['flavors'][3]} ) def get_flavors_4(self, **kw): return ( 200, {}, {'flavor': self.get_flavors_detail(is_public='None')[2]['flavors'][2]} ) def delete_flavors_flavordelete(self, **kw): return (202, {}, None) def delete_flavors_2(self, **kw): return (202, {}, None) def post_flavors(self, body, **kw): return ( 202, {}, {'flavor': self.get_flavors_detail(is_public='None')[2]['flavors'][0]} ) def get_flavors_1_os_extra_specs(self, **kw): return (200, {}, {'extra_specs': {"k1": "v1"}}) def get_flavors_2_os_extra_specs(self, **kw): return (200, {}, {'extra_specs': {"k2": "v2"}}) def get_flavors_aa1_os_extra_specs(self, **kw): return (200, {}, {'extra_specs': {"k3": "v3"}}) def get_flavors_4_os_extra_specs(self, **kw): return (200, {}, {'extra_specs': {"k4": "v4"}}) def post_flavors_1_os_extra_specs(self, body, **kw): assert list(body) == ['extra_specs'] fakes.assert_has_keys(body['extra_specs'], required=['k1']) return (200, {}, {'extra_specs': {"k1": "v1"}}) def post_flavors_4_os_extra_specs(self, body, **kw): assert list(body) == ['extra_specs'] return (200, {}, body) def delete_flavors_1_os_extra_specs_k1(self, **kw): return (204, {}, None) # # Flavor access # def get_flavors_1_os_flavor_access(self, **kw): return (404, {}, None) def get_flavors_2_os_flavor_access(self, **kw): return (200, {}, {'flavor_access': [ {'flavor_id': '2', 'tenant_id': 'proj1'}, {'flavor_id': '2', 'tenant_id': 'proj2'} ]}) def post_flavors_2_action(self, body, **kw): return (202, {}, self.get_flavors_2_os_flavor_access()[2]) # # Floating ips # def get_os_floating_ip_pools(self): return ( 200, {}, {'floating_ip_pools': [{'name': 'foo'}, {'name': 'bar'}]} ) def get_os_floating_ips(self, **kw): return ( 200, {}, {'floating_ips': [ {'id': 1, 'fixed_ip': '10.0.0.1', 'ip': '11.0.0.1'}, {'id': 2, 'fixed_ip': '10.0.0.2', 'ip': '11.0.0.2'}, ]}, ) def get_os_floating_ips_1(self, **kw): return (200, {}, {'floating_ip': {'id': 1, 'fixed_ip': '10.0.0.1', 'ip': '11.0.0.1'} }) def post_os_floating_ips(self, body): if body.get('pool'): return (200, {}, {'floating_ip': {'id': 1, 'fixed_ip': '10.0.0.1', 'ip': '11.0.0.1', 'pool': 'nova'}}) else: return (200, {}, {'floating_ip': {'id': 1, 'fixed_ip': '10.0.0.1', 'ip': '11.0.0.1', 'pool': None}}) def delete_os_floating_ips_1(self, **kw): return (204, {}, None) def get_os_floating_ip_dns(self, **kw): return (205, {}, {'domain_entries': [{'domain': 'example.org'}, {'domain': 'example.com'}]}) def get_os_floating_ip_dns_testdomain_entries(self, **kw): if kw.get('ip'): return (205, {}, {'dns_entries': [{'dns_entry': {'ip': kw.get('ip'), 'name': "host1", 'type': "A", 'domain': 'testdomain'}}, {'dns_entry': {'ip': kw.get('ip'), 'name': "host2", 'type': "A", 'domain': 'testdomain'}}]}) else: return (404, {}, None) def get_os_floating_ip_dns_testdomain_entries_testname(self, **kw): return (205, {}, {'dns_entry': {'ip': "10.10.10.10", 'name': 'testname', 'type': "A", 'domain': 'testdomain'}}) def put_os_floating_ip_dns_testdomain(self, body, **kw): if body['domain_entry']['scope'] == 'private': fakes.assert_has_keys(body['domain_entry'], required=['availability_zone', 'scope']) elif body['domain_entry']['scope'] == 'public': fakes.assert_has_keys(body['domain_entry'], required=['project', 'scope']) else: fakes.assert_has_keys(body['domain_entry'], required=['project', 'scope']) return (205, {}, body) def put_os_floating_ip_dns_testdomain_entries_testname(self, body, **kw): fakes.assert_has_keys(body['dns_entry'], required=['ip', 'dns_type']) return (205, {}, body) def delete_os_floating_ip_dns_testdomain(self, **kw): return (200, {}, None) def delete_os_floating_ip_dns_testdomain_entries_testname(self, **kw): return (200, {}, None) def get_os_floating_ips_bulk(self, **kw): return (200, {}, {'floating_ip_info': [ {'id': 1, 'fixed_ip': '10.0.0.1', 'ip': '11.0.0.1'}, {'id': 2, 'fixed_ip': '10.0.0.2', 'ip': '11.0.0.2'}, ]}) def get_os_floating_ips_bulk_testHost(self, **kw): return (200, {}, {'floating_ip_info': [ {'id': 1, 'fixed_ip': '10.0.0.1', 'ip': '11.0.0.1'}, {'id': 2, 'fixed_ip': '10.0.0.2', 'ip': '11.0.0.2'}, ]}) def post_os_floating_ips_bulk(self, **kw): params = kw.get('body').get('floating_ips_bulk_create') pool = params.get('pool', 'defaultPool') interface = params.get('interface', 'defaultInterface') return (200, {}, {'floating_ips_bulk_create': {'ip_range': '192.168.1.0/30', 'pool': pool, 'interface': interface}}) def put_os_floating_ips_bulk_delete(self, **kw): ip_range = kw.get('body').get('ip_range') return (200, {}, {'floating_ips_bulk_delete': ip_range}) # # Images # def get_images(self, **kw): return (200, {}, {'images': [ {'id': 1, 'name': 'CentOS 5.2'}, {'id': 2, 'name': 'My Server Backup'} ]}) def get_images_detail(self, **kw): return (200, {}, {'images': [ { 'id': 1, 'name': 'CentOS 5.2', "updated": "2010-10-10T12:00:00Z", "created": "2010-08-10T12:00:00Z", "status": "ACTIVE", "metadata": { "test_key": "test_value", }, "links": {}, }, { "id": 2, "name": "My Server Backup", "serverId": 1234, "updated": "2010-10-10T12:00:00Z", "created": "2010-08-10T12:00:00Z", "status": "SAVING", "progress": 80, "links": {}, } ]}) def get_images_1(self, **kw): return (200, {}, {'image': self.get_images_detail()[2]['images'][0]}) def get_images_2(self, **kw): return (200, {}, {'image': self.get_images_detail()[2]['images'][1]}) def get_images_456(self, **kw): return (200, {}, {'image': self.get_images_detail()[2]['images'][1]}) def post_images(self, body, **kw): assert list(body) == ['image'] fakes.assert_has_keys(body['image'], required=['serverId', 'name']) return (202, {}, self.get_images_1()[2]) def post_images_1_metadata(self, body, **kw): assert list(body) == ['metadata'] fakes.assert_has_keys(body['metadata'], required=['test_key']) return (200, {}, {'metadata': self.get_images_1()[2]['image']['metadata']}) def delete_images_1(self, **kw): return (204, {}, None) def delete_images_2(self, **kw): return (204, {}, None) def delete_images_1_metadata_test_key(self, **kw): return (204, {}, None) # # Keypairs # def get_os_keypairs_test(self, *kw): return (200, {}, {'keypair': self.get_os_keypairs()[2]['keypairs'][0]}) def get_os_keypairs(self, *kw): return (200, {}, {"keypairs": [ {'fingerprint': 'FAKE_KEYPAIR', 'name': 'test'} ]}) def delete_os_keypairs_test(self, **kw): return (202, {}, None) def post_os_keypairs(self, body, **kw): assert list(body) == ['keypair'] fakes.assert_has_keys(body['keypair'], required=['name']) r = {'keypair': self.get_os_keypairs()[2]['keypairs'][0]} return (202, {}, r) # # Virtual Interfaces # def get_servers_1234_os_virtual_interfaces(self, **kw): return (200, {}, {"virtual_interfaces": [ {'id': 'fakeid', 'mac_address': 'fakemac'} ]}) # # Quotas # def get_os_quota_sets_test(self, **kw): return (200, {}, {'quota_set': { 'tenant_id': 'test', 'metadata_items': [], 'injected_file_content_bytes': 1, 'injected_file_path_bytes': 1, 'ram': 1, 'floating_ips': 1, 'instances': 1, 'injected_files': 1, 'cores': 1, 'keypairs': 1, 'security_groups': 1, 'security_group_rules': 1}}) def get_os_quota_sets_tenant_id(self, **kw): return (200, {}, {'quota_set': { 'tenant_id': 'test', 'metadata_items': [], 'injected_file_content_bytes': 1, 'injected_file_path_bytes': 1, 'ram': 1, 'floating_ips': 1, 'instances': 1, 'injected_files': 1, 'cores': 1, 'keypairs': 1, 'security_groups': 1, 'security_group_rules': 1}}) def get_os_quota_sets_97f4c221bff44578b0300df4ef119353(self, **kw): return (200, {}, {'quota_set': { 'tenant_id': '97f4c221bff44578b0300df4ef119353', 'metadata_items': [], 'injected_file_content_bytes': 1, 'injected_file_path_bytes': 1, 'ram': 1, 'floating_ips': 1, 'instances': 1, 'injected_files': 1, 'cores': 1, 'keypairs': 1, 'security_groups': 1, 'security_group_rules': 1}}) def put_os_quota_sets_97f4c221_bff4_4578_b030_0df4ef119353(self, **kw): return (200, {}, {'quota_set': { 'tenant_id': '97f4c221-bff4-4578-b030-0df4ef119353', 'metadata_items': [], 'injected_file_content_bytes': 1, 'injected_file_path_bytes': 1, 'ram': 1, 'floating_ips': 1, 'instances': 1, 'injected_files': 1, 'cores': 1, 'keypairs': 1, 'security_groups': 1, 'security_group_rules': 1}}) def get_os_quota_sets_97f4c221_bff4_4578_b030_0df4ef119353(self, **kw): return (200, {}, {'quota_set': { 'tenant_id': '97f4c221-bff4-4578-b030-0df4ef119353', 'metadata_items': [], 'injected_file_content_bytes': 1, 'injected_file_path_bytes': 1, 'ram': 1, 'floating_ips': 1, 'instances': 1, 'injected_files': 1, 'cores': 1, 'keypairs': 1, 'security_groups': 1, 'security_group_rules': 1}}) def get_os_quota_sets_97f4c221bff44578b0300df4ef119353_defaults(self): return (200, {}, {'quota_set': { 'tenant_id': 'test', 'metadata_items': [], 'injected_file_content_bytes': 1, 'injected_file_path_bytes': 1, 'ram': 1, 'floating_ips': 1, 'instances': 1, 'injected_files': 1, 'cores': 1, 'keypairs': 1, 'security_groups': 1, 'security_group_rules': 1}}) def get_os_quota_sets_tenant_id_defaults(self): return (200, {}, {'quota_set': { 'tenant_id': 'test', 'metadata_items': [], 'injected_file_content_bytes': 1, 'injected_file_path_bytes': 1, 'ram': 1, 'floating_ips': 1, 'instances': 1, 'injected_files': 1, 'cores': 1, 'keypairs': 1, 'security_groups': 1, 'security_group_rules': 1}}) def put_os_quota_sets_97f4c221bff44578b0300df4ef119353(self, body, **kw): assert list(body) == ['quota_set'] fakes.assert_has_keys(body['quota_set'], required=['tenant_id']) return (200, {}, {'quota_set': { 'tenant_id': '97f4c221bff44578b0300df4ef119353', 'metadata_items': [], 'injected_file_content_bytes': 1, 'injected_file_path_bytes': 1, 'ram': 1, 'floating_ips': 1, 'instances': 1, 'injected_files': 1, 'cores': 1, 'keypairs': 1, 'security_groups': 1, 'security_group_rules': 1}}) def delete_os_quota_sets_test(self, **kw): return (202, {}, {}) def delete_os_quota_sets_97f4c221bff44578b0300df4ef119353(self, **kw): return (202, {}, {}) # # Quota Classes # def get_os_quota_class_sets_test(self, **kw): return (200, {}, {'quota_class_set': { 'id': 'test', 'metadata_items': 1, 'injected_file_content_bytes': 1, 'injected_file_path_bytes': 1, 'ram': 1, 'floating_ips': 1, 'instances': 1, 'injected_files': 1, 'cores': 1, 'key_pairs': 1, 'security_groups': 1, 'security_group_rules': 1}}) def put_os_quota_class_sets_test(self, body, **kw): assert list(body) == ['quota_class_set'] return (200, {}, {'quota_class_set': { 'metadata_items': 1, 'injected_file_content_bytes': 1, 'injected_file_path_bytes': 1, 'ram': 1, 'floating_ips': 1, 'instances': 1, 'injected_files': 1, 'cores': 1, 'key_pairs': 1, 'security_groups': 1, 'security_group_rules': 1}}) def put_os_quota_class_sets_97f4c221bff44578b0300df4ef119353(self, body, **kw): assert list(body) == ['quota_class_set'] return (200, {}, {'quota_class_set': { 'metadata_items': 1, 'injected_file_content_bytes': 1, 'injected_file_path_bytes': 1, 'ram': 1, 'floating_ips': 1, 'instances': 1, 'injected_files': 1, 'cores': 1, 'key_pairs': 1, 'security_groups': 1, 'security_group_rules': 1}}) # # Security Groups # def get_os_security_groups(self, **kw): return (200, {}, {"security_groups": [ {"name": "test", "description": "FAKE_SECURITY_GROUP", "tenant_id": "4ffc664c198e435e9853f2538fbcd7a7", "id": 1, "rules": [ {"id": 11, "group": {}, "ip_protocol": "TCP", "from_port": 22, "to_port": 22, "parent_group_id": 1, "ip_range": {"cidr": "10.0.0.0/8"}}, {"id": 12, "group": { "tenant_id": "272bee4c1e624cd4a72a6b0ea55b4582", "name": "test2"}, "ip_protocol": "TCP", "from_port": 222, "to_port": 222, "parent_group_id": 1, "ip_range": {}}]}, {"name": "test2", "description": "FAKE_SECURITY_GROUP2", "tenant_id": "272bee4c1e624cd4a72a6b0ea55b4582", "id": 2, "rules": []} ]} ) def get_os_security_groups_1(self, **kw): return (200, {}, {"security_group": {'id': 1, 'name': 'test', 'description': 'FAKE_SECURITY_GROUP'} }) def delete_os_security_groups_1(self, **kw): return (202, {}, None) def post_os_security_groups(self, body, **kw): assert list(body) == ['security_group'] fakes.assert_has_keys(body['security_group'], required=['name', 'description']) r = {'security_group': self.get_os_security_groups()[2]['security_groups'][0]} return (202, {}, r) def put_os_security_groups_1(self, body, **kw): assert list(body) == ['security_group'] fakes.assert_has_keys(body['security_group'], required=['name', 'description']) return (205, {}, body) # # Security Group Rules # def get_os_security_group_rules(self, **kw): return (200, {}, {"security_group_rules": [ {'id': 1, 'parent_group_id': 1, 'group_id': 2, 'ip_protocol': 'TCP', 'from_port': '22', 'to_port': 22, 'cidr': '10.0.0.0/8'} ]}) def delete_os_security_group_rules_1(self, **kw): return (202, {}, None) def delete_os_security_group_rules_11(self, **kw): return (202, {}, None) def delete_os_security_group_rules_12(self, **kw): return (202, {}, None) def post_os_security_group_rules(self, body, **kw): assert list(body) == ['security_group_rule'] fakes.assert_has_keys(body['security_group_rule'], required=['parent_group_id'], optional=['group_id', 'ip_protocol', 'from_port', 'to_port', 'cidr']) r = {'security_group_rule': self.get_os_security_group_rules()[2]['security_group_rules'][0]} return (202, {}, r) # # Tenant Usage # def get_os_simple_tenant_usage(self, **kw): return (200, {}, {six.u('tenant_usages'): [{ six.u('total_memory_mb_usage'): 25451.762807466665, six.u('total_vcpus_usage'): 49.71047423333333, six.u('total_hours'): 49.71047423333333, six.u('tenant_id'): six.u('7b0a1d73f8fb41718f3343c207597869'), six.u('stop'): six.u('2012-01-22 19:48:41.750722'), six.u('server_usages'): [{ six.u('hours'): 49.71047423333333, six.u('uptime'): 27035, six.u('local_gb'): 0, six.u('ended_at'): None, six.u('name'): six.u('f15image1'), six.u('tenant_id'): six.u('7b0a1d73f8fb41718f3343c207597869'), six.u('vcpus'): 1, six.u('memory_mb'): 512, six.u('state'): six.u('active'), six.u('flavor'): six.u('m1.tiny'), six.u('started_at'): six.u('2012-01-20 18:06:06.479998')}], six.u('start'): six.u('2011-12-25 19:48:41.750687'), six.u('total_local_gb_usage'): 0.0}]}) def get_os_simple_tenant_usage_tenantfoo(self, **kw): return (200, {}, {six.u('tenant_usage'): { six.u('total_memory_mb_usage'): 25451.762807466665, six.u('total_vcpus_usage'): 49.71047423333333, six.u('total_hours'): 49.71047423333333, six.u('tenant_id'): six.u('7b0a1d73f8fb41718f3343c207597869'), six.u('stop'): six.u('2012-01-22 19:48:41.750722'), six.u('server_usages'): [{ six.u('hours'): 49.71047423333333, six.u('uptime'): 27035, six.u('local_gb'): 0, six.u('ended_at'): None, six.u('name'): six.u('f15image1'), six.u('tenant_id'): six.u('7b0a1d73f8fb41718f3343c207597869'), six.u('vcpus'): 1, six.u('memory_mb'): 512, six.u('state'): six.u('active'), six.u('flavor'): six.u('m1.tiny'), six.u('started_at'): six.u('2012-01-20 18:06:06.479998')}], six.u('start'): six.u('2011-12-25 19:48:41.750687'), six.u('total_local_gb_usage'): 0.0}}) def get_os_simple_tenant_usage_test(self, **kw): return (200, {}, {six.u('tenant_usage'): { six.u('total_memory_mb_usage'): 25451.762807466665, six.u('total_vcpus_usage'): 49.71047423333333, six.u('total_hours'): 49.71047423333333, six.u('tenant_id'): six.u('7b0a1d73f8fb41718f3343c207597869'), six.u('stop'): six.u('2012-01-22 19:48:41.750722'), six.u('server_usages'): [{ six.u('hours'): 49.71047423333333, six.u('uptime'): 27035, six.u('local_gb'): 0, six.u('ended_at'): None, six.u('name'): six.u('f15image1'), six.u('tenant_id'): six.u('7b0a1d73f8fb41718f3343c207597869'), six.u('vcpus'): 1, six.u('memory_mb'): 512, six.u('state'): six.u('active'), six.u('flavor'): six.u('m1.tiny'), six.u('started_at'): six.u('2012-01-20 18:06:06.479998')}], six.u('start'): six.u('2011-12-25 19:48:41.750687'), six.u('total_local_gb_usage'): 0.0}}) def get_os_simple_tenant_usage_tenant_id(self, **kw): return (200, {}, {six.u('tenant_usage'): { six.u('total_memory_mb_usage'): 25451.762807466665, six.u('total_vcpus_usage'): 49.71047423333333, six.u('total_hours'): 49.71047423333333, six.u('tenant_id'): six.u('7b0a1d73f8fb41718f3343c207597869'), six.u('stop'): six.u('2012-01-22 19:48:41.750722'), six.u('server_usages'): [{ six.u('hours'): 49.71047423333333, six.u('uptime'): 27035, six.u('local_gb'): 0, six.u('ended_at'): None, six.u('name'): six.u('f15image1'), six.u('tenant_id'): six.u('7b0a1d73f8fb41718f3343c207597869'), six.u('vcpus'): 1, six.u('memory_mb'): 512, six.u('state'): six.u('active'), six.u('flavor'): six.u('m1.tiny'), six.u('started_at'): six.u('2012-01-20 18:06:06.479998')}], six.u('start'): six.u('2011-12-25 19:48:41.750687'), six.u('total_local_gb_usage'): 0.0}}) # # Certificates # def get_os_certificates_root(self, **kw): return ( 200, {}, {'certificate': {'private_key': None, 'data': 'foo'}} ) def post_os_certificates(self, **kw): return ( 200, {}, {'certificate': {'private_key': 'foo', 'data': 'bar'}} ) # # Aggregates # def get_os_aggregates(self, *kw): return (200, {}, {"aggregates": [ {'id': '1', 'name': 'test', 'availability_zone': 'nova1'}, {'id': '2', 'name': 'test2', 'availability_zone': 'nova1'}, ]}) def _return_aggregate(self): r = {'aggregate': self.get_os_aggregates()[2]['aggregates'][0]} return (200, {}, r) def get_os_aggregates_1(self, **kw): return self._return_aggregate() def post_os_aggregates(self, body, **kw): return self._return_aggregate() def put_os_aggregates_1(self, body, **kw): return self._return_aggregate() def put_os_aggregates_2(self, body, **kw): return self._return_aggregate() def post_os_aggregates_1_action(self, body, **kw): return self._return_aggregate() def post_os_aggregates_2_action(self, body, **kw): return self._return_aggregate() def delete_os_aggregates_1(self, **kw): return (202, {}, None) # # Services # def get_os_services(self, **kw): host = kw.get('host', 'host1') binary = kw.get('binary', 'nova-compute') return (200, {}, {'services': [{'binary': binary, 'host': host, 'zone': 'nova', 'status': 'enabled', 'state': 'up', 'updated_at': datetime(2012, 10, 29, 13, 42, 2)}, {'binary': binary, 'host': host, 'zone': 'nova', 'status': 'disabled', 'state': 'down', 'updated_at': datetime(2012, 9, 18, 8, 3, 38)}, ]}) def put_os_services_enable(self, body, **kw): return (200, {}, {'service': {'host': body['host'], 'binary': body['binary'], 'status': 'enabled'}}) def put_os_services_disable(self, body, **kw): return (200, {}, {'service': {'host': body['host'], 'binary': body['binary'], 'status': 'disabled'}}) def put_os_services_disable_log_reason(self, body, **kw): return (200, {}, {'service': {'host': body['host'], 'binary': body['binary'], 'status': 'disabled', 'disabled_reason': body['disabled_reason']}}) # # Fixed IPs # def get_os_fixed_ips_192_168_1_1(self, *kw): return (200, {}, {"fixed_ip": {'cidr': '192.168.1.0/24', 'address': '192.168.1.1', 'hostname': 'foo', 'host': 'bar'}}) def post_os_fixed_ips_192_168_1_1_action(self, body, **kw): return (202, {}, None) # # Hosts # def get_os_hosts_host(self, *kw): return (200, {}, {'host': [{'resource': {'project': '(total)', 'host': 'dummy', 'cpu': 16, 'memory_mb': 32234, 'disk_gb': 128}}, {'resource': {'project': '(used_now)', 'host': 'dummy', 'cpu': 1, 'memory_mb': 2075, 'disk_gb': 45}}, {'resource': {'project': '(used_max)', 'host': 'dummy', 'cpu': 1, 'memory_mb': 2048, 'disk_gb': 30}}, {'resource': {'project': 'admin', 'host': 'dummy', 'cpu': 1, 'memory_mb': 2048, 'disk_gb': 30}}]}) def get_os_hosts(self, **kw): zone = kw.get('zone', 'nova1') return (200, {}, {'hosts': [{'host': 'host1', 'service': 'nova-compute', 'zone': zone}, {'host': 'host1', 'service': 'nova-cert', 'zone': zone}]}) def get_os_hosts_sample_host(self, *kw): return (200, {}, {'host': [{'resource': {'host': 'sample_host'}}], }) def put_os_hosts_sample_host_1(self, body, **kw): return (200, {}, {'host': 'sample-host_1', 'status': 'enabled'}) def put_os_hosts_sample_host_2(self, body, **kw): return (200, {}, {'host': 'sample-host_2', 'maintenance_mode': 'on_maintenance'}) def put_os_hosts_sample_host_3(self, body, **kw): return (200, {}, {'host': 'sample-host_3', 'status': 'enabled', 'maintenance_mode': 'on_maintenance'}) def get_os_hosts_sample_host_reboot(self, **kw): return (200, {}, {'host': 'sample_host', 'power_action': 'reboot'}) def get_os_hosts_sample_host_startup(self, **kw): return (200, {}, {'host': 'sample_host', 'power_action': 'startup'}) def get_os_hosts_sample_host_shutdown(self, **kw): return (200, {}, {'host': 'sample_host', 'power_action': 'shutdown'}) def put_os_hosts_sample_host(self, body, **kw): result = {'host': 'dummy'} result.update(body) return (200, {}, result) def get_os_hypervisors(self, **kw): return (200, {}, {"hypervisors": [ {'id': 1234, 'hypervisor_hostname': 'hyper1'}, {'id': 5678, 'hypervisor_hostname': 'hyper2'}, ]}) def get_os_hypervisors_detail(self, **kw): return (200, {}, {"hypervisors": [ {'id': 1234, 'service': {'id': 1, 'host': 'compute1'}, 'vcpus': 4, 'memory_mb': 10 * 1024, 'local_gb': 250, 'vcpus_used': 2, 'memory_mb_used': 5 * 1024, 'local_gb_used': 125, 'hypervisor_type': "xen", 'hypervisor_version': 3, 'hypervisor_hostname': "hyper1", 'free_ram_mb': 5 * 1024, 'free_disk_gb': 125, 'current_workload': 2, 'running_vms': 2, 'cpu_info': 'cpu_info', 'disk_available_least': 100}, {'id': 2, 'service': {'id': 2, 'host': "compute2"}, 'vcpus': 4, 'memory_mb': 10 * 1024, 'local_gb': 250, 'vcpus_used': 2, 'memory_mb_used': 5 * 1024, 'local_gb_used': 125, 'hypervisor_type': "xen", 'hypervisor_version': 3, 'hypervisor_hostname': "hyper2", 'free_ram_mb': 5 * 1024, 'free_disk_gb': 125, 'current_workload': 2, 'running_vms': 2, 'cpu_info': 'cpu_info', 'disk_available_least': 100} ]}) def get_os_hypervisors_statistics(self, **kw): return (200, {}, {"hypervisor_statistics": { 'count': 2, 'vcpus': 8, 'memory_mb': 20 * 1024, 'local_gb': 500, 'vcpus_used': 4, 'memory_mb_used': 10 * 1024, 'local_gb_used': 250, 'free_ram_mb': 10 * 1024, 'free_disk_gb': 250, 'current_workload': 4, 'running_vms': 4, 'disk_available_least': 200, }}) def get_os_hypervisors_hyper_search(self, **kw): return (200, {}, {'hypervisors': [ {'id': 1234, 'hypervisor_hostname': 'hyper1'}, {'id': 5678, 'hypervisor_hostname': 'hyper2'} ]}) def get_os_hypervisors_hyper_servers(self, **kw): return (200, {}, {'hypervisors': [ {'id': 1234, 'hypervisor_hostname': 'hyper1', 'servers': [ {'name': 'inst1', 'uuid': 'uuid1'}, {'name': 'inst2', 'uuid': 'uuid2'} ]}, {'id': 5678, 'hypervisor_hostname': 'hyper2', 'servers': [ {'name': 'inst3', 'uuid': 'uuid3'}, {'name': 'inst4', 'uuid': 'uuid4'} ]} ]}) def get_os_hypervisors_hyper_no_servers_servers(self, **kw): return (200, {}, {'hypervisors': [{'id': 1234, 'hypervisor_hostname': 'hyper1'}]}) def get_os_hypervisors_1234(self, **kw): return (200, {}, {'hypervisor': {'id': 1234, 'service': {'id': 1, 'host': 'compute1'}, 'vcpus': 4, 'memory_mb': 10 * 1024, 'local_gb': 250, 'vcpus_used': 2, 'memory_mb_used': 5 * 1024, 'local_gb_used': 125, 'hypervisor_type': "xen", 'hypervisor_version': 3, 'hypervisor_hostname': "hyper1", 'free_ram_mb': 5 * 1024, 'free_disk_gb': 125, 'current_workload': 2, 'running_vms': 2, 'cpu_info': 'cpu_info', 'disk_available_least': 100}}) def get_os_hypervisors_1234_uptime(self, **kw): return (200, {}, {'hypervisor': {'id': 1234, 'hypervisor_hostname': "hyper1", 'uptime': "fake uptime"}}) def get_os_networks(self, **kw): return (200, {}, {'networks': [{"label": "1", "cidr": "10.0.0.0/24", 'project_id': '4ffc664c198e435e9853f2538fbcd7a7', 'id': '1'}]}) def post_os_networks(self, **kw): return (202, {}, {'network': kw}) def get_os_networks_1(self, **kw): return (200, {}, {'network': {"label": "1", "cidr": "10.0.0.0/24"}}) def delete_os_networks_networkdelete(self, **kw): return (202, {}, None) def post_os_networks_add(self, **kw): return (202, {}, None) def post_os_networks_networkdisassociate_action(self, **kw): return (202, {}, None) def get_os_fping(self, **kw): return ( 200, {}, { 'servers': [ { "id": "1", "project_id": "fake-project", "alive": True, }, { "id": "2", "project_id": "fake-project", "alive": True, }, ] } ) def get_os_fping_1(self, **kw): return ( 200, {}, { 'server': { "id": "1", "project_id": "fake-project", "alive": True, } } ) def post_os_networks_1_action(self, **kw): return (202, {}, None) def post_os_networks_networktest_action(self, **kw): return (202, {}, None) def post_os_networks_2_action(self, **kw): return (202, {}, None) def get_os_availability_zone(self, **kw): return (200, {}, {"availabilityZoneInfo": [ {"zoneName": "zone-1", "zoneState": {"available": True}, "hosts": None}, {"zoneName": "zone-2", "zoneState": {"available": False}, "hosts": None}]}) def get_os_availability_zone_detail(self, **kw): return (200, {}, {"availabilityZoneInfo": [ {"zoneName": "zone-1", "zoneState": {"available": True}, "hosts": { "fake_host-1": { "nova-compute": {"active": True, "available": True, "updated_at": datetime(2012, 12, 26, 14, 45, 25, 0)}}}}, {"zoneName": "internal", "zoneState": {"available": True}, "hosts": { "fake_host-1": { "nova-sched": { "active": True, "available": True, "updated_at": datetime(2012, 12, 26, 14, 45, 25, 0)}}, "fake_host-2": { "nova-network": { "active": True, "available": False, "updated_at": datetime(2012, 12, 26, 14, 45, 24, 0)}}}}, {"zoneName": "zone-2", "zoneState": {"available": False}, "hosts": None}]}) def get_servers_1234_os_interface(self, **kw): return (200, {}, {"interfaceAttachments": [ {"port_state": "ACTIVE", "net_id": "net-id-1", "port_id": "port-id-1", "mac_address": "aa:bb:cc:dd:ee:ff", "fixed_ips": [{"ip_address": "1.2.3.4"}], }, {"port_state": "ACTIVE", "net_id": "net-id-1", "port_id": "port-id-1", "mac_address": "aa:bb:cc:dd:ee:ff", "fixed_ips": [{"ip_address": "1.2.3.4"}], }]}) def post_servers_1234_os_interface(self, **kw): return (200, {}, {'interfaceAttachment': {}}) def delete_servers_1234_os_interface_port_id(self, **kw): return (200, {}, None) # NOTE (vkhomenko): # Volume responses was taken from: # https://wiki.openstack.org/wiki/CreateVolumeFromImage # http://jorgew.github.com/block-storage-api/content/ # GET_listDetailVolumes_v1__tenantId__volumes_detail_.html # I suppose they are outdated and should be updated after Cinder released def get_volumes_detail(self, **kw): return (200, {}, {"volumes": [ { "display_name": "Work", "display_description": "volume for work", "status": "ATTACHED", "id": "15e59938-07d5-11e1-90e3-e3dffe0c5983", "created_at": "2011-09-09T00:00:00Z", "attached": "2011-11-11T00:00:00Z", "size": 1024, "attachments": [ {"id": "3333", "links": ''}], "metadata": {}}, { "display_name": "Work2", "display_description": "volume for work2", "status": "ATTACHED", "id": "15e59938-07d5-11e1-90e3-ee32ba30feaa", "created_at": "2011-09-09T00:00:00Z", "attached": "2011-11-11T00:00:00Z", "size": 1024, "attachments": [ {"id": "2222", "links": ''}], "metadata": {}}]}) def get_volumes(self, **kw): return (200, {}, {"volumes": [ { "display_name": "Work", "display_description": "volume for work", "status": "ATTACHED", "id": "15e59938-07d5-11e1-90e3-e3dffe0c5983", "created_at": "2011-09-09T00:00:00Z", "attached": "2011-11-11T00:00:00Z", "size": 1024, "attachments": [ {"id": "3333", "links": ''}], "metadata": {}}, { "display_name": "Work2", "display_description": "volume for work2", "status": "ATTACHED", "id": "15e59938-07d5-11e1-90e3-ee32ba30feaa", "created_at": "2011-09-09T00:00:00Z", "attached": "2011-11-11T00:00:00Z", "size": 1024, "attachments": [ {"id": "2222", "links": ''}], "metadata": {}}]}) def get_volumes_15e59938_07d5_11e1_90e3_e3dffe0c5983(self, **kw): return (200, {}, { "volume": self.get_volumes_detail()[2]['volumes'][0]}) def get_volumes_15e59938_07d5_11e1_90e3_ee32ba30feaa(self, **kw): return (200, {}, { "volume": self.get_volumes_detail()[2]['volumes'][1]}) def post_volumes(self, **kw): return (200, {}, {"volume": {"status": "creating", "display_name": "vol-007", "attachments": [(0)], "availability_zone": "cinder", "created_at": "2012-08-13T10:57:17.000000", "display_description": "create volume from image", "image_id": "f4cf905f-7c58-4d7b-8314-8dd8a2d1d483", "volume_type": "None", "metadata": {}, "id": "5cb239f6-1baf-4fe1-bd78-c852cf00fa39", "size": 1}}) def delete_volumes_15e59938_07d5_11e1_90e3_e3dffe0c5983(self, **kw): return (200, {}, {}) def delete_volumes_15e59938_07d5_11e1_90e3_ee32ba30feaa(self, **kw): return (200, {}, {}) def post_servers_1234_os_volume_attachments(self, **kw): return (200, {}, {"volumeAttachment": {"device": "/dev/vdb", "volumeId": 2}}) def put_servers_1234_os_volume_attachments_Work(self, **kw): return (200, {}, {"volumeAttachment": {"volumeId": 2}}) def get_servers_1234_os_volume_attachments(self, **kw): return (200, {}, {"volumeAttachments": [ {"display_name": "Work", "display_description": "volume for work", "status": "ATTACHED", "id": "15e59938-07d5-11e1-90e3-e3dffe0c5983", "created_at": "2011-09-09T00:00:00Z", "attached": "2011-11-11T00:00:00Z", "size": 1024, "attachments": [ {"id": "3333", "links": ''}], "metadata": {}}]}) def get_servers_1234_os_volume_attachments_Work(self, **kw): return (200, {}, {"volumeAttachment": {"display_name": "Work", "display_description": "volume for work", "status": "ATTACHED", "id": "15e59938-07d5-11e1-90e3-e3dffe0c5983", "created_at": "2011-09-09T00:00:00Z", "attached": "2011-11-11T00:00:00Z", "size": 1024, "attachments": [ {"id": "3333", "links": ''}], "metadata": {}}}) def delete_servers_1234_os_volume_attachments_Work(self, **kw): return (200, {}, {}) def get_servers_1234_os_instance_actions(self, **kw): return (200, {}, {"instanceActions": [{"instance_uuid": "1234", "user_id": "b968c25e04ab405f9fe4e6ca54cce9a5", "start_time": "2013-03-25T13:45:09.000000", "request_id": "req-abcde12345", "action": "create", "message": None, "project_id": "04019601fe3648c0abd4f4abfb9e6106"}]}) def get_servers_1234_os_instance_actions_req_abcde12345(self, **kw): return (200, {}, {"instanceAction": {"instance_uuid": "1234", "user_id": "b968c25e04ab405f9fe4e6ca54cce9a5", "start_time": "2013-03-25T13:45:09.000000", "request_id": "req-abcde12345", "action": "create", "message": None, "project_id": "04019601fe3648c0abd4f4abfb9e6106"}}) def post_servers_uuid1_action(self, **kw): return 202, {}, {} def post_servers_uuid2_action(self, **kw): return 202, {}, {} def post_servers_uuid3_action(self, **kw): return 202, {}, {} def post_servers_uuid4_action(self, **kw): return 202, {}, {} def get_os_cells_child_cell(self, **kw): cell = {'cell': { 'username': 'cell1_user', 'name': 'cell1', 'rpc_host': '10.0.1.10', 'info': { 'username': 'cell1_user', 'rpc_host': '10.0.1.10', 'type': 'child', 'name': 'cell1', 'rpc_port': 5673 }, 'type': 'child', 'rpc_port': 5673, 'loaded': True }} return (200, {}, cell) def get_os_cells_capacities(self, **kw): cell_capacities_response = {"cell": {"capacities": {"ram_free": { "units_by_mb": {"8192": 0, "512": 13, "4096": 1, "2048": 3, "16384": 0}, "total_mb": 7680}, "disk_free": { "units_by_mb": {"81920": 11, "20480": 46, "40960": 23, "163840": 5, "0": 0}, "total_mb": 1052672}}}} return (200, {}, cell_capacities_response) def get_os_cells_child_cell_capacities(self, **kw): return self.get_os_cells_capacities() def get_os_migrations(self, **kw): migrations = {'migrations': [{ "created_at": "2012-10-29T13:42:02.000000", "dest_compute": "compute2", "dest_host": "1.2.3.4", "dest_node": "node2", "id": 1234, "instance_uuid": "instance_id_123", "new_instance_type_id": 2, "old_instance_type_id": 1, "source_compute": "compute1", "source_node": "node1", "status": "Done", "updated_at": "2012-10-29T13:42:02.000000" }]} return (200, {}, migrations) def post_os_server_external_events(self, **kw): return (200, {}, {'events': [ {'name': 'network-changed', 'server_uuid': '1234'}]}) python-novaclient-2.17.0/novaclient/tests/v1_1/test_agents.py0000664000175400017540000000553112306432770025374 0ustar jenkinsjenkins00000000000000# Copyright 2012 IBM Corp. # 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 novaclient.tests import utils from novaclient.tests.v1_1 import fakes from novaclient.v1_1 import agents class AgentsTest(utils.TestCase): def setUp(self): super(AgentsTest, self).setUp() self.cs = self._get_fake_client() self.agent_type = self._get_agent_type() def _get_fake_client(self): return fakes.FakeClient() def _get_agent_type(self): return agents.Agent def test_list_agents(self): ags = self.cs.agents.list() self.cs.assert_called('GET', '/os-agents') for a in ags: self.assertIsInstance(a, self.agent_type) self.assertEqual(a.hypervisor, 'kvm') def test_list_agents_with_hypervisor(self): ags = self.cs.agents.list('xen') self.cs.assert_called('GET', '/os-agents?hypervisor=xen') for a in ags: self.assertIsInstance(a, self.agent_type) self.assertEqual(a.hypervisor, 'xen') def test_agents_create(self): ag = self.cs.agents.create('win', 'x86', '7.0', '/xxx/xxx/xxx', 'add6bb58e139be103324d04d82d8f546', 'xen') body = {'agent': { 'url': '/xxx/xxx/xxx', 'hypervisor': 'xen', 'md5hash': 'add6bb58e139be103324d04d82d8f546', 'version': '7.0', 'architecture': 'x86', 'os': 'win'}} self.cs.assert_called('POST', '/os-agents', body) self.assertEqual(1, ag._info.copy()['id']) def test_agents_delete(self): self.cs.agents.delete('1') self.cs.assert_called('DELETE', '/os-agents/1') def _build_example_update_body(self): return {"para": { "url": "/yyy/yyyy/yyyy", "version": "8.0", "md5hash": "add6bb58e139be103324d04d82d8f546"}} def test_agents_modify(self): ag = self.cs.agents.update('1', '8.0', '/yyy/yyyy/yyyy', 'add6bb58e139be103324d04d82d8f546') body = self._build_example_update_body() self.cs.assert_called('PUT', '/os-agents/1', body) self.assertEqual(1, ag.id) python-novaclient-2.17.0/novaclient/tests/v1_1/utils.py0000664000175400017540000000254112306432770024212 0ustar jenkinsjenkins00000000000000# # 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 nose.tools import ok_ def fail(msg): raise AssertionError(msg) def assert_in(thing, seq, msg=None): msg = msg or "'%s' not found in %s" % (thing, seq) ok_(thing in seq, msg) def assert_not_in(thing, seq, msg=None): msg = msg or "unexpected '%s' found in %s" % (thing, seq) ok_(thing not in seq, msg) def assert_has_keys(dict, required=[], optional=[]): keys = dict.keys() for k in required: assert_in(k, keys, "required key %s missing from %s" % (k, dict)) allowed_keys = set(required) | set(optional) extra_keys = set(keys).difference(allowed_keys) if extra_keys: fail("found unexpected keys: %s" % list(extra_keys)) def assert_isinstance(thing, kls): ok_(isinstance(thing, kls), "%s is not an instance of %s" % (thing, kls)) python-novaclient-2.17.0/novaclient/tests/v1_1/test_services.py0000664000175400017540000000760512306432770025742 0ustar jenkinsjenkins00000000000000# Copyright 2012 IBM Corp. # 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 novaclient.tests import utils from novaclient.tests.v1_1 import fakes from novaclient.v1_1 import services class ServicesTest(utils.TestCase): def setUp(self): super(ServicesTest, self).setUp() self.cs = self._get_fake_client() self.service_type = self._get_service_type() def _get_fake_client(self): return fakes.FakeClient() def _get_service_type(self): return services.Service def test_list_services(self): svs = self.cs.services.list() self.cs.assert_called('GET', '/os-services') for s in svs: self.assertIsInstance(s, self._get_service_type()) self.assertEqual(s.binary, 'nova-compute') self.assertEqual(s.host, 'host1') self.assertTrue(str(s).startswith(' 0) for r in all_exts: self.assertTrue(len(r.summary) > 0) python-novaclient-2.17.0/novaclient/tests/v1_1/contrib/test_assisted_volume_snapshots.py0000664000175400017540000000266512306432770033070 0ustar jenkinsjenkins00000000000000# Copyright (C) 2013, 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. """ Assisted volume snapshots - to be used by Cinder and not end users. """ from novaclient import extension from novaclient.tests import utils from novaclient.tests.v1_1.contrib import fakes from novaclient.v1_1.contrib import assisted_volume_snapshots as assisted_snaps extensions = [ extension.Extension(assisted_snaps.__name__.split(".")[-1], assisted_snaps), ] cs = fakes.FakeClient(extensions=extensions) class AssistedVolumeSnapshotsTestCase(utils.TestCase): def test_create_snap(self): res = cs.assisted_volume_snapshots.create('1', {}) cs.assert_called('POST', '/os-assisted-volume-snapshots') def test_delete_snap(self): res = cs.assisted_volume_snapshots.delete('x', {}) cs.assert_called('DELETE', '/os-assisted-volume-snapshots/x?delete_info={}') python-novaclient-2.17.0/novaclient/tests/v1_1/contrib/test_cells.py0000664000175400017540000000270212306432770026652 0ustar jenkinsjenkins00000000000000# Copyright 2013 Rackspace Hosting # 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 novaclient import extension from novaclient.tests import utils from novaclient.tests.v1_1.contrib import fakes from novaclient.v1_1.contrib import cells extensions = [ extension.Extension(cells.__name__.split(".")[-1], cells), ] cs = fakes.FakeClient(extensions=extensions) class CellsExtensionTests(utils.TestCase): def test_get_cells(self): cell_name = 'child_cell' cs.cells.get(cell_name) cs.assert_called('GET', '/os-cells/%s' % cell_name) def test_get_capacities_for_a_given_cell(self): cell_name = 'child_cell' cs.cells.capacities(cell_name) cs.assert_called('GET', '/os-cells/%s/capacities' % cell_name) def test_get_capacities_for_all_cells(self): cs.cells.capacities() cs.assert_called('GET', '/os-cells/capacities') python-novaclient-2.17.0/novaclient/tests/v1_1/contrib/fakes.py0000664000175400017540000001207312306432771025605 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # # 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 novaclient.tests.v1_1 import fakes from novaclient.v1_1 import client class FakeClient(fakes.FakeClient): def __init__(self, *args, **kwargs): client.Client.__init__(self, 'username', 'password', 'project_id', 'auth_url', extensions=kwargs.get('extensions')) self.client = FakeHTTPClient(**kwargs) class FakeHTTPClient(fakes.FakeHTTPClient): def get_os_tenant_networks(self): return (200, {}, {'networks': [{"label": "1", "cidr": "10.0.0.0/24", 'project_id': '4ffc664c198e435e9853f2538fbcd7a7', 'id': '1'}]}) def get_os_tenant_networks_1(self, **kw): return (200, {}, {'network': {"label": "1", "cidr": "10.0.0.0/24", 'project_id': '4ffc664c198e435e9853f2538fbcd7a7', 'id': '1'}}) def post_os_tenant_networks(self, **kw): return (201, {}, {'network': {"label": "1", "cidr": "10.0.0.0/24", 'project_id': '4ffc664c198e435e9853f2538fbcd7a7', 'id': '1'}}) def delete_os_tenant_networks_1(self, **kw): return (204, {}, None) def get_os_baremetal_nodes(self, **kw): return ( 200, {}, { 'nodes': [ { "id": 1, "instance_uuid": None, "interfaces": [], "cpus": 2, "local_gb": 10, "memory_mb": 5, "pm_address": "2.3.4.5", "pm_user": "pmuser", "pm_password": "pmpass", "prov_mac_address": "aa:bb:cc:dd:ee:ff", "prov_vlan_id": 1, "service_host": "somehost", "terminal_port": 8080, } ] } ) def get_os_baremetal_nodes_1(self, **kw): return ( 200, {}, { 'node': { "id": 1, "instance_uuid": None, "pm_address": "1.2.3.4", "interfaces": [], "cpus": 2, "local_gb": 10, "memory_mb": 5, "pm_user": "pmuser", "pm_password": "pmpass", "prov_mac_address": "aa:bb:cc:dd:ee:ff", "prov_vlan_id": 1, "service_host": "somehost", "terminal_port": 8080, } } ) def post_os_baremetal_nodes(self, **kw): return ( 200, {}, { 'node': { "id": 1, "instance_uuid": None, "cpus": 2, "local_gb": 10, "memory_mb": 5, "pm_address": "2.3.4.5", "pm_user": "pmuser", "pm_password": "pmpass", "prov_mac_address": "aa:bb:cc:dd:ee:ff", "prov_vlan_id": 1, "service_host": "somehost", "terminal_port": 8080, } } ) def delete_os_baremetal_nodes_1(self, **kw): return (202, {}, {}) def post_os_baremetal_nodes_1_action(self, **kw): body = kw['body'] action = list(body)[0] if action == "add_interface": return ( 200, {}, { 'interface': { "id": 2, "address": "bb:cc:dd:ee:ff:aa", "datapath_id": 1, "port_no": 2, } } ) elif action == "remove_interface": return (202, {}, {}) else: return (500, {}, {}) def post_os_assisted_volume_snapshots(self, **kw): return (202, {}, {'snapshot': {'id': 'blah', 'volumeId': '1'}}) def delete_os_assisted_volume_snapshots_x(self, **kw): return (202, {}, {}) def post_os_server_external_events(self, **kw): return (200, {}, {'events': [ {'name': 'test-event', 'status': 'completed', 'tag': 'tag', 'server_uuid': 'fake-uuid1'}, {'name': 'test-event', 'status': 'completed', 'tag': 'tag', 'server_uuid': 'fake-uuid2'}]}) python-novaclient-2.17.0/novaclient/tests/v1_1/contrib/test_tenant_networks.py0000664000175400017540000000321612306432770030776 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # 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 novaclient import extension from novaclient.v1_1.contrib import tenant_networks from novaclient.tests import utils from novaclient.tests.v1_1.contrib import fakes extensions = [ extension.Extension(tenant_networks.__name__.split(".")[-1], tenant_networks), ] cs = fakes.FakeClient(extensions=extensions) class TenantNetworkExtensionTests(utils.TestCase): def test_list_tenant_networks(self): nets = cs.tenant_networks.list() cs.assert_called('GET', '/os-tenant-networks') self.assertTrue(len(nets) > 0) def test_get_tenant_network(self): cs.tenant_networks.get(1) cs.assert_called('GET', '/os-tenant-networks/1') def test_create_tenant_networks(self): cs.tenant_networks.create(label="net", cidr="10.0.0.0/24") cs.assert_called('POST', '/os-tenant-networks') def test_delete_tenant_networks(self): cs.tenant_networks.delete(1) cs.assert_called('DELETE', '/os-tenant-networks/1') python-novaclient-2.17.0/novaclient/tests/v1_1/contrib/test_server_external_events.py0000664000175400017540000000311712306432771032346 0ustar jenkinsjenkins00000000000000# Copyright (C) 2014, 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. """ External event triggering for servers, not to be used by users. """ from novaclient import extension from novaclient.tests import utils from novaclient.tests.v1_1.contrib import fakes from novaclient.v1_1.contrib import server_external_events as ext_events extensions = [ extension.Extension(ext_events.__name__.split(".")[-1], ext_events), ] cs = fakes.FakeClient(extensions=extensions) class ServerExternalEventsTestCase(utils.TestCase): def test_external_event(self): events = [{'server_uuid': 'fake-uuid1', 'name': 'test-event', 'status': 'completed', 'tag': 'tag'}, {'server_uuid': 'fake-uuid2', 'name': 'test-event', 'status': 'completed', 'tag': 'tag'}] result = cs.server_external_events.create(events) self.assertEqual(events, result) cs.assert_called('POST', '/os-server-external-events') python-novaclient-2.17.0/novaclient/tests/v1_1/contrib/test_baremetal.py0000664000175400017540000000446512306432770027514 0ustar jenkinsjenkins00000000000000# Copyright 2013 Hewlett-Packard Development Company, L.P. # 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 novaclient import extension from novaclient.v1_1.contrib import baremetal from novaclient.tests import utils from novaclient.tests.v1_1.contrib import fakes extensions = [ extension.Extension(baremetal.__name__.split(".")[-1], baremetal), ] cs = fakes.FakeClient(extensions=extensions) class BaremetalExtensionTest(utils.TestCase): def test_list_nodes(self): nl = cs.baremetal.list() cs.assert_called('GET', '/os-baremetal-nodes') for n in nl: self.assertIsInstance(n, baremetal.BareMetalNode) def test_get_node(self): n = cs.baremetal.get(1) cs.assert_called('GET', '/os-baremetal-nodes/1') self.assertIsInstance(n, baremetal.BareMetalNode) def test_create_node(self): n = cs.baremetal.create("service_host", 1, 1024, 2048, "aa:bb:cc:dd:ee:ff") cs.assert_called('POST', '/os-baremetal-nodes') self.assertIsInstance(n, baremetal.BareMetalNode) def test_delete_node(self): n = cs.baremetal.get(1) cs.baremetal.delete(n) cs.assert_called('DELETE', '/os-baremetal-nodes/1') def test_node_add_interface(self): i = cs.baremetal.add_interface(1, "bb:cc:dd:ee:ff:aa", 1, 2) cs.assert_called('POST', '/os-baremetal-nodes/1/action') self.assertIsInstance(i, baremetal.BareMetalNodeInterface) def test_node_remove_interface(self): cs.baremetal.remove_interface(1, "bb:cc:dd:ee:ff:aa") cs.assert_called('POST', '/os-baremetal-nodes/1/action') def test_node_list_interfaces(self): cs.baremetal.list_interfaces(1) cs.assert_called('GET', '/os-baremetal-nodes/1') python-novaclient-2.17.0/novaclient/tests/v1_1/contrib/test_instance_actions.py0000664000175400017540000000275512306432770031104 0ustar jenkinsjenkins00000000000000# Copyright 2013 Rackspace Hosting # 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 novaclient import extension from novaclient.v1_1.contrib import instance_action from novaclient.tests import utils from novaclient.tests.v1_1.contrib import fakes extensions = [ extension.Extension(instance_action.__name__.split(".")[-1], instance_action), ] cs = fakes.FakeClient(extensions=extensions) class InstanceActionExtensionTests(utils.TestCase): def test_list_instance_actions(self): server_uuid = '1234' cs.instance_action.list(server_uuid) cs.assert_called('GET', '/servers/%s/os-instance-actions' % server_uuid) def test_get_instance_action(self): server_uuid = '1234' request_id = 'req-abcde12345' cs.instance_action.get(server_uuid, request_id) cs.assert_called('GET', '/servers/%s/os-instance-actions/%s' % (server_uuid, request_id)) python-novaclient-2.17.0/novaclient/tests/v1_1/contrib/test_migrations.py0000664000175400017540000000267612306432770027736 0ustar jenkinsjenkins00000000000000# 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 novaclient import extension from novaclient.tests import utils from novaclient.tests.v1_1 import fakes from novaclient.v1_1.contrib import migrations extensions = [ extension.Extension(migrations.__name__.split(".")[-1], migrations), ] cs = fakes.FakeClient(extensions=extensions) class MigrationsTest(utils.TestCase): def test_list_migrations(self): ml = cs.migrations.list() cs.assert_called('GET', '/os-migrations') for m in ml: self.assertIsInstance(m, migrations.Migration) def test_list_migrations_with_filters(self): ml = cs.migrations.list('host1', 'finished', 'child1') cs.assert_called('GET', '/os-migrations?cell_name=child1&host=host1' '&status=finished') for m in ml: self.assertIsInstance(m, migrations.Migration) python-novaclient-2.17.0/novaclient/tests/v1_1/contrib/__init__.py0000664000175400017540000000000012306432770026235 0ustar jenkinsjenkins00000000000000python-novaclient-2.17.0/novaclient/tests/v1_1/test_flavors.py0000664000175400017540000002053112306432770025564 0ustar jenkinsjenkins00000000000000# Copyright (c) 2013, OpenStack # 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 novaclient import exceptions from novaclient.tests import utils from novaclient.tests.v1_1 import fakes from novaclient.v1_1 import flavors class FlavorsTest(utils.TestCase): def setUp(self): super(FlavorsTest, self).setUp() self.cs = self._get_fake_client() self.flavor_type = self._get_flavor_type() def _get_fake_client(self): return fakes.FakeClient() def _get_flavor_type(self): return flavors.Flavor def test_list_flavors(self): fl = self.cs.flavors.list() self.cs.assert_called('GET', '/flavors/detail') for flavor in fl: self.assertIsInstance(flavor, self.flavor_type) def test_list_flavors_undetailed(self): fl = self.cs.flavors.list(detailed=False) self.cs.assert_called('GET', '/flavors') for flavor in fl: self.assertIsInstance(flavor, self.flavor_type) def test_list_flavors_is_public_none(self): fl = self.cs.flavors.list(is_public=None) self.cs.assert_called('GET', '/flavors/detail?is_public=None') for flavor in fl: self.assertIsInstance(flavor, self.flavor_type) def test_list_flavors_is_public_false(self): fl = self.cs.flavors.list(is_public=False) self.cs.assert_called('GET', '/flavors/detail?is_public=False') for flavor in fl: self.assertIsInstance(flavor, self.flavor_type) def test_list_flavors_is_public_true(self): fl = self.cs.flavors.list(is_public=True) self.cs.assert_called('GET', '/flavors/detail') for flavor in fl: self.assertIsInstance(flavor, self.flavor_type) def test_get_flavor_details(self): f = self.cs.flavors.get(1) self.cs.assert_called('GET', '/flavors/1') self.assertIsInstance(f, self.flavor_type) self.assertEqual(f.ram, 256) self.assertEqual(f.disk, 10) self.assertEqual(f.ephemeral, 10) self.assertEqual(f.is_public, True) def test_get_flavor_details_alphanum_id(self): f = self.cs.flavors.get('aa1') self.cs.assert_called('GET', '/flavors/aa1') self.assertIsInstance(f, self.flavor_type) self.assertEqual(f.ram, 128) self.assertEqual(f.disk, 0) self.assertEqual(f.ephemeral, 0) self.assertEqual(f.is_public, True) def test_get_flavor_details_diablo(self): f = self.cs.flavors.get(3) self.cs.assert_called('GET', '/flavors/3') self.assertIsInstance(f, self.flavor_type) self.assertEqual(f.ram, 256) self.assertEqual(f.disk, 10) self.assertEqual(f.ephemeral, 'N/A') self.assertEqual(f.is_public, 'N/A') def test_find(self): f = self.cs.flavors.find(ram=256) self.cs.assert_called('GET', '/flavors/detail') self.assertEqual(f.name, '256 MB Server') f = self.cs.flavors.find(disk=0) self.assertEqual(f.name, '128 MB Server') self.assertRaises(exceptions.NotFound, self.cs.flavors.find, disk=12345) def _create_body(self, name, ram, vcpus, disk, ephemeral, id, swap, rxtx_factor, is_public): return { "flavor": { "name": name, "ram": ram, "vcpus": vcpus, "disk": disk, "OS-FLV-EXT-DATA:ephemeral": ephemeral, "id": id, "swap": swap, "rxtx_factor": rxtx_factor, "os-flavor-access:is_public": is_public, } } def test_create(self): f = self.cs.flavors.create("flavorcreate", 512, 1, 10, 1234, ephemeral=10, is_public=False) body = self._create_body("flavorcreate", 512, 1, 10, 10, 1234, 0, 1.0, False) self.cs.assert_called('POST', '/flavors', body) self.assertIsInstance(f, self.flavor_type) def test_create_with_id_as_string(self): flavor_id = 'foobar' f = self.cs.flavors.create("flavorcreate", 512, 1, 10, flavor_id, ephemeral=10, is_public=False) body = self._create_body("flavorcreate", 512, 1, 10, 10, flavor_id, 0, 1.0, False) self.cs.assert_called('POST', '/flavors', body) self.assertIsInstance(f, self.flavor_type) def test_create_ephemeral_ispublic_defaults(self): f = self.cs.flavors.create("flavorcreate", 512, 1, 10, 1234) body = self._create_body("flavorcreate", 512, 1, 10, 0, 1234, 0, 1.0, True) self.cs.assert_called('POST', '/flavors', body) self.assertIsInstance(f, self.flavor_type) def test_invalid_parameters_create(self): self.assertRaises(exceptions.CommandError, self.cs.flavors.create, "flavorcreate", "invalid", 1, 10, 1234, swap=0, ephemeral=0, rxtx_factor=1.0, is_public=True) self.assertRaises(exceptions.CommandError, self.cs.flavors.create, "flavorcreate", 512, "invalid", 10, 1234, swap=0, ephemeral=0, rxtx_factor=1.0, is_public=True) self.assertRaises(exceptions.CommandError, self.cs.flavors.create, "flavorcreate", 512, 1, "invalid", 1234, swap=0, ephemeral=0, rxtx_factor=1.0, is_public=True) self.assertRaises(exceptions.CommandError, self.cs.flavors.create, "flavorcreate", 512, 1, 10, 1234, swap="invalid", ephemeral=0, rxtx_factor=1.0, is_public=True) self.assertRaises(exceptions.CommandError, self.cs.flavors.create, "flavorcreate", 512, 1, 10, 1234, swap=0, ephemeral="invalid", rxtx_factor=1.0, is_public=True) self.assertRaises(exceptions.CommandError, self.cs.flavors.create, "flavorcreate", 512, 1, 10, 1234, swap=0, ephemeral=0, rxtx_factor="invalid", is_public=True) self.assertRaises(exceptions.CommandError, self.cs.flavors.create, "flavorcreate", 512, 1, 10, 1234, swap=0, ephemeral=0, rxtx_factor=1.0, is_public='invalid') def test_delete(self): self.cs.flavors.delete("flavordelete") self.cs.assert_called('DELETE', '/flavors/flavordelete') def test_delete_with_flavor_instance(self): f = self.cs.flavors.get(2) self.cs.flavors.delete(f) self.cs.assert_called('DELETE', '/flavors/2') def test_delete_with_flavor_instance_method(self): f = self.cs.flavors.get(2) f.delete() self.cs.assert_called('DELETE', '/flavors/2') def test_set_keys(self): f = self.cs.flavors.get(1) f.set_keys({'k1': 'v1'}) self.cs.assert_called('POST', '/flavors/1/os-extra_specs', {"extra_specs": {'k1': 'v1'}}) def test_set_with_valid_keys(self): valid_keys = ['key4', 'month.price', 'I-Am:AK-ey.44-', 'key with spaces and _'] f = self.cs.flavors.get(4) for key in valid_keys: f.set_keys({key: 'v4'}) self.cs.assert_called('POST', '/flavors/4/os-extra_specs', {"extra_specs": {key: 'v4'}}) def test_set_with_invalid_keys(self): invalid_keys = ['/1', '?1', '%1', '<', '>'] f = self.cs.flavors.get(1) for key in invalid_keys: self.assertRaises(exceptions.CommandError, f.set_keys, {key: 'v1'}) def test_unset_keys(self): f = self.cs.flavors.get(1) f.unset_keys(['k1']) self.cs.assert_called('DELETE', '/flavors/1/os-extra_specs/k1') python-novaclient-2.17.0/novaclient/tests/v1_1/test_keypairs.py0000664000175400017540000000444712306432770025747 0ustar jenkinsjenkins00000000000000# # 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 novaclient.tests import utils from novaclient.tests.v1_1 import fakes from novaclient.v1_1 import keypairs class KeypairsTest(utils.TestCase): def setUp(self): super(KeypairsTest, self).setUp() self.cs = self._get_fake_client() self.keypair_type = self._get_keypair_type() self.keypair_prefix = keypairs.KeypairManager.keypair_prefix def _get_fake_client(self): return fakes.FakeClient() def _get_keypair_type(self): return keypairs.Keypair def test_get_keypair(self): kp = self.cs.keypairs.get('test') self.cs.assert_called('GET', '/%s/test' % self.keypair_prefix) self.assertIsInstance(kp, keypairs.Keypair) self.assertEqual(kp.name, 'test') def test_list_keypairs(self): kps = self.cs.keypairs.list() self.cs.assert_called('GET', '/%s' % self.keypair_prefix) [self.assertIsInstance(kp, keypairs.Keypair) for kp in kps] def test_delete_keypair(self): kp = self.cs.keypairs.list()[0] kp.delete() self.cs.assert_called('DELETE', '/%s/test' % self.keypair_prefix) self.cs.keypairs.delete('test') self.cs.assert_called('DELETE', '/%s/test' % self.keypair_prefix) self.cs.keypairs.delete(kp) self.cs.assert_called('DELETE', '/%s/test' % self.keypair_prefix) def test_create_keypair(self): kp = self.cs.keypairs.create("foo") self.cs.assert_called('POST', '/%s' % self.keypair_prefix) self.assertIsInstance(kp, keypairs.Keypair) def test_import_keypair(self): kp = self.cs.keypairs.create("foo", "fake-public-key") self.cs.assert_called('POST', '/%s' % self.keypair_prefix) self.assertIsInstance(kp, keypairs.Keypair) python-novaclient-2.17.0/novaclient/tests/v1_1/test_floating_ips.py0000664000175400017540000000347712306432770026600 0ustar jenkinsjenkins00000000000000# Copyright (c) 2011 X.commerce, a business unit of eBay Inc. # # 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 novaclient.tests import utils from novaclient.tests.v1_1 import fakes from novaclient.v1_1 import floating_ips cs = fakes.FakeClient() class FloatingIPsTest(utils.TestCase): def test_list_floating_ips(self): fl = cs.floating_ips.list() cs.assert_called('GET', '/os-floating-ips') [self.assertIsInstance(f, floating_ips.FloatingIP) for f in fl] def test_delete_floating_ip(self): fl = cs.floating_ips.list()[0] fl.delete() cs.assert_called('DELETE', '/os-floating-ips/1') cs.floating_ips.delete(1) cs.assert_called('DELETE', '/os-floating-ips/1') cs.floating_ips.delete(fl) cs.assert_called('DELETE', '/os-floating-ips/1') def test_create_floating_ip(self): fl = cs.floating_ips.create() cs.assert_called('POST', '/os-floating-ips') self.assertIsNone(fl.pool) self.assertIsInstance(fl, floating_ips.FloatingIP) def test_create_floating_ip_with_pool(self): fl = cs.floating_ips.create('foo') cs.assert_called('POST', '/os-floating-ips') self.assertEqual(fl.pool, 'nova') self.assertIsInstance(fl, floating_ips.FloatingIP) python-novaclient-2.17.0/novaclient/tests/v1_1/test_auth.py0000664000175400017540000004352512306432770025061 0ustar jenkinsjenkins00000000000000# # 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 copy import json import mock import requests from novaclient import exceptions from novaclient.tests import utils from novaclient.v1_1 import client class AuthenticateAgainstKeystoneTests(utils.TestCase): def test_authenticate_success(self): cs = client.Client("username", "password", "project_id", "auth_url/v2.0", service_type='compute') resp = { "access": { "token": { "expires": "12345", "id": "FAKE_ID", "tenant": { "id": "FAKE_TENANT_ID", } }, "serviceCatalog": [ { "type": "compute", "endpoints": [ { "region": "RegionOne", "adminURL": "http://localhost:8774/v1.1", "internalURL": "http://localhost:8774/v1.1", "publicURL": "http://localhost:8774/v1.1/", }, ], }, ], }, } auth_response = utils.TestResponse({ "status_code": 200, "text": json.dumps(resp), }) mock_request = mock.Mock(return_value=(auth_response)) @mock.patch.object(requests.Session, "request", mock_request) def test_auth_call(): cs.client.authenticate() headers = { 'User-Agent': cs.client.USER_AGENT, 'Content-Type': 'application/json', 'Accept': 'application/json', } body = { 'auth': { 'passwordCredentials': { 'username': cs.client.user, 'password': cs.client.password, }, 'tenantName': cs.client.projectid, }, } token_url = cs.client.auth_url + "/tokens" mock_request.assert_called_with( "POST", token_url, headers=headers, data=json.dumps(body), allow_redirects=True, **self.TEST_REQUEST_BASE) endpoints = resp["access"]["serviceCatalog"][0]['endpoints'] public_url = endpoints[0]["publicURL"].rstrip('/') self.assertEqual(cs.client.management_url, public_url) token_id = resp["access"]["token"]["id"] self.assertEqual(cs.client.auth_token, token_id) test_auth_call() def test_authenticate_failure(self): cs = client.Client("username", "password", "project_id", "auth_url/v2.0") resp = {"unauthorized": {"message": "Unauthorized", "code": "401"}} auth_response = utils.TestResponse({ "status_code": 401, "text": json.dumps(resp), }) mock_request = mock.Mock(return_value=(auth_response)) @mock.patch.object(requests.Session, "request", mock_request) def test_auth_call(): self.assertRaises(exceptions.Unauthorized, cs.client.authenticate) test_auth_call() def test_v1_auth_redirect(self): cs = client.Client("username", "password", "project_id", "auth_url/v1.0", service_type='compute') dict_correct_response = { "access": { "token": { "expires": "12345", "id": "FAKE_ID", "tenant": { "id": "FAKE_TENANT_ID", } }, "serviceCatalog": [ { "type": "compute", "endpoints": [ { "adminURL": "http://localhost:8774/v1.1", "region": "RegionOne", "internalURL": "http://localhost:8774/v1.1", "publicURL": "http://localhost:8774/v1.1/", }, ], }, ], }, } correct_response = json.dumps(dict_correct_response) dict_responses = [ {"headers": {'location': 'http://127.0.0.1:5001'}, "status_code": 305, "text": "Use proxy"}, # Configured on admin port, nova redirects to v2.0 port. # When trying to connect on it, keystone auth succeed by v1.0 # protocol (through headers) but tokens are being returned in # body (looks like keystone bug). Leaved for compatibility. {"headers": {}, "status_code": 200, "text": correct_response}, {"headers": {}, "status_code": 200, "text": correct_response} ] responses = [(utils.TestResponse(resp)) for resp in dict_responses] def side_effect(*args, **kwargs): return responses.pop(0) mock_request = mock.Mock(side_effect=side_effect) @mock.patch.object(requests.Session, "request", mock_request) def test_auth_call(): cs.client.authenticate() headers = { 'User-Agent': cs.client.USER_AGENT, 'Content-Type': 'application/json', 'Accept': 'application/json', } body = { 'auth': { 'passwordCredentials': { 'username': cs.client.user, 'password': cs.client.password, }, 'tenantName': cs.client.projectid, }, } token_url = cs.client.auth_url + "/tokens" kwargs = copy.copy(self.TEST_REQUEST_BASE) kwargs['headers'] = headers kwargs['data'] = json.dumps(body) mock_request.assert_called_with( "POST", token_url, allow_redirects=True, **kwargs) resp = dict_correct_response endpoints = resp["access"]["serviceCatalog"][0]['endpoints'] public_url = endpoints[0]["publicURL"].rstrip('/') self.assertEqual(cs.client.management_url, public_url) token_id = resp["access"]["token"]["id"] self.assertEqual(cs.client.auth_token, token_id) test_auth_call() def test_v2_auth_redirect(self): cs = client.Client("username", "password", "project_id", "auth_url/v2.0", service_type='compute') dict_correct_response = { "access": { "token": { "expires": "12345", "id": "FAKE_ID", "tenant": { "id": "FAKE_TENANT_ID", } }, "serviceCatalog": [ { "type": "compute", "endpoints": [ { "adminURL": "http://localhost:8774/v1.1", "region": "RegionOne", "internalURL": "http://localhost:8774/v1.1", "publicURL": "http://localhost:8774/v1.1/", }, ], }, ], }, } correct_response = json.dumps(dict_correct_response) dict_responses = [ {"headers": {'location': 'http://127.0.0.1:5001'}, "status_code": 305, "text": "Use proxy"}, # Configured on admin port, nova redirects to v2.0 port. # When trying to connect on it, keystone auth succeed by v1.0 # protocol (through headers) but tokens are being returned in # body (looks like keystone bug). Leaved for compatibility. {"headers": {}, "status_code": 200, "text": correct_response}, {"headers": {}, "status_code": 200, "text": correct_response} ] responses = [(utils.TestResponse(resp)) for resp in dict_responses] def side_effect(*args, **kwargs): return responses.pop(0) mock_request = mock.Mock(side_effect=side_effect) @mock.patch.object(requests.Session, "request", mock_request) def test_auth_call(): cs.client.authenticate() headers = { 'User-Agent': cs.client.USER_AGENT, 'Content-Type': 'application/json', 'Accept': 'application/json', } body = { 'auth': { 'passwordCredentials': { 'username': cs.client.user, 'password': cs.client.password, }, 'tenantName': cs.client.projectid, }, } token_url = cs.client.auth_url + "/tokens" kwargs = copy.copy(self.TEST_REQUEST_BASE) kwargs['headers'] = headers kwargs['data'] = json.dumps(body) mock_request.assert_called_with( "POST", token_url, allow_redirects=True, **kwargs) resp = dict_correct_response endpoints = resp["access"]["serviceCatalog"][0]['endpoints'] public_url = endpoints[0]["publicURL"].rstrip('/') self.assertEqual(cs.client.management_url, public_url) token_id = resp["access"]["token"]["id"] self.assertEqual(cs.client.auth_token, token_id) test_auth_call() def test_ambiguous_endpoints(self): cs = client.Client("username", "password", "project_id", "auth_url/v2.0", service_type='compute') resp = { "access": { "token": { "expires": "12345", "id": "FAKE_ID", "tenant": { "id": "FAKE_TENANT_ID", } }, "serviceCatalog": [ { "adminURL": "http://localhost:8774/v1.1", "type": "compute", "name": "Compute CLoud", "endpoints": [ { "region": "RegionOne", "internalURL": "http://localhost:8774/v1.1", "publicURL": "http://localhost:8774/v1.1/", }, ], }, { "adminURL": "http://localhost:8774/v1.1", "type": "compute", "name": "Hyper-compute Cloud", "endpoints": [ { "internalURL": "http://localhost:8774/v1.1", "publicURL": "http://localhost:8774/v1.1/", }, ], }, ], }, } auth_response = utils.TestResponse({ "status_code": 200, "text": json.dumps(resp), }) mock_request = mock.Mock(return_value=(auth_response)) @mock.patch.object(requests.Session, "request", mock_request) def test_auth_call(): self.assertRaises(exceptions.AmbiguousEndpoints, cs.client.authenticate) test_auth_call() def test_authenticate_with_token_success(self): cs = client.Client("username", None, "project_id", "auth_url/v2.0", service_type='compute') cs.client.auth_token = "FAKE_ID" resp = { "access": { "token": { "expires": "12345", "id": "FAKE_ID", "tenant": { "id": "FAKE_TENANT_ID", } }, "serviceCatalog": [ { "type": "compute", "endpoints": [ { "region": "RegionOne", "adminURL": "http://localhost:8774/v1.1", "internalURL": "http://localhost:8774/v1.1", "publicURL": "http://localhost:8774/v1.1/", }, ], }, ], }, } auth_response = utils.TestResponse({ "status_code": 200, "text": json.dumps(resp), }) mock_request = mock.Mock(return_value=(auth_response)) with mock.patch.object(requests.Session, "request", mock_request): cs.client.authenticate() headers = { 'User-Agent': cs.client.USER_AGENT, 'Content-Type': 'application/json', 'Accept': 'application/json', } body = { 'auth': { 'token': { 'id': cs.client.auth_token, }, 'tenantName': cs.client.projectid, }, } token_url = cs.client.auth_url + "/tokens" mock_request.assert_called_with( "POST", token_url, headers=headers, data=json.dumps(body), allow_redirects=True, **self.TEST_REQUEST_BASE) endpoints = resp["access"]["serviceCatalog"][0]['endpoints'] public_url = endpoints[0]["publicURL"].rstrip('/') self.assertEqual(cs.client.management_url, public_url) token_id = resp["access"]["token"]["id"] self.assertEqual(cs.client.auth_token, token_id) def test_authenticate_with_token_failure(self): cs = client.Client("username", None, "project_id", "auth_url/v2.0") cs.client.auth_token = "FAKE_ID" resp = {"unauthorized": {"message": "Unauthorized", "code": "401"}} auth_response = utils.TestResponse({ "status_code": 401, "text": json.dumps(resp), }) mock_request = mock.Mock(return_value=(auth_response)) with mock.patch.object(requests.Session, "request", mock_request): self.assertRaises(exceptions.Unauthorized, cs.client.authenticate) class AuthenticationTests(utils.TestCase): def test_authenticate_success(self): cs = client.Client("username", "password", "project_id", "auth_url") management_url = 'https://localhost/v1.1/443470' auth_response = utils.TestResponse({ 'status_code': 204, 'headers': { 'x-server-management-url': management_url, 'x-auth-token': '1b751d74-de0c-46ae-84f0-915744b582d1', }, }) mock_request = mock.Mock(return_value=(auth_response)) @mock.patch.object(requests.Session, "request", mock_request) def test_auth_call(): cs.client.authenticate() headers = { 'Accept': 'application/json', 'X-Auth-User': 'username', 'X-Auth-Key': 'password', 'X-Auth-Project-Id': 'project_id', 'User-Agent': cs.client.USER_AGENT } mock_request.assert_called_with( "GET", cs.client.auth_url, headers=headers, **self.TEST_REQUEST_BASE) self.assertEqual(cs.client.management_url, auth_response.headers['x-server-management-url']) self.assertEqual(cs.client.auth_token, auth_response.headers['x-auth-token']) test_auth_call() def test_authenticate_failure(self): cs = client.Client("username", "password", "project_id", "auth_url") auth_response = utils.TestResponse({'status_code': 401}) mock_request = mock.Mock(return_value=(auth_response)) @mock.patch.object(requests.Session, "request", mock_request) def test_auth_call(): self.assertRaises(exceptions.Unauthorized, cs.client.authenticate) test_auth_call() def test_auth_automatic(self): cs = client.Client("username", "password", "project_id", "auth_url") http_client = cs.client http_client.management_url = '' mock_request = mock.Mock(return_value=(None, None)) @mock.patch.object(http_client, 'request', mock_request) @mock.patch.object(http_client, 'authenticate') def test_auth_call(m): http_client.get('/') m.assert_called() mock_request.assert_called() test_auth_call() def test_auth_manual(self): cs = client.Client("username", "password", "project_id", "auth_url") @mock.patch.object(cs.client, 'authenticate') def test_auth_call(m): cs.authenticate() m.assert_called() test_auth_call() python-novaclient-2.17.0/novaclient/tests/v1_1/test_floating_ip_pools.py0000664000175400017540000000216312306432770027620 0ustar jenkinsjenkins00000000000000# Copyright (c) 2011 X.commerce, a business unit of eBay Inc. # # 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 novaclient.tests import utils from novaclient.tests.v1_1 import fakes from novaclient.v1_1 import floating_ip_pools cs = fakes.FakeClient() class TestFloatingIPPools(utils.TestCase): def test_list_floating_ips(self): fl = cs.floating_ip_pools.list() cs.assert_called('GET', '/os-floating-ip-pools') [self.assertIsInstance(f, floating_ip_pools.FloatingIPPool) for f in fl] python-novaclient-2.17.0/novaclient/tests/v1_1/test_certs.py0000664000175400017540000000253212306432770025231 0ustar jenkinsjenkins00000000000000# # 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 novaclient.tests import utils from novaclient.tests.v1_1 import fakes from novaclient.v1_1 import certs class CertsTest(utils.TestCase): def setUp(self): super(CertsTest, self).setUp() self.cs = self._get_fake_client() self.cert_type = self._get_cert_type() def _get_fake_client(self): return fakes.FakeClient() def _get_cert_type(self): return certs.Certificate def test_create_cert(self): cert = self.cs.certs.create() self.cs.assert_called('POST', '/os-certificates') self.assertIsInstance(cert, certs.Certificate) def test_get_root_cert(self): cert = self.cs.certs.get() self.cs.assert_called('GET', '/os-certificates/root') self.assertIsInstance(cert, certs.Certificate) python-novaclient-2.17.0/novaclient/tests/v1_1/test_usage.py0000664000175400017540000000363712306432770025224 0ustar jenkinsjenkins00000000000000# # 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 novaclient.tests import utils from novaclient.tests.v1_1 import fakes from novaclient.v1_1 import usage class UsageTest(utils.TestCase): def setUp(self): super(UsageTest, self).setUp() self.cs = self._get_fake_client() self.usage_type = self._get_usage_type() def _get_fake_client(self): return fakes.FakeClient() def _get_usage_type(self): return usage.Usage def test_usage_list(self, detailed=False): now = datetime.datetime.now() usages = self.cs.usage.list(now, now, detailed) self.cs.assert_called('GET', "/os-simple-tenant-usage?" + ("start=%s&" % now.isoformat()) + ("end=%s&" % now.isoformat()) + ("detailed=%s" % int(bool(detailed)))) [self.assertIsInstance(u, usage.Usage) for u in usages] def test_usage_list_detailed(self): self.test_usage_list(True) def test_usage_get(self): now = datetime.datetime.now() u = self.cs.usage.get("tenantfoo", now, now) self.cs.assert_called('GET', "/os-simple-tenant-usage/tenantfoo?" + ("start=%s&" % now.isoformat()) + ("end=%s" % now.isoformat())) self.assertIsInstance(u, usage.Usage) python-novaclient-2.17.0/novaclient/tests/v1_1/__init__.py0000664000175400017540000000000012306432770024575 0ustar jenkinsjenkins00000000000000python-novaclient-2.17.0/novaclient/tests/v1_1/test_floating_ips_bulk.py0000664000175400017540000000521112306432770027601 0ustar jenkinsjenkins00000000000000# Copyright 2012 IBM Corp. # 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 novaclient.tests import utils from novaclient.tests.v1_1 import fakes from novaclient.v1_1 import floating_ips_bulk cs = fakes.FakeClient() class FloatingIPsBulkTest(utils.TestCase): def test_list_floating_ips_bulk(self): fl = cs.floating_ips_bulk.list() cs.assert_called('GET', '/os-floating-ips-bulk') [self.assertIsInstance(f, floating_ips_bulk.FloatingIP) for f in fl] def test_list_floating_ips_bulk_host_filter(self): fl = cs.floating_ips_bulk.list('testHost') cs.assert_called('GET', '/os-floating-ips-bulk/testHost') [self.assertIsInstance(f, floating_ips_bulk.FloatingIP) for f in fl] def test_create_floating_ips_bulk(self): fl = cs.floating_ips_bulk.create('192.168.1.0/30') body = {'floating_ips_bulk_create': {'ip_range': '192.168.1.0/30'}} cs.assert_called('POST', '/os-floating-ips-bulk', body) self.assertEqual(fl.ip_range, body['floating_ips_bulk_create']['ip_range']) def test_create_floating_ips_bulk_with_pool_and_host(self): fl = cs.floating_ips_bulk.create('192.168.1.0/30', 'poolTest', 'interfaceTest') body = {'floating_ips_bulk_create': {'ip_range': '192.168.1.0/30', 'pool': 'poolTest', 'interface': 'interfaceTest'}} cs.assert_called('POST', '/os-floating-ips-bulk', body) self.assertEqual(fl.ip_range, body['floating_ips_bulk_create']['ip_range']) self.assertEqual(fl.pool, body['floating_ips_bulk_create']['pool']) self.assertEqual(fl.interface, body['floating_ips_bulk_create']['interface']) def test_delete_floating_ips_bulk(self): fl = cs.floating_ips_bulk.delete('192.168.1.0/30') body = {'ip_range': '192.168.1.0/30'} cs.assert_called('PUT', '/os-floating-ips-bulk/delete', body) self.assertEqual(fl.floating_ips_bulk_delete, body['ip_range']) python-novaclient-2.17.0/novaclient/tests/test_client.py0000664000175400017540000002264512306432770024630 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # 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 mock import requests import novaclient.client import novaclient.extension import novaclient.tests.fakes as fakes from novaclient.tests import utils import novaclient.v1_1.client import novaclient.v3.client import json class ClientTest(utils.TestCase): def test_client_with_timeout(self): instance = novaclient.client.HTTPClient(user='user', password='password', projectid='project', timeout=2, auth_url="http://www.blah.com") self.assertEqual(instance.timeout, 2) mock_request = mock.Mock() mock_request.return_value = requests.Response() mock_request.return_value.status_code = 200 mock_request.return_value.headers = { 'x-server-management-url': 'blah.com', 'x-auth-token': 'blah', } with mock.patch('requests.Session.request', mock_request): instance.authenticate() requests.Session.request.assert_called_with(mock.ANY, mock.ANY, timeout=2, headers=mock.ANY, verify=mock.ANY) def test_client_reauth(self): instance = novaclient.client.HTTPClient(user='user', password='password', projectid='project', timeout=2, auth_url="http://www.blah.com") instance.auth_token = 'foobar' instance.management_url = 'http://example.com' instance.version = 'v2.0' mock_request = mock.Mock() mock_request.side_effect = novaclient.exceptions.Unauthorized(401) with mock.patch('requests.Session.request', mock_request): try: instance.get('/servers/detail') except Exception: pass get_headers = {'X-Auth-Project-Id': 'project', 'X-Auth-Token': 'foobar', 'User-Agent': 'python-novaclient', 'Accept': 'application/json'} reauth_headers = {'Content-Type': 'application/json', 'Accept': 'application/json', 'User-Agent': 'python-novaclient'} data = { "auth": { "tenantName": "project", "passwordCredentials": { "username": "user", "password": "password" } } } expected = [mock.call('GET', 'http://example.com/servers/detail', timeout=mock.ANY, headers=get_headers, verify=mock.ANY), mock.call('POST', 'http://www.blah.com/tokens', timeout=mock.ANY, headers=reauth_headers, allow_redirects=mock.ANY, data=json.dumps(data), verify=mock.ANY)] self.assertEqual(mock_request.call_args_list, expected) def test_get_client_class_v3(self): output = novaclient.client.get_client_class('3') self.assertEqual(output, novaclient.v3.client.Client) def test_get_client_class_v2(self): output = novaclient.client.get_client_class('2') self.assertEqual(output, novaclient.v1_1.client.Client) def test_get_client_class_v2_int(self): output = novaclient.client.get_client_class(2) self.assertEqual(output, novaclient.v1_1.client.Client) def test_get_client_class_v1_1(self): output = novaclient.client.get_client_class('1.1') self.assertEqual(output, novaclient.v1_1.client.Client) def test_get_client_class_unknown(self): self.assertRaises(novaclient.exceptions.UnsupportedVersion, novaclient.client.get_client_class, '0') def test_client_with_os_cache_enabled(self): cs = novaclient.v1_1.client.Client("user", "password", "project_id", auth_url="foo/v2", os_cache=True) self.assertEqual(True, cs.os_cache) self.assertEqual(True, cs.client.os_cache) def test_client_with_os_cache_disabled(self): cs = novaclient.v1_1.client.Client("user", "password", "project_id", auth_url="foo/v2", os_cache=False) self.assertEqual(False, cs.os_cache) self.assertEqual(False, cs.client.os_cache) def test_client_with_no_cache_enabled(self): cs = novaclient.v1_1.client.Client("user", "password", "project_id", auth_url="foo/v2", no_cache=True) self.assertEqual(False, cs.os_cache) self.assertEqual(False, cs.client.os_cache) def test_client_with_no_cache_disabled(self): cs = novaclient.v1_1.client.Client("user", "password", "project_id", auth_url="foo/v2", no_cache=False) self.assertEqual(True, cs.os_cache) self.assertEqual(True, cs.client.os_cache) def test_client_set_management_url_v1_1(self): cs = novaclient.v1_1.client.Client("user", "password", "project_id", auth_url="foo/v2") cs.set_management_url("blabla") self.assertEqual("blabla", cs.client.management_url) def test_client_get_reset_timings_v1_1(self): cs = novaclient.v1_1.client.Client("user", "password", "project_id", auth_url="foo/v2") self.assertEqual(0, len(cs.get_timings())) cs.client.times.append("somevalue") self.assertEqual(1, len(cs.get_timings())) self.assertEqual("somevalue", cs.get_timings()[0]) cs.reset_timings() self.assertEqual(0, len(cs.get_timings())) def test_client_set_management_url_v3(self): cs = novaclient.v3.client.Client("user", "password", "project_id", auth_url="foo/v2") cs.set_management_url("blabla") self.assertEqual("blabla", cs.client.management_url) def test_client_get_reset_timings_v3(self): cs = novaclient.v3.client.Client("user", "password", "project_id", auth_url="foo/v2") self.assertEqual(0, len(cs.get_timings())) cs.client.times.append("somevalue") self.assertEqual(["somevalue"], cs.get_timings()) cs.reset_timings() self.assertEqual(0, len(cs.get_timings())) def test_clent_extensions_v3(self): fake_attribute_name1 = "FakeAttribute1" fake_attribute_name2 = "FakeAttribute2" extensions = [ novaclient.extension.Extension(fake_attribute_name1, fakes), novaclient.extension.Extension(fake_attribute_name2, utils), ] cs = novaclient.v3.client.Client("user", "password", "project_id", auth_url="foo/v2", extensions=extensions) self.assertIsInstance(getattr(cs, fake_attribute_name1, None), fakes.FakeManager) self.assertFalse(hasattr(cs, fake_attribute_name2)) @mock.patch.object(novaclient.client.HTTPClient, 'authenticate') def test_authenticate_call_v3(self, mock_authenticate): cs = novaclient.v3.client.Client("user", "password", "project_id", auth_url="foo/v2") cs.authenticate() self.assertTrue(mock_authenticate.called) def test_get_password_simple(self): cs = novaclient.client.HTTPClient("user", "password", "", "") cs.password_func = mock.Mock() self.assertEqual(cs._get_password(), "password") self.assertFalse(cs.password_func.called) def test_get_password_none(self): cs = novaclient.client.HTTPClient("user", None, "", "") self.assertIsNone(cs._get_password()) def test_get_password_func(self): cs = novaclient.client.HTTPClient("user", None, "", "") cs.password_func = mock.Mock(return_value="password") self.assertEqual(cs._get_password(), "password") cs.password_func.assert_called_once_with() cs.password_func = mock.Mock() self.assertEqual(cs._get_password(), "password") self.assertFalse(cs.password_func.called) python-novaclient-2.17.0/novaclient/tests/fakes.py0000664000175400017540000000564512306432770023405 0ustar jenkinsjenkins00000000000000# # 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. """ A fake server that "responds" to API methods with pre-canned responses. All of these responses come from the spec, so if for some reason the spec's wrong the tests might raise AssertionError. I've indicated in comments the places where actual behavior differs from the spec. """ from novaclient import base def assert_has_keys(dict, required=[], optional=[]): keys = dict.keys() for k in required: try: assert k in keys except AssertionError: extra_keys = set(keys).difference(set(required + optional)) raise AssertionError("found unexpected keys: %s" % list(extra_keys)) class FakeClient(object): def assert_called(self, method, url, body=None, pos=-1): """ Assert than an API method was just called. """ expected = (method, url) called = self.client.callstack[pos][0:2] assert self.client.callstack, \ "Expected %s %s but no calls were made." % expected assert expected == called, 'Expected %s %s; got %s %s' % \ (expected + called) if body is not None: if self.client.callstack[pos][2] != body: raise AssertionError('%r != %r' % (self.client.callstack[pos][2], body)) def assert_called_anytime(self, method, url, body=None): """ Assert than an API method was called anytime in the test. """ expected = (method, url) assert self.client.callstack, \ "Expected %s %s but no calls were made." % expected found = False for entry in self.client.callstack: if expected == entry[0:2]: found = True break assert found, 'Expected %s; got %s' % \ (expected, self.client.callstack) if body is not None: try: assert entry[2] == body except AssertionError: print(entry[2]) print("!=") print(body) raise self.client.callstack = [] def clear_callstack(self): self.client.callstack = [] def authenticate(self): pass # Fake class that will be used as an extension class FakeManager(base.Manager): pass python-novaclient-2.17.0/novaclient/tests/utils.py0000664000175400017540000000375312306432770023452 0ustar jenkinsjenkins00000000000000# # 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 fixtures import requests import testtools class TestCase(testtools.TestCase): TEST_REQUEST_BASE = { 'verify': True, } def setUp(self): super(TestCase, self).setUp() if (os.environ.get('OS_STDOUT_CAPTURE') == 'True' or os.environ.get('OS_STDOUT_CAPTURE') == '1'): stdout = self.useFixture(fixtures.StringStream('stdout')).stream self.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout)) if (os.environ.get('OS_STDERR_CAPTURE') == 'True' or os.environ.get('OS_STDERR_CAPTURE') == '1'): stderr = self.useFixture(fixtures.StringStream('stderr')).stream self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr)) class TestResponse(requests.Response): """ Class used to wrap requests.Response and provide some convenience to initialize with a dict """ def __init__(self, data): super(TestResponse, self).__init__() self._text = None if isinstance(data, dict): self.status_code = data.get('status_code') self.headers = data.get('headers') # Fake the text attribute to streamline Response creation self._text = data.get('text') else: self.status_code = data def __eq__(self, other): return self.__dict__ == other.__dict__ @property def text(self): return self._text python-novaclient-2.17.0/novaclient/tests/test_auth_plugins.py0000664000175400017540000003105712306432770026051 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # 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 argparse import mock import pkg_resources import requests try: import json except ImportError: import simplejson as json from novaclient import auth_plugin from novaclient import exceptions from novaclient.tests import utils from novaclient.v1_1 import client def mock_http_request(resp=None): """Mock an HTTP Request.""" if not resp: resp = { "access": { "token": { "expires": "12345", "id": "FAKE_ID", "tenant": { "id": "FAKE_TENANT_ID", } }, "serviceCatalog": [ { "type": "compute", "endpoints": [ { "region": "RegionOne", "adminURL": "http://localhost:8774/v1.1", "internalURL": "http://localhost:8774/v1.1", "publicURL": "http://localhost:8774/v1.1/", }, ], }, ], }, } auth_response = utils.TestResponse({ "status_code": 200, "text": json.dumps(resp), }) return mock.Mock(return_value=(auth_response)) def requested_headers(cs): """Return requested passed headers.""" return { 'User-Agent': cs.client.USER_AGENT, 'Content-Type': 'application/json', 'Accept': 'application/json', } class DeprecatedAuthPluginTest(utils.TestCase): def test_auth_system_success(self): class MockEntrypoint(pkg_resources.EntryPoint): def load(self): return self.authenticate def authenticate(self, cls, auth_url): cls._authenticate(auth_url, {"fake": "me"}) def mock_iter_entry_points(_type, name): if _type == 'openstack.client.authenticate': return [MockEntrypoint("fake", "fake", ["fake"])] else: return [] mock_request = mock_http_request() @mock.patch.object(pkg_resources, "iter_entry_points", mock_iter_entry_points) @mock.patch.object(requests.Session, "request", mock_request) def test_auth_call(): plugin = auth_plugin.DeprecatedAuthPlugin("fake") cs = client.Client("username", "password", "project_id", "auth_url/v2.0", auth_system="fake", auth_plugin=plugin) cs.client.authenticate() headers = requested_headers(cs) token_url = cs.client.auth_url + "/tokens" mock_request.assert_called_with( "POST", token_url, headers=headers, data='{"fake": "me"}', allow_redirects=True, **self.TEST_REQUEST_BASE) test_auth_call() def test_auth_system_not_exists(self): def mock_iter_entry_points(_t, name=None): return [pkg_resources.EntryPoint("fake", "fake", ["fake"])] mock_request = mock_http_request() @mock.patch.object(pkg_resources, "iter_entry_points", mock_iter_entry_points) @mock.patch.object(requests.Session, "request", mock_request) def test_auth_call(): auth_plugin.discover_auth_systems() plugin = auth_plugin.DeprecatedAuthPlugin("notexists") cs = client.Client("username", "password", "project_id", "auth_url/v2.0", auth_system="notexists", auth_plugin=plugin) self.assertRaises(exceptions.AuthSystemNotFound, cs.client.authenticate) test_auth_call() def test_auth_system_defining_auth_url(self): class MockAuthUrlEntrypoint(pkg_resources.EntryPoint): def load(self): return self.auth_url def auth_url(self): return "http://faked/v2.0" class MockAuthenticateEntrypoint(pkg_resources.EntryPoint): def load(self): return self.authenticate def authenticate(self, cls, auth_url): cls._authenticate(auth_url, {"fake": "me"}) def mock_iter_entry_points(_type, name): if _type == 'openstack.client.auth_url': return [MockAuthUrlEntrypoint("fakewithauthurl", "fakewithauthurl", ["auth_url"])] elif _type == 'openstack.client.authenticate': return [MockAuthenticateEntrypoint("fakewithauthurl", "fakewithauthurl", ["authenticate"])] else: return [] mock_request = mock_http_request() @mock.patch.object(pkg_resources, "iter_entry_points", mock_iter_entry_points) @mock.patch.object(requests.Session, "request", mock_request) def test_auth_call(): plugin = auth_plugin.DeprecatedAuthPlugin("fakewithauthurl") cs = client.Client("username", "password", "project_id", auth_system="fakewithauthurl", auth_plugin=plugin) cs.client.authenticate() self.assertEqual(cs.client.auth_url, "http://faked/v2.0") test_auth_call() @mock.patch.object(pkg_resources, "iter_entry_points") def test_client_raises_exc_without_auth_url(self, mock_iter_entry_points): class MockAuthUrlEntrypoint(pkg_resources.EntryPoint): def load(self): return self.auth_url def auth_url(self): return None mock_iter_entry_points.side_effect = lambda _t, name: [ MockAuthUrlEntrypoint("fakewithauthurl", "fakewithauthurl", ["auth_url"])] plugin = auth_plugin.DeprecatedAuthPlugin("fakewithauthurl") self.assertRaises( exceptions.EndpointNotFound, client.Client, "username", "password", "project_id", auth_system="fakewithauthurl", auth_plugin=plugin) class AuthPluginTest(utils.TestCase): @mock.patch.object(requests.Session, "request") @mock.patch.object(pkg_resources, "iter_entry_points") def test_auth_system_success(self, mock_iter_entry_points, mock_request): """Test that we can authenticate using the auth system.""" class MockEntrypoint(pkg_resources.EntryPoint): def load(self): return FakePlugin class FakePlugin(auth_plugin.BaseAuthPlugin): def authenticate(self, cls, auth_url): cls._authenticate(auth_url, {"fake": "me"}) mock_iter_entry_points.side_effect = lambda _t: [ MockEntrypoint("fake", "fake", ["FakePlugin"])] mock_request.side_effect = mock_http_request() auth_plugin.discover_auth_systems() plugin = auth_plugin.load_plugin("fake") cs = client.Client("username", "password", "project_id", "auth_url/v2.0", auth_system="fake", auth_plugin=plugin) cs.client.authenticate() headers = requested_headers(cs) token_url = cs.client.auth_url + "/tokens" mock_request.assert_called_with( "POST", token_url, headers=headers, data='{"fake": "me"}', allow_redirects=True, **self.TEST_REQUEST_BASE) @mock.patch.object(pkg_resources, "iter_entry_points") def test_discover_auth_system_options(self, mock_iter_entry_points): """Test that we can load the auth system options.""" class FakePlugin(auth_plugin.BaseAuthPlugin): @staticmethod def add_opts(parser): parser.add_argument('--auth_system_opt', default=False, action='store_true', help="Fake option") return parser class MockEntrypoint(pkg_resources.EntryPoint): def load(self): return FakePlugin mock_iter_entry_points.side_effect = lambda _t: [ MockEntrypoint("fake", "fake", ["FakePlugin"])] parser = argparse.ArgumentParser() auth_plugin.discover_auth_systems() auth_plugin.load_auth_system_opts(parser) opts, args = parser.parse_known_args(['--auth_system_opt']) self.assertTrue(opts.auth_system_opt) @mock.patch.object(pkg_resources, "iter_entry_points") def test_parse_auth_system_options(self, mock_iter_entry_points): """Test that we can parse the auth system options.""" class MockEntrypoint(pkg_resources.EntryPoint): def load(self): return FakePlugin class FakePlugin(auth_plugin.BaseAuthPlugin): def __init__(self): self.opts = {"fake_argument": True} def parse_opts(self, args): return self.opts mock_iter_entry_points.side_effect = lambda _t: [ MockEntrypoint("fake", "fake", ["FakePlugin"])] auth_plugin.discover_auth_systems() plugin = auth_plugin.load_plugin("fake") plugin.parse_opts([]) self.assertIn("fake_argument", plugin.opts) @mock.patch.object(pkg_resources, "iter_entry_points") def test_auth_system_defining_url(self, mock_iter_entry_points): """Test the auth_system defining an url.""" class MockEntrypoint(pkg_resources.EntryPoint): def load(self): return FakePlugin class FakePlugin(auth_plugin.BaseAuthPlugin): def get_auth_url(self): return "http://faked/v2.0" mock_iter_entry_points.side_effect = lambda _t: [ MockEntrypoint("fake", "fake", ["FakePlugin"])] auth_plugin.discover_auth_systems() plugin = auth_plugin.load_plugin("fake") cs = client.Client("username", "password", "project_id", auth_system="fakewithauthurl", auth_plugin=plugin) self.assertEqual(cs.client.auth_url, "http://faked/v2.0") @mock.patch.object(pkg_resources, "iter_entry_points") def test_exception_if_no_authenticate(self, mock_iter_entry_points): """Test that no authenticate raises a proper exception.""" class MockEntrypoint(pkg_resources.EntryPoint): def load(self): return FakePlugin class FakePlugin(auth_plugin.BaseAuthPlugin): pass mock_iter_entry_points.side_effect = lambda _t: [ MockEntrypoint("fake", "fake", ["FakePlugin"])] auth_plugin.discover_auth_systems() plugin = auth_plugin.load_plugin("fake") self.assertRaises( exceptions.EndpointNotFound, client.Client, "username", "password", "project_id", auth_system="fake", auth_plugin=plugin) @mock.patch.object(pkg_resources, "iter_entry_points") def test_exception_if_no_url(self, mock_iter_entry_points): """Test that no auth_url at all raises exception.""" class MockEntrypoint(pkg_resources.EntryPoint): def load(self): return FakePlugin class FakePlugin(auth_plugin.BaseAuthPlugin): pass mock_iter_entry_points.side_effect = lambda _t: [ MockEntrypoint("fake", "fake", ["FakePlugin"])] auth_plugin.discover_auth_systems() plugin = auth_plugin.load_plugin("fake") self.assertRaises( exceptions.EndpointNotFound, client.Client, "username", "password", "project_id", auth_system="fake", auth_plugin=plugin) python-novaclient-2.17.0/novaclient/tests/test_http.py0000664000175400017540000001031212306432770024315 0ustar jenkinsjenkins00000000000000# # 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 mock import requests from novaclient import client from novaclient import exceptions from novaclient.tests import utils fake_response = utils.TestResponse({ "status_code": 200, "text": '{"hi": "there"}', }) mock_request = mock.Mock(return_value=(fake_response)) refused_response = utils.TestResponse({ "status_code": 400, "text": '[Errno 111] Connection refused', }) refused_mock_request = mock.Mock(return_value=(refused_response)) bad_req_response = utils.TestResponse({ "status_code": 400, "text": '', }) bad_req_mock_request = mock.Mock(return_value=(bad_req_response)) def get_client(): cl = client.HTTPClient("username", "password", "project_id", "auth_test") return cl def get_authed_client(): cl = get_client() cl.management_url = "http://example.com" cl.auth_token = "token" return cl class ClientTest(utils.TestCase): def test_get(self): cl = get_authed_client() @mock.patch.object(requests.Session, "request", mock_request) @mock.patch('time.time', mock.Mock(return_value=1234)) def test_get_call(): resp, body = cl.get("/hi") headers = {"X-Auth-Token": "token", "X-Auth-Project-Id": "project_id", "User-Agent": cl.USER_AGENT, 'Accept': 'application/json', } mock_request.assert_called_with( "GET", "http://example.com/hi", headers=headers, **self.TEST_REQUEST_BASE) # Automatic JSON parsing self.assertEqual(body, {"hi": "there"}) test_get_call() def test_post(self): cl = get_authed_client() @mock.patch.object(requests.Session, "request", mock_request) def test_post_call(): cl.post("/hi", body=[1, 2, 3]) headers = { "X-Auth-Token": "token", "X-Auth-Project-Id": "project_id", "Content-Type": "application/json", 'Accept': 'application/json', "User-Agent": cl.USER_AGENT } mock_request.assert_called_with( "POST", "http://example.com/hi", headers=headers, data='[1, 2, 3]', **self.TEST_REQUEST_BASE) test_post_call() def test_auth_failure(self): cl = get_client() # response must not have x-server-management-url header @mock.patch.object(requests.Session, "request", mock_request) def test_auth_call(): self.assertRaises(exceptions.AuthorizationFailure, cl.authenticate) test_auth_call() def test_connection_refused(self): cl = get_client() @mock.patch.object(requests.Session, "request", refused_mock_request) def test_refused_call(): self.assertRaises(exceptions.ConnectionRefused, cl.get, "/hi") test_refused_call() def test_bad_request(self): cl = get_client() @mock.patch.object(requests.Session, "request", bad_req_mock_request) def test_refused_call(): self.assertRaises(exceptions.BadRequest, cl.get, "/hi") test_refused_call() def test_client_logger(self): cl1 = client.HTTPClient("username", "password", "project_id", "auth_test", http_log_debug=True) self.assertEqual(len(cl1._logger.handlers), 1) cl2 = client.HTTPClient("username", "password", "project_id", "auth_test", http_log_debug=True) self.assertEqual(len(cl2._logger.handlers), 1) python-novaclient-2.17.0/novaclient/tests/test_utils.py0000664000175400017540000002463612306432770024514 0ustar jenkinsjenkins00000000000000# # 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 sys import mock import six from novaclient import base from novaclient import exceptions from novaclient.tests import utils as test_utils from novaclient import utils UUID = '8e8ec658-c7b0-4243-bdf8-6f7f2952c0d0' class FakeResource(object): NAME_ATTR = 'name' def __init__(self, _id, properties): self.id = _id try: self.name = properties['name'] except KeyError: pass class FakeManager(base.ManagerWithFind): resource_class = FakeResource resources = [ FakeResource('1234', {'name': 'entity_one'}), FakeResource(UUID, {'name': 'entity_two'}), FakeResource('5678', {'name': '9876'}), FakeResource('01234', {'name': 'entity_three'}) ] is_alphanum_id_allowed = None def __init__(self, alphanum_id_allowed=False): self.is_alphanum_id_allowed = alphanum_id_allowed def get(self, resource_id): for resource in self.resources: if resource.id == str(resource_id): return resource raise exceptions.NotFound(resource_id) def list(self): return self.resources class FakeDisplayResource(object): NAME_ATTR = 'display_name' def __init__(self, _id, properties): self.id = _id try: self.display_name = properties['display_name'] except KeyError: pass class FakeDisplayManager(FakeManager): resource_class = FakeDisplayResource resources = [ FakeDisplayResource('4242', {'display_name': 'entity_three'}), ] class FindResourceTestCase(test_utils.TestCase): def setUp(self): super(FindResourceTestCase, self).setUp() self.manager = FakeManager(None) def test_find_none(self): """Test a few non-valid inputs.""" self.assertRaises(exceptions.CommandError, utils.find_resource, self.manager, 'asdf') self.assertRaises(exceptions.CommandError, utils.find_resource, self.manager, None) self.assertRaises(exceptions.CommandError, utils.find_resource, self.manager, {}) def test_find_by_integer_id(self): output = utils.find_resource(self.manager, 1234) self.assertEqual(output, self.manager.get('1234')) def test_find_by_str_id(self): output = utils.find_resource(self.manager, '1234') self.assertEqual(output, self.manager.get('1234')) def test_find_by_uuid(self): output = utils.find_resource(self.manager, UUID) self.assertEqual(output, self.manager.get(UUID)) def test_find_by_str_name(self): output = utils.find_resource(self.manager, 'entity_one') self.assertEqual(output, self.manager.get('1234')) def test_find_by_str_displayname(self): display_manager = FakeDisplayManager(None) output = utils.find_resource(display_manager, 'entity_three') self.assertEqual(output, display_manager.get('4242')) def test_find_in_alphanum_allowd_manager_by_str_id_(self): alphanum_manager = FakeManager(True) output = utils.find_resource(alphanum_manager, '01234') self.assertEqual(output, alphanum_manager.get('01234')) class _FakeResult(object): def __init__(self, name, value): self.name = name self.value = value class PrintResultTestCase(test_utils.TestCase): @mock.patch('sys.stdout', six.StringIO()) def test_print_dict(self): dict = {'key': 'value'} utils.print_dict(dict) self.assertEqual(sys.stdout.getvalue(), '+----------+-------+\n' '| Property | Value |\n' '+----------+-------+\n' '| key | value |\n' '+----------+-------+\n') @mock.patch('sys.stdout', six.StringIO()) def test_print_dict_wrap(self): dict = {'key1': 'not wrapped', 'key2': 'this will be wrapped'} utils.print_dict(dict, wrap=16) self.assertEqual(sys.stdout.getvalue(), '+----------+--------------+\n' '| Property | Value |\n' '+----------+--------------+\n' '| key1 | not wrapped |\n' '| key2 | this will be |\n' '| | wrapped |\n' '+----------+--------------+\n') @mock.patch('sys.stdout', six.StringIO()) def test_print_list_sort_by_str(self): objs = [_FakeResult("k1", 1), _FakeResult("k3", 2), _FakeResult("k2", 3)] utils.print_list(objs, ["Name", "Value"], sortby_index=0) self.assertEqual(sys.stdout.getvalue(), '+------+-------+\n' '| Name | Value |\n' '+------+-------+\n' '| k1 | 1 |\n' '| k2 | 3 |\n' '| k3 | 2 |\n' '+------+-------+\n') @mock.patch('sys.stdout', six.StringIO()) def test_print_list_sort_by_integer(self): objs = [_FakeResult("k1", 1), _FakeResult("k3", 2), _FakeResult("k2", 3)] utils.print_list(objs, ["Name", "Value"], sortby_index=1) self.assertEqual(sys.stdout.getvalue(), '+------+-------+\n' '| Name | Value |\n' '+------+-------+\n' '| k1 | 1 |\n' '| k3 | 2 |\n' '| k2 | 3 |\n' '+------+-------+\n') # without sorting @mock.patch('sys.stdout', six.StringIO()) def test_print_list_sort_by_none(self): objs = [_FakeResult("k1", 1), _FakeResult("k3", 3), _FakeResult("k2", 2)] utils.print_list(objs, ["Name", "Value"], sortby_index=None) self.assertEqual(sys.stdout.getvalue(), '+------+-------+\n' '| Name | Value |\n' '+------+-------+\n' '| k1 | 1 |\n' '| k3 | 3 |\n' '| k2 | 2 |\n' '+------+-------+\n') @mock.patch('sys.stdout', six.StringIO()) def test_print_dict_dictionary(self): dict = {'k': {'foo': 'bar'}} utils.print_dict(dict) self.assertEqual(sys.stdout.getvalue(), '+----------+----------------+\n' '| Property | Value |\n' '+----------+----------------+\n' '| k | {"foo": "bar"} |\n' '+----------+----------------+\n') @mock.patch('sys.stdout', six.StringIO()) def test_print_dict_list_dictionary(self): dict = {'k': [{'foo': 'bar'}]} utils.print_dict(dict) self.assertEqual(sys.stdout.getvalue(), '+----------+------------------+\n' '| Property | Value |\n' '+----------+------------------+\n' '| k | [{"foo": "bar"}] |\n' '+----------+------------------+\n') @mock.patch('sys.stdout', six.StringIO()) def test_print_dict_list(self): dict = {'k': ['foo', 'bar']} utils.print_dict(dict) self.assertEqual(sys.stdout.getvalue(), '+----------+----------------+\n' '| Property | Value |\n' '+----------+----------------+\n' '| k | ["foo", "bar"] |\n' '+----------+----------------+\n') class FlattenTestCase(test_utils.TestCase): def test_flattening(self): squashed = utils.flatten_dict( {'a1': {'b1': 1234, 'b2': 'string', 'b3': set((1, 2, 3)), 'b4': {'c1': ['l', 'l', ['l']], 'c2': 'string'}}, 'a2': ['l'], 'a3': ('t',)}) self.assertEqual({'a1_b1': 1234, 'a1_b2': 'string', 'a1_b3': set([1, 2, 3]), 'a1_b4_c1': ['l', 'l', ['l']], 'a1_b4_c2': 'string', 'a2': ['l'], 'a3': ('t',)}, squashed) def test_pretty_choice_list(self): l = [] r = utils.pretty_choice_list(l) self.assertEqual(r, "") l = ["v1", "v2", "v3"] r = utils.pretty_choice_list(l) self.assertEqual(r, "'v1', 'v2', 'v3'") def test_pretty_choice_dict(self): d = {} r = utils.pretty_choice_dict(d) self.assertEqual(r, "") d = {"k1": "v1", "k2": "v2", "k3": "v3"} r = utils.pretty_choice_dict(d) self.assertEqual(r, "'k1=v1', 'k2=v2', 'k3=v3'") class ValidationsTestCase(test_utils.TestCase): def test_validate_flavor_metadata_keys_with_valid_keys(self): valid_keys = ['key1', 'month.price', 'I-Am:AK-ey.01-', 'spaces and _'] utils.validate_flavor_metadata_keys(valid_keys) def test_validate_flavor_metadata_keys_with_invalid_keys(self): invalid_keys = ['/1', '?1', '%1', '<', '>', '\1'] for key in invalid_keys: try: utils.validate_flavor_metadata_keys([key]) self.fail("Invalid key passed validation: %s" % key) except exceptions.CommandError as ce: self.assertTrue(key in str(ce)) python-novaclient-2.17.0/novaclient/tests/test_discover.py0000664000175400017540000000565412306432770025171 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # 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 imp import inspect import mock import pkg_resources import novaclient.shell from novaclient.tests import utils class DiscoverTest(utils.TestCase): def test_discover_via_entry_points(self): def mock_iter_entry_points(group): if group == 'novaclient.extension': fake_ep = mock.Mock() fake_ep.name = 'foo' fake_ep.module = imp.new_module('foo') fake_ep.load.return_value = fake_ep.module return [fake_ep] @mock.patch.object(pkg_resources, 'iter_entry_points', mock_iter_entry_points) def test(): shell = novaclient.shell.OpenStackComputeShell() for name, module in shell._discover_via_entry_points(): self.assertEqual(name, 'foo') self.assertTrue(inspect.ismodule(module)) test() def test_discover_extensions(self): def mock_discover_via_python_path(self): yield 'foo', imp.new_module('foo') def mock_discover_via_contrib_path(self, version): yield 'bar', imp.new_module('bar') def mock_discover_via_entry_points(self): yield 'baz', imp.new_module('baz') @mock.patch.object(novaclient.shell.OpenStackComputeShell, '_discover_via_python_path', mock_discover_via_python_path) @mock.patch.object(novaclient.shell.OpenStackComputeShell, '_discover_via_contrib_path', mock_discover_via_contrib_path) @mock.patch.object(novaclient.shell.OpenStackComputeShell, '_discover_via_entry_points', mock_discover_via_entry_points) def test(): shell = novaclient.shell.OpenStackComputeShell() extensions = shell._discover_extensions('1.1') self.assertEqual(len(extensions), 3) names = sorted(['foo', 'bar', 'baz']) sorted_extensions = sorted(extensions, key=lambda ext: ext.name) for i in range(len(names)): ext = sorted_extensions[i] name = names[i] self.assertEqual(ext.name, name) self.assertTrue(inspect.ismodule(ext.module)) test() python-novaclient-2.17.0/novaclient/tests/test_service_catalog.py0000664000175400017540000001410412306432770026473 0ustar jenkinsjenkins00000000000000# # 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 novaclient import exceptions from novaclient import service_catalog from novaclient.tests import utils # Taken directly from keystone/content/common/samples/auth.json # Do not edit this structure. Instead, grab the latest from there. SERVICE_CATALOG = { "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": "235", "name": "object-store:admin", "tenantId": "1", } ], "roles_links": [], }, "serviceCatalog": [ { "name": "Cloud Servers", "type": "compute", "endpoints": [ { # Tenant 1, no region, v1.0 "tenantId": "1", "publicURL": "https://compute1.host/v1/1", "internalURL": "https://compute1.host/v1/1", "versionId": "1.0", "versionInfo": "https://compute1.host/v1.0/", "versionList": "https://compute1.host/" }, { # Tenant 2, with region, v1.1 "tenantId": "2", "publicURL": "https://compute1.host/v1.1/2", "internalURL": "https://compute1.host/v1.1/2", "region": "North", "versionId": "1.1", "versionInfo": "https://compute1.host/v1.1/", "versionList": "https://compute1.host/" }, { # Tenant 1, with region, v2.0 "tenantId": "1", "publicURL": "https://compute1.host/v2/1", "internalURL": "https://compute1.host/v2/1", "region": "North", "versionId": "2", "versionInfo": "https://compute1.host/v2/", "versionList": "https://compute1.host/" }, ], "endpoints_links": [], }, { "name": "Nova Volumes", "type": "volume", "endpoints": [ { "tenantId": "1", "publicURL": "https://volume1.host/v1/1", "internalURL": "https://volume1.host/v1/1", "region": "South", "versionId": "1.0", "versionInfo": "uri", "versionList": "uri" }, { "tenantId": "2", "publicURL": "https://volume1.host/v1.1/2", "internalURL": "https://volume1.host/v1.1/2", "region": "South", "versionId": "1.1", "versionInfo": "https://volume1.host/v1.1/", "versionList": "https://volume1.host/" }, ], "endpoints_links": [ { "rel": "next", "href": "https://identity1.host/v2.0/endpoints" }, ], }, ], "serviceCatalog_links": [ { "rel": "next", "href": "https://identity.host/v2.0/endpoints?session=2hfh8Ar", }, ], }, } class ServiceCatalogTest(utils.TestCase): def test_building_a_service_catalog(self): sc = service_catalog.ServiceCatalog(SERVICE_CATALOG) self.assertRaises(exceptions.AmbiguousEndpoints, sc.url_for, service_type='compute') self.assertEqual(sc.url_for('tenantId', '1', service_type='compute'), "https://compute1.host/v2/1") self.assertEqual(sc.url_for('tenantId', '2', service_type='compute'), "https://compute1.host/v1.1/2") self.assertRaises(exceptions.EndpointNotFound, sc.url_for, "region", "South", service_type='compute') def test_building_a_service_catalog_insensitive_case(self): sc = service_catalog.ServiceCatalog(SERVICE_CATALOG) # Matching south (and catalog has South). self.assertRaises(exceptions.AmbiguousEndpoints, sc.url_for, 'region', 'south', service_type='volume') def test_alternate_service_type(self): sc = service_catalog.ServiceCatalog(SERVICE_CATALOG) self.assertRaises(exceptions.AmbiguousEndpoints, sc.url_for, service_type='volume') self.assertEqual(sc.url_for('tenantId', '1', service_type='volume'), "https://volume1.host/v1/1") self.assertEqual(sc.url_for('tenantId', '2', service_type='volume'), "https://volume1.host/v1.1/2") self.assertRaises(exceptions.EndpointNotFound, sc.url_for, "region", "North", service_type='volume') python-novaclient-2.17.0/novaclient/tests/test_base.py0000664000175400017540000000471512306432770024262 0ustar jenkinsjenkins00000000000000# # 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 novaclient import base from novaclient import exceptions from novaclient.tests import utils from novaclient.tests.v1_1 import fakes from novaclient.v1_1 import flavors cs = fakes.FakeClient() class BaseTest(utils.TestCase): def test_resource_repr(self): r = base.Resource(None, dict(foo="bar", baz="spam")) self.assertEqual(repr(r), "") def test_getid(self): self.assertEqual(base.getid(4), 4) class TmpObject(object): id = 4 self.assertEqual(base.getid(TmpObject), 4) def test_resource_lazy_getattr(self): f = flavors.Flavor(cs.flavors, {'id': 1}) self.assertEqual(f.name, '256 MB Server') cs.assert_called('GET', '/flavors/1') # Missing stuff still fails after a second get self.assertRaises(AttributeError, getattr, f, 'blahblah') def test_eq(self): # Two resources of the same type with the same id: equal r1 = base.Resource(None, {'id': 1, 'name': 'hi'}) r2 = base.Resource(None, {'id': 1, 'name': 'hello'}) self.assertEqual(r1, r2) # Two resoruces of different types: never equal r1 = base.Resource(None, {'id': 1}) r2 = flavors.Flavor(None, {'id': 1}) self.assertNotEqual(r1, r2) # Two resources with no ID: equal if their info is equal r1 = base.Resource(None, {'name': 'joe', 'age': 12}) r2 = base.Resource(None, {'name': 'joe', 'age': 12}) self.assertEqual(r1, r2) def test_findall_invalid_attribute(self): # Make sure findall with an invalid attribute doesn't cause errors. # The following should not raise an exception. cs.flavors.findall(vegetable='carrot') # However, find() should raise an error self.assertRaises(exceptions.NotFound, cs.flavors.find, vegetable='carrot') python-novaclient-2.17.0/novaclient/tests/__init__.py0000664000175400017540000000000012306432770024027 0ustar jenkinsjenkins00000000000000python-novaclient-2.17.0/novaclient/shell.py0000664000175400017540000007251112306432770022255 0ustar jenkinsjenkins00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 OpenStack Foundation # 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. """ Command-line interface to the OpenStack Nova API. """ from __future__ import print_function import argparse import getpass import glob import imp import itertools import logging import os import pkgutil import sys import pkg_resources import six HAS_KEYRING = False all_errors = ValueError try: import keyring HAS_KEYRING = True except ImportError: pass import novaclient import novaclient.auth_plugin from novaclient import client from novaclient import exceptions as exc import novaclient.extension from novaclient.openstack.common import cliutils from novaclient.openstack.common.gettextutils import _ from novaclient.openstack.common import strutils from novaclient import utils from novaclient.v1_1 import shell as shell_v1_1 from novaclient.v3 import shell as shell_v3 DEFAULT_OS_COMPUTE_API_VERSION = "1.1" DEFAULT_NOVA_ENDPOINT_TYPE = 'publicURL' # NOTE(cyeoh): Having the service type dependent on the API version # is pretty ugly, but we have to do this because traditionally the # catalog entry for compute points directly to the V2 API rather than # the root, and then doing version discovery. DEFAULT_NOVA_SERVICE_TYPE_MAP = {'1.1': 'compute', '2': 'compute', '3': 'computev3'} logger = logging.getLogger(__name__) def positive_non_zero_float(text): if text is None: return None try: value = float(text) except ValueError: msg = _("%s must be a float") % text raise argparse.ArgumentTypeError(msg) if value <= 0: msg = _("%s must be greater than 0") % text raise argparse.ArgumentTypeError(msg) return value class SecretsHelper(object): def __init__(self, args, client): self.args = args self.client = client self.key = None self._password = None def _validate_string(self, text): if text is None or len(text) == 0: return False return True def _make_key(self): if self.key is not None: return self.key keys = [ self.client.auth_url, self.client.projectid, self.client.user, self.client.region_name, self.client.endpoint_type, self.client.service_type, self.client.service_name, self.client.volume_service_name, ] for (index, key) in enumerate(keys): if key is None: keys[index] = '?' else: keys[index] = str(keys[index]) self.key = "/".join(keys) return self.key def _prompt_password(self, verify=True): pw = None if hasattr(sys.stdin, 'isatty') and sys.stdin.isatty(): # Check for Ctl-D try: while True: pw1 = getpass.getpass('OS Password: ') if verify: pw2 = getpass.getpass('Please verify: ') else: pw2 = pw1 if pw1 == pw2 and self._validate_string(pw1): pw = pw1 break except EOFError: pass return pw def save(self, auth_token, management_url, tenant_id): if not HAS_KEYRING or not self.args.os_cache: return if (auth_token == self.auth_token and management_url == self.management_url): # Nothing changed.... return if not all([management_url, auth_token, tenant_id]): raise ValueError(_("Unable to save empty management url/auth " "token")) value = "|".join([str(auth_token), str(management_url), str(tenant_id)]) keyring.set_password("novaclient_auth", self._make_key(), value) @property def password(self): # Cache password so we prompt user at most once if self._password: pass elif self._validate_string(self.args.os_password): self._password = self.args.os_password else: verify_pass = strutils.bool_from_string( utils.env("OS_VERIFY_PASSWORD", default=False), True) self._password = self._prompt_password(verify_pass) if not self._password: raise exc.CommandError( 'Expecting a password provided via either ' '--os-password, env[OS_PASSWORD], or ' 'prompted response') return self._password @property def management_url(self): if not HAS_KEYRING or not self.args.os_cache: return None management_url = None try: block = keyring.get_password('novaclient_auth', self._make_key()) if block: _token, management_url, _tenant_id = block.split('|', 2) except all_errors: pass return management_url @property def auth_token(self): # Now is where it gets complicated since we # want to look into the keyring module, if it # exists and see if anything was provided in that # file that we can use. if not HAS_KEYRING or not self.args.os_cache: return None token = None try: block = keyring.get_password('novaclient_auth', self._make_key()) if block: token, _management_url, _tenant_id = block.split('|', 2) except all_errors: pass return token @property def tenant_id(self): if not HAS_KEYRING or not self.args.os_cache: return None tenant_id = None try: block = keyring.get_password('novaclient_auth', self._make_key()) if block: _token, _management_url, tenant_id = block.split('|', 2) except all_errors: pass return tenant_id class NovaClientArgumentParser(argparse.ArgumentParser): def __init__(self, *args, **kwargs): super(NovaClientArgumentParser, self).__init__(*args, **kwargs) def error(self, message): """error(message: string) Prints a usage message incorporating the message to stderr and exits. """ self.print_usage(sys.stderr) #FIXME(lzyeval): if changes occur in argparse.ArgParser._check_value choose_from = ' (choose from' progparts = self.prog.partition(' ') self.exit(2, _("error: %(errmsg)s\nTry '%(mainp)s help %(subp)s'" " for more information.\n") % {'errmsg': message.split(choose_from)[0], 'mainp': progparts[0], 'subp': progparts[2]}) class OpenStackComputeShell(object): def get_base_parser(self): parser = NovaClientArgumentParser( prog='nova', description=__doc__.strip(), epilog='See "nova help COMMAND" ' 'for help on a specific command.', add_help=False, formatter_class=OpenStackHelpFormatter, ) # Global arguments parser.add_argument('-h', '--help', action='store_true', help=argparse.SUPPRESS, ) parser.add_argument('--version', action='version', version=novaclient.__version__) parser.add_argument('--debug', default=False, action='store_true', help=_("Print debugging output")) parser.add_argument('--os-cache', default=strutils.bool_from_string( utils.env('OS_CACHE', default=False), True), action='store_true', help=_("Use the auth token cache. Defaults to False if " "env[OS_CACHE] is not set.")) parser.add_argument('--timings', default=False, action='store_true', help=_("Print call timing info")) parser.add_argument('--timeout', default=600, metavar='', type=positive_non_zero_float, help=_("Set HTTP call timeout (in seconds)")) parser.add_argument('--os-auth-token', default=utils.env('OS_AUTH_TOKEN'), help='Defaults to env[OS_AUTH_TOKEN]') parser.add_argument('--os-username', metavar='', default=utils.env('OS_USERNAME', 'NOVA_USERNAME'), help=_('Defaults to env[OS_USERNAME].')) parser.add_argument('--os_username', help=argparse.SUPPRESS) parser.add_argument('--os-password', metavar='', default=utils.env('OS_PASSWORD', 'NOVA_PASSWORD'), help=_('Defaults to env[OS_PASSWORD].')) parser.add_argument('--os_password', help=argparse.SUPPRESS) parser.add_argument('--os-tenant-name', metavar='', default=utils.env('OS_TENANT_NAME', 'NOVA_PROJECT_ID'), help=_('Defaults to env[OS_TENANT_NAME].')) parser.add_argument('--os_tenant_name', help=argparse.SUPPRESS) parser.add_argument('--os-tenant-id', metavar='', default=utils.env('OS_TENANT_ID'), help=_('Defaults to env[OS_TENANT_ID].')) parser.add_argument('--os-auth-url', metavar='', default=utils.env('OS_AUTH_URL', 'NOVA_URL'), help=_('Defaults to env[OS_AUTH_URL].')) parser.add_argument('--os_auth_url', help=argparse.SUPPRESS) parser.add_argument('--os-region-name', metavar='', default=utils.env('OS_REGION_NAME', 'NOVA_REGION_NAME'), help=_('Defaults to env[OS_REGION_NAME].')) parser.add_argument('--os_region_name', help=argparse.SUPPRESS) parser.add_argument('--os-auth-system', metavar='', default=utils.env('OS_AUTH_SYSTEM'), help='Defaults to env[OS_AUTH_SYSTEM].') parser.add_argument('--os_auth_system', help=argparse.SUPPRESS) parser.add_argument('--service-type', metavar='', help=_('Defaults to compute for most actions')) parser.add_argument('--service_type', help=argparse.SUPPRESS) parser.add_argument('--service-name', metavar='', default=utils.env('NOVA_SERVICE_NAME'), help=_('Defaults to env[NOVA_SERVICE_NAME]')) parser.add_argument('--service_name', help=argparse.SUPPRESS) parser.add_argument('--volume-service-name', metavar='', default=utils.env('NOVA_VOLUME_SERVICE_NAME'), help=_('Defaults to env[NOVA_VOLUME_SERVICE_NAME]')) parser.add_argument('--volume_service_name', help=argparse.SUPPRESS) parser.add_argument('--endpoint-type', metavar='', default=utils.env('NOVA_ENDPOINT_TYPE', default=DEFAULT_NOVA_ENDPOINT_TYPE), help=_('Defaults to env[NOVA_ENDPOINT_TYPE] or ') + DEFAULT_NOVA_ENDPOINT_TYPE + '.') # NOTE(dtroyer): We can't add --endpoint_type here due to argparse # thinking usage-list --end is ambiguous; but it # works fine with only --endpoint-type present # Go figure. I'm leaving this here for doc purposes. #parser.add_argument('--endpoint_type', # help=argparse.SUPPRESS) parser.add_argument('--os-compute-api-version', metavar='', default=utils.env('OS_COMPUTE_API_VERSION', default=DEFAULT_OS_COMPUTE_API_VERSION), help=_('Accepts 1.1 or 3, ' 'defaults to env[OS_COMPUTE_API_VERSION].')) parser.add_argument('--os_compute_api_version', help=argparse.SUPPRESS) parser.add_argument('--os-cacert', metavar='', default=utils.env('OS_CACERT', default=None), help='Specify a CA bundle file to use in ' 'verifying a TLS (https) server certificate. ' 'Defaults to env[OS_CACERT]') parser.add_argument('--insecure', default=utils.env('NOVACLIENT_INSECURE', default=False), action='store_true', help=_("Explicitly allow novaclient to perform \"insecure\" " "SSL (https) requests. The server's certificate will " "not be verified against any certificate authorities. " "This option should be used with caution.")) parser.add_argument('--bypass-url', metavar='', dest='bypass_url', help="Use this API endpoint instead of the Service Catalog") parser.add_argument('--bypass_url', help=argparse.SUPPRESS) # The auth-system-plugins might require some extra options novaclient.auth_plugin.load_auth_system_opts(parser) return parser def get_subcommand_parser(self, version): parser = self.get_base_parser() self.subcommands = {} subparsers = parser.add_subparsers(metavar='') try: actions_module = { '1.1': shell_v1_1, '2': shell_v1_1, '3': shell_v3, }[version] except KeyError: actions_module = shell_v1_1 self._find_actions(subparsers, actions_module) self._find_actions(subparsers, self) for extension in self.extensions: self._find_actions(subparsers, extension.module) self._add_bash_completion_subparser(subparsers) return parser def _discover_extensions(self, version): extensions = [] for name, module in itertools.chain( self._discover_via_python_path(), self._discover_via_contrib_path(version), self._discover_via_entry_points()): extension = novaclient.extension.Extension(name, module) extensions.append(extension) return extensions def _discover_via_python_path(self): for (module_loader, name, _ispkg) in pkgutil.iter_modules(): if name.endswith('_python_novaclient_ext'): if not hasattr(module_loader, 'load_module'): # Python 2.6 compat: actually get an ImpImporter obj module_loader = module_loader.find_module(name) module = module_loader.load_module(name) if hasattr(module, 'extension_name'): name = module.extension_name yield name, module def _discover_via_contrib_path(self, version): module_path = os.path.dirname(os.path.abspath(__file__)) version_str = "v%s" % version.replace('.', '_') ext_path = os.path.join(module_path, version_str, 'contrib') ext_glob = os.path.join(ext_path, "*.py") for ext_path in glob.iglob(ext_glob): name = os.path.basename(ext_path)[:-3] if name == "__init__": continue module = imp.load_source(name, ext_path) yield name, module def _discover_via_entry_points(self): for ep in pkg_resources.iter_entry_points('novaclient.extension'): name = ep.name module = ep.load() yield name, module def _add_bash_completion_subparser(self, subparsers): subparser = subparsers.add_parser('bash_completion', add_help=False, formatter_class=OpenStackHelpFormatter ) self.subcommands['bash_completion'] = subparser subparser.set_defaults(func=self.do_bash_completion) def _find_actions(self, subparsers, actions_module): for attr in (a for a in dir(actions_module) if a.startswith('do_')): # I prefer to be hyphen-separated instead of underscores. command = attr[3:].replace('_', '-') callback = getattr(actions_module, attr) desc = callback.__doc__ or '' action_help = desc.strip() arguments = getattr(callback, 'arguments', []) subparser = subparsers.add_parser(command, help=action_help, description=desc, add_help=False, formatter_class=OpenStackHelpFormatter ) subparser.add_argument('-h', '--help', action='help', help=argparse.SUPPRESS, ) self.subcommands[command] = subparser for (args, kwargs) in arguments: subparser.add_argument(*args, **kwargs) subparser.set_defaults(func=callback) def setup_debugging(self, debug): if not debug: return streamformat = "%(levelname)s (%(module)s:%(lineno)d) %(message)s" # Set up the root logger to debug so that the submodules can # print debug messages logging.basicConfig(level=logging.DEBUG, format=streamformat) def main(self, argv): # Parse args once to find version and debug settings parser = self.get_base_parser() (options, args) = parser.parse_known_args(argv) self.setup_debugging(options.debug) # Discover available auth plugins novaclient.auth_plugin.discover_auth_systems() # build available subcommands based on version self.extensions = self._discover_extensions( options.os_compute_api_version) self._run_extension_hooks('__pre_parse_args__') # NOTE(dtroyer): Hackery to handle --endpoint_type due to argparse # thinking usage-list --end is ambiguous; but it # works fine with only --endpoint-type present # Go figure. if '--endpoint_type' in argv: spot = argv.index('--endpoint_type') argv[spot] = '--endpoint-type' subcommand_parser = self.get_subcommand_parser( options.os_compute_api_version) self.parser = subcommand_parser if options.help or not argv: subcommand_parser.print_help() return 0 args = subcommand_parser.parse_args(argv) self._run_extension_hooks('__post_parse_args__', args) # Short-circuit and deal with help right away. if args.func == self.do_help: self.do_help(args) return 0 elif args.func == self.do_bash_completion: self.do_bash_completion(args) return 0 os_username = args.os_username os_password = None # Fetched and set later as needed os_tenant_name = args.os_tenant_name os_tenant_id = args.os_tenant_id os_auth_url = args.os_auth_url os_region_name = args.os_region_name os_auth_system = args.os_auth_system endpoint_type = args.endpoint_type insecure = args.insecure service_type = args.service_type service_name = args.service_name volume_service_name = args.volume_service_name bypass_url = args.bypass_url os_cache = args.os_cache cacert = args.os_cacert timeout = args.timeout # We may have either, both or none of these. # If we have both, we don't need USERNAME, PASSWORD etc. # Fill in the blanks from the SecretsHelper if possible. # Finally, authenticate unless we have both. # Note if we don't auth we probably don't have a tenant ID so we can't # cache the token. auth_token = args.os_auth_token if args.os_auth_token else None management_url = bypass_url if bypass_url else None if os_auth_system and os_auth_system != "keystone": auth_plugin = novaclient.auth_plugin.load_plugin(os_auth_system) else: auth_plugin = None if not endpoint_type: endpoint_type = DEFAULT_NOVA_ENDPOINT_TYPE if not service_type: os_compute_api_version = (options.os_compute_api_version or DEFAULT_OS_COMPUTE_API_VERSION) try: service_type = DEFAULT_NOVA_SERVICE_TYPE_MAP[ os_compute_api_version] except KeyError: service_type = DEFAULT_NOVA_SERVICE_TYPE_MAP[ DEFAULT_OS_COMPUTE_API_VERSION] service_type = utils.get_service_type(args.func) or service_type # If we have an auth token but no management_url, we must auth anyway. # Expired tokens are handled by client.py:_cs_request must_auth = not (cliutils.isunauthenticated(args.func) or (auth_token and management_url)) #FIXME(usrleon): Here should be restrict for project id same as # for os_username or os_password but for compatibility it is not. if must_auth: if auth_plugin: auth_plugin.parse_opts(args) if not auth_plugin or not auth_plugin.opts: if not os_username: raise exc.CommandError(_("You must provide a username " "via either --os-username or env[OS_USERNAME]")) if not os_tenant_name and not os_tenant_id: raise exc.CommandError(_("You must provide a tenant name " "or tenant id via --os-tenant-name, " "--os-tenant-id, env[OS_TENANT_NAME] " "or env[OS_TENANT_ID]")) if not os_auth_url: if os_auth_system and os_auth_system != 'keystone': os_auth_url = auth_plugin.get_auth_url() if not os_auth_url: raise exc.CommandError(_("You must provide an auth url " "via either --os-auth-url or env[OS_AUTH_URL] " "or specify an auth_system which defines a " "default url with --os-auth-system " "or env[OS_AUTH_SYSTEM]")) if (options.os_compute_api_version and options.os_compute_api_version != '1.0'): if not os_tenant_name and not os_tenant_id: raise exc.CommandError(_("You must provide a tenant name " "or tenant id via --os-tenant-name, " "--os-tenant-id, env[OS_TENANT_NAME] " "or env[OS_TENANT_ID]")) if not os_auth_url: raise exc.CommandError(_("You must provide an auth url " "via either --os-auth-url or env[OS_AUTH_URL]")) self.cs = client.Client(options.os_compute_api_version, os_username, os_password, os_tenant_name, tenant_id=os_tenant_id, auth_url=os_auth_url, insecure=insecure, region_name=os_region_name, endpoint_type=endpoint_type, extensions=self.extensions, service_type=service_type, service_name=service_name, auth_system=os_auth_system, auth_plugin=auth_plugin, auth_token=auth_token, volume_service_name=volume_service_name, timings=args.timings, bypass_url=bypass_url, os_cache=os_cache, http_log_debug=options.debug, cacert=cacert, timeout=timeout) # Now check for the password/token of which pieces of the # identifying keyring key can come from the underlying client if must_auth: helper = SecretsHelper(args, self.cs.client) if (auth_plugin and auth_plugin.opts and "os_password" not in auth_plugin.opts): use_pw = False else: use_pw = True tenant_id = helper.tenant_id # Allow commandline to override cache if not auth_token: auth_token = helper.auth_token if not management_url: management_url = helper.management_url if tenant_id and auth_token and management_url: self.cs.client.tenant_id = tenant_id self.cs.client.auth_token = auth_token self.cs.client.management_url = management_url self.cs.client.password_func = lambda: helper.password elif use_pw: # We're missing something, so auth with user/pass and save # the result in our helper. self.cs.client.password = helper.password self.cs.client.keyring_saver = helper try: # This does a couple of bits which are useful even if we've # got the token + service URL already. It exits fast in that case. if not cliutils.isunauthenticated(args.func): self.cs.authenticate() except exc.Unauthorized: raise exc.CommandError(_("Invalid OpenStack Nova credentials.")) except exc.AuthorizationFailure: raise exc.CommandError(_("Unable to authorize user")) if os_compute_api_version == "3" and service_type != 'image': # NOTE(cyeoh): create an image based client because the # images api is no longer proxied by the V3 API and we # sometimes need to be able to look up images information # via glance when connected to the nova api. image_service_type = 'image' self.cs.image_cs = client.Client( options.os_compute_api_version, os_username, os_password, os_tenant_name, tenant_id=os_tenant_id, auth_url=os_auth_url, insecure=insecure, region_name=os_region_name, endpoint_type=endpoint_type, extensions=self.extensions, service_type=image_service_type, service_name=service_name, auth_system=os_auth_system, auth_plugin=auth_plugin, volume_service_name=volume_service_name, timings=args.timings, bypass_url=bypass_url, os_cache=os_cache, http_log_debug=options.debug, cacert=cacert, timeout=timeout) args.func(self.cs, args) if args.timings: self._dump_timings(self.cs.get_timings()) def _dump_timings(self, timings): class Tyme(object): def __init__(self, url, seconds): self.url = url self.seconds = seconds results = [Tyme(url, end - start) for url, start, end in timings] total = 0.0 for tyme in results: total += tyme.seconds results.append(Tyme("Total", total)) utils.print_list(results, ["url", "seconds"], sortby_index=None) def _run_extension_hooks(self, hook_type, *args, **kwargs): """Run hooks for all registered extensions.""" for extension in self.extensions: extension.run_hooks(hook_type, *args, **kwargs) def do_bash_completion(self, _args): """ Prints all of the commands and options to stdout so that the nova.bash_completion script doesn't have to hard code them. """ commands = set() options = set() for sc_str, sc in self.subcommands.items(): commands.add(sc_str) for option in sc._optionals._option_string_actions.keys(): options.add(option) commands.remove('bash-completion') commands.remove('bash_completion') print(' '.join(commands | options)) @utils.arg('command', metavar='', nargs='?', help='Display help for ') def do_help(self, args): """ Display help about this program or one of its subcommands. """ if args.command: if args.command in self.subcommands: self.subcommands[args.command].print_help() else: raise exc.CommandError(_("'%s' is not a valid subcommand") % args.command) else: self.parser.print_help() # I'm picky about my shell help. class OpenStackHelpFormatter(argparse.HelpFormatter): def start_section(self, heading): # Title-case the headings heading = '%s%s' % (heading[0].upper(), heading[1:]) super(OpenStackHelpFormatter, self).start_section(heading) def main(): try: OpenStackComputeShell().main(map(strutils.safe_decode, sys.argv[1:])) except Exception as e: logger.debug(e, exc_info=1) print("ERROR: %s" % strutils.safe_encode(six.text_type(e)), file=sys.stderr) sys.exit(1) except KeyboardInterrupt as e: print("Shutting down novaclient", file=sys.stderr) sys.exit(1) if __name__ == "__main__": main() python-novaclient-2.17.0/novaclient/utils.py0000664000175400017540000002604212306432770022304 0ustar jenkinsjenkins00000000000000# # 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 pkg_resources import re import sys import textwrap import uuid import prettytable import six from novaclient import exceptions from novaclient.openstack.common import cliutils from novaclient.openstack.common.gettextutils import _ from novaclient.openstack.common import jsonutils from novaclient.openstack.common import strutils arg = cliutils.arg env = cliutils.env VALID_KEY_REGEX = re.compile(r"[\w\.\- :]+$", re.UNICODE) def add_resource_manager_extra_kwargs_hook(f, hook): """Add hook to bind CLI arguments to ResourceManager calls. The `do_foo` calls in shell.py will receive CLI args and then in turn pass them through to the ResourceManager. Before passing through the args, the hooks registered here will be called, giving us a chance to add extra kwargs (taken from the command-line) to what's passed to the ResourceManager. """ if not hasattr(f, 'resource_manager_kwargs_hooks'): f.resource_manager_kwargs_hooks = [] names = [h.__name__ for h in f.resource_manager_kwargs_hooks] if hook.__name__ not in names: f.resource_manager_kwargs_hooks.append(hook) def get_resource_manager_extra_kwargs(f, args, allow_conflicts=False): """Return extra_kwargs by calling resource manager kwargs hooks.""" hooks = getattr(f, "resource_manager_kwargs_hooks", []) extra_kwargs = {} for hook in hooks: hook_kwargs = hook(args) conflicting_keys = set(hook_kwargs.keys()) & set(extra_kwargs.keys()) if conflicting_keys and not allow_conflicts: raise Exception(_("Hook '%(hook_name)s' is attempting to redefine" " attributes '%(conflicting_keys)s'") % {'hook_name': hook_name, 'conflicting_keys': conflicting_keys}) extra_kwargs.update(hook_kwargs) return extra_kwargs def service_type(stype): """ Adds 'service_type' attribute to decorated function. Usage: @service_type('volume') def mymethod(f): ... """ def inner(f): f.service_type = stype return f return inner def get_service_type(f): """ Retrieves service type from function """ return getattr(f, 'service_type', None) def pretty_choice_list(l): return ', '.join("'%s'" % i for i in l) def pretty_choice_dict(d): """Returns a formatted dict as 'key=value'.""" return pretty_choice_list(['%s=%s' % (k, d[k]) for k in sorted(d.keys())]) def print_list(objs, fields, formatters={}, sortby_index=None): if sortby_index is None: sortby = None else: sortby = fields[sortby_index] mixed_case_fields = ['serverId'] pt = prettytable.PrettyTable([f for f in fields], caching=False) pt.align = 'l' for o in objs: row = [] for field in fields: if field in formatters: row.append(formatters[field](o)) else: if field in mixed_case_fields: field_name = field.replace(' ', '_') else: field_name = field.lower().replace(' ', '_') data = getattr(o, field_name, '') if data is None: data = '-' row.append(data) pt.add_row(row) if sortby is not None: result = strutils.safe_encode(pt.get_string(sortby=sortby)) else: result = strutils.safe_encode(pt.get_string()) print(result) def _flatten(data, prefix=None): """Flatten a dict, using name as a prefix for the keys of dict. >>> _flatten('cpu_info', {'arch':'x86_64'}) [('cpu_info_arch': 'x86_64')] """ if isinstance(data, dict): for key, value in six.iteritems(data): new_key = '%s_%s' % (prefix, key) if prefix else key if isinstance(value, (dict, list)): for item in _flatten(value, new_key): yield item else: yield new_key, value else: yield prefix, data def flatten_dict(data): """Return a new dict whose sub-dicts have been merged into the original. Each of the parents keys are prepended to the child's to prevent collisions. Any string elements will be JSON parsed before flattening. >>> flatten_dict({'service': {'host':'cloud9@compute-068', 'id': 143}}) {'service_host': colud9@compute-068', 'service_id': 143} """ data = data.copy() # Try and decode any nested JSON structures. for key, value in six.iteritems(data): if isinstance(value, six.string_types): try: data[key] = json.loads(value) except ValueError: pass return dict(_flatten(data)) def print_dict(d, dict_property="Property", dict_value="Value", wrap=0): pt = prettytable.PrettyTable([dict_property, dict_value], caching=False) pt.align = 'l' for k, v in sorted(d.items()): # convert dict to str to check length if isinstance(v, (dict, list)): v = jsonutils.dumps(v) if wrap > 0: v = textwrap.fill(str(v), wrap) # if value has a newline, add in multiple rows # e.g. fault with stacktrace if v and isinstance(v, six.string_types) and r'\n' in v: lines = v.strip().split(r'\n') col1 = k for line in lines: pt.add_row([col1, line]) col1 = '' else: if v is None: v = '-' pt.add_row([k, v]) result = strutils.safe_encode(pt.get_string()) print(result) def find_resource(manager, name_or_id, **find_args): """Helper for the _find_* methods.""" # for str id which is not uuid (for Flavor search currently) if getattr(manager, 'is_alphanum_id_allowed', False): try: return manager.get(name_or_id) except exceptions.NotFound: pass # try to get entity as integer id try: return manager.get(int(name_or_id)) except (TypeError, ValueError, exceptions.NotFound): pass # now try to get entity as uuid try: tmp_id = strutils.safe_encode(name_or_id) uuid.UUID(tmp_id) return manager.get(tmp_id) except (TypeError, ValueError, exceptions.NotFound): pass try: try: return manager.find(human_id=name_or_id, **find_args) except exceptions.NotFound: pass # finally try to find entity by name try: resource = getattr(manager, 'resource_class', None) name_attr = resource.NAME_ATTR if resource else 'name' kwargs = {name_attr: name_or_id} kwargs.update(find_args) return manager.find(**kwargs) except exceptions.NotFound: msg = _("No %(class)s with a name or ID of '%(name)s' exists.") % \ {'class': manager.resource_class.__name__.lower(), 'name': name_or_id} raise exceptions.CommandError(msg) except exceptions.NoUniqueMatch: msg = (_("Multiple %(class)s matches found for '%(name)s', use an ID " "to be more specific.") % {'class': manager.resource_class.__name__.lower(), 'name': name_or_id}) raise exceptions.CommandError(msg) def _format_servers_list_networks(server): output = [] for (network, addresses) in server.networks.items(): if len(addresses) == 0: continue addresses_csv = ', '.join(addresses) group = "%s=%s" % (network, addresses_csv) output.append(group) return '; '.join(output) def _format_security_groups(groups): return ', '.join(group['name'] for group in groups) def _format_field_name(attr): """Format an object attribute in a human-friendly way.""" # Split at ':' and leave the extension name as-is. parts = attr.rsplit(':', 1) name = parts[-1].replace('_', ' ') # Don't title() on mixed case if name.isupper() or name.islower(): name = name.title() parts[-1] = name return ': '.join(parts) def _make_field_formatter(attr, filters=None): """ Given an object attribute, return a formatted field name and a formatter suitable for passing to print_list. Optionally pass a dict mapping attribute names to a function. The function will be passed the value of the attribute and should return the string to display. """ filter_ = None if filters: filter_ = filters.get(attr) def get_field(obj): field = getattr(obj, attr, '') if field and filter_: field = filter_(field) return field name = _format_field_name(attr) formatter = get_field return name, formatter class HookableMixin(object): """Mixin so classes can register and run hooks.""" _hooks_map = {} @classmethod def add_hook(cls, hook_type, hook_func): if hook_type not in cls._hooks_map: cls._hooks_map[hook_type] = [] cls._hooks_map[hook_type].append(hook_func) @classmethod def run_hooks(cls, hook_type, *args, **kwargs): hook_funcs = cls._hooks_map.get(hook_type) or [] for hook_func in hook_funcs: hook_func(*args, **kwargs) def safe_issubclass(*args): """Like issubclass, but will just return False if not a class.""" try: if issubclass(*args): return True except TypeError: pass return False def import_class(import_str): """Returns a class from a string including module and class.""" mod_str, _sep, class_str = import_str.rpartition('.') __import__(mod_str) return getattr(sys.modules[mod_str], class_str) def _load_entry_point(ep_name, name=None): """Try to load the entry point ep_name that matches name.""" for ep in pkg_resources.iter_entry_points(ep_name, name=name): try: return ep.load() except (ImportError, pkg_resources.UnknownExtra, AttributeError): continue def is_integer_like(val): """Returns validation of a value as an integer.""" try: value = int(val) return True except (TypeError, ValueError, AttributeError): return False def validate_flavor_metadata_keys(keys): for key in keys: valid_name = VALID_KEY_REGEX.match(key) if not valid_name: msg = _('Invalid key: "%s". Keys may only contain letters, ' 'numbers, spaces, underscores, periods, colons and ' 'hyphens.') raise exceptions.CommandError(msg % key) python-novaclient-2.17.0/novaclient/openstack/0000775000175400017540000000000012306433034022547 5ustar jenkinsjenkins00000000000000python-novaclient-2.17.0/novaclient/openstack/common/0000775000175400017540000000000012306433034024037 5ustar jenkinsjenkins00000000000000python-novaclient-2.17.0/novaclient/openstack/common/apiclient/0000775000175400017540000000000012306433034026007 5ustar jenkinsjenkins00000000000000python-novaclient-2.17.0/novaclient/openstack/common/apiclient/fake_client.py0000664000175400017540000001333212306432770030635 0ustar jenkinsjenkins00000000000000# Copyright 2013 OpenStack Foundation # 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. """ A fake server that "responds" to API methods with pre-canned responses. All of these responses come from the spec, so if for some reason the spec's wrong the tests might raise AssertionError. I've indicated in comments the places where actual behavior differs from the spec. """ # W0102: Dangerous default value %s as argument # pylint: disable=W0102 import json import requests import six from six.moves.urllib import parse from novaclient.openstack.common.apiclient import client def assert_has_keys(dct, required=[], optional=[]): for k in required: try: assert k in dct except AssertionError: extra_keys = set(dct.keys()).difference(set(required + optional)) raise AssertionError("found unexpected keys: %s" % list(extra_keys)) class TestResponse(requests.Response): """Wrap requests.Response and provide a convenient initialization. """ def __init__(self, data): super(TestResponse, self).__init__() self._content_consumed = True if isinstance(data, dict): self.status_code = data.get('status_code', 200) # Fake the text attribute to streamline Response creation text = data.get('text', "") if isinstance(text, (dict, list)): self._content = json.dumps(text) default_headers = { "Content-Type": "application/json", } else: self._content = text default_headers = {} if six.PY3 and isinstance(self._content, six.string_types): self._content = self._content.encode('utf-8', 'strict') self.headers = data.get('headers') or default_headers else: self.status_code = data def __eq__(self, other): return (self.status_code == other.status_code and self.headers == other.headers and self._content == other._content) class FakeHTTPClient(client.HTTPClient): def __init__(self, *args, **kwargs): self.callstack = [] self.fixtures = kwargs.pop("fixtures", None) or {} if not args and not "auth_plugin" in kwargs: args = (None, ) super(FakeHTTPClient, self).__init__(*args, **kwargs) def assert_called(self, method, url, body=None, pos=-1): """Assert than an API method was just called. """ expected = (method, url) called = self.callstack[pos][0:2] assert self.callstack, \ "Expected %s %s but no calls were made." % expected assert expected == called, 'Expected %s %s; got %s %s' % \ (expected + called) if body is not None: if self.callstack[pos][3] != body: raise AssertionError('%r != %r' % (self.callstack[pos][3], body)) def assert_called_anytime(self, method, url, body=None): """Assert than an API method was called anytime in the test. """ expected = (method, url) assert self.callstack, \ "Expected %s %s but no calls were made." % expected found = False entry = None for entry in self.callstack: if expected == entry[0:2]: found = True break assert found, 'Expected %s %s; got %s' % \ (method, url, self.callstack) if body is not None: assert entry[3] == body, "%s != %s" % (entry[3], body) self.callstack = [] def clear_callstack(self): self.callstack = [] def authenticate(self): pass def client_request(self, client, method, url, **kwargs): # Check that certain things are called correctly if method in ["GET", "DELETE"]: assert "json" not in kwargs # Note the call self.callstack.append( (method, url, kwargs.get("headers") or {}, kwargs.get("json") or kwargs.get("data"))) try: fixture = self.fixtures[url][method] except KeyError: pass else: return TestResponse({"headers": fixture[0], "text": fixture[1]}) # Call the method args = parse.parse_qsl(parse.urlparse(url)[4]) kwargs.update(args) munged_url = url.rsplit('?', 1)[0] munged_url = munged_url.strip('/').replace('/', '_').replace('.', '_') munged_url = munged_url.replace('-', '_') callback = "%s_%s" % (method.lower(), munged_url) if not hasattr(self, callback): raise AssertionError('Called unknown API method: %s %s, ' 'expected fakes method name: %s' % (method, url, callback)) resp = getattr(self, callback)(**kwargs) if len(resp) == 3: status, headers, body = resp else: status, body = resp headers = {} return TestResponse({ "status_code": status, "text": body, "headers": headers, }) python-novaclient-2.17.0/novaclient/openstack/common/apiclient/base.py0000664000175400017540000003750612306432770027314 0ustar jenkinsjenkins00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 OpenStack Foundation # Copyright 2012 Grid Dynamics # Copyright 2013 OpenStack Foundation # 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. """ Base utilities to build API operation managers and objects on top of. """ # E1102: %s is not callable # pylint: disable=E1102 import abc import copy import six from six.moves.urllib import parse from novaclient.openstack.common.apiclient import exceptions from novaclient.openstack.common import strutils def getid(obj): """Return id if argument is a Resource. Abstracts the common pattern of allowing both an object or an object's ID (UUID) as a parameter when dealing with relationships. """ try: if obj.uuid: return obj.uuid except AttributeError: pass try: return obj.id except AttributeError: return obj # TODO(aababilov): call run_hooks() in HookableMixin's child classes class HookableMixin(object): """Mixin so classes can register and run hooks.""" _hooks_map = {} @classmethod def add_hook(cls, hook_type, hook_func): """Add a new hook of specified type. :param cls: class that registers hooks :param hook_type: hook type, e.g., '__pre_parse_args__' :param hook_func: hook function """ if hook_type not in cls._hooks_map: cls._hooks_map[hook_type] = [] cls._hooks_map[hook_type].append(hook_func) @classmethod def run_hooks(cls, hook_type, *args, **kwargs): """Run all hooks of specified type. :param cls: class that registers hooks :param hook_type: hook type, e.g., '__pre_parse_args__' :param **args: args to be passed to every hook function :param **kwargs: kwargs to be passed to every hook function """ hook_funcs = cls._hooks_map.get(hook_type) or [] for hook_func in hook_funcs: hook_func(*args, **kwargs) class BaseManager(HookableMixin): """Basic manager type providing common operations. Managers interact with a particular type of API (servers, flavors, images, etc.) and provide CRUD operations for them. """ resource_class = None def __init__(self, client): """Initializes BaseManager with `client`. :param client: instance of BaseClient descendant for HTTP requests """ super(BaseManager, self).__init__() self.client = client def _list(self, url, response_key, obj_class=None, json=None): """List the collection. :param url: a partial URL, e.g., '/servers' :param response_key: the key to be looked up in response dictionary, e.g., 'servers' :param obj_class: class for constructing the returned objects (self.resource_class will be used by default) :param json: data that will be encoded as JSON and passed in POST request (GET will be sent by default) """ if json: body = self.client.post(url, json=json).json() else: body = self.client.get(url).json() if obj_class is None: obj_class = self.resource_class data = body[response_key] # NOTE(ja): keystone returns values as list as {'values': [ ... ]} # unlike other services which just return the list... try: data = data['values'] except (KeyError, TypeError): pass return [obj_class(self, res, loaded=True) for res in data if res] def _get(self, url, response_key): """Get an object from collection. :param url: a partial URL, e.g., '/servers' :param response_key: the key to be looked up in response dictionary, e.g., 'server' """ body = self.client.get(url).json() return self.resource_class(self, body[response_key], loaded=True) def _head(self, url): """Retrieve request headers for an object. :param url: a partial URL, e.g., '/servers' """ resp = self.client.head(url) return resp.status_code == 204 def _post(self, url, json, response_key, return_raw=False): """Create an object. :param url: a partial URL, e.g., '/servers' :param json: data that will be encoded as JSON and passed in POST request (GET will be sent by default) :param response_key: the key to be looked up in response dictionary, e.g., 'servers' :param return_raw: flag to force returning raw JSON instead of Python object of self.resource_class """ body = self.client.post(url, json=json).json() if return_raw: return body[response_key] return self.resource_class(self, body[response_key]) def _put(self, url, json=None, response_key=None): """Update an object with PUT method. :param url: a partial URL, e.g., '/servers' :param json: data that will be encoded as JSON and passed in POST request (GET will be sent by default) :param response_key: the key to be looked up in response dictionary, e.g., 'servers' """ resp = self.client.put(url, json=json) # PUT requests may not return a body if resp.content: body = resp.json() if response_key is not None: return self.resource_class(self, body[response_key]) else: return self.resource_class(self, body) def _patch(self, url, json=None, response_key=None): """Update an object with PATCH method. :param url: a partial URL, e.g., '/servers' :param json: data that will be encoded as JSON and passed in POST request (GET will be sent by default) :param response_key: the key to be looked up in response dictionary, e.g., 'servers' """ body = self.client.patch(url, json=json).json() if response_key is not None: return self.resource_class(self, body[response_key]) else: return self.resource_class(self, body) def _delete(self, url): """Delete an object. :param url: a partial URL, e.g., '/servers/my-server' """ return self.client.delete(url) @six.add_metaclass(abc.ABCMeta) class ManagerWithFind(BaseManager): """Manager with additional `find()`/`findall()` methods.""" @abc.abstractmethod def list(self): pass def find(self, **kwargs): """Find a single item with attributes matching ``**kwargs``. This isn't very efficient: it loads the entire list then filters on the Python side. """ matches = self.findall(**kwargs) num_matches = len(matches) if num_matches == 0: msg = "No %s matching %s." % (self.resource_class.__name__, kwargs) raise exceptions.NotFound(msg) elif num_matches > 1: raise exceptions.NoUniqueMatch() else: return matches[0] def findall(self, **kwargs): """Find all items with attributes matching ``**kwargs``. This isn't very efficient: it loads the entire list then filters on the Python side. """ found = [] searches = kwargs.items() for obj in self.list(): try: if all(getattr(obj, attr) == value for (attr, value) in searches): found.append(obj) except AttributeError: continue return found class CrudManager(BaseManager): """Base manager class for manipulating entities. Children of this class are expected to define a `collection_key` and `key`. - `collection_key`: Usually a plural noun by convention (e.g. `entities`); used to refer collections in both URL's (e.g. `/v3/entities`) and JSON objects containing a list of member resources (e.g. `{'entities': [{}, {}, {}]}`). - `key`: Usually a singular noun by convention (e.g. `entity`); used to refer to an individual member of the collection. """ collection_key = None key = None def build_url(self, base_url=None, **kwargs): """Builds a resource URL for the given kwargs. Given an example collection where `collection_key = 'entities'` and `key = 'entity'`, the following URL's could be generated. By default, the URL will represent a collection of entities, e.g.:: /entities If kwargs contains an `entity_id`, then the URL will represent a specific member, e.g.:: /entities/{entity_id} :param base_url: if provided, the generated URL will be appended to it """ url = base_url if base_url is not None else '' url += '/%s' % self.collection_key # do we have a specific entity? entity_id = kwargs.get('%s_id' % self.key) if entity_id is not None: url += '/%s' % entity_id return url def _filter_kwargs(self, kwargs): """Drop null values and handle ids.""" for key, ref in six.iteritems(kwargs.copy()): if ref is None: kwargs.pop(key) else: if isinstance(ref, Resource): kwargs.pop(key) kwargs['%s_id' % key] = getid(ref) return kwargs def create(self, **kwargs): kwargs = self._filter_kwargs(kwargs) return self._post( self.build_url(**kwargs), {self.key: kwargs}, self.key) def get(self, **kwargs): kwargs = self._filter_kwargs(kwargs) return self._get( self.build_url(**kwargs), self.key) def head(self, **kwargs): kwargs = self._filter_kwargs(kwargs) return self._head(self.build_url(**kwargs)) def list(self, base_url=None, **kwargs): """List the collection. :param base_url: if provided, the generated URL will be appended to it """ kwargs = self._filter_kwargs(kwargs) return self._list( '%(base_url)s%(query)s' % { 'base_url': self.build_url(base_url=base_url, **kwargs), 'query': '?%s' % parse.urlencode(kwargs) if kwargs else '', }, self.collection_key) def put(self, base_url=None, **kwargs): """Update an element. :param base_url: if provided, the generated URL will be appended to it """ kwargs = self._filter_kwargs(kwargs) return self._put(self.build_url(base_url=base_url, **kwargs)) def update(self, **kwargs): kwargs = self._filter_kwargs(kwargs) params = kwargs.copy() params.pop('%s_id' % self.key) return self._patch( self.build_url(**kwargs), {self.key: params}, self.key) def delete(self, **kwargs): kwargs = self._filter_kwargs(kwargs) return self._delete( self.build_url(**kwargs)) def find(self, base_url=None, **kwargs): """Find a single item with attributes matching ``**kwargs``. :param base_url: if provided, the generated URL will be appended to it """ kwargs = self._filter_kwargs(kwargs) rl = self._list( '%(base_url)s%(query)s' % { 'base_url': self.build_url(base_url=base_url, **kwargs), 'query': '?%s' % parse.urlencode(kwargs) if kwargs else '', }, self.collection_key) num = len(rl) if num == 0: msg = "No %s matching %s." % (self.resource_class.__name__, kwargs) raise exceptions.NotFound(404, msg) elif num > 1: raise exceptions.NoUniqueMatch else: return rl[0] class Extension(HookableMixin): """Extension descriptor.""" SUPPORTED_HOOKS = ('__pre_parse_args__', '__post_parse_args__') manager_class = None def __init__(self, name, module): super(Extension, self).__init__() self.name = name self.module = module self._parse_extension_module() def _parse_extension_module(self): self.manager_class = None for attr_name, attr_value in self.module.__dict__.items(): if attr_name in self.SUPPORTED_HOOKS: self.add_hook(attr_name, attr_value) else: try: if issubclass(attr_value, BaseManager): self.manager_class = attr_value except TypeError: pass def __repr__(self): return "" % self.name class Resource(object): """Base class for OpenStack resources (tenant, user, etc.). This is pretty much just a bag for attributes. """ HUMAN_ID = False NAME_ATTR = 'name' def __init__(self, manager, info, loaded=False): """Populate and bind to a manager. :param manager: BaseManager object :param info: dictionary representing resource attributes :param loaded: prevent lazy-loading if set to True """ self.manager = manager self._info = info self._add_details(info) self._loaded = loaded def __repr__(self): reprkeys = sorted(k for k in self.__dict__.keys() if k[0] != '_' and k != 'manager') info = ", ".join("%s=%s" % (k, getattr(self, k)) for k in reprkeys) return "<%s %s>" % (self.__class__.__name__, info) @property def human_id(self): """Human-readable ID which can be used for bash completion. """ if self.NAME_ATTR in self.__dict__ and self.HUMAN_ID: return strutils.to_slug(getattr(self, self.NAME_ATTR)) return None def _add_details(self, info): for (k, v) in six.iteritems(info): try: setattr(self, k, v) self._info[k] = v except AttributeError: # In this case we already defined the attribute on the class pass def __getattr__(self, k): if k not in self.__dict__: #NOTE(bcwaldon): disallow lazy-loading if already loaded once if not self.is_loaded(): self.get() return self.__getattr__(k) raise AttributeError(k) else: return self.__dict__[k] def get(self): """Support for lazy loading details. Some clients, such as novaclient have the option to lazy load the details, details which can be loaded with this function. """ # set_loaded() first ... so if we have to bail, we know we tried. self.set_loaded(True) if not hasattr(self.manager, 'get'): return new = self.manager.get(self.id) if new: self._add_details(new._info) def __eq__(self, other): if not isinstance(other, Resource): return NotImplemented # two resources of different types are not equal if not isinstance(other, self.__class__): return False if hasattr(self, 'id') and hasattr(other, 'id'): return self.id == other.id return self._info == other._info def is_loaded(self): return self._loaded def set_loaded(self, val): self._loaded = val def to_dict(self): return copy.deepcopy(self._info) python-novaclient-2.17.0/novaclient/openstack/common/apiclient/exceptions.py0000664000175400017540000002767412306432770030570 0ustar jenkinsjenkins00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 Nebula, Inc. # Copyright 2013 Alessio Ababilov # Copyright 2013 OpenStack Foundation # 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. """ Exception definitions. """ import inspect import sys import six class ClientException(Exception): """The base exception class for all exceptions this library raises. """ pass class MissingArgs(ClientException): """Supplied arguments are not sufficient for calling a function.""" def __init__(self, missing): self.missing = missing msg = "Missing argument(s): %s" % ", ".join(missing) super(MissingArgs, self).__init__(msg) class ValidationError(ClientException): """Error in validation on API client side.""" pass class UnsupportedVersion(ClientException): """User is trying to use an unsupported version of the API.""" pass class CommandError(ClientException): """Error in CLI tool.""" pass class AuthorizationFailure(ClientException): """Cannot authorize API client.""" pass class ConnectionRefused(ClientException): """Cannot connect to API service.""" pass class AuthPluginOptionsMissing(AuthorizationFailure): """Auth plugin misses some options.""" def __init__(self, opt_names): super(AuthPluginOptionsMissing, self).__init__( "Authentication failed. Missing options: %s" % ", ".join(opt_names)) self.opt_names = opt_names class AuthSystemNotFound(AuthorizationFailure): """User has specified a AuthSystem that is not installed.""" def __init__(self, auth_system): super(AuthSystemNotFound, self).__init__( "AuthSystemNotFound: %s" % repr(auth_system)) self.auth_system = auth_system class NoUniqueMatch(ClientException): """Multiple entities found instead of one.""" pass class EndpointException(ClientException): """Something is rotten in Service Catalog.""" pass class EndpointNotFound(EndpointException): """Could not find requested endpoint in Service Catalog.""" pass class AmbiguousEndpoints(EndpointException): """Found more than one matching endpoint in Service Catalog.""" def __init__(self, endpoints=None): super(AmbiguousEndpoints, self).__init__( "AmbiguousEndpoints: %s" % repr(endpoints)) self.endpoints = endpoints class HttpError(ClientException): """The base exception class for all HTTP exceptions. """ http_status = 0 message = "HTTP Error" def __init__(self, message=None, details=None, response=None, request_id=None, url=None, method=None, http_status=None): self.http_status = http_status or self.http_status self.message = message or self.message self.details = details self.request_id = request_id self.response = response self.url = url self.method = method formatted_string = "%s (HTTP %s)" % (self.message, self.http_status) if request_id: formatted_string += " (Request-ID: %s)" % request_id super(HttpError, self).__init__(formatted_string) class HTTPRedirection(HttpError): """HTTP Redirection.""" message = "HTTP Redirection" class HTTPClientError(HttpError): """Client-side HTTP error. Exception for cases in which the client seems to have erred. """ message = "HTTP Client Error" class HttpServerError(HttpError): """Server-side HTTP error. Exception for cases in which the server is aware that it has erred or is incapable of performing the request. """ message = "HTTP Server Error" class MultipleChoices(HTTPRedirection): """HTTP 300 - Multiple Choices. Indicates multiple options for the resource that the client may follow. """ http_status = 300 message = "Multiple Choices" class BadRequest(HTTPClientError): """HTTP 400 - Bad Request. The request cannot be fulfilled due to bad syntax. """ http_status = 400 message = "Bad Request" class Unauthorized(HTTPClientError): """HTTP 401 - Unauthorized. Similar to 403 Forbidden, but specifically for use when authentication is required and has failed or has not yet been provided. """ http_status = 401 message = "Unauthorized" class PaymentRequired(HTTPClientError): """HTTP 402 - Payment Required. Reserved for future use. """ http_status = 402 message = "Payment Required" class Forbidden(HTTPClientError): """HTTP 403 - Forbidden. The request was a valid request, but the server is refusing to respond to it. """ http_status = 403 message = "Forbidden" class NotFound(HTTPClientError): """HTTP 404 - Not Found. The requested resource could not be found but may be available again in the future. """ http_status = 404 message = "Not Found" class MethodNotAllowed(HTTPClientError): """HTTP 405 - Method Not Allowed. A request was made of a resource using a request method not supported by that resource. """ http_status = 405 message = "Method Not Allowed" class NotAcceptable(HTTPClientError): """HTTP 406 - Not Acceptable. The requested resource is only capable of generating content not acceptable according to the Accept headers sent in the request. """ http_status = 406 message = "Not Acceptable" class ProxyAuthenticationRequired(HTTPClientError): """HTTP 407 - Proxy Authentication Required. The client must first authenticate itself with the proxy. """ http_status = 407 message = "Proxy Authentication Required" class RequestTimeout(HTTPClientError): """HTTP 408 - Request Timeout. The server timed out waiting for the request. """ http_status = 408 message = "Request Timeout" class Conflict(HTTPClientError): """HTTP 409 - Conflict. Indicates that the request could not be processed because of conflict in the request, such as an edit conflict. """ http_status = 409 message = "Conflict" class Gone(HTTPClientError): """HTTP 410 - Gone. Indicates that the resource requested is no longer available and will not be available again. """ http_status = 410 message = "Gone" class LengthRequired(HTTPClientError): """HTTP 411 - Length Required. The request did not specify the length of its content, which is required by the requested resource. """ http_status = 411 message = "Length Required" class PreconditionFailed(HTTPClientError): """HTTP 412 - Precondition Failed. The server does not meet one of the preconditions that the requester put on the request. """ http_status = 412 message = "Precondition Failed" class RequestEntityTooLarge(HTTPClientError): """HTTP 413 - Request Entity Too Large. The request is larger than the server is willing or able to process. """ http_status = 413 message = "Request Entity Too Large" def __init__(self, *args, **kwargs): try: self.retry_after = int(kwargs.pop('retry_after')) except (KeyError, ValueError): self.retry_after = 0 super(RequestEntityTooLarge, self).__init__(*args, **kwargs) class RequestUriTooLong(HTTPClientError): """HTTP 414 - Request-URI Too Long. The URI provided was too long for the server to process. """ http_status = 414 message = "Request-URI Too Long" class UnsupportedMediaType(HTTPClientError): """HTTP 415 - Unsupported Media Type. The request entity has a media type which the server or resource does not support. """ http_status = 415 message = "Unsupported Media Type" class RequestedRangeNotSatisfiable(HTTPClientError): """HTTP 416 - Requested Range Not Satisfiable. The client has asked for a portion of the file, but the server cannot supply that portion. """ http_status = 416 message = "Requested Range Not Satisfiable" class ExpectationFailed(HTTPClientError): """HTTP 417 - Expectation Failed. The server cannot meet the requirements of the Expect request-header field. """ http_status = 417 message = "Expectation Failed" class UnprocessableEntity(HTTPClientError): """HTTP 422 - Unprocessable Entity. The request was well-formed but was unable to be followed due to semantic errors. """ http_status = 422 message = "Unprocessable Entity" class InternalServerError(HttpServerError): """HTTP 500 - Internal Server Error. A generic error message, given when no more specific message is suitable. """ http_status = 500 message = "Internal Server Error" # NotImplemented is a python keyword. class HttpNotImplemented(HttpServerError): """HTTP 501 - Not Implemented. The server either does not recognize the request method, or it lacks the ability to fulfill the request. """ http_status = 501 message = "Not Implemented" class BadGateway(HttpServerError): """HTTP 502 - Bad Gateway. The server was acting as a gateway or proxy and received an invalid response from the upstream server. """ http_status = 502 message = "Bad Gateway" class ServiceUnavailable(HttpServerError): """HTTP 503 - Service Unavailable. The server is currently unavailable. """ http_status = 503 message = "Service Unavailable" class GatewayTimeout(HttpServerError): """HTTP 504 - Gateway Timeout. The server was acting as a gateway or proxy and did not receive a timely response from the upstream server. """ http_status = 504 message = "Gateway Timeout" class HttpVersionNotSupported(HttpServerError): """HTTP 505 - HttpVersion Not Supported. The server does not support the HTTP protocol version used in the request. """ http_status = 505 message = "HTTP Version Not Supported" # _code_map contains all the classes that have http_status attribute. _code_map = dict( (getattr(obj, 'http_status', None), obj) for name, obj in six.iteritems(vars(sys.modules[__name__])) if inspect.isclass(obj) and getattr(obj, 'http_status', False) ) def from_response(response, method, url): """Returns an instance of :class:`HttpError` or subclass based on response. :param response: instance of `requests.Response` class :param method: HTTP method used for request :param url: URL used for request """ kwargs = { "http_status": response.status_code, "response": response, "method": method, "url": url, "request_id": response.headers.get("x-compute-request-id"), } if "retry-after" in response.headers: kwargs["retry_after"] = response.headers["retry-after"] content_type = response.headers.get("Content-Type", "") if content_type.startswith("application/json"): try: body = response.json() except ValueError: pass else: if isinstance(body, dict): error = list(body.values())[0] kwargs["message"] = error.get("message") kwargs["details"] = error.get("details") elif content_type.startswith("text/"): kwargs["details"] = response.text try: cls = _code_map[response.status_code] except KeyError: if 500 <= response.status_code < 600: cls = HttpServerError elif 400 <= response.status_code < 500: cls = HTTPClientError else: cls = HttpError return cls(**kwargs) python-novaclient-2.17.0/novaclient/openstack/common/apiclient/client.py0000664000175400017540000003066612306432770027660 0ustar jenkinsjenkins00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 OpenStack Foundation # Copyright 2011 Piston Cloud Computing, Inc. # Copyright 2013 Alessio Ababilov # Copyright 2013 Grid Dynamics # Copyright 2013 OpenStack Foundation # 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 Client interface. Handles the REST calls and responses. """ # E0202: An attribute inherited from %s hide this method # pylint: disable=E0202 import logging import time try: import simplejson as json except ImportError: import json import requests from novaclient.openstack.common.apiclient import exceptions from novaclient.openstack.common import importutils _logger = logging.getLogger(__name__) class HTTPClient(object): """This client handles sending HTTP requests to OpenStack servers. Features: - share authentication information between several clients to different services (e.g., for compute and image clients); - reissue authentication request for expired tokens; - encode/decode JSON bodies; - raise exceptions on HTTP errors; - pluggable authentication; - store authentication information in a keyring; - store time spent for requests; - register clients for particular services, so one can use `http_client.identity` or `http_client.compute`; - log requests and responses in a format that is easy to copy-and-paste into terminal and send the same request with curl. """ user_agent = "novaclient.openstack.common.apiclient" def __init__(self, auth_plugin, region_name=None, endpoint_type="publicURL", original_ip=None, verify=True, cert=None, timeout=None, timings=False, keyring_saver=None, debug=False, user_agent=None, http=None): self.auth_plugin = auth_plugin self.endpoint_type = endpoint_type self.region_name = region_name self.original_ip = original_ip self.timeout = timeout self.verify = verify self.cert = cert self.keyring_saver = keyring_saver self.debug = debug self.user_agent = user_agent or self.user_agent self.times = [] # [("item", starttime, endtime), ...] self.timings = timings # requests within the same session can reuse TCP connections from pool self.http = http or requests.Session() self.cached_token = None def _http_log_req(self, method, url, kwargs): if not self.debug: return string_parts = [ "curl -i", "-X '%s'" % method, "'%s'" % url, ] for element in kwargs['headers']: header = "-H '%s: %s'" % (element, kwargs['headers'][element]) string_parts.append(header) _logger.debug("REQ: %s" % " ".join(string_parts)) if 'data' in kwargs: _logger.debug("REQ BODY: %s\n" % (kwargs['data'])) def _http_log_resp(self, resp): if not self.debug: return _logger.debug( "RESP: [%s] %s\n", resp.status_code, resp.headers) if resp._content_consumed: _logger.debug( "RESP BODY: %s\n", resp.text) def serialize(self, kwargs): if kwargs.get('json') is not None: kwargs['headers']['Content-Type'] = 'application/json' kwargs['data'] = json.dumps(kwargs['json']) try: del kwargs['json'] except KeyError: pass def get_timings(self): return self.times def reset_timings(self): self.times = [] def request(self, method, url, **kwargs): """Send an http request with the specified characteristics. Wrapper around `requests.Session.request` to handle tasks such as setting headers, JSON encoding/decoding, and error handling. :param method: method of HTTP request :param url: URL of HTTP request :param kwargs: any other parameter that can be passed to ' requests.Session.request (such as `headers`) or `json` that will be encoded as JSON and used as `data` argument """ kwargs.setdefault("headers", kwargs.get("headers", {})) kwargs["headers"]["User-Agent"] = self.user_agent if self.original_ip: kwargs["headers"]["Forwarded"] = "for=%s;by=%s" % ( self.original_ip, self.user_agent) if self.timeout is not None: kwargs.setdefault("timeout", self.timeout) kwargs.setdefault("verify", self.verify) if self.cert is not None: kwargs.setdefault("cert", self.cert) self.serialize(kwargs) self._http_log_req(method, url, kwargs) if self.timings: start_time = time.time() resp = self.http.request(method, url, **kwargs) if self.timings: self.times.append(("%s %s" % (method, url), start_time, time.time())) self._http_log_resp(resp) if resp.status_code >= 400: _logger.debug( "Request returned failure status: %s", resp.status_code) raise exceptions.from_response(resp, method, url) return resp @staticmethod def concat_url(endpoint, url): """Concatenate endpoint and final URL. E.g., "http://keystone/v2.0/" and "/tokens" are concatenated to "http://keystone/v2.0/tokens". :param endpoint: the base URL :param url: the final URL """ return "%s/%s" % (endpoint.rstrip("/"), url.strip("/")) def client_request(self, client, method, url, **kwargs): """Send an http request using `client`'s endpoint and specified `url`. If request was rejected as unauthorized (possibly because the token is expired), issue one authorization attempt and send the request once again. :param client: instance of BaseClient descendant :param method: method of HTTP request :param url: URL of HTTP request :param kwargs: any other parameter that can be passed to ' `HTTPClient.request` """ filter_args = { "endpoint_type": client.endpoint_type or self.endpoint_type, "service_type": client.service_type, } token, endpoint = (self.cached_token, client.cached_endpoint) just_authenticated = False if not (token and endpoint): try: token, endpoint = self.auth_plugin.token_and_endpoint( **filter_args) except exceptions.EndpointException: pass if not (token and endpoint): self.authenticate() just_authenticated = True token, endpoint = self.auth_plugin.token_and_endpoint( **filter_args) if not (token and endpoint): raise exceptions.AuthorizationFailure( "Cannot find endpoint or token for request") old_token_endpoint = (token, endpoint) kwargs.setdefault("headers", {})["X-Auth-Token"] = token self.cached_token = token client.cached_endpoint = endpoint # Perform the request once. If we get Unauthorized, then it # might be because the auth token expired, so try to # re-authenticate and try again. If it still fails, bail. try: return self.request( method, self.concat_url(endpoint, url), **kwargs) except exceptions.Unauthorized as unauth_ex: if just_authenticated: raise self.cached_token = None client.cached_endpoint = None self.authenticate() try: token, endpoint = self.auth_plugin.token_and_endpoint( **filter_args) except exceptions.EndpointException: raise unauth_ex if (not (token and endpoint) or old_token_endpoint == (token, endpoint)): raise unauth_ex self.cached_token = token client.cached_endpoint = endpoint kwargs["headers"]["X-Auth-Token"] = token return self.request( method, self.concat_url(endpoint, url), **kwargs) def add_client(self, base_client_instance): """Add a new instance of :class:`BaseClient` descendant. `self` will store a reference to `base_client_instance`. Example: >>> def test_clients(): ... from keystoneclient.auth import keystone ... from openstack.common.apiclient import client ... auth = keystone.KeystoneAuthPlugin( ... username="user", password="pass", tenant_name="tenant", ... auth_url="http://auth:5000/v2.0") ... openstack_client = client.HTTPClient(auth) ... # create nova client ... from novaclient.v1_1 import client ... client.Client(openstack_client) ... # create keystone client ... from keystoneclient.v2_0 import client ... client.Client(openstack_client) ... # use them ... openstack_client.identity.tenants.list() ... openstack_client.compute.servers.list() """ service_type = base_client_instance.service_type if service_type and not hasattr(self, service_type): setattr(self, service_type, base_client_instance) def authenticate(self): self.auth_plugin.authenticate(self) # Store the authentication results in the keyring for later requests if self.keyring_saver: self.keyring_saver.save(self) class BaseClient(object): """Top-level object to access the OpenStack API. This client uses :class:`HTTPClient` to send requests. :class:`HTTPClient` will handle a bunch of issues such as authentication. """ service_type = None endpoint_type = None # "publicURL" will be used cached_endpoint = None def __init__(self, http_client, extensions=None): self.http_client = http_client http_client.add_client(self) # Add in any extensions... if extensions: for extension in extensions: if extension.manager_class: setattr(self, extension.name, extension.manager_class(self)) def client_request(self, method, url, **kwargs): return self.http_client.client_request( self, method, url, **kwargs) def head(self, url, **kwargs): return self.client_request("HEAD", url, **kwargs) def get(self, url, **kwargs): return self.client_request("GET", url, **kwargs) def post(self, url, **kwargs): return self.client_request("POST", url, **kwargs) def put(self, url, **kwargs): return self.client_request("PUT", url, **kwargs) def delete(self, url, **kwargs): return self.client_request("DELETE", url, **kwargs) def patch(self, url, **kwargs): return self.client_request("PATCH", url, **kwargs) @staticmethod def get_class(api_name, version, version_map): """Returns the client class for the requested API version :param api_name: the name of the API, e.g. 'compute', 'image', etc :param version: the requested API version :param version_map: a dict of client classes keyed by version :rtype: a client class for the requested API version """ try: client_path = version_map[str(version)] except (KeyError, ValueError): msg = "Invalid %s client version '%s'. must be one of: %s" % ( (api_name, version, ', '.join(version_map.keys()))) raise exceptions.UnsupportedVersion(msg) return importutils.import_class(client_path) python-novaclient-2.17.0/novaclient/openstack/common/apiclient/auth.py0000664000175400017540000001557612306432770027346 0ustar jenkinsjenkins00000000000000# Copyright 2013 OpenStack Foundation # Copyright 2013 Spanish National Research Council. # 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. # E0202: An attribute inherited from %s hide this method # pylint: disable=E0202 import abc import argparse import os import six from stevedore import extension from novaclient.openstack.common.apiclient import exceptions _discovered_plugins = {} def discover_auth_systems(): """Discover the available auth-systems. This won't take into account the old style auth-systems. """ global _discovered_plugins _discovered_plugins = {} def add_plugin(ext): _discovered_plugins[ext.name] = ext.plugin ep_namespace = "novaclient.openstack.common.apiclient.auth" mgr = extension.ExtensionManager(ep_namespace) mgr.map(add_plugin) def load_auth_system_opts(parser): """Load options needed by the available auth-systems into a parser. This function will try to populate the parser with options from the available plugins. """ group = parser.add_argument_group("Common auth options") BaseAuthPlugin.add_common_opts(group) for name, auth_plugin in six.iteritems(_discovered_plugins): group = parser.add_argument_group( "Auth-system '%s' options" % name, conflict_handler="resolve") auth_plugin.add_opts(group) def load_plugin(auth_system): try: plugin_class = _discovered_plugins[auth_system] except KeyError: raise exceptions.AuthSystemNotFound(auth_system) return plugin_class(auth_system=auth_system) def load_plugin_from_args(args): """Load required plugin and populate it with options. Try to guess auth system if it is not specified. Systems are tried in alphabetical order. :type args: argparse.Namespace :raises: AuthPluginOptionsMissing """ auth_system = args.os_auth_system if auth_system: plugin = load_plugin(auth_system) plugin.parse_opts(args) plugin.sufficient_options() return plugin for plugin_auth_system in sorted(six.iterkeys(_discovered_plugins)): plugin_class = _discovered_plugins[plugin_auth_system] plugin = plugin_class() plugin.parse_opts(args) try: plugin.sufficient_options() except exceptions.AuthPluginOptionsMissing: continue return plugin raise exceptions.AuthPluginOptionsMissing(["auth_system"]) @six.add_metaclass(abc.ABCMeta) class BaseAuthPlugin(object): """Base class for authentication plugins. An authentication plugin needs to override at least the authenticate method to be a valid plugin. """ auth_system = None opt_names = [] common_opt_names = [ "auth_system", "username", "password", "tenant_name", "token", "auth_url", ] def __init__(self, auth_system=None, **kwargs): self.auth_system = auth_system or self.auth_system self.opts = dict((name, kwargs.get(name)) for name in self.opt_names) @staticmethod def _parser_add_opt(parser, opt): """Add an option to parser in two variants. :param opt: option name (with underscores) """ dashed_opt = opt.replace("_", "-") env_var = "OS_%s" % opt.upper() arg_default = os.environ.get(env_var, "") arg_help = "Defaults to env[%s]." % env_var parser.add_argument( "--os-%s" % dashed_opt, metavar="<%s>" % dashed_opt, default=arg_default, help=arg_help) parser.add_argument( "--os_%s" % opt, metavar="<%s>" % dashed_opt, help=argparse.SUPPRESS) @classmethod def add_opts(cls, parser): """Populate the parser with the options for this plugin. """ for opt in cls.opt_names: # use `BaseAuthPlugin.common_opt_names` since it is never # changed in child classes if opt not in BaseAuthPlugin.common_opt_names: cls._parser_add_opt(parser, opt) @classmethod def add_common_opts(cls, parser): """Add options that are common for several plugins. """ for opt in cls.common_opt_names: cls._parser_add_opt(parser, opt) @staticmethod def get_opt(opt_name, args): """Return option name and value. :param opt_name: name of the option, e.g., "username" :param args: parsed arguments """ return (opt_name, getattr(args, "os_%s" % opt_name, None)) def parse_opts(self, args): """Parse the actual auth-system options if any. This method is expected to populate the attribute `self.opts` with a dict containing the options and values needed to make authentication. """ self.opts.update(dict(self.get_opt(opt_name, args) for opt_name in self.opt_names)) def authenticate(self, http_client): """Authenticate using plugin defined method. The method usually analyses `self.opts` and performs a request to authentication server. :param http_client: client object that needs authentication :type http_client: HTTPClient :raises: AuthorizationFailure """ self.sufficient_options() self._do_authenticate(http_client) @abc.abstractmethod def _do_authenticate(self, http_client): """Protected method for authentication. """ def sufficient_options(self): """Check if all required options are present. :raises: AuthPluginOptionsMissing """ missing = [opt for opt in self.opt_names if not self.opts.get(opt)] if missing: raise exceptions.AuthPluginOptionsMissing(missing) @abc.abstractmethod def token_and_endpoint(self, endpoint_type, service_type): """Return token and endpoint. :param service_type: Service type of the endpoint :type service_type: string :param endpoint_type: Type of endpoint. Possible values: public or publicURL, internal or internalURL, admin or adminURL :type endpoint_type: string :returns: tuple of token and endpoint strings :raises: EndpointException """ python-novaclient-2.17.0/novaclient/openstack/common/apiclient/__init__.py0000664000175400017540000000000012306432770030114 0ustar jenkinsjenkins00000000000000python-novaclient-2.17.0/novaclient/openstack/common/importutils.py0000664000175400017540000000451712306432770027021 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation. # 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 related utilities and helper functions. """ import sys import traceback 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 (ValueError, AttributeError): raise ImportError('Class %s cannot be found (%s)' % (class_str, traceback.format_exception(*sys.exc_info()))) def import_object(import_str, *args, **kwargs): """Import a class and return an instance of it.""" return import_class(import_str)(*args, **kwargs) def import_object_ns(name_space, import_str, *args, **kwargs): """Tries to import object from default namespace. Imports a class and return an instance of it, first by trying to find the class in a default namespace, then failing back to a full path if not found in the default namespace. """ import_value = "%s.%s" % (name_space, import_str) try: return import_class(import_value)(*args, **kwargs) except ImportError: return import_class(import_str)(*args, **kwargs) def import_module(import_str): """Import a module.""" __import__(import_str) return sys.modules[import_str] def import_versioned_module(version, submodule=None): module = 'novaclient.v%s' % version if submodule: module = '.'.join((module, submodule)) return import_module(module) def try_import(import_str, default=None): """Try to import a module and if it fails return default.""" try: return import_module(import_str) except ImportError: return default python-novaclient-2.17.0/novaclient/openstack/common/cliutils.py0000664000175400017540000002202712306432770026252 0ustar jenkinsjenkins00000000000000# Copyright 2012 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. # W0603: Using the global statement # W0621: Redefining name %s from outer scope # pylint: disable=W0603,W0621 from __future__ import print_function import getpass import inspect import os import sys import textwrap import prettytable import six from six import moves from novaclient.openstack.common.apiclient import exceptions from novaclient.openstack.common.gettextutils import _ from novaclient.openstack.common import strutils from novaclient.openstack.common import uuidutils def validate_args(fn, *args, **kwargs): """Check that the supplied args are sufficient for calling a function. >>> validate_args(lambda a: None) Traceback (most recent call last): ... MissingArgs: Missing argument(s): a >>> validate_args(lambda a, b, c, d: None, 0, c=1) Traceback (most recent call last): ... MissingArgs: Missing argument(s): b, d :param fn: the function to check :param arg: the positional arguments supplied :param kwargs: the keyword arguments supplied """ argspec = inspect.getargspec(fn) num_defaults = len(argspec.defaults or []) required_args = argspec.args[:len(argspec.args) - num_defaults] def isbound(method): return getattr(method, 'im_self', None) is not None if isbound(fn): required_args.pop(0) missing = [arg for arg in required_args if arg not in kwargs] missing = missing[len(args):] if missing: raise exceptions.MissingArgs(missing) def arg(*args, **kwargs): """Decorator for CLI args. Example: >>> @arg("name", help="Name of the new entity") ... def entity_create(args): ... pass """ def _decorator(func): add_arg(func, *args, **kwargs) return func return _decorator def env(*args, **kwargs): """Returns the first environment variable set. If all are empty, defaults to '' or keyword arg `default`. """ for arg in args: value = os.environ.get(arg) if value: return value return kwargs.get('default', '') def add_arg(func, *args, **kwargs): """Bind CLI arguments to a shell.py `do_foo` function.""" if not hasattr(func, 'arguments'): func.arguments = [] # NOTE(sirp): avoid dups that can occur when the module is shared across # tests. if (args, kwargs) not in func.arguments: # Because of the semantics of decorator composition if we just append # to the options list positional options will appear to be backwards. func.arguments.insert(0, (args, kwargs)) def unauthenticated(func): """Adds 'unauthenticated' attribute to decorated function. Usage: >>> @unauthenticated ... def mymethod(f): ... pass """ func.unauthenticated = True return func def isunauthenticated(func): """Checks if the function does not require authentication. Mark such functions with the `@unauthenticated` decorator. :returns: bool """ return getattr(func, 'unauthenticated', False) def print_list(objs, fields, formatters=None, sortby_index=0, mixed_case_fields=None): """Print a list or objects as a table, one row per object. :param objs: iterable of :class:`Resource` :param fields: attributes that correspond to columns, in order :param formatters: `dict` of callables for field formatting :param sortby_index: index of the field for sorting table rows :param mixed_case_fields: fields corresponding to object attributes that have mixed case names (e.g., 'serverId') """ formatters = formatters or {} mixed_case_fields = mixed_case_fields or [] if sortby_index is None: kwargs = {} else: kwargs = {'sortby': fields[sortby_index]} pt = prettytable.PrettyTable(fields, caching=False) pt.align = 'l' for o in objs: row = [] for field in fields: if field in formatters: row.append(formatters[field](o)) else: if field in mixed_case_fields: field_name = field.replace(' ', '_') else: field_name = field.lower().replace(' ', '_') data = getattr(o, field_name, '') row.append(data) pt.add_row(row) print(strutils.safe_encode(pt.get_string(**kwargs))) def print_dict(dct, dict_property="Property", wrap=0): """Print a `dict` as a table of two columns. :param dct: `dict` to print :param dict_property: name of the first column :param wrap: wrapping for the second column """ pt = prettytable.PrettyTable([dict_property, 'Value'], caching=False) pt.align = 'l' for k, v in six.iteritems(dct): # convert dict to str to check length if isinstance(v, dict): v = six.text_type(v) if wrap > 0: v = textwrap.fill(six.text_type(v), wrap) # if value has a newline, add in multiple rows # e.g. fault with stacktrace if v and isinstance(v, six.string_types) and r'\n' in v: lines = v.strip().split(r'\n') col1 = k for line in lines: pt.add_row([col1, line]) col1 = '' else: pt.add_row([k, v]) print(strutils.safe_encode(pt.get_string())) def get_password(max_password_prompts=3): """Read password from TTY.""" verify = strutils.bool_from_string(env("OS_VERIFY_PASSWORD")) pw = None if hasattr(sys.stdin, "isatty") and sys.stdin.isatty(): # Check for Ctrl-D try: for __ in moves.range(max_password_prompts): pw1 = getpass.getpass("OS Password: ") if verify: pw2 = getpass.getpass("Please verify: ") else: pw2 = pw1 if pw1 == pw2 and pw1: pw = pw1 break except EOFError: pass return pw def find_resource(manager, name_or_id, **find_args): """Look for resource in a given manager. Used as a helper for the _find_* methods. Example: def _find_hypervisor(cs, hypervisor): #Get a hypervisor by name or ID. return cliutils.find_resource(cs.hypervisors, hypervisor) """ # first try to get entity as integer id try: return manager.get(int(name_or_id)) except (TypeError, ValueError, exceptions.NotFound): pass # now try to get entity as uuid try: tmp_id = strutils.safe_encode(name_or_id) if uuidutils.is_uuid_like(tmp_id): return manager.get(tmp_id) except (TypeError, ValueError, exceptions.NotFound): pass # for str id which is not uuid if getattr(manager, 'is_alphanum_id_allowed', False): try: return manager.get(name_or_id) except exceptions.NotFound: pass try: try: return manager.find(human_id=name_or_id, **find_args) except exceptions.NotFound: pass # finally try to find entity by name try: resource = getattr(manager, 'resource_class', None) name_attr = resource.NAME_ATTR if resource else 'name' kwargs = {name_attr: name_or_id} kwargs.update(find_args) return manager.find(**kwargs) except exceptions.NotFound: msg = _("No %(name)s with a name or " "ID of '%(name_or_id)s' exists.") % \ { "name": manager.resource_class.__name__.lower(), "name_or_id": name_or_id } raise exceptions.CommandError(msg) except exceptions.NoUniqueMatch: msg = _("Multiple %(name)s matches found for " "'%(name_or_id)s', use an ID to be more specific.") % \ { "name": manager.resource_class.__name__.lower(), "name_or_id": name_or_id } raise exceptions.CommandError(msg) def service_type(stype): """Adds 'service_type' attribute to decorated function. Usage: @service_type('volume') def mymethod(f): ... """ def inner(f): f.service_type = stype return f return inner def get_service_type(f): """Retrieves service type from function.""" return getattr(f, 'service_type', None) def pretty_choice_list(l): return ', '.join("'%s'" % i for i in l) def exit(msg=''): if msg: print (msg, file=sys.stderr) sys.exit(1) python-novaclient-2.17.0/novaclient/openstack/common/strutils.py0000664000175400017540000001770512306432770026322 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation. # 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. """ System-level utilities and helper functions. """ import math import re import sys import unicodedata import six from novaclient.openstack.common.gettextutils import _ UNIT_PREFIX_EXPONENT = { 'k': 1, 'K': 1, 'Ki': 1, 'M': 2, 'Mi': 2, 'G': 3, 'Gi': 3, 'T': 4, 'Ti': 4, } UNIT_SYSTEM_INFO = { 'IEC': (1024, re.compile(r'(^[-+]?\d*\.?\d+)([KMGT]i?)?(b|bit|B)$')), 'SI': (1000, re.compile(r'(^[-+]?\d*\.?\d+)([kMGT])?(b|bit|B)$')), } TRUE_STRINGS = ('1', 't', 'true', 'on', 'y', 'yes') FALSE_STRINGS = ('0', 'f', 'false', 'off', 'n', 'no') SLUGIFY_STRIP_RE = re.compile(r"[^\w\s-]") SLUGIFY_HYPHENATE_RE = re.compile(r"[-\s]+") def int_from_bool_as_string(subject): """Interpret a string as a boolean and return either 1 or 0. Any string value in: ('True', 'true', 'On', 'on', '1') is interpreted as a boolean True. Useful for JSON-decoded stuff and config file parsing """ return bool_from_string(subject) and 1 or 0 def bool_from_string(subject, strict=False, default=False): """Interpret a string as a boolean. A case-insensitive match is performed such that strings matching 't', 'true', 'on', 'y', 'yes', or '1' are considered True and, when `strict=False`, anything else returns the value specified by 'default'. Useful for JSON-decoded stuff and config file parsing. If `strict=True`, unrecognized values, including None, will raise a ValueError which is useful when parsing values passed in from an API call. Strings yielding False are 'f', 'false', 'off', 'n', 'no', or '0'. """ if not isinstance(subject, six.string_types): subject = str(subject) lowered = subject.strip().lower() if lowered in TRUE_STRINGS: return True elif lowered in FALSE_STRINGS: return False elif strict: acceptable = ', '.join( "'%s'" % s for s in sorted(TRUE_STRINGS + FALSE_STRINGS)) msg = _("Unrecognized value '%(val)s', acceptable values are:" " %(acceptable)s") % {'val': subject, 'acceptable': acceptable} raise ValueError(msg) else: return default def safe_decode(text, incoming=None, errors='strict'): """Decodes incoming str using `incoming` if they're not already unicode. :param incoming: Text's current encoding :param errors: Errors handling policy. See here for valid values http://docs.python.org/2/library/codecs.html :returns: text or a unicode `incoming` encoded representation of it. :raises TypeError: If text is not an instance of str """ if not isinstance(text, six.string_types): raise TypeError("%s can't be decoded" % type(text)) if isinstance(text, six.text_type): return text if not incoming: incoming = (sys.stdin.encoding or sys.getdefaultencoding()) try: return text.decode(incoming, errors) except UnicodeDecodeError: # Note(flaper87) If we get here, it means that # sys.stdin.encoding / sys.getdefaultencoding # didn't return a suitable encoding to decode # text. This happens mostly when global LANG # var is not set correctly and there's no # default encoding. In this case, most likely # python will use ASCII or ANSI encoders as # default encodings but they won't be capable # of decoding non-ASCII characters. # # Also, UTF-8 is being used since it's an ASCII # extension. return text.decode('utf-8', errors) def safe_encode(text, incoming=None, encoding='utf-8', errors='strict'): """Encodes incoming str/unicode using `encoding`. If incoming is not specified, text is expected to be encoded with current python's default encoding. (`sys.getdefaultencoding`) :param incoming: Text's current encoding :param encoding: Expected encoding for text (Default UTF-8) :param errors: Errors handling policy. See here for valid values http://docs.python.org/2/library/codecs.html :returns: text or a bytestring `encoding` encoded representation of it. :raises TypeError: If text is not an instance of str """ if not isinstance(text, six.string_types): raise TypeError("%s can't be encoded" % type(text)) if not incoming: incoming = (sys.stdin.encoding or sys.getdefaultencoding()) if isinstance(text, six.text_type): if six.PY3: return text.encode(encoding, errors).decode(incoming) else: return text.encode(encoding, errors) elif text and encoding != incoming: # Decode text before encoding it with `encoding` text = safe_decode(text, incoming, errors) if six.PY3: return text.encode(encoding, errors).decode(incoming) else: return text.encode(encoding, errors) return text def string_to_bytes(text, unit_system='IEC', return_int=False): """Converts a string into an float representation of bytes. The units supported for IEC :: Kb(it), Kib(it), Mb(it), Mib(it), Gb(it), Gib(it), Tb(it), Tib(it) KB, KiB, MB, MiB, GB, GiB, TB, TiB The units supported for SI :: kb(it), Mb(it), Gb(it), Tb(it) kB, MB, GB, TB Note that the SI unit system does not support capital letter 'K' :param text: String input for bytes size conversion. :param unit_system: Unit system for byte size conversion. :param return_int: If True, returns integer representation of text in bytes. (default: decimal) :returns: Numerical representation of text in bytes. :raises ValueError: If text has an invalid value. """ try: base, reg_ex = UNIT_SYSTEM_INFO[unit_system] except KeyError: msg = _('Invalid unit system: "%s"') % unit_system raise ValueError(msg) match = reg_ex.match(text) if match: magnitude = float(match.group(1)) unit_prefix = match.group(2) if match.group(3) in ['b', 'bit']: magnitude /= 8 else: msg = _('Invalid string format: %s') % text raise ValueError(msg) if not unit_prefix: res = magnitude else: res = magnitude * pow(base, UNIT_PREFIX_EXPONENT[unit_prefix]) if return_int: return int(math.ceil(res)) return res def to_slug(value, incoming=None, errors="strict"): """Normalize string. Convert to lowercase, remove non-word characters, and convert spaces to hyphens. Inspired by Django's `slugify` filter. :param value: Text to slugify :param incoming: Text's current encoding :param errors: Errors handling policy. See here for valid values http://docs.python.org/2/library/codecs.html :returns: slugified unicode representation of `value` :raises TypeError: If text is not an instance of str """ value = safe_decode(value, incoming, errors) # NOTE(aababilov): no need to use safe_(encode|decode) here: # encodings are always "ascii", error handling is always "ignore" # and types are always known (first: unicode; second: str) value = unicodedata.normalize("NFKD", value).encode( "ascii", "ignore").decode("ascii") value = SLUGIFY_STRIP_RE.sub("", value).strip().lower() return SLUGIFY_HYPHENATE_RE.sub("-", value) python-novaclient-2.17.0/novaclient/openstack/common/py3kcompat/0000775000175400017540000000000012306433034026131 5ustar jenkinsjenkins00000000000000python-novaclient-2.17.0/novaclient/openstack/common/py3kcompat/urlutils.py0000664000175400017540000000356012306432770030400 0ustar jenkinsjenkins00000000000000# # Copyright 2013 Canonical Ltd. # 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. # """ Python2/Python3 compatibility layer for OpenStack """ import six if six.PY3: # python3 import urllib.error import urllib.parse import urllib.request urlencode = urllib.parse.urlencode urljoin = urllib.parse.urljoin quote = urllib.parse.quote quote_plus = urllib.parse.quote_plus parse_qsl = urllib.parse.parse_qsl unquote = urllib.parse.unquote unquote_plus = urllib.parse.unquote_plus urlparse = urllib.parse.urlparse urlsplit = urllib.parse.urlsplit urlunsplit = urllib.parse.urlunsplit SplitResult = urllib.parse.SplitResult urlopen = urllib.request.urlopen URLError = urllib.error.URLError pathname2url = urllib.request.pathname2url else: # python2 import urllib import urllib2 import urlparse urlencode = urllib.urlencode quote = urllib.quote quote_plus = urllib.quote_plus unquote = urllib.unquote unquote_plus = urllib.unquote_plus parse = urlparse parse_qsl = parse.parse_qsl urljoin = parse.urljoin urlparse = parse.urlparse urlsplit = parse.urlsplit urlunsplit = parse.urlunsplit SplitResult = parse.SplitResult urlopen = urllib2.urlopen URLError = urllib2.URLError pathname2url = urllib.pathname2url python-novaclient-2.17.0/novaclient/openstack/common/py3kcompat/__init__.py0000664000175400017540000000000012306432770030236 0ustar jenkinsjenkins00000000000000python-novaclient-2.17.0/novaclient/openstack/common/timeutils.py0000664000175400017540000001424112306432770026440 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation. # 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. """ Time related utilities and helper functions. """ import calendar import datetime import time import iso8601 import six # ISO 8601 extended time format with microseconds _ISO8601_TIME_FORMAT_SUBSECOND = '%Y-%m-%dT%H:%M:%S.%f' _ISO8601_TIME_FORMAT = '%Y-%m-%dT%H:%M:%S' PERFECT_TIME_FORMAT = _ISO8601_TIME_FORMAT_SUBSECOND def isotime(at=None, subsecond=False): """Stringify time in ISO 8601 format.""" if not at: at = utcnow() st = at.strftime(_ISO8601_TIME_FORMAT if not subsecond else _ISO8601_TIME_FORMAT_SUBSECOND) tz = at.tzinfo.tzname(None) if at.tzinfo else 'UTC' st += ('Z' if tz == 'UTC' else tz) return st def parse_isotime(timestr): """Parse time from ISO 8601 format.""" try: return iso8601.parse_date(timestr) except iso8601.ParseError as e: raise ValueError(six.text_type(e)) except TypeError as e: raise ValueError(six.text_type(e)) def strtime(at=None, fmt=PERFECT_TIME_FORMAT): """Returns formatted utcnow.""" if not at: at = utcnow() return at.strftime(fmt) def parse_strtime(timestr, fmt=PERFECT_TIME_FORMAT): """Turn a formatted time back into a datetime.""" return datetime.datetime.strptime(timestr, fmt) def normalize_time(timestamp): """Normalize time in arbitrary timezone to UTC naive object.""" offset = timestamp.utcoffset() if offset is None: return timestamp return timestamp.replace(tzinfo=None) - offset def is_older_than(before, seconds): """Return True if before is older than seconds.""" if isinstance(before, six.string_types): before = parse_strtime(before).replace(tzinfo=None) else: before = before.replace(tzinfo=None) return utcnow() - before > datetime.timedelta(seconds=seconds) def is_newer_than(after, seconds): """Return True if after is newer than seconds.""" if isinstance(after, six.string_types): after = parse_strtime(after).replace(tzinfo=None) else: after = after.replace(tzinfo=None) return after - utcnow() > datetime.timedelta(seconds=seconds) def utcnow_ts(): """Timestamp version of our utcnow function.""" if utcnow.override_time is None: # NOTE(kgriffs): This is several times faster # than going through calendar.timegm(...) return int(time.time()) return calendar.timegm(utcnow().timetuple()) def utcnow(): """Overridable version of utils.utcnow.""" if utcnow.override_time: try: return utcnow.override_time.pop(0) except AttributeError: return utcnow.override_time return datetime.datetime.utcnow() def iso8601_from_timestamp(timestamp): """Returns a iso8601 formatted date from timestamp.""" return isotime(datetime.datetime.utcfromtimestamp(timestamp)) utcnow.override_time = None def set_time_override(override_time=None): """Overrides utils.utcnow. Make it return a constant time or a list thereof, one at a time. :param override_time: datetime instance or list thereof. If not given, defaults to the current UTC time. """ utcnow.override_time = override_time or datetime.datetime.utcnow() def advance_time_delta(timedelta): """Advance overridden time using a datetime.timedelta.""" assert(not utcnow.override_time is None) try: for dt in utcnow.override_time: dt += timedelta except TypeError: utcnow.override_time += timedelta def advance_time_seconds(seconds): """Advance overridden time by seconds.""" advance_time_delta(datetime.timedelta(0, seconds)) def clear_time_override(): """Remove the overridden time.""" utcnow.override_time = None def marshall_now(now=None): """Make an rpc-safe datetime with microseconds. Note: tzinfo is stripped, but not required for relative times. """ if not now: now = utcnow() return dict(day=now.day, month=now.month, year=now.year, hour=now.hour, minute=now.minute, second=now.second, microsecond=now.microsecond) def unmarshall_time(tyme): """Unmarshall a datetime dict.""" return datetime.datetime(day=tyme['day'], month=tyme['month'], year=tyme['year'], hour=tyme['hour'], minute=tyme['minute'], second=tyme['second'], microsecond=tyme['microsecond']) def delta_seconds(before, after): """Return the difference between two timing objects. Compute the difference in seconds between two date, time, or datetime objects (as a float, to microsecond resolution). """ delta = after - before return total_seconds(delta) def total_seconds(delta): """Return the total seconds of datetime.timedelta object. Compute total seconds of datetime.timedelta, datetime.timedelta doesn't have method total_seconds in Python2.6, calculate it manually. """ try: return delta.total_seconds() except AttributeError: return ((delta.days * 24 * 3600) + delta.seconds + float(delta.microseconds) / (10 ** 6)) def is_soon(dt, window): """Determines if time is going to happen in the next window seconds. :param dt: the time :param window: minimum seconds to remain to consider the time not soon :return: True if expiration is within the given duration """ soon = (utcnow() + datetime.timedelta(seconds=window)) return normalize_time(dt) <= soon python-novaclient-2.17.0/novaclient/openstack/common/jsonutils.py0000664000175400017540000001510312306432770026451 0ustar jenkinsjenkins00000000000000# Copyright 2010 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. # Copyright 2011 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. ''' JSON related utilities. This module provides a few things: 1) A handy function for getting an object down to something that can be JSON serialized. See to_primitive(). 2) Wrappers around loads() and dumps(). The dumps() wrapper will automatically use to_primitive() for you if needed. 3) This sets up anyjson to use the loads() and dumps() wrappers if anyjson is available. ''' import datetime import functools import inspect import itertools import json try: import xmlrpclib except ImportError: # NOTE(jaypipes): xmlrpclib was renamed to xmlrpc.client in Python3 # however the function and object call signatures # remained the same. This whole try/except block should # be removed and replaced with a call to six.moves once # six 1.4.2 is released. See http://bit.ly/1bqrVzu import xmlrpc.client as xmlrpclib import six from novaclient.openstack.common import gettextutils from novaclient.openstack.common import importutils from novaclient.openstack.common import timeutils netaddr = importutils.try_import("netaddr") _nasty_type_tests = [inspect.ismodule, inspect.isclass, inspect.ismethod, inspect.isfunction, inspect.isgeneratorfunction, inspect.isgenerator, inspect.istraceback, inspect.isframe, inspect.iscode, inspect.isbuiltin, inspect.isroutine, inspect.isabstract] _simple_types = (six.string_types + six.integer_types + (type(None), bool, float)) def to_primitive(value, convert_instances=False, convert_datetime=True, level=0, max_depth=3): """Convert a complex object into primitives. Handy for JSON serialization. We can optionally handle instances, but since this is a recursive function, we could have cyclical data structures. To handle cyclical data structures we could track the actual objects visited in a set, but not all objects are hashable. Instead we just track the depth of the object inspections and don't go too deep. Therefore, convert_instances=True is lossy ... be aware. """ # handle obvious types first - order of basic types determined by running # full tests on nova project, resulting in the following counts: # 572754 # 460353 # 379632 # 274610 # 199918 # 114200 # 51817 # 26164 # 6491 # 283 # 19 if isinstance(value, _simple_types): return value if isinstance(value, datetime.datetime): if convert_datetime: return timeutils.strtime(value) else: return value # value of itertools.count doesn't get caught by nasty_type_tests # and results in infinite loop when list(value) is called. if type(value) == itertools.count: return six.text_type(value) # FIXME(vish): Workaround for LP bug 852095. Without this workaround, # tests that raise an exception in a mocked method that # has a @wrap_exception with a notifier will fail. If # we up the dependency to 0.5.4 (when it is released) we # can remove this workaround. if getattr(value, '__module__', None) == 'mox': return 'mock' if level > max_depth: return '?' # The try block may not be necessary after the class check above, # but just in case ... try: recursive = functools.partial(to_primitive, convert_instances=convert_instances, convert_datetime=convert_datetime, level=level, max_depth=max_depth) if isinstance(value, dict): return dict((k, recursive(v)) for k, v in six.iteritems(value)) elif isinstance(value, (list, tuple)): return [recursive(lv) for lv in value] # It's not clear why xmlrpclib created their own DateTime type, but # for our purposes, make it a datetime type which is explicitly # handled if isinstance(value, xmlrpclib.DateTime): value = datetime.datetime(*tuple(value.timetuple())[:6]) if convert_datetime and isinstance(value, datetime.datetime): return timeutils.strtime(value) elif isinstance(value, gettextutils.Message): return value.data elif hasattr(value, 'iteritems'): return recursive(dict(value.iteritems()), level=level + 1) elif hasattr(value, '__iter__'): return recursive(list(value)) elif convert_instances and hasattr(value, '__dict__'): # Likely an instance of something. Watch for cycles. # Ignore class member vars. return recursive(value.__dict__, level=level + 1) elif netaddr and isinstance(value, netaddr.IPAddress): return six.text_type(value) else: if any(test(value) for test in _nasty_type_tests): return six.text_type(value) return value except TypeError: # Class objects are tricky since they may define something like # __iter__ defined but it isn't callable as list(). return six.text_type(value) def dumps(value, default=to_primitive, **kwargs): return json.dumps(value, default=default, **kwargs) def loads(s): return json.loads(s) def load(s): return json.load(s) try: import anyjson except ImportError: pass else: anyjson._modules.append((__name__, 'dumps', TypeError, 'loads', ValueError, 'load')) anyjson.force_implementation(__name__) python-novaclient-2.17.0/novaclient/openstack/common/gettextutils.py0000664000175400017540000004457612306432770027204 0ustar jenkinsjenkins00000000000000# Copyright 2012 Red Hat, Inc. # Copyright 2013 IBM Corp. # 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. """ gettext for openstack-common modules. Usual usage in an openstack.common module: from novaclient.openstack.common.gettextutils import _ """ import copy import functools import gettext import locale from logging import handlers import os import re from babel import localedata import six _localedir = os.environ.get('novaclient'.upper() + '_LOCALEDIR') _t = gettext.translation('novaclient', localedir=_localedir, fallback=True) # We use separate translation catalogs for each log level, so set up a # mapping between the log level name and the translator. The domain # for the log level is project_name + "-log-" + log_level so messages # for each level end up in their own catalog. _t_log_levels = dict( (level, gettext.translation('novaclient' + '-log-' + level, localedir=_localedir, fallback=True)) for level in ['info', 'warning', 'error', 'critical'] ) _AVAILABLE_LANGUAGES = {} USE_LAZY = False def enable_lazy(): """Convenience function for configuring _() to use lazy gettext Call this at the start of execution to enable the gettextutils._ function to use lazy gettext functionality. This is useful if your project is importing _ directly instead of using the gettextutils.install() way of importing the _ function. """ global USE_LAZY USE_LAZY = True def _(msg): if USE_LAZY: return Message(msg, domain='novaclient') else: if six.PY3: return _t.gettext(msg) return _t.ugettext(msg) def _log_translation(msg, level): """Build a single translation of a log message """ if USE_LAZY: return Message(msg, domain='novaclient' + '-log-' + level) else: translator = _t_log_levels[level] if six.PY3: return translator.gettext(msg) return translator.ugettext(msg) # Translators for log levels. # # The abbreviated names are meant to reflect the usual use of a short # name like '_'. The "L" is for "log" and the other letter comes from # the level. _LI = functools.partial(_log_translation, level='info') _LW = functools.partial(_log_translation, level='warning') _LE = functools.partial(_log_translation, level='error') _LC = functools.partial(_log_translation, level='critical') def install(domain, lazy=False): """Install a _() function using the given translation domain. Given a translation domain, install a _() function using gettext's install() function. The main difference from gettext.install() is that we allow overriding the default localedir (e.g. /usr/share/locale) using a translation-domain-specific environment variable (e.g. NOVA_LOCALEDIR). :param domain: the translation domain :param lazy: indicates whether or not to install the lazy _() function. The lazy _() introduces a way to do deferred translation of messages by installing a _ that builds Message objects, instead of strings, which can then be lazily translated into any available locale. """ if lazy: # NOTE(mrodden): Lazy gettext functionality. # # The following introduces a deferred way to do translations on # messages in OpenStack. We override the standard _() function # and % (format string) operation to build Message objects that can # later be translated when we have more information. def _lazy_gettext(msg): """Create and return a Message object. Lazy gettext function for a given domain, it is a factory method for a project/module to get a lazy gettext function for its own translation domain (i.e. nova, glance, cinder, etc.) Message encapsulates a string so that we can translate it later when needed. """ return Message(msg, domain=domain) from six import moves moves.builtins.__dict__['_'] = _lazy_gettext else: localedir = '%s_LOCALEDIR' % domain.upper() if six.PY3: gettext.install(domain, localedir=os.environ.get(localedir)) else: gettext.install(domain, localedir=os.environ.get(localedir), unicode=True) class Message(six.text_type): """A Message object is a unicode object that can be translated. Translation of Message is done explicitly using the translate() method. For all non-translation intents and purposes, a Message is simply unicode, and can be treated as such. """ def __new__(cls, msgid, msgtext=None, params=None, domain='novaclient', *args): """Create a new Message object. In order for translation to work gettext requires a message ID, this msgid will be used as the base unicode text. It is also possible for the msgid and the base unicode text to be different by passing the msgtext parameter. """ # If the base msgtext is not given, we use the default translation # of the msgid (which is in English) just in case the system locale is # not English, so that the base text will be in that locale by default. if not msgtext: msgtext = Message._translate_msgid(msgid, domain) # We want to initialize the parent unicode with the actual object that # would have been plain unicode if 'Message' was not enabled. msg = super(Message, cls).__new__(cls, msgtext) msg.msgid = msgid msg.domain = domain msg.params = params return msg def translate(self, desired_locale=None): """Translate this message to the desired locale. :param desired_locale: The desired locale to translate the message to, if no locale is provided the message will be translated to the system's default locale. :returns: the translated message in unicode """ translated_message = Message._translate_msgid(self.msgid, self.domain, desired_locale) if self.params is None: # No need for more translation return translated_message # This Message object may have been formatted with one or more # Message objects as substitution arguments, given either as a single # argument, part of a tuple, or as one or more values in a dictionary. # When translating this Message we need to translate those Messages too translated_params = _translate_args(self.params, desired_locale) translated_message = translated_message % translated_params return translated_message @staticmethod def _translate_msgid(msgid, domain, desired_locale=None): if not desired_locale: system_locale = locale.getdefaultlocale() # If the system locale is not available to the runtime use English if not system_locale[0]: desired_locale = 'en_US' else: desired_locale = system_locale[0] locale_dir = os.environ.get(domain.upper() + '_LOCALEDIR') lang = gettext.translation(domain, localedir=locale_dir, languages=[desired_locale], fallback=True) if six.PY3: translator = lang.gettext else: translator = lang.ugettext translated_message = translator(msgid) return translated_message def __mod__(self, other): # When we mod a Message we want the actual operation to be performed # by the parent class (i.e. unicode()), the only thing we do here is # save the original msgid and the parameters in case of a translation params = self._sanitize_mod_params(other) unicode_mod = super(Message, self).__mod__(params) modded = Message(self.msgid, msgtext=unicode_mod, params=params, domain=self.domain) return modded def _sanitize_mod_params(self, other): """Sanitize the object being modded with this Message. - Add support for modding 'None' so translation supports it - Trim the modded object, which can be a large dictionary, to only those keys that would actually be used in a translation - Snapshot the object being modded, in case the message is translated, it will be used as it was when the Message was created """ if other is None: params = (other,) elif isinstance(other, dict): params = self._trim_dictionary_parameters(other) else: params = self._copy_param(other) return params def _trim_dictionary_parameters(self, dict_param): """Return a dict that only has matching entries in the msgid.""" # NOTE(luisg): Here we trim down the dictionary passed as parameters # to avoid carrying a lot of unnecessary weight around in the message # object, for example if someone passes in Message() % locals() but # only some params are used, and additionally we prevent errors for # non-deepcopyable objects by unicoding() them. # Look for %(param) keys in msgid; # Skip %% and deal with the case where % is first character on the line keys = re.findall('(?:[^%]|^)?%\((\w*)\)[a-z]', self.msgid) # If we don't find any %(param) keys but have a %s if not keys and re.findall('(?:[^%]|^)%[a-z]', self.msgid): # Apparently the full dictionary is the parameter params = self._copy_param(dict_param) else: params = {} # Save our existing parameters as defaults to protect # ourselves from losing values if we are called through an # (erroneous) chain that builds a valid Message with # arguments, and then does something like "msg % kwds" # where kwds is an empty dictionary. src = {} if isinstance(self.params, dict): src.update(self.params) src.update(dict_param) for key in keys: params[key] = self._copy_param(src[key]) return params def _copy_param(self, param): try: return copy.deepcopy(param) except TypeError: # Fallback to casting to unicode this will handle the # python code-like objects that can't be deep-copied return six.text_type(param) def __add__(self, other): msg = _('Message objects do not support addition.') raise TypeError(msg) def __radd__(self, other): return self.__add__(other) def __str__(self): # NOTE(luisg): Logging in python 2.6 tries to str() log records, # and it expects specifically a UnicodeError in order to proceed. msg = _('Message objects do not support str() because they may ' 'contain non-ascii characters. ' 'Please use unicode() or translate() instead.') raise UnicodeError(msg) def get_available_languages(domain): """Lists the available languages for the given translation domain. :param domain: the domain to get languages for """ if domain in _AVAILABLE_LANGUAGES: return copy.copy(_AVAILABLE_LANGUAGES[domain]) localedir = '%s_LOCALEDIR' % domain.upper() find = lambda x: gettext.find(domain, localedir=os.environ.get(localedir), languages=[x]) # NOTE(mrodden): en_US should always be available (and first in case # order matters) since our in-line message strings are en_US language_list = ['en_US'] # NOTE(luisg): Babel <1.0 used a function called list(), which was # renamed to locale_identifiers() in >=1.0, the requirements master list # requires >=0.9.6, uncapped, so defensively work with both. We can remove # this check when the master list updates to >=1.0, and update all projects list_identifiers = (getattr(localedata, 'list', None) or getattr(localedata, 'locale_identifiers')) locale_identifiers = list_identifiers() for i in locale_identifiers: if find(i) is not None: language_list.append(i) # NOTE(luisg): Babel>=1.0,<1.3 has a bug where some OpenStack supported # locales (e.g. 'zh_CN', and 'zh_TW') aren't supported even though they # are perfectly legitimate locales: # https://github.com/mitsuhiko/babel/issues/37 # In Babel 1.3 they fixed the bug and they support these locales, but # they are still not explicitly "listed" by locale_identifiers(). # That is why we add the locales here explicitly if necessary so that # they are listed as supported. aliases = {'zh': 'zh_CN', 'zh_Hant_HK': 'zh_HK', 'zh_Hant': 'zh_TW', 'fil': 'tl_PH'} for (locale, alias) in six.iteritems(aliases): if locale in language_list and alias not in language_list: language_list.append(alias) _AVAILABLE_LANGUAGES[domain] = language_list return copy.copy(language_list) def translate(obj, desired_locale=None): """Gets the translated unicode representation of the given object. If the object is not translatable it is returned as-is. If the locale is None the object is translated to the system locale. :param obj: the object to translate :param desired_locale: the locale to translate the message to, if None the default system locale will be used :returns: the translated object in unicode, or the original object if it could not be translated """ message = obj if not isinstance(message, Message): # If the object to translate is not already translatable, # let's first get its unicode representation message = six.text_type(obj) if isinstance(message, Message): # Even after unicoding() we still need to check if we are # running with translatable unicode before translating return message.translate(desired_locale) return obj def _translate_args(args, desired_locale=None): """Translates all the translatable elements of the given arguments object. This method is used for translating the translatable values in method arguments which include values of tuples or dictionaries. If the object is not a tuple or a dictionary the object itself is translated if it is translatable. If the locale is None the object is translated to the system locale. :param args: the args to translate :param desired_locale: the locale to translate the args to, if None the default system locale will be used :returns: a new args object with the translated contents of the original """ if isinstance(args, tuple): return tuple(translate(v, desired_locale) for v in args) if isinstance(args, dict): translated_dict = {} for (k, v) in six.iteritems(args): translated_v = translate(v, desired_locale) translated_dict[k] = translated_v return translated_dict return translate(args, desired_locale) class TranslationHandler(handlers.MemoryHandler): """Handler that translates records before logging them. The TranslationHandler takes a locale and a target logging.Handler object to forward LogRecord objects to after translating them. This handler depends on Message objects being logged, instead of regular strings. The handler can be configured declaratively in the logging.conf as follows: [handlers] keys = translatedlog, translator [handler_translatedlog] class = handlers.WatchedFileHandler args = ('/var/log/api-localized.log',) formatter = context [handler_translator] class = openstack.common.log.TranslationHandler target = translatedlog args = ('zh_CN',) If the specified locale is not available in the system, the handler will log in the default locale. """ def __init__(self, locale=None, target=None): """Initialize a TranslationHandler :param locale: locale to use for translating messages :param target: logging.Handler object to forward LogRecord objects to after translation """ # NOTE(luisg): In order to allow this handler to be a wrapper for # other handlers, such as a FileHandler, and still be able to # configure it using logging.conf, this handler has to extend # MemoryHandler because only the MemoryHandlers' logging.conf # parsing is implemented such that it accepts a target handler. handlers.MemoryHandler.__init__(self, capacity=0, target=target) self.locale = locale def setFormatter(self, fmt): self.target.setFormatter(fmt) def emit(self, record): # We save the message from the original record to restore it # after translation, so other handlers are not affected by this original_msg = record.msg original_args = record.args try: self._translate_and_log_record(record) finally: record.msg = original_msg record.args = original_args def _translate_and_log_record(self, record): record.msg = translate(record.msg, self.locale) # In addition to translating the message, we also need to translate # arguments that were passed to the log method that were not part # of the main message e.g., log.info(_('Some message %s'), this_one)) record.args = _translate_args(record.args, self.locale) self.target.emit(record) python-novaclient-2.17.0/novaclient/openstack/common/__init__.py0000664000175400017540000000010312306432770026150 0ustar jenkinsjenkins00000000000000import six six.add_move(six.MovedModule('mox', 'mox', 'mox3.mox')) python-novaclient-2.17.0/novaclient/openstack/common/uuidutils.py0000664000175400017540000000204512306432770026447 0ustar jenkinsjenkins00000000000000# Copyright (c) 2012 Intel Corporation. # 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. """ UUID related utilities and helper functions. """ import uuid def generate_uuid(): return str(uuid.uuid4()) def is_uuid_like(val): """Returns validation of a value as a UUID. For our purposes, a UUID is a canonical form string: aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa """ try: return str(uuid.UUID(val)) == val except (TypeError, ValueError, AttributeError): return False python-novaclient-2.17.0/novaclient/openstack/__init__.py0000664000175400017540000000000012306432770024654 0ustar jenkinsjenkins00000000000000python-novaclient-2.17.0/novaclient/base.py0000664000175400017540000002217112306432770022055 0ustar jenkinsjenkins00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 OpenStack Foundation # 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. """ Base utilities to build API operation managers and objects on top of. """ import abc import contextlib import hashlib import inspect import os import threading import six from novaclient import exceptions from novaclient.openstack.common.apiclient import base from novaclient import utils Resource = base.Resource def getid(obj): """ Abstracts the common pattern of allowing both an object or an object's ID as a parameter when dealing with relationships. """ try: return obj.id except AttributeError: return obj class Manager(utils.HookableMixin): """ Managers interact with a particular type of API (servers, flavors, images, etc.) and provide CRUD operations for them. """ resource_class = None cache_lock = threading.RLock() def __init__(self, api): self.api = api def _list(self, url, response_key, obj_class=None, body=None): if body: _resp, body = self.api.client.post(url, body=body) else: _resp, body = self.api.client.get(url) if obj_class is None: obj_class = self.resource_class data = body[response_key] # NOTE(ja): keystone returns values as list as {'values': [ ... ]} # unlike other services which just return the list... if isinstance(data, dict): try: data = data['values'] except KeyError: pass with self.completion_cache('human_id', obj_class, mode="w"): with self.completion_cache('uuid', obj_class, mode="w"): return [obj_class(self, res, loaded=True) for res in data if res] @contextlib.contextmanager def completion_cache(self, cache_type, obj_class, mode): """ The completion cache store items that can be used for bash autocompletion, like UUIDs or human-friendly IDs. A resource listing will clear and repopulate the cache. A resource create will append to the cache. Delete is not handled because listings are assumed to be performed often enough to keep the cache reasonably up-to-date. """ # NOTE(wryan): This lock protects read and write access to the # completion caches with self.cache_lock: base_dir = utils.env('NOVACLIENT_UUID_CACHE_DIR', default="~/.novaclient") # NOTE(sirp): Keep separate UUID caches for each username + # endpoint pair username = utils.env('OS_USERNAME', 'NOVA_USERNAME') url = utils.env('OS_URL', 'NOVA_URL') uniqifier = hashlib.md5(username.encode('utf-8') + url.encode('utf-8')).hexdigest() cache_dir = os.path.expanduser(os.path.join(base_dir, uniqifier)) try: os.makedirs(cache_dir, 0o755) except OSError: # NOTE(kiall): This is typically either permission denied while # attempting to create the directory, or the # directory already exists. Either way, don't # fail. pass resource = obj_class.__name__.lower() filename = "%s-%s-cache" % (resource, cache_type.replace('_', '-')) path = os.path.join(cache_dir, filename) cache_attr = "_%s_cache" % cache_type try: setattr(self, cache_attr, open(path, mode)) except IOError: # NOTE(kiall): This is typically a permission denied while # attempting to write the cache file. pass try: yield finally: cache = getattr(self, cache_attr, None) if cache: cache.close() delattr(self, cache_attr) def write_to_completion_cache(self, cache_type, val): cache = getattr(self, "_%s_cache" % cache_type, None) if cache: cache.write("%s\n" % val) def _get(self, url, response_key): _resp, body = self.api.client.get(url) return self.resource_class(self, body[response_key], loaded=True) def _create(self, url, body, response_key, return_raw=False, **kwargs): self.run_hooks('modify_body_for_create', body, **kwargs) _resp, body = self.api.client.post(url, body=body) if return_raw: return body[response_key] with self.completion_cache('human_id', self.resource_class, mode="a"): with self.completion_cache('uuid', self.resource_class, mode="a"): return self.resource_class(self, body[response_key]) def _delete(self, url): _resp, _body = self.api.client.delete(url) def _update(self, url, body, response_key=None, **kwargs): self.run_hooks('modify_body_for_update', body, **kwargs) _resp, body = self.api.client.put(url, body=body) if body: if response_key: return self.resource_class(self, body[response_key]) else: return self.resource_class(self, body) @six.add_metaclass(abc.ABCMeta) class ManagerWithFind(Manager): """ Like a `Manager`, but with additional `find()`/`findall()` methods. """ @abc.abstractmethod def list(self): pass def find(self, **kwargs): """ Find a single item with attributes matching ``**kwargs``. This isn't very efficient: it loads the entire list then filters on the Python side. """ matches = self.findall(**kwargs) num_matches = len(matches) if num_matches == 0: msg = "No %s matching %s." % (self.resource_class.__name__, kwargs) raise exceptions.NotFound(404, msg) elif num_matches > 1: raise exceptions.NoUniqueMatch else: return matches[0] def findall(self, **kwargs): """ Find all items with attributes matching ``**kwargs``. This isn't very efficient: it loads the entire list then filters on the Python side. """ found = [] searches = kwargs.items() detailed = True list_kwargs = {} list_argspec = inspect.getargspec(self.list) if 'detailed' in list_argspec.args: detailed = ("human_id" not in kwargs and "name" not in kwargs and "display_name" not in kwargs) list_kwargs['detailed'] = detailed if 'is_public' in list_argspec.args and 'is_public' in kwargs: is_public = kwargs['is_public'] list_kwargs['is_public'] = is_public if is_public is None: tmp_kwargs = kwargs.copy() del tmp_kwargs['is_public'] searches = tmp_kwargs.items() listing = self.list(**list_kwargs) for obj in listing: try: if all(getattr(obj, attr) == value for (attr, value) in searches): if detailed: found.append(obj) else: found.append(self.get(obj.id)) except AttributeError: continue return found class BootingManagerWithFind(ManagerWithFind): """Like a `ManagerWithFind`, but has the ability to boot servers.""" def _parse_block_device_mapping(self, block_device_mapping): bdm = [] for device_name, mapping in six.iteritems(block_device_mapping): # # The mapping is in the format: # :[]:[]:[] # bdm_dict = {'device_name': device_name} mapping_parts = mapping.split(':') source_id = mapping_parts[0] if len(mapping_parts) == 1: bdm_dict['volume_id'] = source_id elif len(mapping_parts) > 1: source_type = mapping_parts[1] if source_type.startswith('snap'): bdm_dict['snapshot_id'] = source_id else: bdm_dict['volume_id'] = source_id if len(mapping_parts) > 2 and mapping_parts[2]: bdm_dict['volume_size'] = str(int(mapping_parts[2])) if len(mapping_parts) > 3: bdm_dict['delete_on_termination'] = mapping_parts[3] bdm.append(bdm_dict) return bdm python-novaclient-2.17.0/novaclient/service_catalog.py0000664000175400017540000000672112306432770024300 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # Copyright 2011, Piston Cloud Computing, Inc. # # 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 novaclient.exceptions class ServiceCatalog(object): """Helper methods for dealing with a Keystone Service Catalog.""" def __init__(self, resource_dict): self.catalog = resource_dict def get_token(self): return self.catalog['access']['token']['id'] def get_tenant_id(self): return self.catalog['access']['token']['tenant']['id'] def url_for(self, attr=None, filter_value=None, service_type=None, endpoint_type='publicURL', service_name=None, volume_service_name=None): """Fetch the public URL from the Compute service for a particular endpoint attribute. If none given, return the first. See tests for sample service catalog. """ matching_endpoints = [] if 'endpoints' in self.catalog: # We have a bastardized service catalog. Treat it special. :/ for endpoint in self.catalog['endpoints']: if not filter_value or endpoint[attr] == filter_value: # Ignore 1.0 compute endpoints if endpoint.get("type") == 'compute' and \ endpoint.get('versionId') in (None, '1.1', '2'): matching_endpoints.append(endpoint) if not matching_endpoints: raise novaclient.exceptions.EndpointNotFound() # We don't always get a service catalog back ... if 'serviceCatalog' not in self.catalog['access']: return None # Full catalog ... catalog = self.catalog['access']['serviceCatalog'] for service in catalog: if service.get("type") != service_type: continue if (service_name and service_type == 'compute' and service.get('name') != service_name): continue if (volume_service_name and service_type == 'volume' and service.get('name') != volume_service_name): continue endpoints = service['endpoints'] for endpoint in endpoints: # Ignore 1.0 compute endpoints if service.get("type") == 'compute' and \ endpoint.get('versionId', '2') not in ('1.1', '2'): continue if not filter_value or \ endpoint.get(attr).lower() == filter_value.lower(): endpoint["serviceName"] = service.get("name") matching_endpoints.append(endpoint) if not matching_endpoints: raise novaclient.exceptions.EndpointNotFound() elif len(matching_endpoints) > 1: raise novaclient.exceptions.AmbiguousEndpoints( endpoints=matching_endpoints) else: return matching_endpoints[0][endpoint_type] python-novaclient-2.17.0/novaclient/crypto.py0000664000175400017540000000241412306432770022461 0ustar jenkinsjenkins00000000000000# Copyright 2013 Nebula, Inc. # 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 subprocess class DecryptionFailure(Exception): pass def decrypt_password(private_key, password): """Base64 decodes password and unecrypts it with private key. Requires openssl binary available in the path. """ unencoded = base64.b64decode(password) cmd = ['openssl', 'rsautl', '-decrypt', '-inkey', private_key] proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = proc.communicate(unencoded) proc.stdin.close() if proc.returncode: raise DecryptionFailure(err) return out python-novaclient-2.17.0/novaclient/exceptions.py0000664000175400017540000001343712306432770023331 0ustar jenkinsjenkins00000000000000# Copyright 2010 Jacob Kaplan-Moss # # 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. """ Exception definitions. """ class UnsupportedVersion(Exception): """Indicates that the user is trying to use an unsupported version of the API. """ pass class CommandError(Exception): pass class AuthorizationFailure(Exception): pass class NoUniqueMatch(Exception): pass class AuthSystemNotFound(Exception): """When the user specify a AuthSystem but not installed.""" def __init__(self, auth_system): self.auth_system = auth_system def __str__(self): return "AuthSystemNotFound: %s" % repr(self.auth_system) class NoTokenLookupException(Exception): """This form of authentication does not support looking up endpoints from an existing token. """ pass class EndpointNotFound(Exception): """Could not find Service or Region in Service Catalog.""" pass class AmbiguousEndpoints(Exception): """Found more than one matching endpoint in Service Catalog.""" def __init__(self, endpoints=None): self.endpoints = endpoints def __str__(self): return "AmbiguousEndpoints: %s" % repr(self.endpoints) class ConnectionRefused(Exception): """ Connection refused: the server refused the connection. """ def __init__(self, response=None): self.response = response def __str__(self): return "ConnectionRefused: %s" % repr(self.response) class ClientException(Exception): """ The base exception class for all exceptions this library raises. """ def __init__(self, code, message=None, details=None, request_id=None, url=None, method=None): self.code = code self.message = message or self.__class__.message self.details = details self.request_id = request_id self.url = url self.method = method def __str__(self): formatted_string = "%s (HTTP %s)" % (self.message, self.code) if self.request_id: formatted_string += " (Request-ID: %s)" % self.request_id return formatted_string class BadRequest(ClientException): """ HTTP 400 - Bad request: you sent some malformed data. """ http_status = 400 message = "Bad request" class Unauthorized(ClientException): """ HTTP 401 - Unauthorized: bad credentials. """ http_status = 401 message = "Unauthorized" class Forbidden(ClientException): """ HTTP 403 - Forbidden: your credentials don't give you access to this resource. """ http_status = 403 message = "Forbidden" class NotFound(ClientException): """ HTTP 404 - Not found """ http_status = 404 message = "Not found" class MethodNotAllowed(ClientException): """ HTTP 405 - Method Not Allowed """ http_status = 405 message = "Method Not Allowed" class Conflict(ClientException): """ HTTP 409 - Conflict """ http_status = 409 message = "Conflict" class OverLimit(ClientException): """ HTTP 413 - Over limit: you're over the API limits for this time period. """ http_status = 413 message = "Over limit" def __init__(self, *args, **kwargs): try: self.retry_after = int(kwargs.pop('retry_after')) except (KeyError, ValueError): self.retry_after = 0 super(OverLimit, self).__init__(*args, **kwargs) class RateLimit(OverLimit): """ HTTP 429 - Rate limit: you've sent too many requests for this time period. """ http_status = 429 message = "Rate limit" # NotImplemented is a python keyword. class HTTPNotImplemented(ClientException): """ HTTP 501 - Not Implemented: the server does not support this operation. """ http_status = 501 message = "Not Implemented" # In Python 2.4 Exception is old-style and thus doesn't have a __subclasses__() # so we can do this: # _code_map = dict((c.http_status, c) # for c in ClientException.__subclasses__()) # # Instead, we have to hardcode it: _error_classes = [BadRequest, Unauthorized, Forbidden, NotFound, MethodNotAllowed, Conflict, OverLimit, RateLimit, HTTPNotImplemented] _code_map = dict((c.http_status, c) for c in _error_classes) def from_response(response, body, url, method=None): """ Return an instance of an ClientException or subclass based on an requests response. Usage:: resp, body = requests.request(...) if resp.status_code != 200: raise exception_from_response(resp, rest.text) """ kwargs = { 'code': response.status_code, 'method': method, 'url': url, 'request_id': None, } if response.headers: kwargs['request_id'] = response.headers.get('x-compute-request-id') if 'retry-after' in response.headers: kwargs['retry_after'] = response.headers.get('retry-after') if body: message = "n/a" details = "n/a" if hasattr(body, 'keys'): error = body[list(body)[0]] message = error.get('message') details = error.get('details') kwargs['message'] = message kwargs['details'] = details cls = _code_map.get(response.status_code, ClientException) return cls(**kwargs) python-novaclient-2.17.0/novaclient/client.py0000664000175400017540000004536212306432770022430 0ustar jenkinsjenkins00000000000000# Copyright 2010 Jacob Kaplan-Moss # Copyright 2011 OpenStack Foundation # Copyright 2011 Piston Cloud Computing, Inc. # 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 Client interface. Handles the REST calls and responses. """ import logging import time import requests from requests import adapters try: import json except ImportError: import simplejson as json from six.moves.urllib import parse from novaclient import exceptions from novaclient.openstack.common.gettextutils import _ from novaclient import service_catalog from novaclient import utils _ADAPTERS = {} def _adapter_pool(url): """ Store and reuse HTTP adapters per Service URL. """ if url not in _ADAPTERS: _ADAPTERS[url] = adapters.HTTPAdapter() return _ADAPTERS[url] class HTTPClient(object): USER_AGENT = 'python-novaclient' def __init__(self, user, password, projectid=None, auth_url=None, insecure=False, timeout=None, proxy_tenant_id=None, proxy_token=None, region_name=None, endpoint_type='publicURL', service_type=None, service_name=None, volume_service_name=None, timings=False, bypass_url=None, os_cache=False, no_cache=True, http_log_debug=False, auth_system='keystone', auth_plugin=None, auth_token=None, cacert=None, tenant_id=None): self.user = user self.password = password self.projectid = projectid self.tenant_id = tenant_id # This will be called by #_get_password if self.password is None. # EG if a password can only be obtained by prompting the user, but a # token is available, you don't want to prompt until the token has # been proven invalid self.password_func = None if auth_system and auth_system != 'keystone' and not auth_plugin: raise exceptions.AuthSystemNotFound(auth_system) if not auth_url and auth_system and auth_system != 'keystone': auth_url = auth_plugin.get_auth_url() if not auth_url: raise exceptions.EndpointNotFound() self.auth_url = auth_url.rstrip('/') self.version = 'v1.1' self.region_name = region_name self.endpoint_type = endpoint_type self.service_type = service_type self.service_name = service_name self.volume_service_name = volume_service_name self.timings = timings self.bypass_url = bypass_url self.os_cache = os_cache or not no_cache self.http_log_debug = http_log_debug if timeout is not None: self.timeout = float(timeout) else: self.timeout = None self.times = [] # [("item", starttime, endtime), ...] self.management_url = self.bypass_url or None self.auth_token = auth_token self.proxy_token = proxy_token self.proxy_tenant_id = proxy_tenant_id self.keyring_saver = None self.keyring_saved = False if insecure: self.verify_cert = False else: if cacert: self.verify_cert = cacert else: self.verify_cert = True self.auth_system = auth_system self.auth_plugin = auth_plugin self._current_url = None self._http = None self._logger = logging.getLogger(__name__) if self.http_log_debug and not self._logger.handlers: # Logging level is already set on the root logger ch = logging.StreamHandler() self._logger.addHandler(ch) self._logger.propagate = False if hasattr(requests, 'logging'): rql = requests.logging.getLogger(requests.__name__) rql.addHandler(ch) # Since we have already setup the root logger on debug, we # have to set it up here on WARNING (its original level) # otherwise we will get all the requests logging messages rql.setLevel(logging.WARNING) def use_token_cache(self, use_it): self.os_cache = use_it def unauthenticate(self): """Forget all of our authentication information.""" self.management_url = None self.auth_token = None def set_management_url(self, url): self.management_url = url def get_timings(self): return self.times def reset_timings(self): self.times = [] def http_log_req(self, method, url, kwargs): if not self.http_log_debug: return string_parts = ['curl -i'] if not kwargs.get('verify', True): string_parts.append(' --insecure') string_parts.append(" '%s'" % url) string_parts.append(' -X %s' % method) for element in kwargs['headers']: header = ' -H "%s: %s"' % (element, kwargs['headers'][element]) string_parts.append(header) if 'data' in kwargs: string_parts.append(" -d '%s'" % (kwargs['data'])) self._logger.debug("\nREQ: %s\n" % "".join(string_parts)) def http_log_resp(self, resp): if not self.http_log_debug: return self._logger.debug(_("RESP: [%(status)s] %(headers)s\nRESP BODY: " "%(text)s\n"), {'status': resp.status_code, 'headers': resp.headers, 'text': resp.text}) def http(self, url): magic_tuple = parse.urlsplit(url) scheme, netloc, path, query, frag = magic_tuple service_url = '%s://%s' % (scheme, netloc) if self._current_url != service_url: # Invalidate Session object in case the url is somehow changed if self._http: self._http.close() self._current_url = service_url self._logger.debug("New session created for: (%s)" % service_url) self._http = requests.Session() self._http.mount(service_url, _adapter_pool(service_url)) return self._http def request(self, url, method, **kwargs): kwargs.setdefault('headers', kwargs.get('headers', {})) kwargs['headers']['User-Agent'] = self.USER_AGENT kwargs['headers']['Accept'] = 'application/json' if 'body' in kwargs: kwargs['headers']['Content-Type'] = 'application/json' kwargs['data'] = json.dumps(kwargs['body']) del kwargs['body'] if self.timeout is not None: kwargs.setdefault('timeout', self.timeout) kwargs['verify'] = self.verify_cert self.http_log_req(method, url, kwargs) resp = self.http(url).request( method, url, **kwargs) self.http_log_resp(resp) if resp.text: # TODO(dtroyer): verify the note below in a requests context # NOTE(alaski): Because force_exceptions_to_status_code=True # httplib2 returns a connection refused event as a 400 response. # To determine if it is a bad request or refused connection we need # to check the body. httplib2 tests check for 'Connection refused' # or 'actively refused' in the body, so that's what we'll do. if resp.status_code == 400: if ('Connection refused' in resp.text or 'actively refused' in resp.text): raise exceptions.ConnectionRefused(resp.text) try: body = json.loads(resp.text) except ValueError: body = None else: body = None if resp.status_code >= 400: raise exceptions.from_response(resp, body, url, method) return resp, body def _time_request(self, url, method, **kwargs): start_time = time.time() resp, body = self.request(url, method, **kwargs) self.times.append(("%s %s" % (method, url), start_time, time.time())) return resp, body def _cs_request(self, url, method, **kwargs): if not self.management_url: self.authenticate() # Perform the request once. If we get a 401 back then it # might be because the auth token expired, so try to # re-authenticate and try again. If it still fails, bail. try: kwargs.setdefault('headers', {})['X-Auth-Token'] = self.auth_token if self.projectid: kwargs['headers']['X-Auth-Project-Id'] = self.projectid resp, body = self._time_request(self.management_url + url, method, **kwargs) return resp, body except exceptions.Unauthorized as e: try: # first discard auth token, to avoid the possibly expired # token being re-used in the re-authentication attempt self.unauthenticate() # overwrite bad token self.keyring_saved = False self.authenticate() kwargs['headers']['X-Auth-Token'] = self.auth_token resp, body = self._time_request(self.management_url + url, method, **kwargs) return resp, body except exceptions.Unauthorized: raise e def _get_password(self): if not self.password and self.password_func: self.password = self.password_func() return self.password def get(self, url, **kwargs): return self._cs_request(url, 'GET', **kwargs) def post(self, url, **kwargs): return self._cs_request(url, 'POST', **kwargs) def put(self, url, **kwargs): return self._cs_request(url, 'PUT', **kwargs) def delete(self, url, **kwargs): return self._cs_request(url, 'DELETE', **kwargs) def _extract_service_catalog(self, url, resp, body, extract_token=True): """See what the auth service told us and process the response. We may get redirected to another site, fail or actually get back a service catalog with a token and our endpoints. """ # content must always present if resp.status_code == 200 or resp.status_code == 201: try: self.auth_url = url self.service_catalog = \ service_catalog.ServiceCatalog(body) if extract_token: self.auth_token = self.service_catalog.get_token() self.tenant_id = self.service_catalog.get_tenant_id() management_url = self.service_catalog.url_for( attr='region', filter_value=self.region_name, endpoint_type=self.endpoint_type, service_type=self.service_type, service_name=self.service_name, volume_service_name=self.volume_service_name,) self.management_url = management_url.rstrip('/') return None except exceptions.AmbiguousEndpoints: print(_("Found more than one valid endpoint. Use a more " "restrictive filter")) raise except KeyError: raise exceptions.AuthorizationFailure() except exceptions.EndpointNotFound: print(_("Could not find any suitable endpoint. Correct " "region?")) raise elif resp.status_code == 305: return resp.headers['location'] else: raise exceptions.from_response(resp, body, url) def _fetch_endpoints_from_auth(self, url): """We have a token, but don't know the final endpoint for the region. We have to go back to the auth service and ask again. This request requires an admin-level token to work. The proxy token supplied could be from a low-level enduser. We can't get this from the keystone service endpoint, we have to use the admin endpoint. This will overwrite our admin token with the user token. """ # GET ...:5001/v2.0/tokens/#####/endpoints url = '/'.join([url, 'tokens', '%s?belongsTo=%s' % (self.proxy_token, self.proxy_tenant_id)]) self._logger.debug(_("Using Endpoint URL: %s") % url) resp, body = self._time_request( url, "GET", headers={'X-Auth-Token': self.auth_token}) return self._extract_service_catalog(url, resp, body, extract_token=False) def authenticate(self): magic_tuple = parse.urlsplit(self.auth_url) scheme, netloc, path, query, frag = magic_tuple port = magic_tuple.port if port is None: port = 80 path_parts = path.split('/') for part in path_parts: if len(part) > 0 and part[0] == 'v': self.version = part break if self.auth_token and self.management_url: self._save_keys() return # TODO(sandy): Assume admin endpoint is 35357 for now. # Ideally this is going to have to be provided by the service catalog. new_netloc = netloc.replace(':%d' % port, ':%d' % (35357,)) admin_url = parse.urlunsplit( (scheme, new_netloc, path, query, frag)) auth_url = self.auth_url if self.version == "v2.0": # FIXME(chris): This should be better. while auth_url: if not self.auth_system or self.auth_system == 'keystone': auth_url = self._v2_auth(auth_url) else: auth_url = self._plugin_auth(auth_url) # Are we acting on behalf of another user via an # existing token? If so, our actual endpoints may # be different than that of the admin token. if self.proxy_token: if self.bypass_url: self.set_management_url(self.bypass_url) else: self._fetch_endpoints_from_auth(admin_url) # Since keystone no longer returns the user token # with the endpoints any more, we need to replace # our service account token with the user token. self.auth_token = self.proxy_token else: try: while auth_url: auth_url = self._v1_auth(auth_url) # In some configurations nova makes redirection to # v2.0 keystone endpoint. Also, new location does not contain # real endpoint, only hostname and port. except exceptions.AuthorizationFailure: if auth_url.find('v2.0') < 0: auth_url = auth_url + '/v2.0' self._v2_auth(auth_url) if self.bypass_url: self.set_management_url(self.bypass_url) elif not self.management_url: raise exceptions.Unauthorized('Nova Client') self._save_keys() def _save_keys(self): # Store the token/mgmt url in the keyring for later requests. if (self.keyring_saver and self.os_cache and not self.keyring_saved and self.auth_token and self.management_url and self.tenant_id): self.keyring_saver.save(self.auth_token, self.management_url, self.tenant_id) # Don't save it again self.keyring_saved = True def _v1_auth(self, url): if self.proxy_token: raise exceptions.NoTokenLookupException() headers = {'X-Auth-User': self.user, 'X-Auth-Key': self._get_password()} if self.projectid: headers['X-Auth-Project-Id'] = self.projectid resp, body = self._time_request(url, 'GET', headers=headers) if resp.status_code in (200, 204): # in some cases we get No Content try: mgmt_header = 'x-server-management-url' self.management_url = resp.headers[mgmt_header].rstrip('/') self.auth_token = resp.headers['x-auth-token'] self.auth_url = url except (KeyError, TypeError): raise exceptions.AuthorizationFailure() elif resp.status_code == 305: return resp.headers['location'] else: raise exceptions.from_response(resp, body, url) def _plugin_auth(self, auth_url): return self.auth_plugin.authenticate(self, auth_url) def _v2_auth(self, url): """Authenticate against a v2.0 auth service.""" if self.auth_token: body = {"auth": { "token": {"id": self.auth_token}}} else: body = {"auth": { "passwordCredentials": {"username": self.user, "password": self._get_password()}}} if self.tenant_id: body['auth']['tenantId'] = self.tenant_id elif self.projectid: body['auth']['tenantName'] = self.projectid return self._authenticate(url, body) def _authenticate(self, url, body, **kwargs): """Authenticate and extract the service catalog.""" method = "POST" token_url = url + "/tokens" # Make sure we follow redirects when trying to reach Keystone resp, respbody = self._time_request( token_url, method, body=body, allow_redirects=True, **kwargs) return self._extract_service_catalog(url, resp, respbody) def get_client_class(version): version_map = { '1.1': 'novaclient.v1_1.client.Client', '2': 'novaclient.v1_1.client.Client', '3': 'novaclient.v3.client.Client', } try: client_path = version_map[str(version)] except (KeyError, ValueError): msg = _("Invalid client version '%(version)s'. must be one of: " "%(keys)s") % {'version': version, 'keys': ', '.join(version_map.keys())} raise exceptions.UnsupportedVersion(msg) return utils.import_class(client_path) def Client(version, *args, **kwargs): client_class = get_client_class(version) return client_class(*args, **kwargs) python-novaclient-2.17.0/novaclient/extension.py0000664000175400017540000000256312306432770023162 0ustar jenkinsjenkins00000000000000# Copyright 2011 OpenStack Foundation # 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 novaclient import base from novaclient import utils class Extension(utils.HookableMixin): """Extension descriptor.""" SUPPORTED_HOOKS = ('__pre_parse_args__', '__post_parse_args__') def __init__(self, name, module): self.name = name self.module = module self._parse_extension_module() def _parse_extension_module(self): self.manager_class = None for attr_name, attr_value in self.module.__dict__.items(): if attr_name in self.SUPPORTED_HOOKS: self.add_hook(attr_name, attr_value) elif utils.safe_issubclass(attr_value, base.Manager): self.manager_class = attr_value def __repr__(self): return "" % self.name python-novaclient-2.17.0/novaclient/__init__.py0000664000175400017540000000127612306432770022705 0ustar jenkinsjenkins00000000000000# Copyright 2012 OpenStack Foundation # # 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 pbr.version __version__ = pbr.version.VersionInfo('python-novaclient').version_string() python-novaclient-2.17.0/setup.cfg0000664000175400017540000000174712306433034020250 0ustar jenkinsjenkins00000000000000[metadata] name = python-novaclient summary = Client library for OpenStack Compute API description-file = README.rst license = Apache License, Version 2.0 author = OpenStack author-email = openstack-dev@lists.openstack.org home-page = https://git.openstack.org/cgit/openstack/python-novaclient classifier = Development Status :: 5 - Production/Stable Environment :: Console Environment :: OpenStack Intended Audience :: Developers Intended Audience :: Information Technology License :: OSI Approved :: Apache Software License Operating System :: OS Independent Programming Language :: Python Programming Language :: Python :: 2.6 Programming Language :: Python :: 2.7 Programming Language :: Python :: 3.3 [files] packages = novaclient [entry_points] console_scripts = nova = novaclient.shell:main [build_sphinx] source-dir = doc/source build-dir = doc/build all_files = 1 [upload_sphinx] upload-dir = doc/build/html [egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 python-novaclient-2.17.0/setup.py0000664000175400017540000000141512306432770020137 0ustar jenkinsjenkins00000000000000#!/usr/bin/env python # Copyright (c) 2013 Hewlett-Packard Development Company, L.P. # # 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 MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT import setuptools setuptools.setup( setup_requires=['pbr'], pbr=True) python-novaclient-2.17.0/tools/0000775000175400017540000000000012306433034017556 5ustar jenkinsjenkins00000000000000python-novaclient-2.17.0/tools/install_venv.py0000664000175400017540000000475412306432770022654 0ustar jenkinsjenkins00000000000000# Copyright 2010 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. # All Rights Reserved. # # Copyright 2010 OpenStack Foundation # Copyright 2013 IBM Corp. # Copyright (c) 2013 Hewlett-Packard Development Company, L.P. # # 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 ConfigParser import os import sys import install_venv_common as install_venv # flake8: noqa def print_help(project, venv, root): help = """ %(project)s development environment setup is complete. %(project)s development uses virtualenv to track and manage Python dependencies while in development and testing. To activate the %(project)s virtualenv for the extent of your current shell session you can run: $ source %(venv)s/bin/activate Or, if you prefer, you can run commands in the virtualenv on a case by case basis by running: $ %(root)s/tools/with_venv.sh """ print help % dict(project=project, venv=venv, root=root) def main(argv): root = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) if os.environ.get('tools_path'): root = os.environ['tools_path'] venv = os.path.join(root, '.venv') if os.environ.get('venv'): venv = os.environ['venv'] pip_requires = os.path.join(root, 'requirements.txt') test_requires = os.path.join(root, 'test-requirements.txt') py_version = "python%s.%s" % (sys.version_info[0], sys.version_info[1]) setup_cfg = ConfigParser.ConfigParser() setup_cfg.read('setup.cfg') project = setup_cfg.get('metadata', 'name') install = install_venv.InstallVenv( root, venv, pip_requires, test_requires, py_version, project) options = install.parse_args(argv) install.check_python_version() install.check_dependencies() install.create_virtualenv(no_site_packages=options.no_site_packages) install.install_dependencies() print_help(project, venv, root) if __name__ == '__main__': main(sys.argv) python-novaclient-2.17.0/tools/nova.zsh_completion0000664000175400017540000000133012306432770023503 0ustar jenkinsjenkins00000000000000#compdef nova local -a nbc _nova_opts _nova_flags _nova_opts_exp cur prev nbc=(${(ps: :)$(_call_program options "$service bash-completion" 2>/dev/null)}) _nova_opts=(${nbc:#-*}) _nova_flags=(${(M)nbc:#-*}) _nova_opt_exp=${${nbc:#-*}// /|} cur=$words[CURRENT] prev=$words[(( CURRENT - 1 ))] _checkcomp(){ for word in $words[@]; do if [[ -n ${_nova_opts[(r)$word]} ]]; then return 0 fi done return 1 } echo $_nova_opts[@] |grep --color nova if [[ "$prev" != "help" ]] && _checkcomp; then COMPLETION_CACHE=(~/.novaclient/*/*-cache) cflags=($_nova_flags[@] ${(ps: :)$(cat $COMPLETION_CACHE 2>/dev/null)}) compadd "$@" -d $cflags[@] else compadd "$@" -d $_nova_opts[@] fi python-novaclient-2.17.0/tools/with_venv.sh0000775000175400017540000000012412306432770022131 0ustar jenkinsjenkins00000000000000#!/bin/bash TOOLS=`dirname $0` VENV=$TOOLS/../.venv source $VENV/bin/activate && $@ python-novaclient-2.17.0/tools/install_venv_common.py0000664000175400017540000001350612306432770024217 0ustar jenkinsjenkins00000000000000# Copyright 2013 OpenStack Foundation # Copyright 2013 IBM Corp. # # 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. """Provides methods needed by installation script for OpenStack development virtual environments. Since this script is used to bootstrap a virtualenv from the system's Python environment, it should be kept strictly compatible with Python 2.6. Synced in from openstack-common """ from __future__ import print_function import optparse import os import subprocess import sys class InstallVenv(object): def __init__(self, root, venv, requirements, test_requirements, py_version, project): self.root = root self.venv = venv self.requirements = requirements self.test_requirements = test_requirements self.py_version = py_version self.project = project def die(self, message, *args): print(message % args, file=sys.stderr) sys.exit(1) def check_python_version(self): if sys.version_info < (2, 6): self.die("Need Python Version >= 2.6") def run_command_with_code(self, cmd, redirect_output=True, check_exit_code=True): """Runs a command in an out-of-process shell. Returns the output of that command. Working directory is self.root. """ if redirect_output: stdout = subprocess.PIPE else: stdout = None proc = subprocess.Popen(cmd, cwd=self.root, stdout=stdout) output = proc.communicate()[0] if check_exit_code and proc.returncode != 0: self.die('Command "%s" failed.\n%s', ' '.join(cmd), output) return (output, proc.returncode) def run_command(self, cmd, redirect_output=True, check_exit_code=True): return self.run_command_with_code(cmd, redirect_output, check_exit_code)[0] def get_distro(self): if (os.path.exists('/etc/fedora-release') or os.path.exists('/etc/redhat-release')): return Fedora( self.root, self.venv, self.requirements, self.test_requirements, self.py_version, self.project) else: return Distro( self.root, self.venv, self.requirements, self.test_requirements, self.py_version, self.project) def check_dependencies(self): self.get_distro().install_virtualenv() def create_virtualenv(self, no_site_packages=True): """Creates the virtual environment and installs PIP. Creates the virtual environment and installs PIP only into the virtual environment. """ if not os.path.isdir(self.venv): print('Creating venv...', end=' ') if no_site_packages: self.run_command(['virtualenv', '-q', '--no-site-packages', self.venv]) else: self.run_command(['virtualenv', '-q', self.venv]) print('done.') else: print("venv already exists...") pass def pip_install(self, *args): self.run_command(['tools/with_venv.sh', 'pip', 'install', '--upgrade'] + list(args), redirect_output=False) def install_dependencies(self): print('Installing dependencies with pip (this can take a while)...') # First things first, make sure our venv has the latest pip and # setuptools and pbr self.pip_install('pip>=1.4') self.pip_install('setuptools') self.pip_install('pbr') self.pip_install('-r', self.requirements, '-r', self.test_requirements) def parse_args(self, argv): """Parses command-line arguments.""" parser = optparse.OptionParser() parser.add_option('-n', '--no-site-packages', action='store_true', help="Do not inherit packages from global Python " "install") return parser.parse_args(argv[1:])[0] class Distro(InstallVenv): def check_cmd(self, cmd): return bool(self.run_command(['which', cmd], check_exit_code=False).strip()) def install_virtualenv(self): if self.check_cmd('virtualenv'): return if self.check_cmd('easy_install'): print('Installing virtualenv via easy_install...', end=' ') if self.run_command(['easy_install', 'virtualenv']): print('Succeeded') return else: print('Failed') self.die('ERROR: virtualenv not found.\n\n%s development' ' requires virtualenv, please install it using your' ' favorite package management tool' % self.project) class Fedora(Distro): """This covers all Fedora-based distributions. Includes: Fedora, RHEL, CentOS, Scientific Linux """ def check_pkg(self, pkg): return self.run_command_with_code(['rpm', '-q', pkg], check_exit_code=False)[1] == 0 def install_virtualenv(self): if self.check_cmd('virtualenv'): return if not self.check_pkg('python-virtualenv'): self.die("Please install 'python-virtualenv'.") super(Fedora, self).install_virtualenv() python-novaclient-2.17.0/tools/nova.bash_completion0000664000175400017540000000156512306432770023626 0ustar jenkinsjenkins00000000000000_nova_opts="" # lazy init _nova_flags="" # lazy init _nova_opts_exp="" # lazy init _nova() { local cur prev nbc cflags COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" if [ "x$_nova_opts" == "x" ] ; then nbc="`nova bash-completion | sed -e "s/ *-h */ /" -e "s/ *-i */ /"`" _nova_opts="`echo "$nbc" | sed -e "s/--[a-z0-9_-]*//g" -e "s/ */ /g"`" _nova_flags="`echo " $nbc" | sed -e "s/ [^-][^-][a-z0-9_-]*//g" -e "s/ */ /g"`" _nova_opts_exp="`echo "$_nova_opts" | tr ' ' '|'`" fi if [[ " ${COMP_WORDS[@]} " =~ " "($_nova_opts_exp)" " && "$prev" != "help" ]] ; then COMPLETION_CACHE=~/.novaclient/*/*-cache cflags="$_nova_flags "$(cat $COMPLETION_CACHE 2> /dev/null | tr '\n' ' ') COMPREPLY=($(compgen -W "${cflags}" -- ${cur})) else COMPREPLY=($(compgen -W "${_nova_opts}" -- ${cur})) fi return 0 } complete -F _nova nova python-novaclient-2.17.0/README.rst0000664000175400017540000003022612306432771020117 0ustar jenkinsjenkins00000000000000Python bindings to the OpenStack Nova API ================================================== This is a client for the OpenStack Nova API. There's a Python API (the ``novaclient`` module), and a command-line script (``nova``). Each implements 100% of the OpenStack Nova API. See the `OpenStack CLI guide`_ for information on how to use the ``nova`` command-line tool. You may also want to look at the `OpenStack API documentation`_. .. _OpenStack CLI Guide: http://docs.openstack.org/cli/quick-start/content/ .. _OpenStack API documentation: http://docs.openstack.org/api/ The project is hosted on `Launchpad`_, where bugs can be filed. The code is hosted on `Github`_. Patches must be submitted using `Gerrit`_, *not* Github pull requests. .. _Github: https://github.com/openstack/python-novaclient .. _Launchpad: https://launchpad.net/python-novaclient .. _Gerrit: http://wiki.openstack.org/GerritWorkflow python-novaclient is licensed under the Apache License like the rest of OpenStack. .. contents:: Contents: :local: Command-line API ---------------- Installing this package gets you a shell command, ``nova``, that you can use to interact with any OpenStack cloud. You'll need to provide your OpenStack username and password. You can do this with the ``--os-username``, ``--os-password`` and ``--os-tenant-name`` params, but it's easier to just set them as environment variables:: export OS_USERNAME=openstack export OS_PASSWORD=yadayada export OS_TENANT_NAME=myproject You will also need to define the authentication url with ``--os-auth-url`` and the version of the API with ``--os-compute-api-version``. Or set them as an environment variables as well:: export OS_AUTH_URL=http://example.com:8774/v1.1/ export OS_COMPUTE_API_VERSION=1.1 If you are using Keystone, you need to set the OS_AUTH_URL to the keystone endpoint:: export OS_AUTH_URL=http://example.com:5000/v2.0/ Since Keystone can return multiple regions in the Service Catalog, you can specify the one you want with ``--os-region-name`` (or ``export OS_REGION_NAME``). It defaults to the first in the list returned. You'll find complete documentation on the shell by running ``nova help``:: usage: nova [--debug] [--no-cache] [--timings] [--os-username ] [--os-password ] [--os-tenant-name ] [--os-auth-url ] [--os-region-name ] [--os-auth-system ] [--service-type ] [--service-name ] [--volume-service-name ] [--endpoint-type ] [--os-compute-api-version ] [--insecure] [--bypass-url ] ... Command-line interface to the OpenStack Nova API. Positional arguments: absolute-limits Print a list of absolute limits for a user actions Retrieve server actions. add-fixed-ip Add new IP address to network. add-floating-ip Add a floating IP address to a server. aggregate-add-host Add the host to the specified aggregate. aggregate-create Create a new aggregate with the specified details. aggregate-delete Delete the aggregate by its id. aggregate-details Show details of the specified aggregate. aggregate-list Print a list of all aggregates. aggregate-remove-host Remove the specified host from the specified aggregate. aggregate-set-metadata Update the metadata associated with the aggregate. aggregate-update Update the aggregate's name and optionally availability zone. boot Boot a new server. console-log Get console log output of a server. credentials Show user credentials returned from auth delete Immediately shut down and delete a server. describe-resource Show details about a resource diagnostics Retrieve server diagnostics. dns-create Create a DNS entry for domain, name and ip. dns-create-private-domain Create the specified DNS domain. dns-create-public-domain Create the specified DNS domain. dns-delete Delete the specified DNS entry. dns-delete-domain Delete the specified DNS domain. dns-domains Print a list of available dns domains. dns-list List current DNS entries for domain and ip or domain and name. endpoints Discover endpoints that get returned from the authenticate services evacuate Evacuate a server from failed host flavor-create Create a new flavor. flavor-delete Delete a specific flavor. flavor-list Print a list of available 'flavors' (sizes of servers). flavor-show Show details about the given flavor. flavor-key Set or unset extra_spec for a flavor. flavor-access-list Print access information about the given flavor. flavor-access-add Add flavor access for the given tenant. flavor-access-remove Remove flavor access for the given tenant. floating-ip-create Allocate a floating IP for the current tenant. floating-ip-delete De-allocate a floating IP. floating-ip-list List floating ips for this tenant. floating-ip-pool-list List all floating ip pools. get-vnc-console Get a vnc console to a server. get-spice-console Get a spice console to a server. host-action Perform a power action on a host. host-update Update host settings. image-create Create a new image by taking a snapshot of a running server. image-delete Delete an image. image-list Print a list of available images to boot from. image-meta Set or Delete metadata on an image. image-show Show details about the given image. keypair-add Create a new key pair for use with instances keypair-delete Delete keypair by its name keypair-list Print a list of keypairs for a user list List active servers. live-migration Migrates a running instance to a new machine. lock Lock a server. meta Set or Delete metadata on a server. migrate Migrate a server. pause Pause a server. rate-limits Print a list of rate limits for a user reboot Reboot a server. rebuild Shutdown, re-image, and re-boot a server. refresh-network Refresh server network information. remove-fixed-ip Remove an IP address from a server. remove-floating-ip Remove a floating IP address from a server. rename Rename a server. rescue Rescue a server. resize Resize a server. resize-confirm Confirm a previous resize. resize-revert Revert a previous resize (and return to the previous VM). resume Resume a server. root-password Change the root password for a server. secgroup-add-group-rule Add a source group rule to a security group. secgroup-add-rule Add a rule to a security group. secgroup-create Create a security group. secgroup-update Update a security group. secgroup-delete Delete a security group. secgroup-delete-group-rule Delete a source group rule from a security group. secgroup-delete-rule Delete a rule from a security group. secgroup-list List security groups for the curent tenant. secgroup-list-rules List rules for a security group. show Show details about the given server. ssh SSH into a server. start Start a server. stop Stop a server. suspend Suspend a server. unlock Unlock a server. unpause Unpause a server. unrescue Unrescue a server. usage-list List usage data for all tenants volume-attach Attach a volume to a server. volume-create Add a new volume. volume-delete Remove a volume. volume-detach Detach a volume from a server. volume-list List all the volumes. volume-show Show details about a volume. volume-snapshot-create Add a new snapshot. volume-snapshot-delete Remove a snapshot. volume-snapshot-list List all the snapshots. volume-snapshot-show Show details about a snapshot. volume-type-create Create a new volume type. volume-type-delete Delete a specific flavor volume-type-list Print a list of available 'volume types'. x509-create-cert Create x509 cert for a user in tenant x509-get-root-cert Fetches the x509 root cert. bash-completion Prints all of the commands and options to stdout so that the nova.bash_completion script doesn't have to hard code them. help Display help about this program or one of its subcommands. Optional arguments: --debug Print debugging output --no-cache Don't use the auth token cache. --timings Print call timing info --os-username Defaults to env[OS_USERNAME]. --os-password Defaults to env[OS_PASSWORD]. --os-tenant-name Defaults to env[OS_TENANT_NAME]. --os-auth-url Defaults to env[OS_AUTH_URL]. --os-region-name Defaults to env[OS_REGION_NAME]. --os-auth-system Defaults to env[OS_AUTH_SYSTEM]. --service-type Defaults to compute for most actions --service-name Defaults to env[NOVA_SERVICE_NAME] --volume-service-name Defaults to env[NOVA_VOLUME_SERVICE_NAME] --endpoint-type Defaults to env[NOVA_ENDPOINT_TYPE] or publicURL. --os-compute-api-version Accepts 1.1, defaults to env[OS_COMPUTE_API_VERSION]. --username USERNAME Deprecated --insecure Explicitly allow novaclient to perform "insecure" SSL (https) requests. The server's certificate will not be verified against any certificate authorities. This option should be used with caution. --bypass-url Use this API endpoint instead of the Service Catalog See "nova help COMMAND" for help on a specific command. Python API ---------- There's also a complete Python API, but it has not yet been documented. Quick-start using keystone:: # use v2.0 auth with http://example.com:5000/v2.0/") >>> from novaclient.v1_1 import client >>> nt = client.Client(USER, PASS, TENANT, AUTH_URL, service_type="compute") >>> nt.flavors.list() [...] >>> nt.servers.list() [...] >>> nt.keypairs.list() [...] python-novaclient-2.17.0/openstack-common.conf0000664000175400017540000000037612306432770022556 0ustar jenkinsjenkins00000000000000[DEFAULT] # The list of modules to copy from openstack-common module=jsonutils module=strutils module=timeutils module=uuidutils module=apiclient module=importutils module=cliutils # The base module to hold the copy of openstack.common base=novaclient python-novaclient-2.17.0/PKG-INFO0000664000175400017540000003577712306433034017536 0ustar jenkinsjenkins00000000000000Metadata-Version: 1.1 Name: python-novaclient Version: 2.17.0 Summary: Client library for OpenStack Compute API Home-page: https://git.openstack.org/cgit/openstack/python-novaclient Author: OpenStack Author-email: openstack-dev@lists.openstack.org License: Apache License, Version 2.0 Description: Python bindings to the OpenStack Nova API ================================================== This is a client for the OpenStack Nova API. There's a Python API (the ``novaclient`` module), and a command-line script (``nova``). Each implements 100% of the OpenStack Nova API. See the `OpenStack CLI guide`_ for information on how to use the ``nova`` command-line tool. You may also want to look at the `OpenStack API documentation`_. .. _OpenStack CLI Guide: http://docs.openstack.org/cli/quick-start/content/ .. _OpenStack API documentation: http://docs.openstack.org/api/ The project is hosted on `Launchpad`_, where bugs can be filed. The code is hosted on `Github`_. Patches must be submitted using `Gerrit`_, *not* Github pull requests. .. _Github: https://github.com/openstack/python-novaclient .. _Launchpad: https://launchpad.net/python-novaclient .. _Gerrit: http://wiki.openstack.org/GerritWorkflow python-novaclient is licensed under the Apache License like the rest of OpenStack. .. contents:: Contents: :local: Command-line API ---------------- Installing this package gets you a shell command, ``nova``, that you can use to interact with any OpenStack cloud. You'll need to provide your OpenStack username and password. You can do this with the ``--os-username``, ``--os-password`` and ``--os-tenant-name`` params, but it's easier to just set them as environment variables:: export OS_USERNAME=openstack export OS_PASSWORD=yadayada export OS_TENANT_NAME=myproject You will also need to define the authentication url with ``--os-auth-url`` and the version of the API with ``--os-compute-api-version``. Or set them as an environment variables as well:: export OS_AUTH_URL=http://example.com:8774/v1.1/ export OS_COMPUTE_API_VERSION=1.1 If you are using Keystone, you need to set the OS_AUTH_URL to the keystone endpoint:: export OS_AUTH_URL=http://example.com:5000/v2.0/ Since Keystone can return multiple regions in the Service Catalog, you can specify the one you want with ``--os-region-name`` (or ``export OS_REGION_NAME``). It defaults to the first in the list returned. You'll find complete documentation on the shell by running ``nova help``:: usage: nova [--debug] [--no-cache] [--timings] [--os-username ] [--os-password ] [--os-tenant-name ] [--os-auth-url ] [--os-region-name ] [--os-auth-system ] [--service-type ] [--service-name ] [--volume-service-name ] [--endpoint-type ] [--os-compute-api-version ] [--insecure] [--bypass-url ] ... Command-line interface to the OpenStack Nova API. Positional arguments: absolute-limits Print a list of absolute limits for a user actions Retrieve server actions. add-fixed-ip Add new IP address to network. add-floating-ip Add a floating IP address to a server. aggregate-add-host Add the host to the specified aggregate. aggregate-create Create a new aggregate with the specified details. aggregate-delete Delete the aggregate by its id. aggregate-details Show details of the specified aggregate. aggregate-list Print a list of all aggregates. aggregate-remove-host Remove the specified host from the specified aggregate. aggregate-set-metadata Update the metadata associated with the aggregate. aggregate-update Update the aggregate's name and optionally availability zone. boot Boot a new server. console-log Get console log output of a server. credentials Show user credentials returned from auth delete Immediately shut down and delete a server. describe-resource Show details about a resource diagnostics Retrieve server diagnostics. dns-create Create a DNS entry for domain, name and ip. dns-create-private-domain Create the specified DNS domain. dns-create-public-domain Create the specified DNS domain. dns-delete Delete the specified DNS entry. dns-delete-domain Delete the specified DNS domain. dns-domains Print a list of available dns domains. dns-list List current DNS entries for domain and ip or domain and name. endpoints Discover endpoints that get returned from the authenticate services evacuate Evacuate a server from failed host flavor-create Create a new flavor. flavor-delete Delete a specific flavor. flavor-list Print a list of available 'flavors' (sizes of servers). flavor-show Show details about the given flavor. flavor-key Set or unset extra_spec for a flavor. flavor-access-list Print access information about the given flavor. flavor-access-add Add flavor access for the given tenant. flavor-access-remove Remove flavor access for the given tenant. floating-ip-create Allocate a floating IP for the current tenant. floating-ip-delete De-allocate a floating IP. floating-ip-list List floating ips for this tenant. floating-ip-pool-list List all floating ip pools. get-vnc-console Get a vnc console to a server. get-spice-console Get a spice console to a server. host-action Perform a power action on a host. host-update Update host settings. image-create Create a new image by taking a snapshot of a running server. image-delete Delete an image. image-list Print a list of available images to boot from. image-meta Set or Delete metadata on an image. image-show Show details about the given image. keypair-add Create a new key pair for use with instances keypair-delete Delete keypair by its name keypair-list Print a list of keypairs for a user list List active servers. live-migration Migrates a running instance to a new machine. lock Lock a server. meta Set or Delete metadata on a server. migrate Migrate a server. pause Pause a server. rate-limits Print a list of rate limits for a user reboot Reboot a server. rebuild Shutdown, re-image, and re-boot a server. refresh-network Refresh server network information. remove-fixed-ip Remove an IP address from a server. remove-floating-ip Remove a floating IP address from a server. rename Rename a server. rescue Rescue a server. resize Resize a server. resize-confirm Confirm a previous resize. resize-revert Revert a previous resize (and return to the previous VM). resume Resume a server. root-password Change the root password for a server. secgroup-add-group-rule Add a source group rule to a security group. secgroup-add-rule Add a rule to a security group. secgroup-create Create a security group. secgroup-update Update a security group. secgroup-delete Delete a security group. secgroup-delete-group-rule Delete a source group rule from a security group. secgroup-delete-rule Delete a rule from a security group. secgroup-list List security groups for the curent tenant. secgroup-list-rules List rules for a security group. show Show details about the given server. ssh SSH into a server. start Start a server. stop Stop a server. suspend Suspend a server. unlock Unlock a server. unpause Unpause a server. unrescue Unrescue a server. usage-list List usage data for all tenants volume-attach Attach a volume to a server. volume-create Add a new volume. volume-delete Remove a volume. volume-detach Detach a volume from a server. volume-list List all the volumes. volume-show Show details about a volume. volume-snapshot-create Add a new snapshot. volume-snapshot-delete Remove a snapshot. volume-snapshot-list List all the snapshots. volume-snapshot-show Show details about a snapshot. volume-type-create Create a new volume type. volume-type-delete Delete a specific flavor volume-type-list Print a list of available 'volume types'. x509-create-cert Create x509 cert for a user in tenant x509-get-root-cert Fetches the x509 root cert. bash-completion Prints all of the commands and options to stdout so that the nova.bash_completion script doesn't have to hard code them. help Display help about this program or one of its subcommands. Optional arguments: --debug Print debugging output --no-cache Don't use the auth token cache. --timings Print call timing info --os-username Defaults to env[OS_USERNAME]. --os-password Defaults to env[OS_PASSWORD]. --os-tenant-name Defaults to env[OS_TENANT_NAME]. --os-auth-url Defaults to env[OS_AUTH_URL]. --os-region-name Defaults to env[OS_REGION_NAME]. --os-auth-system Defaults to env[OS_AUTH_SYSTEM]. --service-type Defaults to compute for most actions --service-name Defaults to env[NOVA_SERVICE_NAME] --volume-service-name Defaults to env[NOVA_VOLUME_SERVICE_NAME] --endpoint-type Defaults to env[NOVA_ENDPOINT_TYPE] or publicURL. --os-compute-api-version Accepts 1.1, defaults to env[OS_COMPUTE_API_VERSION]. --username USERNAME Deprecated --insecure Explicitly allow novaclient to perform "insecure" SSL (https) requests. The server's certificate will not be verified against any certificate authorities. This option should be used with caution. --bypass-url Use this API endpoint instead of the Service Catalog See "nova help COMMAND" for help on a specific command. Python API ---------- There's also a complete Python API, but it has not yet been documented. Quick-start using keystone:: # use v2.0 auth with http://example.com:5000/v2.0/") >>> from novaclient.v1_1 import client >>> nt = client.Client(USER, PASS, TENANT, AUTH_URL, service_type="compute") >>> nt.flavors.list() [...] >>> nt.servers.list() [...] >>> nt.keypairs.list() [...] Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Console Classifier: Environment :: OpenStack Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Information Technology Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3.3 python-novaclient-2.17.0/.testr.conf0000664000175400017540000000035412306432770020514 0ustar jenkinsjenkins00000000000000[DEFAULT] test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} ${PYTHON:-python} -m subunit.run discover -t ./ ./ $LISTOPT $IDOPTION test_id_option=--load-list $IDFILE test_list_option=--list python-novaclient-2.17.0/python_novaclient.egg-info/0000775000175400017540000000000012306433034023653 5ustar jenkinsjenkins00000000000000python-novaclient-2.17.0/python_novaclient.egg-info/dependency_links.txt0000664000175400017540000000000112306433034027721 0ustar jenkinsjenkins00000000000000 python-novaclient-2.17.0/python_novaclient.egg-info/not-zip-safe0000664000175400017540000000000112306433031026076 0ustar jenkinsjenkins00000000000000 python-novaclient-2.17.0/python_novaclient.egg-info/PKG-INFO0000664000175400017540000003577712306433034024773 0ustar jenkinsjenkins00000000000000Metadata-Version: 1.1 Name: python-novaclient Version: 2.17.0 Summary: Client library for OpenStack Compute API Home-page: https://git.openstack.org/cgit/openstack/python-novaclient Author: OpenStack Author-email: openstack-dev@lists.openstack.org License: Apache License, Version 2.0 Description: Python bindings to the OpenStack Nova API ================================================== This is a client for the OpenStack Nova API. There's a Python API (the ``novaclient`` module), and a command-line script (``nova``). Each implements 100% of the OpenStack Nova API. See the `OpenStack CLI guide`_ for information on how to use the ``nova`` command-line tool. You may also want to look at the `OpenStack API documentation`_. .. _OpenStack CLI Guide: http://docs.openstack.org/cli/quick-start/content/ .. _OpenStack API documentation: http://docs.openstack.org/api/ The project is hosted on `Launchpad`_, where bugs can be filed. The code is hosted on `Github`_. Patches must be submitted using `Gerrit`_, *not* Github pull requests. .. _Github: https://github.com/openstack/python-novaclient .. _Launchpad: https://launchpad.net/python-novaclient .. _Gerrit: http://wiki.openstack.org/GerritWorkflow python-novaclient is licensed under the Apache License like the rest of OpenStack. .. contents:: Contents: :local: Command-line API ---------------- Installing this package gets you a shell command, ``nova``, that you can use to interact with any OpenStack cloud. You'll need to provide your OpenStack username and password. You can do this with the ``--os-username``, ``--os-password`` and ``--os-tenant-name`` params, but it's easier to just set them as environment variables:: export OS_USERNAME=openstack export OS_PASSWORD=yadayada export OS_TENANT_NAME=myproject You will also need to define the authentication url with ``--os-auth-url`` and the version of the API with ``--os-compute-api-version``. Or set them as an environment variables as well:: export OS_AUTH_URL=http://example.com:8774/v1.1/ export OS_COMPUTE_API_VERSION=1.1 If you are using Keystone, you need to set the OS_AUTH_URL to the keystone endpoint:: export OS_AUTH_URL=http://example.com:5000/v2.0/ Since Keystone can return multiple regions in the Service Catalog, you can specify the one you want with ``--os-region-name`` (or ``export OS_REGION_NAME``). It defaults to the first in the list returned. You'll find complete documentation on the shell by running ``nova help``:: usage: nova [--debug] [--no-cache] [--timings] [--os-username ] [--os-password ] [--os-tenant-name ] [--os-auth-url ] [--os-region-name ] [--os-auth-system ] [--service-type ] [--service-name ] [--volume-service-name ] [--endpoint-type ] [--os-compute-api-version ] [--insecure] [--bypass-url ] ... Command-line interface to the OpenStack Nova API. Positional arguments: absolute-limits Print a list of absolute limits for a user actions Retrieve server actions. add-fixed-ip Add new IP address to network. add-floating-ip Add a floating IP address to a server. aggregate-add-host Add the host to the specified aggregate. aggregate-create Create a new aggregate with the specified details. aggregate-delete Delete the aggregate by its id. aggregate-details Show details of the specified aggregate. aggregate-list Print a list of all aggregates. aggregate-remove-host Remove the specified host from the specified aggregate. aggregate-set-metadata Update the metadata associated with the aggregate. aggregate-update Update the aggregate's name and optionally availability zone. boot Boot a new server. console-log Get console log output of a server. credentials Show user credentials returned from auth delete Immediately shut down and delete a server. describe-resource Show details about a resource diagnostics Retrieve server diagnostics. dns-create Create a DNS entry for domain, name and ip. dns-create-private-domain Create the specified DNS domain. dns-create-public-domain Create the specified DNS domain. dns-delete Delete the specified DNS entry. dns-delete-domain Delete the specified DNS domain. dns-domains Print a list of available dns domains. dns-list List current DNS entries for domain and ip or domain and name. endpoints Discover endpoints that get returned from the authenticate services evacuate Evacuate a server from failed host flavor-create Create a new flavor. flavor-delete Delete a specific flavor. flavor-list Print a list of available 'flavors' (sizes of servers). flavor-show Show details about the given flavor. flavor-key Set or unset extra_spec for a flavor. flavor-access-list Print access information about the given flavor. flavor-access-add Add flavor access for the given tenant. flavor-access-remove Remove flavor access for the given tenant. floating-ip-create Allocate a floating IP for the current tenant. floating-ip-delete De-allocate a floating IP. floating-ip-list List floating ips for this tenant. floating-ip-pool-list List all floating ip pools. get-vnc-console Get a vnc console to a server. get-spice-console Get a spice console to a server. host-action Perform a power action on a host. host-update Update host settings. image-create Create a new image by taking a snapshot of a running server. image-delete Delete an image. image-list Print a list of available images to boot from. image-meta Set or Delete metadata on an image. image-show Show details about the given image. keypair-add Create a new key pair for use with instances keypair-delete Delete keypair by its name keypair-list Print a list of keypairs for a user list List active servers. live-migration Migrates a running instance to a new machine. lock Lock a server. meta Set or Delete metadata on a server. migrate Migrate a server. pause Pause a server. rate-limits Print a list of rate limits for a user reboot Reboot a server. rebuild Shutdown, re-image, and re-boot a server. refresh-network Refresh server network information. remove-fixed-ip Remove an IP address from a server. remove-floating-ip Remove a floating IP address from a server. rename Rename a server. rescue Rescue a server. resize Resize a server. resize-confirm Confirm a previous resize. resize-revert Revert a previous resize (and return to the previous VM). resume Resume a server. root-password Change the root password for a server. secgroup-add-group-rule Add a source group rule to a security group. secgroup-add-rule Add a rule to a security group. secgroup-create Create a security group. secgroup-update Update a security group. secgroup-delete Delete a security group. secgroup-delete-group-rule Delete a source group rule from a security group. secgroup-delete-rule Delete a rule from a security group. secgroup-list List security groups for the curent tenant. secgroup-list-rules List rules for a security group. show Show details about the given server. ssh SSH into a server. start Start a server. stop Stop a server. suspend Suspend a server. unlock Unlock a server. unpause Unpause a server. unrescue Unrescue a server. usage-list List usage data for all tenants volume-attach Attach a volume to a server. volume-create Add a new volume. volume-delete Remove a volume. volume-detach Detach a volume from a server. volume-list List all the volumes. volume-show Show details about a volume. volume-snapshot-create Add a new snapshot. volume-snapshot-delete Remove a snapshot. volume-snapshot-list List all the snapshots. volume-snapshot-show Show details about a snapshot. volume-type-create Create a new volume type. volume-type-delete Delete a specific flavor volume-type-list Print a list of available 'volume types'. x509-create-cert Create x509 cert for a user in tenant x509-get-root-cert Fetches the x509 root cert. bash-completion Prints all of the commands and options to stdout so that the nova.bash_completion script doesn't have to hard code them. help Display help about this program or one of its subcommands. Optional arguments: --debug Print debugging output --no-cache Don't use the auth token cache. --timings Print call timing info --os-username Defaults to env[OS_USERNAME]. --os-password Defaults to env[OS_PASSWORD]. --os-tenant-name Defaults to env[OS_TENANT_NAME]. --os-auth-url Defaults to env[OS_AUTH_URL]. --os-region-name Defaults to env[OS_REGION_NAME]. --os-auth-system Defaults to env[OS_AUTH_SYSTEM]. --service-type Defaults to compute for most actions --service-name Defaults to env[NOVA_SERVICE_NAME] --volume-service-name Defaults to env[NOVA_VOLUME_SERVICE_NAME] --endpoint-type Defaults to env[NOVA_ENDPOINT_TYPE] or publicURL. --os-compute-api-version Accepts 1.1, defaults to env[OS_COMPUTE_API_VERSION]. --username USERNAME Deprecated --insecure Explicitly allow novaclient to perform "insecure" SSL (https) requests. The server's certificate will not be verified against any certificate authorities. This option should be used with caution. --bypass-url Use this API endpoint instead of the Service Catalog See "nova help COMMAND" for help on a specific command. Python API ---------- There's also a complete Python API, but it has not yet been documented. Quick-start using keystone:: # use v2.0 auth with http://example.com:5000/v2.0/") >>> from novaclient.v1_1 import client >>> nt = client.Client(USER, PASS, TENANT, AUTH_URL, service_type="compute") >>> nt.flavors.list() [...] >>> nt.servers.list() [...] >>> nt.keypairs.list() [...] Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Console Classifier: Environment :: OpenStack Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Information Technology Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3.3 python-novaclient-2.17.0/python_novaclient.egg-info/entry_points.txt0000664000175400017540000000006012306433034027145 0ustar jenkinsjenkins00000000000000[console_scripts] nova = novaclient.shell:main python-novaclient-2.17.0/python_novaclient.egg-info/top_level.txt0000664000175400017540000000001312306433034026377 0ustar jenkinsjenkins00000000000000novaclient python-novaclient-2.17.0/python_novaclient.egg-info/requires.txt0000664000175400017540000000015012306433034026247 0ustar jenkinsjenkins00000000000000pbr>=0.6,<1.0 iso8601>=0.1.8 PrettyTable>=0.7,<0.8 requests>=1.1 simplejson>=2.0.9 six>=1.5.2 Babel>=1.3python-novaclient-2.17.0/python_novaclient.egg-info/SOURCES.txt0000664000175400017540000001502012306433034025535 0ustar jenkinsjenkins00000000000000.coveragerc .mailmap .testr.conf AUTHORS CONTRIBUTING.rst ChangeLog HACKING.rst LICENSE MANIFEST.in README.rst openstack-common.conf requirements.txt run_tests.sh setup.cfg setup.py test-requirements.txt tox.ini doc/.gitignore doc/Makefile doc/source/api.rst doc/source/conf.py doc/source/index.rst doc/source/shell.rst doc/source/man/nova.rst novaclient/__init__.py novaclient/auth_plugin.py novaclient/base.py novaclient/client.py novaclient/crypto.py novaclient/exceptions.py novaclient/extension.py novaclient/service_catalog.py novaclient/shell.py novaclient/utils.py novaclient/openstack/__init__.py novaclient/openstack/common/__init__.py novaclient/openstack/common/cliutils.py novaclient/openstack/common/gettextutils.py novaclient/openstack/common/importutils.py novaclient/openstack/common/jsonutils.py novaclient/openstack/common/strutils.py novaclient/openstack/common/timeutils.py novaclient/openstack/common/uuidutils.py novaclient/openstack/common/apiclient/__init__.py novaclient/openstack/common/apiclient/auth.py novaclient/openstack/common/apiclient/base.py novaclient/openstack/common/apiclient/client.py novaclient/openstack/common/apiclient/exceptions.py novaclient/openstack/common/apiclient/fake_client.py novaclient/openstack/common/py3kcompat/__init__.py novaclient/openstack/common/py3kcompat/urlutils.py novaclient/tests/__init__.py novaclient/tests/fakes.py novaclient/tests/idfake.pem novaclient/tests/test_auth_plugins.py novaclient/tests/test_base.py novaclient/tests/test_client.py novaclient/tests/test_discover.py novaclient/tests/test_http.py novaclient/tests/test_service_catalog.py novaclient/tests/test_shell.py novaclient/tests/test_utils.py novaclient/tests/utils.py novaclient/tests/v1_1/__init__.py novaclient/tests/v1_1/fakes.py novaclient/tests/v1_1/test_agents.py novaclient/tests/v1_1/test_aggregates.py novaclient/tests/v1_1/test_auth.py novaclient/tests/v1_1/test_availability_zone.py novaclient/tests/v1_1/test_certs.py novaclient/tests/v1_1/test_cloudpipe.py novaclient/tests/v1_1/test_fixed_ips.py novaclient/tests/v1_1/test_flavor_access.py novaclient/tests/v1_1/test_flavors.py novaclient/tests/v1_1/test_floating_ip_dns.py novaclient/tests/v1_1/test_floating_ip_pools.py novaclient/tests/v1_1/test_floating_ips.py novaclient/tests/v1_1/test_floating_ips_bulk.py novaclient/tests/v1_1/test_fping.py novaclient/tests/v1_1/test_hosts.py novaclient/tests/v1_1/test_hypervisors.py novaclient/tests/v1_1/test_images.py novaclient/tests/v1_1/test_keypairs.py novaclient/tests/v1_1/test_limits.py novaclient/tests/v1_1/test_networks.py novaclient/tests/v1_1/test_quota_classes.py novaclient/tests/v1_1/test_quotas.py novaclient/tests/v1_1/test_security_group_rules.py novaclient/tests/v1_1/test_security_groups.py novaclient/tests/v1_1/test_servers.py novaclient/tests/v1_1/test_services.py novaclient/tests/v1_1/test_shell.py novaclient/tests/v1_1/test_usage.py novaclient/tests/v1_1/test_volumes.py novaclient/tests/v1_1/testfile.txt novaclient/tests/v1_1/utils.py novaclient/tests/v1_1/contrib/__init__.py novaclient/tests/v1_1/contrib/fakes.py novaclient/tests/v1_1/contrib/test_assisted_volume_snapshots.py novaclient/tests/v1_1/contrib/test_baremetal.py novaclient/tests/v1_1/contrib/test_cells.py novaclient/tests/v1_1/contrib/test_instance_actions.py novaclient/tests/v1_1/contrib/test_list_extensions.py novaclient/tests/v1_1/contrib/test_migrations.py novaclient/tests/v1_1/contrib/test_server_external_events.py novaclient/tests/v1_1/contrib/test_tenant_networks.py novaclient/tests/v3/__init__.py novaclient/tests/v3/fakes.py novaclient/tests/v3/test_agents.py novaclient/tests/v3/test_aggregates.py novaclient/tests/v3/test_availability_zone.py novaclient/tests/v3/test_certs.py novaclient/tests/v3/test_flavor_access.py novaclient/tests/v3/test_flavors.py novaclient/tests/v3/test_hosts.py novaclient/tests/v3/test_hypervisors.py novaclient/tests/v3/test_images.py novaclient/tests/v3/test_keypairs.py novaclient/tests/v3/test_quota_classes.py novaclient/tests/v3/test_quotas.py novaclient/tests/v3/test_servers.py novaclient/tests/v3/test_services.py novaclient/tests/v3/test_shell.py novaclient/tests/v3/test_usage.py novaclient/tests/v3/test_volumes.py novaclient/v1_1/__init__.py novaclient/v1_1/agents.py novaclient/v1_1/aggregates.py novaclient/v1_1/availability_zones.py novaclient/v1_1/certs.py novaclient/v1_1/client.py novaclient/v1_1/cloudpipe.py novaclient/v1_1/fixed_ips.py novaclient/v1_1/flavor_access.py novaclient/v1_1/flavors.py novaclient/v1_1/floating_ip_dns.py novaclient/v1_1/floating_ip_pools.py novaclient/v1_1/floating_ips.py novaclient/v1_1/floating_ips_bulk.py novaclient/v1_1/fping.py novaclient/v1_1/hosts.py novaclient/v1_1/hypervisors.py novaclient/v1_1/images.py novaclient/v1_1/keypairs.py novaclient/v1_1/limits.py novaclient/v1_1/networks.py novaclient/v1_1/quota_classes.py novaclient/v1_1/quotas.py novaclient/v1_1/security_group_rules.py novaclient/v1_1/security_groups.py novaclient/v1_1/servers.py novaclient/v1_1/services.py novaclient/v1_1/shell.py novaclient/v1_1/usage.py novaclient/v1_1/virtual_interfaces.py novaclient/v1_1/volume_snapshots.py novaclient/v1_1/volume_types.py novaclient/v1_1/volumes.py novaclient/v1_1/contrib/__init__.py novaclient/v1_1/contrib/assisted_volume_snapshots.py novaclient/v1_1/contrib/baremetal.py novaclient/v1_1/contrib/cells.py novaclient/v1_1/contrib/deferred_delete.py novaclient/v1_1/contrib/host_evacuate.py novaclient/v1_1/contrib/host_servers_migrate.py novaclient/v1_1/contrib/instance_action.py novaclient/v1_1/contrib/list_extensions.py novaclient/v1_1/contrib/metadata_extensions.py novaclient/v1_1/contrib/migrations.py novaclient/v1_1/contrib/server_external_events.py novaclient/v1_1/contrib/tenant_networks.py novaclient/v3/__init__.py novaclient/v3/agents.py novaclient/v3/aggregates.py novaclient/v3/availability_zones.py novaclient/v3/certs.py novaclient/v3/client.py novaclient/v3/flavor_access.py novaclient/v3/flavors.py novaclient/v3/hosts.py novaclient/v3/hypervisors.py novaclient/v3/images.py novaclient/v3/keypairs.py novaclient/v3/quota_classes.py novaclient/v3/quotas.py novaclient/v3/servers.py novaclient/v3/services.py novaclient/v3/shell.py novaclient/v3/usage.py novaclient/v3/volumes.py python_novaclient.egg-info/PKG-INFO python_novaclient.egg-info/SOURCES.txt python_novaclient.egg-info/dependency_links.txt python_novaclient.egg-info/entry_points.txt python_novaclient.egg-info/not-zip-safe python_novaclient.egg-info/requires.txt python_novaclient.egg-info/top_level.txt tools/install_venv.py tools/install_venv_common.py tools/nova.bash_completion tools/nova.zsh_completion tools/with_venv.shpython-novaclient-2.17.0/LICENSE0000664000175400017540000002707312306432770017442 0ustar jenkinsjenkins00000000000000Copyright (c) 2009 Jacob Kaplan-Moss - initial codebase (< v2.1) Copyright (c) 2011 Rackspace - OpenStack extensions (>= v2.1) All rights reserved. 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. --- License for python-novaclient versions prior to 2.1 --- All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of this project nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. python-novaclient-2.17.0/requirements.txt0000664000175400017540000000016212306432770021707 0ustar jenkinsjenkins00000000000000pbr>=0.6,<1.0 argparse iso8601>=0.1.8 PrettyTable>=0.7,<0.8 requests>=1.1 simplejson>=2.0.9 six>=1.5.2 Babel>=1.3 python-novaclient-2.17.0/MANIFEST.in0000664000175400017540000000011112306432770020153 0ustar jenkinsjenkins00000000000000include AUTHORS include ChangeLog exclude .gitignore exclude .gitreview python-novaclient-2.17.0/run_tests.sh0000775000175400017540000001056212306432770021015 0ustar jenkinsjenkins00000000000000#!/bin/bash set -eu function usage { echo "Usage: $0 [OPTION]..." echo "Run python-novaclient test suite" echo "" 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 " -s, --no-site-packages Isolate the virtualenv from the global Python environment" 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 " -p, --pep8 Just run pep8" 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 "" 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." exit } function process_option { 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;; -s|--no-site-packages) no_site_packages=1;; -f|--force) force=1;; -p|--pep8) just_pep8=1;; -P|--no-pep8) no_pep8=1;; -c|--coverage) coverage=1;; -*) testropts="$testropts $1";; *) testrargs="$testrargs $1" esac } venv=.venv with_venv=tools/with_venv.sh always_venv=0 never_venv=0 force=0 no_site_packages=0 installvenvopts= testrargs= testropts= wrapper="" just_pep8=0 no_pep8=0 coverage=0 LANG=en_US.UTF-8 LANGUAGE=en_US:en LC_ALL=C for arg in "$@"; do process_option $arg done if [ $no_site_packages -eq 1 ]; then installvenvopts="--no-site-packages" fi function init_testr { if [ ! -d .testrepository ]; then ${wrapper} testr init fi } function run_tests { # Cleanup *pyc ${wrapper} find . -type f -name "*.pyc" -delete if [ $coverage -eq 1 ]; then # Do not test test_coverage_ext when gathering coverage. if [ "x$testrargs" = "x" ]; then testrargs="^(?!.*test_coverage_ext).*$" fi export PYTHON="${wrapper} coverage run --source novaclient --parallel-mode" fi # Just run the test suites in current environment set +e TESTRTESTS="$TESTRTESTS $testrargs" echo "Running \`${wrapper} $TESTRTESTS\`" ${wrapper} $TESTRTESTS RESULT=$? set -e copy_subunit_log return $RESULT } function copy_subunit_log { LOGNAME=`cat .testrepository/next-stream` LOGNAME=$(($LOGNAME - 1)) LOGNAME=".testrepository/${LOGNAME}" cp $LOGNAME subunit.log } function run_pep8 { echo "Running flake8 ..." ${wrapper} flake8 } TESTRTESTS="testr run --parallel $testropts" if [ $never_venv -eq 0 ] then # Remove the virtual environment if --force used if [ $force -eq 1 ]; then echo "Cleaning virtualenv..." rm -rf ${venv} fi if [ -e ${venv} ]; then wrapper="${with_venv}" else if [ $always_venv -eq 1 ]; then # Automatically install the virtualenv python tools/install_venv.py $installvenvopts wrapper="${with_venv}" else echo -e "No virtual environment found...create one? (Y/n) \c" read use_ve if [ "x$use_ve" = "xY" -o "x$use_ve" = "x" -o "x$use_ve" = "xy" ]; then # Install the virtualenv and run the test suite in it python tools/install_venv.py $installvenvopts wrapper=${with_venv} fi fi fi fi # Delete old coverage data from previous runs if [ $coverage -eq 1 ]; then ${wrapper} coverage erase fi if [ $just_pep8 -eq 1 ]; then run_pep8 exit fi init_testr run_tests # 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 (testrargs). if [ -z "$testrargs" ]; then if [ $no_pep8 -eq 0 ]; then run_pep8 fi fi if [ $coverage -eq 1 ]; then echo "Generating coverage report in covhtml/" ${wrapper} coverage combine ${wrapper} coverage html --include='novaclient/*' --omit='novaclient/openstack/common/*' -d covhtml -i fi python-novaclient-2.17.0/AUTHORS0000664000175400017540000000000112306433034017455 0ustar jenkinsjenkins00000000000000 python-novaclient-2.17.0/tox.ini0000664000175400017540000000123012306432770017733 0ustar jenkinsjenkins00000000000000[tox] envlist = py26,py27,py33,pypy,pep8 minversion = 1.6 skipsdist = True [testenv] usedevelop = True install_command = pip install -U {opts} {packages} setenv = VIRTUAL_ENV={envdir} deps = -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt commands = python setup.py testr --testr-args='{posargs}' [testenv:pep8] commands = flake8 [testenv:venv] commands = {posargs} [testenv:cover] commands = python setup.py testr --coverage --testr-args='{posargs}' [tox:jenkins] downloadcache = ~/cache/pip [flake8] ignore = E12,F841,F811,F821,H302,H404 show-source = True exclude=.venv,.git,.tox,dist,*openstack/common*,*lib/python*,*egg,build