pax_global_header00006660000000000000000000000064132436506550014523gustar00rootroot0000000000000052 comment=0bef6fca71ac6c820ff1bd57ff0988bf0ea58351 softlayer-python-5.4.2/000077500000000000000000000000001324365065500150625ustar00rootroot00000000000000softlayer-python-5.4.2/.gitignore000066400000000000000000000001751324365065500170550ustar00rootroot00000000000000.DS_Store *.swp Thumbs.db .svn ._* *.pyc .coverage htmlcov cover/* .tox docs/_build/* build/* dist/* *.egg-info .cache .idea softlayer-python-5.4.2/.travis.yml000066400000000000000000000007371324365065500172020ustar00rootroot00000000000000language: python sudo: false matrix: include: - python: "2.7" env: TOX_ENV=py27 - python: "3.5" env: TOX_ENV=py35 - python: "3.6" env: TOX_ENV=py36 - python: "pypy2.7-5.8.0" env: TOX_ENV=pypy - python: "2.7" env: TOX_ENV=analysis - python: "2.7" env: TOX_ENV=coverage install: - pip install tox - pip install coveralls script: - tox -e $TOX_ENV after_success: - if [[ $TOX_ENV = "coverage" ]]; then coveralls; fi softlayer-python-5.4.2/CHANGELOG.md000066400000000000000000000557571324365065500167160ustar00rootroot00000000000000# Change Log ## [5.4.2] - 2018-02-22 - Changes: https://github.com/softlayer/softlayer-python/compare/v5.4.1...master - add GPU to the virtual create-options table - Remove 'virtual' from the hardware ready command. - Carefully check for the metric tracking id on virtual guests when building a bandwidth report. - Do not fail if the source or destination subnet mask does not exist for ipv6 rules. ## [5.4.1] - 2018-02-05 - Changes: https://github.com/softlayer/softlayer-python/compare/v5.4.0...v5.4.1 - Improve error conditions when adding SSH keys - added type filters to package-list, auto-removes bluemix_services on package listing - Add boot mode option to virtual guest creation - Update documentation for security group rule add - Add fix for unsetting of values in edit SG rules ## [5.4.0] - 2018-01-15 - Changes: https://github.com/softlayer/softlayer-python/compare/v5.3.2...v5.4.0 - Upgraded Requests and Urllib3 library to latest. This allows the library to make use of connection retries, and connection pools. This should prevent the client from crashing if the API gives a connection reset / connection timeout error - reworked wait_for_ready function for virtual, and added to hardware managers. - fixed block/file iops in the `slcli block|file detail` view - Added sub items to `hw detail --price`, removed reverse PTR entries ### Added to CLI - slcli order ``` $ ./slcli order Usage: slcli order [OPTIONS] COMMAND [ARGS]... Options: -h, --help Show this message and exit. Commands: category-list List the categories of a package. item-list List package items used for ordering. package-list List packages that can be ordered via the... package-locations List Datacenters a package can be ordered in. place Place or verify an order. preset-list List package presets. ``` ## [5.3.2] - 2017-12-18 - Changes: https://github.com/softlayer/softlayer-python/compare/v5.3.1...v5.3.2 - Expanded `@retry` useage to a few areas in the hardware manager - Added INTERVAL options to block and file replication - Fixed pricing error on `hw detail --price` - Added sub items to `hw detail --price`, removed reverse PTR entries ### Added to CLI - slcli dedicatedhost ## [5.3.1] - 2017-12-07 - Changes: https://github.com/softlayer/softlayer-python/compare/v5.3.0...v5.3.1 - Added support for storage volume modifications ### Added to CLI - slcli block volume-modify - slcli file volume-modify ## [5.3.0] - 2017-12-01 - Changes: https://github.com/softlayer/softlayer-python/compare/v5.2.15...v5.3.0 - Added a retry decorator. currently only used in setTags for VSI creation, which should allos VSI creation to be a bit more robust. - Updated unit tests to work with pytest3.3 ## [5.2.15] - 2017-10-30 - Changes: https://github.com/softlayer/softlayer-python/compare/v5.2.14...v5.2.15 - Added dedicated host info to virt detail - #885 - Fixed createObjects on the rest api endpoint - changed securityGroups to use createObject instead of createObjects - Always set the endpoint_url by defaulting to the public URL if the endpoint type cannot be determined. - resource metadata update ## [5.2.14] - 2017-09-13 - Changes: https://github.com/softlayer/softlayer-python/compare/v5.2.13...v5.2.14 - Improved slcli vs create-options output - Updated slcli vs create to support new virtual server public and dedicated host offerings ## [5.2.13] - 2017-09-05 - Changes: https://github.com/softlayer/softlayer-python/compare/v5.2.12...v5.2.13 - Support for hourly billing of storage - Added exception handling for Managers.VSManager.wait_for_ready() - Added windows support for unit testing - Updated pypy version ## [5.2.12] - 2017-08-09 - Changes: https://github.com/softlayer/softlayer-python/compare/v5.2.11...v5.2.12 - Support for storage_as_a_service block and file storage #### Added to CLI - block volume-count - file volume-count - securitygroups - create Create a security group. - delete Deletes the given security group - detail Get details about a security group. - edit Edit details of a security group. - interface-add Attach an interface to a security group. - interface-list List interfaces associated with security... - interface-remove Detach an interface from a security group. - list List security groups. - rule-add Add a security group rule to a security... - rule-edit Edit a security group rule in a security... - rule-list List security group rules. - rule-remove ## [5.2.11] - 2017-08-04 - Changes: https://github.com/softlayer/softlayer-python/compare/v5.2.10...v5.2.11 - Sync VLAN and subnet detail CLI output ## [5.2.10] - 2017-07-27 - Changes: https://github.com/softlayer/softlayer-python/compare/v5.2.9...v5.2.10 - Avoid blindly passing memory result to formatter ## [5.2.9] - 2017-07-27 - Changes: https://github.com/softlayer/softlayer-python/compare/v5.2.8...v5.2.9 - Add support for dedicated host instances to virtual server upgrades #### Added to CLI * block volume-set-lun-id ## [5.2.8] - 2017-07-19 - Changes: https://github.com/softlayer/softlayer-python/compare/v5.2.7...v5.2.8 * Resolved https://github.com/softlayer/softlayer-python/issues/835 * Resolved https://github.com/softlayer/softlayer-python/issues/826 * Fix dedicated/private VSI price retrieval for upgrades #### Added to CLI * block access-password ## [5.2.7] - 2017-06-22 - Changes: https://github.com/softlayer/softlayer-python/compare/v5.2.6...v5.2.7 Adds support for duplicating block and file storage volumes. Only works on Storage as a Service volumes (Volumes that support encryption at rest). #### Added to CLI * [block|file] volume-duplicate ## [5.2.6] - 2017-05-22 - Changes: https://github.com/softlayer/softlayer-python/compare/v5.2.5...v5.2.6 #### Added To CLI * ipsec list * ipsec detail * ipsec configure * ipsec update * ipsec subnet-add * ipsec subnet-remove * ipsec translation-add * ipsec translation-remove * ipsec translation-update ## [5.2.5] - 2017-05-05 - Changes: https://github.com/softlayer/softlayer-python/compare/v5.2.1...v5.2.5 The SoftLayer_Network_Storage::storageTierLevel relational property changed in https://softlayer.github.io/release_notes/20170503/ , this version fixes problems caused by that. ### Changed - https://github.com/softlayer/softlayer-python/issues/818 - https://github.com/softlayer/softlayer-python/pull/817 ## [5.2.4] - 2017-04-06 - Changes: https://github.com/softlayer/softlayer-python/compare/v5.2.3...v5.2.4 ### Changed Removed some debug code that was accidently added in the pypi release ## [5.2.3] - 2017-04-05 - Changes: https://github.com/softlayer/softlayer-python/compare/v5.2.2...v5.2.3 ### Added - Adds Python 3.6 support ### Changed - CLI+API: Removes the iSCSI manager and commands - API: Fixes hardware order failing to find a single bare metal fast provision package to use ## [5.2.2] - 2017-02-24 - Changes: https://github.com/softlayer/softlayer-python/compare/v5.2.1...v5.2.2 ### Added - Adds release process documentation - CLI: Displays NFS mount point for volumes in volume list and detail commands - CLI+API: Enables `slcli file` and `block` storage commands to order tier 10 endurance storage and replica ### Changed - Updates docs to replace `sl` command with `slcli` - CLI: Removes requirement to have `--os-type` provided for file storage ordering - API: Fixes block storage ordering to handle size provided properly - CLI: Fixes load balancer detail output so that JSON output is sane - API: Includes check if object storage endpoints were provided by the API before trying to add them to the endpoints returned by `list_endpoints` ## [5.2.1] - 2016-10-4 - Changes: https://github.com/softlayer/softlayer-python/compare/v5.2.0...v5.2.1 ### Added - CLI: Adds a new 'jsonraw' format option that will print JSON without whitespace. This is useful for integrating the CLI with other tooling. ### Changed - API: Fixes JSON loading while using the REST transport with Python 3 - CLI+API: Metadata disks are now excluded when capturing "all" block devices with `slcli virtual capture --all` - CLI: Fixes a bug where dns zone importing was not importing wildcard records ## [5.2.0] - 2016-08-25 - Changes: https://github.com/softlayer/softlayer-python/compare/v5.1.0...v5.2.0 ### Added - CLI+API: Significant additions to `slcli file` and `slcli block` commands. You can now authorize hosts, revoke access. You can also create, delete, restore, disable, enable snapshots. These features need to be battle-tested so report any issues that you see. - CLI+API: Adds logic to `SoftLayer.create_client_from_env` that detects if a REST endpoint_url was given in order to use the REST transport automatically. This means that you can also configure REST endpoints for `slcli`. The default still uses XML-RPC endpoint, but from a small amount of testing shows that the REST transport is significantly faster. - CLI: Adds `--network-space` to `slcli subnet list` in order to filter subnets based on network space. The two main options are PUBLIC and PRIVATE. For example, to list all public subnets, you can run: `slcli subnet list --network-space=PUBLIC` - CLI: Adds a new, non-default column, "created_by" that shows who ordered the volume for `slcli file volume-list` and `slcli block volume-list`. - CLI: Adds a new `slcli report bandwidth` command that will print a report of all bandwidth pools and virtual/hardware servers that your user has access to. - CLI: Adds an "IN" syntax to the `slcli call-api` command. For example, to find VSIs that are in either the dal05 or sng01 datacenter you can run this command: `slcli call-api Account getVirtualGuests -f 'virtualGuests.datacenter.name IN dal05,sng01'` ### Changed - CLI: Fixes a UnicodeEncodeError when piping slcli output with unicode characters. This was mostly reported with `slcli image list` but could also happen with many other calls. - CLI: Fixed a bug where os_version was not displaying correctly in `slcli virtual detail` or `slcli virtual detail` ## [5.1.0] - 2016-05-12 - Changes: https://github.com/softlayer/softlayer-python/compare/v5.0.1...v5.1.0 ### Added - CLI+API: Added block storage functionality. You can order, list, detail, cancel volumes. You can list and delete snapshots. You can also list ACLs for volumes. - Added functionality to attach/detach devices to tickets - CLI: Virtual list now lists users and passwords for all known software ### Changed - CLI: Fixes bug with `vlan detail` CLI command ## [5.0.1] - 2016-03-30 - https://github.com/softlayer/softlayer-python/compare/v5.0.0...v5.0.1 ### Changed - CLI: Adds missing dependency that was previously pulled in by prompt_toolkit - API: Fix a bug by updating the CDN manager to use the new purge method - CLI: Fixes bug that occured when iscsi listings with resources have no datacenter ## [5.0.0] - 2016-03-18 - Changes: https://github.com/softlayer/softlayer-python/compare/v4.1.1...v5.0.0 ### Added - CLI: Adds a shell (accessable with `slcli shell`) which provides autocomplete for slcli commands and options - CLI: How filters work with `slcli call-api` has changed significantly. Instead of accepting JSON, it now accepts an easier-to-use format. See `slcli call-api -h` for examples - API: Adds manager for object storage - API: Improved REST transport support ### Changed - CLI: Move modifying nic speed to `slcli virtual edit` and `slcli hardware edit` instead of having its own command - CLI: 'virtual' and 'hardware' are preferred over 'vs' and 'server' in the CLI - CLI+API: Many unmentioned bug fixes ## [4.1.1] - 2015-08-17 - Changes: https://github.com/softlayer/softlayer-python/compare/v4.1.0...v4.1.1 ### Added - CLI: Re-adds `--no-public` option to only provision private interfaces with servers via `slcli server create` ### Changed - CLI: Fixes to work with Click v5 - Removes non-functional `--vlan-public` and `--vlan-private` from `slcli server create` - VSManager.wait_for_ready will now behave as it is documented to behave. ## [4.1.0] - 2015-08-17 - Changes: https://github.com/softlayer/softlayer-python/compare/v4.0.4...v4.1.0 ### Added - CLI: Adds a shell which provides a shell interface for `slcli`. This is available by using `slcli shell` - CLI: `slcli vs create` and `slcli server create` will now prompt for missing required options - CLI+API: Adds editing of hardware tags ### Changed - CLI: Fixes `slcli firewall add` command - CLI: Handles case where `slcli vs detail` and `slcli server detail` was causing an error when trying to display the creator - API: Fixes VSManager.verify_create_instance() with tags (and, in turn, `slcli vs create --test` with tags) - CLI: Fixes `vs resume` command - API+CLI: Updates hardware ordering to deal with location-specific prices - CLI: Fixes several description errors in the CLI - CLI: Running `vs edit` without a tag option will no longer remove all tags ## [4.0.4] - 2015-06-30 - Changes: https://github.com/softlayer/softlayer-python/compare/v4.0.3...v4.0.4 ### Changed - CLI: Fixes bug with pulling the userData property for the virtual server detail - CLI: Fixes a class of bugs invloving unicode from the API ## [4.0.3] - 2015-06-15 - Changes: https://github.com/softlayer/softlayer-python/compare/v4.0.2...v4.0.3 ### Changed - CLI: Fixes bug with `slcli vs ready` command - CLI: Fixes bug with `slcli loadbal service-add` command - CLI: Fixes bug with `slcli vlan list` with vlans that don't have a datacenter - CLI: Improves validation of virtual server and hardware create commands ## [4.0.2] - 2015-05-04 - Changes https://github.com/softlayer/softlayer-python/compare/v4.0.1...v4.0.2 ### Changed - CLI: Fixes a bug that breaks user confirmation prompts - CLI: Fixes general issue with sorting on certain row types in the CLI - API: Fixes image capture for Windows guests ## [4.0.1] - 2015-04-28 - Changes: https://github.com/softlayer/softlayer-python/compare/v4.0.0...v4.0.1 ### Changed - CLI: Fixes bug in `sl setup` command not properly defaulting to current values. - API: Fixes bug where turning off compression headers would still send compression headers. - CLI: Reverts to using ids over global identifiers for `sl vs list` and `sl server list`. ## [4.0.0] - 2015-04-21 - Changes: https://github.com/softlayer/softlayer-python/compare/v3.3.0...v4.0.0 - Because there are many changes between version 3 and version 4, it is strongly recommend to pin the version of the SoftLayer python bindings as soon as you can in order to prevent unintentional breakage when upgrading. To keep yourself on version 3, you can use this directive: softlayer>=3,<4. That can be used with pip (pip install softlayer>=3,<4), requirements in your setup.py and/or in your requirements.txt file. ### Added - API: The client transport is now pluggable. If you want to add extra logging or accounting, you can now subclass or wrap softlayer.transports.XmlRpcTransport in order to do so. A good example of that is done with softlayer.transports.TimingTransport. - API+CLI: Adds ability to import virtual images from a given URI. The API only supports importing from a swift account using 'swift://'. For more details, see http://developer.softlayer.com/reference/services/SoftLayer_Virtual_Guest_Block_Device_Template_Group/createFromExternalSource. - CLI: A `--fixtures` global flag was added to pull from fixture data instead of the API. This is useful for discovery, demonstration and testing purposes. - CLI: A `--verbose` or `-v` flag was added to eventually replace `--debug`. To make a command more verbose, simply add more `-v` flags. For example `sl -vvv vs list` will be the most verbose and show everything down to request/response tracing. - CLI: Credentials can now be requested using `sl vs credentials `, `sl hardware credentials ` and `sl nas credentials ` for virtual servers, hardware servers and NAS accounts respectively. - CLI: Adds virtual server rescue command, `sl vs rescue ` ### Changed - CLI: The command is renamed from `sl` to `slcli` to avoid package conflicts. - CLI: Global options now need to be specified right after the `slcli` command. For example, you would now use `slcli --format=raw list` over `slcli vs list --format=raw`. This is a change for the following options: - --format - -c or --config - --debug - --proxy - -y or --really - --version - API: The hardware manager has a significant update to how place_order() works. It will now only support the fast server provisioning package which has presets for options like CPU, Memory and disk. - API: Removed deprecated SoftLayer.CCIManager. - API: Adds virtual server rescue command to SoftLayer.VSManager - CLI: Significant changes were done to the CLI argument parsing. Docopt was dropped in favor of click. Therefore, some subtle differences which aren't documented here may exist. ## [3.3.0] - 2014-10-23 - Changes: https://github.com/softlayer/softlayer-python/compare/v3.2.0...v3.3.0 ### Added - CLI+API: Load balancer support - CLI: More detail added to the `sl image detail` and `sl image list` commands - CLI: Adds command to import DNS entries from BIND zone files - CLI+API: Adds support for booting into rescue images for virtual servers and hardware - API: Adds ability to order virtual and hardare servers from a quote to the ordering manager ### Changed - CLI: Fixes bug with `sl server list-chassis` and `sl server list-chassis` - API: Restructure of the way custom authentication can be plugged in the API client - Several other bug fixes ## [3.2.0] - 2014-07-09 - Changes: https://github.com/softlayer/softlayer-python/compare/v3.1.0...v3.2.0 ### Added - CLI+API: Added firewall manager and CLI module - CLI+API: Added iscsi manager and CLI module - API: Added ability to create multiple virtual servers at once to VSManager - API: Added OrderingManager. Remove hard-coded price IDs ### Changed - Fixed several small bugs ## [3.1.0] - 2014-04-24 - Changes: https://github.com/softlayer/softlayer-python/compare/v3.0.2...v3.1.0 ### Added - CLI+API: Added CDN manager and CLI module - CLI+API: Added ticket manager and CLI module - CLI+API: Added image manager and improves image CLI module - CLI+API: Added the ability to specify a proxy URL for API bindings and the CLI - CLI+API: Added ability to resize a virtual machine - CLI+API: Added firewall manager and CLI module - CLI+API: Added load balancer manager and CLI module ### Changed - API: six is now used to provide support for Python 2 and Python 3 with the same source - CLI+API: Implemented product name changes in accordance with SoftLayer's new product names. Existing managers should continue to work as before. Minor CLI changes were necessary. - Many bug fixes and minor suggested improvements ## [3.0.2] - 2013-12-9 - Changes: https://github.com/softlayer/softlayer-python/compare/v3.0.1...v3.0.2 ### Added - CLI+API: Simplified object mask reformatting and added support for more complex masks. - CLI+API: Added IPMI IP address to hardware details. - CLI: Added support for ordering multiple disks when creating a CCI. - API: Added flag to disable compression on HTTP requests. - CLI: Added CIDR information to subnet displays. ### Changed - CLI: Fixed the sl bmc create --network argument. - CLI+API: Improved output of the message queue feature and fixed some minor bugs. - CLI: Fixed an error when using --test and ordering a non-private subnet. - API: Fix to prevent double counting results in summary_by_datacenter(). ### [3.0.1] - 2013-10-11 - Changes: https://github.com/softlayer/softlayer-python/compare/v3.0.0...v3.0.1 ### Added - CLI+API: Added ability to specify SSH keys when reloading CCIs and servers. ### Changed - CLI: Fixed an error message about pricing information that appeared when ordering a new private subnet. ## [3.0.0] - 2013-09-19 - Changes: https://github.com/softlayer/softlayer-python/compare/v2.3.0...v3.0.0 ### Added - CLI+API: Adds SoftLayer Message Queue Service bindings (as a manager) and a CLI counterpart. With this you can interact with existing message queue accounts - CLI+API: Adds the ability to create CCIs with the following options: metadata, post-install script, SSH key - CLI+API: Improved dedicated server ordering. Adds power management for hardware servers: power-on, power-off, power-cycle, reboot - CLI+API: Adds a networking manager and adds several network-related CLI modules. This includes the ability to: - list, create, cancel and assign global IPs - list, create, cancel and detail subnets. Also has the ability to lookup details about an IP address with 'sl subnet lookup' - list, detail VLANs - show and edit RWhois data - CLI+API: Ability to manage SSH Keys with a manager and a CLI module - CLI: Adds a --debug option to print out debugging information. --debug=3 is the highest log level which prints full HTTP request/responses including the body - CLI+API: Adds the ability to create hardware servers with a default SSH key - CLI: Adds templating for creating CCIs and hardware nodes which can be used to create more CCIs and hardware with the same settings ### Changed - Many bug fixes and consistency improvements - API: Removes old API client interfaces which have been deprecated in the v2. See link for more details: https://softlayer-api-python-client.readthedocs.org/en/latest/api/client/#backwards-compatibility - CLI: The commands in the main help are now organized into categories ## [2.3.0] - 2013-07-19 - Changes: https://github.com/softlayer/softlayer-python/compare/v2.2.0...v2.3.0 ### Added - CLI+API: Added much more hardware support: Filters for hardware listing, dedicated server/bare metal cloud ordering, hardware cancellation - CLI+API: Added DNS Zone filtering (server side) - CLI+API: Added Post Install script support for CCIs and hardware - CLI: Added Message queue functionality - CLI: Added --debug option to CLI commands - API: Added more logging - API: Added token-based auth so you can use the API bindings with your username/password if you want. (It's still highly recommended to use your API key instead of your password) ### Changed - Several bug fixes and improvements - Removed Python 2.5 support. Some stuff MIGHT work with 2.5 but it is no longer tested - API: Refactored managers into their own module to not clutter the top level ## [2.2.0] - 2013-04-11 ### Added - Added sphinx documentation. See it here: https://softlayer-api-python-client.readthedocs.org - CCI: Adds Support for Additional Disks - CCI: Adds a way to block until transactions are done on a CCI - CLI: For most CCI commands, you can specify id, hostname, private ip or public ip as - CLI: Adds the ability to filter list results for CCIs - API: for large result sets, requests can now be chunked into smaller batches on the server side. Using service.iter_call('getObjects', ...) or service.getObjects(..., iter=True) will return a generator regardless of the results returned. offset and limit can be passed in like normal. An additional named parameter of 'chunk' is used to limit the number of items coming back in a single request, defaults to 100 ### Changed - Consistency changes/bug fixes softlayer-python-5.4.2/CONTRIBUTING.md000066400000000000000000000011621324365065500173130ustar00rootroot00000000000000# Contributing to softlayer-python We are happy to accept contributions to softlayer-python. Please follow the guidelines below. * Sign our contributor agreement (CLA) You can find the [CLA here](./docs/dev/cla-individual.md). * If you're contributing on behalf of your employer we'll need a signed copy of our corporate contributor agreement (CCLA) as well. You can find the [CCLA here](./docs/dev/cla-corporate.md). * Fork the repo, make your changes, and open a pull request. * Additional infomration can be found in our [contribution guide](http://softlayer-python.readthedocs.org/en/latest/dev/index.html) softlayer-python-5.4.2/CONTRIBUTORS000066400000000000000000000023511324365065500167430ustar00rootroot00000000000000Amol Jadhav Aparna Patil Boden Russell Brian Cline chechuironman Christopher Gallo David Ibarra Hans Kristian Moen Jake Williams Jason Johnson Kevin Landreth Kevin McDonald Łukasz Oleś Michael Fork Nathan Beittenmiller Neetu Jain Paul Sroufe Phil Jackson Robert Chumbley Ryan Hanson Scott Thompson Sergio Carlos Shane Poage Shravan Kumar Raghu simplydave SoftLayer suppandi Swapnil Khanapurkar The SoftLayer Developer Network Tim Ariyeh Wissam Elriachy Anthony Monthe (ZuluPro) softlayer-python-5.4.2/ISSUE_TEMPLATE000066400000000000000000000004451324365065500171730ustar00rootroot00000000000000**Please triple-check to make sure that you have properly masked out user credentials like usernames, passwords and API keys before submitting your issue** ### Expected Behavior ### Actual Behavior ### Environment Information Operating System: softlayer-python version (`slcli --version`): softlayer-python-5.4.2/LICENSE000066400000000000000000000021051324365065500160650ustar00rootroot00000000000000Copyright (c) 2016 SoftLayer Technologies, Inc. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. softlayer-python-5.4.2/MANIFEST.in000066400000000000000000000000431324365065500166150ustar00rootroot00000000000000include LICENSE include README.rst softlayer-python-5.4.2/README.rst000066400000000000000000000061421324365065500165540ustar00rootroot00000000000000SoftLayer API Python Client =========================== .. image:: https://travis-ci.org/softlayer/softlayer-python.svg?branch=master :target: https://travis-ci.org/softlayer/softlayer-python .. image:: https://landscape.io/github/softlayer/softlayer-python/master/landscape.svg :target: https://landscape.io/github/softlayer/softlayer-python/master .. image:: https://badge.fury.io/py/SoftLayer.svg :target: http://badge.fury.io/py/SoftLayer .. image:: https://coveralls.io/repos/github/softlayer/softlayer-python/badge.svg?branch=master :target: https://coveralls.io/github/softlayer/softlayer-python?branch=master This library provides a simple Python client to interact with `SoftLayer's XML-RPC API `_. A command-line interface is also included and can be used to manage various SoftLayer products and services. Documentation ------------- Documentation for the Python client is available at http://softlayer.github.io/softlayer-python/. Additional API documentation can be found on the SoftLayer Development Network: * `SoftLayer API reference `_ * `Object mask information and examples `_ * `Code Examples `_ Installation ------------ Install via pip: .. code-block:: bash $ pip install softlayer Or you can install from source. Download source and run: .. code-block:: bash $ python setup.py install The most up-to-date version of this library can be found on the SoftLayer GitHub public repositories at http://github.com/softlayer. For questions regarding the use of this library please post to Stack Overflow at https://stackoverflow.com/ and your posts with “SoftLayer” so our team can easily find your post. To report a bug with this library please create an Issue on github. InsecurePlatformWarning Notice ------------------------------ This library relies on the `requests `_ library to make HTTP requests. On Python versions below Python 2.7.9, requests has started emitting a security warning (InsecurePlatformWarning) due to insecurities with creating SSL connections. To resolve this, upgrade to Python 2.7.9+ or follow the instructions here: http://stackoverflow.com/a/29099439. Getting Help ------------ Bugs and feature requests about this library should have a `GitHub issue `_ opened about them. Issues with the Softlayer API itself should be addressed by opening a ticket. System Requirements ------------------- * Python 2.7, 3.3, 3.4, 3.5 or 3.6. * A valid SoftLayer API username and key. * A connection to SoftLayer's private network is required to use our private network API endpoints. Python Packages --------------- * six >= 1.7.0 * prettytable >= 0.7.0 * click >= 5 * requests >= 2.18.4 * prompt_toolkit >= 0.53 * pygments >= 2.0.0 * urllib3 >= 1.22 Copyright --------- This software is Copyright (c) 2016-2018 SoftLayer Technologies, Inc. See the bundled LICENSE file for more information. softlayer-python-5.4.2/RELEASE.md000066400000000000000000000013121324365065500164610ustar00rootroot00000000000000# Release steps * Update version constants (find them by running `git grep [VERSION_NUMBER]`) * Create changelog entry (edit CHANGELOG.md with a one-liner for each closed issue going in the release) * Commit and push changes to master with the message: "Version Bump to v[VERSION_NUMBER]" * Push tag and PyPi `fab release:[VERSION_NUMBER]`. Before you do this, make sure you have the organization repository set up as upstream remote & fabric installed (`pip install fabric`), also make sure that you have pip set up with your PyPi user credentials. The easiest way to do that is to create a file at `~/.pypirc` with the following contents: ``` [server-login] username:YOUR_USERNAME password:YOUR_PASSWORD ``` softlayer-python-5.4.2/SoftLayer/000077500000000000000000000000001324365065500167725ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/API.py000066400000000000000000000330151324365065500177570ustar00rootroot00000000000000""" SoftLayer.API ~~~~~~~~~~~~~ SoftLayer API bindings :license: MIT, see LICENSE for more details. """ # pylint: disable=invalid-name import warnings from SoftLayer import auth as slauth from SoftLayer import config from SoftLayer import consts from SoftLayer import transports API_PUBLIC_ENDPOINT = consts.API_PUBLIC_ENDPOINT API_PRIVATE_ENDPOINT = consts.API_PRIVATE_ENDPOINT __all__ = [ 'create_client_from_env', 'Client', 'BaseClient', 'API_PUBLIC_ENDPOINT', 'API_PRIVATE_ENDPOINT', ] VALID_CALL_ARGS = set(( 'id', 'mask', 'filter', 'headers', 'compress', 'raw_headers', 'limit', 'offset', 'verify', )) def create_client_from_env(username=None, api_key=None, endpoint_url=None, timeout=None, auth=None, config_file=None, proxy=None, user_agent=None, transport=None, verify=True): """Creates a SoftLayer API client using your environment. Settings are loaded via keyword arguments, environemtal variables and config file. :param username: an optional API username if you wish to bypass the package's built-in username :param api_key: an optional API key if you wish to bypass the package's built in API key :param endpoint_url: the API endpoint base URL you wish to connect to. Set this to API_PRIVATE_ENDPOINT to connect via SoftLayer's private network. :param proxy: proxy to be used to make API calls :param integer timeout: timeout for API requests :param auth: an object which responds to get_headers() to be inserted into the xml-rpc headers. Example: `BasicAuthentication` :param config_file: A path to a configuration file used to load settings :param user_agent: an optional User Agent to report when making API calls if you wish to bypass the packages built in User Agent string :param transport: An object that's callable with this signature: transport(SoftLayer.transports.Request) :param bool verify: decide to verify the server's SSL/TLS cert. DO NOT SET TO FALSE WITHOUT UNDERSTANDING THE IMPLICATIONS. Usage: >>> import SoftLayer >>> client = SoftLayer.create_client_from_env() >>> resp = client.call('Account', 'getObject') >>> resp['companyName'] 'Your Company' """ settings = config.get_client_settings(username=username, api_key=api_key, endpoint_url=endpoint_url, timeout=timeout, proxy=proxy, verify=verify, config_file=config_file) if transport is None: url = settings.get('endpoint_url') if url is not None and '/rest' in url: # If this looks like a rest endpoint, use the rest transport transport = transports.RestTransport( endpoint_url=settings.get('endpoint_url'), proxy=settings.get('proxy'), timeout=settings.get('timeout'), user_agent=user_agent, verify=verify, ) else: # Default the transport to use XMLRPC transport = transports.XmlRpcTransport( endpoint_url=settings.get('endpoint_url'), proxy=settings.get('proxy'), timeout=settings.get('timeout'), user_agent=user_agent, verify=verify, ) # If we have enough information to make an auth driver, let's do it if auth is None and settings.get('username') and settings.get('api_key'): # NOTE(kmcdonald): some transports mask other transports, so this is # a way to find the 'real' one real_transport = getattr(transport, 'transport', transport) if isinstance(real_transport, transports.XmlRpcTransport): auth = slauth.BasicAuthentication( settings.get('username'), settings.get('api_key'), ) elif isinstance(real_transport, transports.RestTransport): auth = slauth.BasicHTTPAuthentication( settings.get('username'), settings.get('api_key'), ) return BaseClient(auth=auth, transport=transport) def Client(**kwargs): """Get a SoftLayer API Client using environmental settings. Deprecated in favor of create_client_from_env() """ warnings.warn("use SoftLayer.create_client_from_env() instead", DeprecationWarning) return create_client_from_env(**kwargs) class BaseClient(object): """Base SoftLayer API client. :param auth: auth driver that looks like SoftLayer.auth.AuthenticationBase :param transport: An object that's callable with this signature: transport(SoftLayer.transports.Request) """ _prefix = "SoftLayer_" def __init__(self, auth=None, transport=None): self.auth = auth self.transport = transport def authenticate_with_password(self, username, password, security_question_id=None, security_question_answer=None): """Performs Username/Password Authentication :param string username: your SoftLayer username :param string password: your SoftLayer password :param int security_question_id: The security question id to answer :param string security_question_answer: The answer to the security question """ self.auth = None res = self.call('User_Customer', 'getPortalLoginToken', username, password, security_question_id, security_question_answer) self.auth = slauth.TokenAuthentication(res['userId'], res['hash']) return res['userId'], res['hash'] def __getitem__(self, name): """Get a SoftLayer Service. :param name: The name of the service. E.G. Account Usage: >>> import SoftLayer >>> client = SoftLayer.create_client_from_env() >>> client['Account'] """ return Service(self, name) def call(self, service, method, *args, **kwargs): """Make a SoftLayer API call. :param method: the method to call on the service :param \\*args: (optional) arguments for the remote call :param id: (optional) id for the resource :param mask: (optional) object mask :param dict filter: (optional) filter dict :param dict headers: (optional) optional XML-RPC headers :param boolean compress: (optional) Enable/Disable HTTP compression :param dict raw_headers: (optional) HTTP transport headers :param int limit: (optional) return at most this many results :param int offset: (optional) offset results by this many :param boolean iter: (optional) if True, returns a generator with the results :param bool verify: verify SSL cert :param cert: client certificate path Usage: >>> import SoftLayer >>> client = SoftLayer.create_client_from_env() >>> client.call('Account', 'getVirtualGuests', mask="id", limit=10) [...] """ if kwargs.pop('iter', False): return self.iter_call(service, method, *args, **kwargs) invalid_kwargs = set(kwargs.keys()) - VALID_CALL_ARGS if invalid_kwargs: raise TypeError( 'Invalid keyword arguments: %s' % ','.join(invalid_kwargs)) if self._prefix and not service.startswith(self._prefix): service = self._prefix + service http_headers = {'Accept': '*/*'} if kwargs.get('compress', True): http_headers['Accept-Encoding'] = 'gzip, deflate, compress' else: http_headers['Accept-Encoding'] = None if kwargs.get('raw_headers'): http_headers.update(kwargs.get('raw_headers')) request = transports.Request() request.service = service request.method = method request.args = args request.transport_headers = http_headers request.identifier = kwargs.get('id') request.mask = kwargs.get('mask') request.filter = kwargs.get('filter') request.limit = kwargs.get('limit') request.offset = kwargs.get('offset') if kwargs.get('verify') is not None: request.verify = kwargs.get('verify') if self.auth: extra_headers = self.auth.get_headers() if extra_headers: warnings.warn("auth.get_headers() is deprecated and will be " "removed in the next major version", DeprecationWarning) request.headers.update(extra_headers) request = self.auth.get_request(request) request.headers.update(kwargs.get('headers', {})) return self.transport(request) __call__ = call def iter_call(self, service, method, *args, **kwargs): """A generator that deals with paginating through results. :param service: the name of the SoftLayer API service :param method: the method to call on the service :param integer chunk: result size for each API call (defaults to 100) :param \\*args: same optional arguments that ``Service.call`` takes :param \\*\\*kwargs: same optional keyword arguments that ``Service.call`` takes """ chunk = kwargs.pop('chunk', 100) limit = kwargs.pop('limit', None) offset = kwargs.pop('offset', 0) if chunk <= 0: raise AttributeError("Chunk size should be greater than zero.") if limit: chunk = min(chunk, limit) result_count = 0 kwargs['iter'] = False while True: if limit: # We've reached the end of the results if result_count >= limit: break # Don't over-fetch past the given limit if chunk + result_count > limit: chunk = limit - result_count results = self.call(service, method, offset=offset, limit=chunk, *args, **kwargs) # It looks like we ran out results if not results: break # Apparently this method doesn't return a list. # Why are you even iterating over this? if not isinstance(results, list): yield results break for item in results: yield item result_count += 1 offset += chunk if len(results) < chunk: break def __repr__(self): return "Client(transport=%r, auth=%r)" % (self.transport, self.auth) __str__ = __repr__ def __len__(self): return 0 class Service(object): """A SoftLayer Service. :param client: A SoftLayer.API.Client instance :param name str: The service name """ def __init__(self, client, name): self.client = client self.name = name def call(self, name, *args, **kwargs): """Make a SoftLayer API call :param service: the name of the SoftLayer API service :param method: the method to call on the service :param \\*args: same optional arguments that ``BaseClient.call`` takes :param \\*\\*kwargs: same optional keyword arguments that ``BaseClient.call`` takes :param service: the name of the SoftLayer API service Usage: >>> import SoftLayer >>> client = SoftLayer.create_client_from_env() >>> client['Account'].getVirtualGuests(mask="id", limit=10) [...] """ return self.client.call(self.name, name, *args, **kwargs) __call__ = call def iter_call(self, name, *args, **kwargs): """A generator that deals with paginating through results. :param method: the method to call on the service :param integer chunk: result size for each API call :param \\*args: same optional arguments that ``Service.call`` takes :param \\*\\*kwargs: same optional keyword arguments that ``Service.call`` takes Usage: >>> import SoftLayer >>> client = SoftLayer.create_client_from_env() >>> gen = client.call('Account', 'getVirtualGuests', iter=True) >>> for virtual_guest in gen: ... virtual_guest['id'] ... 1234 4321 """ return self.client.iter_call(self.name, name, *args, **kwargs) def __getattr__(self, name): if name in ["__name__", "__bases__"]: raise AttributeError("'Obj' object has no attribute '%s'" % name) def call_handler(*args, **kwargs): " Handler that actually makes the API call " return self(name, *args, **kwargs) return call_handler def __repr__(self): return "" % (self.name,) __str__ = __repr__ softlayer-python-5.4.2/SoftLayer/CLI/000077500000000000000000000000001324365065500174015ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/CLI/__init__.py000066400000000000000000000005331324365065500215130ustar00rootroot00000000000000""" SoftLayer.CLI ~~~~~~~~~~~~~~ Contains all code related to the CLI interface :license: MIT, see LICENSE for more details. """ # pylint: disable=w0401, invalid-name import logging from SoftLayer.CLI.helpers import * # NOQA logger = logging.getLogger() logger.addHandler(logging.StreamHandler()) logger.setLevel(logging.INFO) softlayer-python-5.4.2/SoftLayer/CLI/block/000077500000000000000000000000001324365065500204735ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/CLI/block/__init__.py000066400000000000000000000000251324365065500226010ustar00rootroot00000000000000"""Block Storage.""" softlayer-python-5.4.2/SoftLayer/CLI/block/access/000077500000000000000000000000001324365065500217345ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/CLI/block/access/__init__.py000066400000000000000000000000441324365065500240430ustar00rootroot00000000000000"""Block Storage Access Control.""" softlayer-python-5.4.2/SoftLayer/CLI/block/access/authorize.py000066400000000000000000000036621324365065500243270ustar00rootroot00000000000000"""Authorizes hosts on a specific block volume.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions @click.command() @click.argument('volume_id') @click.option('--hardware-id', '-h', multiple=True, help='The id of one SoftLayer_Hardware to authorize') @click.option('--virtual-id', '-v', multiple=True, help='The id of one SoftLayer_Virtual_Guest to authorize') @click.option('--ip-address-id', '-i', multiple=True, help='The id of one SoftLayer_Network_Subnet_IpAddress' ' to authorize') @click.option('--ip-address', multiple=True, help='An IP address to authorize') @environment.pass_env def cli(env, volume_id, hardware_id, virtual_id, ip_address_id, ip_address): """Authorizes hosts to access a given volume""" block_manager = SoftLayer.BlockStorageManager(env.client) ip_address_id_list = list(ip_address_id) # Convert actual IP Addresses to their SoftLayer ids if ip_address is not None: network_manager = SoftLayer.NetworkManager(env.client) for ip_address_value in ip_address: ip_address_object = network_manager.ip_lookup(ip_address_value) if ip_address_object == "": click.echo("IP Address not found on your account. " + "Please confirm IP and try again.") raise exceptions.ArgumentError('Incorrect IP Address') else: ip_address_id_list.append(ip_address_object['id']) block_manager.authorize_host_to_volume(volume_id, hardware_id, virtual_id, ip_address_id_list) # If no exception was raised, the command succeeded click.echo('The specified hosts were authorized to access %s' % volume_id) softlayer-python-5.4.2/SoftLayer/CLI/block/access/list.py000066400000000000000000000027061324365065500232660ustar00rootroot00000000000000"""List hosts with access to block volume.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import columns as column_helper from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.CLI import storage_utils @click.command() @click.argument('volume_id') @click.option('--sortby', help='Column to sort by', default='name') @click.option('--columns', callback=column_helper.get_formatter(storage_utils.COLUMNS), help='Columns to display. Options: {0}'.format( ', '.join(column.name for column in storage_utils.COLUMNS)), default=','.join(storage_utils.DEFAULT_COLUMNS)) @environment.pass_env def cli(env, columns, sortby, volume_id): """List ACLs.""" block_manager = SoftLayer.BlockStorageManager(env.client) access_list = block_manager.get_block_volume_access_list( volume_id=volume_id) table = formatting.Table(columns.columns) table.sortby = sortby for key, type_name in [('allowedVirtualGuests', 'VIRTUAL'), ('allowedHardware', 'HARDWARE'), ('allowedSubnets', 'SUBNET'), ('allowedIpAddresses', 'IP')]: for obj in access_list.get(key, []): obj['type'] = type_name table.add_row([value or formatting.blank() for value in columns.row(obj)]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/block/access/password.py000066400000000000000000000015211324365065500241470ustar00rootroot00000000000000"""Modifies a password for a volume's access""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment @click.command() @click.argument('access_id') @click.option('--password', '-p', multiple=False, help='Password you want to set, this command will fail if the password is not strong') @environment.pass_env def cli(env, access_id, password): """Changes a password for a volume's access. access id is the allowed_host_id from slcli block access-list """ block_manager = SoftLayer.BlockStorageManager(env.client) result = block_manager.set_credential_password(access_id=access_id, password=password) if result: click.echo('Password updated for %s' % access_id) else: click.echo('FAILED updating password for %s' % access_id) softlayer-python-5.4.2/SoftLayer/CLI/block/access/revoke.py000066400000000000000000000033541324365065500236060ustar00rootroot00000000000000"""Revokes hosts' access on a specific block volume.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment @click.command() @click.argument('volume_id') @click.option('--hardware-id', '-h', multiple=True, help='The id of one SoftLayer_Hardware' ' to revoke authorization') @click.option('--virtual-id', '-v', multiple=True, help='The id of one SoftLayer_Virtual_Guest' ' to revoke authorization') @click.option('--ip-address-id', '-i', multiple=True, help='The id of one SoftLayer_Network_Subnet_IpAddress' ' to revoke authorization') @click.option('--ip-address', multiple=True, help='An IP address to revoke authorization') @environment.pass_env def cli(env, volume_id, hardware_id, virtual_id, ip_address_id, ip_address): """Revokes authorization for hosts accessing a given volume""" block_manager = SoftLayer.BlockStorageManager(env.client) ip_address_id_list = list(ip_address_id) # Convert actual IP Addresses to their SoftLayer ids if ip_address is not None: network_manager = SoftLayer.NetworkManager(env.client) for ip_address_value in ip_address: ip_address_object = network_manager.ip_lookup(ip_address_value) ip_address_id_list.append(ip_address_object['id']) block_manager.deauthorize_host_to_volume(volume_id, hardware_id, virtual_id, ip_address_id_list) # If no exception was raised, the command succeeded click.echo('Access to %s was revoked for the specified hosts' % volume_id) softlayer-python-5.4.2/SoftLayer/CLI/block/cancel.py000066400000000000000000000025361324365065500223000ustar00rootroot00000000000000"""Cancel an existing iSCSI account.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting @click.command() @click.argument('volume-id') @click.option('--reason', help="An optional reason for cancellation") @click.option('--immediate', is_flag=True, help="Cancels the block storage volume immediately instead " "of on the billing anniversary") @environment.pass_env def cli(env, volume_id, reason, immediate): """Cancel an existing block storage volume.""" block_storage_manager = SoftLayer.BlockStorageManager(env.client) if not (env.skip_confirmations or formatting.no_going_back(volume_id)): raise exceptions.CLIAbort('Aborted') cancelled = block_storage_manager.cancel_block_volume(volume_id, reason, immediate) if cancelled: if immediate: click.echo('Block volume with id %s has been marked' ' for immediate cancellation' % volume_id) else: click.echo('Block volume with id %s has been marked' ' for cancellation' % volume_id) else: click.echo('Unable to cancel block volume %s' % volume_id) softlayer-python-5.4.2/SoftLayer/CLI/block/count.py000066400000000000000000000030171324365065500221760ustar00rootroot00000000000000"""List number of block storage volumes per datacenter.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting DEFAULT_COLUMNS = [ 'Datacenter', 'Count' ] @click.command() @click.option('--datacenter', '-d', help='Datacenter shortname') @click.option('--sortby', help='Column to sort by', default='Datacenter') @environment.pass_env def cli(env, sortby, datacenter): """List number of block storage volumes per datacenter.""" block_manager = SoftLayer.BlockStorageManager(env.client) mask = "mask[serviceResource[datacenter[name]],"\ "replicationPartners[serviceResource[datacenter[name]]]]" block_volumes = block_manager.list_block_volumes(datacenter=datacenter, mask=mask) # cycle through all block volumes and count datacenter occurences. datacenters = dict() for volume in block_volumes: service_resource = volume['serviceResource'] if 'datacenter' in service_resource: datacenter_name = service_resource['datacenter']['name'] if datacenter_name not in datacenters.keys(): datacenters[datacenter_name] = 1 else: datacenters[datacenter_name] += 1 table = formatting.KeyValueTable(DEFAULT_COLUMNS) table.sortby = sortby for datacenter_name in datacenters: table.add_row([datacenter_name, datacenters[datacenter_name]]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/block/detail.py000066400000000000000000000107531324365065500223150ustar00rootroot00000000000000"""Display details for a specified volume.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer import utils @click.command() @click.argument('volume_id') @environment.pass_env def cli(env, volume_id): """Display details for a specified volume.""" block_manager = SoftLayer.BlockStorageManager(env.client) block_volume = block_manager.get_block_volume_details(volume_id) block_volume = utils.NestedDict(block_volume) table = formatting.KeyValueTable(['Name', 'Value']) table.align['Name'] = 'r' table.align['Value'] = 'l' storage_type = block_volume['storageType']['keyName'].split('_').pop(0) table.add_row(['ID', block_volume['id']]) table.add_row(['Username', block_volume['username']]) table.add_row(['Type', storage_type]) table.add_row(['Capacity (GB)', "%iGB" % block_volume['capacityGb']]) table.add_row(['LUN Id', "%s" % block_volume['lunId']]) if block_volume.get('provisionedIops'): table.add_row(['IOPs', int(block_volume['provisionedIops'])]) if block_volume.get('storageTierLevel'): table.add_row([ 'Endurance Tier', block_volume['storageTierLevel'], ]) table.add_row([ 'Data Center', block_volume['serviceResource']['datacenter']['name'], ]) table.add_row([ 'Target IP', block_volume['serviceResourceBackendIpAddress'], ]) if block_volume['snapshotCapacityGb']: table.add_row([ 'Snapshot Capacity (GB)', block_volume['snapshotCapacityGb'], ]) if 'snapshotSizeBytes' in block_volume['parentVolume']: table.add_row([ 'Snapshot Used (Bytes)', block_volume['parentVolume']['snapshotSizeBytes'], ]) table.add_row(['# of Active Transactions', "%i" % block_volume['activeTransactionCount']]) if block_volume['activeTransactions']: for trans in block_volume['activeTransactions']: if 'transactionStatus' in trans and 'friendlyName' in trans['transactionStatus']: table.add_row(['Ongoing Transaction', trans['transactionStatus']['friendlyName']]) table.add_row(['Replicant Count', "%u" % block_volume.get('replicationPartnerCount', 0)]) if block_volume['replicationPartnerCount'] > 0: # This if/else temporarily handles a bug in which the SL API # returns a string or object for 'replicationStatus'; it seems that # the type is string for File volumes and object for Block volumes if 'message' in block_volume['replicationStatus']: table.add_row(['Replication Status', "%s" % block_volume['replicationStatus']['message']]) else: table.add_row(['Replication Status', "%s" % block_volume['replicationStatus']]) replicant_list = [] for replicant in block_volume['replicationPartners']: replicant_table = formatting.Table(['Replicant ID', replicant['id']]) replicant_table.add_row([ 'Volume Name', utils.lookup(replicant, 'username')]) replicant_table.add_row([ 'Target IP', utils.lookup(replicant, 'serviceResourceBackendIpAddress')]) replicant_table.add_row([ 'Data Center', utils.lookup(replicant, 'serviceResource', 'datacenter', 'name')]) replicant_table.add_row([ 'Schedule', utils.lookup(replicant, 'replicationSchedule', 'type', 'keyname')]) replicant_list.append(replicant_table) table.add_row(['Replicant Volumes', replicant_list]) if block_volume.get('originalVolumeSize'): original_volume_info = formatting.Table(['Property', 'Value']) original_volume_info.add_row(['Original Volume Size', block_volume['originalVolumeSize']]) if block_volume.get('originalVolumeName'): original_volume_info.add_row(['Original Volume Name', block_volume['originalVolumeName']]) if block_volume.get('originalSnapshotName'): original_volume_info.add_row(['Original Snapshot Name', block_volume['originalSnapshotName']]) table.add_row(['Original Volume Properties', original_volume_info]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/block/duplicate.py000066400000000000000000000101371324365065500230210ustar00rootroot00000000000000"""Order a duplicate block storage volume.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions CONTEXT_SETTINGS = {'token_normalize_func': lambda x: x.upper()} @click.command(context_settings=CONTEXT_SETTINGS) @click.argument('origin-volume-id') @click.option('--origin-snapshot-id', '-o', type=int, help="ID of an origin volume snapshot to use for duplcation.") @click.option('--duplicate-size', '-c', type=int, help='Size of duplicate block volume in GB. ' '***If no size is specified, the size of ' 'the origin volume will be used.***\n' 'Potential Sizes: [20, 40, 80, 100, 250, ' '500, 1000, 2000, 4000, 8000, 12000] ' 'Minimum: [the size of the origin volume]') @click.option('--duplicate-iops', '-i', type=int, help='Performance Storage IOPS, between 100 and 6000 in ' 'multiples of 100 [only used for performance volumes] ' '***If no IOPS value is specified, the IOPS value of the ' 'origin volume will be used.***\n' 'Requirements: [If IOPS/GB for the origin volume is less ' 'than 0.3, IOPS/GB for the duplicate must also be less ' 'than 0.3. If IOPS/GB for the origin volume is greater ' 'than or equal to 0.3, IOPS/GB for the duplicate must ' 'also be greater than or equal to 0.3.]') @click.option('--duplicate-tier', '-t', help='Endurance Storage Tier (IOPS per GB) [only used for ' 'endurance volumes] ***If no tier is specified, the tier ' 'of the origin volume will be used.***\n' 'Requirements: [If IOPS/GB for the origin volume is 0.25, ' 'IOPS/GB for the duplicate must also be 0.25. If IOPS/GB ' 'for the origin volume is greater than 0.25, IOPS/GB ' 'for the duplicate must also be greater than 0.25.]', type=click.Choice(['0.25', '2', '4', '10'])) @click.option('--duplicate-snapshot-size', '-s', type=int, help='The size of snapshot space to order for the duplicate. ' '***If no snapshot space size is specified, the snapshot ' 'space size of the origin block volume will be used.***\n' 'Input "0" for this parameter to order a duplicate volume ' 'with no snapshot space.') @click.option('--billing', type=click.Choice(['hourly', 'monthly']), default='monthly', help="Optional parameter for Billing rate (default to monthly)") @environment.pass_env def cli(env, origin_volume_id, origin_snapshot_id, duplicate_size, duplicate_iops, duplicate_tier, duplicate_snapshot_size, billing): """Order a duplicate block storage volume.""" block_manager = SoftLayer.BlockStorageManager(env.client) hourly_billing_flag = False if billing.lower() == "hourly": hourly_billing_flag = True if duplicate_tier is not None: duplicate_tier = float(duplicate_tier) try: order = block_manager.order_duplicate_volume( origin_volume_id, origin_snapshot_id=origin_snapshot_id, duplicate_size=duplicate_size, duplicate_iops=duplicate_iops, duplicate_tier_level=duplicate_tier, duplicate_snapshot_size=duplicate_snapshot_size, hourly_billing_flag=hourly_billing_flag ) except ValueError as ex: raise exceptions.ArgumentError(str(ex)) if 'placedOrder' in order.keys(): click.echo("Order #{0} placed successfully!".format( order['placedOrder']['id'])) for item in order['placedOrder']['items']: click.echo(" > %s" % item['description']) else: click.echo("Order could not be placed! Please verify your options " + "and try again.") softlayer-python-5.4.2/SoftLayer/CLI/block/list.py000066400000000000000000000060761324365065500220310ustar00rootroot00000000000000"""List block storage volumes.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import columns as column_helper from SoftLayer.CLI import environment from SoftLayer.CLI import formatting COLUMNS = [ column_helper.Column('id', ('id',), mask="id"), column_helper.Column('username', ('username',), mask="username"), column_helper.Column('datacenter', ('serviceResource', 'datacenter', 'name'), mask="serviceResource.datacenter.name"), column_helper.Column( 'storage_type', lambda b: b['storageType']['keyName'].split('_').pop(0) if 'storageType' in b and 'keyName' in b['storageType'] and isinstance(b['storageType']['keyName'], str) else '-', mask="storageType.keyName"), column_helper.Column('capacity_gb', ('capacityGb',), mask="capacityGb"), column_helper.Column('bytes_used', ('bytesUsed',), mask="bytesUsed"), column_helper.Column('ip_addr', ('serviceResourceBackendIpAddress',), mask="serviceResourceBackendIpAddress"), column_helper.Column('lunId', ('lunId',), mask="lunId"), column_helper.Column('active_transactions', ('activeTransactionCount',), mask="activeTransactionCount"), column_helper.Column('rep_partner_count', ('replicationPartnerCount',), mask="replicationPartnerCount"), column_helper.Column( 'created_by', ('billingItem', 'orderItem', 'order', 'userRecord', 'username')), ] DEFAULT_COLUMNS = [ 'id', 'username', 'datacenter', 'storage_type', 'capacity_gb', 'bytes_used', 'ip_addr', 'lunId', 'active_transactions', 'rep_partner_count' ] @click.command() @click.option('--username', '-u', help='Volume username') @click.option('--datacenter', '-d', help='Datacenter shortname') @click.option('--storage-type', help='Type of storage volume', type=click.Choice(['performance', 'endurance'])) @click.option('--sortby', help='Column to sort by', default='username') @click.option('--columns', callback=column_helper.get_formatter(COLUMNS), help='Columns to display. Options: {0}'.format( ', '.join(column.name for column in COLUMNS)), default=','.join(DEFAULT_COLUMNS)) @environment.pass_env def cli(env, sortby, columns, datacenter, username, storage_type): """List block storage.""" block_manager = SoftLayer.BlockStorageManager(env.client) block_volumes = block_manager.list_block_volumes(datacenter=datacenter, username=username, storage_type=storage_type, mask=columns.mask()) table = formatting.Table(columns.columns) table.sortby = sortby for block_volume in block_volumes: table.add_row([value or formatting.blank() for value in columns.row(block_volume)]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/block/lun.py000066400000000000000000000022011324365065500216360ustar00rootroot00000000000000"""Set the LUN ID on an iSCSI volume.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment @click.command() @click.argument('volume-id') @click.argument('lun-id') @environment.pass_env def cli(env, volume_id, lun_id): """Set the LUN ID on an existing block storage volume. The LUN ID only takes effect during the Host Authorization process. It is recommended (but not necessary) to de-authorize all hosts before using this method. See `block access-revoke`. VOLUME_ID - the volume ID on which to set the LUN ID. LUN_ID - recommended range is an integer between 0 and 255. Advanced users can use an integer between 0 and 4095. """ block_storage_manager = SoftLayer.BlockStorageManager(env.client) res = block_storage_manager.create_or_update_lun_id(volume_id, lun_id) if 'value' in res and lun_id == res['value']: click.echo( 'Block volume with id %s is reporting LUN ID %s' % (res['volumeId'], res['value'])) else: click.echo( 'Failed to confirm the new LUN ID on volume %s' % (volume_id)) softlayer-python-5.4.2/SoftLayer/CLI/block/modify.py000066400000000000000000000052421324365065500223370ustar00rootroot00000000000000"""Modify an existing block storage volume.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions CONTEXT_SETTINGS = {'token_normalize_func': lambda x: x.upper()} @click.command(context_settings=CONTEXT_SETTINGS) @click.argument('volume-id') @click.option('--new-size', '-c', type=int, help='New Size of block volume in GB. ***If no size is given, the original size of volume is used.***\n' 'Potential Sizes: [20, 40, 80, 100, 250, 500, 1000, 2000, 4000, 8000, 12000]\n' 'Minimum: [the original size of the volume]') @click.option('--new-iops', '-i', type=int, help='Performance Storage IOPS, between 100 and 6000 in multiples of 100 [only for performance volumes] ' '***If no IOPS value is specified, the original IOPS value of the volume will be used.***\n' 'Requirements: [If original IOPS/GB for the volume is less than 0.3, new IOPS/GB must also be ' 'less than 0.3. If original IOPS/GB for the volume is greater than or equal to 0.3, new IOPS/GB ' 'for the volume must also be greater than or equal to 0.3.]') @click.option('--new-tier', '-t', help='Endurance Storage Tier (IOPS per GB) [only for endurance volumes] ' '***If no tier is specified, the original tier of the volume will be used.***\n' 'Requirements: [If original IOPS/GB for the volume is 0.25, new IOPS/GB for the volume must also ' 'be 0.25. If original IOPS/GB for the volume is greater than 0.25, new IOPS/GB for the volume ' 'must also be greater than 0.25.]', type=click.Choice(['0.25', '2', '4', '10'])) @environment.pass_env def cli(env, volume_id, new_size, new_iops, new_tier): """Modify an existing block storage volume.""" block_manager = SoftLayer.BlockStorageManager(env.client) if new_tier is not None: new_tier = float(new_tier) try: order = block_manager.order_modified_volume( volume_id, new_size=new_size, new_iops=new_iops, new_tier_level=new_tier, ) except ValueError as ex: raise exceptions.ArgumentError(str(ex)) if 'placedOrder' in order.keys(): click.echo("Order #{0} placed successfully!".format(order['placedOrder']['id'])) for item in order['placedOrder']['items']: click.echo(" > %s" % item['description']) else: click.echo("Order could not be placed! Please verify your options and try again.") softlayer-python-5.4.2/SoftLayer/CLI/block/order.py000066400000000000000000000120521324365065500221600ustar00rootroot00000000000000"""Order a block storage volume.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions CONTEXT_SETTINGS = {'token_normalize_func': lambda x: x.upper()} @click.command(context_settings=CONTEXT_SETTINGS) @click.option('--storage-type', help='Type of block storage volume', type=click.Choice(['performance', 'endurance']), required=True) @click.option('--size', type=int, help='Size of block storage volume in GB. Permitted Sizes:\n' '20, 40, 80, 100, 250, 500, 1000, 2000, 4000, 8000, 12000', required=True) @click.option('--iops', type=int, help='Performance Storage IOPs,' ' between 100 and 6000 in multiples of 100' ' [required for storage-type performance]') @click.option('--tier', help='Endurance Storage Tier (IOP per GB)' ' [required for storage-type endurance]', type=click.Choice(['0.25', '2', '4', '10'])) @click.option('--os-type', help='Operating System', type=click.Choice([ 'HYPER_V', 'LINUX', 'VMWARE', 'WINDOWS_2008', 'WINDOWS_GPT', 'WINDOWS', 'XEN']), required=True) @click.option('--location', help='Datacenter short name (e.g.: dal09)', required=True) @click.option('--snapshot-size', type=int, help='Optional parameter for ordering snapshot ' 'space along with endurance block storage; specifies ' 'the size (in GB) of snapshot space to order') @click.option('--service-offering', help='The service offering package to use for placing ' 'the order [optional, default is \'storage_as_a_service\']', default='storage_as_a_service', type=click.Choice([ 'storage_as_a_service', 'enterprise', 'performance'])) @click.option('--billing', type=click.Choice(['hourly', 'monthly']), default='monthly', help="Optional parameter for Billing rate (default to monthly)") @environment.pass_env def cli(env, storage_type, size, iops, tier, os_type, location, snapshot_size, service_offering, billing): """Order a block storage volume.""" block_manager = SoftLayer.BlockStorageManager(env.client) storage_type = storage_type.lower() hourly_billing_flag = False if billing.lower() == "hourly": hourly_billing_flag = True if hourly_billing_flag and service_offering != 'storage_as_a_service': raise exceptions.CLIAbort( 'Hourly billing is only available for the storage_as_a_service ' 'service offering' ) if storage_type == 'performance': if iops is None: raise exceptions.CLIAbort( 'Option --iops required with Performance') if iops % 100 != 0: raise exceptions.CLIAbort( 'Option --iops must be a multiple of 100' ) if service_offering == 'performance' and snapshot_size is not None: raise exceptions.CLIAbort( '--snapshot-size is not available for performance volumes ' 'ordered with the \'performance\' service offering option' ) try: order = block_manager.order_block_volume( storage_type=storage_type, location=location, size=int(size), iops=iops, os_type=os_type, snapshot_size=snapshot_size, service_offering=service_offering, hourly_billing_flag=hourly_billing_flag ) except ValueError as ex: raise exceptions.ArgumentError(str(ex)) if storage_type == 'endurance': if tier is None: raise exceptions.CLIAbort( 'Option --tier required with Endurance in IOPS/GB ' '[0.25,2,4,10]' ) try: order = block_manager.order_block_volume( storage_type=storage_type, location=location, size=int(size), tier_level=float(tier), os_type=os_type, snapshot_size=snapshot_size, service_offering=service_offering, hourly_billing_flag=hourly_billing_flag ) except ValueError as ex: raise exceptions.ArgumentError(str(ex)) if 'placedOrder' in order.keys(): click.echo("Order #{0} placed successfully!".format( order['placedOrder']['id'])) for item in order['placedOrder']['items']: click.echo(" > %s" % item['description']) else: click.echo("Order could not be placed! Please verify your options " + "and try again.") softlayer-python-5.4.2/SoftLayer/CLI/block/replication/000077500000000000000000000000001324365065500230045ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/CLI/block/replication/__init__.py000066400000000000000000000000511324365065500251110ustar00rootroot00000000000000"""Block Storage Replication Control.""" softlayer-python-5.4.2/SoftLayer/CLI/block/replication/failback.py000066400000000000000000000013371324365065500251160ustar00rootroot00000000000000"""Failback from a replicant volume.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment @click.command() @click.argument('volume-id') @click.option('--replicant-id', help="ID of the replicant volume") @environment.pass_env def cli(env, volume_id, replicant_id): """Failback a block volume from the given replicant volume.""" block_storage_manager = SoftLayer.BlockStorageManager(env.client) success = block_storage_manager.failback_from_replicant( volume_id, replicant_id ) if success: click.echo("Failback from replicant is now in progress.") else: click.echo("Failback operation could not be initiated.") softlayer-python-5.4.2/SoftLayer/CLI/block/replication/failover.py000066400000000000000000000016041324365065500251660ustar00rootroot00000000000000"""Failover to a replicant volume.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment @click.command() @click.argument('volume-id') @click.option('--replicant-id', help="ID of the replicant volume") @click.option('--immediate', is_flag=True, default=False, help="Failover to replicant immediately.") @environment.pass_env def cli(env, volume_id, replicant_id, immediate): """Failover a block volume to the given replicant volume.""" block_storage_manager = SoftLayer.BlockStorageManager(env.client) success = block_storage_manager.failover_to_replicant( volume_id, replicant_id, immediate ) if success: click.echo("Failover to replicant is now in progress.") else: click.echo("Failover operation could not be initiated.") softlayer-python-5.4.2/SoftLayer/CLI/block/replication/locations.py000066400000000000000000000031051324365065500253500ustar00rootroot00000000000000"""List suitable replication datacenters for the given volume.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import columns as column_helper from SoftLayer.CLI import environment from SoftLayer.CLI import formatting COLUMNS = [ column_helper.Column('ID', ('id',), mask="id"), column_helper.Column('Long Name', ('longName',), mask="longName"), column_helper.Column('Short Name', ('name',), mask="name"), ] DEFAULT_COLUMNS = [ 'ID', 'Long Name', 'Short Name', ] @click.command() @click.argument('volume-id') @click.option('--sortby', help='Column to sort by', default='Long Name') @click.option('--columns', callback=column_helper.get_formatter(COLUMNS), help='Columns to display. Options: {0}'.format( ', '.join(column.name for column in COLUMNS)), default=','.join(DEFAULT_COLUMNS)) @environment.pass_env def cli(env, columns, sortby, volume_id): """List suitable replication datacenters for the given volume.""" block_storage_manager = SoftLayer.BlockStorageManager(env.client) legal_centers = block_storage_manager.get_replication_locations( volume_id ) if not legal_centers: click.echo("No data centers compatible for replication.") else: table = formatting.KeyValueTable(columns.columns) table.sortby = sortby for legal_center in legal_centers: table.add_row([value or formatting.blank() for value in columns.row(legal_center)]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/block/replication/order.py000066400000000000000000000044421324365065500244750ustar00rootroot00000000000000"""Order a block storage replica volume.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions CONTEXT_SETTINGS = {'token_normalize_func': lambda x: x.upper()} @click.command(context_settings=CONTEXT_SETTINGS) @click.argument('volume_id') @click.option('--snapshot-schedule', '-s', help='Snapshot schedule to use for replication, ' '(INTERVAL | HOURLY | DAILY | WEEKLY)', required=True, type=click.Choice(['INTERVAL', 'HOURLY', 'DAILY', 'WEEKLY'])) @click.option('--location', '-l', help='Short name of the data center for the replicant ' '(e.g.: dal09)', required=True) @click.option('--tier', help='Endurance Storage Tier (IOPS per GB) of the primary' ' volume for which a replicant is ordered [optional]', type=click.Choice(['0.25', '2', '4', '10'])) @click.option('--os-type', help='Operating System Type (e.g.: LINUX) of the primary' ' volume for which a replica is ordered [optional]', type=click.Choice([ 'HYPER_V', 'LINUX', 'VMWARE', 'WINDOWS_2008', 'WINDOWS_GPT', 'WINDOWS', 'XEN'])) @environment.pass_env def cli(env, volume_id, snapshot_schedule, location, tier, os_type): """Order a block storage replica volume.""" block_manager = SoftLayer.BlockStorageManager(env.client) if tier is not None: tier = float(tier) try: order = block_manager.order_replicant_volume( volume_id, snapshot_schedule=snapshot_schedule, location=location, tier=tier, os_type=os_type, ) except ValueError as ex: raise exceptions.ArgumentError(str(ex)) if 'placedOrder' in order.keys(): click.echo("Order #{0} placed successfully!".format( order['placedOrder']['id'])) for item in order['placedOrder']['items']: click.echo(" > %s" % item['description']) else: click.echo("Order could not be placed! Please verify your options " + "and try again.") softlayer-python-5.4.2/SoftLayer/CLI/block/replication/partners.py000066400000000000000000000036331324365065500252210ustar00rootroot00000000000000"""List existing replicant volumes for a block volume.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import columns as column_helper from SoftLayer.CLI import environment from SoftLayer.CLI import formatting COLUMNS = [ column_helper.Column('ID', ('id',)), column_helper.Column('Username', ('username',), mask="username"), column_helper.Column('Account ID', ('accountId',), mask="accountId"), column_helper.Column('Capacity (GB)', ('capacityGb',), mask="capacityGb"), column_helper.Column('Hardware ID', ('hardwareId',), mask="hardwareId"), column_helper.Column('Guest ID', ('guestId',), mask="guestId"), column_helper.Column('Host ID', ('hostId',), mask="hostId"), ] DEFAULT_COLUMNS = [ 'ID', 'Username', 'Account ID', 'Capacity (GB)', 'Hardware ID', 'Guest ID', 'Host ID' ] @click.command() @click.argument('volume-id') @click.option('--sortby', help='Column to sort by', default='Username') @click.option('--columns', callback=column_helper.get_formatter(COLUMNS), help='Columns to display. Options: {0}'.format( ', '.join(column.name for column in COLUMNS)), default=','.join(DEFAULT_COLUMNS)) @environment.pass_env def cli(env, columns, sortby, volume_id): """List existing replicant volumes for a block volume.""" block_storage_manager = SoftLayer.BlockStorageManager(env.client) legal_volumes = block_storage_manager.get_replication_partners( volume_id ) if not legal_volumes: click.echo("There are no replication partners for the given volume.") else: table = formatting.Table(columns.columns) table.sortby = sortby for legal_volume in legal_volumes: table.add_row([value or formatting.blank() for value in columns.row(legal_volume)]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/block/snapshot/000077500000000000000000000000001324365065500223325ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/CLI/block/snapshot/__init__.py000066400000000000000000000000461324365065500244430ustar00rootroot00000000000000"""Block Storage Snapshot Control.""" softlayer-python-5.4.2/SoftLayer/CLI/block/snapshot/cancel.py000066400000000000000000000025571324365065500241420ustar00rootroot00000000000000"""Cancel a snapshot space subscription.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting @click.command() @click.argument('volume-id') @click.option('--reason', help="An optional reason for cancellation") @click.option('--immediate', is_flag=True, help="Cancels the snapshot space immediately instead " "of on the billing anniversary") @environment.pass_env def cli(env, volume_id, reason, immediate): """Cancel existing snapshot space for a given volume.""" block_storage_manager = SoftLayer.BlockStorageManager(env.client) if not (env.skip_confirmations or formatting.no_going_back(volume_id)): raise exceptions.CLIAbort('Aborted') cancelled = block_storage_manager.cancel_snapshot_space( volume_id, reason, immediate) if cancelled: if immediate: click.echo('Block volume with id %s has been marked' ' for immediate snapshot cancellation' % volume_id) else: click.echo('Block volume with id %s has been marked' ' for snapshot cancellation' % volume_id) else: click.echo('Unable to cancel snapshot space for block volume %s' % volume_id) softlayer-python-5.4.2/SoftLayer/CLI/block/snapshot/create.py000066400000000000000000000014661324365065500241560ustar00rootroot00000000000000"""Create a block storage snapshot.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment @click.command() @click.argument('volume_id') @click.option('--notes', '-n', help='Notes to set on the new snapshot') @environment.pass_env def cli(env, volume_id, notes): """Creates a snapshot on a given volume""" block_manager = SoftLayer.BlockStorageManager(env.client) snapshot = block_manager.create_snapshot(volume_id, notes=notes) if 'id' in snapshot: click.echo('New snapshot created with id: %s' % snapshot['id']) else: click.echo('Error occurred while creating snapshot.\n' 'Ensure volume is not failed over or in another ' 'state which prevents taking snapshots.') softlayer-python-5.4.2/SoftLayer/CLI/block/snapshot/delete.py000066400000000000000000000007551324365065500241550ustar00rootroot00000000000000"""Delete a block storage snapshot.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment @click.command() @click.argument('snapshot_id') @environment.pass_env def cli(env, snapshot_id): """Deletes a snapshot on a given volume""" block_manager = SoftLayer.BlockStorageManager(env.client) deleted = block_manager.delete_snapshot(snapshot_id) if deleted: click.echo('Snapshot %s deleted' % snapshot_id) softlayer-python-5.4.2/SoftLayer/CLI/block/snapshot/disable.py000066400000000000000000000017221324365065500243110ustar00rootroot00000000000000"""Disable scheduled snapshots of a specific volume""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions @click.command() @click.argument('volume_id') @click.option('--schedule-type', help='Snapshot schedule [INTERVAL|HOURLY|DAILY|WEEKLY]', required=True) @environment.pass_env def cli(env, volume_id, schedule_type): """Disables snapshots on the specified schedule for a given volume""" if (schedule_type not in ['INTERVAL', 'HOURLY', 'DAILY', 'WEEKLY']): raise exceptions.CLIAbort( '--schedule-type must be INTERVAL, HOURLY, DAILY, or WEEKLY') block_manager = SoftLayer.BlockStorageManager(env.client) disabled = block_manager.disable_snapshots(volume_id, schedule_type) if disabled: click.echo('%s snapshots have been disabled for volume %s' % (schedule_type, volume_id)) softlayer-python-5.4.2/SoftLayer/CLI/block/snapshot/enable.py000066400000000000000000000044651324365065500241430ustar00rootroot00000000000000# snapshot_enable.py """Create a block storage snapshot [ENABLE].""" import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions @click.command() @click.argument('volume_id') @click.option('--schedule-type', help='Snapshot schedule [INTERVAL|HOURLY|DAILY|WEEKLY]', required=True) @click.option('--retention-count', help='Number of snapshots to retain', required=True) @click.option('--minute', help='Minute of the day when snapshots should be taken', default=0) @click.option('--hour', help='Hour of the day when snapshots should be taken', default=0) @click.option('--day-of-week', help='Day of the week when snapshots should be taken', default='SUNDAY') @environment.pass_env def cli(env, volume_id, schedule_type, retention_count, minute, hour, day_of_week): """Enables snapshots for a given volume on the specified schedule""" block_manager = SoftLayer.BlockStorageManager(env.client) valid_schedule_types = {'INTERVAL', 'HOURLY', 'DAILY', 'WEEKLY'} valid_days = {'SUNDAY', 'MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY', 'SATURDAY'} if schedule_type not in valid_schedule_types: raise exceptions.CLIAbort( '--schedule-type must be INTERVAL, HOURLY, DAILY,' + 'or WEEKLY, not ' + schedule_type) if schedule_type == 'INTERVAL' and (minute < 30 or minute > 59): raise exceptions.CLIAbort( '--minute value must be between 30 and 59') if minute < 0 or minute > 59: raise exceptions.CLIAbort( '--minute value must be between 0 and 59') if hour < 0 or hour > 23: raise exceptions.CLIAbort( '--hour value must be between 0 and 23') if day_of_week not in valid_days: raise exceptions.CLIAbort( '--day_of_week value must be a valid day (ex: SUNDAY)') enabled = block_manager.enable_snapshots(volume_id, schedule_type, retention_count, minute, hour, day_of_week) if enabled: click.echo('%s snapshots have been enabled for volume %s' % (schedule_type, volume_id)) softlayer-python-5.4.2/SoftLayer/CLI/block/snapshot/list.py000066400000000000000000000031031324365065500236540ustar00rootroot00000000000000"""List block storage snapshots.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import columns as column_helper from SoftLayer.CLI import environment from SoftLayer.CLI import formatting COLUMNS = [ column_helper.Column('id', ('id',), mask='id'), column_helper.Column('name', ('notes',), mask='notes'), column_helper.Column('created', ('snapshotCreationTimestamp',), mask='snapshotCreationTimestamp'), column_helper.Column('size_bytes', ('snapshotSizeBytes',), mask='snapshotSizeBytes'), ] DEFAULT_COLUMNS = [ 'id', 'name', 'created', 'size_bytes' ] @click.command() @click.argument('volume_id') @click.option('--sortby', help='Column to sort by', default='created') @click.option('--columns', callback=column_helper.get_formatter(COLUMNS), help='Columns to display. Options: {0}'.format( ', '.join(column.name for column in COLUMNS)), default=','.join(DEFAULT_COLUMNS)) @environment.pass_env def cli(env, volume_id, sortby, columns): """List block storage snapshots.""" block_manager = SoftLayer.BlockStorageManager(env.client) snapshots = block_manager.get_block_volume_snapshot_list( volume_id, mask=columns.mask() ) table = formatting.Table(columns.columns) table.sortby = sortby for snapshot in snapshots: table.add_row([value or formatting.blank() for value in columns.row(snapshot)]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/block/snapshot/order.py000066400000000000000000000035011324365065500240160ustar00rootroot00000000000000"""Order snapshot space for a block storage volume.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions @click.command() @click.argument('volume_id') @click.option('--capacity', type=int, help='Size of snapshot space to create in GB', required=True) @click.option('--tier', help='Endurance Storage Tier (IOPS per GB) of the block' ' volume for which space is ordered [optional, and only' ' valid for endurance storage volumes]', type=click.Choice(['0.25', '2', '4', '10'])) @click.option('--upgrade', type=bool, help='Flag to indicate that the order is an upgrade', default=False, is_flag=True) @environment.pass_env def cli(env, volume_id, capacity, tier, upgrade): """Order snapshot space for a block storage volume.""" block_manager = SoftLayer.BlockStorageManager(env.client) if tier is not None: tier = float(tier) try: order = block_manager.order_snapshot_space( volume_id, capacity=capacity, tier=tier, upgrade=upgrade ) except ValueError as ex: raise exceptions.ArgumentError(str(ex)) if 'placedOrder' in order.keys(): click.echo("Order #{0} placed successfully!".format( order['placedOrder']['id'])) for item in order['placedOrder']['items']: click.echo(" > %s" % item['description']) if 'status' in order['placedOrder'].keys(): click.echo(" > Order status: %s" % order['placedOrder']['status']) else: click.echo("Order could not be placed! Please verify your options " + "and try again.") softlayer-python-5.4.2/SoftLayer/CLI/block/snapshot/restore.py000066400000000000000000000013471324365065500243740ustar00rootroot00000000000000"""Restore a block volume from a snapshot.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment @click.command() @click.argument('volume_id') @click.option('--snapshot-id', '-s', help='The id of the snapshot which will be used' ' to restore the block volume') @environment.pass_env def cli(env, volume_id, snapshot_id): """Restore block volume using a given snapshot""" block_manager = SoftLayer.BlockStorageManager(env.client) success = block_manager.restore_from_snapshot(volume_id, snapshot_id) if success: click.echo('Block volume %s is being restored using snapshot %s' % (volume_id, snapshot_id)) softlayer-python-5.4.2/SoftLayer/CLI/block/snapshot/schedule_list.py000066400000000000000000000045041324365065500255360ustar00rootroot00000000000000"""List scheduled snapshots of a specific volume""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting @click.command() @click.argument('volume_id') @environment.pass_env def cli(env, volume_id): """Lists snapshot schedules for a given volume""" block_manager = SoftLayer.BlockStorageManager(env.client) snapshot_schedules = block_manager.list_volume_schedules(volume_id) table = formatting.Table(['id', 'active', 'type', 'replication', 'date_created', 'minute', 'hour', 'day', 'week', 'day_of_week', 'date_of_month', 'month_of_year', 'maximum_snapshots']) for schedule in snapshot_schedules: if 'REPLICATION' in schedule['type']['keyname']: replication = '*' else: replication = formatting.blank() block_schedule_type = schedule['type']['keyname'].replace('REPLICATION_', '') block_schedule_type = block_schedule_type.replace('SNAPSHOT_', '') property_list = ['MINUTE', 'HOUR', 'DAY', 'WEEK', 'DAY_OF_WEEK', 'DAY_OF_MONTH', 'MONTH_OF_YEAR', 'SNAPSHOT_LIMIT'] schedule_properties = [] for prop_key in property_list: item = formatting.blank() for schedule_property in schedule.get('properties', []): if schedule_property['type']['keyname'] == prop_key: if schedule_property['value'] == '-1': item = '*' else: item = schedule_property['value'] break schedule_properties.append(item) table_row = [ schedule['id'], '*' if schedule.get('active', '') else '', block_schedule_type, replication, schedule.get('createDate', '')] table_row.extend(schedule_properties) table.add_row(table_row) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/call_api.py000066400000000000000000000077321324365065500215300ustar00rootroot00000000000000"""Call arbitrary API endpoints.""" import click from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting from SoftLayer.CLI import helpers from SoftLayer import utils SPLIT_TOKENS = [ ('in', ' IN '), ('eq', '='), ] def _build_filters(_filters): """Builds filters using the filter options passed into the CLI. This only supports the equals keyword at the moment. """ root = utils.NestedDict({}) for _filter in _filters: operation = None for operation, token in SPLIT_TOKENS: # split "some.key=value" into ["some.key", "value"] top_parts = _filter.split(token, 1) if len(top_parts) == 2: break else: raise exceptions.CLIAbort('Failed to find valid operation for: %s' % _filter) key, value = top_parts current = root # split "some.key" into ["some", "key"] parts = [part.strip() for part in key.split('.')] # Actually drill down and add the filter for part in parts[:-1]: current = current[part] if operation == 'eq': current[parts[-1]] = utils.query_filter(value.strip()) elif operation == 'in': current[parts[-1]] = { 'operation': 'in', 'options': [{ 'name': 'data', 'value': [p.strip() for p in value.split(',')], }], } return root.to_dict() def _build_python_example(args, kwargs): sorted_kwargs = sorted(kwargs.items()) call_str = 'import SoftLayer\n\n' call_str += 'client = SoftLayer.create_client_from_env()\n' call_str += 'result = client.call(' arg_list = [repr(arg) for arg in args] arg_list += [key + '=' + repr(value) for key, value in sorted_kwargs if value] call_str += ',\n '.join(arg_list) call_str += ')' return call_str @click.command('call', short_help="Call arbitrary API endpoints.") @click.argument('service') @click.argument('method') @click.argument('parameters', nargs=-1) @click.option('--id', '_id', help="Init parameter") @helpers.multi_option('--filter', '-f', '_filters', help="Object filters. This should be of the form: " "'property=value' or 'nested.property=value'. Complex " "filters like betweenDate are not currently supported.") @click.option('--mask', help="String-based object mask") @click.option('--limit', type=click.INT, help="Result limit") @click.option('--offset', type=click.INT, help="Result offset") @click.option('--output-python / --no-output-python', help="Show python example code instead of executing the call") @environment.pass_env def cli(env, service, method, parameters, _id, _filters, mask, limit, offset, output_python=False): """Call arbitrary API endpoints with the given SERVICE and METHOD. \b Examples: slcli call-api Account getObject slcli call-api Account getVirtualGuests --limit=10 --mask=id,hostname slcli call-api Virtual_Guest getObject --id=12345 slcli call-api Metric_Tracking_Object getBandwidthData --id=1234 \\ "2015-01-01 00:00:00" "2015-01-1 12:00:00" public slcli call-api Account getVirtualGuests \\ -f 'virtualGuests.datacenter.name=dal05' \\ -f 'virtualGuests.maxCpu=4' \\ --mask=id,hostname,datacenter.name,maxCpu slcli call-api Account getVirtualGuests \\ -f 'virtualGuests.datacenter.name IN dal05,sng01' """ args = [service, method] + list(parameters) kwargs = { 'id': _id, 'filter': _build_filters(_filters), 'mask': mask, 'limit': limit, 'offset': offset, } if output_python: env.out(_build_python_example(args, kwargs)) else: result = env.client.call(*args, **kwargs) env.fout(formatting.iter_to_table(result)) softlayer-python-5.4.2/SoftLayer/CLI/cdn/000077500000000000000000000000001324365065500201455ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/CLI/cdn/__init__.py000066400000000000000000000001171324365065500222550ustar00rootroot00000000000000"""Content Delivery Network.""" # :license: MIT, see LICENSE for more details. softlayer-python-5.4.2/SoftLayer/CLI/cdn/detail.py000066400000000000000000000016231324365065500217630ustar00rootroot00000000000000"""Detail a CDN Account.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting @click.command() @click.argument('account_id') @environment.pass_env def cli(env, account_id): """Detail a CDN Account.""" manager = SoftLayer.CDNManager(env.client) account = manager.get_account(account_id) table = formatting.KeyValueTable(['name', 'value']) table.align['name'] = 'r' table.align['value'] = 'l' table.add_row(['id', account['id']]) table.add_row(['account_name', account['cdnAccountName']]) table.add_row(['type', account['cdnSolutionName']]) table.add_row(['status', account['status']['name']]) table.add_row(['created', account['createDate']]) table.add_row(['notes', account.get('cdnAccountNote', formatting.blank())]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/cdn/list.py000066400000000000000000000023441324365065500214750ustar00rootroot00000000000000"""List CDN Accounts.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting @click.command() @click.option('--sortby', help='Column to sort by', type=click.Choice(['id', 'datacenter', 'host', 'cores', 'memory', 'primary_ip', 'backend_ip'])) @environment.pass_env def cli(env, sortby): """List all CDN accounts.""" manager = SoftLayer.CDNManager(env.client) accounts = manager.list_accounts() table = formatting.Table(['id', 'account_name', 'type', 'created', 'notes']) for account in accounts: table.add_row([ account['id'], account['cdnAccountName'], account['cdnSolutionName'], account['createDate'], account.get('cdnAccountNote', formatting.blank()) ]) table.sortby = sortby env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/cdn/load.py000066400000000000000000000007241324365065500214410ustar00rootroot00000000000000"""Cache one or more files on all edge nodes.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment @click.command() @click.argument('account_id') @click.argument('content_url', nargs=-1) @environment.pass_env def cli(env, account_id, content_url): """Cache one or more files on all edge nodes.""" manager = SoftLayer.CDNManager(env.client) manager.load_content(account_id, content_url) softlayer-python-5.4.2/SoftLayer/CLI/cdn/origin_add.py000066400000000000000000000013551324365065500226220ustar00rootroot00000000000000"""Create an origin pull mapping.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment # pylint: disable=redefined-builtin @click.command() @click.argument('account_id') @click.argument('content_url') @click.option('--type', help='The media type for this mapping (http, flash, wm, ...)', default='http', show_default=True) @click.option('--cname', help='An optional CNAME to attach to the mapping') @environment.pass_env def cli(env, account_id, content_url, type, cname): """Create an origin pull mapping.""" manager = SoftLayer.CDNManager(env.client) manager.add_origin(account_id, type, content_url, cname) softlayer-python-5.4.2/SoftLayer/CLI/cdn/origin_list.py000066400000000000000000000013311324365065500230370ustar00rootroot00000000000000"""List origin pull mappings.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting @click.command() @click.argument('account_id') @environment.pass_env def cli(env, account_id): """List origin pull mappings.""" manager = SoftLayer.CDNManager(env.client) origins = manager.get_origins(account_id) table = formatting.Table(['id', 'media_type', 'cname', 'origin_url']) for origin in origins: table.add_row([origin['id'], origin['mediaType'], origin.get('cname', formatting.blank()), origin['originUrl']]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/cdn/origin_remove.py000066400000000000000000000006551324365065500233710ustar00rootroot00000000000000"""Remove an origin pull mapping.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment @click.command() @click.argument('account_id') @click.argument('origin_id') @environment.pass_env def cli(env, account_id, origin_id): """Remove an origin pull mapping.""" manager = SoftLayer.CDNManager(env.client) manager.remove_origin(account_id, origin_id) softlayer-python-5.4.2/SoftLayer/CLI/cdn/purge.py000066400000000000000000000007171324365065500216460ustar00rootroot00000000000000"""Purge cached files from all edge nodes.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment @click.command() @click.argument('account_id') @click.argument('content_url', nargs=-1) @environment.pass_env def cli(env, account_id, content_url): """Purge cached files from all edge nodes.""" manager = SoftLayer.CDNManager(env.client) manager.purge_content(account_id, content_url) softlayer-python-5.4.2/SoftLayer/CLI/columns.py000066400000000000000000000043761324365065500214450ustar00rootroot00000000000000""" SoftLayer.CLI.columns ~~~~~~~~~~~~~~~~~~~~~~ Utilties for user-defined columns :license: MIT, see LICENSE for more details. """ import click from SoftLayer import utils # pylint: disable=unused-argument class Column(object): """Column desctribes an attribute and how to fetch/display it.""" def __init__(self, name, path, mask=None): self.name = name self.path = path self.mask = mask # If the mask is not set explicitly, infer it from the path if self.mask is None and isinstance(path, (tuple, list)): self.mask = '.'.join(path) class ColumnFormatter(object): """Maps each column using a function""" def __init__(self): self.columns = [] self.column_funcs = [] self.mask_parts = set() def add_column(self, column): """Add a new column along with a formatting function.""" self.columns.append(column.name) self.column_funcs.append(column.path) if column.mask is not None: self.mask_parts.add(column.mask) def row(self, data): """Return a formatted row for the given data.""" for column in self.column_funcs: if callable(column): yield column(data) else: yield utils.lookup(data, *column) def mask(self): """Returns a SoftLayer mask to fetch data needed for each column.""" return ','.join(self.mask_parts) def get_formatter(columns): """This function returns a callback to use with click options. The retuend function parses a comma-separated value and returns a new ColumnFormatter. :param columns: a list of Column instances """ column_map = dict((column.name, column) for column in columns) def validate(ctx, param, value): """Click validation function.""" if value == '': raise click.BadParameter('At least one column is required.') formatter = ColumnFormatter() for column in [col.strip() for col in value.split(',')]: if column in column_map: formatter.add_column(column_map[column]) else: formatter.add_column(Column(column, column.split('.'))) return formatter return validate softlayer-python-5.4.2/SoftLayer/CLI/config/000077500000000000000000000000001324365065500206465ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/CLI/config/__init__.py000066400000000000000000000027761324365065500227730ustar00rootroot00000000000000"""CLI configuration.""" # :license: MIT, see LICENSE for more details. from SoftLayer.CLI import formatting def _resolve_transport(transport): """recursively look for transports which refer to other transports.""" nested_transport = getattr(transport, 'transport', None) if nested_transport is not None: return nested_transport return _resolve_transport(nested_transport) def get_settings_from_client(client): """Pull out settings from a SoftLayer.BaseClient instance. :param client: SoftLayer.BaseClient instance """ settings = { 'username': '', 'api_key': '', 'timeout': '', 'endpoint_url': '', } try: settings['username'] = client.auth.username settings['api_key'] = client.auth.api_key except AttributeError: pass transport = _resolve_transport(client.transport) try: settings['timeout'] = transport.timeout settings['endpoint_url'] = transport.endpoint_url except AttributeError: pass return settings def config_table(settings): """Returns a config table.""" table = formatting.KeyValueTable(['name', 'value']) table.align['name'] = 'r' table.align['value'] = 'l' table.add_row(['Username', settings['username'] or 'not set']) table.add_row(['API Key', settings['api_key'] or 'not set']) table.add_row(['Endpoint URL', settings['endpoint_url'] or 'not set']) table.add_row(['Timeout', settings['timeout'] or 'not set']) return table softlayer-python-5.4.2/SoftLayer/CLI/config/setup.py000066400000000000000000000100221324365065500223530ustar00rootroot00000000000000"""Setup CLI configuration.""" # :license: MIT, see LICENSE for more details. import os.path import click import SoftLayer from SoftLayer import auth from SoftLayer.CLI import config from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting from SoftLayer import utils def get_api_key(client, username, secret): """Attempts API-Key and password auth to get an API key. This will also generate an API key if one doesn't exist """ # Try to use a client with username/api key if len(secret) == 64: try: client.auth = auth.BasicAuthentication(username, secret) client['Account'].getCurrentUser() return secret except SoftLayer.SoftLayerAPIError as ex: if 'invalid api token' not in ex.faultString.lower(): raise else: # Try to use a client with username/password client.authenticate_with_password(username, secret) user_record = client['Account'].getCurrentUser( mask='id, apiAuthenticationKeys') api_keys = user_record['apiAuthenticationKeys'] if len(api_keys) == 0: return client['User_Customer'].addApiAuthenticationKey( id=user_record['id']) return api_keys[0]['authenticationKey'] @click.command() @environment.pass_env def cli(env): """Edit configuration.""" username, secret, endpoint_url, timeout = get_user_input(env) env.client.transport.transport.endpoint_url = endpoint_url api_key = get_api_key(env.client, username, secret) path = '~/.softlayer' if env.config_file: path = env.config_file config_path = os.path.expanduser(path) env.out(env.fmt(config.config_table({'username': username, 'api_key': api_key, 'endpoint_url': endpoint_url, 'timeout': timeout}))) if not formatting.confirm('Are you sure you want to write settings ' 'to "%s"?' % config_path, default=True): raise exceptions.CLIAbort('Aborted.') # Persist the config file. Read the target config file in before # setting the values to avoid clobbering settings parsed_config = utils.configparser.RawConfigParser() parsed_config.read(config_path) try: parsed_config.add_section('softlayer') except utils.configparser.DuplicateSectionError: pass parsed_config.set('softlayer', 'username', username) parsed_config.set('softlayer', 'api_key', api_key) parsed_config.set('softlayer', 'endpoint_url', endpoint_url) parsed_config.set('softlayer', 'timeout', timeout) config_fd = os.fdopen(os.open(config_path, (os.O_WRONLY | os.O_CREAT | os.O_TRUNC), 0o600), 'w') try: parsed_config.write(config_fd) finally: config_fd.close() env.fout("Configuration Updated Successfully") def get_user_input(env): """Ask for username, secret (api_key or password) and endpoint_url.""" defaults = config.get_settings_from_client(env.client) # Ask for username username = env.input('Username', default=defaults['username']) # Ask for 'secret' which can be api_key or their password secret = env.getpass('API Key or Password', default=defaults['api_key']) # Ask for which endpoint they want to use endpoint_type = env.input( 'Endpoint (public|private|custom)', default='public') endpoint_type = endpoint_type.lower() if endpoint_type == 'custom': endpoint_url = env.input('Endpoint URL', default=defaults['endpoint_url']) elif endpoint_type == 'private': endpoint_url = SoftLayer.API_PRIVATE_ENDPOINT else: endpoint_url = SoftLayer.API_PUBLIC_ENDPOINT # Ask for timeout timeout = env.input('Timeout', default=defaults['timeout'] or 0) return username, secret, endpoint_url, timeout softlayer-python-5.4.2/SoftLayer/CLI/config/show.py000066400000000000000000000005601324365065500222010ustar00rootroot00000000000000"""Show current CLI configuration.""" # :license: MIT, see LICENSE for more details. import click from SoftLayer.CLI import config from SoftLayer.CLI import environment @click.command() @environment.pass_env def cli(env): """Show current configuration.""" settings = config.get_settings_from_client(env.client) env.fout(config.config_table(settings)) softlayer-python-5.4.2/SoftLayer/CLI/core.py000066400000000000000000000140221324365065500207020ustar00rootroot00000000000000""" SoftLayer.CLI.core ~~~~~~~~~~~~~~~~~~ Core for the SoftLayer CLI :license: MIT, see LICENSE for more details. """ from __future__ import print_function import logging import os import sys import time import types import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting from SoftLayer import consts # pylint: disable=too-many-public-methods, broad-except, unused-argument # pylint: disable=redefined-builtin, super-init-not-called, arguments-differ START_TIME = time.time() DEBUG_LOGGING_MAP = { 0: logging.CRITICAL, 1: logging.WARNING, 2: logging.INFO, 3: logging.DEBUG } VALID_FORMATS = ['table', 'raw', 'json', 'jsonraw'] DEFAULT_FORMAT = 'raw' if sys.stdout.isatty(): DEFAULT_FORMAT = 'table' class CommandLoader(click.MultiCommand): """Loads module for click.""" def __init__(self, *path, **attrs): click.MultiCommand.__init__(self, **attrs) self.path = path def list_commands(self, ctx): """List all sub-commands.""" env = ctx.ensure_object(environment.Environment) env.load() return sorted(env.list_commands(*self.path)) def get_command(self, ctx, name): """Get command for click.""" env = ctx.ensure_object(environment.Environment) env.load() # Do alias lookup (only available for root commands) if len(self.path) == 0: name = env.resolve_alias(name) new_path = list(self.path) new_path.append(name) module = env.get_command(*new_path) if isinstance(module, types.ModuleType): return CommandLoader(*new_path, help=module.__doc__ or '') else: return module @click.group(help="SoftLayer Command-line Client", epilog="""To use most commands your SoftLayer username and api_key need to be configured. The easiest way to do that is to use: 'slcli setup'""", cls=CommandLoader, context_settings={'help_option_names': ['-h', '--help'], 'auto_envvar_prefix': 'SLCLI'}) @click.option('--format', default=DEFAULT_FORMAT, show_default=True, help="Output format", type=click.Choice(VALID_FORMATS)) @click.option('--config', '-C', required=False, default=click.get_app_dir('softlayer', force_posix=True), show_default=True, help="Config file location", type=click.Path(resolve_path=True)) @click.option('--verbose', '-v', help="Sets the debug noise level, specify multiple times " "for more verbosity.", type=click.IntRange(0, 3, clamp=True), count=True) @click.option('--proxy', required=False, help="HTTP[S] proxy to be use to make API calls") @click.option('--really / --not-really', '-y', is_flag=True, required=False, help="Confirm all prompt actions") @click.option('--demo / --no-demo', is_flag=True, required=False, help="Use demo data instead of actually making API calls") @click.version_option(prog_name="slcli (SoftLayer Command-line)") @environment.pass_env def cli(env, format='table', config=None, verbose=0, proxy=None, really=False, demo=False, **kwargs): """Main click CLI entry-point.""" if verbose > 0: logger = logging.getLogger() logger.addHandler(logging.StreamHandler()) logger.setLevel(DEBUG_LOGGING_MAP.get(verbose, logging.DEBUG)) # Populate environement with client and set it as the context object env.skip_confirmations = really env.config_file = config env.format = format env.ensure_client(config_file=config, is_demo=demo, proxy=proxy) env.vars['_start'] = time.time() env.vars['_timings'] = SoftLayer.TimingTransport(env.client.transport) env.client.transport = env.vars['_timings'] @cli.resultcallback() @environment.pass_env def output_diagnostics(env, verbose=0, **kwargs): """Output diagnostic information.""" if verbose > 0: diagnostic_table = formatting.Table(['name', 'value']) diagnostic_table.add_row(['execution_time', '%fs' % (time.time() - START_TIME)]) api_call_value = [] for call, _, duration in env.vars['_timings'].get_last_calls(): api_call_value.append( "%s::%s (%fs)" % (call.service, call.method, duration)) diagnostic_table.add_row(['api_calls', api_call_value]) diagnostic_table.add_row(['version', consts.USER_AGENT]) diagnostic_table.add_row(['python_version', sys.version]) diagnostic_table.add_row(['library_location', os.path.dirname(SoftLayer.__file__)]) env.err(env.fmt(diagnostic_table)) def main(reraise_exceptions=False, **kwargs): """Main program. Catches several common errors and displays them nicely.""" exit_status = 0 try: cli.main(**kwargs) except SoftLayer.SoftLayerAPIError as ex: if 'invalid api token' in ex.faultString.lower(): print("Authentication Failed: To update your credentials," " use 'slcli config setup'") exit_status = 1 else: print(str(ex)) exit_status = 1 except SoftLayer.SoftLayerError as ex: print(str(ex)) exit_status = 1 except exceptions.CLIAbort as ex: print(str(ex.message)) exit_status = ex.code except Exception: if reraise_exceptions: raise import traceback print("An unexpected error has occured:") print(str(traceback.format_exc())) print("Feel free to report this error as it is likely a bug:") print(" https://github.com/softlayer/softlayer-python/issues") exit_status = 1 sys.exit(exit_status) if __name__ == '__main__': main() softlayer-python-5.4.2/SoftLayer/CLI/custom_types.py000066400000000000000000000017761324365065500225240ustar00rootroot00000000000000""" SoftLayer.CLI.custom_types ~~~~~~~~~~~~~~~~~~~~~~~~ Custom type declarations extending click.ParamType :license: MIT, see LICENSE for more details. """ import click class NetworkParamType(click.ParamType): """Validates a network parameter type and converts to a tuple. todo: Implement to ipaddress.ip_network once the ipaddress backport module can be added as a dependency or is available on all supported python versions. """ name = 'network' def convert(self, value, param, ctx): try: # Inlined from python standard ipaddress module # https://docs.python.org/3/library/ipaddress.html address = str(value).split('/') if len(address) != 2: raise ValueError("Only one '/' permitted in %r" % value) ip_address, cidr = address return (ip_address, int(cidr)) except ValueError: self.fail('{} is not a valid network'.format(value), param, ctx) softlayer-python-5.4.2/SoftLayer/CLI/dedicatedhost/000077500000000000000000000000001324365065500222055ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/CLI/dedicatedhost/__init__.py000066400000000000000000000001051324365065500243120ustar00rootroot00000000000000"""Dedicated Host.""" # :license: MIT, see LICENSE for more details. softlayer-python-5.4.2/SoftLayer/CLI/dedicatedhost/create.py000066400000000000000000000076011324365065500240260ustar00rootroot00000000000000"""Order/create a dedicated Host.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting from SoftLayer.CLI import template @click.command( epilog="See 'slcli dedicatedhost create-options' for valid options.") @click.option('--hostname', '-H', help="Host portion of the FQDN", required=True, prompt=True) @click.option('--router', '-r', help="Router hostname ex. fcr02a.dal13", show_default=True) @click.option('--domain', '-D', help="Domain portion of the FQDN", required=True, prompt=True) @click.option('--datacenter', '-d', help="Datacenter shortname", required=True, prompt=True) @click.option('--flavor', '-f', help="Dedicated Virtual Host flavor", required=True, prompt=True) @click.option('--billing', type=click.Choice(['hourly', 'monthly']), default='hourly', show_default=True, help="Billing rate") @click.option('--verify', is_flag=True, help="Verify dedicatedhost without creating it.") @click.option('--template', '-t', is_eager=True, callback=template.TemplateCallback(list_args=['key']), help="A template file that defaults the command-line options", type=click.Path(exists=True, readable=True, resolve_path=True)) @click.option('--export', type=click.Path(writable=True, resolve_path=True), help="Exports options to a template file") @environment.pass_env def cli(env, **kwargs): """Order/create a dedicated host.""" mgr = SoftLayer.DedicatedHostManager(env.client) order = { 'hostname': kwargs['hostname'], 'domain': kwargs['domain'], 'flavor': kwargs['flavor'], 'location': kwargs['datacenter'], 'hourly': kwargs.get('billing') == 'hourly', } if kwargs['router']: order['router'] = kwargs['router'] do_create = not (kwargs['export'] or kwargs['verify']) output = None result = mgr.verify_order(**order) table = formatting.Table(['Item', 'cost']) table.align['Item'] = 'r' table.align['cost'] = 'r' if len(result['prices']) != 1: raise exceptions.ArgumentError("More than 1 price was found or no " "prices found") price = result['prices'] if order['hourly']: total = float(price[0].get('hourlyRecurringFee', 0.0)) else: total = float(price[0].get('recurringFee', 0.0)) if order['hourly']: table.add_row(['Total hourly cost', "%.2f" % total]) else: table.add_row(['Total monthly cost', "%.2f" % total]) output = [] output.append(table) output.append(formatting.FormattedItem( '', ' -- ! Prices reflected here are retail and do not ' 'take account level discounts and are not guaranteed.')) if kwargs['export']: export_file = kwargs.pop('export') template.export_to_template(export_file, kwargs, exclude=['wait', 'verify']) env.fout('Successfully exported options to a template file.') if do_create: if not env.skip_confirmations and not formatting.confirm( "This action will incur charges on your account. " "Continue?"): raise exceptions.CLIAbort('Aborting dedicated host order.') result = mgr.place_order(**order) table = formatting.KeyValueTable(['name', 'value']) table.align['name'] = 'r' table.align['value'] = 'l' table.add_row(['id', result['orderId']]) table.add_row(['created', result['orderDate']]) output.append(table) env.fout(output) softlayer-python-5.4.2/SoftLayer/CLI/dedicatedhost/create_options.py000066400000000000000000000044261324365065500256030ustar00rootroot00000000000000"""Options for ordering a dedicated host""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting @click.command() @click.option('--datacenter', '-d', help="Router hostname (requires --flavor) " "ex. ams01", show_default=True) @click.option('--flavor', '-f', help="Dedicated Virtual Host flavor (requires --datacenter)" " ex. 56_CORES_X_242_RAM_X_1_4_TB", show_default=True) @environment.pass_env def cli(env, **kwargs): """host order options for a given dedicated host. To get a list of available backend routers see example: slcli dh create-options --datacenter dal05 --flavor 56_CORES_X_242_RAM_X_1_4_TB """ mgr = SoftLayer.DedicatedHostManager(env.client) tables = [] if not kwargs['flavor'] and not kwargs['datacenter']: options = mgr.get_create_options() # Datacenters dc_table = formatting.Table(['datacenter', 'value']) dc_table.sortby = 'value' for location in options['locations']: dc_table.add_row([location['name'], location['key']]) tables.append(dc_table) dh_table = formatting.Table(['Dedicated Virtual Host Flavor(s)', 'value']) dh_table.sortby = 'value' for item in options['dedicated_host']: dh_table.add_row([item['name'], item['key']]) tables.append(dh_table) else: if kwargs['flavor'] is None or kwargs['datacenter'] is None: raise exceptions.ArgumentError('Both a flavor and datacenter need ' 'to be passed as arguments ' 'ex. slcli dh create-options -d ' 'ams01 -f ' '56_CORES_X_242_RAM_X_1_4_TB') router_opt = mgr.get_router_options(kwargs['datacenter'], kwargs['flavor']) br_table = formatting.Table( ['Available Backend Routers']) for router in router_opt: br_table.add_row([router['hostname']]) tables.append(br_table) env.fout(formatting.listing(tables, separator='\n')) softlayer-python-5.4.2/SoftLayer/CLI/dedicatedhost/detail.py000066400000000000000000000046511324365065500240270ustar00rootroot00000000000000"""Get details for a dedicated host.""" # :license: MIT, see LICENSE for more details. import logging import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer import utils LOGGER = logging.getLogger(__name__) @click.command() @click.argument('identifier') @click.option('--price', is_flag=True, help='Show associated prices') @click.option('--guests', is_flag=True, help='Show guests on dedicated host') @environment.pass_env def cli(env, identifier, price=False, guests=False): """Get details for a virtual server.""" dhost = SoftLayer.DedicatedHostManager(env.client) table = formatting.KeyValueTable(['name', 'value']) table.align['name'] = 'r' table.align['value'] = 'l' result = dhost.get_host(identifier) result = utils.NestedDict(result) table.add_row(['id', result['id']]) table.add_row(['name', result['name']]) table.add_row(['cpu count', result['cpuCount']]) table.add_row(['memory capacity', result['memoryCapacity']]) table.add_row(['disk capacity', result['diskCapacity']]) table.add_row(['create date', result['createDate']]) table.add_row(['modify date', result['modifyDate']]) table.add_row(['router id', result['backendRouter']['id']]) table.add_row(['router hostname', result['backendRouter']['hostname']]) table.add_row(['owner', formatting.FormattedItem( utils.lookup(result, 'billingItem', 'orderItem', 'order', 'userRecord', 'username') or formatting.blank(),)]) if price: total_price = utils.lookup(result, 'billingItem', 'nextInvoiceTotalRecurringAmount') or 0 total_price += sum(p['nextInvoiceTotalRecurringAmount'] for p in utils.lookup(result, 'billingItem', 'children') or []) table.add_row(['price_rate', total_price]) table.add_row(['guest count', result['guestCount']]) if guests: guest_table = formatting.Table(['id', 'hostname', 'domain', 'uuid']) for guest in result['guests']: guest_table.add_row([ guest['id'], guest['hostname'], guest['domain'], guest['uuid']]) table.add_row(['guests', guest_table]) table.add_row(['datacenter', result['datacenter']['name']]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/dedicatedhost/list.py000066400000000000000000000044111324365065500235320ustar00rootroot00000000000000"""List dedicated servers.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import columns as column_helper from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.CLI import helpers COLUMNS = [ column_helper.Column('datacenter', ('datacenter', 'name')), column_helper.Column( 'created_by', ('billingItem', 'orderItem', 'order', 'userRecord', 'username')), column_helper.Column( 'tags', lambda server: formatting.tags(server.get('tagReferences')), mask="tagReferences.tag.name"), ] DEFAULT_COLUMNS = [ 'id', 'name', 'cpuCount', 'diskCapacity', 'memoryCapacity', 'datacenter', 'guestCount', ] @click.command() @click.option('--cpu', '-c', help='Number of CPU cores', type=click.INT) @helpers.multi_option('--tag', help='Filter by tags') @click.option('--sortby', help='Column to sort by', default='name', show_default=True) @click.option('--columns', callback=column_helper.get_formatter(COLUMNS), help='Columns to display. [options: %s]' % ', '.join(column.name for column in COLUMNS), default=','.join(DEFAULT_COLUMNS), show_default=True) @click.option('--datacenter', '-d', help='Datacenter shortname') @click.option('--name', '-H', help='Host portion of the FQDN') @click.option('--memory', '-m', help='Memory capacity in mebibytes', type=click.INT) @click.option('--disk', '-D', help='Disk capacity') @environment.pass_env def cli(env, sortby, cpu, columns, datacenter, name, memory, disk, tag): """List dedicated host.""" mgr = SoftLayer.DedicatedHostManager(env.client) hosts = mgr.list_instances(cpus=cpu, datacenter=datacenter, hostname=name, memory=memory, disk=disk, tags=tag, mask=columns.mask()) table = formatting.Table(columns.columns) table.sortby = sortby for host in hosts: table.add_row([value or formatting.blank() for value in columns.row(host)]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/deprecated.py000066400000000000000000000006701324365065500220560ustar00rootroot00000000000000""" SoftLayer.CLI.deprecated ~~~~~~~~~~~~~~~~~~~~~~~~ Handles usage of the deprecated command name, 'sl'. :license: MIT, see LICENSE for more details. """ from __future__ import print_function import sys def main(): """Main function for the deprecated 'sl' command.""" print("ERROR: Use the 'slcli' command instead.", file=sys.stderr) print("> slcli %s" % ' '.join(sys.argv[1:]), file=sys.stderr) exit(-1) softlayer-python-5.4.2/SoftLayer/CLI/dns/000077500000000000000000000000001324365065500201655ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/CLI/dns/__init__.py000066400000000000000000000001111324365065500222670ustar00rootroot00000000000000"""Domain Name System.""" # :license: MIT, see LICENSE for more details. softlayer-python-5.4.2/SoftLayer/CLI/dns/record_add.py000066400000000000000000000014221324365065500226240ustar00rootroot00000000000000"""Add resource record.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import helpers # pylint: disable=redefined-builtin @click.command() @click.argument('zone') @click.argument('record') @click.argument('type') @click.argument('data') @click.option('--ttl', type=click.INT, default=7200, show_default=True, help='TTL value in seconds, such as 86400') @environment.pass_env def cli(env, zone, record, type, data, ttl): """Add resource record.""" manager = SoftLayer.DNSManager(env.client) zone_id = helpers.resolve_id(manager.resolve_ids, zone, name='zone') manager.create_record(zone_id, record, type, data, ttl=ttl) softlayer-python-5.4.2/SoftLayer/CLI/dns/record_edit.py000066400000000000000000000020731324365065500230240ustar00rootroot00000000000000"""Update DNS record.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import helpers # pylint: disable=redefined-builtin @click.command() @click.argument('zone-id') @click.option('--by-record', help='Edit by host record, such as www') @click.option('--by-id', help='Edit a single record by its ID') @click.option('--data', help='Record data, such as an IP address') @click.option('--ttl', type=click.INT, help='TTL value in seconds, such as 86400') @environment.pass_env def cli(env, zone_id, by_record, by_id, data, ttl): """Update DNS record.""" manager = SoftLayer.DNSManager(env.client) zone_id = helpers.resolve_id(manager.resolve_ids, zone_id, name='zone') results = manager.get_records(zone_id, host=by_record) for result in results: if by_id and str(result['id']) != by_id: continue result['data'] = data or result['data'] result['ttl'] = ttl or result['ttl'] manager.edit_record(result) softlayer-python-5.4.2/SoftLayer/CLI/dns/record_list.py000066400000000000000000000027411324365065500230540ustar00rootroot00000000000000"""List all records in a zone.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.CLI import helpers # pylint: disable=redefined-builtin, redefined-argument-from-local @click.command() @click.argument('zone') @click.option('--data', help='Record data, such as an IP address') @click.option('--record', help='Host record, such as www') @click.option('--ttl', type=click.INT, help='TTL value in seconds, such as 86400') @click.option('--type', help='Record type, such as A or CNAME') @environment.pass_env def cli(env, zone, data, record, ttl, type): """List all records in a zone.""" manager = SoftLayer.DNSManager(env.client) table = formatting.Table(['id', 'record', 'type', 'ttl', 'data']) table.align['ttl'] = 'l' table.align['record'] = 'r' table.align['data'] = 'l' zone_id = helpers.resolve_id(manager.resolve_ids, zone, name='zone') records = manager.get_records(zone_id, record_type=type, host=record, ttl=ttl, data=data) for the_record in records: table.add_row([ the_record['id'], the_record['host'], the_record['type'].upper(), the_record['ttl'], the_record['data'] ]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/dns/record_remove.py000066400000000000000000000010531324365065500233710ustar00rootroot00000000000000"""Remove resource record.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting @click.command() @click.argument('record_id') @environment.pass_env def cli(env, record_id): """Remove resource record.""" manager = SoftLayer.DNSManager(env.client) if not (env.skip_confirmations or formatting.no_going_back('yes')): raise exceptions.CLIAbort("Aborted.") manager.delete_record(record_id) softlayer-python-5.4.2/SoftLayer/CLI/dns/zone_create.py000066400000000000000000000005061324365065500230360ustar00rootroot00000000000000"""Create a zone.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment @click.command() @click.argument('zone') @environment.pass_env def cli(env, zone): """Create a zone.""" manager = SoftLayer.DNSManager(env.client) manager.create_zone(zone) softlayer-python-5.4.2/SoftLayer/CLI/dns/zone_delete.py000066400000000000000000000011611324365065500230330ustar00rootroot00000000000000"""Delete zone.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting from SoftLayer.CLI import helpers @click.command() @click.argument('zone') @environment.pass_env def cli(env, zone): """Delete zone.""" manager = SoftLayer.DNSManager(env.client) zone_id = helpers.resolve_id(manager.resolve_ids, zone, name='zone') if not (env.skip_confirmations or formatting.no_going_back(zone)): raise exceptions.CLIAbort("Aborted.") manager.delete_zone(zone_id) softlayer-python-5.4.2/SoftLayer/CLI/dns/zone_import.py000066400000000000000000000075261324365065500231160ustar00rootroot00000000000000"""Import zone based off a BIND zone file.""" # :license: MIT, see LICENSE for more details. import re import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import helpers RECORD_REGEX = re.compile(r"""^((?P(([\w-]+|\*)(\.)?)*|\@)?\s+ (?P\d+)?\s+ (?P\w+)?)?\s+ (?P\w+)\s+ (?P.*)""", re.X) RECORD_FMT = "type={type}, record={record}, data={data}, ttl={ttl}" @click.command() @click.argument('zonefile', type=click.Path(exists=True, readable=True, resolve_path=True)) @click.option('--dry-run', is_flag=True, help="Don't actually create records") @environment.pass_env def cli(env, zonefile, dry_run): """Import zone based off a BIND zone file.""" manager = SoftLayer.DNSManager(env.client) with open(zonefile) as zone_f: zone_contents = zone_f.read() zone, records, bad_lines = parse_zone_details(zone_contents) env.out("Parsed: zone=%s" % zone) for record in records: env.out("Parsed: %s" % RECORD_FMT.format(**record)) for line in bad_lines: env.out("Unparsed: %s" % line) if dry_run: return # Find zone id or create the zone if it doesn't exist try: zone_id = helpers.resolve_id(manager.resolve_ids, zone, name='zone') except exceptions.CLIAbort: zone_id = manager.create_zone(zone)['id'] env.out(click.style("Created: %s" % zone, fg='green')) # Attempt to create each record for record in records: try: manager.create_record(zone_id, record['record'], record['type'], record['data'], record['ttl']) env.out(click.style("Created: %s" % RECORD_FMT.format(**record), fg='green')) except SoftLayer.SoftLayerAPIError as ex: env.out(click.style("Failed: %s" % RECORD_FMT.format(**record), fg='red')) env.out(click.style(str(ex), fg='red')) env.out(click.style("Finished", fg='green')) def parse_zone_details(zone_contents): """Parses a zone file into python data-structures.""" records = [] bad_lines = [] zone_lines = [line.strip() for line in zone_contents.split('\n')] zone_search = re.search(r'^\$ORIGIN (?P.*)\.', zone_lines[0]) zone = zone_search.group('zone') for line in zone_lines[1:]: record_search = re.search(RECORD_REGEX, line) if record_search is None: bad_lines.append(line) continue name = record_search.group('domain') # The API requires we send a host, although bind allows a blank # entry. @ is the same thing as blank if name is None: name = "@" ttl = record_search.group('ttl') # we don't do anything with the class # domain_class = domainSearch.group('class') record_type = record_search.group('type').upper() data = record_search.group('data') # the dns class doesn't support weighted MX records yet, so we chomp # that part out. if record_type == "MX": record_search = re.search(r'(?P\d+)\s+(?P.*)', data) data = record_search.group('data') # This will skip the SOA record bit. And any domain that gets # parsed oddly. if record_type == 'IN': bad_lines.append(line) continue records.append({ 'record': name, 'type': record_type, 'data': data, 'ttl': ttl, }) return zone, records, bad_lines softlayer-python-5.4.2/SoftLayer/CLI/dns/zone_list.py000066400000000000000000000012231324365065500225430ustar00rootroot00000000000000"""List all zones.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting @click.command() @environment.pass_env def cli(env): """List all zones.""" manager = SoftLayer.DNSManager(env.client) zones = manager.list_zones() table = formatting.Table(['id', 'zone', 'serial', 'updated']) table.align['serial'] = 'c' table.align['updated'] = 'c' for zone in zones: table.add_row([ zone['id'], zone['name'], zone['serial'], zone['updateDate'], ]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/dns/zone_print.py000066400000000000000000000007241324365065500227310ustar00rootroot00000000000000"""Print zone in BIND format.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import helpers @click.command() @click.argument('zone') @environment.pass_env def cli(env, zone): """Print zone in BIND format.""" manager = SoftLayer.DNSManager(env.client) zone_id = helpers.resolve_id(manager.resolve_ids, zone, name='zone') env.fout(manager.dump_zone(zone_id)) softlayer-python-5.4.2/SoftLayer/CLI/environment.py000066400000000000000000000123651324365065500223260ustar00rootroot00000000000000""" SoftLayer.CLI.environment ~~~~~~~~~~~~~~~~~~~~~~~~~ Abstracts everything related to the user's environment when running the CLI :license: MIT, see LICENSE for more details. """ import importlib import click import pkg_resources import SoftLayer from SoftLayer.CLI import formatting from SoftLayer.CLI import routes # pylint: disable=too-many-instance-attributes, invalid-name, no-self-use # Calling pkg_resources.iter_entry_points shows a false-positive # pylint: disable=no-member class Environment(object): """Provides access to the current CLI environment.""" def __init__(self): # {'path:to:command': ModuleLoader()} # {'vs:list': ModuleLoader()} self.commands = {} self.aliases = {} self.vars = {} self.client = None self.format = 'table' self.skip_confirmations = False self.config_file = None self._modules_loaded = False def out(self, output, newline=True): """Outputs a string to the console (stdout).""" click.echo(output, nl=newline) def err(self, output, newline=True): """Outputs an error string to the console (stderr).""" click.echo(output, nl=newline, err=True) def fmt(self, output): """Format output based on current the environment format.""" return formatting.format_output(output, fmt=self.format) def fout(self, output, newline=True): """Format the input and output to the console (stdout).""" if output is not None: self.out(self.fmt(output), newline=newline) def input(self, prompt, default=None, show_default=True): """Provide a command prompt.""" return click.prompt(prompt, default=default, show_default=show_default) def getpass(self, prompt, default=None): """Provide a password prompt.""" return click.prompt(prompt, hide_input=True, default=default) # Command loading methods def list_commands(self, *path): """Command listing.""" path_str = ':'.join(path) commands = [] for command in self.commands: # Filter based on prefix and the segment length if all([command.startswith(path_str), len(path) == command.count(":")]): # offset is used to exclude the path that the caller requested. offset = len(path_str) + 1 if path_str else 0 commands.append(command[offset:]) return sorted(commands) def get_command(self, *path): """Return command at the given path or raise error.""" path_str = ':'.join(path) if path_str in self.commands: return self.commands[path_str].load() return None def resolve_alias(self, path_str): """Returns the actual command name. Uses the alias mapping.""" if path_str in self.aliases: return self.aliases[path_str] return path_str def load(self): """Loads all modules.""" if self._modules_loaded is True: return self.load_modules_from_python(routes.ALL_ROUTES) self.aliases.update(routes.ALL_ALIASES) self._load_modules_from_entry_points('softlayer.cli') self._modules_loaded = True def load_modules_from_python(self, route_list): """Load modules from the native python source.""" for name, modpath in route_list: if ':' in modpath: path, attr = modpath.split(':', 1) else: path, attr = modpath, None self.commands[name] = ModuleLoader(path, attr=attr) def _load_modules_from_entry_points(self, entry_point_group): """Load modules from the entry_points (slower). Entry points can be used to add new commands to the CLI. Usage: entry_points={'softlayer.cli': ['new-cmd = mymodule.new_cmd.cli']} """ for obj in pkg_resources.iter_entry_points(group=entry_point_group, name=None): self.commands[obj.name] = obj def ensure_client(self, config_file=None, is_demo=False, proxy=None): """Create a new SLAPI client to the environment. This will be a no-op if there is already a client in this environment. """ if self.client is not None: return # Environment can be passed in explicitly. This is used for testing if is_demo: client = SoftLayer.BaseClient( transport=SoftLayer.FixtureTransport(), auth=None, ) else: # Create SL Client client = SoftLayer.create_client_from_env( proxy=proxy, config_file=config_file, ) self.client = client class ModuleLoader(object): """Module loader that acts a little like an EntryPoint object.""" def __init__(self, import_path, attr=None): self.import_path = import_path self.attr = attr def load(self): """load and return the module/attribute.""" module = importlib.import_module(self.import_path) if self.attr: return getattr(module, self.attr) return module pass_env = click.make_pass_decorator(Environment, ensure=True) softlayer-python-5.4.2/SoftLayer/CLI/exceptions.py000066400000000000000000000020261324365065500221340ustar00rootroot00000000000000""" SoftLayer.CLI.exceptions ~~~~~~~~~~~~~~~~~~~~~~~~ Exceptions to be used in the CLI modules. :license: MIT, see LICENSE for more details. """ # pylint: disable=keyword-arg-before-vararg class CLIHalt(SystemExit): """Smoothly halt the execution of the command. No error.""" def __init__(self, code=0, *args): super(CLIHalt, self).__init__(*args) self.code = code def __str__(self): return "" % (self.code, getattr(self, 'message')) __repr__ = __str__ class CLIAbort(CLIHalt): """Halt the execution of the command. Gives an exit code of 2.""" def __init__(self, msg, *args): super(CLIAbort, self).__init__(code=2, *args) self.message = msg class ArgumentError(CLIAbort): """Halt the execution of the command because of invalid arguments.""" def __init__(self, msg, *args): super(ArgumentError, self).__init__(msg, *args) self.message = "Argument Error: %s" % msg softlayer-python-5.4.2/SoftLayer/CLI/file/000077500000000000000000000000001324365065500203205ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/CLI/file/__init__.py000066400000000000000000000000241324365065500224250ustar00rootroot00000000000000"""File Storage.""" softlayer-python-5.4.2/SoftLayer/CLI/file/access/000077500000000000000000000000001324365065500215615ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/CLI/file/access/__init__.py000066400000000000000000000000431324365065500236670ustar00rootroot00000000000000"""File Storage Access Control.""" softlayer-python-5.4.2/SoftLayer/CLI/file/access/authorize.py000066400000000000000000000041571324365065500241540ustar00rootroot00000000000000"""Authorizes hosts on a specific file volume.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions @click.command() @click.argument('volume_id') @click.option('--hardware-id', '-h', multiple=True, help='The id of one SoftLayer_Hardware to authorize') @click.option('--virtual-id', '-v', multiple=True, help='The id of one SoftLayer_Virtual_Guest to authorize') @click.option('--ip-address-id', '-i', multiple=True, help='The id of one SoftLayer_Network_Subnet_IpAddress' ' to authorize') @click.option('--ip-address', multiple=True, help='An IP address to authorize') @click.option('--subnet-id', '-s', multiple=True, help='The id of one SoftLayer_Network_Subnet to authorize') @environment.pass_env def cli(env, volume_id, hardware_id, virtual_id, ip_address_id, ip_address, subnet_id): """Authorizes hosts to access a given volume""" file_manager = SoftLayer.FileStorageManager(env.client) ip_address_id_list = list(ip_address_id) # Convert actual IP Addresses to their SoftLayer ids if ip_address is not None: network_manager = SoftLayer.NetworkManager(env.client) for ip_address_value in ip_address: ip_address_object = network_manager.ip_lookup(ip_address_value) if ip_address_object == "": click.echo("IP Address not found on your account. " + "Please confirm IP and try again.") raise exceptions.ArgumentError('Incorrect IP Address') else: ip_address_id_list.append(ip_address_object['id']) file_manager.authorize_host_to_volume(volume_id, hardware_id, virtual_id, ip_address_id_list, subnet_id) # If no exception was raised, the command succeeded click.echo('The specified hosts were authorized to access %s' % volume_id) softlayer-python-5.4.2/SoftLayer/CLI/file/access/list.py000066400000000000000000000027011324365065500231060ustar00rootroot00000000000000"""List hosts with access to file volume.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import columns as column_helper from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.CLI import storage_utils @click.command() @click.argument('volume_id') @click.option('--sortby', help='Column to sort by', default='name') @click.option('--columns', callback=column_helper.get_formatter(storage_utils.COLUMNS), help='Columns to display. Options: {0}'.format( ', '.join(column.name for column in storage_utils.COLUMNS)), default=','.join(storage_utils.DEFAULT_COLUMNS)) @environment.pass_env def cli(env, columns, sortby, volume_id): """List ACLs.""" file_manager = SoftLayer.FileStorageManager(env.client) access_list = file_manager.get_file_volume_access_list( volume_id=volume_id) table = formatting.Table(columns.columns) table.sortby = sortby for key, type_name in [('allowedVirtualGuests', 'VIRTUAL'), ('allowedHardware', 'HARDWARE'), ('allowedSubnets', 'SUBNET'), ('allowedIpAddresses', 'IP')]: for obj in access_list.get(key, []): obj['type'] = type_name table.add_row([value or formatting.blank() for value in columns.row(obj)]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/file/access/revoke.py000066400000000000000000000037071324365065500234350ustar00rootroot00000000000000"""Revokes hosts' access on a specific file volume.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment @click.command() @click.argument('volume_id') @click.option('--hardware-id', '-h', multiple=True, help='The id of one SoftLayer_Hardware' ' to revoke authorization') @click.option('--virtual-id', '-v', multiple=True, help='The id of one SoftLayer_Virtual_Guest' ' to revoke authorization') @click.option('--ip-address-id', '-i', multiple=True, help='The id of one SoftLayer_Network_Subnet_IpAddress' ' to revoke authorization') @click.option('--ip-address', multiple=True, help='An IP address to revoke authorization') @click.option('--subnet-id', '-s', multiple=True, help='The id of one SoftLayer_Network_Subnet' ' to revoke authorization') @environment.pass_env def cli(env, volume_id, hardware_id, virtual_id, ip_address_id, ip_address, subnet_id): """Revokes authorization for hosts accessing a given volume""" file_manager = SoftLayer.FileStorageManager(env.client) ip_address_id_list = list(ip_address_id) # Convert actual IP Addresses to their SoftLayer ids if ip_address is not None: network_manager = SoftLayer.NetworkManager(env.client) for ip_address_value in ip_address: ip_address_object = network_manager.ip_lookup(ip_address_value) ip_address_id_list.append(ip_address_object['id']) file_manager.deauthorize_host_to_volume(volume_id, hardware_id, virtual_id, ip_address_id_list, subnet_id) # If no exception was raised, the command succeeded click.echo('Access to %s was revoked for the specified hosts' % volume_id) softlayer-python-5.4.2/SoftLayer/CLI/file/cancel.py000066400000000000000000000025311324365065500221200ustar00rootroot00000000000000"""Cancel an existing file storage volume.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting @click.command() @click.argument('volume-id') @click.option('--reason', help="An optional reason for cancellation") @click.option('--immediate', is_flag=True, help="Cancels the file storage volume immediately instead " "of on the billing anniversary") @environment.pass_env def cli(env, volume_id, reason, immediate): """Cancel an existing file storage volume.""" file_storage_manager = SoftLayer.FileStorageManager(env.client) if not (env.skip_confirmations or formatting.no_going_back(volume_id)): raise exceptions.CLIAbort('Aborted') cancelled = file_storage_manager.cancel_file_volume(volume_id, reason, immediate) if cancelled: if immediate: click.echo('File volume with id %s has been marked' ' for immediate cancellation' % volume_id) else: click.echo('File volume with id %s has been marked' ' for cancellation' % volume_id) else: click.echo('Unable to cancle file volume %s' % volume_id) softlayer-python-5.4.2/SoftLayer/CLI/file/count.py000066400000000000000000000026751324365065500220340ustar00rootroot00000000000000"""List number of file storage volumes per datacenter.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting DEFAULT_COLUMNS = [ 'Datacenter', 'Count' ] @click.command() @click.option('--datacenter', '-d', help='Datacenter shortname') @click.option('--sortby', help='Column to sort by', default='Datacenter') @environment.pass_env def cli(env, sortby, datacenter): """List number of file storage volumes per datacenter.""" file_manager = SoftLayer.FileStorageManager(env.client) mask = "mask[serviceResource[datacenter[name]],"\ "replicationPartners[serviceResource[datacenter[name]]]]" file_volumes = file_manager.list_file_volumes(datacenter=datacenter, mask=mask) datacenters = dict() for volume in file_volumes: service_resource = volume['serviceResource'] if 'datacenter' in service_resource: datacenter_name = service_resource['datacenter']['name'] if datacenter_name not in datacenters.keys(): datacenters[datacenter_name] = 1 else: datacenters[datacenter_name] += 1 table = formatting.KeyValueTable(DEFAULT_COLUMNS) table.sortby = sortby for datacenter_name in datacenters: table.add_row([datacenter_name, datacenters[datacenter_name]]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/file/detail.py000066400000000000000000000120131324365065500221310ustar00rootroot00000000000000"""Display details for a specified volume.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer import utils @click.command() @click.argument('volume_id') @environment.pass_env def cli(env, volume_id): """Display details for a specified volume.""" file_manager = SoftLayer.FileStorageManager(env.client) file_volume = file_manager.get_file_volume_details(volume_id) file_volume = utils.NestedDict(file_volume) table = formatting.KeyValueTable(['Name', 'Value']) table.align['Name'] = 'r' table.align['Value'] = 'l' storage_type = file_volume['storageType']['keyName'].split('_').pop(0) table.add_row(['ID', file_volume['id']]) table.add_row(['Username', file_volume['username']]) table.add_row(['Type', storage_type]) table.add_row(['Capacity (GB)', "%iGB" % file_volume['capacityGb']]) used_space = int(file_volume['bytesUsed'])\ if file_volume['bytesUsed'] else 0 if used_space < (1 << 10): table.add_row(['Used Space', "%dB" % used_space]) elif used_space < (1 << 20): table.add_row(['Used Space', "%dKB" % (used_space / (1 << 10))]) elif used_space < (1 << 30): table.add_row(['Used Space', "%dMB" % (used_space / (1 << 20))]) else: table.add_row(['Used Space', "%dGB" % (used_space / (1 << 30))]) if file_volume.get('provisionedIops'): table.add_row(['IOPs', int(file_volume['provisionedIops'])]) if file_volume.get('storageTierLevel'): table.add_row([ 'Endurance Tier', file_volume['storageTierLevel'], ]) table.add_row([ 'Data Center', file_volume['serviceResource']['datacenter']['name'], ]) table.add_row([ 'Target IP', file_volume['serviceResourceBackendIpAddress'], ]) if file_volume['fileNetworkMountAddress']: table.add_row([ 'Mount Address', file_volume['fileNetworkMountAddress'], ]) if file_volume['snapshotCapacityGb']: table.add_row([ 'Snapshot Capacity (GB)', file_volume['snapshotCapacityGb'], ]) if 'snapshotSizeBytes' in file_volume['parentVolume']: table.add_row([ 'Snapshot Used (Bytes)', file_volume['parentVolume']['snapshotSizeBytes'], ]) table.add_row(['# of Active Transactions', "%i" % file_volume['activeTransactionCount']]) if file_volume['activeTransactions']: for trans in file_volume['activeTransactions']: if 'transactionStatus' in trans and 'friendlyName' in trans['transactionStatus']: table.add_row(['Ongoing Transaction', trans['transactionStatus']['friendlyName']]) table.add_row(['Replicant Count', "%u" % file_volume.get('replicationPartnerCount', 0)]) if file_volume['replicationPartnerCount'] > 0: # This if/else temporarily handles a bug in which the SL API # returns a string or object for 'replicationStatus'; it seems that # the type is string for File volumes and object for Block volumes if 'message' in file_volume['replicationStatus']: table.add_row(['Replication Status', "%s" % file_volume['replicationStatus']['message']]) else: table.add_row(['Replication Status', "%s" % file_volume['replicationStatus']]) replicant_list = [] for replicant in file_volume['replicationPartners']: replicant_table = formatting.Table(['Replicant ID', replicant['id']]) replicant_table.add_row([ 'Volume Name', utils.lookup(replicant, 'username')]) replicant_table.add_row([ 'Target IP', utils.lookup(replicant, 'serviceResourceBackendIpAddress')]) replicant_table.add_row([ 'Data Center', utils.lookup(replicant, 'serviceResource', 'datacenter', 'name')]) replicant_table.add_row([ 'Schedule', utils.lookup(replicant, 'replicationSchedule', 'type', 'keyname')]) replicant_list.append(replicant_table) table.add_row(['Replicant Volumes', replicant_list]) if file_volume.get('originalVolumeSize'): original_volume_info = formatting.Table(['Property', 'Value']) original_volume_info.add_row(['Original Volume Size', file_volume['originalVolumeSize']]) if file_volume.get('originalVolumeName'): original_volume_info.add_row(['Original Volume Name', file_volume['originalVolumeName']]) if file_volume.get('originalSnapshotName'): original_volume_info.add_row(['Original Snapshot Name', file_volume['originalSnapshotName']]) table.add_row(['Original Volume Properties', original_volume_info]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/file/duplicate.py000066400000000000000000000077401324365065500226540ustar00rootroot00000000000000"""Order a duplicate file storage volume.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions CONTEXT_SETTINGS = {'token_normalize_func': lambda x: x.upper()} @click.command(context_settings=CONTEXT_SETTINGS) @click.argument('origin-volume-id') @click.option('--origin-snapshot-id', '-o', type=int, help="ID of an origin volume snapshot to use for duplcation.") @click.option('--duplicate-size', '-c', type=int, help='Size of duplicate file volume in GB. ' '***If no size is specified, the size of ' 'the origin volume will be used.***\n' 'Minimum: [the size of the origin volume]') @click.option('--duplicate-iops', '-i', type=int, help='Performance Storage IOPS, between 100 and 6000 in ' 'multiples of 100 [only used for performance volumes] ' '***If no IOPS value is specified, the IOPS value of the ' 'origin volume will be used.***\n' 'Requirements: [If IOPS/GB for the origin volume is less ' 'than 0.3, IOPS/GB for the duplicate must also be less ' 'than 0.3. If IOPS/GB for the origin volume is greater ' 'than or equal to 0.3, IOPS/GB for the duplicate must ' 'also be greater than or equal to 0.3.]') @click.option('--duplicate-tier', '-t', help='Endurance Storage Tier (IOPS per GB) [only used for ' 'endurance volumes] ***If no tier is specified, the tier ' 'of the origin volume will be used.***\n' 'Requirements: [If IOPS/GB for the origin volume is 0.25, ' 'IOPS/GB for the duplicate must also be 0.25. If IOPS/GB ' 'for the origin volume is greater than 0.25, IOPS/GB ' 'for the duplicate must also be greater than 0.25.]', type=click.Choice(['0.25', '2', '4', '10'])) @click.option('--duplicate-snapshot-size', '-s', type=int, help='The size of snapshot space to order for the duplicate. ' '***If no snapshot space size is specified, the snapshot ' 'space size of the origin file volume will be used.***\n' 'Input "0" for this parameter to order a duplicate volume ' 'with no snapshot space.') @click.option('--billing', type=click.Choice(['hourly', 'monthly']), default='monthly', help="Optional parameter for Billing rate (default to monthly)") @environment.pass_env def cli(env, origin_volume_id, origin_snapshot_id, duplicate_size, duplicate_iops, duplicate_tier, duplicate_snapshot_size, billing): """Order a duplicate file storage volume.""" file_manager = SoftLayer.FileStorageManager(env.client) hourly_billing_flag = False if billing.lower() == "hourly": hourly_billing_flag = True if duplicate_tier is not None: duplicate_tier = float(duplicate_tier) try: order = file_manager.order_duplicate_volume( origin_volume_id, origin_snapshot_id=origin_snapshot_id, duplicate_size=duplicate_size, duplicate_iops=duplicate_iops, duplicate_tier_level=duplicate_tier, duplicate_snapshot_size=duplicate_snapshot_size, hourly_billing_flag=hourly_billing_flag ) except ValueError as ex: raise exceptions.ArgumentError(str(ex)) if 'placedOrder' in order.keys(): click.echo("Order #{0} placed successfully!".format( order['placedOrder']['id'])) for item in order['placedOrder']['items']: click.echo(" > %s" % item['description']) else: click.echo("Order could not be placed! Please verify your options " + "and try again.") softlayer-python-5.4.2/SoftLayer/CLI/file/list.py000066400000000000000000000061631324365065500216530ustar00rootroot00000000000000"""List file storage volumes.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import columns as column_helper from SoftLayer.CLI import environment from SoftLayer.CLI import formatting COLUMNS = [ column_helper.Column('id', ('id',), mask="id"), column_helper.Column('username', ('username',), mask="username"), column_helper.Column('datacenter', ('serviceResource', 'datacenter', 'name'), mask="serviceResource.datacenter.name"), column_helper.Column( 'storage_type', lambda b: b['storageType']['keyName'].split('_').pop(0) if 'storageType' in b and 'keyName' in b['storageType'] and isinstance(b['storageType']['keyName'], str) else '-', mask="storageType.keyName"), column_helper.Column('capacity_gb', ('capacityGb',), mask="capacityGb"), column_helper.Column('bytes_used', ('bytesUsed',), mask="bytesUsed"), column_helper.Column('ip_addr', ('serviceResourceBackendIpAddress',), mask="serviceResourceBackendIpAddress"), column_helper.Column('active_transactions', ('activeTransactionCount',), mask="activeTransactionCount"), column_helper.Column('mount_addr', ('fileNetworkMountAddress',), mask="fileNetworkMountAddress",), column_helper.Column('rep_partner_count', ('replicationPartnerCount',), mask="replicationPartnerCount"), column_helper.Column( 'created_by', ('billingItem', 'orderItem', 'order', 'userRecord', 'username')), ] DEFAULT_COLUMNS = [ 'id', 'username', 'datacenter', 'storage_type', 'capacity_gb', 'bytes_used', 'ip_addr', 'active_transactions', 'mount_addr', 'rep_partner_count' ] @click.command() @click.option('--username', '-u', help='Volume username') @click.option('--datacenter', '-d', help='Datacenter shortname') @click.option('--storage-type', help='Type of storage volume', type=click.Choice(['performance', 'endurance'])) @click.option('--sortby', help='Column to sort by', default='username') @click.option('--columns', callback=column_helper.get_formatter(COLUMNS), help='Columns to display. Options: {0}'.format( ', '.join(column.name for column in COLUMNS)), default=','.join(DEFAULT_COLUMNS)) @environment.pass_env def cli(env, sortby, columns, datacenter, username, storage_type): """List file storage.""" file_manager = SoftLayer.FileStorageManager(env.client) file_volumes = file_manager.list_file_volumes(datacenter=datacenter, username=username, storage_type=storage_type, mask=columns.mask()) table = formatting.Table(columns.columns) table.sortby = sortby for file_volume in file_volumes: table.add_row([value or formatting.blank() for value in columns.row(file_volume)]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/file/modify.py000066400000000000000000000052341324365065500221650ustar00rootroot00000000000000"""Modify an existing file storage volume.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions CONTEXT_SETTINGS = {'token_normalize_func': lambda x: x.upper()} @click.command(context_settings=CONTEXT_SETTINGS) @click.argument('volume-id') @click.option('--new-size', '-c', type=int, help='New Size of file volume in GB. ***If no size is given, the original size of volume is used.***\n' 'Potential Sizes: [20, 40, 80, 100, 250, 500, 1000, 2000, 4000, 8000, 12000]\n' 'Minimum: [the original size of the volume]') @click.option('--new-iops', '-i', type=int, help='Performance Storage IOPS, between 100 and 6000 in multiples of 100 [only for performance volumes] ' '***If no IOPS value is specified, the original IOPS value of the volume will be used.***\n' 'Requirements: [If original IOPS/GB for the volume is less than 0.3, new IOPS/GB must also be ' 'less than 0.3. If original IOPS/GB for the volume is greater than or equal to 0.3, new IOPS/GB ' 'for the volume must also be greater than or equal to 0.3.]') @click.option('--new-tier', '-t', help='Endurance Storage Tier (IOPS per GB) [only for endurance volumes] ' '***If no tier is specified, the original tier of the volume will be used.***\n' 'Requirements: [If original IOPS/GB for the volume is 0.25, new IOPS/GB for the volume must also ' 'be 0.25. If original IOPS/GB for the volume is greater than 0.25, new IOPS/GB for the volume ' 'must also be greater than 0.25.]', type=click.Choice(['0.25', '2', '4', '10'])) @environment.pass_env def cli(env, volume_id, new_size, new_iops, new_tier): """Modify an existing file storage volume.""" file_manager = SoftLayer.FileStorageManager(env.client) if new_tier is not None: new_tier = float(new_tier) try: order = file_manager.order_modified_volume( volume_id, new_size=new_size, new_iops=new_iops, new_tier_level=new_tier, ) except ValueError as ex: raise exceptions.ArgumentError(str(ex)) if 'placedOrder' in order.keys(): click.echo("Order #{0} placed successfully!".format(order['placedOrder']['id'])) for item in order['placedOrder']['items']: click.echo(" > %s" % item['description']) else: click.echo("Order could not be placed! Please verify your options and try again.") softlayer-python-5.4.2/SoftLayer/CLI/file/order.py000066400000000000000000000110301324365065500220000ustar00rootroot00000000000000"""Order a file storage volume.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions CONTEXT_SETTINGS = {'token_normalize_func': lambda x: x.upper()} @click.command(context_settings=CONTEXT_SETTINGS) @click.option('--storage-type', help='Type of file storage volume', type=click.Choice(['performance', 'endurance']), required=True) @click.option('--size', type=int, help='Size of file storage volume in GB', required=True) @click.option('--iops', type=int, help='Performance Storage IOPs,' ' between 100 and 6000 in multiples of 100' ' [required for storage-type performance]') @click.option('--tier', help='Endurance Storage Tier (IOP per GB)' ' [required for storage-type endurance]', type=click.Choice(['0.25', '2', '4', '10'])) @click.option('--location', help='Datacenter short name (e.g.: dal09)', required=True) @click.option('--snapshot-size', type=int, help='Optional parameter for ordering snapshot ' 'space along with endurance file storage; specifies ' 'the size (in GB) of snapshot space to order') @click.option('--service-offering', help='The service offering package to use for placing ' 'the order [optional, default is \'storage_as_a_service\']', default='storage_as_a_service', type=click.Choice([ 'storage_as_a_service', 'enterprise', 'performance'])) @click.option('--billing', type=click.Choice(['hourly', 'monthly']), default='monthly', help="Optional parameter for Billing rate (default to monthly)") @environment.pass_env def cli(env, storage_type, size, iops, tier, location, snapshot_size, service_offering, billing): """Order a file storage volume.""" file_manager = SoftLayer.FileStorageManager(env.client) storage_type = storage_type.lower() hourly_billing_flag = False if billing.lower() == "hourly": hourly_billing_flag = True if hourly_billing_flag and service_offering != 'storage_as_a_service': raise exceptions.CLIAbort( 'Hourly billing is only available for the storage_as_a_service ' 'service offering' ) if storage_type == 'performance': if iops is None: raise exceptions.CLIAbort( 'Option --iops required with Performance') if iops % 100 != 0: raise exceptions.CLIAbort( 'Option --iops must be a multiple of 100' ) if service_offering == 'performance' and snapshot_size is not None: raise exceptions.CLIAbort( '--snapshot-size is not available for performance volumes ' 'ordered with the \'performance\' service offering option' ) try: order = file_manager.order_file_volume( storage_type=storage_type, location=location, size=size, iops=iops, snapshot_size=snapshot_size, service_offering=service_offering, hourly_billing_flag=hourly_billing_flag ) except ValueError as ex: raise exceptions.ArgumentError(str(ex)) if storage_type == 'endurance': if tier is None: raise exceptions.CLIAbort( 'Option --tier required with Endurance in IOPS/GB ' '[0.25,2,4,10]' ) try: order = file_manager.order_file_volume( storage_type=storage_type, location=location, size=size, tier_level=float(tier), snapshot_size=snapshot_size, service_offering=service_offering, hourly_billing_flag=hourly_billing_flag ) except ValueError as ex: raise exceptions.ArgumentError(str(ex)) if 'placedOrder' in order.keys(): click.echo("Order #{0} placed successfully!".format( order['placedOrder']['id'])) for item in order['placedOrder']['items']: click.echo(" > %s" % item['description']) else: click.echo("Order could not be placed! Please verify your options " + "and try again.") softlayer-python-5.4.2/SoftLayer/CLI/file/replication/000077500000000000000000000000001324365065500226315ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/CLI/file/replication/__init__.py000066400000000000000000000000501324365065500247350ustar00rootroot00000000000000"""File Storage Replication Control.""" softlayer-python-5.4.2/SoftLayer/CLI/file/replication/failback.py000066400000000000000000000013341324365065500247400ustar00rootroot00000000000000"""Failback from a replicant volume.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment @click.command() @click.argument('volume-id') @click.option('--replicant-id', help="ID of the replicant volume") @environment.pass_env def cli(env, volume_id, replicant_id): """Failback a file volume from the given replicant volume.""" file_storage_manager = SoftLayer.FileStorageManager(env.client) success = file_storage_manager.failback_from_replicant( volume_id, replicant_id ) if success: click.echo("Failback from replicant is now in progress.") else: click.echo("Failback operation could not be initiated.") softlayer-python-5.4.2/SoftLayer/CLI/file/replication/failover.py000066400000000000000000000016001324365065500250070ustar00rootroot00000000000000"""Failover to a replicant volume.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment @click.command() @click.argument('volume-id') @click.option('--replicant-id', help="ID of the replicant volume") @click.option('--immediate', is_flag=True, default=False, help="Failover to replicant immediately.") @environment.pass_env def cli(env, volume_id, replicant_id, immediate): """Failover a file volume to the given replicant volume.""" file_storage_manager = SoftLayer.FileStorageManager(env.client) success = file_storage_manager.failover_to_replicant( volume_id, replicant_id, immediate ) if success: click.echo("Failover to replicant is now in progress.") else: click.echo("Failover operation could not be initiated.") softlayer-python-5.4.2/SoftLayer/CLI/file/replication/locations.py000066400000000000000000000031021324365065500251720ustar00rootroot00000000000000"""List suitable replication datacenters for the given volume.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import columns as column_helper from SoftLayer.CLI import environment from SoftLayer.CLI import formatting COLUMNS = [ column_helper.Column('ID', ('id',), mask="id"), column_helper.Column('Long Name', ('longName',), mask="longName"), column_helper.Column('Short Name', ('name',), mask="name"), ] DEFAULT_COLUMNS = [ 'ID', 'Long Name', 'Short Name', ] @click.command() @click.argument('volume-id') @click.option('--sortby', help='Column to sort by', default='Long Name') @click.option('--columns', callback=column_helper.get_formatter(COLUMNS), help='Columns to display. Options: {0}'.format( ', '.join(column.name for column in COLUMNS)), default=','.join(DEFAULT_COLUMNS)) @environment.pass_env def cli(env, columns, sortby, volume_id): """List suitable replication datacenters for the given volume.""" file_storage_manager = SoftLayer.FileStorageManager(env.client) legal_centers = file_storage_manager.get_replication_locations( volume_id ) if not legal_centers: click.echo("No data centers compatible for replication.") else: table = formatting.KeyValueTable(columns.columns) table.sortby = sortby for legal_center in legal_centers: table.add_row([value or formatting.blank() for value in columns.row(legal_center)]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/file/replication/order.py000066400000000000000000000035401324365065500243200ustar00rootroot00000000000000"""Order a file storage replica volume.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions CONTEXT_SETTINGS = {'token_normalize_func': lambda x: x.upper()} @click.command(context_settings=CONTEXT_SETTINGS) @click.argument('volume_id') @click.option('--snapshot-schedule', '-s', help='Snapshot schedule to use for replication, ' '(INTERVAL | HOURLY | DAILY | WEEKLY)', required=True, type=click.Choice(['INTERVAL', 'HOURLY', 'DAILY', 'WEEKLY'])) @click.option('--location', '-l', help='Short name of the data center for the replicant ' '(e.g.: dal09)', required=True) @click.option('--tier', help='Endurance Storage Tier (IOPS per GB) of the primary' ' volume for which a replicant is ordered [optional]', type=click.Choice(['0.25', '2', '4', '10'])) @environment.pass_env def cli(env, volume_id, snapshot_schedule, location, tier): """Order a file storage replica volume.""" file_manager = SoftLayer.FileStorageManager(env.client) if tier is not None: tier = float(tier) try: order = file_manager.order_replicant_volume( volume_id, snapshot_schedule=snapshot_schedule, location=location, tier=tier, ) except ValueError as ex: raise exceptions.ArgumentError(str(ex)) if 'placedOrder' in order.keys(): click.echo("Order #{0} placed successfully!".format( order['placedOrder']['id'])) for item in order['placedOrder']['items']: click.echo(" > %s" % item['description']) else: click.echo("Order could not be placed! Please verify your options " + "and try again.") softlayer-python-5.4.2/SoftLayer/CLI/file/replication/partners.py000066400000000000000000000037261324365065500250510ustar00rootroot00000000000000"""List existing replicant volumes for a file volume.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import columns as column_helper from SoftLayer.CLI import environment from SoftLayer.CLI import formatting COLUMNS = [ column_helper.Column('ID', ('id',)), column_helper.Column('Username', ('username',), mask="username"), column_helper.Column('Account ID', ('accountId',), mask="accountId"), column_helper.Column('Capacity (GB)', ('capacityGb',), mask="capacityGb"), column_helper.Column('Hardware ID', ('hardwareId',), mask="hardwareId"), column_helper.Column('Guest ID', ('guestId',), mask="guestId"), column_helper.Column('Host ID', ('hostId',), mask="hostId"), ] # In-line comment to avoid similarity flag with block version DEFAULT_COLUMNS = [ 'ID', 'Username', 'Account ID', 'Capacity (GB)', 'Hardware ID', 'Guest ID', 'Host ID' ] @click.command() @click.argument('volume-id') @click.option('--sortby', help='Column to sort by', default='Username') @click.option('--columns', callback=column_helper.get_formatter(COLUMNS), help='Columns to display. Options: {0}'.format( ', '.join(column.name for column in COLUMNS)), default=','.join(DEFAULT_COLUMNS)) @environment.pass_env def cli(env, columns, sortby, volume_id): """List existing replicant volumes for a file volume.""" file_storage_manager = SoftLayer.FileStorageManager(env.client) legal_volumes = file_storage_manager.get_replication_partners( volume_id ) if not legal_volumes: click.echo("There are no replication partners for the given volume.") else: table = formatting.Table(columns.columns) table.sortby = sortby for legal_volume in legal_volumes: table.add_row([value or formatting.blank() for value in columns.row(legal_volume)]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/file/snapshot/000077500000000000000000000000001324365065500221575ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/CLI/file/snapshot/__init__.py000066400000000000000000000000451324365065500242670ustar00rootroot00000000000000"""File Storage Snapshot Control.""" softlayer-python-5.4.2/SoftLayer/CLI/file/snapshot/cancel.py000066400000000000000000000025511324365065500237610ustar00rootroot00000000000000"""Cancel a snapshot space subscription.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting @click.command() @click.argument('volume-id') @click.option('--reason', help="An optional reason for cancellation") @click.option('--immediate', is_flag=True, help="Cancels the snapshot space immediately instead " "of on the billing anniversary") @environment.pass_env def cli(env, volume_id, reason, immediate): """Cancel existing snapshot space for a given volume.""" file_storage_manager = SoftLayer.FileStorageManager(env.client) if not (env.skip_confirmations or formatting.no_going_back(volume_id)): raise exceptions.CLIAbort('Aborted') cancelled = file_storage_manager.cancel_snapshot_space( volume_id, reason, immediate) if cancelled: if immediate: click.echo('File volume with id %s has been marked' ' for immediate snapshot cancellation' % volume_id) else: click.echo('File volume with id %s has been marked' ' for snapshot cancellation' % volume_id) else: click.echo('Unable to cancel snapshot space for file volume %s' % volume_id) softlayer-python-5.4.2/SoftLayer/CLI/file/snapshot/create.py000066400000000000000000000015021324365065500237720ustar00rootroot00000000000000"""Create a file storage snapshot.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment @click.command() @click.argument('volume_id') @click.option('--notes', '-n', help='Notes to set on the new snapshot') @environment.pass_env def cli(env, volume_id, notes): """Creates a snapshot on a given volume""" file_storage_manager = SoftLayer.FileStorageManager(env.client) snapshot = file_storage_manager.create_snapshot(volume_id, notes=notes) if 'id' in snapshot: click.echo('New snapshot created with id: %s' % snapshot['id']) else: click.echo('Error occurred while creating snapshot.\n' 'Ensure volume is not failed over or in another ' 'state which prevents taking snapshots.') softlayer-python-5.4.2/SoftLayer/CLI/file/snapshot/delete.py000066400000000000000000000007711324365065500240000ustar00rootroot00000000000000"""Delete a file storage snapshot.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment @click.command() @click.argument('snapshot_id') @environment.pass_env def cli(env, snapshot_id): """Deletes a snapshot on a given volume""" file_storage_manager = SoftLayer.FileStorageManager(env.client) deleted = file_storage_manager.delete_snapshot(snapshot_id) if deleted: click.echo('Snapshot %s deleted' % snapshot_id) softlayer-python-5.4.2/SoftLayer/CLI/file/snapshot/disable.py000066400000000000000000000017171324365065500241420ustar00rootroot00000000000000"""Disable scheduled snapshots of a specific volume""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions @click.command() @click.argument('volume_id') @click.option('--schedule-type', help='Snapshot schedule [INTERVAL|HOURLY|DAILY|WEEKLY]', required=True) @environment.pass_env def cli(env, volume_id, schedule_type): """Disables snapshots on the specified schedule for a given volume""" if (schedule_type not in ['INTERVAL', 'HOURLY', 'DAILY', 'WEEKLY']): raise exceptions.CLIAbort( '--schedule_type must be INTERVAL, HOURLY, DAILY, or WEEKLY') file_manager = SoftLayer.FileStorageManager(env.client) disabled = file_manager.disable_snapshots(volume_id, schedule_type) if disabled: click.echo('%s snapshots have been disabled for volume %s' % (schedule_type, volume_id)) softlayer-python-5.4.2/SoftLayer/CLI/file/snapshot/enable.py000066400000000000000000000044601324365065500237630ustar00rootroot00000000000000# snapshot_enable.py """Create a file storage snapshot [ENABLE].""" import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions @click.command() @click.argument('volume_id') @click.option('--schedule-type', help='Snapshot schedule [INTERVAL|HOURLY|DAILY|WEEKLY]', required=True) @click.option('--retention-count', help='Number of snapshots to retain', required=True) @click.option('--minute', help='Minute of the day when snapshots should be taken', default=0) @click.option('--hour', help='Hour of the day when snapshots should be taken', default=0) @click.option('--day-of-week', help='Day of the week when snapshots should be taken', default='SUNDAY') @environment.pass_env def cli(env, volume_id, schedule_type, retention_count, minute, hour, day_of_week): """Enables snapshots for a given volume on the specified schedule""" file_manager = SoftLayer.FileStorageManager(env.client) valid_schedule_types = {'INTERVAL', 'HOURLY', 'DAILY', 'WEEKLY'} valid_days = {'SUNDAY', 'MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY', 'SATURDAY'} if schedule_type not in valid_schedule_types: raise exceptions.CLIAbort( '--schedule-type must be INTERVAL, HOURLY, ' + 'DAILY, or WEEKLY, not ' + schedule_type) if schedule_type == 'INTERVAL' and (minute < 30 or minute > 59): raise exceptions.CLIAbort( '--minute value must be between 30 and 59') if minute < 0 or minute > 59: raise exceptions.CLIAbort( '--minute value must be between 0 and 59') if hour < 0 or hour > 23: raise exceptions.CLIAbort( '--hour value must be between 0 and 23') if day_of_week not in valid_days: raise exceptions.CLIAbort( '--day_of_week value must be a valid day (ex: SUNDAY)') enabled = file_manager.enable_snapshots(volume_id, schedule_type, retention_count, minute, hour, day_of_week) if enabled: click.echo('%s snapshots have been enabled for volume %s' % (schedule_type, volume_id)) softlayer-python-5.4.2/SoftLayer/CLI/file/snapshot/list.py000066400000000000000000000030751324365065500235110ustar00rootroot00000000000000"""List file storage snapshots.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import columns as column_helper from SoftLayer.CLI import environment from SoftLayer.CLI import formatting COLUMNS = [ column_helper.Column('id', ('id',), mask='id'), column_helper.Column('name', ('notes',), mask='notes'), column_helper.Column('created', ('snapshotCreationTimestamp',), mask='snapshotCreationTimestamp'), column_helper.Column('size_bytes', ('snapshotSizeBytes',), mask='snapshotSizeBytes'), ] DEFAULT_COLUMNS = [ 'id', 'name', 'created', 'size_bytes' ] @click.command() @click.argument('volume_id') @click.option('--sortby', help='Column to sort by', default='created') @click.option('--columns', callback=column_helper.get_formatter(COLUMNS), help='Columns to display. Options: {0}'.format( ', '.join(column.name for column in COLUMNS)), default=','.join(DEFAULT_COLUMNS)) @environment.pass_env def cli(env, volume_id, sortby, columns): """List file storage snapshots.""" file_manager = SoftLayer.FileStorageManager(env.client) snapshots = file_manager.get_file_volume_snapshot_list( volume_id, mask=columns.mask() ) table = formatting.Table(columns.columns) table.sortby = sortby for snapshot in snapshots: table.add_row([value or formatting.blank() for value in columns.row(snapshot)]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/file/snapshot/order.py000066400000000000000000000034731324365065500236530ustar00rootroot00000000000000"""Order snapshot space for a file storage volume.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions @click.command() @click.argument('volume_id') @click.option('--capacity', type=int, help='Size of snapshot space to create in GB', required=True) @click.option('--tier', help='Endurance Storage Tier (IOPS per GB) of the file' ' volume for which space is ordered [optional, and only' ' valid for endurance storage volumes]', type=click.Choice(['0.25', '2', '4', '10'])) @click.option('--upgrade', type=bool, help='Flag to indicate that the order is an upgrade', default=False, is_flag=True) @environment.pass_env def cli(env, volume_id, capacity, tier, upgrade): """Order snapshot space for a file storage volume.""" file_manager = SoftLayer.FileStorageManager(env.client) if tier is not None: tier = float(tier) try: order = file_manager.order_snapshot_space( volume_id, capacity=capacity, tier=tier, upgrade=upgrade ) except ValueError as ex: raise exceptions.ArgumentError(str(ex)) if 'placedOrder' in order.keys(): click.echo("Order #{0} placed successfully!".format( order['placedOrder']['id'])) for item in order['placedOrder']['items']: click.echo(" > %s" % item['description']) if 'status' in order['placedOrder'].keys(): click.echo(" > Order status: %s" % order['placedOrder']['status']) else: click.echo("Order could not be placed! Please verify your options " + "and try again.") softlayer-python-5.4.2/SoftLayer/CLI/file/snapshot/restore.py000066400000000000000000000013411324365065500242130ustar00rootroot00000000000000"""Restore a file volume from a snapshot.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment @click.command() @click.argument('volume_id') @click.option('--snapshot-id', '-s', help='The id of the snapshot which will be used' ' to restore the block volume') @environment.pass_env def cli(env, volume_id, snapshot_id): """Restore file volume using a given snapshot""" file_manager = SoftLayer.FileStorageManager(env.client) success = file_manager.restore_from_snapshot(volume_id, snapshot_id) if success: click.echo('File volume %s is being restored using snapshot %s' % (volume_id, snapshot_id)) softlayer-python-5.4.2/SoftLayer/CLI/file/snapshot/schedule_list.py000066400000000000000000000045111324365065500253610ustar00rootroot00000000000000"""List scheduled snapshots of a specific volume""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting @click.command() @click.argument('volume_id') @environment.pass_env def cli(env, volume_id): """Lists snapshot schedules for a given volume""" file_manager = SoftLayer.FileStorageManager(env.client) snapshot_schedules = file_manager.list_volume_schedules(volume_id) table = formatting.Table(['id', 'active', 'type', 'replication', 'date_created', 'minute', 'hour', 'day', 'week', 'day_of_week', 'date_of_month', 'month_of_year', 'maximum_snapshots']) for schedule in snapshot_schedules: if 'REPLICATION' in schedule['type']['keyname']: replication = '*' else: replication = formatting.blank() file_schedule_type = schedule['type']['keyname'].replace('REPLICATION_', '') file_schedule_type = file_schedule_type.replace('SNAPSHOT_', '') property_list = ['MINUTE', 'HOUR', 'DAY', 'WEEK', 'DAY_OF_WEEK', 'DAY_OF_MONTH', 'MONTH_OF_YEAR', 'SNAPSHOT_LIMIT'] schedule_properties = [] for prop_key in property_list: item = formatting.blank() for schedule_property in schedule.get('properties', []): if schedule_property['type']['keyname'] == prop_key: if schedule_property['value'] == '-1': item = '*' else: item = schedule_property['value'] break schedule_properties.append(item) table_row = [ schedule['id'], '*' if schedule.get('active', '') else '', file_schedule_type, replication, schedule.get('createDate', '') ] table_row.extend(schedule_properties) table.add_row(table_row) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/firewall/000077500000000000000000000000001324365065500212065ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/CLI/firewall/__init__.py000066400000000000000000000007471324365065500233270ustar00rootroot00000000000000"""Firewalls.""" # :license: MIT, see LICENSE for more details. from SoftLayer.CLI import exceptions def parse_id(input_id): """Helper package to retrieve the actual IDs. :param input_id: the ID provided by the user :returns: A list of valid IDs """ key_value = input_id.split(':') if len(key_value) != 2: raise exceptions.CLIAbort( 'Invalid ID %s: ID should be of the form xxx:yyy' % input_id) return key_value[0], int(key_value[1]) softlayer-python-5.4.2/SoftLayer/CLI/firewall/add.py000066400000000000000000000036511324365065500223150ustar00rootroot00000000000000"""Create new firewall.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting @click.command() @click.argument('target') @click.option('--firewall-type', type=click.Choice(['vs', 'vlan', 'server']), help='Firewall type', required=True) @click.option('--ha', '--high-availability', is_flag=True, help='High available firewall option') @environment.pass_env def cli(env, target, firewall_type, high_availability): """Create new firewall. TARGET: Id of the server the firewall will protect """ mgr = SoftLayer.FirewallManager(env.client) if not env.skip_confirmations: if firewall_type == 'vlan': pkg = mgr.get_dedicated_package(ha_enabled=high_availability) elif firewall_type == 'vs': pkg = mgr.get_standard_package(target, is_virt=True) elif firewall_type == 'server': pkg = mgr.get_standard_package(target, is_virt=False) if not pkg: exceptions.CLIAbort( "Unable to add firewall - Is network public enabled?") env.out("******************") env.out("Product: %s" % pkg[0]['description']) env.out("Price: $%s monthly" % pkg[0]['prices'][0]['recurringFee']) env.out("******************") if not formatting.confirm("This action will incur charges on your " "account. Continue?"): raise exceptions.CLIAbort('Aborted.') if firewall_type == 'vlan': mgr.add_vlan_firewall(target, ha_enabled=high_availability) elif firewall_type == 'vs': mgr.add_standard_firewall(target, is_virt=True) elif firewall_type == 'server': mgr.add_standard_firewall(target, is_virt=False) env.fout("Firewall is being created!") softlayer-python-5.4.2/SoftLayer/CLI/firewall/cancel.py000066400000000000000000000020451324365065500230060ustar00rootroot00000000000000"""Cancels a firewall.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import firewall from SoftLayer.CLI import formatting @click.command() @click.argument('identifier') @environment.pass_env def cli(env, identifier): """Cancels a firewall.""" mgr = SoftLayer.FirewallManager(env.client) firewall_type, firewall_id = firewall.parse_id(identifier) if not (env.skip_confirmations or formatting.confirm("This action will cancel a firewall from your " "account. Continue?")): raise exceptions.CLIAbort('Aborted.') if firewall_type in ['vs', 'server']: mgr.cancel_firewall(firewall_id, dedicated=False) elif firewall_type == 'vlan': mgr.cancel_firewall(firewall_id, dedicated=True) else: raise exceptions.CLIAbort('Unknown firewall type: %s' % firewall_type) env.fout('Firewall with id %s is being cancelled!' % identifier) softlayer-python-5.4.2/SoftLayer/CLI/firewall/detail.py000066400000000000000000000027521324365065500230300ustar00rootroot00000000000000"""Detail firewall.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import firewall from SoftLayer.CLI import formatting from SoftLayer import utils @click.command() @click.argument('identifier') @environment.pass_env def cli(env, identifier): """Detail firewall.""" mgr = SoftLayer.FirewallManager(env.client) firewall_type, firewall_id = firewall.parse_id(identifier) if firewall_type == 'vlan': rules = mgr.get_dedicated_fwl_rules(firewall_id) else: rules = mgr.get_standard_fwl_rules(firewall_id) env.fout(get_rules_table(rules)) def get_rules_table(rules): """Helper to format the rules into a table. :param list rules: A list containing the rules of the firewall :returns: a formatted table of the firewall rules """ table = formatting.Table(['#', 'action', 'protocol', 'src_ip', 'src_mask', 'dest', 'dest_mask']) table.sortby = '#' for rule in rules: table.add_row([ rule['orderValue'], rule['action'], rule['protocol'], rule['sourceIpAddress'], utils.lookup(rule, 'sourceIpSubnetMask'), '%s:%s-%s' % (rule['destinationIpAddress'], rule['destinationPortRangeStart'], rule['destinationPortRangeEnd']), utils.lookup(rule, 'destinationIpSubnetMask')]) return table softlayer-python-5.4.2/SoftLayer/CLI/firewall/edit.py000066400000000000000000000147651324365065500225220ustar00rootroot00000000000000"""List firewalls.""" # :license: MIT, see LICENSE for more details. import os import subprocess import tempfile import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import firewall from SoftLayer.CLI import formatting DELIMITER = "=========================================\n" def parse_rules(content=None): """Helper to parse the input from the user into a list of rules. :param string content: the content of the editor :returns: a list of rules """ rules = content.split(DELIMITER) parsed_rules = list() order = 1 for rule in rules: if rule.strip() == '': continue parsed_rule = {} lines = rule.split("\n") parsed_rule['orderValue'] = order order += 1 for line in lines: if line.strip() == '': continue key_value = line.strip().split(':') key = key_value[0].strip() value = key_value[1].strip() if key == 'action': parsed_rule['action'] = value elif key == 'protocol': parsed_rule['protocol'] = value elif key == 'source_ip_address': parsed_rule['sourceIpAddress'] = value elif key == 'source_ip_subnet_mask': parsed_rule['sourceIpSubnetMask'] = value elif key == 'destination_ip_address': parsed_rule['destinationIpAddress'] = value elif key == 'destination_ip_subnet_mask': parsed_rule['destinationIpSubnetMask'] = value elif key == 'destination_port_range_start': parsed_rule['destinationPortRangeStart'] = int(value) elif key == 'destination_port_range_end': parsed_rule['destinationPortRangeEnd'] = int(value) elif key == 'version': parsed_rule['version'] = int(value) parsed_rules.append(parsed_rule) return parsed_rules def open_editor(rules=None, content=None): """Helper to open an editor for editing the firewall rules. This method takes two parameters, if content is provided, that means that submitting the rules failed and we are allowing the user to re-edit what they provided. If content is not provided, the rules retrieved from the firewall will be displayed to the user. :param list rules: A list containing the rules of the firewall :param string content: the content that the user provided in the editor :returns: a formatted string that get be pushed into the editor """ # Let's get the default EDITOR of the environment, # use nano if none is specified editor = os.environ.get('EDITOR', 'nano') with tempfile.NamedTemporaryFile(suffix=".tmp") as tfile: if content: # if content is provided, just display it as is tfile.write(content) tfile.flush() subprocess.call([editor, tfile.name]) tfile.seek(0) data = tfile.read() return data if not rules: # if the firewall has no rules, provide a template tfile.write(DELIMITER) tfile.write(get_formatted_rule()) else: # if the firewall has rules, display those to the user for rule in rules: tfile.write(DELIMITER) tfile.write(get_formatted_rule(rule)) tfile.write(DELIMITER) tfile.flush() subprocess.call([editor, tfile.name]) tfile.seek(0) data = tfile.read() return data def get_formatted_rule(rule=None): """Helper to format the rule into a user friendly format. :param dict rule: A dict containing one rule of the firewall :returns: a formatted string that get be pushed into the editor """ rule = rule or {} return ('action: %s\n' 'protocol: %s\n' 'source_ip_address: %s\n' 'source_ip_subnet_mask: %s\n' 'destination_ip_address: %s\n' 'destination_ip_subnet_mask: %s\n' 'destination_port_range_start: %s\n' 'destination_port_range_end: %s\n' 'version: %s\n' % (rule.get('action', 'permit'), rule.get('protocol', 'tcp'), rule.get('sourceIpAddress', 'any'), rule.get('sourceIpSubnetMask', '255.255.255.255'), rule.get('destinationIpAddress', 'any'), rule.get('destinationIpSubnetMask', '255.255.255.255'), rule.get('destinationPortRangeStart', 1), rule.get('destinationPortRangeEnd', 1), rule.get('version', 4))) @click.command() @click.argument('identifier') @environment.pass_env def cli(env, identifier): """Edit firewall rules.""" mgr = SoftLayer.FirewallManager(env.client) firewall_type, firewall_id = firewall.parse_id(identifier) if firewall_type == 'vlan': orig_rules = mgr.get_dedicated_fwl_rules(firewall_id) else: orig_rules = mgr.get_standard_fwl_rules(firewall_id) # open an editor for the user to enter their rules edited_rules = open_editor(rules=orig_rules) env.out(edited_rules) if formatting.confirm("Would you like to submit the rules. " "Continue?"): while True: try: rules = parse_rules(edited_rules) if firewall_type == 'vlan': rules = mgr.edit_dedicated_fwl_rules(firewall_id, rules) else: rules = mgr.edit_standard_fwl_rules(firewall_id, rules) break except (SoftLayer.SoftLayerError, ValueError) as error: env.out("Unexpected error({%s})" % (error)) if formatting.confirm("Would you like to continue editing " "the rules. Continue?"): edited_rules = open_editor(content=edited_rules) env.out(edited_rules) if formatting.confirm("Would you like to submit the " "rules. Continue?"): continue else: raise exceptions.CLIAbort('Aborted.') else: raise exceptions.CLIAbort('Aborted.') env.fout('Firewall updated!') else: raise exceptions.CLIAbort('Aborted.') softlayer-python-5.4.2/SoftLayer/CLI/firewall/list.py000066400000000000000000000050061324365065500225340ustar00rootroot00000000000000"""List firewalls.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer import utils @click.command() @environment.pass_env def cli(env): """List firewalls.""" mgr = SoftLayer.FirewallManager(env.client) table = formatting.Table(['firewall id', 'type', 'features', 'server/vlan id']) fwvlans = mgr.get_firewalls() dedicated_firewalls = [firewall for firewall in fwvlans if firewall['dedicatedFirewallFlag']] for vlan in dedicated_firewalls: features = [] if vlan['highAvailabilityFirewallFlag']: features.append('HA') if features: feature_list = formatting.listing(features, separator=',') else: feature_list = formatting.blank() table.add_row([ 'vlan:%s' % vlan['networkVlanFirewall']['id'], 'VLAN - dedicated', feature_list, vlan['id'] ]) shared_vlan = [firewall for firewall in fwvlans if not firewall['dedicatedFirewallFlag']] for vlan in shared_vlan: vs_firewalls = [guest for guest in vlan['firewallGuestNetworkComponents'] if has_firewall_component(guest)] for firewall in vs_firewalls: table.add_row([ 'vs:%s' % firewall['id'], 'Virtual Server - standard', '-', firewall['guestNetworkComponent']['guest']['id'] ]) server_firewalls = [server for server in vlan['firewallNetworkComponents'] if has_firewall_component(server)] for firewall in server_firewalls: table.add_row([ 'server:%s' % firewall['id'], 'Server - standard', '-', utils.lookup(firewall, 'networkComponent', 'downlinkComponent', 'hardwareId') ]) env.fout(table) def has_firewall_component(server): """Helper to determine whether or not a server has a firewall. :param dict server: A dictionary representing a server :returns: True if the Server has a firewall. """ if server['status'] != 'no_edit': return True return False softlayer-python-5.4.2/SoftLayer/CLI/formatting.py000066400000000000000000000303221324365065500221250ustar00rootroot00000000000000""" SoftLayer.formatting ~~~~~~~~~~~~~~~~~~~~ Provider classes and helper functions to display output onto a command-line. :license: MIT, see LICENSE for more details. """ # pylint: disable=E0202, consider-merging-isinstance, arguments-differ, keyword-arg-before-vararg import collections import json import os import click import prettytable from SoftLayer.CLI import exceptions from SoftLayer import utils FALSE_VALUES = ['0', 'false', 'FALSE', 'no', 'False'] def format_output(data, fmt='table'): # pylint: disable=R0911,R0912 """Given some data, will format it for console output. :param data: One of: String, Table, FormattedItem, List, Tuple, SequentialOutput :param string fmt (optional): One of: table, raw, json, python """ if isinstance(data, utils.string_types): if fmt in ('json', 'jsonraw'): return json.dumps(data) return data # responds to .prettytable() if hasattr(data, 'prettytable'): if fmt == 'table': return str(format_prettytable(data)) elif fmt == 'raw': return str(format_no_tty(data)) # responds to .to_python() if hasattr(data, 'to_python'): if fmt == 'json': return json.dumps( format_output(data, fmt='python'), indent=4, cls=CLIJSONEncoder) elif fmt == 'jsonraw': return json.dumps(format_output(data, fmt='python'), cls=CLIJSONEncoder) elif fmt == 'python': return data.to_python() # responds to .formatted if hasattr(data, 'formatted'): if fmt == 'table': return data.formatted # responds to .separator if hasattr(data, 'separator'): output = [format_output(d, fmt=fmt) for d in data if d] return str(SequentialOutput(data.separator, output)) # is iterable if isinstance(data, list) or isinstance(data, tuple): output = [format_output(d, fmt=fmt) for d in data] if fmt == 'python': return output return format_output(listing(output, separator=os.linesep)) # fallback, convert this odd object to a string return data def format_prettytable(table): """Converts SoftLayer.CLI.formatting.Table instance to a prettytable.""" for i, row in enumerate(table.rows): for j, item in enumerate(row): table.rows[i][j] = format_output(item) ptable = table.prettytable() ptable.hrules = prettytable.FRAME ptable.horizontal_char = '.' ptable.vertical_char = ':' ptable.junction_char = ':' return ptable def format_no_tty(table): """Converts SoftLayer.CLI.formatting.Table instance to a prettytable.""" for i, row in enumerate(table.rows): for j, item in enumerate(row): table.rows[i][j] = format_output(item, fmt='raw') ptable = table.prettytable() for col in table.columns: ptable.align[col] = 'l' ptable.hrules = prettytable.NONE ptable.border = False ptable.header = False ptable.left_padding_width = 0 ptable.right_padding_width = 2 return ptable def mb_to_gb(megabytes): """Converts number of megabytes to a FormattedItem in gigabytes. :param int megabytes: number of megabytes """ return FormattedItem(megabytes, "%dG" % (float(megabytes) / 1024)) def b_to_gb(_bytes): """Converts number of bytes to a FormattedItem in gigabytes. :param int _bytes: number of bytes """ return FormattedItem(_bytes, "%.2fG" % (float(_bytes) / 1024 / 1024 / 1024)) def gb(gigabytes): # pylint: disable=C0103 """Converts number of gigabytes to a FormattedItem in gigabytes. :param int gigabytes: number of gigabytes """ return FormattedItem(int(float(gigabytes)) * 1024, "%dG" % int(float(gigabytes))) def blank(): """Returns a blank FormattedItem.""" return FormattedItem(None, '-') def listing(items, separator=','): """Given an iterable return a FormattedItem which display the list of items :param items: An iterable that outputs strings :param string separator: the separator to use """ return SequentialOutput(separator, items) def active_txn(item): """Returns a FormattedItem describing the active transaction on a object. If no active transaction is running, returns a blank FormattedItem. :param item: An object capable of having an active transaction """ return transaction_status(utils.lookup(item, 'activeTransaction')) def transaction_status(transaction): """Returns a FormattedItem describing the given transaction. :param item: An object capable of having an active transaction """ if not transaction or not transaction.get('transactionStatus'): return blank() return FormattedItem( transaction['transactionStatus'].get('name'), transaction['transactionStatus'].get('friendlyName')) def tags(tag_references): """Returns a formatted list of tags.""" if not tag_references: return blank() tag_row = [] for tag_detail in tag_references: tag = utils.lookup(tag_detail, 'tag', 'name') if tag is not None: tag_row.append(tag) return listing(tag_row, separator=', ') def confirm(prompt_str, default=False): """Show a confirmation prompt to a command-line user. :param string prompt_str: prompt to give to the user :param bool default: Default value to True or False """ if default: default_str = 'y' prompt = '%s [Y/n]' % prompt_str else: default_str = 'n' prompt = '%s [y/N]' % prompt_str ans = click.prompt(prompt, default=default_str, show_default=False) if ans.lower() in ('y', 'yes', 'yeah', 'yup', 'yolo'): return True return False def no_going_back(confirmation): """Show a confirmation to a user. :param confirmation str: the string the user has to enter in order to confirm their action. """ if not confirmation: confirmation = 'yes' prompt = ('This action cannot be undone! Type "%s" or press Enter ' 'to abort' % confirmation) ans = click.prompt(prompt, default='', show_default=False) if ans.lower() == str(confirmation): return True return False class SequentialOutput(list): """SequentialOutput is used for outputting sequential items. The purpose is to de-couple the separator from the output itself. :param separator str: string to use as a default separator """ def __init__(self, separator=os.linesep, *args, **kwargs): self.separator = separator super(SequentialOutput, self).__init__(*args, **kwargs) def to_python(self): """returns itself, since it itself is a list.""" return self def __str__(self): return self.separator.join(str(x) for x in self) class CLIJSONEncoder(json.JSONEncoder): """A JSON encoder which is able to use a .to_python() method on objects.""" def default(self, obj): """Encode object if it implements to_python().""" if hasattr(obj, 'to_python'): return obj.to_python() return super(CLIJSONEncoder, self).default(obj) class Table(object): """A Table structure used for output. :param list columns: a list of column names """ def __init__(self, columns): duplicated_cols = [col for col, count in collections.Counter(columns).items() if count > 1] if len(duplicated_cols) > 0: raise exceptions.CLIAbort("Duplicated columns are not allowed: %s" % ','.join(duplicated_cols)) self.columns = columns self.rows = [] self.align = {} self.sortby = None def add_row(self, row): """Add a row to the table. :param list row: the row of string to be added """ self.rows.append(row) def to_python(self): """Decode this Table object to standard Python types.""" # Adding rows items = [] for row in self.rows: formatted_row = [_format_python_value(v) for v in row] items.append(dict(zip(self.columns, formatted_row))) return items def prettytable(self): """Returns a new prettytable instance.""" table = prettytable.PrettyTable(self.columns) if self.sortby: if self.sortby in self.columns: table.sortby = self.sortby else: msg = "Column (%s) doesn't exist to sort by" % self.sortby raise exceptions.CLIAbort(msg) for a_col, alignment in self.align.items(): table.align[a_col] = alignment # Adding rows for row in self.rows: table.add_row(row) return table class KeyValueTable(Table): """A table that is oriented towards key-value pairs.""" def to_python(self): """Decode this KeyValueTable object to standard Python types.""" mapping = {} for row in self.rows: mapping[row[0]] = _format_python_value(row[1]) return mapping class FormattedItem(object): """This is an object that can be displayed as a human readable and raw. :param original: raw (machine-readable) value :param string formatted: human-readable value """ def __init__(self, original, formatted=None): self.original = original if formatted is not None: self.formatted = formatted else: self.formatted = self.original def to_python(self): """returns the original (raw) value.""" return self.original def __str__(self): """returns the formatted value.""" # If the original value is None, represent this as 'NULL' if self.original is None: return 'NULL' try: return str(self.original) except UnicodeError: return 'invalid' def __repr__(self): return 'FormattedItem(%r, %r)' % (self.original, self.formatted) # Implement sorting methods. # NOTE(kmcdonald): functools.total_ordering could be used once support for # Python 2.6 is dropped def __eq__(self, other): return self.original == getattr(other, 'original', other) def __lt__(self, other): if self.original is None: return True other_val = getattr(other, 'original', other) if other_val is None: return False return self.original < other_val def __gt__(self, other): return not (self < other or self == other) def __le__(self, other): return self < other or self == other def __ge__(self, other): return self >= other def _format_python_value(value): """If the value has to_python() defined then return that.""" if hasattr(value, 'to_python'): return value.to_python() return value def iter_to_table(value): """Convert raw API responses to response tables.""" if isinstance(value, list): return _format_list(value) if isinstance(value, dict): return _format_dict(value) return value def _format_dict(result): """Format dictionary responses into key-value table.""" table = KeyValueTable(['name', 'value']) table.align['name'] = 'r' table.align['value'] = 'l' for key, value in result.items(): value = iter_to_table(value) table.add_row([key, value]) return table def _format_list(result): """Format list responses into a table.""" if not result: return result if isinstance(result[0], dict): return _format_list_objects(result) table = Table(['value']) for item in result: table.add_row([iter_to_table(item)]) return table def _format_list_objects(result): """Format list of objects into a table.""" all_keys = set() for item in result: all_keys = all_keys.union(item.keys()) all_keys = sorted(all_keys) table = Table(all_keys) for item in result: values = [] for key in all_keys: value = iter_to_table(item.get(key)) values.append(value) table.add_row(values) return table softlayer-python-5.4.2/SoftLayer/CLI/globalip/000077500000000000000000000000001324365065500211725ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/CLI/globalip/__init__.py000066400000000000000000000001121324365065500232750ustar00rootroot00000000000000"""Global IP addresses.""" # :license: MIT, see LICENSE for more details. softlayer-python-5.4.2/SoftLayer/CLI/globalip/assign.py000066400000000000000000000011241324365065500230260ustar00rootroot00000000000000"""Assigns the global IP to a target.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import helpers @click.command() @click.argument('identifier') @click.argument('target') @environment.pass_env def cli(env, identifier, target): """Assigns the global IP to a target.""" mgr = SoftLayer.NetworkManager(env.client) global_ip_id = helpers.resolve_id(mgr.resolve_global_ip_ids, identifier, name='global ip') mgr.assign_global_ip(global_ip_id, target) softlayer-python-5.4.2/SoftLayer/CLI/globalip/cancel.py000066400000000000000000000013201324365065500227650ustar00rootroot00000000000000"""Cancel global IP.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting from SoftLayer.CLI import helpers @click.command() @click.argument('identifier') @environment.pass_env def cli(env, identifier): """Cancel global IP.""" mgr = SoftLayer.NetworkManager(env.client) global_ip_id = helpers.resolve_id(mgr.resolve_global_ip_ids, identifier, name='global ip') if not (env.skip_confirmations or formatting.no_going_back(global_ip_id)): raise exceptions.CLIAbort('Aborted') mgr.cancel_global_ip(global_ip_id) softlayer-python-5.4.2/SoftLayer/CLI/globalip/create.py000066400000000000000000000023471324365065500230150ustar00rootroot00000000000000"""Creates a global IP.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting @click.command() @click.option('--v6', '--ipv6', is_flag=True, help='Order a IPv6 IP') @click.option('--test', help='test order') @environment.pass_env def cli(env, ipv6, test): """Creates a global IP.""" mgr = SoftLayer.NetworkManager(env.client) version = 4 if ipv6: version = 6 if not (test or env.skip_confirmations): if not formatting.confirm("This action will incur charges on your " "account. Continue?"): raise exceptions.CLIAbort('Cancelling order.') result = mgr.add_global_ip(version=version, test_order=test) table = formatting.Table(['item', 'cost']) table.align['Item'] = 'r' table.align['cost'] = 'r' total = 0.0 for price in result['orderDetails']['prices']: total += float(price.get('recurringFee', 0.0)) rate = "%.2f" % float(price['recurringFee']) table.add_row([price['item']['description'], rate]) table.add_row(['Total monthly cost', "%.2f" % total]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/globalip/list.py000066400000000000000000000027101324365065500225170ustar00rootroot00000000000000"""List all global IPs.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting @click.command() @click.option('--ip-version', help='Display only IPv4', type=click.Choice(['v4', 'v6'])) @environment.pass_env def cli(env, ip_version): """List all global IPs.""" mgr = SoftLayer.NetworkManager(env.client) table = formatting.Table(['id', 'ip', 'assigned', 'target']) version = None if ip_version == 'v4': version = 4 elif ip_version == 'v6': version = 6 ips = mgr.list_global_ips(version=version) for ip_address in ips: assigned = 'No' target = 'None' if ip_address.get('destinationIpAddress'): dest = ip_address['destinationIpAddress'] assigned = 'Yes' target = dest['ipAddress'] virtual_guest = dest.get('virtualGuest') if virtual_guest: target += (' (%s)' % virtual_guest['fullyQualifiedDomainName']) elif ip_address['destinationIpAddress'].get('hardware'): target += (' (%s)' % dest['hardware']['fullyQualifiedDomainName']) table.add_row([ip_address['id'], ip_address['ipAddress']['ipAddress'], assigned, target]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/globalip/unassign.py000066400000000000000000000010601324365065500233700ustar00rootroot00000000000000"""Unassigns a global IP from a target.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import helpers @click.command() @click.argument('identifier') @environment.pass_env def cli(env, identifier): """Unassigns a global IP from a target.""" mgr = SoftLayer.NetworkManager(env.client) global_ip_id = helpers.resolve_id(mgr.resolve_global_ip_ids, identifier, name='global ip') mgr.unassign_global_ip(global_ip_id) softlayer-python-5.4.2/SoftLayer/CLI/hardware/000077500000000000000000000000001324365065500211765ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/CLI/hardware/__init__.py000066400000000000000000000000301324365065500233000ustar00rootroot00000000000000"""Hardware servers.""" softlayer-python-5.4.2/SoftLayer/CLI/hardware/cancel.py000066400000000000000000000021671324365065500230030ustar00rootroot00000000000000"""Cancel a dedicated server.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting from SoftLayer.CLI import helpers @click.command() @click.argument('identifier') @click.option('--immediate', is_flag=True, default=False, help="""Cancels the server immediately (instead of on the billing anniversary)""") @click.option('--comment', help="An optional comment to add to the cancellation ticket") @click.option('--reason', help="""An optional cancellation reason. See cancel-reasons for a list of available options""") @environment.pass_env def cli(env, identifier, immediate, comment, reason): """Cancel a dedicated server.""" mgr = SoftLayer.HardwareManager(env.client) hw_id = helpers.resolve_id(mgr.resolve_ids, identifier, 'hardware') if not (env.skip_confirmations or formatting.no_going_back(hw_id)): raise exceptions.CLIAbort('Aborted') mgr.cancel_hardware(hw_id, reason, comment, immediate) softlayer-python-5.4.2/SoftLayer/CLI/hardware/cancel_reasons.py000066400000000000000000000011161324365065500245260ustar00rootroot00000000000000"""Display a list of cancellation reasons.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting @click.command() @environment.pass_env def cli(env): """Display a list of cancellation reasons.""" table = formatting.Table(['Code', 'Reason']) table.align['Code'] = 'r' table.align['Reason'] = 'l' mgr = SoftLayer.HardwareManager(env.client) for code, reason in mgr.get_cancellation_reasons().items(): table.add_row([code, reason]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/hardware/create.py000066400000000000000000000115131324365065500230140ustar00rootroot00000000000000"""Order/create a dedicated server.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting from SoftLayer.CLI import helpers from SoftLayer.CLI import template @click.command(epilog="See 'slcli server create-options' for valid options.") @click.option('--hostname', '-H', help="Host portion of the FQDN", required=True, prompt=True) @click.option('--domain', '-D', help="Domain portion of the FQDN", required=True, prompt=True) @click.option('--size', '-s', help="Hardware size", required=True, prompt=True) @click.option('--os', '-o', help="OS install code", required=True, prompt=True) @click.option('--datacenter', '-d', help="Datacenter shortname", required=True, prompt=True) @click.option('--port-speed', type=click.INT, help="Port speeds", required=True, prompt=True) @click.option('--billing', type=click.Choice(['hourly', 'monthly']), default='hourly', show_default=True, help="Billing rate") @click.option('--postinstall', '-i', help="Post-install script to download") @helpers.multi_option('--key', '-k', help="SSH keys to add to the root user") @click.option('--no-public', is_flag=True, help="Private network only") @helpers.multi_option('--extra', '-e', help="Extra options") @click.option('--test', is_flag=True, help="Do not actually create the server") @click.option('--template', '-t', is_eager=True, callback=template.TemplateCallback(list_args=['key']), help="A template file that defaults the command-line options", type=click.Path(exists=True, readable=True, resolve_path=True)) @click.option('--export', type=click.Path(writable=True, resolve_path=True), help="Exports options to a template file") @click.option('--wait', type=click.INT, help="Wait until the server is finished provisioning for up to " "X seconds before returning") @environment.pass_env def cli(env, **args): """Order/create a dedicated server.""" mgr = SoftLayer.HardwareManager(env.client) # Get the SSH keys ssh_keys = [] for key in args.get('key'): resolver = SoftLayer.SshKeyManager(env.client).resolve_ids key_id = helpers.resolve_id(resolver, key, 'SshKey') ssh_keys.append(key_id) order = { 'hostname': args['hostname'], 'domain': args['domain'], 'size': args['size'], 'location': args.get('datacenter'), 'ssh_keys': ssh_keys, 'post_uri': args.get('postinstall'), 'os': args['os'], 'hourly': args.get('billing') == 'hourly', 'port_speed': args.get('port_speed'), 'no_public': args.get('no_public') or False, 'extras': args.get('extra'), } # Do not create hardware server with --test or --export do_create = not (args['export'] or args['test']) output = None if args.get('test'): result = mgr.verify_order(**order) table = formatting.Table(['Item', 'cost']) table.align['Item'] = 'r' table.align['cost'] = 'r' total = 0.0 for price in result['prices']: total += float(price.get('recurringFee', 0.0)) rate = "%.2f" % float(price['recurringFee']) table.add_row([price['item']['description'], rate]) table.add_row(['Total monthly cost', "%.2f" % total]) output = [] output.append(table) output.append(formatting.FormattedItem( '', ' -- ! Prices reflected here are retail and do not ' 'take account level discounts and are not guaranteed.')) if args['export']: export_file = args.pop('export') template.export_to_template(export_file, args, exclude=['wait', 'test']) env.fout('Successfully exported options to a template file.') return if do_create: if not (env.skip_confirmations or formatting.confirm( "This action will incur charges on your account. " "Continue?")): raise exceptions.CLIAbort('Aborting dedicated server order.') result = mgr.place_order(**order) table = formatting.KeyValueTable(['name', 'value']) table.align['name'] = 'r' table.align['value'] = 'l' table.add_row(['id', result['orderId']]) table.add_row(['created', result['orderDate']]) output = table env.fout(output) softlayer-python-5.4.2/SoftLayer/CLI/hardware/create_options.py000066400000000000000000000033431324365065500245710ustar00rootroot00000000000000"""Server order options for a given chassis.""" # :license: MIT, see LICENSE for more details. import click from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.managers import hardware @click.command() @environment.pass_env def cli(env): """Server order options for a given chassis.""" hardware_manager = hardware.HardwareManager(env.client) options = hardware_manager.get_create_options() tables = [] # Datacenters dc_table = formatting.Table(['datacenter', 'value']) dc_table.sortby = 'value' for location in options['locations']: dc_table.add_row([location['name'], location['key']]) tables.append(dc_table) # Presets preset_table = formatting.Table(['size', 'value']) preset_table.sortby = 'value' for size in options['sizes']: preset_table.add_row([size['name'], size['key']]) tables.append(preset_table) # Operating systems os_table = formatting.Table(['operating_system', 'value']) os_table.sortby = 'value' for operating_system in options['operating_systems']: os_table.add_row([operating_system['name'], operating_system['key']]) tables.append(os_table) # Port speed port_speed_table = formatting.Table(['port_speed', 'value']) port_speed_table.sortby = 'value' for speed in options['port_speeds']: port_speed_table.add_row([speed['name'], speed['key']]) tables.append(port_speed_table) # Extras extras_table = formatting.Table(['extras', 'value']) extras_table.sortby = 'value' for extra in options['extras']: extras_table.add_row([extra['name'], extra['key']]) tables.append(extras_table) env.fout(formatting.listing(tables, separator='\n')) softlayer-python-5.4.2/SoftLayer/CLI/hardware/credentials.py000066400000000000000000000014431324365065500240470ustar00rootroot00000000000000"""List server credentials.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.CLI import helpers @click.command() @click.argument('identifier') @environment.pass_env def cli(env, identifier): """List server credentials.""" manager = SoftLayer.HardwareManager(env.client) hardware_id = helpers.resolve_id(manager.resolve_ids, identifier, 'hardware') instance = manager.get_hardware(hardware_id) table = formatting.Table(['username', 'password']) for item in instance['operatingSystem']['passwords']: table.add_row([item['username'], item['password']]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/hardware/detail.py000066400000000000000000000072161324365065500230200ustar00rootroot00000000000000"""Get details for a hardware device.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.CLI import helpers from SoftLayer import utils @click.command() @click.argument('identifier') @click.option('--passwords', is_flag=True, help='Show passwords (check over your shoulder!)') @click.option('--price', is_flag=True, help='Show associated prices') @environment.pass_env def cli(env, identifier, passwords, price): """Get details for a hardware device.""" hardware = SoftLayer.HardwareManager(env.client) table = formatting.KeyValueTable(['name', 'value']) table.align['name'] = 'r' table.align['value'] = 'l' hardware_id = helpers.resolve_id(hardware.resolve_ids, identifier, 'hardware') result = hardware.get_hardware(hardware_id) result = utils.NestedDict(result) operating_system = utils.lookup(result, 'operatingSystem', 'softwareLicense', 'softwareDescription') or {} memory = formatting.gb(result.get('memoryCapacity', 0)) owner = None if utils.lookup(result, 'billingItem') != []: owner = utils.lookup(result, 'billingItem', 'orderItem', 'order', 'userRecord', 'username') table.add_row(['id', result['id']]) table.add_row(['guid', result['globalIdentifier'] or formatting.blank()]) table.add_row(['hostname', result['hostname']]) table.add_row(['domain', result['domain']]) table.add_row(['fqdn', result['fullyQualifiedDomainName']]) table.add_row(['status', result['hardwareStatus']['status']]) table.add_row(['datacenter', result['datacenter']['name'] or formatting.blank()]) table.add_row(['cores', result['processorPhysicalCoreAmount']]) table.add_row(['memory', memory]) table.add_row(['public_ip', result['primaryIpAddress'] or formatting.blank()]) table.add_row(['private_ip', result['primaryBackendIpAddress'] or formatting.blank()]) table.add_row(['ipmi_ip', result['networkManagementIpAddress'] or formatting.blank()]) table.add_row(['os', operating_system.get('name') or formatting.blank()]) table.add_row(['os_version', operating_system.get('version') or formatting.blank()]) table.add_row(['created', result['provisionDate'] or formatting.blank()]) table.add_row(['owner', owner or formatting.blank()]) vlan_table = formatting.Table(['type', 'number', 'id']) for vlan in result['networkVlans']: vlan_table.add_row([vlan['networkSpace'], vlan['vlanNumber'], vlan['id']]) table.add_row(['vlans', vlan_table]) if result.get('notes'): table.add_row(['notes', result['notes']]) if price: total_price = utils.lookup(result, 'billingItem', 'nextInvoiceTotalRecurringAmount') or 0 price_table = formatting.Table(['Item', 'Recurring Price']) price_table.add_row(['Total', total_price]) for item in utils.lookup(result, 'billingItem', 'children') or []: price_table.add_row([item['description'], item['nextInvoiceTotalRecurringAmount']]) table.add_row(['prices', price_table]) if passwords: pass_table = formatting.Table(['username', 'password']) for item in result['operatingSystem']['passwords']: pass_table.add_row([item['username'], item['password']]) table.add_row(['users', pass_table]) pass_table = formatting.Table(['ipmi_username', 'password']) for item in result['remoteManagementAccounts']: pass_table.add_row([item['username'], item['password']]) table.add_row(['remote users', pass_table]) table.add_row(['tags', formatting.tags(result['tagReferences'])]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/hardware/edit.py000066400000000000000000000040771324365065500225050ustar00rootroot00000000000000"""Edit hardware details.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import helpers @click.command() @click.argument('identifier') @click.option('--domain', '-D', help="Domain portion of the FQDN") @click.option('--userfile', '-F', help="Read userdata from file", type=click.Path(exists=True, readable=True, resolve_path=True)) @click.option('--tag', '-g', multiple=True, help="Tags to set or empty string to remove all") @click.option('--hostname', '-H', help="Host portion of the FQDN") @click.option('--userdata', '-u', help="User defined metadata string") @click.option('--public-speed', help="Public port speed.", default=None, type=click.Choice(['0', '10', '100', '1000', '10000'])) @click.option('--private-speed', help="Private port speed.", default=None, type=click.Choice(['0', '10', '100', '1000', '10000'])) @environment.pass_env def cli(env, identifier, domain, userfile, tag, hostname, userdata, public_speed, private_speed): """Edit hardware details.""" if userdata and userfile: raise exceptions.ArgumentError( '[-u | --userdata] not allowed with [-F | --userfile]') data = { 'hostname': hostname, 'domain': domain, } if userdata: data['userdata'] = userdata elif userfile: with open(userfile, 'r') as userfile_obj: data['userdata'] = userfile_obj.read() if tag: data['tags'] = ','.join(tag) mgr = SoftLayer.HardwareManager(env.client) hw_id = helpers.resolve_id(mgr.resolve_ids, identifier, 'hardware') if not mgr.edit(hw_id, **data): raise exceptions.CLIAbort("Failed to update hardware") if public_speed is not None: mgr.change_port_speed(hw_id, True, int(public_speed)) if private_speed is not None: mgr.change_port_speed(hw_id, False, int(private_speed)) softlayer-python-5.4.2/SoftLayer/CLI/hardware/list.py000066400000000000000000000056761324365065500225410ustar00rootroot00000000000000"""List hardware servers.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import columns as column_helper from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.CLI import helpers # pylint: disable=unnecessary-lambda COLUMNS = [ column_helper.Column('guid', ('globalIdentifier',)), column_helper.Column('primary_ip', ('primaryIpAddress',)), column_helper.Column('backend_ip', ('primaryBackendIpAddress',)), column_helper.Column('datacenter', ('datacenter', 'name')), column_helper.Column( 'action', lambda server: formatting.active_txn(server), mask=''' mask(SoftLayer_Hardware_Server)[activeTransaction[ id,transactionStatus[name,friendlyName] ]]'''), column_helper.Column('power_state', ('powerState', 'name')), column_helper.Column( 'created_by', ('billingItem', 'orderItem', 'order', 'userRecord', 'username')), column_helper.Column( 'tags', lambda server: formatting.tags(server.get('tagReferences')), mask="tagReferences.tag.name"), ] DEFAULT_COLUMNS = [ 'id', 'hostname', 'primary_ip', 'backend_ip', 'datacenter', 'action', ] @click.command() @click.option('--cpu', '-c', help='Filter by number of CPU cores') @click.option('--domain', '-D', help='Filter by domain') @click.option('--datacenter', '-d', help='Filter by datacenter') @click.option('--hostname', '-H', help='Filter by hostname') @click.option('--memory', '-m', help='Filter by memory in gigabytes') @click.option('--network', '-n', help='Filter by network port speed in Mbps') @helpers.multi_option('--tag', help='Filter by tags') @click.option('--sortby', help='Column to sort by', default='hostname', show_default=True) @click.option('--columns', callback=column_helper.get_formatter(COLUMNS), help='Columns to display. [options: %s]' % ', '.join(column.name for column in COLUMNS), default=','.join(DEFAULT_COLUMNS), show_default=True) @environment.pass_env def cli(env, sortby, cpu, domain, datacenter, hostname, memory, network, tag, columns): """List hardware servers.""" manager = SoftLayer.HardwareManager(env.client) servers = manager.list_hardware(hostname=hostname, domain=domain, cpus=cpu, memory=memory, datacenter=datacenter, nic_speed=network, tags=tag, mask=columns.mask()) table = formatting.Table(columns.columns) table.sortby = sortby for server in servers: table.add_row([value or formatting.blank() for value in columns.row(server)]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/hardware/power.py000066400000000000000000000055761324365065500227210ustar00rootroot00000000000000"""Power commands.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting from SoftLayer.CLI import helpers @click.command() @click.argument('identifier') @environment.pass_env def power_off(env, identifier): """Power off an active server.""" mgr = SoftLayer.HardwareManager(env.client) hw_id = helpers.resolve_id(mgr.resolve_ids, identifier, 'hardware') if not (env.skip_confirmations or formatting.confirm('This will power off the server with id %s ' 'Continue?' % hw_id)): raise exceptions.CLIAbort('Aborted.') env.client['Hardware_Server'].powerOff(id=hw_id) @click.command() @click.argument('identifier') @click.option('--hard/--soft', default=None, help="Perform a hard or soft reboot") @environment.pass_env def reboot(env, identifier, hard): """Reboot an active server.""" hardware_server = env.client['Hardware_Server'] mgr = SoftLayer.HardwareManager(env.client) hw_id = helpers.resolve_id(mgr.resolve_ids, identifier, 'hardware') if not (env.skip_confirmations or formatting.confirm('This will power off the server with id %s. ' 'Continue?' % hw_id)): raise exceptions.CLIAbort('Aborted.') if hard is True: hardware_server.rebootHard(id=hw_id) elif hard is False: hardware_server.rebootSoft(id=hw_id) else: hardware_server.rebootDefault(id=hw_id) @click.command() @click.argument('identifier') @environment.pass_env def power_on(env, identifier): """Power on a server.""" mgr = SoftLayer.HardwareManager(env.client) hw_id = helpers.resolve_id(mgr.resolve_ids, identifier, 'hardware') env.client['Hardware_Server'].powerOn(id=hw_id) @click.command() @click.argument('identifier') @environment.pass_env def power_cycle(env, identifier): """Power cycle a server.""" mgr = SoftLayer.HardwareManager(env.client) hw_id = helpers.resolve_id(mgr.resolve_ids, identifier, 'hardware') if not (env.skip_confirmations or formatting.confirm('This will power off the server with id %s. ' 'Continue?' % hw_id)): raise exceptions.CLIAbort('Aborted.') env.client['Hardware_Server'].powerCycle(id=hw_id) @click.command() @click.argument('identifier') @environment.pass_env def rescue(env, identifier): """Reboot server into a rescue image.""" mgr = SoftLayer.HardwareManager(env.client) hw_id = helpers.resolve_id(mgr.resolve_ids, identifier, 'hardware') if not (env.skip_confirmations or formatting.confirm("This action will reboot this server. Continue?")): raise exceptions.CLIAbort('Aborted') env.client['Hardware_Server'].bootToRescueLayer(id=hw_id) softlayer-python-5.4.2/SoftLayer/CLI/hardware/ready.py000066400000000000000000000014561324365065500226620ustar00rootroot00000000000000"""Check if a server is ready.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import helpers @click.command() @click.argument('identifier') @click.option('--wait', default=0, show_default=True, type=click.INT, help="Seconds to wait") @environment.pass_env def cli(env, identifier, wait): """Check if a server is ready.""" compute = SoftLayer.HardwareManager(env.client) compute_id = helpers.resolve_id(compute.resolve_ids, identifier, 'hardware') ready = compute.wait_for_ready(compute_id, wait) if ready: env.fout("READY") else: raise exceptions.CLIAbort("Server %s not ready" % compute_id) softlayer-python-5.4.2/SoftLayer/CLI/hardware/reload.py000066400000000000000000000024271324365065500230230ustar00rootroot00000000000000"""Reload operating system on a server.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting from SoftLayer.CLI import helpers @click.command() @click.argument('identifier') @click.option('--postinstall', '-i', help=("Post-install script to download " "(Only HTTPS executes, HTTP leaves file in /root")) @helpers.multi_option('--key', '-k', help="SSH keys to add to the root user") @environment.pass_env def cli(env, identifier, postinstall, key): """Reload operating system on a server.""" hardware = SoftLayer.HardwareManager(env.client) hardware_id = helpers.resolve_id(hardware.resolve_ids, identifier, 'hardware') key_list = [] if key: for single_key in key: resolver = SoftLayer.SshKeyManager(env.client).resolve_ids key_id = helpers.resolve_id(resolver, single_key, 'SshKey') key_list.append(key_id) if not (env.skip_confirmations or formatting.no_going_back(hardware_id)): raise exceptions.CLIAbort('Aborted') hardware.reload(hardware_id, postinstall, key_list) softlayer-python-5.4.2/SoftLayer/CLI/hardware/update_firmware.py000066400000000000000000000014061324365065500247270ustar00rootroot00000000000000"""Update firmware.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting from SoftLayer.CLI import helpers @click.command() @click.argument('identifier') @environment.pass_env def cli(env, identifier): """Update server firmware.""" mgr = SoftLayer.HardwareManager(env.client) hw_id = helpers.resolve_id(mgr.resolve_ids, identifier, 'hardware') if not (env.skip_confirmations or formatting.confirm('This will power off the server with id %s and ' 'update device firmware. Continue?' % hw_id)): raise exceptions.CLIAbort('Aborted.') mgr.update_firmware(hw_id) softlayer-python-5.4.2/SoftLayer/CLI/helpers.py000066400000000000000000000025561324365065500214250ustar00rootroot00000000000000""" SoftLayer.CLI.helpers ~~~~~~~~~~~~~~~~~~~~~ Helpers to be used in CLI modules in SoftLayer.CLI.modules.* :license: MIT, see LICENSE for more details. """ import click from SoftLayer.CLI import exceptions def multi_option(*param_decls, **attrs): """modify help text and indicate option is permitted multiple times :param param_decls: :param attrs: :return: """ attrhelp = attrs.get('help', None) if attrhelp is not None: newhelp = attrhelp + " (multiple occurrence permitted)" attrs['help'] = newhelp attrs['multiple'] = True return click.option(*param_decls, **attrs) def resolve_id(resolver, identifier, name='object'): """Resolves a single id using a resolver function. :param resolver: function that resolves ids. Should return None or a list of ids. :param string identifier: a string identifier used to resolve ids :param string name: the object type, to be used in error messages """ ids = resolver(identifier) if len(ids) == 0: raise exceptions.CLIAbort("Error: Unable to find %s '%s'" % (name, identifier)) if len(ids) > 1: raise exceptions.CLIAbort( "Error: Multiple %s found for '%s': %s" % (name, identifier, ', '.join([str(_id) for _id in ids]))) return ids[0] softlayer-python-5.4.2/SoftLayer/CLI/image/000077500000000000000000000000001324365065500204635ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/CLI/image/__init__.py000066400000000000000000000007231324365065500225760ustar00rootroot00000000000000"""Compute images.""" # :license: MIT, see LICENSE for more details. from SoftLayer.CLI import formatting MASK = ('id,accountId,name,globalIdentifier,parentId,publicFlag,flexImageFlag,' 'imageType') DETAIL_MASK = MASK + (',children[id,blockDevicesDiskSpaceTotal,datacenter],' 'note,createDate,status,transaction') PUBLIC_TYPE = formatting.FormattedItem('PUBLIC', 'Public') PRIVATE_TYPE = formatting.FormattedItem('PRIVATE', 'Private') softlayer-python-5.4.2/SoftLayer/CLI/image/delete.py000066400000000000000000000007221324365065500223000ustar00rootroot00000000000000"""Delete an image.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import helpers @click.command() @click.argument('identifier') @environment.pass_env def cli(env, identifier): """Delete an image.""" image_mgr = SoftLayer.ImageManager(env.client) image_id = helpers.resolve_id(image_mgr.resolve_ids, identifier, 'image') image_mgr.delete_image(image_id) softlayer-python-5.4.2/SoftLayer/CLI/image/detail.py000066400000000000000000000044271324365065500223060ustar00rootroot00000000000000"""Get details for an image.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.CLI import helpers from SoftLayer.CLI import image as image_mod from SoftLayer import utils @click.command() @click.argument('identifier') @environment.pass_env def cli(env, identifier): """Get details for an image.""" image_mgr = SoftLayer.ImageManager(env.client) image_id = helpers.resolve_id(image_mgr.resolve_ids, identifier, 'image') image = image_mgr.get_image(image_id, mask=image_mod.DETAIL_MASK) disk_space = 0 datacenters = [] for child in image.get('children'): disk_space = int(child.get('blockDevicesDiskSpaceTotal', 0)) if child.get('datacenter'): datacenters.append(utils.lookup(child, 'datacenter', 'name')) table = formatting.KeyValueTable(['name', 'value']) table.align['name'] = 'r' table.align['value'] = 'l' table.add_row(['id', image['id']]) table.add_row(['global_identifier', image.get('globalIdentifier', formatting.blank())]) table.add_row(['name', image['name'].strip()]) table.add_row(['status', formatting.FormattedItem( utils.lookup(image, 'status', 'keyname'), utils.lookup(image, 'status', 'name'), )]) table.add_row([ 'active_transaction', formatting.transaction_status(image.get('transaction')), ]) table.add_row(['account', image.get('accountId', formatting.blank())]) table.add_row(['visibility', image_mod.PUBLIC_TYPE if image['publicFlag'] else image_mod.PRIVATE_TYPE]) table.add_row(['type', formatting.FormattedItem( utils.lookup(image, 'imageType', 'keyName'), utils.lookup(image, 'imageType', 'name'), )]) table.add_row(['flex', image.get('flexImageFlag')]) table.add_row(['note', image.get('note')]) table.add_row(['created', image.get('createDate')]) table.add_row(['disk_space', formatting.b_to_gb(disk_space)]) table.add_row(['datacenters', formatting.listing(sorted(datacenters), separator=',')]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/image/edit.py000066400000000000000000000016031324365065500217620ustar00rootroot00000000000000"""Edit details of an image.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import helpers @click.command() @click.argument('identifier') @click.option('--name', help="Name of the image") @click.option('--note', help="Additional note for the image") @click.option('--tag', help="Tags for the image") @environment.pass_env def cli(env, identifier, name, note, tag): """Edit details of an image.""" image_mgr = SoftLayer.ImageManager(env.client) data = {} if name: data['name'] = name if note: data['note'] = note if tag: data['tag'] = tag image_id = helpers.resolve_id(image_mgr.resolve_ids, identifier, 'image') if not image_mgr.edit(image_id, **data): raise exceptions.CLIAbort("Failed to Edit Image") softlayer-python-5.4.2/SoftLayer/CLI/image/export.py000066400000000000000000000014361324365065500223620ustar00rootroot00000000000000"""Export an image.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import helpers @click.command() @click.argument('identifier') @click.argument('uri') @environment.pass_env def cli(env, identifier, uri): """Export an image to object storage. The URI for an object storage object (.vhd/.iso file) of the format: swift://@// """ image_mgr = SoftLayer.ImageManager(env.client) image_id = helpers.resolve_id(image_mgr.resolve_ids, identifier, 'image') result = image_mgr.export_image_to_uri(image_id, uri) if not result: raise exceptions.CLIAbort("Failed to export Image") softlayer-python-5.4.2/SoftLayer/CLI/image/import.py000066400000000000000000000026061324365065500223530ustar00rootroot00000000000000"""Import an image.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting @click.command() @click.argument('name') @click.argument('uri') @click.option('--note', default="", help="The note to be applied to the imported template") @click.option('--os-code', default="", help="The referenceCode of the operating system software" " description for the imported VHD") @environment.pass_env def cli(env, name, note, os_code, uri): """Import an image. The URI for an object storage object (.vhd/.iso file) of the format: swift://@// """ image_mgr = SoftLayer.ImageManager(env.client) result = image_mgr.import_image_from_uri( name=name, note=note, os_code=os_code, uri=uri, ) if not result: raise exceptions.CLIAbort("Failed to import Image") table = formatting.KeyValueTable(['name', 'value']) table.align['name'] = 'r' table.align['value'] = 'l' table.add_row(['name', result['name']]) table.add_row(['id', result['id']]) table.add_row(['created', result['createDate']]) table.add_row(['guid', result['globalIdentifier']]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/image/list.py000066400000000000000000000035671324365065500220230ustar00rootroot00000000000000"""List images.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.CLI import image as image_mod from SoftLayer import utils @click.command() @click.option('--name', default=None, help='Filter on image name') @click.option('--public/--private', is_flag=True, default=None, help='Display only public or private images') @environment.pass_env def cli(env, name, public): """List images.""" image_mgr = SoftLayer.ImageManager(env.client) images = [] if public in [False, None]: for image in image_mgr.list_private_images(name=name, mask=image_mod.MASK): images.append(image) if public in [True, None]: for image in image_mgr.list_public_images(name=name, mask=image_mod.MASK): images.append(image) table = formatting.Table(['id', 'name', 'type', 'visibility', 'account']) images = [image for image in images if image['parentId'] == ''] for image in images: visibility = (image_mod.PUBLIC_TYPE if image['publicFlag'] else image_mod.PRIVATE_TYPE) table.add_row([ image.get('id', formatting.blank()), formatting.FormattedItem(image['name'], click.wrap_text(image['name'], width=50)), formatting.FormattedItem( utils.lookup(image, 'imageType', 'keyName'), utils.lookup(image, 'imageType', 'name')), visibility, image.get('accountId', formatting.blank()), ]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/loadbal/000077500000000000000000000000001324365065500207775ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/CLI/loadbal/__init__.py000066400000000000000000000005561324365065500231160ustar00rootroot00000000000000"""Load balancers.""" from SoftLayer.CLI import exceptions def parse_id(input_id): """Parse the load balancer kind and actual id from the "kind:id" form.""" parts = input_id.split(':') if len(parts) != 2: raise exceptions.CLIAbort( 'Invalid ID %s: ID should be of the form "kind:id"' % input_id) return parts[0], int(parts[1]) softlayer-python-5.4.2/SoftLayer/CLI/loadbal/cancel.py000066400000000000000000000014641324365065500226030ustar00rootroot00000000000000"""Cancel an existing load balancer.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting from SoftLayer.CLI import loadbal @click.command() @click.argument('identifier') @environment.pass_env def cli(env, identifier): """Cancel an existing load balancer.""" mgr = SoftLayer.LoadBalancerManager(env.client) _, loadbal_id = loadbal.parse_id(identifier) if not (env.skip_confirmations or formatting.confirm("This action will cancel a load balancer. " "Continue?")): raise exceptions.CLIAbort('Aborted.') mgr.cancel_lb(loadbal_id) env.fout('Load Balancer with id %s is being cancelled!' % identifier) softlayer-python-5.4.2/SoftLayer/CLI/loadbal/create.py000066400000000000000000000015471324365065500226230ustar00rootroot00000000000000"""Adds a load balancer given the id returned from create-options.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting @click.command() @click.argument('billing-id') @click.option('--datacenter', '-d', help='Datacenter shortname (sng01, dal05, ...)') @environment.pass_env def cli(env, billing_id, datacenter): """Adds a load balancer given the id returned from create-options.""" mgr = SoftLayer.LoadBalancerManager(env.client) if not formatting.confirm("This action will incur charges on your " "account. Continue?"): raise exceptions.CLIAbort('Aborted.') mgr.add_local_lb(billing_id, datacenter=datacenter) env.fout("Load balancer is being created!") softlayer-python-5.4.2/SoftLayer/CLI/loadbal/create_options.py000066400000000000000000000015271324365065500243740ustar00rootroot00000000000000"""Show load balancer options.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting @click.command() @environment.pass_env def cli(env): """Get price options to create a load balancer with.""" mgr = SoftLayer.LoadBalancerManager(env.client) table = formatting.Table(['price_id', 'capacity', 'description', 'price']) table.sortby = 'price' table.align['price'] = 'r' table.align['capacity'] = 'r' table.align['id'] = 'r' packages = mgr.get_lb_pkgs() for package in packages: table.add_row([ package['prices'][0]['id'], package.get('capacity'), package['description'], '%.2f' % float(package['prices'][0]['recurringFee']) ]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/loadbal/detail.py000066400000000000000000000067301324365065500226210ustar00rootroot00000000000000"""Get Load balancer details.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.CLI import loadbal @click.command() @click.argument('identifier') @environment.pass_env def cli(env, identifier): """Get Load balancer details.""" mgr = SoftLayer.LoadBalancerManager(env.client) _, loadbal_id = loadbal.parse_id(identifier) load_balancer = mgr.get_local_lb(loadbal_id) table = formatting.KeyValueTable(['name', 'value']) table.align['name'] = 'l' table.align['value'] = 'l' table.add_row(['ID', 'local:%s' % load_balancer['id']]) table.add_row(['IP Address', load_balancer['ipAddress']['ipAddress']]) name = load_balancer['loadBalancerHardware'][0]['datacenter']['name'] table.add_row(['Datacenter', name]) table.add_row(['Connections limit', load_balancer['connectionLimit']]) table.add_row(['Dedicated', load_balancer['dedicatedFlag']]) table.add_row(['HA', load_balancer['highAvailabilityFlag']]) table.add_row(['SSL Enabled', load_balancer['sslEnabledFlag']]) table.add_row(['SSL Active', load_balancer['sslActiveFlag']]) index0 = 1 for virtual_server in load_balancer['virtualServers']: for group in virtual_server['serviceGroups']: service_group_table = formatting.KeyValueTable(['name', 'value']) table.add_row(['Service Group %s' % index0, service_group_table]) index0 += 1 service_group_table.add_row(['Guest ID', virtual_server['id']]) service_group_table.add_row(['Port', virtual_server['port']]) service_group_table.add_row(['Allocation', '%s %%' % virtual_server['allocation']]) service_group_table.add_row(['Routing Type', '%s:%s' % (group['routingTypeId'], group['routingType']['name'])]) service_group_table.add_row(['Routing Method', '%s:%s' % (group['routingMethodId'], group['routingMethod']['name'])]) index1 = 1 for service in group['services']: service_table = formatting.KeyValueTable(['name', 'value']) service_group_table.add_row(['Service %s' % index1, service_table]) index1 += 1 health_check = service['healthChecks'][0] service_table.add_row(['Service ID', service['id']]) service_table.add_row(['IP Address', service['ipAddress']['ipAddress']]) service_table.add_row(['Port', service['port']]) service_table.add_row(['Health Check', '%s:%s' % (health_check['healthCheckTypeId'], health_check['type']['name'])]) service_table.add_row( ['Weight', service['groupReferences'][0]['weight']]) service_table.add_row(['Enabled', service['enabled']]) service_table.add_row(['Status', service['status']]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/loadbal/group_add.py000066400000000000000000000023511324365065500233160ustar00rootroot00000000000000"""Adds a new load_balancer service.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import loadbal @click.command() @click.argument('identifier') @click.option('--allocation', required=True, type=click.INT, help="The allocated percent of connections") @click.option('--port', required=True, help="The port number", type=click.INT) @click.option('--routing-type', required=True, help="The port routing type") @click.option('--routing-method', required=True, help="The routing method") @environment.pass_env def cli(env, identifier, allocation, port, routing_type, routing_method): """Adds a new load_balancer service.""" mgr = SoftLayer.LoadBalancerManager(env.client) _, loadbal_id = loadbal.parse_id(identifier) mgr.add_service_group(loadbal_id, allocation=allocation, port=port, routing_type=routing_type, routing_method=routing_method) env.fout('Load balancer service group is being added!') softlayer-python-5.4.2/SoftLayer/CLI/loadbal/group_delete.py000066400000000000000000000015161324365065500240320ustar00rootroot00000000000000"""Deletes an existing load balancer service group.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting from SoftLayer.CLI import loadbal @click.command() @click.argument('identifier') @environment.pass_env def cli(env, identifier): """Deletes an existing load balancer service group.""" mgr = SoftLayer.LoadBalancerManager(env.client) _, group_id = loadbal.parse_id(identifier) if not (env.skip_confirmations or formatting.confirm("This action will cancel a service group. " "Continue?")): raise exceptions.CLIAbort('Aborted.') mgr.delete_service_group(group_id) env.fout('Service group %s is being deleted!' % identifier) softlayer-python-5.4.2/SoftLayer/CLI/loadbal/group_edit.py000066400000000000000000000027331324365065500235170ustar00rootroot00000000000000"""Edit an existing load balancer service group.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import loadbal @click.command() @click.argument('identifier') @click.option('--allocation', type=click.INT, help="Change the allocated percent of connections") @click.option('--port', help="Change the port number", type=click.INT) @click.option('--routing-type', help="Change the port routing type") @click.option('--routing-method', help="Change the routing method") @environment.pass_env def cli(env, identifier, allocation, port, routing_type, routing_method): """Edit an existing load balancer service group.""" mgr = SoftLayer.LoadBalancerManager(env.client) loadbal_id, group_id = loadbal.parse_id(identifier) # check if any input is provided if not any([allocation, port, routing_type, routing_method]): raise exceptions.CLIAbort( 'At least one property is required to be changed!') mgr.edit_service_group(loadbal_id, group_id, allocation=allocation, port=port, routing_type=routing_type, routing_method=routing_method) env.fout('Load balancer service group %s is being updated!' % identifier) softlayer-python-5.4.2/SoftLayer/CLI/loadbal/group_reset.py000066400000000000000000000011151324365065500237050ustar00rootroot00000000000000"""Reset connections on a certain service group.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import loadbal @click.command() @click.argument('identifier') @environment.pass_env def cli(env, identifier): """Reset connections on a certain service group.""" mgr = SoftLayer.LoadBalancerManager(env.client) loadbal_id, group_id = loadbal.parse_id(identifier) mgr.reset_service_group(loadbal_id, group_id) env.fout('Load balancer service group connections are being reset!') softlayer-python-5.4.2/SoftLayer/CLI/loadbal/health_checks.py000066400000000000000000000011341324365065500241350ustar00rootroot00000000000000"""List health check types.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting @click.command() @environment.pass_env def cli(env): """List health check types.""" mgr = SoftLayer.LoadBalancerManager(env.client) hc_types = mgr.get_hc_types() table = formatting.KeyValueTable(['ID', 'Name']) table.align['ID'] = 'l' table.align['Name'] = 'l' table.sortby = 'ID' for hc_type in hc_types: table.add_row([hc_type['id'], hc_type['name']]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/loadbal/list.py000066400000000000000000000026471324365065500223350ustar00rootroot00000000000000"""List active load balancers.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting @click.command() @environment.pass_env def cli(env): """List active load balancers.""" mgr = SoftLayer.LoadBalancerManager(env.client) load_balancers = mgr.get_local_lbs() table = formatting.Table(['ID', 'VIP Address', 'Location', 'SSL Offload', 'Connections/second', 'Type']) table.align['Connections/second'] = 'r' for load_balancer in load_balancers: ssl_support = 'Not Supported' if load_balancer['sslEnabledFlag']: if load_balancer['sslActiveFlag']: ssl_support = 'On' else: ssl_support = 'Off' lb_type = 'Standard' if load_balancer['dedicatedFlag']: lb_type = 'Dedicated' elif load_balancer['highAvailabilityFlag']: lb_type = 'HA' table.add_row([ 'local:%s' % load_balancer['id'], load_balancer['ipAddress']['ipAddress'], load_balancer['loadBalancerHardware'][0]['datacenter']['name'], ssl_support, load_balancer['connectionLimit'], lb_type ]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/loadbal/routing_methods.py000066400000000000000000000011751324365065500245670ustar00rootroot00000000000000"""List routing methods.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting @click.command() @environment.pass_env def cli(env): """List routing types.""" mgr = SoftLayer.LoadBalancerManager(env.client) routing_methods = mgr.get_routing_methods() table = formatting.KeyValueTable(['ID', 'Name']) table.align['ID'] = 'l' table.align['Name'] = 'l' table.sortby = 'ID' for routing_method in routing_methods: table.add_row([routing_method['id'], routing_method['name']]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/loadbal/routing_types.py000066400000000000000000000011561324365065500242670ustar00rootroot00000000000000"""List routing types.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting @click.command() @environment.pass_env def cli(env): """List routing types.""" mgr = SoftLayer.LoadBalancerManager(env.client) routing_types = mgr.get_routing_types() table = formatting.KeyValueTable(['ID', 'Name']) table.align['ID'] = 'l' table.align['Name'] = 'l' table.sortby = 'ID' for routing_type in routing_types: table.add_row([routing_type['id'], routing_type['name']]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/loadbal/service_add.py000066400000000000000000000032461324365065500236260ustar00rootroot00000000000000"""Adds a new load balancer service.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import loadbal @click.command() @click.argument('identifier') @click.option('--enabled / --disabled', required=True, help="Create the service as enabled or disabled") @click.option('--port', required=True, help="The port number for the service", type=click.INT) @click.option('--weight', required=True, type=click.INT, help="The weight of the service") @click.option('--healthcheck-type', required=True, help="The health check type") @click.option('--ip-address', required=True, help="The IP address of the service") @environment.pass_env def cli(env, identifier, enabled, port, weight, healthcheck_type, ip_address): """Adds a new load balancer service.""" mgr = SoftLayer.LoadBalancerManager(env.client) loadbal_id, group_id = loadbal.parse_id(identifier) # check if the IP is valid ip_address_id = None if ip_address: ip_service = env.client['Network_Subnet_IpAddress'] ip_record = ip_service.getByIpAddress(ip_address) if len(ip_record) > 0: ip_address_id = ip_record['id'] mgr.add_service(loadbal_id, group_id, ip_address_id=ip_address_id, enabled=enabled, port=port, weight=weight, hc_type=healthcheck_type) env.fout('Load balancer service is being added!') softlayer-python-5.4.2/SoftLayer/CLI/loadbal/service_delete.py000066400000000000000000000015341324365065500243360ustar00rootroot00000000000000"""Deletes an existing load balancer service.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting from SoftLayer.CLI import loadbal @click.command() @click.argument('identifier') @environment.pass_env def cli(env, identifier): """Deletes an existing load balancer service.""" mgr = SoftLayer.LoadBalancerManager(env.client) _, service_id = loadbal.parse_id(identifier) if not (env.skip_confirmations or formatting.confirm("This action will cancel a service from your " "load balancer. Continue?")): raise exceptions.CLIAbort('Aborted.') mgr.delete_service(service_id) env.fout('Load balancer service %s is being cancelled!' % service_id) softlayer-python-5.4.2/SoftLayer/CLI/loadbal/service_edit.py000066400000000000000000000034561324365065500240260ustar00rootroot00000000000000"""Edit the properties of a service group.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import loadbal @click.command() @click.argument('identifier') @click.option('--enabled / --disabled', default=None, help="Enable or disable the service") @click.option('--port', help="Change the port number for the service", type=click.INT) @click.option('--weight', type=click.INT, help="Change the weight of the service") @click.option('--healthcheck-type', help="Change the health check type") @click.option('--ip-address', help="Change the IP address of the service") @environment.pass_env def cli(env, identifier, enabled, port, weight, healthcheck_type, ip_address): """Edit the properties of a service group.""" mgr = SoftLayer.LoadBalancerManager(env.client) loadbal_id, service_id = loadbal.parse_id(identifier) # check if any input is provided if ((not any([ip_address, weight, port, healthcheck_type])) and enabled is None): raise exceptions.CLIAbort( 'At least one property is required to be changed!') # check if the IP is valid ip_address_id = None if ip_address: ip_service = env.client['Network_Subnet_IpAddress'] ip_record = ip_service.getByIpAddress(ip_address) ip_address_id = ip_record['id'] mgr.edit_service(loadbal_id, service_id, ip_address_id=ip_address_id, enabled=enabled, port=port, weight=weight, hc_type=healthcheck_type) env.fout('Load balancer service %s is being modified!' % identifier) softlayer-python-5.4.2/SoftLayer/CLI/loadbal/service_toggle.py000066400000000000000000000015611324365065500243550ustar00rootroot00000000000000"""Toggle the status of an existing load balancer service.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting from SoftLayer.CLI import loadbal @click.command() @click.argument('identifier') @environment.pass_env def cli(env, identifier): """Toggle the status of an existing load balancer service.""" mgr = SoftLayer.LoadBalancerManager(env.client) _, service_id = loadbal.parse_id(identifier) if not (env.skip_confirmations or formatting.confirm("This action will toggle the status on the " "service. Continue?")): raise exceptions.CLIAbort('Aborted.') mgr.toggle_service_status(service_id) env.fout('Load balancer service %s status updated!' % identifier) softlayer-python-5.4.2/SoftLayer/CLI/metadata.py000066400000000000000000000047021324365065500215360ustar00rootroot00000000000000"""Find details about this machine.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting META_CHOICES = [ 'backend_ip', 'backend_mac', 'datacenter', 'datacenter_id', 'fqdn', 'frontend_mac', 'id', 'ip', 'network', 'provision_state', 'tags', 'user_data', ] META_MAPPING = { 'backend_ip': 'primary_backend_ip', 'ip': 'primary_ip', } HELP = """Find details about this machine \b PROP Choices %s \b Examples : %s """ % ('*'+'\n*'.join(META_CHOICES), 'slcli metadata '+'\nslcli metadata '.join(META_CHOICES)) @click.command(help=HELP, short_help="Find details about this machine.", epilog="These commands only work on devices on the backend " "SoftLayer network. This allows for self-discovery for " "newly provisioned resources.") @click.argument('prop', type=click.Choice(META_CHOICES)) @environment.pass_env def cli(env, prop): """Find details about this machine.""" try: if prop == 'network': env.fout(get_network()) return meta_prop = META_MAPPING.get(prop) or prop env.fout(SoftLayer.MetadataManager().get(meta_prop)) except SoftLayer.TransportError: raise exceptions.CLIAbort( 'Cannot connect to the backend service address. Make sure ' 'this command is being ran from a device on the backend ' 'network.') def get_network(): """Returns a list of tables with public and private network details.""" meta = SoftLayer.MetadataManager() network_tables = [] for network_func in [meta.public_network, meta.private_network]: network = network_func() table = formatting.KeyValueTable(['name', 'value']) table.align['name'] = 'r' table.align['value'] = 'l' table.add_row(['mac addresses', formatting.listing(network['mac_addresses'], separator=',')]) table.add_row(['router', network['router']]) table.add_row(['vlans', formatting.listing(network['vlans'], separator=',')]) table.add_row(['vlan ids', formatting.listing(network['vlan_ids'], separator=',')]) network_tables.append(table) return network_tables softlayer-python-5.4.2/SoftLayer/CLI/mq/000077500000000000000000000000001324365065500200165ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/CLI/mq/__init__.py000066400000000000000000000036771324365065500221440ustar00rootroot00000000000000"""Message queue service.""" # :license: MIT, see LICENSE for more details. from SoftLayer.CLI import formatting def queue_table(queue): """Returns a table with details about a queue.""" table = formatting.Table(['property', 'value']) table.align['property'] = 'r' table.align['value'] = 'l' table.add_row(['name', queue['name']]) table.add_row(['message_count', queue['message_count']]) table.add_row(['visible_message_count', queue['visible_message_count']]) table.add_row(['tags', formatting.listing(queue['tags'] or [])]) table.add_row(['expiration', queue['expiration']]) table.add_row(['visibility_interval', queue['visibility_interval']]) return table def message_table(message): """Returns a table with details about a message.""" table = formatting.Table(['property', 'value']) table.align['property'] = 'r' table.align['value'] = 'l' table.add_row(['id', message['id']]) table.add_row(['initial_entry_time', message['initial_entry_time']]) table.add_row(['visibility_delay', message['visibility_delay']]) table.add_row(['visibility_interval', message['visibility_interval']]) table.add_row(['fields', message['fields']]) return [table, message['body']] def topic_table(topic): """Returns a table with details about a topic.""" table = formatting.Table(['property', 'value']) table.align['property'] = 'r' table.align['value'] = 'l' table.add_row(['name', topic['name']]) table.add_row(['tags', formatting.listing(topic['tags'] or [])]) return table def subscription_table(sub): """Returns a table with details about a subscription.""" table = formatting.Table(['property', 'value']) table.align['property'] = 'r' table.align['value'] = 'l' table.add_row(['id', sub['id']]) table.add_row(['endpoint_type', sub['endpoint_type']]) for key, val in sub['endpoint'].items(): table.add_row([key, val]) return table softlayer-python-5.4.2/SoftLayer/CLI/mq/accounts_list.py000066400000000000000000000013031324365065500232370ustar00rootroot00000000000000"""List SoftLayer Message Queue Accounts.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting @click.command() @environment.pass_env def cli(env): """List SoftLayer Message Queue Accounts.""" manager = SoftLayer.MessagingManager(env.client) accounts = manager.list_accounts() table = formatting.Table(['id', 'name', 'status']) for account in accounts: if not account['nodes']: continue table.add_row([account['nodes'][0]['accountName'], account['name'], account['status']['name']]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/mq/endpoints_list.py000066400000000000000000000012731324365065500234310ustar00rootroot00000000000000"""List SoftLayer Message Queue Endpoints.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting @click.command() @environment.pass_env def cli(env): """List SoftLayer Message Queue Endpoints.""" manager = SoftLayer.MessagingManager(env.client) regions = manager.get_endpoints() table = formatting.Table(['name', 'public', 'private']) for region, endpoints in regions.items(): table.add_row([ region, endpoints.get('public') or formatting.blank(), endpoints.get('private') or formatting.blank(), ]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/mq/ping.py000066400000000000000000000013151324365065500213250ustar00rootroot00000000000000"""Ping the SoftLayer Message Queue service.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions @click.command() @click.option('--datacenter', help="Datacenter, E.G.: dal05") @click.option('--network', type=click.Choice(['public', 'private']), help="Network type") @environment.pass_env def cli(env, datacenter, network): """Ping the SoftLayer Message Queue service.""" manager = SoftLayer.MessagingManager(env.client) okay = manager.ping(datacenter=datacenter, network=network) if okay: env.fout('OK') else: exceptions.CLIAbort('Ping failed') softlayer-python-5.4.2/SoftLayer/CLI/mq/queue_add.py000066400000000000000000000027161324365065500223320ustar00rootroot00000000000000"""Create a queue.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import helpers from SoftLayer.CLI import mq @click.command() @click.argument('account-id') @click.argument('queue-name') @click.option('--datacenter', help="Datacenter, E.G.: dal05") @click.option('--network', type=click.Choice(['public', 'private']), help="Network type") @click.option('--visibility-interval', type=click.INT, default=30, show_default=True, help="Time in seconds that messages will re-appear after being " "popped") @click.option('--expiration', type=click.INT, default=604800, show_default=True, help="Time in seconds that messages will live") @helpers.multi_option('--tag', '-g', help="Tags to add to the queue") @environment.pass_env def cli(env, account_id, queue_name, datacenter, network, visibility_interval, expiration, tag): """Create a queue.""" manager = SoftLayer.MessagingManager(env.client) mq_client = manager.get_connection(account_id, datacenter=datacenter, network=network) queue = mq_client.create_queue( queue_name, visibility_interval=visibility_interval, expiration=expiration, tags=tag, ) env.fout(mq.queue_table(queue)) softlayer-python-5.4.2/SoftLayer/CLI/mq/queue_detail.py000066400000000000000000000014361324365065500230420ustar00rootroot00000000000000"""Detail a queue.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import mq @click.command() @click.argument('account-id') @click.argument('queue-name') @click.option('--datacenter', help="Datacenter, E.G.: dal05") @click.option('--network', type=click.Choice(['public', 'private']), help="Network type") @environment.pass_env def cli(env, account_id, queue_name, datacenter, network): """Detail a queue.""" manager = SoftLayer.MessagingManager(env.client) mq_client = manager.get_connection(account_id, datacenter=datacenter, network=network) queue = mq_client.get_queue(queue_name) env.fout(mq.queue_table(queue)) softlayer-python-5.4.2/SoftLayer/CLI/mq/queue_edit.py000066400000000000000000000027161324365065500225270ustar00rootroot00000000000000"""Modify a queue.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import helpers from SoftLayer.CLI import mq @click.command() @click.argument('account-id') @click.argument('queue-name') @click.option('--datacenter', help="Datacenter, E.G.: dal05") @click.option('--network', type=click.Choice(['public', 'private']), help="Network type") @click.option('--visibility-interval', type=click.INT, default=30, show_default=True, help="Time in seconds that messages will re-appear after being " "popped") @click.option('--expiration', type=click.INT, default=604800, show_default=True, help="Time in seconds that messages will live") @helpers.multi_option('--tag', '-g', help="Tags to add to the queue") @environment.pass_env def cli(env, account_id, queue_name, datacenter, network, visibility_interval, expiration, tag): """Modify a queue.""" manager = SoftLayer.MessagingManager(env.client) mq_client = manager.get_connection(account_id, datacenter=datacenter, network=network) queue = mq_client.modify_queue( queue_name, visibility_interval=visibility_interval, expiration=expiration, tags=tag, ) env.fout(mq.queue_table(queue)) softlayer-python-5.4.2/SoftLayer/CLI/mq/queue_list.py000066400000000000000000000021001324365065500225400ustar00rootroot00000000000000"""List all queues on an account.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting @click.command() @click.argument('account-id') @click.option('--datacenter', help="Datacenter, E.G.: dal05") @click.option('--network', type=click.Choice(['public', 'private']), help="Network type") @environment.pass_env def cli(env, account_id, datacenter, network): """List all queues on an account.""" manager = SoftLayer.MessagingManager(env.client) mq_client = manager.get_connection(account_id, datacenter=datacenter, network=network) queues = mq_client.get_queues()['items'] table = formatting.Table(['name', 'message_count', 'visible_message_count']) for queue in queues: table.add_row([queue['name'], queue['message_count'], queue['visible_message_count']]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/mq/queue_pop.py000066400000000000000000000025571324365065500224030ustar00rootroot00000000000000"""Pops a message from a queue.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import mq @click.command() @click.argument('account-id') @click.argument('queue-name') @click.option('--count', default=1, show_default=True, type=click.INT, help="Count of messages to pop") @click.option('--delete-after', is_flag=True, help="Remove popped messages from the queue") @click.option('--datacenter', help="Datacenter, E.G.: dal05") @click.option('--network', type=click.Choice(['public', 'private']), help="Network type") @environment.pass_env def cli(env, account_id, queue_name, count, delete_after, datacenter, network): """Pops a message from a queue.""" manager = SoftLayer.MessagingManager(env.client) mq_client = manager.get_connection(account_id, datacenter=datacenter, network=network) messages = mq_client.pop_messages(queue_name, count) formatted_messages = [] for message in messages['items']: formatted_messages.append(mq.message_table(message)) if delete_after: for message in messages['items']: mq_client.delete_message(queue_name, message['id']) env.fout(formatted_messages) softlayer-python-5.4.2/SoftLayer/CLI/mq/queue_push.py000066400000000000000000000017141324365065500225560ustar00rootroot00000000000000"""Push a message into a queue.""" # :license: MIT, see LICENSE for more details. import sys import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import mq @click.command() @click.argument('account-id') @click.argument('queue-name') @click.argument('message') @click.option('--datacenter', help="Datacenter, E.G.: dal05") @click.option('--network', type=click.Choice(['public', 'private']), help="Network type") @environment.pass_env def cli(env, account_id, queue_name, message, datacenter, network): """Push a message into a queue.""" manager = SoftLayer.MessagingManager(env.client) mq_client = manager.get_connection(account_id, datacenter=datacenter, network=network) body = '' if message == '-': body = sys.stdin.read() else: body = message env.fout(mq.message_table(mq_client.push_queue_message(queue_name, body))) softlayer-python-5.4.2/SoftLayer/CLI/mq/queue_remove.py000066400000000000000000000017621324365065500230770ustar00rootroot00000000000000"""Delete a queue or a queued message.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment @click.command() @click.argument('account-id') @click.argument('queue-name') @click.argument('message-id', required=False) @click.option('--force', is_flag=True, help="Force the deletion of the queue") @click.option('--datacenter', help="Datacenter, E.G.: dal05") @click.option('--network', type=click.Choice(['public', 'private']), help="Network type") @environment.pass_env def cli(env, account_id, queue_name, message_id, force, datacenter, network): """Delete a queue or a queued message.""" manager = SoftLayer.MessagingManager(env.client) mq_client = manager.get_connection(account_id, datacenter=datacenter, network=network) if message_id: mq_client.delete_message(queue_name, message_id) else: mq_client.delete_queue(queue_name, force) softlayer-python-5.4.2/SoftLayer/CLI/mq/topic_add.py000066400000000000000000000027261324365065500223250ustar00rootroot00000000000000"""Create a new topic.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import helpers from SoftLayer.CLI import mq @click.command() @click.argument('account-id') @click.argument('topic-name') @click.option('--datacenter', help="Datacenter, E.G.: dal05") @click.option('--network', type=click.Choice(['public', 'private']), help="Network type") @click.option('--visibility-interval', type=click.INT, default=30, show_default=True, help="Time in seconds that messages will re-appear after being " "popped") @click.option('--expiration', type=click.INT, default=604800, show_default=True, help="Time in seconds that messages will live") @helpers.multi_option('--tag', '-g', help="Tags to add to the topic") @environment.pass_env def cli(env, account_id, topic_name, datacenter, network, visibility_interval, expiration, tag): """Create a new topic.""" manager = SoftLayer.MessagingManager(env.client) mq_client = manager.get_connection(account_id, datacenter=datacenter, network=network) topic = mq_client.create_topic( topic_name, visibility_interval=visibility_interval, expiration=expiration, tags=tag, ) env.fout(mq.topic_table(topic)) softlayer-python-5.4.2/SoftLayer/CLI/mq/topic_detail.py000066400000000000000000000017151324365065500230340ustar00rootroot00000000000000"""Detail a topic.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import mq @click.command() @click.argument('account-id') @click.argument('topic-name') @click.option('--datacenter', help="Datacenter, E.G.: dal05") @click.option('--network', type=click.Choice(['public', 'private']), help="Network type") @environment.pass_env def cli(env, account_id, topic_name, datacenter, network): """Detail a topic.""" manager = SoftLayer.MessagingManager(env.client) mq_client = manager.get_connection(account_id, datacenter=datacenter, network=network) topic = mq_client.get_topic(topic_name) subscriptions = mq_client.get_subscriptions(topic_name) tables = [] for sub in subscriptions['items']: tables.append(mq.subscription_table(sub)) env.fout([mq.topic_table(topic), tables]) softlayer-python-5.4.2/SoftLayer/CLI/mq/topic_list.py000066400000000000000000000015631324365065500225460ustar00rootroot00000000000000"""List all topics on an account.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting @click.command() @click.argument('account-id') @click.option('--datacenter', help="Datacenter, E.G.: dal05") @click.option('--network', type=click.Choice(['public', 'private']), help="Network type") @environment.pass_env def cli(env, account_id, datacenter, network): """List all topics on an account.""" manager = SoftLayer.MessagingManager(env.client) mq_client = manager.get_connection(account_id, datacenter=datacenter, network=network) topics = mq_client.get_topics()['items'] table = formatting.Table(['name']) for topic in topics: table.add_row([topic['name']]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/mq/topic_push.py000066400000000000000000000020201324365065500225370ustar00rootroot00000000000000"""Push a message into a topic.""" # :license: MIT, see LICENSE for more details. import sys import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import mq @click.command() @click.argument('account-id') @click.argument('topic-name') @click.argument('message') @click.option('--datacenter', help="Datacenter, E.G.: dal05") @click.option('--network', type=click.Choice(['public', 'private']), help="Network type") @environment.pass_env def cli(env, account_id, topic_name, message, datacenter, network): """Push a message into a topic.""" manager = SoftLayer.MessagingManager(env.client) mq_client = manager.get_connection(account_id, datacenter=datacenter, network=network) # the message body comes from the positional argument or stdin body = '' if message == '-': body = sys.stdin.read() else: body = message env.fout(mq.message_table(mq_client.push_topic_message(topic_name, body))) softlayer-python-5.4.2/SoftLayer/CLI/mq/topic_remove.py000066400000000000000000000014651324365065500230710ustar00rootroot00000000000000"""Delete a topic.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment @click.command() @click.argument('account-id') @click.argument('topic-name') @click.option('--force', is_flag=True, help="Force the deletion of the queue") @click.option('--datacenter', help="Datacenter, E.G.: dal05") @click.option('--network', type=click.Choice(['public', 'private']), help="Network type") @environment.pass_env def cli(env, account_id, topic_name, force, datacenter, network): """Delete a topic.""" manager = SoftLayer.MessagingManager(env.client) mq_client = manager.get_connection(account_id, datacenter=datacenter, network=network) mq_client.delete_topic(topic_name, force) softlayer-python-5.4.2/SoftLayer/CLI/mq/topic_subscribe.py000066400000000000000000000033111324365065500235450ustar00rootroot00000000000000"""Create a subscription on a topic.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import mq @click.command() @click.argument('account-id') @click.argument('topic-name') @click.option('--datacenter', help="Datacenter, E.G.: dal05") @click.option('--network', type=click.Choice(['public', 'private']), help="Network type") @click.option('--sub-type', type=click.Choice(['http', 'queue']), help="Type of endpoint") @click.option('--queue-name', help="Queue name. Required if --type is queue") @click.option('--http-method', help="HTTP Method to use if --type is http") @click.option('--http-url', help="HTTP/HTTPS URL to use. Required if --type is http") @click.option('--http-body', help="HTTP Body template to use if --type is http") @environment.pass_env def cli(env, account_id, topic_name, datacenter, network, sub_type, queue_name, http_method, http_url, http_body): """Create a subscription on a topic.""" manager = SoftLayer.MessagingManager(env.client) mq_client = manager.get_connection(account_id, datacenter=datacenter, network=network) if sub_type == 'queue': subscription = mq_client.create_subscription(topic_name, 'queue', queue_name=queue_name) elif sub_type == 'http': subscription = mq_client.create_subscription( topic_name, 'http', method=http_method, url=http_url, body=http_body, ) env.fout(mq.subscription_table(subscription)) softlayer-python-5.4.2/SoftLayer/CLI/mq/topic_unsubscribe.py000066400000000000000000000015101324365065500241070ustar00rootroot00000000000000"""Remove a subscription on a topic.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment @click.command() @click.argument('account-id') @click.argument('topic-name') @click.argument('subscription-id') @click.option('--datacenter', help="Datacenter, E.G.: dal05") @click.option('--network', type=click.Choice(['public', 'private']), help="Network type") @environment.pass_env def cli(env, account_id, topic_name, subscription_id, datacenter, network): """Remove a subscription on a topic.""" manager = SoftLayer.MessagingManager(env.client) mq_client = manager.get_connection(account_id, datacenter=datacenter, network=network) mq_client.delete_subscription(topic_name, subscription_id) softlayer-python-5.4.2/SoftLayer/CLI/nas/000077500000000000000000000000001324365065500201625ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/CLI/nas/__init__.py000066400000000000000000000000401324365065500222650ustar00rootroot00000000000000"""Network Attached Storage.""" softlayer-python-5.4.2/SoftLayer/CLI/nas/credentials.py000066400000000000000000000011101324365065500230220ustar00rootroot00000000000000"""List NAS account credentials.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting @click.command() @click.argument('identifier') @environment.pass_env def cli(env, identifier): """List NAS account credentials.""" nw_mgr = SoftLayer.NetworkManager(env.client) result = nw_mgr.get_nas_credentials(identifier) table = formatting.Table(['username', 'password']) table.add_row([result['username'], result['password']]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/nas/list.py000066400000000000000000000020441324365065500215070ustar00rootroot00000000000000"""List NAS accounts.""" # :license: MIT, see LICENSE for more details. import click from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer import utils @click.command() @environment.pass_env def cli(env): """List NAS accounts.""" account = env.client['Account'] nas_accounts = account.getNasNetworkStorage( mask='eventCount,serviceResource[datacenter.name]') table = formatting.Table(['id', 'datacenter', 'size', 'server']) for nas_account in nas_accounts: table.add_row([ nas_account['id'], utils.lookup(nas_account, 'serviceResource', 'datacenter', 'name') or formatting.blank(), formatting.FormattedItem( nas_account.get('capacityGb', formatting.blank()), "%dGB" % nas_account.get('capacityGb', 0)), nas_account.get('serviceResourceBackendIpAddress', formatting.blank())]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/object_storage/000077500000000000000000000000001324365065500223735ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/CLI/object_storage/__init__.py000066400000000000000000000000261324365065500245020ustar00rootroot00000000000000"""Object Storage.""" softlayer-python-5.4.2/SoftLayer/CLI/object_storage/list_accounts.py000066400000000000000000000011141324365065500256140ustar00rootroot00000000000000"""List Object Storage accounts.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting @click.command() @environment.pass_env def cli(env): """List object storage accounts.""" mgr = SoftLayer.ObjectStorageManager(env.client) accounts = mgr.list_accounts() table = formatting.Table(['id', 'name']) table.sortby = 'id' for account in accounts: table.add_row([ account['id'], account['username'], ]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/object_storage/list_endpoints.py000066400000000000000000000012011324365065500257750ustar00rootroot00000000000000"""List Object Storage endpoints.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting @click.command() @environment.pass_env def cli(env): """List object storage endpoints.""" mgr = SoftLayer.ObjectStorageManager(env.client) endpoints = mgr.list_endpoints() table = formatting.Table(['datacenter', 'public', 'private']) for endpoint in endpoints: table.add_row([ endpoint['datacenter']['name'], endpoint['public'], endpoint['private'], ]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/order/000077500000000000000000000000001324365065500205145ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/CLI/order/__init__.py000066400000000000000000000000001324365065500226130ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/CLI/order/category_list.py000066400000000000000000000027631324365065500237460ustar00rootroot00000000000000"""List package categories.""" # :license: MIT, see LICENSE for more details. import click from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.managers import ordering COLUMNS = ['name', 'categoryCode', 'isRequired'] @click.command() @click.argument('package_keyname') @click.option('--required', is_flag=True, help="List only the required categories for the package") @environment.pass_env def cli(env, package_keyname, required): """List the categories of a package. Package keynames can be retrieved from `slcli order package-list` \b Example: # List the categories of Bare Metal servers slcli order category-list BARE_METAL_SERVER When using the --required flag, it will list out only the categories that are required for ordering that package (see `slcli order item-list`) \b Example: # List the required categories for Bare Metal servers slcli order category-list BARE_METAL_SERVER --required """ client = env.client manager = ordering.OrderingManager(client) table = formatting.Table(COLUMNS) categories = manager.list_categories(package_keyname) if required: categories = [cat for cat in categories if cat['isRequired']] for cat in categories: table.add_row([ cat['itemCategory']['name'], cat['itemCategory']['categoryCode'], 'Y' if cat['isRequired'] else 'N' ]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/order/item_list.py000066400000000000000000000046331324365065500230650ustar00rootroot00000000000000"""List package items.""" # :license: MIT, see LICENSE for more details. import click from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.managers import ordering from SoftLayer.utils import lookup COLUMNS = ['category', 'keyName', 'description'] @click.command() @click.argument('package_keyname') @click.option('--keyword', help="A word (or string) used to filter item names.") @click.option('--category', help="Category code to filter items by") @environment.pass_env def cli(env, package_keyname, keyword, category): """List package items used for ordering. The items listed can be used with `slcli order place` to specify the items that are being ordered in the package. Package keynames can be retrieved using `slcli order package-list` \b Note: Items with a numbered category, like disk0 or gpu0, can be included multiple times in an order to match how many of the item you want to order. \b Example: # List all items in the VSI package slcli order item-list CLOUD_SERVER The --keyword option is used to filter items by name. The --category option is used to filter items by category. Both --keyword and --category can be used together. \b Example: # List Ubuntu OSes from the os category of the Bare Metal package slcli order item-list BARE_METAL_SERVER --category os --keyword ubuntu """ table = formatting.Table(COLUMNS) manager = ordering.OrderingManager(env.client) _filter = {'items': {}} if keyword: _filter['items']['description'] = {'operation': '*= %s' % keyword} if category: _filter['items']['categories'] = {'categoryCode': {'operation': '_= %s' % category}} items = manager.list_items(package_keyname, filter=_filter) sorted_items = sort_items(items) categories = sorted_items.keys() for catname in sorted(categories): for item in sorted_items[catname]: table.add_row([catname, item['keyName'], item['description']]) env.fout(table) def sort_items(items): """sorts the items into a dictionary of categories, with a list of items""" sorted_items = {} for item in items: category = lookup(item, 'itemCategory', 'categoryCode') if sorted_items.get(category) is None: sorted_items[category] = [] sorted_items[category].append(item) return sorted_items softlayer-python-5.4.2/SoftLayer/CLI/order/package_list.py000066400000000000000000000031501324365065500235130ustar00rootroot00000000000000"""List packages.""" # :license: MIT, see LICENSE for more details. import click from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.managers import ordering COLUMNS = ['name', 'keyName', 'type'] @click.command() @click.option('--keyword', help="A word (or string) used to filter package names.") @click.option('--package_type', help="The keyname for the type of package. BARE_METAL_CPU for example") @environment.pass_env def cli(env, keyword, package_type): """List packages that can be ordered via the placeOrder API. \b Example: # List out all packages for ordering slcli order package-list Keywords can also be used for some simple filtering functionality to help find a package easier. \b Example: # List out all packages with "server" in the name slcli order package-list --keyword server Package types can be used to remove unwanted packages \b Example: slcli order package-list --package_type BARE_METAL_CPU """ manager = ordering.OrderingManager(env.client) table = formatting.Table(COLUMNS) _filter = {'type': {'keyName': {'operation': '!= BLUEMIX_SERVICE'}}} if keyword: _filter['name'] = {'operation': '*= %s' % keyword} if package_type: _filter['type'] = {'keyName': {'operation': package_type}} packages = manager.list_packages(filter=_filter) for package in packages: table.add_row([ package['name'], package['keyName'], package['type']['keyName'] ]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/order/package_locations.py000066400000000000000000000016251324365065500245400ustar00rootroot00000000000000"""List packages.""" # :license: MIT, see LICENSE for more details. import click from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.managers import ordering COLUMNS = ['id', 'dc', 'description', 'keyName'] @click.command() @click.argument('package_keyname') @environment.pass_env def cli(env, package_keyname): """List Datacenters a package can be ordered in. Use the location Key Name to place orders """ manager = ordering.OrderingManager(env.client) table = formatting.Table(COLUMNS) locations = manager.package_locations(package_keyname) for region in locations: for datacenter in region['locations']: table.add_row([ datacenter['location']['id'], datacenter['location']['name'], region['description'], region['keyname'] ]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/order/place.py000066400000000000000000000105201324365065500221500ustar00rootroot00000000000000"""Verify or place an order.""" # :license: MIT, see LICENSE for more details. import json import click from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting from SoftLayer.managers import ordering COLUMNS = ['keyName', 'description', 'cost', ] @click.command() @click.argument('package_keyname') @click.argument('location') @click.option('--preset', help="The order preset (if required by the package)") @click.option('--verify', is_flag=True, help="Flag denoting whether or not to only verify the order, not place it") @click.option('--billing', type=click.Choice(['hourly', 'monthly']), default='hourly', show_default=True, help="Billing rate") @click.option('--complex-type', help=("The complex type of the order. This typically begins" " with 'SoftLayer_Container_Product_Order_'.")) @click.option('--extras', help="JSON string denoting extra data that needs to be sent with the order") @click.argument('order_items', nargs=-1) @environment.pass_env def cli(env, package_keyname, location, preset, verify, billing, complex_type, extras, order_items): """Place or verify an order. This CLI command is used for placing/verifying an order of the specified package in the given location (denoted by a datacenter's long name). Orders made via the CLI can then be converted to be made programmatically by calling SoftLayer.OrderingManager.place_order() with the same keynames. Packages for ordering can be retrived from `slcli order package-list` Presets for ordering can be retrieved from `slcli order preset-list` (not all packages have presets) Items can be retrieved from `slcli order item-list`. In order to find required items for the order, use `slcli order category-list`, and then provide the --category option for each category code in `slcli order item-list`. \b Example: # Order an hourly VSI with 4 CPU, 16 GB RAM, 100 GB SAN disk, # Ubuntu 16.04, and 1 Gbps public & private uplink in dal13 slcli order place --billing hourly CLOUD_SERVER DALLAS13 \\ GUEST_CORES_4 \\ RAM_16_GB \\ REBOOT_REMOTE_CONSOLE \\ 1_GBPS_PUBLIC_PRIVATE_NETWORK_UPLINKS \\ BANDWIDTH_0_GB_2 \\ 1_IP_ADDRESS \\ GUEST_DISK_100_GB_SAN \\ OS_UBUNTU_16_04_LTS_XENIAL_XERUS_MINIMAL_64_BIT_FOR_VSI \\ MONITORING_HOST_PING \\ NOTIFICATION_EMAIL_AND_TICKET \\ AUTOMATED_NOTIFICATION \\ UNLIMITED_SSL_VPN_USERS_1_PPTP_VPN_USER_PER_ACCOUNT \\ NESSUS_VULNERABILITY_ASSESSMENT_REPORTING \\ --extras '{"virtualGuests": [{"hostname": "test", "domain": "softlayer.com"}]}' \\ --complex-type SoftLayer_Container_Product_Order_Virtual_Guest """ manager = ordering.OrderingManager(env.client) if extras: extras = json.loads(extras) args = (package_keyname, location, order_items) kwargs = {'preset_keyname': preset, 'extras': extras, 'quantity': 1, 'complex_type': complex_type, 'hourly': True if billing == 'hourly' else False} if verify: table = formatting.Table(COLUMNS) order_to_place = manager.verify_order(*args, **kwargs) for price in order_to_place['prices']: cost_key = 'hourlyRecurringFee' if billing == 'hourly' else 'recurringFee' table.add_row([ price['item']['keyName'], price['item']['description'], price[cost_key] if cost_key in price else formatting.blank() ]) else: if not (env.skip_confirmations or formatting.confirm( "This action will incur charges on your account. Continue?")): raise exceptions.CLIAbort("Aborting order.") order = manager.place_order(*args, **kwargs) table = formatting.KeyValueTable(['name', 'value']) table.align['name'] = 'r' table.align['value'] = 'l' table.add_row(['id', order['orderId']]) table.add_row(['created', order['orderDate']]) table.add_row(['status', order['placedOrder']['status']]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/order/preset_list.py000066400000000000000000000026531324365065500234310ustar00rootroot00000000000000"""List package presets.""" # :license: MIT, see LICENSE for more details. import click from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.managers import ordering COLUMNS = ['name', 'keyName', 'description', ] @click.command() @click.argument('package_keyname') @click.option('--keyword', help="A word (or string) used to filter preset names.") @environment.pass_env def cli(env, package_keyname, keyword): """List package presets. Package keynames can be retrieved from `slcli order package-list`. Some packages do not have presets. \b Example: # List the presets for Bare Metal servers slcli order preset-list BARE_METAL_SERVER The --keyword option can also be used for additional filtering on the returned presets. \b Example: # List the Bare Metal server presets that include a GPU slcli order preset-list BARE_METAL_SERVER --keyword gpu """ table = formatting.Table(COLUMNS) manager = ordering.OrderingManager(env.client) _filter = {} if keyword: _filter = {'activePresets': {'name': {'operation': '*= %s' % keyword}}} presets = manager.list_presets(package_keyname, filter=_filter) for preset in presets: table.add_row([ preset['name'], preset['keyName'], preset['description'] ]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/report/000077500000000000000000000000001324365065500207145ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/CLI/report/__init__.py000066400000000000000000000000171324365065500230230ustar00rootroot00000000000000"""Reports.""" softlayer-python-5.4.2/SoftLayer/CLI/report/bandwidth.py000066400000000000000000000174401324365065500232400ustar00rootroot00000000000000"""Metric Utilities""" from __future__ import print_function import datetime import itertools import sys import click from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer import utils # pylint: disable=unused-argument def _validate_datetime(ctx, param, value): try: return datetime.datetime.strptime(value, "%Y-%m-%d") except (ValueError, TypeError): pass try: return datetime.datetime.strptime(value, "%Y-%m-%d %H:%M:%S") except (ValueError, TypeError): raise click.BadParameter( "not in the format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'") def _get_pooled_bandwidth(env, start, end): call = env.client.call('Account', 'getVirtualDedicatedRacks', iter=True, mask='id,name,metricTrackingObjectId') types = [ {'keyName': 'PUBLICIN', 'name': 'publicIn', 'summaryType': 'sum'}, {'keyName': 'PUBLICOUT', 'name': 'publicOut', 'summaryType': 'sum'}, {'keyName': 'PRIVATEIN', 'name': 'privateIn', 'summaryType': 'sum'}, {'keyName': 'PRIVATEOUT', 'name': 'privateOut', 'summaryType': 'sum'}, ] with click.progressbar(list(call), label='Calculating for bandwidth pools', file=sys.stderr) as pools: for pool in pools: if not pool.get('metricTrackingObjectId'): continue yield { 'id': pool['id'], 'type': 'pool', 'name': pool['name'], 'data': env.client.call( 'Metric_Tracking_Object', 'getSummaryData', start.strftime('%Y-%m-%d %H:%M:%S %Z'), end.strftime('%Y-%m-%d %H:%M:%S %Z'), types, 300, id=pool['metricTrackingObjectId'], ), } def _get_hardware_bandwidth(env, start, end): hw_call = env.client.call( 'Account', 'getHardware', iter=True, mask='id,hostname,metricTrackingObject.id,' 'virtualRack[id,bandwidthAllotmentTypeId]') types = [ {'keyName': 'PUBLICIN', 'name': 'publicIn', 'summaryType': 'counter'}, {'keyName': 'PUBLICOUT', 'name': 'publicOut', 'summaryType': 'counter'}, {'keyName': 'PRIVATEIN', 'name': 'privateIn', 'summaryType': 'counter'}, {'keyName': 'PRIVATEOUT', 'name': 'privateOut', 'summaryType': 'counter'}, ] with click.progressbar(list(hw_call), label='Calculating for hardware', file=sys.stderr) as hws: for instance in hws: if not utils.lookup(instance, 'metricTrackingObject', 'id'): continue pool_name = None if utils.lookup(instance, 'virtualRack', 'bandwidthAllotmentTypeId') == 2: pool_name = utils.lookup(instance, 'virtualRack', 'name') yield { 'id': instance['id'], 'type': 'hardware', 'name': instance['hostname'], 'pool': pool_name, 'data': env.client.call( 'Metric_Tracking_Object', 'getSummaryData', start.strftime('%Y-%m-%d %H:%M:%S %Z'), end.strftime('%Y-%m-%d %H:%M:%S %Z'), types, 3600, id=instance['metricTrackingObject']['id'], ), } def _get_virtual_bandwidth(env, start, end): call = env.client.call( 'Account', 'getVirtualGuests', iter=True, mask='id,hostname,metricTrackingObjectId,' 'virtualRack[id,bandwidthAllotmentTypeId]') types = [ {'keyName': 'PUBLICIN_NET_OCTET', 'name': 'publicIn_net_octet', 'summaryType': 'sum'}, {'keyName': 'PUBLICOUT_NET_OCTET', 'name': 'publicOut_net_octet', 'summaryType': 'sum'}, {'keyName': 'PRIVATEIN_NET_OCTET', 'name': 'privateIn_net_octet', 'summaryType': 'sum'}, {'keyName': 'PRIVATEOUT_NET_OCTET', 'name': 'privateOut_net_octet', 'summaryType': 'sum'}, ] with click.progressbar(list(call), label='Calculating for virtual', file=sys.stderr) as vms: for instance in vms: metric_tracking_id = utils.lookup(instance, 'metricTrackingObjectId') if metric_tracking_id is None: continue pool_name = None if utils.lookup(instance, 'virtualRack', 'bandwidthAllotmentTypeId') == 2: pool_name = utils.lookup(instance, 'virtualRack', 'id') yield { 'id': instance['id'], 'type': 'virtual', 'name': instance['hostname'], 'pool': pool_name, 'data': env.client.call( 'Metric_Tracking_Object', 'getSummaryData', start.strftime('%Y-%m-%d %H:%M:%S %Z'), end.strftime('%Y-%m-%d %H:%M:%S %Z'), types, 3600, id=metric_tracking_id, ), } @click.command(short_help="Bandwidth report for every pool/server") @click.option( '--start', callback=_validate_datetime, default=( datetime.datetime.now() - datetime.timedelta(days=30) ).strftime('%Y-%m-%d'), help="datetime in the format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'") @click.option( '--end', callback=_validate_datetime, default=datetime.datetime.now().strftime('%Y-%m-%d'), help="datetime in the format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'") @click.option('--sortby', help='Column to sort by', default='hostname', show_default=True) @environment.pass_env def cli(env, start, end, sortby): """Bandwidth report for every pool/server. This reports on the total data transfered for each virtual sever, hardware server and bandwidth pool. """ env.err('Generating bandwidth report for %s to %s' % (start, end)) table = formatting.Table([ 'type', 'name', 'public_in', 'public_out', 'private_in', 'private_out', 'pool', ]) table.sortby = sortby def f_type(key, results): "Filter metric data by type" return (result['counter'] for result in results if result['type'] == key) try: for item in itertools.chain(_get_pooled_bandwidth(env, start, end), _get_virtual_bandwidth(env, start, end), _get_hardware_bandwidth(env, start, end)): pub_in = int(sum(f_type('publicIn_net_octet', item['data']))) pub_out = int(sum(f_type('publicOut_net_octet', item['data']))) pri_in = int(sum(f_type('privateIn_net_octet', item['data']))) pri_out = int(sum(f_type('privateOut_net_octet', item['data']))) table.add_row([ item['type'], item['name'], formatting.b_to_gb(pub_in), formatting.b_to_gb(pub_out), formatting.b_to_gb(pri_in), formatting.b_to_gb(pri_out), item.get('pool') or formatting.blank(), ]) except KeyboardInterrupt: env.err("Printing collected results and then aborting.") env.out(env.fmt(table)) softlayer-python-5.4.2/SoftLayer/CLI/routes.py000066400000000000000000000366531324365065500213110ustar00rootroot00000000000000""" SoftLayer.CLI.routes ~~~~~~~~~~~~~~~~~~~~~ This is how all commands are registered with the CLI. :license: MIT, see LICENSE for more details. """ ALL_ROUTES = [ ('shell', 'SoftLayer.shell.core:cli'), ('call-api', 'SoftLayer.CLI.call_api:cli'), ('virtual', 'SoftLayer.CLI.virt'), ('virtual:cancel', 'SoftLayer.CLI.virt.cancel:cli'), ('virtual:capture', 'SoftLayer.CLI.virt.capture:cli'), ('virtual:create', 'SoftLayer.CLI.virt.create:cli'), ('virtual:create-options', 'SoftLayer.CLI.virt.create_options:cli'), ('virtual:detail', 'SoftLayer.CLI.virt.detail:cli'), ('virtual:dns-sync', 'SoftLayer.CLI.virt.dns:cli'), ('virtual:edit', 'SoftLayer.CLI.virt.edit:cli'), ('virtual:list', 'SoftLayer.CLI.virt.list:cli'), ('virtual:pause', 'SoftLayer.CLI.virt.power:pause'), ('virtual:power-off', 'SoftLayer.CLI.virt.power:power_off'), ('virtual:power-on', 'SoftLayer.CLI.virt.power:power_on'), ('virtual:rescue', 'SoftLayer.CLI.virt.power:rescue'), ('virtual:resume', 'SoftLayer.CLI.virt.power:resume'), ('virtual:ready', 'SoftLayer.CLI.virt.ready:cli'), ('virtual:reboot', 'SoftLayer.CLI.virt.power:reboot'), ('virtual:reload', 'SoftLayer.CLI.virt.reload:cli'), ('virtual:upgrade', 'SoftLayer.CLI.virt.upgrade:cli'), ('virtual:credentials', 'SoftLayer.CLI.virt.credentials:cli'), ('dedicatedhost', 'SoftLayer.CLI.dedicatedhost'), ('dedicatedhost:list', 'SoftLayer.CLI.dedicatedhost.list:cli'), ('dedicatedhost:create', 'SoftLayer.CLI.dedicatedhost.create:cli'), ('dedicatedhost:create-options', 'SoftLayer.CLI.dedicatedhost.create_options:cli'), ('dedicatedhost:detail', 'SoftLayer.CLI.dedicatedhost.detail:cli'), ('cdn', 'SoftLayer.CLI.cdn'), ('cdn:detail', 'SoftLayer.CLI.cdn.detail:cli'), ('cdn:list', 'SoftLayer.CLI.cdn.list:cli'), ('cdn:load', 'SoftLayer.CLI.cdn.load:cli'), ('cdn:origin-add', 'SoftLayer.CLI.cdn.origin_add:cli'), ('cdn:origin-list', 'SoftLayer.CLI.cdn.origin_list:cli'), ('cdn:origin-remove', 'SoftLayer.CLI.cdn.origin_remove:cli'), ('cdn:purge', 'SoftLayer.CLI.cdn.purge:cli'), ('config', 'SoftLayer.CLI.config'), ('config:setup', 'SoftLayer.CLI.config.setup:cli'), ('config:show', 'SoftLayer.CLI.config.show:cli'), ('setup', 'SoftLayer.CLI.config.setup:cli'), ('dns', 'SoftLayer.CLI.dns'), ('dns:import', 'SoftLayer.CLI.dns.zone_import:cli'), ('dns:record-add', 'SoftLayer.CLI.dns.record_add:cli'), ('dns:record-edit', 'SoftLayer.CLI.dns.record_edit:cli'), ('dns:record-list', 'SoftLayer.CLI.dns.record_list:cli'), ('dns:record-remove', 'SoftLayer.CLI.dns.record_remove:cli'), ('dns:zone-create', 'SoftLayer.CLI.dns.zone_create:cli'), ('dns:zone-delete', 'SoftLayer.CLI.dns.zone_delete:cli'), ('dns:zone-list', 'SoftLayer.CLI.dns.zone_list:cli'), ('dns:zone-print', 'SoftLayer.CLI.dns.zone_print:cli'), ('block', 'SoftLayer.CLI.block'), ('block:access-authorize', 'SoftLayer.CLI.block.access.authorize:cli'), ('block:access-list', 'SoftLayer.CLI.block.access.list:cli'), ('block:access-revoke', 'SoftLayer.CLI.block.access.revoke:cli'), ('block:access-password', 'SoftLayer.CLI.block.access.password:cli'), ('block:replica-failback', 'SoftLayer.CLI.block.replication.failback:cli'), ('block:replica-failover', 'SoftLayer.CLI.block.replication.failover:cli'), ('block:replica-order', 'SoftLayer.CLI.block.replication.order:cli'), ('block:replica-partners', 'SoftLayer.CLI.block.replication.partners:cli'), ('block:replica-locations', 'SoftLayer.CLI.block.replication.locations:cli'), ('block:snapshot-cancel', 'SoftLayer.CLI.block.snapshot.cancel:cli'), ('block:snapshot-create', 'SoftLayer.CLI.block.snapshot.create:cli'), ('block:snapshot-delete', 'SoftLayer.CLI.block.snapshot.delete:cli'), ('block:snapshot-disable', 'SoftLayer.CLI.block.snapshot.disable:cli'), ('block:snapshot-enable', 'SoftLayer.CLI.block.snapshot.enable:cli'), ('block:snapshot-schedule-list', 'SoftLayer.CLI.block.snapshot.schedule_list:cli'), ('block:snapshot-list', 'SoftLayer.CLI.block.snapshot.list:cli'), ('block:snapshot-order', 'SoftLayer.CLI.block.snapshot.order:cli'), ('block:snapshot-restore', 'SoftLayer.CLI.block.snapshot.restore:cli'), ('block:volume-cancel', 'SoftLayer.CLI.block.cancel:cli'), ('block:volume-count', 'SoftLayer.CLI.block.count:cli'), ('block:volume-detail', 'SoftLayer.CLI.block.detail:cli'), ('block:volume-duplicate', 'SoftLayer.CLI.block.duplicate:cli'), ('block:volume-list', 'SoftLayer.CLI.block.list:cli'), ('block:volume-modify', 'SoftLayer.CLI.block.modify:cli'), ('block:volume-order', 'SoftLayer.CLI.block.order:cli'), ('block:volume-set-lun-id', 'SoftLayer.CLI.block.lun:cli'), ('file', 'SoftLayer.CLI.file'), ('file:access-authorize', 'SoftLayer.CLI.file.access.authorize:cli'), ('file:access-list', 'SoftLayer.CLI.file.access.list:cli'), ('file:access-revoke', 'SoftLayer.CLI.file.access.revoke:cli'), ('file:replica-failback', 'SoftLayer.CLI.file.replication.failback:cli'), ('file:replica-failover', 'SoftLayer.CLI.file.replication.failover:cli'), ('file:replica-order', 'SoftLayer.CLI.file.replication.order:cli'), ('file:replica-partners', 'SoftLayer.CLI.file.replication.partners:cli'), ('file:replica-locations', 'SoftLayer.CLI.file.replication.locations:cli'), ('file:snapshot-cancel', 'SoftLayer.CLI.file.snapshot.cancel:cli'), ('file:snapshot-create', 'SoftLayer.CLI.file.snapshot.create:cli'), ('file:snapshot-delete', 'SoftLayer.CLI.file.snapshot.delete:cli'), ('file:snapshot-disable', 'SoftLayer.CLI.file.snapshot.disable:cli'), ('file:snapshot-enable', 'SoftLayer.CLI.file.snapshot.enable:cli'), ('file:snapshot-schedule-list', 'SoftLayer.CLI.file.snapshot.schedule_list:cli'), ('file:snapshot-list', 'SoftLayer.CLI.file.snapshot.list:cli'), ('file:snapshot-order', 'SoftLayer.CLI.file.snapshot.order:cli'), ('file:snapshot-restore', 'SoftLayer.CLI.file.snapshot.restore:cli'), ('file:volume-cancel', 'SoftLayer.CLI.file.cancel:cli'), ('file:volume-count', 'SoftLayer.CLI.file.count:cli'), ('file:volume-detail', 'SoftLayer.CLI.file.detail:cli'), ('file:volume-duplicate', 'SoftLayer.CLI.file.duplicate:cli'), ('file:volume-list', 'SoftLayer.CLI.file.list:cli'), ('file:volume-modify', 'SoftLayer.CLI.file.modify:cli'), ('file:volume-order', 'SoftLayer.CLI.file.order:cli'), ('firewall', 'SoftLayer.CLI.firewall'), ('firewall:add', 'SoftLayer.CLI.firewall.add:cli'), ('firewall:cancel', 'SoftLayer.CLI.firewall.cancel:cli'), ('firewall:detail', 'SoftLayer.CLI.firewall.detail:cli'), ('firewall:edit', 'SoftLayer.CLI.firewall.edit:cli'), ('firewall:list', 'SoftLayer.CLI.firewall.list:cli'), ('globalip', 'SoftLayer.CLI.globalip'), ('globalip:assign', 'SoftLayer.CLI.globalip.assign:cli'), ('globalip:cancel', 'SoftLayer.CLI.globalip.cancel:cli'), ('globalip:create', 'SoftLayer.CLI.globalip.create:cli'), ('globalip:list', 'SoftLayer.CLI.globalip.list:cli'), ('globalip:unassign', 'SoftLayer.CLI.globalip.unassign:cli'), ('image', 'SoftLayer.CLI.image'), ('image:delete', 'SoftLayer.CLI.image.delete:cli'), ('image:detail', 'SoftLayer.CLI.image.detail:cli'), ('image:edit', 'SoftLayer.CLI.image.edit:cli'), ('image:list', 'SoftLayer.CLI.image.list:cli'), ('image:import', 'SoftLayer.CLI.image.import:cli'), ('image:export', 'SoftLayer.CLI.image.export:cli'), ('ipsec', 'SoftLayer.CLI.vpn.ipsec'), ('ipsec:configure', 'SoftLayer.CLI.vpn.ipsec.configure:cli'), ('ipsec:detail', 'SoftLayer.CLI.vpn.ipsec.detail:cli'), ('ipsec:list', 'SoftLayer.CLI.vpn.ipsec.list:cli'), ('ipsec:subnet-add', 'SoftLayer.CLI.vpn.ipsec.subnet.add:cli'), ('ipsec:subnet-remove', 'SoftLayer.CLI.vpn.ipsec.subnet.remove:cli'), ('ipsec:translation-add', 'SoftLayer.CLI.vpn.ipsec.translation.add:cli'), ('ipsec:translation-remove', 'SoftLayer.CLI.vpn.ipsec.translation.remove:cli'), ('ipsec:translation-update', 'SoftLayer.CLI.vpn.ipsec.translation.update:cli'), ('ipsec:update', 'SoftLayer.CLI.vpn.ipsec.update:cli'), ('loadbal', 'SoftLayer.CLI.loadbal'), ('loadbal:cancel', 'SoftLayer.CLI.loadbal.cancel:cli'), ('loadbal:create', 'SoftLayer.CLI.loadbal.create:cli'), ('loadbal:create-options', 'SoftLayer.CLI.loadbal.create_options:cli'), ('loadbal:detail', 'SoftLayer.CLI.loadbal.detail:cli'), ('loadbal:group-add', 'SoftLayer.CLI.loadbal.group_add:cli'), ('loadbal:group-delete', 'SoftLayer.CLI.loadbal.group_delete:cli'), ('loadbal:group-edit', 'SoftLayer.CLI.loadbal.group_edit:cli'), ('loadbal:group-reset', 'SoftLayer.CLI.loadbal.group_reset:cli'), ('loadbal:health-checks', 'SoftLayer.CLI.loadbal.health_checks:cli'), ('loadbal:list', 'SoftLayer.CLI.loadbal.list:cli'), ('loadbal:routing-methods', 'SoftLayer.CLI.loadbal.routing_methods:cli'), ('loadbal:routing-types', 'SoftLayer.CLI.loadbal.routing_types:cli'), ('loadbal:service-add', 'SoftLayer.CLI.loadbal.service_add:cli'), ('loadbal:service-delete', 'SoftLayer.CLI.loadbal.service_delete:cli'), ('loadbal:service-edit', 'SoftLayer.CLI.loadbal.service_edit:cli'), ('loadbal:service-toggle', 'SoftLayer.CLI.loadbal.service_toggle:cli'), ('messaging', 'SoftLayer.CLI.mq'), ('messaging:accounts-list', 'SoftLayer.CLI.mq.accounts_list:cli'), ('messaging:endpoints-list', 'SoftLayer.CLI.mq.endpoints_list:cli'), ('messaging:ping', 'SoftLayer.CLI.mq.ping:cli'), ('messaging:queue-add', 'SoftLayer.CLI.mq.queue_add:cli'), ('messaging:queue-detail', 'SoftLayer.CLI.mq.queue_detail:cli'), ('messaging:queue-edit', 'SoftLayer.CLI.mq.queue_edit:cli'), ('messaging:queue-list', 'SoftLayer.CLI.mq.queue_list:cli'), ('messaging:queue-pop', 'SoftLayer.CLI.mq.queue_pop:cli'), ('messaging:queue-push', 'SoftLayer.CLI.mq.queue_push:cli'), ('messaging:queue-remove', 'SoftLayer.CLI.mq.queue_remove:cli'), ('messaging:topic-add', 'SoftLayer.CLI.mq.topic_add:cli'), ('messaging:topic-detail', 'SoftLayer.CLI.mq.topic_detail:cli'), ('messaging:topic-list', 'SoftLayer.CLI.mq.topic_list:cli'), ('messaging:topic-push', 'SoftLayer.CLI.mq.topic_push:cli'), ('messaging:topic-remove', 'SoftLayer.CLI.mq.topic_remove:cli'), ('messaging:topic-subscribe', 'SoftLayer.CLI.mq.topic_subscribe:cli'), ('messaging:topic-unsubscribe', 'SoftLayer.CLI.mq.topic_unsubscribe:cli'), ('metadata', 'SoftLayer.CLI.metadata:cli'), ('nas', 'SoftLayer.CLI.nas'), ('nas:list', 'SoftLayer.CLI.nas.list:cli'), ('nas:credentials', 'SoftLayer.CLI.nas.credentials:cli'), ('object-storage', 'SoftLayer.CLI.object_storage'), ('object-storage:accounts', 'SoftLayer.CLI.object_storage.list_accounts:cli'), ('object-storage:endpoints', 'SoftLayer.CLI.object_storage.list_endpoints:cli'), ('order', 'SoftLayer.CLI.order'), ('order:category-list', 'SoftLayer.CLI.order.category_list:cli'), ('order:item-list', 'SoftLayer.CLI.order.item_list:cli'), ('order:package-list', 'SoftLayer.CLI.order.package_list:cli'), ('order:place', 'SoftLayer.CLI.order.place:cli'), ('order:preset-list', 'SoftLayer.CLI.order.preset_list:cli'), ('order:package-locations', 'SoftLayer.CLI.order.package_locations:cli'), ('rwhois', 'SoftLayer.CLI.rwhois'), ('rwhois:edit', 'SoftLayer.CLI.rwhois.edit:cli'), ('rwhois:show', 'SoftLayer.CLI.rwhois.show:cli'), ('hardware', 'SoftLayer.CLI.hardware'), ('hardware:cancel', 'SoftLayer.CLI.hardware.cancel:cli'), ('hardware:cancel-reasons', 'SoftLayer.CLI.hardware.cancel_reasons:cli'), ('hardware:create', 'SoftLayer.CLI.hardware.create:cli'), ('hardware:create-options', 'SoftLayer.CLI.hardware.create_options:cli'), ('hardware:detail', 'SoftLayer.CLI.hardware.detail:cli'), ('hardware:edit', 'SoftLayer.CLI.hardware.edit:cli'), ('hardware:list', 'SoftLayer.CLI.hardware.list:cli'), ('hardware:power-cycle', 'SoftLayer.CLI.hardware.power:power_cycle'), ('hardware:power-off', 'SoftLayer.CLI.hardware.power:power_off'), ('hardware:power-on', 'SoftLayer.CLI.hardware.power:power_on'), ('hardware:reboot', 'SoftLayer.CLI.hardware.power:reboot'), ('hardware:reload', 'SoftLayer.CLI.hardware.reload:cli'), ('hardware:credentials', 'SoftLayer.CLI.hardware.credentials:cli'), ('hardware:update-firmware', 'SoftLayer.CLI.hardware.update_firmware:cli'), ('hardware:rescue', 'SoftLayer.CLI.hardware.power:rescue'), ('hardware:ready', 'SoftLayer.CLI.hardware.ready:cli'), ('securitygroup', 'SoftLayer.CLI.securitygroup'), ('securitygroup:list', 'SoftLayer.CLI.securitygroup.list:cli'), ('securitygroup:detail', 'SoftLayer.CLI.securitygroup.detail:cli'), ('securitygroup:create', 'SoftLayer.CLI.securitygroup.create:cli'), ('securitygroup:edit', 'SoftLayer.CLI.securitygroup.edit:cli'), ('securitygroup:delete', 'SoftLayer.CLI.securitygroup.delete:cli'), ('securitygroup:rule-list', 'SoftLayer.CLI.securitygroup.rule:rule_list'), ('securitygroup:rule-add', 'SoftLayer.CLI.securitygroup.rule:add'), ('securitygroup:rule-edit', 'SoftLayer.CLI.securitygroup.rule:edit'), ('securitygroup:rule-remove', 'SoftLayer.CLI.securitygroup.rule:remove'), ('securitygroup:interface-list', 'SoftLayer.CLI.securitygroup.interface:interface_list'), ('securitygroup:interface-add', 'SoftLayer.CLI.securitygroup.interface:add'), ('securitygroup:interface-remove', 'SoftLayer.CLI.securitygroup.interface:remove'), ('sshkey', 'SoftLayer.CLI.sshkey'), ('sshkey:add', 'SoftLayer.CLI.sshkey.add:cli'), ('sshkey:remove', 'SoftLayer.CLI.sshkey.remove:cli'), ('sshkey:edit', 'SoftLayer.CLI.sshkey.edit:cli'), ('sshkey:list', 'SoftLayer.CLI.sshkey.list:cli'), ('sshkey:print', 'SoftLayer.CLI.sshkey.print:cli'), ('ssl', 'SoftLayer.CLI.ssl'), ('ssl:add', 'SoftLayer.CLI.ssl.add:cli'), ('ssl:download', 'SoftLayer.CLI.ssl.download:cli'), ('ssl:edit', 'SoftLayer.CLI.ssl.edit:cli'), ('ssl:list', 'SoftLayer.CLI.ssl.list:cli'), ('ssl:remove', 'SoftLayer.CLI.ssl.remove:cli'), ('subnet', 'SoftLayer.CLI.subnet'), ('subnet:cancel', 'SoftLayer.CLI.subnet.cancel:cli'), ('subnet:create', 'SoftLayer.CLI.subnet.create:cli'), ('subnet:detail', 'SoftLayer.CLI.subnet.detail:cli'), ('subnet:list', 'SoftLayer.CLI.subnet.list:cli'), ('subnet:lookup', 'SoftLayer.CLI.subnet.lookup:cli'), ('ticket', 'SoftLayer.CLI.ticket'), ('ticket:create', 'SoftLayer.CLI.ticket.create:cli'), ('ticket:detail', 'SoftLayer.CLI.ticket.detail:cli'), ('ticket:list', 'SoftLayer.CLI.ticket.list:cli'), ('ticket:update', 'SoftLayer.CLI.ticket.update:cli'), ('ticket:upload', 'SoftLayer.CLI.ticket.upload:cli'), ('ticket:subjects', 'SoftLayer.CLI.ticket.subjects:cli'), ('ticket:summary', 'SoftLayer.CLI.ticket.summary:cli'), ('ticket:attach', 'SoftLayer.CLI.ticket.attach:cli'), ('ticket:detach', 'SoftLayer.CLI.ticket.detach:cli'), ('vlan', 'SoftLayer.CLI.vlan'), ('vlan:detail', 'SoftLayer.CLI.vlan.detail:cli'), ('vlan:list', 'SoftLayer.CLI.vlan.list:cli'), ('summary', 'SoftLayer.CLI.summary:cli'), ('report', 'SoftLayer.CLI.report'), ('report:bandwidth', 'SoftLayer.CLI.report.bandwidth:cli'), ] ALL_ALIASES = { 'hw': 'hardware', 'lb': 'loadbal', 'meta': 'metadata', 'my': 'metadata', 'sg': 'securitygroup', 'server': 'hardware', 'vm': 'virtual', 'vs': 'virtual', 'dh': 'dedicatedhost', } softlayer-python-5.4.2/SoftLayer/CLI/rwhois/000077500000000000000000000000001324365065500207145ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/CLI/rwhois/__init__.py000066400000000000000000000000261324365065500230230ustar00rootroot00000000000000"""Referral Whois.""" softlayer-python-5.4.2/SoftLayer/CLI/rwhois/edit.py000066400000000000000000000035431324365065500222200ustar00rootroot00000000000000"""Edit the RWhois data on the account.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions @click.command() @click.option('--abuse', help='Set the abuse email address') @click.option('--address1', help='Update the address 1 field') @click.option('--address2', help='Update the address 2 field') @click.option('--city', help='Set the city name') @click.option('--company', help='Set the company name') @click.option('--country', help='Set the two-letter country code') @click.option('--firstname', help='Update the first name field') @click.option('--lastname', help='Update the last name field') @click.option('--postal', help='Set the postal code field') @click.option('--public/--private', default=None, help='Flags the address as a public or private residence.') @click.option('--state', help='Set the two-letter state code') @environment.pass_env def cli(env, abuse, address1, address2, city, company, country, firstname, lastname, postal, public, state): """Edit the RWhois data on the account.""" mgr = SoftLayer.NetworkManager(env.client) update = { 'abuse_email': abuse, 'address1': address1, 'address2': address2, 'company_name': company, 'city': city, 'country': country, 'first_name': firstname, 'last_name': lastname, 'postal_code': postal, 'state': state, 'private_residence': public, } if public is True: update['private_residence'] = False elif public is False: update['private_residence'] = True check = [x for x in update.values() if x is not None] if not check: raise exceptions.CLIAbort( "You must specify at least one field to update.") mgr.edit_rwhois(**update) softlayer-python-5.4.2/SoftLayer/CLI/rwhois/show.py000066400000000000000000000022041324365065500222440ustar00rootroot00000000000000"""Display the RWhois information for your account.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting @click.command() @environment.pass_env def cli(env): """Display the RWhois information for your account.""" mgr = SoftLayer.NetworkManager(env.client) result = mgr.get_rwhois() table = formatting.KeyValueTable(['name', 'value']) table.align['name'] = 'r' table.align['value'] = 'l' table.add_row(['Name', result['firstName'] + ' ' + result['lastName']]) table.add_row(['Company', result['companyName']]) table.add_row(['Abuse Email', result['abuseEmail']]) table.add_row(['Address 1', result['address1']]) if result.get('address2'): table.add_row(['Address 2', result['address2']]) table.add_row(['City', result['city']]) table.add_row(['State', result.get('state', '-')]) table.add_row(['Postal Code', result.get('postalCode', '-')]) table.add_row(['Country', result['country']]) table.add_row(['Private Residence', result['privateResidenceFlag']]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/securitygroup/000077500000000000000000000000001324365065500223255ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/CLI/securitygroup/__init__.py000066400000000000000000000000371324365065500244360ustar00rootroot00000000000000"""Network security groups.""" softlayer-python-5.4.2/SoftLayer/CLI/securitygroup/create.py000066400000000000000000000017511324365065500241460ustar00rootroot00000000000000"""Create security groups.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting @click.command() @click.option('--name', '-n', help="The name of the security group") @click.option('--description', '-d', help="The description of the security group") @environment.pass_env def cli(env, name, description): """Create a security group.""" mgr = SoftLayer.NetworkManager(env.client) result = mgr.create_securitygroup(name, description) table = formatting.KeyValueTable(['name', 'value']) table.align['name'] = 'r' table.align['value'] = 'l' table.add_row(['id', result['id']]) table.add_row(['name', result.get('name') or formatting.blank()]) table.add_row(['description', result.get('description') or formatting.blank()]) table.add_row(['created', result['createDate']]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/securitygroup/delete.py000066400000000000000000000007731324365065500241500ustar00rootroot00000000000000"""Delete a security group.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions @click.command() @click.argument('securitygroup_id') @environment.pass_env def cli(env, securitygroup_id): """Deletes the given security group""" mgr = SoftLayer.NetworkManager(env.client) if not mgr.delete_securitygroup(securitygroup_id): raise exceptions.CLIAbort("Failed to delete security group") softlayer-python-5.4.2/SoftLayer/CLI/securitygroup/detail.py000066400000000000000000000052241324365065500241440ustar00rootroot00000000000000"""Get details about a security group.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting @click.command() @click.argument('identifier') @environment.pass_env def cli(env, identifier): """Get details about a security group.""" mgr = SoftLayer.NetworkManager(env.client) secgroup = mgr.get_securitygroup(identifier) table = formatting.KeyValueTable(['name', 'value']) table.align['name'] = 'r' table.align['value'] = 'l' table.add_row(['id', secgroup['id']]) table.add_row(['name', secgroup.get('name') or formatting.blank()]) table.add_row(['description', secgroup.get('description') or formatting.blank()]) rule_table = formatting.Table(['id', 'remoteIp', 'remoteGroupId', 'direction', 'ethertype', 'portRangeMin', 'portRangeMax', 'protocol']) for rule in secgroup.get('rules', []): rg_id = rule.get('remoteGroup', {}).get('id') or formatting.blank() port_min = rule.get('portRangeMin') port_max = rule.get('portRangeMax') if port_min is None: port_min = formatting.blank() if port_max is None: port_max = formatting.blank() rule_table.add_row([rule['id'], rule.get('remoteIp') or formatting.blank(), rule.get('remoteGroupId', rg_id), rule['direction'], rule.get('ethertype') or formatting.blank(), port_min, port_max, rule.get('protocol') or formatting.blank()]) table.add_row(['rules', rule_table]) vsi_table = formatting.Table(['id', 'hostname', 'interface', 'ipAddress']) for binding in secgroup.get('networkComponentBindings', []): try: vsi = binding['networkComponent']['guest'] vsi_id = vsi['id'] hostname = vsi['hostname'] interface = ('PRIVATE' if binding['networkComponent']['port'] == 0 else 'PUBLIC') ip_address = (vsi['primaryBackendIpAddress'] if binding['networkComponent']['port'] == 0 else vsi['primaryIpAddress']) except KeyError: vsi_id = "N/A" hostname = "Not enough permission to view" interface = "N/A" ip_address = "N/A" vsi_table.add_row([vsi_id, hostname, interface, ip_address]) table.add_row(['servers', vsi_table]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/securitygroup/edit.py000066400000000000000000000014571324365065500236330ustar00rootroot00000000000000"""Edit details of a security group.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions @click.command() @click.argument('group_id') @click.option('--name', '-n', help="The name of the security group") @click.option('--description', '-d', help="The description of the security group") @environment.pass_env def cli(env, group_id, name, description): """Edit details of a security group.""" mgr = SoftLayer.NetworkManager(env.client) data = {} if name: data['name'] = name if description: data['description'] = description if not mgr.edit_securitygroup(group_id, **data): raise exceptions.CLIAbort("Failed to edit security group") softlayer-python-5.4.2/SoftLayer/CLI/securitygroup/interface.py000066400000000000000000000123051324365065500246400ustar00rootroot00000000000000"""Security group interface operations.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting COLUMNS = ['networkComponentId', 'virtualServerId', 'hostname', 'interface', 'ipAddress', ] @click.command() @click.argument('securitygroup_id') @click.option('--sortby', help='Column to sort by', type=click.Choice(COLUMNS)) @environment.pass_env def interface_list(env, securitygroup_id, sortby): """List interfaces associated with security groups.""" mgr = SoftLayer.NetworkManager(env.client) table = formatting.Table(COLUMNS) table.sortby = sortby mask = ( '''networkComponentBindings[ networkComponentId, networkComponent[ id, port, guest[ id, hostname, primaryBackendIpAddress, primaryIpAddress ] ] ]''' ) secgroup = mgr.get_securitygroup(securitygroup_id, mask=mask) for binding in secgroup.get('networkComponentBindings', []): interface_id = binding['networkComponentId'] try: interface = binding['networkComponent'] vsi = interface['guest'] vsi_id = vsi['id'] hostname = vsi['hostname'] priv_pub = 'PRIVATE' if interface['port'] == 0 else 'PUBLIC' ip_address = (vsi['primaryBackendIpAddress'] if interface['port'] == 0 else vsi['primaryIpAddress']) except KeyError: vsi_id = "N/A" hostname = "Not enough permission to view" priv_pub = "N/A" ip_address = "N/A" table.add_row([ interface_id, vsi_id, hostname, priv_pub, ip_address ]) env.fout(table) @click.command() @click.argument('securitygroup_id') @click.option('--network-component', '-n', help=('The network component to associate ' 'with the security group')) @click.option('--server', '-s', help='The server ID to associate with the security group') @click.option('--interface', '-i', help='The interface of the server to associate (public/private)') @environment.pass_env def add(env, securitygroup_id, network_component, server, interface): """Attach an interface to a security group.""" _validate_args(network_component, server, interface) mgr = SoftLayer.NetworkManager(env.client) component_id = _get_component_id(env, network_component, server, interface) success = mgr.attach_securitygroup_component(securitygroup_id, component_id) if not success: raise exceptions.CLIAbort("Could not attach network component") @click.command() @click.argument('securitygroup_id') @click.option('--network-component', '-n', help=('The network component to remove from ' 'with the security group')) @click.option('--server', '-s', help='The server ID to remove from the security group') @click.option('--interface', '-i', help='The interface of the server to remove (public/private)') @environment.pass_env def remove(env, securitygroup_id, network_component, server, interface): """Detach an interface from a security group.""" _validate_args(network_component, server, interface) mgr = SoftLayer.NetworkManager(env.client) component_id = _get_component_id(env, network_component, server, interface) success = mgr.detach_securitygroup_component(securitygroup_id, component_id) if not success: raise exceptions.CLIAbort("Could not detach network component") def _validate_args(network_component, server, interface): use_server = bool(server and interface and not network_component) use_component = bool(network_component and not bool(server or interface)) if not use_server and not use_component: raise exceptions.CLIAbort("Must set either --network-component " "or both --server and --interface") if use_server and interface.lower() not in ['public', 'private']: raise exceptions.CLIAbort( "Interface must be either 'public' or 'private'") def _get_component_id(env, network_component, server, interface): use_server = bool(server and interface and not network_component) vs_mgr = SoftLayer.VSManager(env.client) if use_server: vs_mask = 'networkComponents[id, port]' vsi = vs_mgr.get_instance(server, mask=vs_mask) port = 0 if interface.lower() == 'private' else 1 component = [c for c in vsi['networkComponents'] if c['port'] == port] if len(component) != 1: raise exceptions.CLIAbort("Instance %s has no %s interface" % (server, interface)) component_id = component[0]['id'] else: component_id = network_component return component_id softlayer-python-5.4.2/SoftLayer/CLI/securitygroup/list.py000066400000000000000000000014731324365065500236570ustar00rootroot00000000000000"""List securitygroups.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting COLUMNS = ['id', 'name', 'description', ] @click.command() @click.option('--sortby', help='Column to sort by', type=click.Choice(COLUMNS)) @environment.pass_env def cli(env, sortby): """List security groups.""" mgr = SoftLayer.NetworkManager(env.client) table = formatting.Table(COLUMNS) table.sortby = sortby sgs = mgr.list_securitygroups() for secgroup in sgs: table.add_row([ secgroup['id'], secgroup.get('name') or formatting.blank(), secgroup.get('description') or formatting.blank(), ]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/securitygroup/rule.py000066400000000000000000000125171324365065500236540ustar00rootroot00000000000000"""Manage security group rules.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting COLUMNS = ['id', 'remoteIp', 'remoteGroupId', 'direction', 'ethertype', 'portRangeMin', 'portRangeMax', 'protocol'] @click.command() @click.argument('securitygroup_id') @click.option('--sortby', help='Column to sort by', type=click.Choice(COLUMNS)) @environment.pass_env def rule_list(env, securitygroup_id, sortby): """List security group rules.""" mgr = SoftLayer.NetworkManager(env.client) table = formatting.Table(COLUMNS) table.sortby = sortby rules = mgr.list_securitygroup_rules(securitygroup_id) for rule in rules: port_min = rule.get('portRangeMin') port_max = rule.get('portRangeMax') if port_min is None: port_min = formatting.blank() if port_max is None: port_max = formatting.blank() table.add_row([ rule['id'], rule.get('remoteIp') or formatting.blank(), rule.get('remoteGroupId') or formatting.blank(), rule['direction'], rule.get('ethertype') or formatting.blank(), port_min, port_max, rule.get('protocol') or formatting.blank() ]) env.fout(table) @click.command() @click.argument('securitygroup_id') @click.option('--remote-ip', '-r', help='The remote IP/CIDR to enforce') @click.option('--remote-group', '-s', type=click.INT, help='The ID of the remote security group to enforce') @click.option('--direction', '-d', help=('The direction of traffic to enforce ' '(ingress, egress)')) @click.option('--ethertype', '-e', help='The ethertype (IPv4 or IPv6) to enforce') @click.option('--port-max', '-M', type=click.INT, help=('The upper port bound to enforce. When the protocol is ICMP, ' 'this specifies the ICMP code to permit')) @click.option('--port-min', '-m', type=click.INT, help=('The lower port bound to enforce. When the protocol is ICMP, ' 'this specifies the ICMP type to permit')) @click.option('--protocol', '-p', help='The protocol (icmp, tcp, udp) to enforce') @environment.pass_env def add(env, securitygroup_id, remote_ip, remote_group, direction, ethertype, port_max, port_min, protocol): """Add a security group rule to a security group. \b Examples: # Add an SSH rule (TCP port 22) to a security group slcli sg rule-add 384727 \\ --direction ingress \\ --protocol tcp \\ --port-min 22 \\ --port-max 22 \b # Add a ping rule (ICMP type 8 code 0) to a security group slcli sg rule-add 384727 \\ --direction ingress \\ --protocol icmp \\ --port-min 8 \\ --port-max 0 """ mgr = SoftLayer.NetworkManager(env.client) ret = mgr.add_securitygroup_rule(securitygroup_id, remote_ip, remote_group, direction, ethertype, port_max, port_min, protocol) if not ret: raise exceptions.CLIAbort("Failed to add security group rule") @click.command() @click.argument('securitygroup_id') @click.argument('rule_id') @click.option('--remote-ip', '-r', help='The remote IP/CIDR to enforce') @click.option('--remote-group', '-s', help='The ID of the remote security group to enforce') @click.option('--direction', '-d', help='The direction of traffic to enforce') @click.option('--ethertype', '-e', help='The ethertype (IPv4 or IPv6) to enforce') @click.option('--port-max', '-M', help='The upper port bound to enforce') @click.option('--port-min', '-m', help='The lower port bound to enforce') @click.option('--protocol', '-p', help='The protocol (icmp, tcp, udp) to enforce') @environment.pass_env def edit(env, securitygroup_id, rule_id, remote_ip, remote_group, direction, ethertype, port_max, port_min, protocol): """Edit a security group rule in a security group.""" mgr = SoftLayer.NetworkManager(env.client) data = {} if remote_ip: data['remote_ip'] = remote_ip if remote_group: data['remote_group'] = remote_group if direction: data['direction'] = direction if ethertype: data['ethertype'] = ethertype if port_max is not None: data['port_max'] = port_max if port_min is not None: data['port_min'] = port_min if protocol: data['protocol'] = protocol if not mgr.edit_securitygroup_rule(securitygroup_id, rule_id, **data): raise exceptions.CLIAbort("Failed to edit security group rule") @click.command() @click.argument('securitygroup_id') @click.argument('rule_id') @environment.pass_env def remove(env, securitygroup_id, rule_id): """Remove a rule from a security group.""" mgr = SoftLayer.NetworkManager(env.client) if not mgr.remove_securitygroup_rule(securitygroup_id, rule_id): raise exceptions.CLIAbort("Failed to remove security group rule") softlayer-python-5.4.2/SoftLayer/CLI/sshkey/000077500000000000000000000000001324365065500207075ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/CLI/sshkey/__init__.py000066400000000000000000000000201324365065500230100ustar00rootroot00000000000000"""SSH Keys.""" softlayer-python-5.4.2/SoftLayer/CLI/sshkey/add.py000066400000000000000000000023511324365065500220120ustar00rootroot00000000000000"""Add a new SSH key.""" # :license: MIT, see LICENSE for more details. from os import path import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions @click.command() @click.argument('label') @click.option('--in-file', '-f', type=click.Path(exists=True), help="The id_rsa.pub file to import for this key") @click.option('--key', '-k', help="The actual SSH key") @click.option('--note', help="Extra note that will be associated with key") @environment.pass_env def cli(env, label, in_file, key, note): """Add a new SSH key.""" if in_file is None and key is None: raise exceptions.ArgumentError( 'Either [-f | --in-file] or [-k | --key] arguments are required to add a key' ) if in_file and key: raise exceptions.ArgumentError( '[-f | --in-file] is not allowed with [-k | --key]' ) if key: key_text = key else: key_file = open(path.expanduser(in_file), 'rU') key_text = key_file.read().strip() key_file.close() mgr = SoftLayer.SshKeyManager(env.client) result = mgr.add_key(key_text, label, note) env.fout("SSH key added: %s" % result.get('fingerprint')) softlayer-python-5.4.2/SoftLayer/CLI/sshkey/edit.py000066400000000000000000000013031324365065500222030ustar00rootroot00000000000000"""Edits an SSH key.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import helpers @click.command() @click.argument('identifier') @click.option('--label', '-k', help="The new label for the key") @click.option('--note', help="New notes for the key") @environment.pass_env def cli(env, identifier, label, note): """Edits an SSH key.""" mgr = SoftLayer.SshKeyManager(env.client) key_id = helpers.resolve_id(mgr.resolve_ids, identifier, 'SshKey') if not mgr.edit_key(key_id, label=label, notes=note): raise exceptions.CLIAbort('Failed to edit SSH key') softlayer-python-5.4.2/SoftLayer/CLI/sshkey/list.py000066400000000000000000000015651324365065500222430ustar00rootroot00000000000000"""List SSH keys.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting @click.command() @click.option('--sortby', help='Column to sort by', type=click.Choice(['id', 'label', 'fingerprint', 'notes'])) @environment.pass_env def cli(env, sortby): """List SSH keys.""" mgr = SoftLayer.SshKeyManager(env.client) keys = mgr.list_keys() table = formatting.Table(['id', 'label', 'fingerprint', 'notes']) table.sortby = sortby for key in keys: table.add_row([key['id'], key.get('label'), key.get('fingerprint'), key.get('notes', '-')]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/sshkey/print.py000066400000000000000000000020331324365065500224130ustar00rootroot00000000000000"""Prints out an SSH key to the screen.""" # :license: MIT, see LICENSE for more details. from os import path import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.CLI import helpers @click.command() @click.argument('identifier') @click.option('--out-file', '-f', type=click.Path(exists=True, writable=True), help="The public SSH key will be written to this file") @environment.pass_env def cli(env, identifier, out_file): """Prints out an SSH key to the screen.""" mgr = SoftLayer.SshKeyManager(env.client) key_id = helpers.resolve_id(mgr.resolve_ids, identifier, 'SshKey') key = mgr.get_key(key_id) if out_file: with open(path.expanduser(out_file), 'w') as pub_file: pub_file.write(key['key']) table = formatting.KeyValueTable(['name', 'value']) table.add_row(['id', key['id']]) table.add_row(['label', key.get('label')]) table.add_row(['notes', key.get('notes', '-')]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/sshkey/remove.py000066400000000000000000000012321324365065500225540ustar00rootroot00000000000000"""Permanently removes an SSH key.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting from SoftLayer.CLI import helpers @click.command() @click.argument('identifier') @environment.pass_env def cli(env, identifier): """Permanently removes an SSH key.""" mgr = SoftLayer.SshKeyManager(env.client) key_id = helpers.resolve_id(mgr.resolve_ids, identifier, 'SshKey') if not (env.skip_confirmations or formatting.no_going_back(key_id)): raise exceptions.CLIAbort('Aborted') mgr.delete_key(key_id) softlayer-python-5.4.2/SoftLayer/CLI/ssl/000077500000000000000000000000001324365065500202025ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/CLI/ssl/__init__.py000066400000000000000000000000301324365065500223040ustar00rootroot00000000000000"""SSL Certificates.""" softlayer-python-5.4.2/SoftLayer/CLI/ssl/add.py000066400000000000000000000023661324365065500213130ustar00rootroot00000000000000"""Add and upload SSL certificate details.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment @click.command() @click.option('--crt', type=click.Path(exists=True), help="Certificate file") @click.option('--csr', type=click.Path(exists=True), help="Certificate Signing Request file") @click.option('--icc', type=click.Path(exists=True), help="Intermediate Certificate file") @click.option('--key', type=click.Path(exists=True), help="Private Key file") @click.option('--notes', help="Additional notes") @environment.pass_env def cli(env, crt, csr, icc, key, notes): """Add and upload SSL certificate details.""" template = { 'intermediateCertificate': '', 'certificateSigningRequest': '', 'notes': notes, } template['certificate'] = open(crt).read() template['privateKey'] = open(key).read() if csr: body = open(csr).read() template['certificateSigningRequest'] = body if icc: body = open(icc).read() template['intermediateCertificate'] = body manager = SoftLayer.SSLManager(env.client) manager.add_certificate(template) softlayer-python-5.4.2/SoftLayer/CLI/ssl/download.py000066400000000000000000000020571324365065500223670ustar00rootroot00000000000000"""Download SSL certificate and key file.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment @click.command() @click.argument('identifier') @environment.pass_env def cli(env, identifier): """Download SSL certificate and key file.""" manager = SoftLayer.SSLManager(env.client) certificate = manager.get_certificate(identifier) write_cert(certificate['commonName'] + '.crt', certificate['certificate']) write_cert(certificate['commonName'] + '.key', certificate['privateKey']) if 'intermediateCertificate' in certificate: write_cert(certificate['commonName'] + '.icc', certificate['intermediateCertificate']) if 'certificateSigningRequest' in certificate: write_cert(certificate['commonName'] + '.csr', certificate['certificateSigningRequest']) def write_cert(filename, content): """Writes certificate body to the given file path.""" with open(filename, 'w') as cert_file: cert_file.write(content) softlayer-python-5.4.2/SoftLayer/CLI/ssl/edit.py000066400000000000000000000023061324365065500215020ustar00rootroot00000000000000"""Edit SSL certificate.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment @click.command() @click.argument('identifier') @click.option('--crt', type=click.Path(exists=True), help="Certificate file") @click.option('--csr', type=click.Path(exists=True), help="Certificate Signing Request file") @click.option('--icc', type=click.Path(exists=True), help="Intermediate Certificate file") @click.option('--key', type=click.Path(exists=True), help="Private Key file") @click.option('--notes', help="Additional notes") @environment.pass_env def cli(env, identifier, crt, csr, icc, key, notes): """Edit SSL certificate.""" template = {'id': identifier} if crt: template['certificate'] = open(crt).read() if key: template['privateKey'] = open(key).read() if csr: template['certificateSigningRequest'] = open(csr).read() if icc: template['intermediateCertificate'] = open(icc).read() if notes: template['notes'] = notes manager = SoftLayer.SSLManager(env.client) manager.edit_certificate(template) softlayer-python-5.4.2/SoftLayer/CLI/ssl/list.py000066400000000000000000000024221324365065500215270ustar00rootroot00000000000000"""List SSL certificates.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting @click.command() @click.option('--status', default="all", show_default=True, type=click.Choice(['all', 'valid', 'expired']), help="Show certificates with this status") @click.option('--sortby', type=click.Choice(['id', 'common_name', 'days_until_expire', 'notes']), help="Column to sort by") @environment.pass_env def cli(env, status, sortby): """List SSL certificates.""" manager = SoftLayer.SSLManager(env.client) certificates = manager.list_certs(status) table = formatting.Table(['id', 'common_name', 'days_until_expire', 'notes']) for certificate in certificates: table.add_row([ certificate['id'], certificate['commonName'], certificate['validityDays'], certificate.get('notes', formatting.blank()) ]) table.sortby = sortby env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/ssl/remove.py000066400000000000000000000010621324365065500220500ustar00rootroot00000000000000"""Remove SSL certificate.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting @click.command() @click.argument('identifier') @environment.pass_env def cli(env, identifier): """Remove SSL certificate.""" manager = SoftLayer.SSLManager(env.client) if not (env.skip_confirmations or formatting.no_going_back('yes')): raise exceptions.CLIAbort("Aborted.") manager.remove_certificate(identifier) softlayer-python-5.4.2/SoftLayer/CLI/storage_utils.py000066400000000000000000000057231324365065500226460ustar00rootroot00000000000000"""Utility functions for use with File and Block commands.""" # :license: MIT, see LICENSE for more details. from SoftLayer.CLI import columns as column_helper def _format_name(obj): if obj['type'] == 'VIRTUAL': return "{0}.{1}".format(obj['hostname'], obj['domain']) elif obj['type'] == 'HARDWARE': return "{0}.{1}".format(obj['hostname'], obj['domain']) elif obj['type'] == 'SUBNET': name = "{0}/{1}".format( obj['networkIdentifier'], obj['cidr'] ) if 'note' in obj.keys(): name = "{0} ({1})".format(name, obj['note']) return name elif obj['type'] == 'IP': name = obj['ipAddress'] if 'note' in obj.keys(): name = "{0} ({1})".format(name, obj['note']) return name else: raise Exception('Unknown type %s' % obj['type']) COLUMNS = [ column_helper.Column('id', ('id',)), column_helper.Column('name', _format_name, """ allowedVirtualGuests[hostname,domain], allowedHardware[hostname,domain], allowedSubnets[networkIdentifier,cidr,note], allowedIpAddresses[ipAddress,note], """), column_helper.Column('type', ('type',)), column_helper.Column( 'private_ip_address', ('primaryBackendIpAddress',), """ allowedVirtualGuests.primaryBackendIpAddress allowedHardware.primaryBackendIpAddress allowedSubnets.primaryBackendIpAddress allowedIpAddresses.primaryBackendIpAddress """), column_helper.Column( 'source_subnet', ('allowedHost', 'sourceSubnet',), """ allowedVirtualGuests.allowedHost.sourceSubnet allowedHardware.allowedHost.sourceSubnet allowedSubnets.allowedHost.sourceSubnet allowedIpAddresses.allowedHost.sourceSubnet """), column_helper.Column( 'host_iqn', ('allowedHost', 'name',), """ allowedVirtualGuests.allowedHost.name allowedHardware.allowedHost.name allowedSubnets.allowedHost.name allowedIpAddresses.allowedHost.name """), column_helper.Column( 'username', ('allowedHost', 'credential', 'username',), """ allowedVirtualGuests.allowedHost.credential.username allowedHardware.allowedHost.credential.username allowedSubnets.allowedHost.credential.username allowedIpAddresses.allowedHost.credential.username """), column_helper.Column( 'password', ('allowedHost', 'credential', 'password',), """ allowedVirtualGuests.allowedHost.credential.password allowedHardware.allowedHost.credential.password allowedSubnets.allowedHost.credential.password allowedIpAddresses.allowedHost.credential.password """), column_helper.Column( 'allowed_host_id', ('allowedHost', 'id',), """ allowedVirtualGuests.allowedHost.id allowedHardware.allowedHost.id allowedSubnets.allowedHost.id allowedIpAddresses.allowedHost.id """), ] DEFAULT_COLUMNS = [ 'id', 'name', 'type', 'private_ip_address', 'source_subnet', 'host_iqn', 'username', 'password', 'allowed_host_id', ] softlayer-python-5.4.2/SoftLayer/CLI/subnet/000077500000000000000000000000001324365065500207015ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/CLI/subnet/__init__.py000066400000000000000000000000271324365065500230110ustar00rootroot00000000000000"""Network subnets.""" softlayer-python-5.4.2/SoftLayer/CLI/subnet/cancel.py000066400000000000000000000012711324365065500225010ustar00rootroot00000000000000"""Cancel a subnet.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting from SoftLayer.CLI import helpers @click.command() @click.argument('identifier') @environment.pass_env def cli(env, identifier): """Cancel a subnet.""" mgr = SoftLayer.NetworkManager(env.client) subnet_id = helpers.resolve_id(mgr.resolve_subnet_ids, identifier, name='subnet') if not (env.skip_confirmations or formatting.no_going_back(subnet_id)): raise exceptions.CLIAbort('Aborted') mgr.cancel_subnet(subnet_id) softlayer-python-5.4.2/SoftLayer/CLI/subnet/create.py000066400000000000000000000040261324365065500225200ustar00rootroot00000000000000"""Add a new subnet to your account.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting @click.command(short_help="Add a new subnet to your account") @click.argument('network', type=click.Choice(['public', 'private'])) @click.argument('quantity', type=click.INT) @click.argument('vlan-id') @click.option('--v6', '--ipv6', is_flag=True, help="Order IPv6 Addresses") @click.option('--test', is_flag=True, help="Do not order the subnet; just get a quote") @environment.pass_env def cli(env, network, quantity, vlan_id, ipv6, test): """Add a new subnet to your account. Valid quantities vary by type. \b Type - Valid Quantities (IPv4) public - 4, 8, 16, 32 private - 4, 8, 16, 32, 64 \b Type - Valid Quantities (IPv6) public - 64 """ mgr = SoftLayer.NetworkManager(env.client) if not (test or env.skip_confirmations): if not formatting.confirm("This action will incur charges on your " "account. Continue?"): raise exceptions.CLIAbort('Cancelling order.') version = 4 if ipv6: version = 6 result = mgr.add_subnet(network, quantity=quantity, vlan_id=vlan_id, version=version, test_order=test) if not result: raise exceptions.CLIAbort( 'Unable to place order: No valid price IDs found.') table = formatting.Table(['Item', 'cost']) table.align['Item'] = 'r' table.align['cost'] = 'r' total = 0.0 if 'prices' in result: for price in result['prices']: total += float(price.get('recurringFee', 0.0)) rate = "%.2f" % float(price['recurringFee']) table.add_row([price['item']['description'], rate]) table.add_row(['Total monthly cost', "%.2f" % total]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/subnet/detail.py000066400000000000000000000052561324365065500225250ustar00rootroot00000000000000"""Get subnet details.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.CLI import helpers from SoftLayer import utils @click.command() @click.argument('identifier') @click.option('--no-vs', is_flag=True, help="Hide virtual server listing") @click.option('--no-hardware', is_flag=True, help="Hide hardware listing") @environment.pass_env def cli(env, identifier, no_vs, no_hardware): """Get subnet details.""" mgr = SoftLayer.NetworkManager(env.client) subnet_id = helpers.resolve_id(mgr.resolve_subnet_ids, identifier, name='subnet') subnet = mgr.get_subnet(subnet_id) table = formatting.KeyValueTable(['name', 'value']) table.align['name'] = 'r' table.align['value'] = 'l' table.add_row(['id', subnet['id']]) table.add_row(['identifier', '%s/%s' % (subnet['networkIdentifier'], str(subnet['cidr']))]) table.add_row(['subnet type', subnet['subnetType']]) table.add_row(['network space', utils.lookup(subnet, 'networkVlan', 'networkSpace')]) table.add_row(['gateway', subnet.get('gateway', formatting.blank())]) table.add_row(['broadcast', subnet.get('broadcastAddress', formatting.blank())]) table.add_row(['datacenter', subnet['datacenter']['name']]) table.add_row(['usable ips', subnet.get('usableIpAddressCount', formatting.blank())]) if not no_vs: if subnet['virtualGuests']: vs_table = formatting.Table(['hostname', 'domain', 'public_ip', 'private_ip']) for vsi in subnet['virtualGuests']: vs_table.add_row([vsi['hostname'], vsi['domain'], vsi.get('primaryIpAddress'), vsi.get('primaryBackendIpAddress')]) table.add_row(['vs', vs_table]) else: table.add_row(['vs', 'none']) if not no_hardware: if subnet['hardware']: hw_table = formatting.Table(['hostname', 'domain', 'public_ip', 'private_ip']) for hardware in subnet['hardware']: hw_table.add_row([hardware['hostname'], hardware['domain'], hardware.get('primaryIpAddress'), hardware.get('primaryBackendIpAddress')]) table.add_row(['hardware', hw_table]) else: table.add_row(['hardware', 'none']) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/subnet/list.py000066400000000000000000000045461324365065500222370ustar00rootroot00000000000000"""List subnets.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer import utils @click.command() @click.option('--sortby', help='Column to sort by', type=click.Choice(['id', 'identifier', 'type', 'network_space', 'datacenter', 'vlan_id', 'IPs', 'hardware', 'vs'])) @click.option('--datacenter', '-d', help="Filter by datacenter shortname (sng01, dal05, ...)") @click.option('--identifier', help="Filter by network identifier") @click.option('--subnet-type', '-t', help="Filter by subnet type") @click.option('--network-space', help="Filter by network space") @click.option('--v4', '--ipv4', is_flag=True, help="Display only IPv4 subnets") @click.option('--v6', '--ipv6', is_flag=True, help="Display only IPv6 subnets") @environment.pass_env def cli(env, sortby, datacenter, identifier, subnet_type, network_space, ipv4, ipv6): """List subnets.""" mgr = SoftLayer.NetworkManager(env.client) table = formatting.Table([ 'id', 'identifier', 'type', 'network_space', 'datacenter', 'vlan_id', 'IPs', 'hardware', 'vs', ]) table.sortby = sortby version = 0 if ipv4: version = 4 elif ipv6: version = 6 subnets = mgr.list_subnets( datacenter=datacenter, version=version, identifier=identifier, subnet_type=subnet_type, network_space=network_space, ) for subnet in subnets: table.add_row([ subnet['id'], '%s/%s' % (subnet['networkIdentifier'], str(subnet['cidr'])), subnet.get('subnetType', formatting.blank()), utils.lookup(subnet, 'networkVlan', 'networkSpace') or formatting.blank(), utils.lookup(subnet, 'datacenter', 'name',) or formatting.blank(), subnet['networkVlanId'], subnet['ipAddressCount'], len(subnet['hardware']), len(subnet['virtualGuests']), ]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/subnet/lookup.py000066400000000000000000000041701324365065500225660ustar00rootroot00000000000000"""Find an IP address and display its subnet and device info.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting @click.command() @click.argument('ip_address') @environment.pass_env def cli(env, ip_address): """Find an IP address and display its subnet and device info.""" mgr = SoftLayer.NetworkManager(env.client) addr_info = mgr.ip_lookup(ip_address) if not addr_info: raise exceptions.CLIAbort('Not found') table = formatting.KeyValueTable(['name', 'value']) table.align['name'] = 'r' table.align['value'] = 'l' table.add_row(['id', addr_info['id']]) table.add_row(['ip', addr_info['ipAddress']]) subnet_table = formatting.KeyValueTable(['name', 'value']) subnet_table.align['name'] = 'r' subnet_table.align['value'] = 'l' subnet_table.add_row(['id', addr_info['subnet']['id']]) subnet_table.add_row(['identifier', '%s/%s' % (addr_info['subnet']['networkIdentifier'], str(addr_info['subnet']['cidr']))]) subnet_table.add_row(['netmask', addr_info['subnet']['netmask']]) if addr_info['subnet'].get('gateway'): subnet_table.add_row(['gateway', addr_info['subnet']['gateway']]) subnet_table.add_row(['type', addr_info['subnet'].get('subnetType')]) table.add_row(['subnet', subnet_table]) if addr_info.get('virtualGuest') or addr_info.get('hardware'): device_table = formatting.KeyValueTable(['name', 'value']) device_table.align['name'] = 'r' device_table.align['value'] = 'l' if addr_info.get('virtualGuest'): device = addr_info['virtualGuest'] device_type = 'vs' else: device = addr_info['hardware'] device_type = 'server' device_table.add_row(['id', device['id']]) device_table.add_row(['name', device['fullyQualifiedDomainName']]) device_table.add_row(['type', device_type]) table.add_row(['device', device_table]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/summary.py000066400000000000000000000020331324365065500214460ustar00rootroot00000000000000"""Account summary.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting COLUMNS = ['datacenter', 'hardware', 'virtual_servers', 'vlans', 'subnets', 'public_ips'] @click.command() @click.option('--sortby', help='Column to sort by', default='datacenter', type=click.Choice(COLUMNS)) @environment.pass_env def cli(env, sortby): """Account summary.""" mgr = SoftLayer.NetworkManager(env.client) datacenters = mgr.summary_by_datacenter() table = formatting.Table(COLUMNS) table.sortby = sortby for name, datacenter in datacenters.items(): table.add_row([ name, datacenter['hardware_count'], datacenter['virtual_guest_count'], datacenter['vlan_count'], datacenter['subnet_count'], datacenter['public_ip_count'], ]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/template.py000066400000000000000000000041061324365065500215670ustar00rootroot00000000000000""" SoftLayer.CLI.template ~~~~~~~~~~~~~~~~~~~~~~ Provides functions for loading/parsing and writing template files. Template files are used for storing CLI arguments in the form of a file to be used later with the --template option. :license: MIT, see LICENSE for more details. """ # pylint: disable=redefined-argument-from-local import os.path from SoftLayer import utils class TemplateCallback(object): """Callback to use to populate click arguments with a template.""" def __init__(self, list_args=None): self.list_args = list_args or [] def __call__(self, ctx, param, value): if value is None: return config = utils.configparser.ConfigParser() ini_str = '[settings]\n' + open( os.path.expanduser(value), 'r').read() ini_fp = utils.StringIO(ini_str) config.readfp(ini_fp) # Merge template options with the options passed in args = {} for key, value in config.items('settings'): if key in self.list_args: value = value.split(',') if not args.get(key): args[key] = value if ctx.default_map is None: ctx.default_map = {} ctx.default_map.update(args) def export_to_template(filename, args, exclude=None): """Exports given options to the given filename in INI format. :param filename: Filename to save options to :param dict args: Arguments to export :param list exclude (optional): Exclusion list for options that should not be exported """ exclude = exclude or [] exclude.append('config') exclude.append('really') exclude.append('format') exclude.append('debug') with open(filename, "w") as template_file: for k, val in args.items(): if val and k not in exclude: if isinstance(val, tuple): val = ','.join(val) if isinstance(val, list): val = ','.join(val) template_file.write('%s=%s\n' % (k, val)) softlayer-python-5.4.2/SoftLayer/CLI/ticket/000077500000000000000000000000001324365065500206645ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/CLI/ticket/__init__.py000066400000000000000000000037201324365065500227770ustar00rootroot00000000000000"""Support tickets.""" import click from SoftLayer.CLI import formatting TEMPLATE_MSG = "***** SoftLayer Ticket Content ******" def get_ticket_results(mgr, ticket_id, update_count=1): """Get output about a ticket. :param integer id: the ticket ID :param integer update_count: number of entries to retrieve from ticket :returns: a KeyValue table containing the details of the ticket """ ticket = mgr.get_ticket(ticket_id) table = formatting.KeyValueTable(['name', 'value']) table.align['name'] = 'r' table.align['value'] = 'l' table.add_row(['id', ticket['id']]) table.add_row(['title', ticket['title']]) if ticket.get('assignedUser'): user = ticket['assignedUser'] table.add_row([ 'user', "%s %s" % (user.get('firstName'), user.get('lastName')), ]) table.add_row(['status', ticket['status']['name']]) table.add_row(['created', ticket.get('createDate')]) table.add_row(['edited', ticket.get('lastEditDate')]) # Only show up to the specified update count updates = ticket.get('updates', []) count = min(len(updates), update_count) count_offset = len(updates) - count + 1 # Display as one-indexed for i, update in enumerate(updates[-count:]): wrapped_entry = "" # Add user details (fields are different between employee and users) editor = update.get('editor') if editor: if editor.get('displayName'): wrapped_entry += "By %s (Employee)\n" % (editor['displayName']) if editor.get('firstName'): wrapped_entry += "By %s %s\n" % (editor.get('firstName'), editor.get('lastName')) # NOTE(kmcdonald): Windows new-line characters need to be stripped out wrapped_entry += click.wrap_text(update['entry'].replace('\r', '')) table.add_row(['update %s' % (count_offset + i,), wrapped_entry]) return table softlayer-python-5.4.2/SoftLayer/CLI/ticket/attach.py000066400000000000000000000031401324365065500225000ustar00rootroot00000000000000"""Attach devices to a ticket.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import helpers @click.command() @click.argument('identifier', type=int) @click.option('--hardware', 'hardware_identifier', help="The identifier for hardware to attach") @click.option('--virtual', 'virtual_identifier', help="The identifier for a virtual server to attach") @environment.pass_env def cli(env, identifier, hardware_identifier, virtual_identifier): """Attach devices to a ticket.""" ticket_mgr = SoftLayer.TicketManager(env.client) if hardware_identifier and virtual_identifier: raise exceptions.ArgumentError( "Cannot attach hardware and a virtual server at the same time") elif hardware_identifier: hardware_mgr = SoftLayer.HardwareManager(env.client) hardware_id = helpers.resolve_id(hardware_mgr.resolve_ids, hardware_identifier, 'hardware') ticket_mgr.attach_hardware(identifier, hardware_id) elif virtual_identifier: vs_mgr = SoftLayer.VSManager(env.client) vs_id = helpers.resolve_id(vs_mgr.resolve_ids, virtual_identifier, 'VS') ticket_mgr.attach_virtual_server(identifier, vs_id) else: raise exceptions.ArgumentError( "Must have a hardware or virtual server identifier to attach") softlayer-python-5.4.2/SoftLayer/CLI/ticket/create.py000066400000000000000000000035571324365065500225130ustar00rootroot00000000000000"""Create a support ticket.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import helpers from SoftLayer.CLI import ticket @click.command() @click.option('--title', required=True, help="The title of the ticket") @click.option('--subject-id', type=int, required=True, help="""The subject id to use for the ticket, issue 'slcli ticket subjects' to get the list""") @click.option('--body', help="The ticket body") @click.option('--hardware', 'hardware_identifier', help="The identifier for hardware to attach") @click.option('--virtual', 'virtual_identifier', help="The identifier for a virtual server to attach") @environment.pass_env def cli(env, title, subject_id, body, hardware_identifier, virtual_identifier): """Create a support ticket.""" ticket_mgr = SoftLayer.TicketManager(env.client) if body is None: body = click.edit('\n\n' + ticket.TEMPLATE_MSG) created_ticket = ticket_mgr.create_ticket( title=title, body=body, subject=subject_id) if hardware_identifier: hardware_mgr = SoftLayer.HardwareManager(env.client) hardware_id = helpers.resolve_id(hardware_mgr.resolve_ids, hardware_identifier, 'hardware') ticket_mgr.attach_hardware(created_ticket['id'], hardware_id) if virtual_identifier: vs_mgr = SoftLayer.VSManager(env.client) vs_id = helpers.resolve_id(vs_mgr.resolve_ids, virtual_identifier, 'VS') ticket_mgr.attach_virtual_server(created_ticket['id'], vs_id) env.fout(ticket.get_ticket_results(ticket_mgr, created_ticket['id'])) softlayer-python-5.4.2/SoftLayer/CLI/ticket/detach.py000066400000000000000000000031441324365065500224700ustar00rootroot00000000000000"""Detach devices from a ticket.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import helpers @click.command() @click.argument('identifier', type=int) @click.option('--hardware', 'hardware_identifier', help="The identifier for hardware to detach") @click.option('--virtual', 'virtual_identifier', help="The identifier for a virtual server to detach") @environment.pass_env def cli(env, identifier, hardware_identifier, virtual_identifier): """Detach devices from a ticket.""" ticket_mgr = SoftLayer.TicketManager(env.client) if hardware_identifier and virtual_identifier: raise exceptions.ArgumentError( "Cannot detach hardware and a virtual server at the same time") elif hardware_identifier: hardware_mgr = SoftLayer.HardwareManager(env.client) hardware_id = helpers.resolve_id(hardware_mgr.resolve_ids, hardware_identifier, 'hardware') ticket_mgr.detach_hardware(identifier, hardware_id) elif virtual_identifier: vs_mgr = SoftLayer.VSManager(env.client) vs_id = helpers.resolve_id(vs_mgr.resolve_ids, virtual_identifier, 'VS') ticket_mgr.detach_virtual_server(identifier, vs_id) else: raise exceptions.ArgumentError( "Must have a hardware or virtual server identifier to detach") softlayer-python-5.4.2/SoftLayer/CLI/ticket/detail.py000066400000000000000000000013041324365065500224760ustar00rootroot00000000000000"""Get details for a ticket.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import helpers from SoftLayer.CLI import ticket @click.command() @click.argument('identifier') @click.option('--count', type=click.INT, help="Number of updates", show_default=True, default=10) @environment.pass_env def cli(env, identifier, count): """Get details for a ticket.""" mgr = SoftLayer.TicketManager(env.client) ticket_id = helpers.resolve_id(mgr.resolve_ids, identifier, 'ticket') env.fout(ticket.get_ticket_results(mgr, ticket_id, update_count=count)) softlayer-python-5.4.2/SoftLayer/CLI/ticket/list.py000066400000000000000000000021721324365065500222130ustar00rootroot00000000000000"""List tickets.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting @click.command() @click.option('--open / --closed', 'is_open', help="Display only open or closed tickets", default=True) @environment.pass_env def cli(env, is_open): """List tickets.""" ticket_mgr = SoftLayer.TicketManager(env.client) tickets = ticket_mgr.list_tickets(open_status=is_open, closed_status=not is_open) table = formatting.Table(['id', 'assigned_user', 'title', 'last_edited', 'status']) for ticket in tickets: user = formatting.blank() if ticket.get('assignedUser'): user = "%s %s" % (ticket['assignedUser']['firstName'], ticket['assignedUser']['lastName']) table.add_row([ ticket['id'], user, click.wrap_text(ticket['title']), ticket['lastEditDate'], ticket['status']['name'], ]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/ticket/subjects.py000066400000000000000000000010171324365065500230570ustar00rootroot00000000000000"""List Subject IDs for ticket creation.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting @click.command() @environment.pass_env def cli(env): """List Subject IDs for ticket creation.""" ticket_mgr = SoftLayer.TicketManager(env.client) table = formatting.Table(['id', 'subject']) for subject in ticket_mgr.list_subjects(): table.add_row([subject['id'], subject['name']]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/ticket/summary.py000066400000000000000000000022141324365065500227320ustar00rootroot00000000000000"""Summary info about tickets.""" # :license: MIT, see LICENSE for more details. import click from SoftLayer.CLI import environment from SoftLayer.CLI import formatting @click.command() @environment.pass_env def cli(env): """Summary info about tickets.""" mask = ('openTicketCount, closedTicketCount, ' 'openBillingTicketCount, openOtherTicketCount, ' 'openSalesTicketCount, openSupportTicketCount, ' 'openAccountingTicketCount') account = env.client['Account'].getObject(mask=mask) table = formatting.Table(['Status', 'count']) nested = formatting.Table(['Type', 'count']) nested.add_row(['Accounting', account['openAccountingTicketCount']]) nested.add_row(['Billing', account['openBillingTicketCount']]) nested.add_row(['Sales', account['openSalesTicketCount']]) nested.add_row(['Support', account['openSupportTicketCount']]) nested.add_row(['Other', account['openOtherTicketCount']]) nested.add_row(['Total', account['openTicketCount']]) table.add_row(['Open', nested]) table.add_row(['Closed', account['closedTicketCount']]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/ticket/update.py000066400000000000000000000013471324365065500225250ustar00rootroot00000000000000"""Adds an update to an existing ticket.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import helpers from SoftLayer.CLI import ticket @click.command() @click.argument('identifier') @click.option('--body', help="The entry that will be appended to the ticket") @environment.pass_env def cli(env, identifier, body): """Adds an update to an existing ticket.""" mgr = SoftLayer.TicketManager(env.client) ticket_id = helpers.resolve_id(mgr.resolve_ids, identifier, 'ticket') if body is None: body = click.edit('\n\n' + ticket.TEMPLATE_MSG) mgr.update_ticket(ticket_id=ticket_id, body=body) env.fout("Ticket Updated!") softlayer-python-5.4.2/SoftLayer/CLI/ticket/upload.py000066400000000000000000000022061324365065500225220ustar00rootroot00000000000000"""Adds an attachment to an existing ticket.""" # :license: MIT, see LICENSE for more details. import os import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import helpers @click.command() @click.argument('identifier') @click.option('--path', help="The path of the attachment to be uploaded") @click.option('--name', help="The name of the attachment shown in the ticket") @environment.pass_env def cli(env, identifier, path, name): """Adds an attachment to an existing ticket.""" mgr = SoftLayer.TicketManager(env.client) ticket_id = helpers.resolve_id(mgr.resolve_ids, identifier, 'ticket') if path is None: raise exceptions.ArgumentError("Missing argument --path") if not os.path.exists(path): raise exceptions.ArgumentError("%s not exist" % path) if name is None: name = os.path.basename(path) attached_file = mgr.upload_attachment(ticket_id=ticket_id, file_path=path, file_name=name) env.fout("File attached: \n%s" % attached_file) softlayer-python-5.4.2/SoftLayer/CLI/virt/000077500000000000000000000000001324365065500203655ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/CLI/virt/__init__.py000066400000000000000000000021421324365065500224750ustar00rootroot00000000000000"""Virtual Servers.""" # :license: MIT, see LICENSE for more details. import re import click MEMORY_RE = re.compile(r"^(?P[0-9]+)(?Pg|gb|m|mb)?$") class MemoryType(click.ParamType): """Memory type.""" name = 'integer' def convert(self, value, param, ctx): # pylint: disable=inconsistent-return-statements """Validate memory argument. Returns the memory value in megabytes.""" matches = MEMORY_RE.match(value.lower()) if matches is None: self.fail('%s is not a valid value for memory amount' % value, param, ctx) amount_str, unit = matches.groups() amount = int(amount_str) if unit in [None, 'm', 'mb']: # Assume the user intends gigabytes if they specify a number < 1024 if amount < 1024: return amount * 1024 else: if amount % 1024 != 0: self.fail('%s is not an integer that is divisable by 1024' % value, param, ctx) return amount elif unit in ['g', 'gb']: return amount * 1024 MEM_TYPE = MemoryType() softlayer-python-5.4.2/SoftLayer/CLI/virt/cancel.py000066400000000000000000000012041324365065500221610ustar00rootroot00000000000000"""Cancel virtual servers.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting from SoftLayer.CLI import helpers @click.command() @click.argument('identifier') @environment.pass_env def cli(env, identifier): """Cancel virtual servers.""" vsi = SoftLayer.VSManager(env.client) vs_id = helpers.resolve_id(vsi.resolve_ids, identifier, 'VS') if not (env.skip_confirmations or formatting.no_going_back(vs_id)): raise exceptions.CLIAbort('Aborted') vsi.cancel_instance(vs_id) softlayer-python-5.4.2/SoftLayer/CLI/virt/capture.py000066400000000000000000000024661324365065500224120ustar00rootroot00000000000000"""Capture virtual server image.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.CLI import helpers # pylint: disable=redefined-builtin @click.command(short_help="Capture SoftLayer image.") @click.argument('identifier') @click.option('--name', '-n', required=True, help="Name of the image") @click.option('--all', help="Capture all disks belonging to the VS") @click.option('--note', help="Add a note to be associated with the image") @environment.pass_env def cli(env, identifier, name, all, note): """Capture one or all disks from a virtual server to a SoftLayer image.""" vsi = SoftLayer.VSManager(env.client) vs_id = helpers.resolve_id(vsi.resolve_ids, identifier, 'VS') capture = vsi.capture(vs_id, name, all, note) table = formatting.KeyValueTable(['name', 'value']) table.align['name'] = 'r' table.align['value'] = 'l' table.add_row(['vs_id', capture['guestId']]) table.add_row(['date', capture['createDate'][:10]]) table.add_row(['time', capture['createDate'][11:19]]) table.add_row(['transaction', formatting.transaction_status(capture)]) table.add_row(['transaction_id', capture['id']]) table.add_row(['all_disks', all]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/virt/create.py000066400000000000000000000326411324365065500222100ustar00rootroot00000000000000"""Manage, delete, order compute instances.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting from SoftLayer.CLI import helpers from SoftLayer.CLI import template from SoftLayer.CLI import virt from SoftLayer import utils def _update_with_like_args(ctx, _, value): """Update arguments with options taken from a currently running VS.""" if value is None: return env = ctx.ensure_object(environment.Environment) vsi = SoftLayer.VSManager(env.client) vs_id = helpers.resolve_id(vsi.resolve_ids, value, 'VS') like_details = vsi.get_instance(vs_id) like_args = { 'hostname': like_details['hostname'], 'domain': like_details['domain'], 'hourly': like_details['hourlyBillingFlag'], 'datacenter': like_details['datacenter']['name'], 'network': like_details['networkComponents'][0]['maxSpeed'], 'userdata': like_details['userData'] or None, 'postinstall': like_details.get('postInstallScriptUri'), 'dedicated': like_details['dedicatedAccountHostOnlyFlag'], 'private': like_details['privateNetworkOnlyFlag'], } like_args['flavor'] = utils.lookup(like_details, 'billingItem', 'orderItem', 'preset', 'keyName') if not like_args['flavor']: like_args['cpu'] = like_details['maxCpu'] like_args['memory'] = '%smb' % like_details['maxMemory'] tag_refs = like_details.get('tagReferences', None) if tag_refs is not None and len(tag_refs) > 0: like_args['tag'] = [t['tag']['name'] for t in tag_refs] # Handle mutually exclusive options like_image = utils.lookup(like_details, 'blockDeviceTemplateGroup', 'globalIdentifier') like_os = utils.lookup(like_details, 'operatingSystem', 'softwareLicense', 'softwareDescription', 'referenceCode') if like_image: like_args['image'] = like_image elif like_os: like_args['os'] = like_os if ctx.default_map is None: ctx.default_map = {} ctx.default_map.update(like_args) def _parse_create_args(client, args): """Converts CLI arguments to args for VSManager.create_instance. :param dict args: CLI arguments """ data = { "hourly": args['billing'] == 'hourly', "domain": args['domain'], "hostname": args['hostname'], "private": args['private'], "dedicated": args['dedicated'], "disks": args['disk'], "cpus": args.get('cpu', None), "memory": args.get('memory', None), "flavor": args.get('flavor', None), "boot_mode": args.get('boot_mode', None) } # The primary disk is included in the flavor and the local_disk flag is not needed # Setting it to None prevents errors from the flag not matching the flavor if not args.get('san') and args.get('flavor'): data['local_disk'] = None else: data['local_disk'] = not args['san'] if args.get('os'): data['os_code'] = args['os'] if args.get('image'): if args.get('image').isdigit(): image_mgr = SoftLayer.ImageManager(client) image_details = image_mgr.get_image(args.get('image'), mask="id,globalIdentifier") data['image_id'] = image_details['globalIdentifier'] else: data['image_id'] = args['image'] if args.get('datacenter'): data['datacenter'] = args['datacenter'] if args.get('network'): data['nic_speed'] = args.get('network') if args.get('userdata'): data['userdata'] = args['userdata'] elif args.get('userfile'): with open(args['userfile'], 'r') as userfile: data['userdata'] = userfile.read() if args.get('postinstall'): data['post_uri'] = args.get('postinstall') # Get the SSH keys if args.get('key'): keys = [] for key in args.get('key'): resolver = SoftLayer.SshKeyManager(client).resolve_ids key_id = helpers.resolve_id(resolver, key, 'SshKey') keys.append(key_id) data['ssh_keys'] = keys if args.get('vlan_public'): data['public_vlan'] = args['vlan_public'] if args.get('vlan_private'): data['private_vlan'] = args['vlan_private'] if args.get('public_security_group'): pub_groups = args.get('public_security_group') data['public_security_groups'] = [group for group in pub_groups] if args.get('private_security_group'): priv_groups = args.get('private_security_group') data['private_security_groups'] = [group for group in priv_groups] if args.get('tag'): data['tags'] = ','.join(args['tag']) if args.get('host_id'): data['host_id'] = args['host_id'] return data @click.command(epilog="See 'slcli vs create-options' for valid options") @click.option('--hostname', '-H', help="Host portion of the FQDN", required=True, prompt=True) @click.option('--domain', '-D', help="Domain portion of the FQDN", required=True, prompt=True) @click.option('--cpu', '-c', help="Number of CPU cores (not available with flavors)", type=click.INT) @click.option('--memory', '-m', help="Memory in mebibytes (not available with flavors)", type=virt.MEM_TYPE) @click.option('--flavor', '-f', help="Public Virtual Server flavor key name", type=click.STRING) @click.option('--datacenter', '-d', help="Datacenter shortname", required=True, prompt=True) @click.option('--os', '-o', help="OS install code. Tip: you can specify _LATEST") @click.option('--image', help="Image ID. See: 'slcli image list' for reference") @click.option('--boot-mode', help="Specify the mode to boot the OS in. Supported modes are HVM and PV.", type=click.STRING) @click.option('--billing', type=click.Choice(['hourly', 'monthly']), default='hourly', show_default=True, help="Billing rate") @click.option('--dedicated/--public', is_flag=True, help="Create a Dedicated Virtual Server") @click.option('--host-id', type=click.INT, help="Host Id to provision a Dedicated Host Virtual Server onto") @click.option('--san', is_flag=True, help="Use SAN storage instead of local disk.") @click.option('--test', is_flag=True, help="Do not actually create the virtual server") @click.option('--export', type=click.Path(writable=True, resolve_path=True), help="Exports options to a template file") @click.option('--postinstall', '-i', help="Post-install script to download") @helpers.multi_option('--key', '-k', help="SSH keys to add to the root user") @helpers.multi_option('--disk', help="Disk sizes") @click.option('--private', is_flag=True, help="Forces the VS to only have access the private network") @click.option('--like', is_eager=True, callback=_update_with_like_args, help="Use the configuration from an existing VS") @click.option('--network', '-n', help="Network port speed in Mbps") @helpers.multi_option('--tag', '-g', help="Tags to add to the instance") @click.option('--template', '-t', is_eager=True, callback=template.TemplateCallback(list_args=['disk', 'key', 'tag']), help="A template file that defaults the command-line options", type=click.Path(exists=True, readable=True, resolve_path=True)) @click.option('--userdata', '-u', help="User defined metadata string") @click.option('--userfile', '-F', help="Read userdata from file", type=click.Path(exists=True, readable=True, resolve_path=True)) @click.option('--vlan-public', help="The ID of the public VLAN on which you want the virtual " "server placed", type=click.INT) @click.option('--vlan-private', help="The ID of the private VLAN on which you want the virtual " "server placed", type=click.INT) @helpers.multi_option('--public-security-group', '-S', help=('Security group ID to associate with ' 'the public interface')) @helpers.multi_option('--private-security-group', '-s', help=('Security group ID to associate with ' 'the private interface')) @click.option('--wait', type=click.INT, help="Wait until VS is finished provisioning for up to X " "seconds before returning") @environment.pass_env def cli(env, **args): """Order/create virtual servers.""" vsi = SoftLayer.VSManager(env.client) _validate_args(env, args) # Do not create a virtual server with test or export do_create = not (args['export'] or args['test']) table = formatting.Table(['Item', 'cost']) table.align['Item'] = 'r' table.align['cost'] = 'r' data = _parse_create_args(env.client, args) output = [] if args.get('test'): result = vsi.verify_create_instance(**data) total_monthly = 0.0 total_hourly = 0.0 table = formatting.Table(['Item', 'cost']) table.align['Item'] = 'r' table.align['cost'] = 'r' for price in result['prices']: total_monthly += float(price.get('recurringFee', 0.0)) total_hourly += float(price.get('hourlyRecurringFee', 0.0)) if args.get('billing') == 'hourly': rate = "%.2f" % float(price['hourlyRecurringFee']) elif args.get('billing') == 'monthly': rate = "%.2f" % float(price['recurringFee']) table.add_row([price['item']['description'], rate]) total = 0 if args.get('billing') == 'hourly': total = total_hourly elif args.get('billing') == 'monthly': total = total_monthly billing_rate = 'monthly' if args.get('billing') == 'hourly': billing_rate = 'hourly' table.add_row(['Total %s cost' % billing_rate, "%.2f" % total]) output.append(table) output.append(formatting.FormattedItem( None, ' -- ! Prices reflected here are retail and do not ' 'take account level discounts and are not guaranteed.')) if args['export']: export_file = args.pop('export') template.export_to_template(export_file, args, exclude=['wait', 'test']) env.fout('Successfully exported options to a template file.') if do_create: if not (env.skip_confirmations or formatting.confirm( "This action will incur charges on your account. Continue?")): raise exceptions.CLIAbort('Aborting virtual server order.') result = vsi.create_instance(**data) table = formatting.KeyValueTable(['name', 'value']) table.align['name'] = 'r' table.align['value'] = 'l' table.add_row(['id', result['id']]) table.add_row(['created', result['createDate']]) table.add_row(['guid', result['globalIdentifier']]) output.append(table) if args.get('wait'): ready = vsi.wait_for_ready(result['id'], args.get('wait') or 1) table.add_row(['ready', ready]) if ready is False: env.out(env.fmt(output)) raise exceptions.CLIHalt(code=1) env.fout(output) def _validate_args(env, args): """Raises an ArgumentError if the given arguments are not valid.""" if all([args['cpu'], args['flavor']]): raise exceptions.ArgumentError( '[-c | --cpu] not allowed with [-f | --flavor]') if all([args['memory'], args['flavor']]): raise exceptions.ArgumentError( '[-m | --memory] not allowed with [-f | --flavor]') if all([args['dedicated'], args['flavor']]): raise exceptions.ArgumentError( '[-d | --dedicated] not allowed with [-f | --flavor]') if all([args['host_id'], args['flavor']]): raise exceptions.ArgumentError( '[-h | --host-id] not allowed with [-f | --flavor]') if all([args['userdata'], args['userfile']]): raise exceptions.ArgumentError( '[-u | --userdata] not allowed with [-F | --userfile]') image_args = [args['os'], args['image']] if all(image_args): raise exceptions.ArgumentError( '[-o | --os] not allowed with [--image]') while not any([args['os'], args['image']]): args['os'] = env.input("Operating System Code", default="", show_default=False) if not args['os']: args['image'] = env.input("Image", default="", show_default=False) softlayer-python-5.4.2/SoftLayer/CLI/virt/create_options.py000066400000000000000000000142321324365065500237570ustar00rootroot00000000000000"""Virtual server order options.""" # :license: MIT, see LICENSE for more details. import os import os.path import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer import utils @click.command() @environment.pass_env def cli(env): """Virtual server order options.""" vsi = SoftLayer.VSManager(env.client) result = vsi.get_create_options() table = formatting.KeyValueTable(['name', 'value']) table.align['name'] = 'r' table.align['value'] = 'l' # Datacenters datacenters = [dc['template']['datacenter']['name'] for dc in result['datacenters']] datacenters = sorted(datacenters) table.add_row(['datacenter', formatting.listing(datacenters, separator='\n')]) def _add_flavor_rows(flavor_key, flavor_label, flavor_options): flavors = [] for flavor_option in flavor_options: flavor_key_name = utils.lookup(flavor_option, 'flavor', 'keyName') if not flavor_key_name.startswith(flavor_key): continue flavors.append(flavor_key_name) if len(flavors) > 0: table.add_row(['flavors (%s)' % flavor_label, formatting.listing(flavors, separator='\n')]) if result.get('flavors', None): _add_flavor_rows('B1', 'balanced', result['flavors']) _add_flavor_rows('BL1', 'balanced local - hdd', result['flavors']) _add_flavor_rows('BL2', 'balanced local - ssd', result['flavors']) _add_flavor_rows('C1', 'compute', result['flavors']) _add_flavor_rows('M1', 'memory', result['flavors']) _add_flavor_rows('AC', 'GPU', result['flavors']) # CPUs standard_cpus = [int(x['template']['startCpus']) for x in result['processors'] if not x['template'].get('dedicatedAccountHostOnlyFlag', False) and not x['template'].get('dedicatedHost', None)] ded_cpus = [int(x['template']['startCpus']) for x in result['processors'] if x['template'].get('dedicatedAccountHostOnlyFlag', False)] ded_host_cpus = [int(x['template']['startCpus']) for x in result['processors'] if x['template'].get('dedicatedHost', None)] standard_cpus = sorted(standard_cpus) table.add_row(['cpus (standard)', formatting.listing(standard_cpus, separator=',')]) ded_cpus = sorted(ded_cpus) table.add_row(['cpus (dedicated)', formatting.listing(ded_cpus, separator=',')]) ded_host_cpus = sorted(ded_host_cpus) table.add_row(['cpus (dedicated host)', formatting.listing(ded_host_cpus, separator=',')]) # Memory memory = [int(m['template']['maxMemory']) for m in result['memory'] if not m['itemPrice'].get('dedicatedHostInstanceFlag', False)] ded_host_memory = [int(m['template']['maxMemory']) for m in result['memory'] if m['itemPrice'].get('dedicatedHostInstanceFlag', False)] memory = sorted(memory) table.add_row(['memory', formatting.listing(memory, separator=',')]) ded_host_memory = sorted(ded_host_memory) table.add_row(['memory (dedicated host)', formatting.listing(ded_host_memory, separator=',')]) # Operating Systems op_sys = [o['template']['operatingSystemReferenceCode'] for o in result['operatingSystems']] op_sys = sorted(op_sys) os_summary = set() for operating_system in op_sys: os_summary.add(operating_system[0:operating_system.find('_')]) for summary in sorted(os_summary): table.add_row([ 'os (%s)' % summary, os.linesep.join(sorted([x for x in op_sys if x[0:len(summary)] == summary])) ]) # Disk local_disks = [x for x in result['blockDevices'] if x['template'].get('localDiskFlag', False) and not x['itemPrice'].get('dedicatedHostInstanceFlag', False)] ded_host_local_disks = [x for x in result['blockDevices'] if x['template'].get('localDiskFlag', False) and x['itemPrice'].get('dedicatedHostInstanceFlag', False)] san_disks = [x for x in result['blockDevices'] if not x['template'].get('localDiskFlag', False)] def add_block_rows(disks, name): """Add block rows to the table.""" simple = {} for disk in disks: block = disk['template']['blockDevices'][0] bid = block['device'] if bid not in simple: simple[bid] = [] simple[bid].append(str(block['diskImage']['capacity'])) for label in sorted(simple): table.add_row(['%s disk(%s)' % (name, label), formatting.listing(simple[label], separator=',')]) add_block_rows(san_disks, 'san') add_block_rows(local_disks, 'local') add_block_rows(ded_host_local_disks, 'local (dedicated host)') # Network speeds = [] ded_host_speeds = [] for option in result['networkComponents']: template = option.get('template', None) price = option.get('itemPrice', None) if not template or not price \ or not template.get('networkComponents', None): continue if not template['networkComponents'][0] \ or not template['networkComponents'][0].get('maxSpeed', None): continue max_speed = str(template['networkComponents'][0]['maxSpeed']) if price.get('dedicatedHostInstanceFlag', False) \ and max_speed not in ded_host_speeds: ded_host_speeds.append(max_speed) elif max_speed not in speeds: speeds.append(max_speed) speeds = sorted(speeds) table.add_row(['nic', formatting.listing(speeds, separator=',')]) ded_host_speeds = sorted(ded_host_speeds) table.add_row(['nic (dedicated host)', formatting.listing(ded_host_speeds, separator=',')]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/virt/credentials.py000066400000000000000000000013051324365065500232330ustar00rootroot00000000000000"""List virtual server credentials.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.CLI import helpers @click.command() @click.argument('identifier') @environment.pass_env def cli(env, identifier): """List virtual server credentials.""" vsi = SoftLayer.VSManager(env.client) vs_id = helpers.resolve_id(vsi.resolve_ids, identifier, 'VS') instance = vsi.get_instance(vs_id) table = formatting.Table(['username', 'password']) for item in instance['operatingSystem']['passwords']: table.add_row([item['username'], item['password']]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/virt/detail.py000066400000000000000000000145741324365065500222140ustar00rootroot00000000000000"""Get details for a virtual server.""" # :license: MIT, see LICENSE for more details. import logging import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.CLI import helpers from SoftLayer import utils LOGGER = logging.getLogger(__name__) @click.command() @click.argument('identifier') @click.option('--passwords', is_flag=True, help='Show passwords (check over your shoulder!)') @click.option('--price', is_flag=True, help='Show associated prices') @environment.pass_env def cli(env, identifier, passwords=False, price=False): """Get details for a virtual server.""" vsi = SoftLayer.VSManager(env.client) table = formatting.KeyValueTable(['name', 'value']) table.align['name'] = 'r' table.align['value'] = 'l' vs_id = helpers.resolve_id(vsi.resolve_ids, identifier, 'VS') result = vsi.get_instance(vs_id) result = utils.NestedDict(result) table.add_row(['id', result['id']]) table.add_row(['guid', result['globalIdentifier']]) table.add_row(['hostname', result['hostname']]) table.add_row(['domain', result['domain']]) table.add_row(['fqdn', result['fullyQualifiedDomainName']]) table.add_row(['status', formatting.FormattedItem( result['status']['keyName'] or formatting.blank(), result['status']['name'] or formatting.blank() )]) table.add_row(['state', formatting.FormattedItem( utils.lookup(result, 'powerState', 'keyName'), utils.lookup(result, 'powerState', 'name'), )]) table.add_row(['active_transaction', formatting.active_txn(result)]) table.add_row(['datacenter', result['datacenter']['name'] or formatting.blank()]) _cli_helper_dedicated_host(env, result, table) operating_system = utils.lookup(result, 'operatingSystem', 'softwareLicense', 'softwareDescription') or {} table.add_row(['os', operating_system.get('name') or formatting.blank()]) table.add_row(['os_version', operating_system.get('version') or formatting.blank()]) table.add_row(['cores', result['maxCpu']]) table.add_row(['memory', formatting.mb_to_gb(result['maxMemory'])]) table.add_row(['public_ip', result['primaryIpAddress'] or formatting.blank()]) table.add_row(['private_ip', result['primaryBackendIpAddress'] or formatting.blank()]) table.add_row(['private_only', result['privateNetworkOnlyFlag']]) table.add_row(['private_cpu', result['dedicatedAccountHostOnlyFlag']]) table.add_row(['created', result['createDate']]) table.add_row(['modified', result['modifyDate']]) if utils.lookup(result, 'billingItem') != []: table.add_row(['owner', formatting.FormattedItem( utils.lookup(result, 'billingItem', 'orderItem', 'order', 'userRecord', 'username') or formatting.blank(), )]) else: table.add_row(['owner', formatting.blank()]) vlan_table = formatting.Table(['type', 'number', 'id']) for vlan in result['networkVlans']: vlan_table.add_row([ vlan['networkSpace'], vlan['vlanNumber'], vlan['id']]) table.add_row(['vlans', vlan_table]) if result.get('networkComponents'): secgroup_table = formatting.Table(['interface', 'id', 'name']) has_secgroups = False for comp in result.get('networkComponents'): interface = 'PRIVATE' if comp['port'] == 0 else 'PUBLIC' for binding in comp['securityGroupBindings']: has_secgroups = True secgroup = binding['securityGroup'] secgroup_table.add_row([ interface, secgroup['id'], secgroup.get('name') or formatting.blank()]) if has_secgroups: table.add_row(['security_groups', secgroup_table]) if result.get('notes'): table.add_row(['notes', result['notes']]) if price: total_price = utils.lookup(result, 'billingItem', 'nextInvoiceTotalRecurringAmount') or 0 total_price += sum(p['nextInvoiceTotalRecurringAmount'] for p in utils.lookup(result, 'billingItem', 'children') or []) table.add_row(['price_rate', total_price]) if passwords: pass_table = formatting.Table(['software', 'username', 'password']) for component in result['softwareComponents']: for item in component['passwords']: pass_table.add_row([ utils.lookup(component, 'softwareLicense', 'softwareDescription', 'name'), item['username'], item['password'], ]) table.add_row(['users', pass_table]) table.add_row(['tags', formatting.tags(result['tagReferences'])]) # Test to see if this actually has a primary (public) ip address try: if not result['privateNetworkOnlyFlag']: ptr_domains = env.client.call( 'Virtual_Guest', 'getReverseDomainRecords', id=vs_id, ) for ptr_domain in ptr_domains: for ptr in ptr_domain['resourceRecords']: table.add_row(['ptr', ptr['data']]) except SoftLayer.SoftLayerAPIError: pass env.fout(table) def _cli_helper_dedicated_host(env, result, table): """Get details on dedicated host for a virtual server.""" dedicated_host_id = utils.lookup(result, 'dedicatedHost', 'id') if dedicated_host_id: table.add_row(['dedicated_host_id', dedicated_host_id]) # Try to find name of dedicated host try: dedicated_host = env.client.call('Virtual_DedicatedHost', 'getObject', id=dedicated_host_id) except SoftLayer.SoftLayerAPIError: LOGGER.error('Unable to get dedicated host id %s', dedicated_host_id) dedicated_host = {} table.add_row(['dedicated_host', dedicated_host.get('name') or formatting.blank()]) softlayer-python-5.4.2/SoftLayer/CLI/virt/dns.py000066400000000000000000000126011324365065500215230ustar00rootroot00000000000000"""Sync DNS records.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting from SoftLayer.CLI import helpers @click.command(epilog="""If you don't specify any arguments, it will attempt to update both the A and PTR records. If you don't want to update both records, you may use the -a or --ptr arguments to limit the records updated.""") @click.argument('identifier') @click.option('--a-record', '-a', is_flag=True, help="Sync the A record for the host") @click.option('--aaaa-record', is_flag=True, help="Sync the AAAA record for the host") @click.option('--ptr', is_flag=True, help="Sync the PTR record for the host") @click.option('--ttl', default=7200, show_default=True, type=click.INT, help="Sets the TTL for the A and/or PTR records") @environment.pass_env def cli(env, identifier, a_record, aaaa_record, ptr, ttl): """Sync DNS records.""" items = ['id', 'globalIdentifier', 'fullyQualifiedDomainName', 'hostname', 'domain', 'primaryBackendIpAddress', 'primaryIpAddress', '''primaryNetworkComponent[ id, primaryIpAddress, primaryVersion6IpAddressRecord[ipAddress] ]'''] mask = "mask[%s]" % ','.join(items) dns = SoftLayer.DNSManager(env.client) vsi = SoftLayer.VSManager(env.client) vs_id = helpers.resolve_id(vsi.resolve_ids, identifier, 'VS') instance = vsi.get_instance(vs_id, mask=mask) zone_id = helpers.resolve_id(dns.resolve_ids, instance['domain'], name='zone') def sync_a_record(): """Sync A record.""" records = dns.get_records(zone_id, host=instance['hostname'], record_type='a') if not records: # don't have a record, lets add one to the base zone dns.create_record(zone['id'], instance['hostname'], 'a', instance['primaryIpAddress'], ttl=ttl) else: if len(records) != 1: raise exceptions.CLIAbort("Aborting A record sync, found " "%d A record exists!" % len(records)) rec = records[0] rec['data'] = instance['primaryIpAddress'] rec['ttl'] = ttl dns.edit_record(rec) def sync_aaaa_record(): """Sync AAAA record.""" records = dns.get_records(zone_id, host=instance['hostname'], record_type='aaaa') try: # done this way to stay within 80 character lines component = instance['primaryNetworkComponent'] record = component['primaryVersion6IpAddressRecord'] ip_address = record['ipAddress'] except KeyError: raise exceptions.CLIAbort("%s does not have an ipv6 address" % instance['fullyQualifiedDomainName']) if not records: # don't have a record, lets add one to the base zone dns.create_record(zone['id'], instance['hostname'], 'aaaa', ip_address, ttl=ttl) else: if len(records) != 1: raise exceptions.CLIAbort("Aborting A record sync, found " "%d A record exists!" % len(records)) rec = records[0] rec['data'] = ip_address rec['ttl'] = ttl dns.edit_record(rec) def sync_ptr_record(): """Sync PTR record.""" host_rec = instance['primaryIpAddress'].split('.')[-1] ptr_domains = (env.client['Virtual_Guest'] .getReverseDomainRecords(id=instance['id'])[0]) edit_ptr = None for ptr in ptr_domains['resourceRecords']: if ptr['host'] == host_rec: ptr['ttl'] = ttl edit_ptr = ptr break if edit_ptr: edit_ptr['data'] = instance['fullyQualifiedDomainName'] dns.edit_record(edit_ptr) else: dns.create_record(ptr_domains['id'], host_rec, 'ptr', instance['fullyQualifiedDomainName'], ttl=ttl) if not instance['primaryIpAddress']: raise exceptions.CLIAbort('No primary IP address associated with ' 'this VS') zone = dns.get_zone(zone_id) go_for_it = env.skip_confirmations or formatting.confirm( "Attempt to update DNS records for %s" % instance['fullyQualifiedDomainName']) if not go_for_it: raise exceptions.CLIAbort("Aborting DNS sync") both = False if not ptr and not a_record and not aaaa_record: both = True if both or a_record: sync_a_record() if both or ptr: sync_ptr_record() if aaaa_record: sync_aaaa_record() softlayer-python-5.4.2/SoftLayer/CLI/virt/edit.py000066400000000000000000000041531324365065500216670ustar00rootroot00000000000000"""Edit a virtual server's details.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import helpers @click.command() @click.argument('identifier') @click.option('--domain', '-D', help="Domain portion of the FQDN") @click.option('--hostname', '-H', help="Host portion of the FQDN. example: server") @click.option('--tag', '-g', multiple=True, help="Tags to set or empty string to remove all") @click.option('--userdata', '-u', help="User defined metadata string") @click.option('--userfile', '-F', help="Read userdata from file", type=click.Path(exists=True, readable=True, resolve_path=True)) @click.option('--public-speed', help="Public port speed.", default=None, type=click.Choice(['0', '10', '100', '1000', '10000'])) @click.option('--private-speed', help="Private port speed.", default=None, type=click.Choice(['0', '10', '100', '1000', '10000'])) @environment.pass_env def cli(env, identifier, domain, userfile, tag, hostname, userdata, public_speed, private_speed): """Edit a virtual server's details.""" if userdata and userfile: raise exceptions.ArgumentError( '[-u | --userdata] not allowed with [-F | --userfile]') data = {} if userdata: data['userdata'] = userdata elif userfile: with open(userfile, 'r') as userfile_obj: data['userdata'] = userfile_obj.read() data['hostname'] = hostname data['domain'] = domain if tag: data['tags'] = ','.join(tag) vsi = SoftLayer.VSManager(env.client) vs_id = helpers.resolve_id(vsi.resolve_ids, identifier, 'VS') if not vsi.edit(vs_id, **data): raise exceptions.CLIAbort("Failed to update virtual server") if public_speed is not None: vsi.change_port_speed(vs_id, True, int(public_speed)) if private_speed is not None: vsi.change_port_speed(vs_id, False, int(private_speed)) softlayer-python-5.4.2/SoftLayer/CLI/virt/list.py000066400000000000000000000063061324365065500217170ustar00rootroot00000000000000"""List virtual servers.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import columns as column_helper from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.CLI import helpers # pylint: disable=unnecessary-lambda COLUMNS = [ column_helper.Column('guid', ('globalIdentifier',)), column_helper.Column('primary_ip', ('primaryIpAddress',)), column_helper.Column('backend_ip', ('primaryBackendIpAddress',)), column_helper.Column('datacenter', ('datacenter', 'name')), column_helper.Column('action', lambda guest: formatting.active_txn(guest), mask=''' activeTransaction[ id,transactionStatus[name,friendlyName] ]'''), column_helper.Column('power_state', ('powerState', 'name')), column_helper.Column( 'created_by', ('billingItem', 'orderItem', 'order', 'userRecord', 'username')), column_helper.Column( 'tags', lambda server: formatting.tags(server.get('tagReferences')), mask="tagReferences.tag.name"), ] DEFAULT_COLUMNS = [ 'id', 'hostname', 'primary_ip', 'backend_ip', 'datacenter', 'action', ] @click.command() @click.option('--cpu', '-c', help='Number of CPU cores', type=click.INT) @click.option('--domain', '-D', help='Domain portion of the FQDN') @click.option('--datacenter', '-d', help='Datacenter shortname') @click.option('--hostname', '-H', help='Host portion of the FQDN') @click.option('--memory', '-m', help='Memory in mebibytes', type=click.INT) @click.option('--network', '-n', help='Network port speed in Mbps') @click.option('--hourly', is_flag=True, help='Show only hourly instances') @click.option('--monthly', is_flag=True, help='Show only monthly instances') @helpers.multi_option('--tag', help='Filter by tags') @click.option('--sortby', help='Column to sort by', default='hostname', show_default=True) @click.option('--columns', callback=column_helper.get_formatter(COLUMNS), help='Columns to display. [options: %s]' % ', '.join(column.name for column in COLUMNS), default=','.join(DEFAULT_COLUMNS), show_default=True) @environment.pass_env def cli(env, sortby, cpu, domain, datacenter, hostname, memory, network, hourly, monthly, tag, columns): """List virtual servers.""" vsi = SoftLayer.VSManager(env.client) guests = vsi.list_instances(hourly=hourly, monthly=monthly, hostname=hostname, domain=domain, cpus=cpu, memory=memory, datacenter=datacenter, nic_speed=network, tags=tag, mask=columns.mask()) table = formatting.Table(columns.columns) table.sortby = sortby for guest in guests: table.add_row([value or formatting.blank() for value in columns.row(guest)]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/virt/power.py000066400000000000000000000063741324365065500221050ustar00rootroot00000000000000"""Command lines which modify power states.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting from SoftLayer.CLI import helpers @click.command() @click.argument('identifier') @environment.pass_env def rescue(env, identifier): """Reboot into a rescue image.""" vsi = SoftLayer.VSManager(env.client) vs_id = helpers.resolve_id(vsi.resolve_ids, identifier, 'VS') if not (env.skip_confirmations or formatting.confirm("This action will reboot this VSI. Continue?")): raise exceptions.CLIAbort('Aborted') vsi.rescue(vs_id) @click.command() @click.argument('identifier') @click.option('--hard/--soft', default=None, help="Perform a hard or soft reboot") @environment.pass_env def reboot(env, identifier, hard): """Reboot an active virtual server.""" virtual_guest = env.client['Virtual_Guest'] mgr = SoftLayer.HardwareManager(env.client) vs_id = helpers.resolve_id(mgr.resolve_ids, identifier, 'VS') if not (env.skip_confirmations or formatting.confirm('This will reboot the VS with id %s. ' 'Continue?' % vs_id)): raise exceptions.CLIAbort('Aborted.') if hard is True: virtual_guest.rebootHard(id=vs_id) elif hard is False: virtual_guest.rebootSoft(id=vs_id) else: virtual_guest.rebootDefault(id=vs_id) @click.command() @click.argument('identifier') @click.option('--hard/--soft', help="Perform a hard shutdown") @environment.pass_env def power_off(env, identifier, hard): """Power off an active virtual server.""" virtual_guest = env.client['Virtual_Guest'] vsi = SoftLayer.VSManager(env.client) vs_id = helpers.resolve_id(vsi.resolve_ids, identifier, 'VS') if not (env.skip_confirmations or formatting.confirm('This will power off the VS with id %s. ' 'Continue?' % vs_id)): raise exceptions.CLIAbort('Aborted.') if hard: virtual_guest.powerOff(id=vs_id) else: virtual_guest.powerOffSoft(id=vs_id) @click.command() @click.argument('identifier') @environment.pass_env def power_on(env, identifier): """Power on a virtual server.""" vsi = SoftLayer.VSManager(env.client) vs_id = helpers.resolve_id(vsi.resolve_ids, identifier, 'VS') env.client['Virtual_Guest'].powerOn(id=vs_id) @click.command() @click.argument('identifier') @environment.pass_env def pause(env, identifier): """Pauses an active virtual server.""" vsi = SoftLayer.VSManager(env.client) vs_id = helpers.resolve_id(vsi.resolve_ids, identifier, 'VS') if not (env.skip_confirmations or formatting.confirm('This will pause the VS with id %s. Continue?' % vs_id)): raise exceptions.CLIAbort('Aborted.') env.client['Virtual_Guest'].pause(id=vs_id) @click.command() @click.argument('identifier') @environment.pass_env def resume(env, identifier): """Resumes a paused virtual server.""" vsi = SoftLayer.VSManager(env.client) vs_id = helpers.resolve_id(vsi.resolve_ids, identifier, 'VS') env.client['Virtual_Guest'].resume(id=vs_id) softlayer-python-5.4.2/SoftLayer/CLI/virt/ready.py000066400000000000000000000013471324365065500220500ustar00rootroot00000000000000"""Check if a virtual server is ready.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import helpers @click.command() @click.argument('identifier') @click.option('--wait', default=0, show_default=True, type=click.INT, help="Seconds to wait") @environment.pass_env def cli(env, identifier, wait): """Check if a virtual server is ready.""" vsi = SoftLayer.VSManager(env.client) vs_id = helpers.resolve_id(vsi.resolve_ids, identifier, 'VS') ready = vsi.wait_for_ready(vs_id, wait) if ready: env.fout("READY") else: raise exceptions.CLIAbort("Instance %s not ready" % vs_id) softlayer-python-5.4.2/SoftLayer/CLI/virt/reload.py000066400000000000000000000025451324365065500222130ustar00rootroot00000000000000"""Reload the OS on a virtual server.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting from SoftLayer.CLI import helpers @click.command() @click.argument('identifier') @click.option('--postinstall', '-i', help="Post-install script to download") @click.option( '--image', help="""Image ID. The default is to use the current operating system. See: 'slcli image list' for reference""") @helpers.multi_option('--key', '-k', help="SSH keys to add to the root user") @environment.pass_env def cli(env, identifier, postinstall, key, image): """Reload operating system on a virtual server.""" vsi = SoftLayer.VSManager(env.client) vs_id = helpers.resolve_id(vsi.resolve_ids, identifier, 'VS') keys = [] if key: for single_key in key: resolver = SoftLayer.SshKeyManager(env.client).resolve_ids key_id = helpers.resolve_id(resolver, single_key, 'SshKey') keys.append(key_id) if not (env.skip_confirmations or formatting.no_going_back(vs_id)): raise exceptions.CLIAbort('Aborted') vsi.reload_instance(vs_id, post_uri=postinstall, ssh_keys=keys, image_id=image) softlayer-python-5.4.2/SoftLayer/CLI/virt/upgrade.py000066400000000000000000000035151324365065500223720ustar00rootroot00000000000000"""Upgrade a virtual server.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting from SoftLayer.CLI import helpers from SoftLayer.CLI import virt @click.command(epilog="""Note: SoftLayer automatically reboots the VS once upgrade request is placed. The VS is halted until the Upgrade transaction is completed. However for Network, no reboot is required.""") @click.argument('identifier') @click.option('--cpu', type=click.INT, help="Number of CPU cores") @click.option('--private', is_flag=True, help="CPU core will be on a dedicated host server.") @click.option('--memory', type=virt.MEM_TYPE, help="Memory in megabytes") @click.option('--network', type=click.INT, help="Network port speed in Mbps") @environment.pass_env def cli(env, identifier, cpu, private, memory, network): """Upgrade a virtual server.""" vsi = SoftLayer.VSManager(env.client) if not any([cpu, memory, network]): raise exceptions.ArgumentError( "Must provide [--cpu], [--memory], or [--network] to upgrade") if private and not cpu: raise exceptions.ArgumentError( "Must specify [--cpu] when using [--private]") vs_id = helpers.resolve_id(vsi.resolve_ids, identifier, 'VS') if not (env.skip_confirmations or formatting.confirm( "This action will incur charges on your account. " "Continue?")): raise exceptions.CLIAbort('Aborted') if memory: memory = int(memory / 1024) if not vsi.upgrade(vs_id, cpus=cpu, memory=memory, nic_speed=network, public=not private): raise exceptions.CLIAbort('VS Upgrade Failed') softlayer-python-5.4.2/SoftLayer/CLI/vlan/000077500000000000000000000000001324365065500203415ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/CLI/vlan/__init__.py000066400000000000000000000000251324365065500224470ustar00rootroot00000000000000"""Network VLANs.""" softlayer-python-5.4.2/SoftLayer/CLI/vlan/detail.py000066400000000000000000000060131324365065500221550ustar00rootroot00000000000000"""Get details about a VLAN.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.CLI import helpers @click.command() @click.argument('identifier') @click.option('--no-vs', is_flag=True, help="Hide virtual server listing") @click.option('--no-hardware', is_flag=True, help="Hide hardware listing") @environment.pass_env def cli(env, identifier, no_vs, no_hardware): """Get details about a VLAN.""" mgr = SoftLayer.NetworkManager(env.client) vlan_id = helpers.resolve_id(mgr.resolve_vlan_ids, identifier, 'VLAN') vlan = mgr.get_vlan(vlan_id) table = formatting.KeyValueTable(['name', 'value']) table.align['name'] = 'r' table.align['value'] = 'l' table.add_row(['id', vlan['id']]) table.add_row(['number', vlan['vlanNumber']]) table.add_row(['datacenter', vlan['primaryRouter']['datacenter']['longName']]) table.add_row(['primary_router', vlan['primaryRouter']['fullyQualifiedDomainName']]) table.add_row(['firewall', 'Yes' if vlan['firewallInterfaces'] else 'No']) subnets = [] for subnet in vlan.get('subnets', []): subnet_table = formatting.KeyValueTable(['name', 'value']) subnet_table.align['name'] = 'r' subnet_table.align['value'] = 'l' subnet_table.add_row(['id', subnet['id']]) subnet_table.add_row(['identifier', subnet['networkIdentifier']]) subnet_table.add_row(['netmask', subnet['netmask']]) subnet_table.add_row(['gateway', subnet.get('gateway', '-')]) subnet_table.add_row(['type', subnet['subnetType']]) subnet_table.add_row(['usable ips', subnet['usableIpAddressCount']]) subnets.append(subnet_table) table.add_row(['subnets', subnets]) server_columns = ['hostname', 'domain', 'public_ip', 'private_ip'] if not no_vs: if vlan.get('virtualGuests'): vs_table = formatting.KeyValueTable(server_columns) for vsi in vlan['virtualGuests']: vs_table.add_row([vsi['hostname'], vsi['domain'], vsi.get('primaryIpAddress'), vsi.get('primaryBackendIpAddress')]) table.add_row(['vs', vs_table]) else: table.add_row(['vs', 'none']) if not no_hardware: if vlan.get('hardware'): hw_table = formatting.Table(server_columns) for hardware in vlan['hardware']: hw_table.add_row([hardware['hostname'], hardware['domain'], hardware.get('primaryIpAddress'), hardware.get('primaryBackendIpAddress')]) table.add_row(['hardware', hw_table]) else: table.add_row(['hardware', 'none']) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/vlan/list.py000066400000000000000000000027521324365065500216740ustar00rootroot00000000000000"""List VLANs.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer import utils COLUMNS = ['id', 'number', 'name', 'firewall', 'datacenter', 'hardware', 'virtual_servers', 'public_ips'] @click.command() @click.option('--sortby', help='Column to sort by', type=click.Choice(COLUMNS)) @click.option('--datacenter', '-d', help='Filter by datacenter shortname (sng01, dal05, ...)') @click.option('--number', '-n', help='Filter by VLAN number') @click.option('--name', help='Filter by VLAN name') @environment.pass_env def cli(env, sortby, datacenter, number, name): """List VLANs.""" mgr = SoftLayer.NetworkManager(env.client) table = formatting.Table(COLUMNS) table.sortby = sortby vlans = mgr.list_vlans(datacenter=datacenter, vlan_number=number, name=name) for vlan in vlans: table.add_row([ vlan['id'], vlan['vlanNumber'], vlan.get('name') or formatting.blank(), 'Yes' if vlan['firewallInterfaces'] else 'No', utils.lookup(vlan, 'primaryRouter', 'datacenter', 'name'), vlan['hardwareCount'], vlan['virtualGuestCount'], vlan['totalPrimaryIpAddressCount'], ]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/vpn/000077500000000000000000000000001324365065500202045ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/CLI/vpn/__init__.py000066400000000000000000000000371324365065500223150ustar00rootroot00000000000000"""Virtual Private Networks""" softlayer-python-5.4.2/SoftLayer/CLI/vpn/ipsec/000077500000000000000000000000001324365065500213075ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/CLI/vpn/ipsec/__init__.py000066400000000000000000000000201324365065500234100ustar00rootroot00000000000000"""IPSEC VPN""" softlayer-python-5.4.2/SoftLayer/CLI/vpn/ipsec/configure.py000066400000000000000000000020351324365065500236420ustar00rootroot00000000000000"""Request network configuration of an IPSEC tunnel context.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI.exceptions import CLIHalt @click.command() @click.argument('context_id', type=int) @environment.pass_env def cli(env, context_id): """Request configuration of a tunnel context. This action will update the advancedConfigurationFlag on the context instance and further modifications against the context will be prevented until all changes can be propgated to network devices. """ manager = SoftLayer.IPSECManager(env.client) # ensure context can be retrieved by given id manager.get_tunnel_context(context_id) succeeded = manager.apply_configuration(context_id) if succeeded: env.out('Configuration request received for context #{}' .format(context_id)) else: raise CLIHalt('Failed to enqueue configuration request for context #{}' .format(context_id)) softlayer-python-5.4.2/SoftLayer/CLI/vpn/ipsec/detail.py000066400000000000000000000175171324365065500231360ustar00rootroot00000000000000"""List IPSEC VPN Tunnel Context Details.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting @click.command() @click.argument('context_id', type=int) @click.option('-i', '--include', default=[], multiple=True, type=click.Choice(['at', 'is', 'rs', 'sr', 'ss']), help='Include additional resources') @environment.pass_env def cli(env, context_id, include): """List IPSEC VPN tunnel context details. Additional resources can be joined using multiple instances of the include option, for which the following choices are available. \b at: address translations is: internal subnets rs: remote subnets sr: statically routed subnets ss: service subnets """ mask = _get_tunnel_context_mask(('at' in include), ('is' in include), ('rs' in include), ('sr' in include), ('ss' in include)) manager = SoftLayer.IPSECManager(env.client) context = manager.get_tunnel_context(context_id, mask=mask) env.out('Context Details:') env.fout(_get_context_table(context)) for relation in include: if relation == 'at': env.out('Address Translations:') env.fout(_get_address_translations_table( context.get('addressTranslations', []))) elif relation == 'is': env.out('Internal Subnets:') env.fout(_get_subnets_table(context.get('internalSubnets', []))) elif relation == 'rs': env.out('Remote Subnets:') env.fout(_get_subnets_table(context.get('customerSubnets', []))) elif relation == 'sr': env.out('Static Subnets:') env.fout(_get_subnets_table(context.get('staticRouteSubnets', []))) elif relation == 'ss': env.out('Service Subnets:') env.fout(_get_subnets_table(context.get('serviceSubnets', []))) def _get_address_translations_table(address_translations): """Yields a formatted table to print address translations. :param List[dict] address_translations: List of address translations. :return Table: Formatted for address translation output. """ table = formatting.Table(['id', 'static IP address', 'static IP address id', 'remote IP address', 'remote IP address id', 'note']) for address_translation in address_translations: table.add_row([address_translation.get('id', ''), address_translation.get('internalIpAddressRecord', {}) .get('ipAddress', ''), address_translation.get('internalIpAddressId', ''), address_translation.get('customerIpAddressRecord', {}) .get('ipAddress', ''), address_translation.get('customerIpAddressId', ''), address_translation.get('notes', '')]) return table def _get_subnets_table(subnets): """Yields a formatted table to print subnet details. :param List[dict] subnets: List of subnets. :return Table: Formatted for subnet output. """ table = formatting.Table(['id', 'network identifier', 'cidr', 'note']) for subnet in subnets: table.add_row([subnet.get('id', ''), subnet.get('networkIdentifier', ''), subnet.get('cidr', ''), subnet.get('note', '')]) return table def _get_tunnel_context_mask(address_translations=False, internal_subnets=False, remote_subnets=False, static_subnets=False, service_subnets=False): """Yields a mask object for a tunnel context. All exposed properties on the tunnel context service are included in the constructed mask. Additional joins may be requested. :param bool address_translations: Whether to join the context's address translation entries. :param bool internal_subnets: Whether to join the context's internal subnet associations. :param bool remote_subnets: Whether to join the context's remote subnet associations. :param bool static_subnets: Whether to join the context's statically routed subnet associations. :param bool service_subnets: Whether to join the SoftLayer service network subnets. :return string: Encoding for the requested mask object. """ entries = ['id', 'accountId', 'advancedConfigurationFlag', 'createDate', 'customerPeerIpAddress', 'modifyDate', 'name', 'friendlyName', 'internalPeerIpAddress', 'phaseOneAuthentication', 'phaseOneDiffieHellmanGroup', 'phaseOneEncryption', 'phaseOneKeylife', 'phaseTwoAuthentication', 'phaseTwoDiffieHellmanGroup', 'phaseTwoEncryption', 'phaseTwoKeylife', 'phaseTwoPerfectForwardSecrecy', 'presharedKey'] if address_translations: entries.append('addressTranslations[internalIpAddressRecord[ipAddress],' 'customerIpAddressRecord[ipAddress]]') if internal_subnets: entries.append('internalSubnets') if remote_subnets: entries.append('customerSubnets') if static_subnets: entries.append('staticRouteSubnets') if service_subnets: entries.append('serviceSubnets') return '[mask[{}]]'.format(','.join(entries)) def _get_context_table(context): """Yields a formatted table to print context details. :param dict context: The tunnel context :return Table: Formatted for tunnel context output """ table = formatting.KeyValueTable(['name', 'value']) table.align['name'] = 'r' table.align['value'] = 'l' table.add_row(['id', context.get('id', '')]) table.add_row(['name', context.get('name', '')]) table.add_row(['friendly name', context.get('friendlyName', '')]) table.add_row(['internal peer IP address', context.get('internalPeerIpAddress', '')]) table.add_row(['remote peer IP address', context.get('customerPeerIpAddress', '')]) table.add_row(['advanced configuration flag', context.get('advancedConfigurationFlag', '')]) table.add_row(['preshared key', context.get('presharedKey', '')]) table.add_row(['phase 1 authentication', context.get('phaseOneAuthentication', '')]) table.add_row(['phase 1 diffie hellman group', context.get('phaseOneDiffieHellmanGroup', '')]) table.add_row(['phase 1 encryption', context.get('phaseOneEncryption', '')]) table.add_row(['phase 1 key life', context.get('phaseOneKeylife', '')]) table.add_row(['phase 2 authentication', context.get('phaseTwoAuthentication', '')]) table.add_row(['phase 2 diffie hellman group', context.get('phaseTwoDiffieHellmanGroup', '')]) table.add_row(['phase 2 encryption', context.get('phaseTwoEncryption', '')]) table.add_row(['phase 2 key life', context.get('phaseTwoKeylife', '')]) table.add_row(['phase 2 perfect forward secrecy', context.get('phaseTwoPerfectForwardSecrecy', '')]) table.add_row(['created', context.get('createDate')]) table.add_row(['modified', context.get('modifyDate')]) return table softlayer-python-5.4.2/SoftLayer/CLI/vpn/ipsec/list.py000066400000000000000000000020241324365065500226320ustar00rootroot00000000000000"""List IPSec VPN Tunnel Contexts.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting @click.command() @environment.pass_env def cli(env): """List IPSec VPN tunnel contexts""" manager = SoftLayer.IPSECManager(env.client) contexts = manager.get_tunnel_contexts() table = formatting.Table(['id', 'name', 'friendly name', 'internal peer IP address', 'remote peer IP address', 'created']) for context in contexts: table.add_row([context.get('id', ''), context.get('name', ''), context.get('friendlyName', ''), context.get('internalPeerIpAddress', ''), context.get('customerPeerIpAddress', ''), context.get('createDate', '')]) env.fout(table) softlayer-python-5.4.2/SoftLayer/CLI/vpn/ipsec/subnet/000077500000000000000000000000001324365065500226075ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/CLI/vpn/ipsec/subnet/__init__.py000066400000000000000000000000301324365065500247110ustar00rootroot00000000000000"""IPSEC VPN Subnets""" softlayer-python-5.4.2/SoftLayer/CLI/vpn/ipsec/subnet/add.py000066400000000000000000000057721324365065500237240ustar00rootroot00000000000000"""Add a subnet to an IPSEC tunnel context.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI.custom_types import NetworkParamType from SoftLayer.CLI import environment from SoftLayer.CLI.exceptions import ArgumentError from SoftLayer.CLI.exceptions import CLIHalt @click.command() @click.argument('context_id', type=int) @click.option('-s', '--subnet-id', default=None, type=int, help='Subnet identifier to add') @click.option('-t', '--type', '--subnet-type', required=True, type=click.Choice(['internal', 'remote', 'service']), help='Subnet type to add') @click.option('-n', '--network', '--network-identifier', default=None, type=NetworkParamType(), help='Subnet network identifier to create') @environment.pass_env def cli(env, context_id, subnet_id, subnet_type, network_identifier): """Add a subnet to an IPSEC tunnel context. A subnet id may be specified to link to the existing tunnel context. Otherwise, a network identifier in CIDR notation should be specified, indicating that a subnet resource should first be created before associating it with the tunnel context. Note that this is only supported for remote subnets, which are also deleted upon failure to attach to a context. A separate configuration request should be made to realize changes on network devices. """ create_remote = False if subnet_id is None: if network_identifier is None: raise ArgumentError('Either a network identifier or subnet id ' 'must be provided.') if subnet_type != 'remote': raise ArgumentError('Unable to create {} subnets' .format(subnet_type)) create_remote = True manager = SoftLayer.IPSECManager(env.client) context = manager.get_tunnel_context(context_id) if create_remote: subnet = manager.create_remote_subnet(context['accountId'], identifier=network_identifier[0], cidr=network_identifier[1]) subnet_id = subnet['id'] env.out('Created subnet {}/{} #{}' .format(network_identifier[0], network_identifier[1], subnet_id)) succeeded = False if subnet_type == 'internal': succeeded = manager.add_internal_subnet(context_id, subnet_id) elif subnet_type == 'remote': succeeded = manager.add_remote_subnet(context_id, subnet_id) elif subnet_type == 'service': succeeded = manager.add_service_subnet(context_id, subnet_id) if succeeded: env.out('Added {} subnet #{}'.format(subnet_type, subnet_id)) else: raise CLIHalt('Failed to add {} subnet #{}' .format(subnet_type, subnet_id)) softlayer-python-5.4.2/SoftLayer/CLI/vpn/ipsec/subnet/remove.py000066400000000000000000000032211324365065500244540ustar00rootroot00000000000000"""Remove a subnet from an IPSEC tunnel context.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI.exceptions import CLIHalt @click.command() @click.argument('context_id', type=int) @click.option('-s', '--subnet-id', required=True, type=int, help='Subnet identifier to remove') @click.option('-t', '--type', '--subnet-type', required=True, type=click.Choice(['internal', 'remote', 'service']), help='Subnet type to add') @environment.pass_env def cli(env, context_id, subnet_id, subnet_type): """Remove a subnet from an IPSEC tunnel context. The subnet id to remove must be specified. Remote subnets are deleted upon removal from a tunnel context. A separate configuration request should be made to realize changes on network devices. """ manager = SoftLayer.IPSECManager(env.client) # ensure context can be retrieved by given id manager.get_tunnel_context(context_id) succeeded = False if subnet_type == 'internal': succeeded = manager.remove_internal_subnet(context_id, subnet_id) elif subnet_type == 'remote': succeeded = manager.remove_remote_subnet(context_id, subnet_id) elif subnet_type == 'service': succeeded = manager.remove_service_subnet(context_id, subnet_id) if succeeded: env.out('Removed {} subnet #{}'.format(subnet_type, subnet_id)) else: raise CLIHalt('Failed to remove {} subnet #{}' .format(subnet_type, subnet_id)) softlayer-python-5.4.2/SoftLayer/CLI/vpn/ipsec/translation/000077500000000000000000000000001324365065500236455ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/CLI/vpn/ipsec/translation/__init__.py000066400000000000000000000000451324365065500257550ustar00rootroot00000000000000"""IPSEC VPN Address Translations""" softlayer-python-5.4.2/SoftLayer/CLI/vpn/ipsec/translation/add.py000066400000000000000000000027751324365065500247620ustar00rootroot00000000000000"""Add an address translation to an IPSEC tunnel context.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment # from SoftLayer.CLI.exceptions import ArgumentError # from SoftLayer.CLI.exceptions import CLIHalt @click.command() @click.argument('context_id', type=int) # todo: Update to utilize custom IP address type @click.option('-s', '--static-ip', required=True, help='Static IP address value') # todo: Update to utilize custom IP address type @click.option('-r', '--remote-ip', required=True, help='Remote IP address value') @click.option('-n', '--note', default=None, help='Note value') @environment.pass_env def cli(env, context_id, static_ip, remote_ip, note): """Add an address translation to an IPSEC tunnel context. A separate configuration request should be made to realize changes on network devices. """ manager = SoftLayer.IPSECManager(env.client) # ensure context can be retrieved by given id manager.get_tunnel_context(context_id) translation = manager.create_translation(context_id, static_ip=static_ip, remote_ip=remote_ip, notes=note) env.out('Created translation from {} to {} #{}' .format(static_ip, remote_ip, translation['id'])) softlayer-python-5.4.2/SoftLayer/CLI/vpn/ipsec/translation/remove.py000066400000000000000000000020741324365065500255170ustar00rootroot00000000000000"""Remove a translation entry from an IPSEC tunnel context.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI.exceptions import CLIHalt @click.command() @click.argument('context_id', type=int) @click.option('-t', '--translation-id', required=True, type=int, help='Translation identifier to remove') @environment.pass_env def cli(env, context_id, translation_id): """Remove a translation entry from an IPSEC tunnel context. A separate configuration request should be made to realize changes on network devices. """ manager = SoftLayer.IPSECManager(env.client) # ensure translation can be retrieved by given id manager.get_translation(context_id, translation_id) succeeded = manager.remove_translation(context_id, translation_id) if succeeded: env.out('Removed translation #{}'.format(translation_id)) else: raise CLIHalt('Failed to remove translation #{}'.format(translation_id)) softlayer-python-5.4.2/SoftLayer/CLI/vpn/ipsec/translation/update.py000066400000000000000000000032201324365065500254760ustar00rootroot00000000000000"""Update an address translation for an IPSEC tunnel context.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI.exceptions import CLIHalt @click.command() @click.argument('context_id', type=int) @click.option('-t', '--translation-id', required=True, type=int, help='Translation identifier to update') # todo: Update to utilize custom IP address type @click.option('-s', '--static-ip', default=None, help='Static IP address value') # todo: Update to utilize custom IP address type @click.option('-r', '--remote-ip', default=None, help='Remote IP address value') @click.option('-n', '--note', default=None, help='Note value') @environment.pass_env def cli(env, context_id, translation_id, static_ip, remote_ip, note): """Update an address translation for an IPSEC tunnel context. A separate configuration request should be made to realize changes on network devices. """ manager = SoftLayer.IPSECManager(env.client) succeeded = manager.update_translation(context_id, translation_id, static_ip=static_ip, remote_ip=remote_ip, notes=note) if succeeded: env.out('Updated translation #{}'.format(translation_id)) else: raise CLIHalt('Failed to update translation #{}'.format(translation_id)) softlayer-python-5.4.2/SoftLayer/CLI/vpn/ipsec/update.py000066400000000000000000000072031324365065500231450ustar00rootroot00000000000000"""Updates an IPSEC tunnel context.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI.exceptions import CLIHalt @click.command() @click.argument('context_id', type=int) @click.option('--friendly-name', default=None, help='Friendly name value') # todo: Update to utilize custom IP address type @click.option('--remote-peer', default=None, help='Remote peer IP address value') @click.option('--preshared-key', default=None, help='Preshared key value') @click.option('--p1-auth', '--phase1-auth', default=None, type=click.Choice(['MD5', 'SHA1', 'SHA256']), help='Phase 1 authentication value') @click.option('--p1-crypto', '--phase1-crypto', default=None, type=click.Choice(['DES', '3DES', 'AES128', 'AES192', 'AES256']), help='Phase 1 encryption value') @click.option('--p1-dh', '--phase1-dh', default=None, type=click.Choice(['0', '1', '2', '5']), help='Phase 1 diffie hellman group value') @click.option('--p1-key-ttl', '--phase1-key-ttl', default=None, type=click.IntRange(120, 172800), help='Phase 1 key life value') @click.option('--p2-auth', '--phase2-auth', default=None, type=click.Choice(['MD5', 'SHA1', 'SHA256']), help='Phase 2 authentication value') @click.option('--p2-crypto', '--phase2-crypto', default=None, type=click.Choice(['DES', '3DES', 'AES128', 'AES192', 'AES256']), help='Phase 2 encryption value') @click.option('--p2-dh', '--phase2-dh', default=None, type=click.Choice(['0', '1', '2', '5']), help='Phase 2 diffie hellman group value') @click.option('--p2-forward-secrecy', '--phase2-forward-secrecy', default=None, type=click.IntRange(0, 1), help='Phase 2 perfect forward secrecy value') @click.option('--p2-key-ttl', '--phase2-key-ttl', default=None, type=click.IntRange(120, 172800), help='Phase 2 key life value') @environment.pass_env def cli(env, context_id, friendly_name, remote_peer, preshared_key, phase1_auth, phase1_crypto, phase1_dh, phase1_key_ttl, phase2_auth, phase2_crypto, phase2_dh, phase2_forward_secrecy, phase2_key_ttl): """Update tunnel context properties. Updates are made atomically, so either all are accepted or none are. Key life values must be in the range 120-172800. Phase 2 perfect forward secrecy must be in the range 0-1. A separate configuration request should be made to realize changes on network devices. """ manager = SoftLayer.IPSECManager(env.client) succeeded = manager.update_tunnel_context( context_id, friendly_name=friendly_name, remote_peer=remote_peer, preshared_key=preshared_key, phase1_auth=phase1_auth, phase1_crypto=phase1_crypto, phase1_dh=phase1_dh, phase1_key_ttl=phase1_key_ttl, phase2_auth=phase2_auth, phase2_crypto=phase2_crypto, phase2_dh=phase2_dh, phase2_forward_secrecy=phase2_forward_secrecy, phase2_key_ttl=phase2_key_ttl ) if succeeded: env.out('Updated context #{}'.format(context_id)) else: raise CLIHalt('Failed to update context #{}'.format(context_id)) softlayer-python-5.4.2/SoftLayer/__init__.py000066400000000000000000000021501324365065500211010ustar00rootroot00000000000000""" SoftLayer Python API Client ~~~~~~~~~~~~~~~~~~~~~~~~~~~ SoftLayer API bindings Usage: >>> import SoftLayer >>> client = SoftLayer.create_client_from_env(username="username", api_key="api_key") >>> resp = client.call('Account', 'getObject') >>> resp['companyName'] 'Your Company' :license: MIT, see LICENSE for more details. """ # pylint: disable=w0401,invalid-name from SoftLayer import consts from SoftLayer.API import * # NOQA from SoftLayer.managers import * # NOQA from SoftLayer.exceptions import * # NOQA from SoftLayer.auth import * # NOQA from SoftLayer.transports import * # NOQA __title__ = 'SoftLayer' __version__ = consts.VERSION __author__ = 'SoftLayer Technologies, Inc.' __license__ = 'MIT' __copyright__ = 'Copyright 2016 SoftLayer Technologies, Inc.' __all__ = [ 'BaseClient', 'create_client_from_env', 'Client', 'BasicAuthentication', 'SoftLayerError', 'SoftLayerAPIError', 'SoftLayerListResult', 'API_PUBLIC_ENDPOINT', 'API_PRIVATE_ENDPOINT', ] softlayer-python-5.4.2/SoftLayer/auth.py000066400000000000000000000053671324365065500203200ustar00rootroot00000000000000""" SoftLayer.auth ~~~~~~~~~~~~~~ Module with the supported auth mechanisms for the SoftLayer API :license: MIT, see LICENSE for more details. """ # pylint: disable=no-self-use __all__ = [ 'BasicAuthentication', 'TokenAuthentication', 'BasicHTTPAuthentication', 'AuthenticationBase', ] class AuthenticationBase(object): """A base authentication class intended to be overridden.""" def get_request(self, request): """Receives request options and returns request options. :param options dict: dictionary of request options """ return request def get_headers(self): """Return a dictionary of headers to be inserted for authentication. .. deprecated:: 3.3.0 Use :func:`get_options` instead. """ return {} class TokenAuthentication(AuthenticationBase): """Token-based authentication class. :param user_id int: a user's id :param auth_token str: a user's auth token, attained through User_Customer::getPortalLoginToken """ def __init__(self, user_id, auth_token): self.user_id = user_id self.auth_token = auth_token def get_request(self, request): """Sets token-based auth headers.""" request.headers['authenticate'] = { 'complexType': 'PortalLoginToken', 'userId': self.user_id, 'authToken': self.auth_token, } return request def __repr__(self): return "TokenAuthentication(%r)" % self.user_id class BasicAuthentication(AuthenticationBase): """Token-based authentication class. :param username str: a user's username :param api_key str: a user's API key """ def __init__(self, username, api_key): self.username = username self.api_key = api_key def get_request(self, request): """Sets token-based auth headers.""" request.headers['authenticate'] = { 'username': self.username, 'apiKey': self.api_key, } return request def __repr__(self): return "BasicAuthentication(username=%r)" % self.username class BasicHTTPAuthentication(AuthenticationBase): """Token-based authentication class. :param username str: a user's username :param api_key str: a user's API key """ def __init__(self, username, api_key): self.username = username self.api_key = api_key def get_request(self, request): """Sets token-based auth headers.""" request.transport_user = self.username request.transport_password = self.api_key return request def __repr__(self): return "BasicHTTPAuthentication(username=%r)" % self.username softlayer-python-5.4.2/SoftLayer/config.py000066400000000000000000000055541324365065500206220ustar00rootroot00000000000000""" SoftLayer.config ~~~~~~~~~~~~~~~~ Handles different methods for loading configuration for the API bindings :license: MIT, see LICENSE for more details. """ import os import os.path from SoftLayer import utils def get_client_settings_args(**kwargs): """Retrieve client settings from user-supplied arguments. :param \\*\\*kwargs: Arguments that are passed into the client instance """ timeout = kwargs.get('timeout') if timeout is not None: timeout = float(timeout) return { 'endpoint_url': kwargs.get('endpoint_url'), 'timeout': timeout, 'proxy': kwargs.get('proxy'), 'username': kwargs.get('username'), 'api_key': kwargs.get('api_key'), } def get_client_settings_env(**_): """Retrieve client settings from environment settings. :param \\*\\*kwargs: Arguments that are passed into the client instance """ return { 'proxy': os.environ.get('https_proxy'), 'username': os.environ.get('SL_USERNAME'), 'api_key': os.environ.get('SL_API_KEY'), } def get_client_settings_config_file(**kwargs): # pylint: disable=inconsistent-return-statements """Retrieve client settings from the possible config file locations. :param \\*\\*kwargs: Arguments that are passed into the client instance """ config_files = ['/etc/softlayer.conf', '~/.softlayer'] if kwargs.get('config_file'): config_files.append(kwargs.get('config_file')) config_files = [os.path.expanduser(f) for f in config_files] config = utils.configparser.RawConfigParser({ 'username': '', 'api_key': '', 'endpoint_url': '', 'timeout': '0', 'proxy': '', }) config.read(config_files) if config.has_section('softlayer'): return { 'endpoint_url': config.get('softlayer', 'endpoint_url'), 'timeout': config.getfloat('softlayer', 'timeout'), 'proxy': config.get('softlayer', 'proxy'), 'username': config.get('softlayer', 'username'), 'api_key': config.get('softlayer', 'api_key'), } SETTING_RESOLVERS = [get_client_settings_args, get_client_settings_env, get_client_settings_config_file] def get_client_settings(**kwargs): """Parse client settings. Parses settings from various input methods, preferring earlier values to later ones. The settings currently come from explicit user arguments, environmental variables and config files. :param \\*\\*kwargs: Arguments that are passed into the client instance """ all_settings = {} for setting_method in SETTING_RESOLVERS: settings = setting_method(**kwargs) if settings: settings.update((k, v) for k, v in all_settings.items() if v) all_settings = settings return all_settings softlayer-python-5.4.2/SoftLayer/consts.py000066400000000000000000000007531324365065500206620ustar00rootroot00000000000000""" SoftLayer.consts ~~~~~~~~~~~~~~~~ Contains constants used throughout the library :license: MIT, see LICENSE for more details. """ VERSION = 'v5.4.2' API_PUBLIC_ENDPOINT = 'https://api.softlayer.com/xmlrpc/v3.1/' API_PRIVATE_ENDPOINT = 'https://api.service.softlayer.com/xmlrpc/v3.1/' API_PUBLIC_ENDPOINT_REST = 'https://api.softlayer.com/rest/v3.1/' API_PRIVATE_ENDPOINT_REST = 'https://api.service.softlayer.com/rest/v3.1/' USER_AGENT = "softlayer-python/%s" % VERSION softlayer-python-5.4.2/SoftLayer/decoration.py000066400000000000000000000035601324365065500214770ustar00rootroot00000000000000""" SoftLayer.decoration ~~~~~~~~~~~~~~~~~~~~ Handy decorators to use :license: MIT, see LICENSE for more details. """ from functools import wraps from random import randint from time import sleep from SoftLayer import exceptions RETRIABLE = ( exceptions.ServerError, exceptions.ApplicationError, exceptions.RemoteSystemError, exceptions.TransportError ) def retry(ex=RETRIABLE, tries=4, delay=5, backoff=2, logger=None): """Retry calling the decorated function using an exponential backoff. http://www.saltycrane.com/blog/2009/11/trying-out-retry-decorator-python/ original from: http://wiki.python.org/moin/PythonDecoratorLibrary#Retry :param ex: the exception to check. may be a tuple of exceptions to check :param tries: number of times to try (not retry) before giving up :param delay: initial delay between retries in seconds. A random 0-5s will be added to this number to stagger calls. :param backoff: backoff multiplier e.g. value of 2 will double the delay each retry :param logger: logger to use. If None, print """ def deco_retry(func): """@retry(arg[, ...]) -> true decorator""" @wraps(func) def f_retry(*args, **kwargs): """true decorator -> decorated function""" mtries, mdelay = tries, delay while mtries > 1: try: return func(*args, **kwargs) except ex as error: sleeping = mdelay + randint(0, 5) msg = "%s, Retrying in %d seconds..." % (str(error), sleeping) if logger: logger.warning(msg) sleep(sleeping) mtries -= 1 mdelay *= backoff return func(*args, **kwargs) return f_retry # true decorator return deco_retry softlayer-python-5.4.2/SoftLayer/exceptions.py000066400000000000000000000035241324365065500215310ustar00rootroot00000000000000""" SoftLayer.exceptions ~~~~~~~~~~~~~~~~~~~~ Exceptions used throughout the library :license: MIT, see LICENSE for more details. """ # pylint: disable=C0103 class SoftLayerError(Exception): """The base SoftLayer error.""" class Unauthenticated(SoftLayerError): """Unauthenticated.""" class SoftLayerAPIError(SoftLayerError): """SoftLayerAPIError is an exception raised during API errors. Provides faultCode and faultString properties. """ def __init__(self, fault_code, fault_string, *args): SoftLayerError.__init__(self, fault_string, *args) self.faultCode = fault_code self.reason = self.faultString = fault_string def __repr__(self): return '<%s(%s): %s>' % (self.__class__.__name__, self.faultCode, self.faultString) def __str__(self): return '%s(%s): %s' % (self.__class__.__name__, self.faultCode, self.faultString) class ParseError(SoftLayerAPIError): """Parse Error.""" class ServerError(SoftLayerAPIError): """Server Error.""" class ApplicationError(SoftLayerAPIError): """Application Error.""" class RemoteSystemError(SoftLayerAPIError): """System Error.""" class TransportError(SoftLayerAPIError): """Transport Error.""" # XMLRPC Errors class NotWellFormed(ParseError): """Request was not well formed.""" pass class UnsupportedEncoding(ParseError): """Encoding not supported.""" pass class InvalidCharacter(ParseError): """There was an invalid character.""" pass class SpecViolation(ServerError): """There was a spec violation.""" pass class MethodNotFound(SoftLayerAPIError): """Method name not found.""" pass class InvalidMethodParameters(SoftLayerAPIError): """Invalid method paramters.""" pass class InternalError(ServerError): """Internal Server Error.""" pass softlayer-python-5.4.2/SoftLayer/fixtures/000077500000000000000000000000001324365065500206435ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/fixtures/SoftLayer_Account.py000066400000000000000000000360601324365065500246060ustar00rootroot00000000000000# -*- coding: UTF-8 -*- getPrivateBlockDeviceTemplateGroups = [{ 'accountId': 1234, 'blockDevices': [], 'createDate': '2013-12-05T21:53:03-06:00', 'globalIdentifier': 'E6DBD73B-1651-4B28-BCBA-A11DF7C9D79E', 'id': 200, 'name': 'test_image', 'parentId': '', 'publicFlag': False, }, { 'accountId': 1234, 'blockDevices': [], 'createDate': '2013-12-05T21:53:03-06:00', 'globalIdentifier': 'F9329795-4220-4B0A-B970-C86B950667FA', 'id': 201, # 'name': 'private_image2', 'name': u'a¬ሴ€耀', 'parentId': '', 'publicFlag': False, }] getVirtualGuests = [{ 'id': 100, 'metricTrackingObjectId': 1, 'hostname': 'vs-test1', 'domain': 'test.sftlyr.ws', 'fullyQualifiedDomainName': 'vs-test1.test.sftlyr.ws', 'status': {'keyName': 'ACTIVE', 'name': 'Active'}, 'datacenter': {'id': 50, 'name': 'TEST00', 'description': 'Test Data Center'}, 'powerState': {'keyName': 'RUNNING', 'name': 'Running'}, 'maxCpu': 2, 'maxMemory': 1024, 'primaryIpAddress': '172.16.240.2', 'globalIdentifier': '1a2b3c-1701', 'primaryBackendIpAddress': '10.45.19.37', 'hourlyBillingFlag': False, 'billingItem': { 'id': 6327, 'recurringFee': 1.54, 'orderItem': { 'order': { 'userRecord': { 'username': 'chechu', } } } }, }, { 'id': 104, 'metricTrackingObjectId': 2, 'hostname': 'vs-test2', 'domain': 'test.sftlyr.ws', 'fullyQualifiedDomainName': 'vs-test2.test.sftlyr.ws', 'status': {'keyName': 'ACTIVE', 'name': 'Active'}, 'datacenter': {'id': 50, 'name': 'TEST00', 'description': 'Test Data Center'}, 'powerState': {'keyName': 'RUNNING', 'name': 'Running'}, 'maxCpu': 4, 'maxMemory': 4096, 'primaryIpAddress': '172.16.240.7', 'globalIdentifier': '05a8ac-6abf0', 'primaryBackendIpAddress': '10.45.19.35', 'hourlyBillingFlag': True, 'billingItem': { 'id': 6327, 'recurringFee': 1.54, 'orderItem': { 'order': { 'userRecord': { 'username': 'chechu', } } } }, 'virtualRack': {'id': 1, 'bandwidthAllotmentTypeId': 2}, }] getMonthlyVirtualGuests = [vs for vs in getVirtualGuests if not vs['hourlyBillingFlag']] getHourlyVirtualGuests = [vs for vs in getVirtualGuests if vs['hourlyBillingFlag']] getHardware = [{ 'id': 1000, 'metricTrackingObject': {'id': 3}, 'globalIdentifier': '1a2b3c-1701', 'datacenter': {'id': 50, 'name': 'TEST00', 'description': 'Test Data Center'}, 'billingItem': { 'id': 6327, 'recurringFee': 1.54, 'orderItem': { 'order': { 'userRecord': { 'username': 'chechu', } } } }, 'primaryIpAddress': '172.16.1.100', 'hostname': 'hardware-test1', 'domain': 'test.sftlyr.ws', 'fullyQualifiedDomainName': 'hardware-test1.test.sftlyr.ws', 'processorPhysicalCoreAmount': 2, 'memoryCapacity': 2, 'primaryBackendIpAddress': '10.1.0.2', 'networkManagementIpAddress': '10.1.0.3', 'hardwareStatus': {'status': 'ACTIVE'}, 'provisionDate': '2013-08-01 15:23:45', 'notes': 'These are test notes.', 'operatingSystem': { 'softwareLicense': { 'softwareDescription': { 'referenceCode': 'Ubuntu', 'name': 'Ubuntu 12.04 LTS', } }, 'passwords': [ {'username': 'root', 'password': 'abc123'} ], }, 'networkVlans': [ { 'networkSpace': 'PRIVATE', 'vlanNumber': 1800, 'id': 9653 }, { 'networkSpace': 'PUBLIC', 'vlanNumber': 3672, 'id': 19082 }, ], 'tagReferences': [ {'tag': {'name': 'test_tag'}} ], 'activeTransaction': { 'transactionStatus': { 'name': 'TXN_NAME', 'friendlyName': 'Friendly Transaction Name', 'id': 6660 } }, }, { 'id': 1001, 'metricTrackingObject': {'id': 4}, 'globalIdentifier': '1a2b3c-1702', 'datacenter': {'name': 'TEST00', 'description': 'Test Data Center'}, 'billingItem': { 'id': 7112, 'orderItem': { 'order': { 'userRecord': { 'username': 'chechu', } } } }, 'primaryIpAddress': '172.16.4.94', 'hostname': 'hardware-test2', 'domain': 'test.sftlyr.ws', 'fullyQualifiedDomainName': 'hardware-test2.test.sftlyr.ws', 'processorPhysicalCoreAmount': 4, 'memoryCapacity': 4, 'primaryBackendIpAddress': '10.1.0.3', 'hardwareStatus': {'status': 'ACTIVE'}, 'provisionDate': '2013-08-03 07:15:22', 'operatingSystem': { 'softwareLicense': { 'softwareDescription': { 'referenceCode': 'Ubuntu', 'name': 'Ubuntu 12.04 LTS', } } }, 'networkVlans': [ { 'networkSpace': 'PRIVATE', 'vlanNumber': 1800, 'id': 9653 }, { 'networkSpace': 'PUBLIC', 'vlanNumber': 3672, 'id': 19082 }, ] }, { 'id': 1002, 'metricTrackingObject': {'id': 5}, 'datacenter': {'name': 'TEST00', 'description': 'Test Data Center'}, 'billingItem': { 'id': 7112, 'orderItem': { 'order': { 'userRecord': { 'username': 'chechu', } } } }, 'primaryIpAddress': '172.16.4.95', 'hostname': 'hardware-bad-memory', 'domain': 'test.sftlyr.ws', 'fullyQualifiedDomainName': 'hardware-bad-memory.test.sftlyr.ws', 'processorPhysicalCoreAmount': 4, 'memoryCapacity': None, 'primaryBackendIpAddress': '10.1.0.4', 'hardwareStatus': {'status': 'ACTIVE'}, 'provisionDate': '2014-04-02 13:48:00', 'operatingSystem': { 'softwareLicense': { 'softwareDescription': { 'referenceCode': 'Ubuntu', 'name': 'Ubuntu 12.04 LTS', } } }, 'networkVlans': [ { 'networkSpace': 'PRIVATE', 'vlanNumber': 1800, 'id': 9653 }, { 'networkSpace': 'PUBLIC', 'vlanNumber': 3672, 'id': 19082 }, ] }, { 'id': 1003, }] getDomains = [{'name': 'example.com', 'id': 12345, 'serial': 2014030728, 'updateDate': '2014-03-07T13:52:31-06:00'}] getObject = { 'cdnAccounts': [ { "cdnAccountName": "1234a", "providerPortalAccessFlag": False, "createDate": "2012-06-25T14:05:28-07:00", "id": 1234, "legacyCdnFlag": False, "dependantServiceFlag": True, "cdnSolutionName": "ORIGIN_PULL", "statusId": 4, "accountId": 1234 }, { "cdnAccountName": "1234a", "providerPortalAccessFlag": False, "createDate": "2012-07-24T13:34:25-07:00", "id": 1234, "legacyCdnFlag": False, "dependantServiceFlag": False, "cdnSolutionName": "POP_PULL", "statusId": 4, "accountId": 1234 } ], 'accountId': 1234 } getRwhoisData = { 'abuseEmail': 'abuseEmail', 'accountId': 1234, 'address1': 'address1', 'address2': 'address2', 'city': 'city', 'companyName': 'companyName', 'country': 'country', 'createDate': 'createDate', 'firstName': 'firstName', 'id': 'id', 'lastName': 'lastName', 'modifyDate': 'modifyDate', 'postalCode': 'postalCode', 'privateResidenceFlag': True, } getGlobalIpRecords = [{ 'id': '200', 'ipAddress': { 'subnet': { 'networkIdentifier': '10.0.0.1', }, 'ipAddress': '127.0.0.1', }, 'destinationIpAddress': { 'ipAddress': '127.0.0.1', 'virtualGuest': {'fullyQualifiedDomainName': 'example.com'}} }, { 'id': '201', 'ipAddress': { 'subnet': { 'networkIdentifier': '10.0.0.1', }, 'ipAddress': '127.0.0.1', }, 'destinationIpAddress': { 'ipAddress': '127.0.0.1', 'hardware': {'fullyQualifiedDomainName': 'example.com'}} }] getSubnets = [ { 'id': '100', 'networkIdentifier': '10.0.0.1', 'datacenter': {'name': 'dal00'}, 'version': 4, 'subnetType': 'PRIMARY' }] getSshKeys = [{'id': '100', 'label': 'Test 1'}, {'id': '101', 'label': 'Test 2', 'finterprint': 'aa:bb:cc:dd', 'notes': 'my key'}] getSecurityCertificates = [{'certificate': '1234', 'commonName': 'cert', 'id': 1234}] getExpiredSecurityCertificates = getSecurityCertificates getValidSecurityCertificates = getSecurityCertificates getTickets = [ { "accountId": 1234, "assignedUserId": 12345, "createDate": "2013-08-01T14:14:04-07:00", "id": 100, "lastEditDate": "2013-08-01T14:16:47-07:00", "lastEditType": "AUTO", "modifyDate": "2013-08-01T14:16:47-07:00", "status": { "id": 1002, "name": "Closed" }, "statusId": 1002, "title": "Cloud Instance Cancellation - 08/01/13" }, { "accountId": 1234, "assignedUserId": 12345, "createDate": "2013-08-01T14:14:04-07:00", "id": 101, "lastEditDate": "2013-08-01T14:16:47-07:00", "lastEditType": "AUTO", "modifyDate": "2013-08-01T14:16:47-07:00", "status": { "id": 1002, "name": "Closed" }, "statusId": 1002, "title": "Cloud Instance Cancellation - 08/01/13" }, { "accountId": 1234, "assignedUserId": 12345, "createDate": "2014-03-03T09:44:01-08:00", "id": 102, "lastEditDate": "2013-08-01T14:16:47-07:00", "lastEditType": "AUTO", "modifyDate": "2014-03-03T09:44:03-08:00", "status": { "id": 1001, "name": "Open" }, 'assignedUser': {'firstName': 'John', 'lastName': 'Smith'}, "statusId": 1001, "title": "Cloud Instance Cancellation - 08/01/13" }] getOpenTickets = [ticket for ticket in getTickets if ticket['statusId'] == 1001] getClosedTickets = [ticket for ticket in getTickets if ticket['statusId'] == 1002] getCurrentUser = {'id': 12345, 'apiAuthenticationKeys': [{'authenticationKey': 'A' * 64}]} getCdnAccounts = [ { "cdnAccountName": "1234a", "providerPortalAccessFlag": False, "createDate": "2012-06-25T14:05:28-07:00", "id": 1234, "legacyCdnFlag": False, "dependantServiceFlag": True, "cdnSolutionName": "ORIGIN_PULL", "statusId": 4, "accountId": 1234 }, { "cdnAccountName": "1234a", "providerPortalAccessFlag": False, "createDate": "2012-07-24T13:34:25-07:00", "id": 1234, "legacyCdnFlag": False, "dependantServiceFlag": False, "cdnSolutionName": "POP_PULL", "statusId": 4, "accountId": 1234 } ] getNetworkVlans = [{ 'id': 1, 'networkSpace': 'PRIVATE', 'hardwareCount': 0, 'hardware': [], 'networkComponents': [], 'primaryRouter': { 'datacenter': {'name': 'dal00'} }, 'virtualGuestCount': 0, 'virtualGuests': [], 'dedicatedFirewallFlag': True, 'highAvailabilityFirewallFlag': True, 'networkVlanFirewall': {'id': 1234}, 'totalPrimaryIpAddressCount': 1, 'subnetCount': 0, 'subnets': [], }, { 'id': 2, 'networkSpace': 'PRIVATE', 'totalPrimaryIpAddressCount': 2, 'dedicatedFirewallFlag': False, 'hardwareCount': 0, 'hardware': [], 'networkComponents': [], 'primaryRouter': { 'datacenter': {'name': 'dal00'} }, 'virtualGuestCount': 0, 'virtualGuests': [], 'firewallGuestNetworkComponents': [{ 'id': 1234, 'guestNetworkComponent': {'guest': {'id': 1}}, 'status': 'ok'}], 'firewallNetworkComponents': [{ 'id': 1234, 'networkComponent': {'downlinkComponent': {'hardwareId': 1}}, 'status': 'ok'}], 'subnetCount': 0, 'subnets': [], }, { 'id': 3, 'networkSpace': 'PRIVATE', 'name': 'dal00', 'hardwareCount': 1, 'hardware': [{'id': 1}], 'networkComponents': [{'id': 2}], 'primaryRouter': { 'datacenter': {'name': 'dal00'} }, 'totalPrimaryIpAddressCount': 3, 'subnetCount': 0, 'subnets': [], 'virtualGuestCount': 1, 'virtualGuests': [{'id': 3}] }] getAdcLoadBalancers = [] getNasNetworkStorage = [{ 'id': 1, 'capacityGb': 10, 'serviceResource': {'datacenter': {'name': 'Dallas'}}, 'username': 'user', 'password': 'pass', 'serviceResourceBackendIpAddress': '127.0.0.1', 'storageType': {'keyName': 'ENDURANCE_STORAGE'}, 'fileNetworkMountAddress': '127.0.0.1:/TEST', }] getActiveQuotes = [{ 'id': 1234, 'name': 'TestQuote1234', 'quoteKey': '1234test4321', }] getOrders = [{ 'id': 1234, 'resourceType': '1 x 2.0 GHz Core', 'hostName': 'test', 'createDate': '2014-05-01T14:03:04-07:00', 'cost': 0.0 }] getBillingInfo = [{ 'id': 1234, 'accountId': 123, 'resourceType': '1 x 2.0 GHz Core', 'hostName': 'test', 'modifyDate': '2014-05-01T14:03:04-07:00', 'createDate': '2014-05-01T14:03:04-07:00', 'anniversaryDayOfMonth': 2, 'percentDiscountOnetime': 2, 'sparePoolAmount': 0, 'currency': { 'KeyName': 'usd', 'id': 1, 'name': 'dollars' } }] getLatestBillDate = '2014-05-01T14:03:04-07:00' getBalance = 40 getNextInvoiceTotalAmount = 2 getHubNetworkStorage = [{'id': 12345, 'username': 'SLOS12345-1'}, {'id': 12346, 'username': 'SLOS12345-2'}] getIscsiNetworkStorage = [{ 'accountId': 1234, 'billingItem': {'id': 449}, 'capacityGb': 20, 'createDate': '2015:50:15-04:00', 'guestId': '', 'hardwareId': '', 'hostId': '', 'id': 100, 'nasType': 'ISCSI', 'notes': """{'status': 'available'}""", 'password': '', 'serviceProviderId': 1, 'serviceResource': {'datacenter': {'name': 'dal05', 'id': 449500}}, 'serviceResourceBackendIpAddress': '10.1.2.3', 'serviceResourceName': 'Storage Type 01 Aggregate staaspar0101_pc01', 'username': 'username', 'storageType': {'keyName': 'ENDURANCE_STORAGE'}, }] getVirtualDedicatedRacks = [{ 'id': 1, 'name': 'my first pool', 'metricTrackingObjectId': 10, }] getDedicatedHosts = [{ 'datacenter': { 'name': 'dal05' }, 'memoryCapacity': 242, 'name': 'khnguyendh', 'diskCapacity': 1200, 'guestCount': 1, 'cpuCount': 56, 'id': 44701 }] softlayer-python-5.4.2/SoftLayer/fixtures/SoftLayer_Billing_Item.py000066400000000000000000000001151324365065500255400ustar00rootroot00000000000000cancelService = True cancelServiceOnAnniversaryDate = True cancelItem = True softlayer-python-5.4.2/SoftLayer/fixtures/SoftLayer_Billing_Order_Quote.py000066400000000000000000000005331324365065500270760ustar00rootroot00000000000000getObject = { 'accountId': 1234, 'id': 1234, 'name': 'TestQuote1234', 'quoteKey': '1234test4321', } getRecalculatedOrderContainer = { 'orderContainers': [{ 'presetId': '', 'prices': [{ 'id': 1921 }], 'quantity': 1, 'packageId': 50, 'useHourlyPricing': '', }], } softlayer-python-5.4.2/SoftLayer/fixtures/SoftLayer_Dns_Domain.py000066400000000000000000000011361324365065500252210ustar00rootroot00000000000000createObject = {'name': 'example.com'} deleteObject = True editObject = True getZoneFileContents = 'lots of text' getResourceRecords = [ {'id': 1, 'ttl': 7200, 'data': 'd', 'host': 'a', 'type': 'cname'}, {'id': 2, 'ttl': 900, 'data': '1', 'host': 'b', 'type': 'a'}, {'id': 3, 'ttl': 900, 'data': 'x', 'host': 'c', 'type': 'ptr'}, {'id': 4, 'ttl': 86400, 'data': 'b', 'host': 'd', 'type': 'txt'}, {'id': 5, 'ttl': 86400, 'data': 'b', 'host': 'e', 'type': 'txt'}, {'id': 6, 'ttl': 600, 'data': 'b', 'host': 'f', 'type': 'txt'}, ] getObject = {'id': 98765, 'name': 'test-example.com'} softlayer-python-5.4.2/SoftLayer/fixtures/SoftLayer_Dns_Domain_ResourceRecord.py000066400000000000000000000001151324365065500302230ustar00rootroot00000000000000createObject = {'name': 'example.com'} deleteObject = True editObject = True softlayer-python-5.4.2/SoftLayer/fixtures/SoftLayer_Hardware_Server.py000066400000000000000000000063431324365065500262760ustar00rootroot00000000000000getObject = { 'id': 1000, 'globalIdentifier': '1a2b3c-1701', 'datacenter': {'id': 50, 'name': 'TEST00', 'description': 'Test Data Center'}, 'billingItem': { 'id': 6327, 'recurringFee': 1.54, 'nextInvoiceTotalRecurringAmount': 16.08, 'children': [ {'description': 'test', 'nextInvoiceTotalRecurringAmount': 1}, ], 'orderItem': { 'order': { 'userRecord': { 'username': 'chechu', } } } }, 'primaryIpAddress': '172.16.1.100', 'hostname': 'hardware-test1', 'domain': 'test.sftlyr.ws', 'bareMetalInstanceFlag': True, 'fullyQualifiedDomainName': 'hardware-test1.test.sftlyr.ws', 'processorPhysicalCoreAmount': 2, 'memoryCapacity': 2, 'primaryBackendIpAddress': '10.1.0.2', 'networkManagementIpAddress': '10.1.0.3', 'hardwareStatus': {'status': 'ACTIVE'}, 'primaryNetworkComponent': {'maxSpeed': 10, 'speed': 10}, 'provisionDate': '2013-08-01 15:23:45', 'notes': 'These are test notes.', 'operatingSystem': { 'softwareLicense': { 'softwareDescription': { 'referenceCode': 'UBUNTU_12_64', 'name': 'Ubuntu', 'version': 'Ubuntu 12.04 LTS', } }, 'passwords': [ {'username': 'root', 'password': 'abc123'} ], }, 'remoteManagementAccounts': [ {'username': 'root', 'password': 'abc123'} ], 'networkVlans': [ { 'networkSpace': 'PRIVATE', 'vlanNumber': 1800, 'id': 9653 }, { 'networkSpace': 'PUBLIC', 'vlanNumber': 3672, 'id': 19082 }, ], 'tagReferences': [ {'tag': {'name': 'test_tag'}} ], 'activeTransaction': { 'transactionStatus': { 'name': 'TXN_NAME', 'friendlyName': 'Friendly Transaction Name', 'id': 6660 } } } editObject = True setTags = True setPrivateNetworkInterfaceSpeed = True setPublicNetworkInterfaceSpeed = True powerOff = True powerOn = True powerCycle = True rebootSoft = True rebootDefault = True rebootHard = True createFirmwareUpdateTransaction = True setUserMetadata = ['meta'] reloadOperatingSystem = 'OK' getReverseDomainRecords = [ {'resourceRecords': [{'data': '2.0.1.10.in-addr.arpa'}]}] bootToRescueLayer = True getFrontendNetworkComponents = [ {'maxSpeed': 100}, { 'maxSpeed': 1000, 'networkComponentGroup': { 'groupTypeId': 2, 'networkComponents': [{'maxSpeed': 1000}, {'maxSpeed': 1000}] } }, { 'maxSpeed': 1000, 'networkComponentGroup': { 'groupTypeId': 2, 'networkComponents': [{'maxSpeed': 1000}, {'maxSpeed': 1000}] } }, { 'maxSpeed': 1000, 'networkComponentGroup': { 'groupTypeId': 2, 'networkComponents': [{'maxSpeed': 1000}, {'maxSpeed': 1000}] } }, { 'maxSpeed': 1000, 'networkComponentGroup': { 'groupTypeId': 2, 'networkComponents': [{'maxSpeed': 1000}, {'maxSpeed': 1000}] } } ] softlayer-python-5.4.2/SoftLayer/fixtures/SoftLayer_Location.py000066400000000000000000000003131324365065500247520ustar00rootroot00000000000000getDataCenters = [ { "id": 358694, "longName": "London 2", "name": "lon02" }, { "id": 168642, "longName": "San Jose 1", "name": "sjc01" }] softlayer-python-5.4.2/SoftLayer/fixtures/SoftLayer_Location_Datacenter.py000066400000000000000000000000701324365065500271040ustar00rootroot00000000000000getDatacenters = [{ 'id': 0, 'name': 'dal05' }] SoftLayer_Network_Application_Delivery_Controller_LoadBalancer_Health_Check_Type.py000066400000000000000000000005201324365065500411170ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/fixturesgetAllObjects = [ {'id': 21, 'keyname': 'DEFAULT', 'name': 'Default'}, {'id': 3, 'keyname': 'DNS', 'name': 'DNS'}, {'id': 2, 'keyname': 'HTTP', 'name': 'HTTP'}, {'id': 5, 'keyname': 'HTTP-CUSTOM', 'name': 'HTTP-CUSTOM'}, {'id': 4, 'keyname': 'ICMP', 'name': 'Ping'}, {'id': 1, 'keyname': 'TCP', 'name': 'TCP'} ] SoftLayer_Network_Application_Delivery_Controller_LoadBalancer_Routing_Method.py000066400000000000000000000031711324365065500405500ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/fixturesgetAllObjects = [{'id': 21, 'keyname': 'CONSISTENT_HASH_IP', 'name': 'Consistent Hash IP'}, {'id': 9, 'keyname': 'INSERT_COOKIE', 'name': 'Insert Cookie'}, {'id': 1, 'keyname': 'LEAST_CONNECTIONS', 'name': 'Least Connections'}, {'id': 7, 'keyname': 'LEAST_CONNECTIONS_INSERT_COOKIE', 'name': 'Least Connections with Inserted Cookie'}, {'id': 4, 'keyname': 'LEAST_CONNECTIONS_PERSISTENT_IP', 'name': 'Least Connections with Persistent IP'}, {'id': 11, 'keyname': 'PERSISTENT_IP', 'name': 'Persistent IP'}, {'id': 10, 'keyname': 'ROUND_ROBIN', 'name': 'Round Robin'}, {'id': 6, 'keyname': 'ROUND_ROBIN_INSERT_COOKIE', 'name': 'Round Robin with Inserted Cookie'}, {'id': 3, 'keyname': 'ROUND_ROBIN_PERSISTENT_IP', 'name': 'Round Robin with Persistent IP'}, {'id': 2, 'keyname': 'SHORTEST_RESPONSE', 'name': 'Shortest Response'}, {'id': 8, 'keyname': 'SHORTEST_RESPONSE_INSERT_COOKIE', 'name': 'Shortest Response with Inserted Cookie'}, {'id': 5, 'keyname': 'SHORTEST_RESPONSE_PERSISTENT_IP', 'name': 'Shortest Response with Persistent IP'}] SoftLayer_Network_Application_Delivery_Controller_LoadBalancer_Routing_Type.py000066400000000000000000000005651324365065500402550ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/fixturesgetAllObjects = [{'id': 4, 'keyname': 'DNS', 'name': 'DNS'}, {'id': 5, 'keyname': 'FTP', 'name': 'FTP'}, {'id': 2, 'keyname': 'HTTP', 'name': 'HTTP'}, {'id': 41, 'keyname': 'HTTPS', 'name': 'HTTPS'}, {'id': 3, 'keyname': 'TCP', 'name': 'TCP'}, {'id': 6, 'keyname': 'UDP', 'name': 'UDP'}] SoftLayer_Network_Application_Delivery_Controller_LoadBalancer_Service.py000066400000000000000000000000501324365065500372120ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/fixturesdeleteObject = True toggleStatus = True SoftLayer_Network_Application_Delivery_Controller_LoadBalancer_Service_Group.py000066400000000000000000000000321324365065500403660ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/fixtureskickAllConnections = True SoftLayer_Network_Application_Delivery_Controller_LoadBalancer_VirtualIpAddress.py000066400000000000000000000060531324365065500410500ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/fixturesgetBillingItem = {'id': 21370814} getObject = { 'accountId': 307608, 'connectionLimit': 500, 'connectionLimitUnits': "connections/second", 'dedicatedFlag': False, 'highAvailabilityFlag': False, 'id': 22348, 'ipAddressId': 7303278, 'managedResourceFlag': False, 'sslActiveFlag': False, 'sslEnabledFlag': True, 'virtualServers': [ { 'allocation': 10, 'id': 50718, 'port': 80, "serviceGroups": [ { 'id': 51758, 'routingMethodId': 10, 'routingTypeId': 3, 'services': [ { 'enabled': 1, 'id': 1234, 'healthChecks': [ { 'id': 112112 } ], 'groupReferences': [ { 'serviceGroupId': 51758, 'serviceId': 84986, 'weight': 2 } ], 'ipAddressId': 14288108, 'port': 8080, 'status': "DOWN" } ] } ], "virtualIpAddress": { 'accountId': 307608, 'connectionLimit': 500, 'connectionLimitUnits': "connections/second", 'id': 22348, 'ipAddressId': 7303278, }, 'virtualIpAddressId': 22348 }]} getVirtualServers = [ { "allocation": 10, "id": 50718, "port": 80, "serviceGroups": [ { "id": 51758, "routingMethodId": 10, "routingTypeId": 3, "services": [ { "enabled": 1, "id": 1234, "healthChecks": [ { "id": 112112 } ], "groupReferences": [ { "serviceGroupId": 51758, "serviceId": 84986, "weight": 2 } ], "ipAddressId": 14288108, "port": 8080, "status": "DOWN" } ] } ], "virtualIpAddress": { "accountId": 307608, "connectionLimit": 500, "connectionLimitUnits": "connections/second", "id": 22348, "ipAddressId": 7303278, }, "virtualIpAddressId": 22348 } ] editObject = True SoftLayer_Network_Application_Delivery_Controller_LoadBalancer_VirtualServer.py000066400000000000000000000000241324365065500404300ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/fixturesdeleteObject = True softlayer-python-5.4.2/SoftLayer/fixtures/SoftLayer_Network_Component_Firewall.py000066400000000000000000000063601324365065500305120ustar00rootroot00000000000000createCancelServerTicket = {'id': 1234, 'title': 'Server Cancellation Request'} getObject = { "guestNetworkComponentId": 1705294, "id": 1234, "status": "allow_edit", "billingItem": { "allowCancellationFlag": 1, "associatedBillingItemId": "20952512", "categoryCode": "firewall", "createDate": "2014-03-21T14:07:04-05:00", "cycleStartDate": "2014-03-21T14:07:04-05:00", "description": "10Mbps Hardware Firewall", "id": 21370814, "laborFee": "0", "laborFeeTaxRate": ".066", "lastBillDate": "2014-03-21T14:07:04-05:00", "modifyDate": "2014-03-21T14:07:07-05:00", "nextBillDate": "2014-04-04T00:00:00-05:00", "oneTimeFee": "0", "oneTimeFeeTaxRate": ".066", "orderItemId": 28712824, "parentId": 20952512, "recurringFee": "0", "recurringFeeTaxRate": ".066", "recurringMonths": 1, "serviceProviderId": 1, "setupFee": "0", "setupFeeTaxRate": ".066" }, "guestNetworkComponent": { "createDate": "2014-03-17T13:49:00-05:00", "guestId": 3895386, "id": 1705294, "macAddress": "06:a4:8d:d2:88:34", "maxSpeed": 10, "modifyDate": "2014-03-17T13:49:20-05:00", "name": "eth", "networkId": 1310218, "port": 1, "speed": 10, "status": "ACTIVE", "uuid": "3f1b5e08-a652-fb3b-1baa-8ace70c90fe9", "guest": { "accountId": 307608, "dedicatedAccountHostOnlyFlag": False, "domain": "test.com", "fullyQualifiedDomainName": "test.test.com", "hostname": "firewalltest", "id": 3895386, "maxCpu": 1, "maxCpuUnits": "CORE", "maxMemory": 1024, "modifyDate": "2014-03-21T14:05:51-05:00", "startCpus": 1, "statusId": 1001, "uuid": "29b40ef0-a43a-8cb6-31be-1878cb6853f0", "status": { "keyName": "ACTIVE", "name": "Active" } } } } getRules = [ { 'destinationIpAddress': 'any on server', 'protocol': 'tcp', 'orderValue': 1, 'destinationIpSubnetMask': '255.255.255.255', 'destinationPortRangeStart': 80, 'sourceIpSubnetMask': '0.0.0.0', 'destinationPortRangeEnd': 80, 'version': 4, 'action': 'permit', 'sourceIpAddress': '0.0.0.0' }, { 'destinationIpAddress': 'any on server', 'protocol': 'tcp', 'orderValue': 2, 'destinationIpSubnetMask': '255.255.255.255', 'destinationPortRangeStart': 1, 'sourceIpSubnetMask': '255.255.255.255', 'destinationPortRangeEnd': 65535, 'version': 4, 'action': 'permit', 'sourceIpAddress': '193.212.1.10' }, { 'destinationIpAddress': 'any on server', 'protocol': 'tcp', 'orderValue': 3, 'destinationIpSubnetMask': '255.255.255.255', 'destinationPortRangeStart': 80, 'sourceIpSubnetMask': '0.0.0.0', 'destinationPortRangeEnd': 800, 'version': 4, 'action': 'permit', 'sourceIpAddress': '0.0.0.0' } ] getBillingItem = {"id": 21370814} softlayer-python-5.4.2/SoftLayer/fixtures/SoftLayer_Network_ContentDelivery_Account.py000066400000000000000000000014621324365065500315130ustar00rootroot00000000000000getObject = { "cdnAccountName": "1234a", "providerPortalAccessFlag": False, "createDate": "2012-06-25T14:05:28-07:00", "id": 1234, "legacyCdnFlag": False, "dependantServiceFlag": True, "cdnSolutionName": "ORIGIN_PULL", "statusId": 4, "accountId": 1234, "status": {'name': 'ACTIVE'}, } getOriginPullMappingInformation = [ { "originUrl": "http://ams01.objectstorage.softlayer.net:80", "mediaType": "FLASH", "id": "12345", "isSecureContent": False }, { "originUrl": "http://sng01.objectstorage.softlayer.net:80", "mediaType": "FLASH", "id": "12345", "isSecureContent": False } ] createOriginPullMapping = True deleteOriginPullRule = True loadContent = True purgeContent = True purgeCache = True softlayer-python-5.4.2/SoftLayer/fixtures/SoftLayer_Network_Firewall_Update_Request.py000066400000000000000000000001501324365065500314710ustar00rootroot00000000000000getObject = { "guestNetworkComponentId": 1705294, "id": 1234, } createObject = {} edit = True softlayer-python-5.4.2/SoftLayer/fixtures/SoftLayer_Network_SecurityGroup.py000066400000000000000000000025671324365065500275540ustar00rootroot00000000000000getAllObjects = [ {'id': 100, 'name': 'secgroup1', 'description': 'Securitygroup1'}, {'id': 104, 'name': 'secgroup2'}, {'id': 110} ] getRules = [ {'id': 100, 'direction': 'egress', 'ethertype': 'IPv4'} ] guest_dict = {'id': 5000, 'hostname': 'test', 'primaryBackendIpAddress': '10.3.4.5', 'primaryIpAddress': '169.23.123.43'} getObject = { 'id': 100, 'name': 'secgroup1', 'description': 'Securitygroup1', 'networkComponentBindings': [{'networkComponentId': 1000, 'networkComponent': {'id': 1000, 'port': 0, 'guest': guest_dict}}, {'networkComponentId': 1001, 'networkComponent': {'id': 1001, 'port': 1, 'guest': guest_dict}}], 'rules': getRules } createObject = {'id': 100, 'name': 'secgroup1', 'description': 'Securitygroup1', 'createDate': '2017-05-05T12:44:43-06:00'} editObject = True deleteObject = True addRules = True editRules = True removeRules = True attachNetworkComponents = True detachNetworkComponents = True softlayer-python-5.4.2/SoftLayer/fixtures/SoftLayer_Network_Storage.py000066400000000000000000000153361324365065500263320ustar00rootroot00000000000000STAAS_TEST_VOLUME = { 'accountId': 1234, 'activeTransactions': None, 'activeTransactionCount': 0, 'billingItem': { 'activeChildren': [{ 'categoryCode': 'storage_snapshot_space', 'id': 125, 'cancellationDate': '', }], 'cancellationDate': '', 'categoryCode': 'storage_as_a_service', 'hourlyFlag': None, 'id': 454, 'location': {'id': 449500} }, 'capacityGb': 500, 'hasEncryptionAtRest': 1, 'id': 102, 'iops': 1000, 'lunId': 2, 'osType': {'keyName': 'LINUX'}, 'originalVolumeSize': '500', 'parentVolume': {'snapshotSizeBytes': 1024}, 'provisionedIops': '1000', 'replicationPartnerCount': 0, 'schedules': [{ 'id': 978, 'type': {'keyname': 'SNAPSHOT_WEEKLY'}, }], 'serviceResource': {'datacenter': {'id': 449500, 'name': 'dal05'}}, 'serviceResourceBackendIpAddress': '10.1.2.3', 'snapshotCapacityGb': '10', 'staasVersion': '2', 'storageTierLevel': 'READHEAVY_TIER', 'storageType': {'keyName': 'ENDURANCE_BLOCK_STORAGE'}, 'username': 'duplicatable_volume_username' } getObject = { 'accountId': 1234, 'activeTransactionCount': 1, 'activeTransactions': [{ 'transactionStatus': {'friendlyName': 'This is a buffer time in which the customer may cancel the server'} }], 'allowedHardware': [{ 'allowedHost': { 'credential': {'username': 'joe', 'password': '12345'}, 'name': 'test-server', }, 'domain': 'example.com', 'hostname': 'test-server', 'id': 1234, 'primaryBackendIpAddress': '10.0.0.2', }], 'allowedIpAddresses': [{ 'allowedHost': { 'credential': {'username': 'joe', 'password': '12345'}, 'name': 'test-server', }, 'id': 1234, 'ipAddress': '10.0.0.1', 'note': 'backend ip', }], 'allowedSubnets': [{ 'allowedHost': { 'credential': {'username': 'joe', 'password': '12345'}, 'name': 'test-server', }, 'cidr': '24', 'id': 1234, 'networkIdentifier': '10.0.0.1', 'note': 'backend subnet', }], 'allowedVirtualGuests': [{ 'allowedHost': { 'credential': {'username': 'joe', 'password': '12345'}, 'name': 'test-server', }, 'domain': 'example.com', 'hostname': 'test-server', 'id': 1234, 'primaryBackendIpAddress': '10.0.0.1', }], 'billingItem': { 'activeChildren': [{ 'cancellationDate': '', 'categoryCode': 'storage_snapshot_space', 'id': 123, }], 'cancellationDate': '', 'categoryCode': 'storage_service_enterprise', 'id': 449, 'location': {'id': 449500} }, 'bytesUsed': 0, 'capacityGb': 20, 'createDate': '2015:50:15-04:00', 'fileNetworkMountAddress': '127.0.0.1:/TEST', 'guestId': '', 'hardwareId': '', 'hasEncryptionAtRest': 0, 'hostId': '', 'id': 100, 'iops': 1000, 'lunId': 2, 'nasType': 'ISCSI', 'notes': """{'status': 'available'}""", 'originalSnapshotName': 'test-original-snapshot-name', 'originalVolumeName': 'test-original-volume-name', 'originalVolumeSize': '20', 'osType': {'keyName': 'LINUX'}, 'parentVolume': {'snapshotSizeBytes': 1024}, 'password': '', 'provisionedIops': '1000', 'replicationPartnerCount': 1, 'replicationPartners': [{ 'createDate': '2017:50:15-04:00', 'id': 1784, 'nasType': 'ISCSI_REPLICANT', 'replicationSchedule': {'type': {'keyname': 'REPLICATION_HOURLY'}}, 'serviceResource': {'datacenter': {'name': 'wdc01'}}, 'serviceResourceBackendIpAddress': '10.3.174.79', 'username': 'TEST_REP_1', }, { 'createDate': '2017:50:15-04:00', 'id': 1785, 'nasType': 'ISCSI_REPLICANT', 'replicationSchedule': {'type': {'keyname': 'REPLICATION_DAILY'}}, 'serviceResource': {'datacenter': {'name': 'dal01'}}, 'serviceResourceBackendIpAddress': '10.3.177.84', 'username': 'TEST_REP_2', }], 'replicationStatus': 'Replicant Volume Provisioning has completed.', 'schedules': [ { 'id': 978, 'type': {'keyname': 'SNAPSHOT_WEEKLY'}, 'properties': [ {'type': {'keyname': 'MINUTE'}, 'value': '30'}, ] }, { 'id': 988, 'type': {'keyname': 'REPLICATION_INTERVAL'}, 'properties': [ {'type': {'keyname': 'MINUTE'}, 'value': '-1'}, ] } ], 'serviceProviderId': 1, 'serviceResource': {'datacenter': {'id': 449500, 'name': 'dal05'}}, 'serviceResourceBackendIpAddress': '10.1.2.3', 'serviceResourceName': 'Storage Type 01 Aggregate staaspar0101_pc01', 'snapshotCapacityGb': '10', 'staasVersion': '1', 'storageTierLevel': 'READHEAVY_TIER', 'storageType': {'keyName': 'ENDURANCE_STORAGE'}, 'username': 'username', } getSnapshots = [{ 'id': 470, 'notes': 'unit_testing_note', 'snapshotCreationTimestamp': '2016-07-06T07:41:19-05:00', 'snapshotSizeBytes': '42', }] getReplicationPartners = [{ 'id': 1784, 'accountId': 3000, 'capacityGb': 20, 'username': 'TEST_REP_1', 'serviceResourceBackendIpAddress': '10.3.174.79', 'nasType': 'ISCSI_REPLICANT', 'hostId': None, 'guestId': None, 'hardwareId': None, 'createDate': '2017:50:15-04:00', 'serviceResource': {'datacenter': {'name': 'wdc01'}}, 'replicationSchedule': {'type': {'keyname': 'REPLICATION_HOURLY'}}, }, { 'id': 1785, 'accountId': 3001, 'capacityGb': 20, 'username': 'TEST_REP_2', 'serviceResourceBackendIpAddress': '10.3.177.84', 'nasType': 'ISCSI_REPLICANT', 'hostId': None, 'guestId': None, 'hardwareId': None, 'createDate': '2017:50:15-04:00', 'serviceResource': {'datacenter': {'name': 'dal01'}}, 'replicationSchedule': {'type': {'keyname': 'REPLICATION_DAILY'}}, }] getValidReplicationTargetDatacenterLocations = [{ 'id': 12345, 'longName': 'Dallas 05', 'name': 'dal05' }] listVolumeSchedules = [ { 'id': 978, 'type': {'keyname': 'SNAPSHOT_WEEKLY'}, 'properties': [{'type': {'keyname': 'MINUTE'}, 'value': '30'}] }, { 'id': 988, 'type': {'keyname': 'REPLICATION_INTERVAL'}, 'properties': [{'type': {'keyname': 'MINUTE'}, 'value': '-1'}] } ] deleteObject = True allowAccessFromHostList = True removeAccessFromHostList = True failoverToReplicant = True failbackFromReplicant = True restoreFromSnapshot = True createSnapshot = { 'id': 449 } enableSnapshots = True disableSnapshots = True softlayer-python-5.4.2/SoftLayer/fixtures/SoftLayer_Network_Storage_Allowed_Host.py000066400000000000000000000000351324365065500307640ustar00rootroot00000000000000setCredentialPassword = True softlayer-python-5.4.2/SoftLayer/fixtures/SoftLayer_Network_Subnet.py000066400000000000000000000012131324365065500261530ustar00rootroot00000000000000getObject = { 'id': 1234, 'billingItem': { 'id': 1056 }, 'number': 999, 'networkIdentifier': '1.2.3.4', 'cidr': '26', 'subnetType': 'ADDITIONAL_PRIMARY', 'networkVlan': { 'networkSpace': 'PUBLIC' }, 'gateway': '1.2.3.254', 'broadcastAddress': '1.2.3.255', 'datacenter': { 'name': 'dal10', 'id': 1 }, 'virtualGuests': [ { 'hostname': 'hostname0', 'domain': 'sl.test', 'primaryIpAddress': '1.2.3.10', 'primaryBackendIpAddress': '10.0.1.2' } ], 'hardware': [], 'usableIpAddressCount': 22 } softlayer-python-5.4.2/SoftLayer/fixtures/SoftLayer_Network_Subnet_IpAddress.py000066400000000000000000000002751324365065500301200ustar00rootroot00000000000000getByIpAddress = { 'id': 12345, 'ipAddress': '10.0.1.37', 'isBroadcast': False, 'isGateway': False, 'isNetwork': False, 'isReserved': False, 'subnetId': 5678, } softlayer-python-5.4.2/SoftLayer/fixtures/SoftLayer_Network_Subnet_IpAddress_Global.py000066400000000000000000000001221324365065500313670ustar00rootroot00000000000000route = True unroute = True getObject = {'id': 1234, 'billingItem': {'id': 1234}} softlayer-python-5.4.2/SoftLayer/fixtures/SoftLayer_Network_Subnet_Rwhois_Data.py000066400000000000000000000000221324365065500304340ustar00rootroot00000000000000editObject = True softlayer-python-5.4.2/SoftLayer/fixtures/SoftLayer_Network_Vlan.py000066400000000000000000000000741324365065500256170ustar00rootroot00000000000000getObject = {'primaryRouter': {'datacenter': {'id': 1234}}} softlayer-python-5.4.2/SoftLayer/fixtures/SoftLayer_Network_Vlan_Firewall.py000066400000000000000000000046771324365065500274610ustar00rootroot00000000000000createCancelServerTicket = {'id': 1234, 'title': 'Server Cancellation Request'} getObject = { "administrativeBypassFlag": "", "customerManagedFlag": False, "billingItem": { "id": 21370815 }, "id": 3130, "primaryIpAddress": "192.155.239.146", "networkVlan": { "accountId": 307608, "id": 371028, "primarySubnetId": 536252, "vlanNumber": 1489, "firewallInterfaces": [ { "id": 6254, "name": "inside", "firewallContextAccessControlLists": [ { "direction": "out", "firewallContextInterfaceId": 6257, "id": 3143 } ] }, { "id": 6256, "name": "outside", "firewallContextAccessControlLists": [ { "direction": "out", "firewallContextInterfaceId": 6257, "id": 3143 }, { "direction": "in", "firewallContextInterfaceId": 6256, "id": 3142 } ] } ] } } getRules = [ { 'destinationIpAddress': 'any on server', 'protocol': 'tcp', 'orderValue': 1, 'destinationIpSubnetMask': '255.255.255.255', 'destinationPortRangeStart': 80, 'sourceIpSubnetMask': '0.0.0.0', 'destinationPortRangeEnd': 80, 'version': 4, 'action': 'permit', 'sourceIpAddress': '0.0.0.0' }, { 'destinationIpAddress': 'any on server', 'protocol': 'tcp', 'orderValue': 2, 'destinationIpSubnetMask': '255.255.255.255', 'destinationPortRangeStart': 1, 'sourceIpSubnetMask': '255.255.255.255', 'destinationPortRangeEnd': 65535, 'version': 4, 'action': 'permit', 'sourceIpAddress': '193.212.1.10' }, { 'destinationIpAddress': 'any on server', 'protocol': 'tcp', 'orderValue': 3, 'destinationIpSubnetMask': '255.255.255.255', 'destinationPortRangeStart': 80, 'sourceIpSubnetMask': '0.0.0.0', 'destinationPortRangeEnd': 800, 'version': 4, 'action': 'permit', 'sourceIpAddress': '0.0.0.0' } ] softlayer-python-5.4.2/SoftLayer/fixtures/SoftLayer_Product_Order.py000066400000000000000000000006611324365065500257630ustar00rootroot00000000000000verifyOrder = { 'orderId': 1234, 'orderDate': '2013-08-01 15:23:45', 'prices': [{ 'id': 1, 'laborFee': '2', 'oneTimeFee': '2', 'oneTimeFeeTax': '.1', 'quantity': 1, 'recurringFee': '2', 'recurringFeeTax': '.1', 'hourlyRecurringFee': '2', 'setupFee': '1', 'item': {'id': 1, 'description': 'this is a thing'}, }]} placeOrder = verifyOrder softlayer-python-5.4.2/SoftLayer/fixtures/SoftLayer_Product_Package.py000066400000000000000000001133451324365065500262470ustar00rootroot00000000000000HARDWARE_ITEMS = [ {'attributes': [], 'capacity': '999', 'description': 'Unknown', 'itemCategory': {'categoryCode': 'unknown', 'id': 325}, 'keyName': 'UNKNOWN', 'prices': [{'accountRestrictions': [], 'currentPriceFlag': '', 'hourlyRecurringFee': '0', 'id': 1245172, 'itemId': 935954, 'laborFee': '0', 'onSaleFlag': '', 'oneTimeFee': '0', 'quantity': '', 'recurringFee': '0', 'setupFee': '0', 'sort': 0}]}, {'attributes': [], 'capacity': '64', 'description': '1 IPv6 Address', 'itemCategory': {'categoryCode': 'pri_ipv6_addresses', 'id': 325}, 'keyName': '1_IPV6_ADDRESS', 'prices': [{'accountRestrictions': [], 'currentPriceFlag': '', 'hourlyRecurringFee': '0', 'id': 17129, 'itemId': 4097, 'laborFee': '0', 'onSaleFlag': '', 'oneTimeFee': '0', 'quantity': '', 'recurringFee': '0', 'setupFee': '0', 'sort': 0}]}, {'attributes': [], 'capacity': '10', 'description': '10 Mbps Public & Private Network Uplinks', 'itemCategory': {'categoryCode': 'port_speed', 'id': 26}, 'keyName': '10_MBPS_PUBLIC_PRIVATE_NETWORK_UPLINKS', 'prices': [{'accountRestrictions': [], 'currentPriceFlag': '', 'hourlyRecurringFee': '0', 'id': 272, 'itemId': 186, 'laborFee': '0', 'onSaleFlag': '', 'oneTimeFee': '0', 'quantity': '', 'recurringFee': '0', 'setupFee': '0', 'sort': 5}]}, {'attributes': [], 'capacity': '0', 'description': 'Ubuntu Linux 14.04 LTS Trusty Tahr (64 bit)', 'itemCategory': {'categoryCode': 'os', 'id': 12}, 'keyName': 'OS_UBUNTU_14_04_LTS_TRUSTY_TAHR_64_BIT', 'prices': [{'accountRestrictions': [], 'currentPriceFlag': '', 'hourlyRecurringFee': '0', 'id': 37650, 'itemId': 4702, 'laborFee': '0', 'onSaleFlag': '', 'oneTimeFee': '0', 'quantity': '', 'recurringFee': '0', 'setupFee': '0', 'sort': 9}], 'softwareDescription': {'id': 1362, 'longDescription': 'Ubuntu / 14.04-64', 'referenceCode': 'UBUNTU_14_64'}}, {'attributes': [], 'capacity': '1', 'description': '1 IP Address', 'itemCategory': {'categoryCode': 'pri_ip_addresses', 'id': 13}, 'keyName': '1_IP_ADDRESS', 'prices': [{'accountRestrictions': [], 'currentPriceFlag': '', 'hourlyRecurringFee': '0', 'id': 21, 'itemId': 15, 'laborFee': '0', 'onSaleFlag': '', 'oneTimeFee': '0', 'quantity': '', 'recurringFee': '0', 'setupFee': '0', 'sort': 0}]}, {'attributes': [{'attributeTypeKeyName': 'RECLAIM_BYPASS', 'id': 1014}], 'description': 'Unlimited SSL VPN Users', 'itemCategory': {'categoryCode': 'vpn_management', 'id': 31}, 'keyName': 'SSL_VPN_USERS_1_PPTP_VPN_USER_PER_ACCOUNT', 'prices': [{'accountRestrictions': [], 'currentPriceFlag': '', 'hourlyRecurringFee': '0', 'id': 420, 'itemId': 309, 'laborFee': '0', 'onSaleFlag': '', 'oneTimeFee': '0', 'quantity': '', 'recurringFee': '0', 'setupFee': '0', 'sort': 0}]}, {'attributes': [], 'description': 'Reboot / KVM over IP', 'itemCategory': {'categoryCode': 'remote_management', 'id': 46}, 'keyName': 'REBOOT_KVM_OVER_IP', 'prices': [{'accountRestrictions': [], 'currentPriceFlag': '', 'hourlyRecurringFee': '0', 'id': 906, 'itemId': 504, 'laborFee': '0', 'onSaleFlag': '', 'oneTimeFee': '0', 'quantity': '', 'recurringFee': '0', 'setupFee': '0', 'sort': 0}]}, {'attributes': [], 'capacity': '0', 'description': '0 GB Bandwidth', 'itemCategory': {'categoryCode': 'bandwidth', 'id': 10}, 'keyName': 'BANDWIDTH_0_GB', 'prices': [{'accountRestrictions': [], 'currentPriceFlag': '', 'id': 22505, 'itemId': 4481, 'laborFee': '0', 'onSaleFlag': '', 'oneTimeFee': '0', 'quantity': '', 'recurringFee': '0', 'setupFee': '0', 'sort': 98}]}, {'attributes': [], 'capacity': '0', 'description': '0 GB Bandwidth', 'itemCategory': {'categoryCode': 'bandwidth', 'id': 10}, 'keyName': 'BANDWIDTH_0_GB_2', 'prices': [{'accountRestrictions': [], 'currentPriceFlag': '', 'hourlyRecurringFee': '0', 'id': 1800, 'itemId': 439, 'laborFee': '0', 'onSaleFlag': '', 'oneTimeFee': '0', 'quantity': '', 'setupFee': '0', 'sort': 99}]}] ENTERPRISE_PACKAGE = { 'categories': [ {'categoryCode': 'storage_service_enterprise'} ], 'id': 240, 'name': 'Endurance', 'items': [ { 'capacity': '0', 'itemCategory': {'categoryCode': 'storage_service_enterprise'}, 'keyName': 'CODENAME_PRIME_STORAGE_SERVICE', 'prices': [ { 'categories': [ {'categoryCode': 'storage_service_enterprise'} ], 'id': 45058, 'locationGroupId': '' } ] }, { 'capacity': '0', 'itemCategory': {'categoryCode': 'storage_file'}, 'keyName': 'FILE_STORAGE_2', 'prices': [ { 'categories': [ {'categoryCode': 'storage_file'} ], 'id': 45108, 'locationGroupId': '' } ] }, { 'capacity': '0', 'itemCategory': {'categoryCode': 'storage_block'}, 'keyName': 'BLOCK_STORAGE_2', 'prices': [ { 'categories': [ {'categoryCode': 'storage_block'} ], 'id': 45098, 'locationGroupId': '' } ] }, { 'capacity': '10', 'itemCategory': {'categoryCode': 'performance_storage_space'}, 'keyName': '10_GB_STORAGE_SPACE', 'prices': [ { 'capacityRestrictionMaximum': '200', 'capacityRestrictionMinimum': '200', 'capacityRestrictionType': 'STORAGE_TIER_LEVEL', 'categories': [ {'categoryCode': 'storage_snapshot_space'} ], 'id': 46160, 'locationGroupId': '' }, { 'capacityRestrictionMaximum': '300', 'capacityRestrictionMinimum': '300', 'capacityRestrictionType': 'STORAGE_TIER_LEVEL', 'categories': [ {'categoryCode': 'storage_snapshot_space'} ], 'id': 46170, 'locationGroupId': '' } ] }, { 'capacity': '20', 'itemCategory': {'categoryCode': 'performance_storage_space'}, 'keyName': '20_GB_PERFORMANCE_STORAGE_SPACE', 'prices': [ { 'capacityRestrictionMaximum': '200', 'capacityRestrictionMinimum': '200', 'capacityRestrictionType': 'STORAGE_TIER_LEVEL', 'categories': [ {'categoryCode': 'storage_snapshot_space'} ], 'id': 45860, 'locationGroupId': '' }, { 'capacityRestrictionMaximum': '200', 'capacityRestrictionMinimum': '200', 'capacityRestrictionType': 'STORAGE_TIER_LEVEL', 'categories': [ {'categoryCode': 'performance_storage_replication'} ], 'id': 46659, 'locationGroupId': '' }, { 'capacityRestrictionMaximum': '200', 'capacityRestrictionMinimum': '200', 'capacityRestrictionType': 'STORAGE_TIER_LEVEL', 'categories': [ {'categoryCode': 'performance_storage_space'} ], 'id': 45128, 'locationGroupId': '' } ] }, { 'capacity': '1000', 'itemCategory': {'categoryCode': 'performance_storage_space'}, 'keyName': '1000_GB_PERFORMANCE_STORAGE_SPACE', 'prices': [ { 'capacityRestrictionMaximum': '300', 'capacityRestrictionMinimum': '300', 'capacityRestrictionType': 'STORAGE_TIER_LEVEL', 'categories': [ {'categoryCode': 'performance_storage_replication'} ], 'id': 46789, 'locationGroupId': '' }, { 'capacityRestrictionMaximum': '300', 'capacityRestrictionMinimum': '300', 'capacityRestrictionType': 'STORAGE_TIER_LEVEL', 'categories': [ {'categoryCode': 'performance_storage_space'} ], 'id': 45318, 'locationGroupId': '' } ] }, { 'attributes': [ {'value': '300'} ], 'capacity': '300', 'itemCategory': {'categoryCode': 'storage_tier_level'}, 'keyName': 'WRITEHEAVY_TIER', 'prices': [ { 'categories': [ {'categoryCode': 'storage_tier_level'} ], 'id': 45088, 'locationGroupId': '' } ] }, { 'attributes': [ {'value': '200'} ], 'capacity': '200', 'itemCategory': {'categoryCode': 'storage_tier_level'}, 'keyName': 'READHEAVY_TIER', 'prices': [ { 'categories': [ {'categoryCode': 'storage_tier_level'} ], 'id': 45078, 'locationGroupId': '' } ] } ] } PERFORMANCE_PACKAGE = { 'categories': [ {'categoryCode': 'performance_storage_iscsi'}, {'categoryCode': 'performance_storage_nfs'} ], 'id': 222, 'name': 'Performance', 'items': [ { 'capacity': '0', 'itemCategory': {'categoryCode': 'performance_storage_iscsi'}, 'keyName': 'BLOCK_STORAGE_PERFORMANCE_ISCSI', 'prices': [ { 'categories': [ {'categoryCode': 'performance_storage_iscsi'} ], 'id': 40672, 'locationGroupId': '' } ] }, { 'capacity': '0', 'itemCategory': {'categoryCode': 'performance_storage_nfs'}, 'keyName': 'FILE_STORAGE_PERFORMANCE_NFS', 'prices': [ { 'categories': [ {'categoryCode': 'performance_storage_nfs'} ], 'id': 40662, 'locationGroupId': '' } ] }, { 'capacity': '20', 'itemCategory': {'categoryCode': 'performance_storage_space'}, 'keyName': '20_GB_PERFORMANCE_STORAGE_SPACE', 'prices': [ { 'categories': [ {'categoryCode': 'performance_storage_space'} ], 'id': 40682, 'locationGroupId': '' } ] }, { 'capacity': '1000', 'itemCategory': {'categoryCode': 'performance_storage_space'}, 'keyName': '1000_GB_PERFORMANCE_STORAGE_SPACE', 'prices': [ { 'categories': [ {'categoryCode': 'performance_storage_space'} ], 'id': 40742, 'locationGroupId': '' } ] }, { 'capacity': '800', 'itemCategory': {'categoryCode': 'performance_storage_iops'}, 'keyName': '800_IOPS_4', 'prices': [ { 'capacityRestrictionMaximum': '1000', 'capacityRestrictionMinimum': '100', 'capacityRestrictionType': 'STORAGE_SPACE', 'categories': [ {'categoryCode': 'performance_storage_iops'} ], 'id': 41562, 'locationGroupId': '' } ] }, { 'capacity': '1000', 'itemCategory': {'categoryCode': 'performance_storage_iops'}, 'keyName': '1000_IOPS', 'prices': [ { 'capacityRestrictionMaximum': '20', 'capacityRestrictionMinimum': '20', 'capacityRestrictionType': 'STORAGE_SPACE', 'categories': [ {'categoryCode': 'performance_storage_iops'} ], 'id': 40882, 'locationGroupId': '' } ] } ] } SAAS_PACKAGE = { 'categories': [ {'categoryCode': 'storage_as_a_service'} ], 'id': 759, 'name': 'Storage As A Service (StaaS)', 'items': [ { 'capacity': '0', 'keyName': '', 'prices': [ { 'id': 189433, 'categories': [ {'categoryCode': 'storage_as_a_service'} ], 'locationGroupId': '' } ] }, { 'capacity': '0', 'keyName': '', 'prices': [ { 'categories': [ {'categoryCode': 'storage_block'} ], 'id': 189443, 'locationGroupId': '' } ] }, { 'capacity': '0', 'keyName': '', 'prices': [ { 'categories': [ {'categoryCode': 'storage_file'} ], 'id': 189453, 'locationGroupId': '' } ] }, { 'capacity': '0', 'capacityMaximum': '999', 'capacityMinimum': '500', 'itemCategory': {'categoryCode': 'performance_storage_space'}, 'keyName': '500_999_GBS', 'prices': [ { 'id': 189993, 'categories': [ {'categoryCode': 'performance_storage_space'} ], 'locationGroupId': '' } ] }, { 'capacity': '0', 'capacityMaximum': '1999', 'capacityMinimum': '1000', 'itemCategory': {'categoryCode': 'performance_storage_space'}, 'keyName': '1000_1999_GBS', 'prices': [ { 'id': 190113, 'categories': [ {'categoryCode': 'performance_storage_space'} ], 'locationGroupId': '' } ] }, { 'capacity': '0', 'capacityMaximum': '12000', 'capacityMinimum': '1', 'keyName': 'STORAGE_SPACE_FOR_2_IOPS_PER_GB', 'prices': [ { 'id': 193433, 'categories': [ {'categoryCode': 'performance_storage_space'} ], 'locationGroupId': '' } ] }, { 'capacity': '0', 'capacityMaximum': '12000', 'capacityMinimum': '1', 'keyName': 'STORAGE_SPACE_FOR_4_IOPS_PER_GB', 'prices': [ { 'id': 194763, 'categories': [ {'categoryCode': 'performance_storage_space'} ], 'locationGroupId': '' } ] }, { 'capacity': '0', 'capacityMaximum': '10000', 'capacityMinimum': '100', 'keyName': '', 'itemCategory': {'categoryCode': 'performance_storage_iops'}, 'prices': [ { 'capacityRestrictionMaximum': '999', 'capacityRestrictionMinimum': '500', 'capacityRestrictionType': 'STORAGE_SPACE', 'categories': [ {'categoryCode': 'performance_storage_iops'} ], 'id': 190053, 'locationGroupId': '' } ] }, { 'capacity': '0', 'capacityMaximum': '20000', 'capacityMinimum': '100', 'keyName': '', 'itemCategory': {'categoryCode': 'performance_storage_iops'}, 'prices': [ { 'capacityRestrictionMaximum': '1999', 'capacityRestrictionMinimum': '1000', 'capacityRestrictionType': 'STORAGE_SPACE', 'categories': [ {'categoryCode': 'performance_storage_iops'} ], 'id': 190173, 'locationGroupId': '' } ] }, { 'capacity': '200', 'itemCategory': {'categoryCode': 'storage_tier_level'}, 'keyName': '', 'prices': [ { 'id': 193373, 'categories': [ {'categoryCode': 'storage_tier_level'} ], 'locationGroupId': '' } ] }, { 'capacity': '300', 'itemCategory': {'categoryCode': 'storage_tier_level'}, 'keyName': '', 'prices': [ { 'id': 194703, 'categories': [ {'categoryCode': 'storage_tier_level'} ], 'locationGroupId': '' } ] }, { 'capacity': '10', 'keyName': '', 'prices': [ { 'capacityRestrictionMaximum': '48000', 'capacityRestrictionMinimum': '100', 'capacityRestrictionType': 'IOPS', 'categories': [ {'categoryCode': 'storage_snapshot_space'} ], 'id': 191193, 'locationGroupId': '' }, { 'capacityRestrictionMaximum': '200', 'capacityRestrictionMinimum': '200', 'capacityRestrictionType': 'STORAGE_TIER_LEVEL', 'categories': [ {'categoryCode': 'storage_snapshot_space'} ], 'id': 193613, 'locationGroupId': '' }, { 'capacityRestrictionMaximum': '300', 'capacityRestrictionMinimum': '300', 'capacityRestrictionType': 'STORAGE_TIER_LEVEL', 'categories': [ {'categoryCode': 'storage_snapshot_space'} ], 'id': 194943, 'locationGroupId': ''}] }, { 'capacity': '20', 'keyName': '', 'prices': [ { 'capacityRestrictionMaximum': '200', 'capacityRestrictionMinimum': '200', 'capacityRestrictionType': 'STORAGE_TIER_LEVEL', 'categories': [ {'categoryCode': 'storage_snapshot_space'} ], 'id': 193853, 'locationGroupId': '' } ] }, { 'capacity': '0', 'itemCategory': { 'categoryCode': 'performance_storage_replication' }, 'keyName': 'REPLICATION_FOR_IOPSBASED_PERFORMANCE', 'prices': [ { 'capacityRestrictionMaximum': '48000', 'capacityRestrictionMinimum': '1', 'capacityRestrictionType': 'IOPS', 'categories': [ {'categoryCode': 'performance_storage_replication'} ], 'id': 192033, 'locationGroupId': '' } ] }, { 'capacity': '0', 'itemCategory': { 'categoryCode': 'performance_storage_replication' }, 'keyName': 'REPLICATION_FOR_TIERBASED_PERFORMANCE', 'prices': [ { 'capacityRestrictionMaximum': '200', 'capacityRestrictionMinimum': '200', 'capacityRestrictionType': 'STORAGE_TIER_LEVEL', 'categories': [ {'categoryCode': 'performance_storage_replication'} ], 'id': 194693, 'locationGroupId': '' } ] } ] } getAllObjects = [{ 'activePresets': [{ 'description': 'Single Xeon 1270, 8GB Ram, 2x1TB SATA disks, Non-RAID', 'id': 64, 'isActive': '1', 'keyName': 'S1270_8GB_2X1TBSATA_NORAID', 'name': 'S1270 8GB 2X1TBSATA NORAID', 'packageId': 200 }], 'description': 'Bare Metal Server', 'firstOrderStepId': 1, 'id': 200, 'isActive': 1, 'items': HARDWARE_ITEMS, 'name': 'Bare Metal Server', 'regions': [{'description': 'WDC01 - Washington, DC - East Coast U.S.', 'keyname': 'WASHINGTON_DC', 'location': {'location': {'id': 37473, 'longName': 'Washington 1', 'name': 'wdc01'}}, 'sortOrder': 10}], 'subDescription': 'Bare Metal Server', 'unitSize': 1, }] getItems = [ { 'id': 1234, 'capacity': '1000', 'description': 'Public & Private Networks', 'itemCategory': {'categoryCode': 'Uplink Port Speeds'}, 'prices': [{'id': 1122, 'categories': [{'id': 26, 'name': 'Uplink Port Speeds', 'categoryCode': 'port_speed'}]}], }, { 'id': 2233, 'capacity': '1000', 'description': 'Public & Private Networks', 'itemCategory': {'categoryCode': 'Uplink Port Speeds'}, 'prices': [{'id': 4477, 'categories': [{'id': 26, 'name': 'Uplink Port Speeds', 'categoryCode': 'port_speed'}]}], }, { 'id': 1239, 'capacity': '2', 'description': 'RAM', 'itemCategory': {'categoryCode': 'RAM'}, 'prices': [{'id': 1133, 'categories': [{'id': 3, 'name': 'RAM', 'categoryCode': 'ram'}]}], }, { 'id': 1240, 'capacity': '4', 'units': 'PRIVATE_CORE', 'description': 'Computing Instance (Dedicated)', 'itemCategory': {'categoryCode': 'Computing Instance'}, 'prices': [{'id': 1007, 'categories': [{'id': 80, 'name': 'Computing Instance', 'categoryCode': 'guest_core'}]}], }, { 'id': 1250, 'capacity': '4', 'units': 'CORE', 'description': 'Computing Instance', 'itemCategory': {'categoryCode': 'Computing Instance'}, 'prices': [{'id': 1144, 'locationGroupId': None, 'categories': [{'id': 80, 'name': 'Computing Instance', 'categoryCode': 'guest_core'}]}], }, { 'id': 112233, 'capacity': '55', 'units': 'CORE', 'description': 'Computing Instance', 'itemCategory': {'categoryCode': 'Computing Instance'}, 'prices': [{'id': 332211, 'locationGroupId': 1, 'categories': [{'id': 80, 'name': 'Computing Instance', 'categoryCode': 'guest_core'}]}], }, { 'id': 4439, 'capacity': '1', 'description': '1 GB iSCSI Storage', 'itemCategory': {'categoryCode': 'iscsi'}, 'prices': [{'id': 2222}], }, { 'id': 1121, 'capacity': '20', 'description': '20 GB iSCSI snapshot', 'itemCategory': {'categoryCode': 'iscsi_snapshot_space'}, 'prices': [{'id': 2014}], }, { 'id': 4440, 'capacity': '4', 'description': '4 Portable Public IP Addresses', 'itemCategory': {'categoryCode': 'sov_sec_ip_addresses_pub'}, 'prices': [{'id': 4444}], }, { 'id': 8880, 'capacity': '8', 'description': '8 Portable Public IP Addresses', 'itemCategory': {'categoryCode': 'sov_sec_ip_addresses_pub'}, 'prices': [{'id': 8888}], }, { 'id': 44400, 'capacity': '4', 'description': '4 Portable Private IP Addresses', 'itemCategory': {'categoryCode': 'sov_sec_ip_addresses_priv'}, 'prices': [{'id': 44441}], }, { 'id': 88800, 'capacity': '8', 'description': '8 Portable Private IP Addresses', 'itemCategory': {'categoryCode': 'sov_sec_ip_addresses_priv'}, 'prices': [{'id': 88881}], }, { 'id': 10, 'capacity': '0', 'description': 'Global IPv4', 'itemCategory': {'categoryCode': 'global_ipv4'}, 'prices': [{'id': 11}], }, { 'id': 66464, 'capacity': '64', 'description': '/64 Block Portable Public IPv6 Addresses', 'itemCategory': {'categoryCode': 'static_ipv6_addresses'}, 'prices': [{'id': 664641}], }, { 'id': 610, 'capacity': '0', 'description': 'Global IPv6', 'itemCategory': {'categoryCode': 'global_ipv6'}, 'prices': [{'id': 611}], }] getItemPrices = [ { 'currentPriceFlag': '', 'id': 2152, 'item': { 'capacity': '1', 'description': '1 GB iSCSI SAN Storage', 'id': 1111, 'softwareDescriptionId': '', 'units': 'GB', 'upgradeItemId': 547}, 'itemId': 1111, 'laborFee': '0', 'onSaleFlag': '', 'oneTimeFee': '0', 'packageReferences': [{'id': 46626, 'itemPriceId': 2152, 'packageId': 0}], 'quantity': '', 'recurringFee': '.35', 'setupFee': '0', 'sort': 0 }, { 'currentPriceFlag': '', 'id': 22501, 'item': {'capacity': '1', 'description': '1 GB iSCSI SAN Storage', 'id': 1111, 'softwareDescriptionId': '', 'units': 'GB', 'upgradeItemId': 547}, 'itemId': 1111, 'laborFee': '0', 'onSaleFlag': '', 'oneTimeFee': '0', 'packageReferences': [{ 'id': 252983, 'itemPriceId': 22501, 'packageId': 0 }], 'quantity': '', 'recurringFee': '0', 'setupFee': '0', 'sort': 0 }, { 'currentPriceFlag': '', 'id': 22441, 'item': { 'capacity': '1', 'description': '1 GB iSCSI SAN Storage', 'id': 1111, 'softwareDescriptionId': '', 'units': 'GB', 'upgradeItemId': 547 }, 'itemId': 1111, 'laborFee': '0', 'onSaleFlag': '', 'oneTimeFee': '0', 'packageReferences': [{'id': 250326, 'itemPriceId': 22441, 'packageId': 0}], 'quantity': '', 'recurringFee': '15', 'setupFee': '0', 'sort': 0 }] verifyOrderDH = { 'preTaxSetup': '0', 'storageGroups': [], 'postTaxRecurring': '3.164', 'billingOrderItemId': '', 'presetId': '', 'hardware': [ { 'domain': 't.com', 'hostname': 't', 'bareMetalInstanceFlag': '', 'hardwareStatusId': '', 'primaryBackendNetworkComponent': { 'router': { 'id': 51218 }, 'networkVlanId': '' }, 'accountId': '' } ], 'prices': [ { 'itemId': 10195, 'setupFee': '0', 'recurringFee': '0', 'hourlyRecurringFee': '3.164', 'oneTimeFee': '0', 'id': 200269, 'item': { 'thirdPartyPolicyAssignments': [], 'capacity': '56', 'description': '56 Cores X 242 RAM X 1.2 TB', 'bundle': [ { 'category': { 'categoryCode': 'dedicated_host_ram', 'id': 850, 'name': 'Dedicated Host RAM' }, 'itemPriceId': 200301, 'itemPrice': { 'itemId': 10199, 'setupFee': '0', 'recurringFee': '0', 'hourlyRecurringFee': '0', 'oneTimeFee': '0', 'id': 200301, 'laborFee': '0' }, 'bundleItemId': 10195, 'bundleItem': { 'units': 'CORE', 'keyName': '56_CORES_X_242_RAM_X_1_4_TB', 'capacity': '56', 'description': '56 Cores X 242 RAM X 1.2 TB', 'id': 10195 }, 'id': 41763 }, { 'category': { 'categoryCode': 'dedicated_host_disk', 'id': 851, 'name': 'Dedicated Host Disk' }, 'itemPriceId': 200299, 'itemPrice': { 'itemId': 10197, 'setupFee': '0', 'recurringFee': '0', 'hourlyRecurringFee': '0', 'oneTimeFee': '0', 'id': 200299, 'laborFee': '0' }, 'bundleItemId': 10195, 'bundleItem': { 'units': 'CORE', 'keyName': '56_CORES_X_242_RAM_X_1_4_TB', 'capacity': '56', 'description': '56 Cores X 242 RAM X 1.2 TB', 'id': 10195 }, 'id': 41761 } ], 'keyName': '56_CORES_X_242_RAM_X_1_4_TB', 'units': 'CORE', 'id': 10195 }, 'laborFee': '0', 'categories': [ { 'categoryCode': 'dedicated_virtual_hosts', 'id': 848, 'name': 'Dedicated Host' } ] } ], 'sendQuoteEmailFlag': '', 'packageId': 813, 'useHourlyPricing': True, 'preTaxRecurringMonthly': '0', 'message': '', 'preTaxRecurring': '3.164', 'primaryDiskPartitionId': '', 'locationObject': { 'id': 138124, 'name': 'dal05', 'longName': 'Dallas 5' }, 'taxCompletedFlag': False, 'isManagedOrder': '', 'imageTemplateId': '', 'postTaxRecurringMonthly': '0', 'resourceGroupTemplateId': '', 'postTaxSetup': '0', 'sshKeys': [], 'location': '138124', 'stepId': '', 'proratedInitialCharge': '0', 'totalRecurringTax': '0', 'paymentType': '', 'resourceGroupId': '', 'sourceVirtualGuestId': '', 'bigDataOrderFlag': False, 'extendedHardwareTesting': '', 'preTaxRecurringHourly': '3.164', 'postTaxRecurringHourly': '3.164', 'currencyShortName': 'USD', 'containerSplHash': '000000003699c54000007f38ef8b0102', 'proratedOrderTotal': '0', 'serverCoreCount': '', 'privateCloudOrderFlag': False, 'totalSetupTax': '0', 'quantity': 1 } getAllObjectsDH = [{ "subDescription": "Dedicated Host", "name": "Dedicated Host", "items": [{ "capacity": "56", "description": "56 Cores X 242 RAM X 1.2 TB", "bundleItems": [ { "capacity": "1200", "categories": [{ "categoryCode": "dedicated_host_disk" }] }, { "capacity": "242", "categories": [{ "categoryCode": "dedicated_host_ram" }] } ], "prices": [ { "itemId": 10195, "setupFee": "0", "recurringFee": "2099", "tierMinimumThreshold": "", "hourlyRecurringFee": "3.164", "oneTimeFee": "0", "currentPriceFlag": "", "id": 200269, "sort": 0, "onSaleFlag": "", "laborFee": "0", "locationGroupId": "", "quantity": "" }, { "itemId": 10195, "setupFee": "0", "recurringFee": "2161.97", "tierMinimumThreshold": "", "hourlyRecurringFee": "3.258", "oneTimeFee": "0", "currentPriceFlag": "", "id": 200271, "sort": 0, "onSaleFlag": "", "laborFee": "0", "locationGroupId": 503, "quantity": "" } ], "keyName": "56_CORES_X_242_RAM_X_1_4_TB", "id": 10195, "itemCategory": { "categoryCode": "dedicated_virtual_hosts" } }], "keyName": "DEDICATED_HOST", "unitSize": "", "regions": [{ "location": { "locationPackageDetails": [{ "isAvailable": 1, "locationId": 138124, "packageId": 813 }], "location": { "statusId": 2, "priceGroups": [{ "locationGroupTypeId": 82, "description": "CDN - North America - Akamai", "locationGroupType": { "name": "PRICING" }, "securityLevelId": "", "id": 1463, "name": "NORTH-AMERICA-AKAMAI" }], "id": 138124, "name": "dal05", "longName": "Dallas 5" } }, "keyname": "DALLAS05", "description": "DAL05 - Dallas", "sortOrder": 12 }], "firstOrderStepId": "", "id": 813, "isActive": 1, "description": "Dedicated Host" }] getRegions = [{ "description": "WDC07 - Washington, DC", "keyname": "WASHINGTON07", "locations": [{ "location": { "euCompliantFlag": False, "id": 2017603, "longName": "Washington 7", "name": "wdc07", "statusId": 2}, "locationPackageDetails": [{ "isAvailable": 1, "locationId": 2017603, "packageId": 46 }] }] }] softlayer-python-5.4.2/SoftLayer/fixtures/SoftLayer_Resource_Metadata.py000066400000000000000000000004221324365065500265720ustar00rootroot00000000000000getFrontendMacAddresses = ['06-00-00-00-00-00'] getBackendMacAddresses = ['07-00-00-00-00-00'] getFrontendMacAddresses = ['06-00-00-00-00-00'] getRouter = 'brc01' getVlans = [10, 124] getVlanIds = [8384, 12446] getDatacenter = 'dal01' getUserMetadata = 'User-supplied data' softlayer-python-5.4.2/SoftLayer/fixtures/SoftLayer_Security_Certificate.py000066400000000000000000000001071324365065500273140ustar00rootroot00000000000000getObject = {} createObject = {} editObject = True deleteObject = True softlayer-python-5.4.2/SoftLayer/fixtures/SoftLayer_Security_Ssh_Key.py000066400000000000000000000004001324365065500264330ustar00rootroot00000000000000deleteObject = True editObject = True getObject = {'id': 1234, 'fingerprint': 'aa:bb:cc:dd', 'label': 'label', 'notes': 'notes', 'key': 'ssh-rsa AAAAB3N...pa67 user@example.com'} createObject = getObject softlayer-python-5.4.2/SoftLayer/fixtures/SoftLayer_Ticket.py000066400000000000000000000030501324365065500244260ustar00rootroot00000000000000createCancelServerTicket = {'id': 1234, 'title': 'Server Cancellation Request'} getObject = { "accountId": 1234, "assignedUserId": 12345, "createDate": "2013-08-01T14:14:04-07:00", "id": 100, "lastEditDate": "2013-08-01T14:16:47-07:00", "lastEditType": "AUTO", "modifyDate": "2013-08-01T14:16:47-07:00", "status": { "id": 1002, "name": "Closed" }, "statusId": 1002, "title": "Cloud Instance Cancellation - 08/01/13", 'updateCount': 3, 'updates': [ {'entry': 'a bot says something'}, {'entry': 'user says something', 'editor': {'firstName': 'John', 'lastName': 'Smith'}}, {'entry': 'employee says something', 'editor': {'displayName': 'emp1'}}, ] } createStandardTicket = { "assignedUserId": 12345, "id": 100, "contents": "body", "subjectId": 1004, "title": "Cloud Instance Cancellation - 08/01/13" } edit = True addUpdate = {} addAttachedHardware = { "id": 123, "createDate": "2013-08-01T14:14:04-07:00", "hardwareId": 1, "ticketId": 100 } addAttachedVirtualGuest = { "id": 123, "createDate": "2013-08-01T14:14:04-07:00", "virtualGuestId": 1, "ticketId": 100 } addAttachedFile = { "id": 123, "fileName": "a_file_name", "fileSize": "100", "ticketId": 100, "updateId": 200, "createDate": "2013-08-01T14:14:04-07:00", "modifyDate": "2013-08-01T14:14:04-07:00", "uploaderType": "USER", "uploaderId": "300" } removeAttachedHardware = True removeAttachedVirtualGuest = True softlayer-python-5.4.2/SoftLayer/fixtures/SoftLayer_Ticket_Subject.py000066400000000000000000000005511324365065500261100ustar00rootroot00000000000000getAllObjects = [ { "id": 1001, "name": "Accounting Request" }, { "id": 1002, "name": "Sales Request" }, { "id": 1003, "name": "Reboots and Console Access" }, { "id": 1004, "name": "DNS Request" }, { "id": 1005, "name": "Hardware Issue" } ] softlayer-python-5.4.2/SoftLayer/fixtures/SoftLayer_Virtual_DedicatedHost.py000066400000000000000000000042051324365065500274200ustar00rootroot00000000000000getObject = { 'id': 37401, 'memoryCapacity': 242, 'modifyDate': '', 'name': 'test-dedicated', 'diskCapacity': 1200, 'createDate': '2017-10-16T12:50:23-05:00', 'cpuCount': 56, 'accountId': 1199911 } getAvailableRouters = [ {'hostname': 'bcr01a.dal05', 'id': 51218}, {'hostname': 'bcr02a.dal05', 'id': 83361}, {'hostname': 'bcr03a.dal05', 'id': 122762}, {'hostname': 'bcr04a.dal05', 'id': 147566} ] getObjectById = { 'datacenter': { 'id': 138124, 'name': 'dal05', 'longName': 'Dallas 5' }, 'memoryCapacity': 242, 'modifyDate': '2017-11-06T11:38:20-06:00', 'name': 'khnguyendh', 'diskCapacity': 1200, 'backendRouter': { 'domain': 'softlayer.com', 'hostname': 'bcr01a.dal05', 'id': 51218 }, 'guestCount': 1, 'cpuCount': 56, 'guests': [{ 'domain': 'Softlayer.com', 'hostname': 'khnguyenDHI', 'id': 43546081, 'uuid': '806a56ec-0383-4c2e-e6a9-7dc89c4b29a2' }], 'billingItem': { 'nextInvoiceTotalRecurringAmount': 1515.556, 'orderItem': { 'id': 263060473, 'order': { 'status': 'APPROVED', 'privateCloudOrderFlag': False, 'modifyDate': '2017-11-02T11:42:50-07:00', 'orderQuoteId': '', 'userRecordId': 6908745, 'createDate': '2017-11-02T11:40:56-07:00', 'impersonatingUserRecordId': '', 'orderTypeId': 7, 'presaleEventId': '', 'userRecord': { 'username': '232298_khuong' }, 'id': 20093269, 'accountId': 232298 } }, 'id': 235379377, 'children': [ { 'nextInvoiceTotalRecurringAmount': 0.0, 'categoryCode': 'dedicated_host_ram' }, { 'nextInvoiceTotalRecurringAmount': 0.0, 'categoryCode': 'dedicated_host_disk' } ] }, 'id': 44701, 'createDate': '2017-11-02T11:40:56-07:00' } softlayer-python-5.4.2/SoftLayer/fixtures/SoftLayer_Virtual_Guest.py000066400000000000000000000355361324365065500260160ustar00rootroot00000000000000getObject = { 'id': 100, 'hostname': 'vs-test1', 'domain': 'test.sftlyr.ws', 'fullyQualifiedDomainName': 'vs-test1.test.sftlyr.ws', 'status': {'keyName': 'ACTIVE', 'name': 'Active'}, 'billingItem': { 'id': 6327, 'nextInvoiceTotalRecurringAmount': 1.54, 'children': [ {'nextInvoiceTotalRecurringAmount': 1}, {'nextInvoiceTotalRecurringAmount': 1}, {'nextInvoiceTotalRecurringAmount': 1}, {'nextInvoiceTotalRecurringAmount': 1}, {'nextInvoiceTotalRecurringAmount': 1}, ], 'orderItem': { 'order': { 'userRecord': { 'username': 'chechu', } } } }, 'datacenter': {'id': 50, 'name': 'TEST00', 'description': 'Test Data Center'}, 'powerState': {'keyName': 'RUNNING', 'name': 'Running'}, 'maxCpu': 2, 'maxMemory': 1024, 'primaryIpAddress': '172.16.240.2', 'globalIdentifier': '1a2b3c-1701', 'primaryBackendIpAddress': '10.45.19.37', 'primaryNetworkComponent': {'speed': 10, 'maxSpeed': 100}, 'hourlyBillingFlag': False, 'createDate': '2013-08-01 15:23:45', 'blockDevices': [{'device': 0, 'mountType': 'Disk', 'uuid': 1}, {'device': 1, 'mountType': 'Disk', 'diskImage': {'type': {'keyName': 'SWAP'}}}, {'device': 2, 'mountType': 'CD'}, {'device': 3, 'mountType': 'Disk', 'uuid': 3}, {'device': 4, 'mountType': 'Disk', 'uuid': 4, 'diskImage': {'metadataFlag': True}}], 'notes': 'notes', 'networkVlans': [{'networkSpace': 'PUBLIC', 'vlanNumber': 23, 'id': 1}], 'dedicatedHost': {'id': 37401}, 'operatingSystem': { 'passwords': [{'username': 'user', 'password': 'pass'}], 'softwareLicense': { 'softwareDescription': {'version': '12.04-64 Minimal for VSI', 'name': 'Ubuntu'}} }, 'softwareComponents': [{ 'passwords': [{'username': 'user', 'password': 'pass'}], 'softwareLicense': { 'softwareDescription': {'name': 'Ubuntu'}} }], 'tagReferences': [{'tag': {'name': 'production'}}], } getCreateObjectOptions = { 'flavors': [ { 'flavor': { 'keyName': 'B1_1X2X25' }, 'template': { 'supplementalCreateObjectOptions': { 'flavorKeyName': 'B1_1X2X25' } } }, { 'flavor': { 'keyName': 'B1_1X2X100' }, 'template': { 'supplementalCreateObjectOptions': { 'flavorKeyName': 'B1_1X2X100' } } }, { 'flavor': { 'keyName': 'BL1_1X2X100' }, 'template': { 'supplementalCreateObjectOptions': { 'flavorKeyName': 'BL1_1X2X100' } } }, { 'flavor': { 'keyName': 'BL2_1X2X100' }, 'template': { 'supplementalCreateObjectOptions': { 'flavorKeyName': 'BL2_1X2X100' } } }, { 'flavor': { 'keyName': 'C1_1X2X25' }, 'template': { 'supplementalCreateObjectOptions': { 'flavorKeyName': 'C1_1X2X25' } } }, { 'flavor': { 'keyName': 'M1_1X2X100' }, 'template': { 'supplementalCreateObjectOptions': { 'flavorKeyName': 'M1_1X2X100' } } }, { 'flavor': { 'keyName': 'AC1_1X2X100' }, 'template': { 'supplementalCreateObjectOptions': { 'flavorKeyName': 'AC1_1X2X100' } } }, { 'flavor': { 'keyName': 'ACL1_1X2X100' }, 'template': { 'supplementalCreateObjectOptions': { 'flavorKeyName': 'ACL1_1X2X100' } } }, ], 'processors': [ { 'itemPrice': { 'item': {'description': '1 x 2.0 GHz Core'}, 'hourlyRecurringFee': '.07', 'recurringFee': '29' }, 'template': {'startCpus': 1} }, { 'itemPrice': { 'item': {'description': '2 x 2.0 GHz Cores'}, 'hourlyRecurringFee': '.14', 'recurringFee': '78' }, 'template': {'startCpus': 2} }, { 'itemPrice': { 'item': {'description': '3 x 2.0 GHz Cores'}, 'hourlyRecurringFee': '.205', 'recurringFee': '123.5' }, 'template': {'startCpus': 3} }, { 'itemPrice': { 'item': {'description': '4 x 2.0 GHz Cores'}, 'hourlyRecurringFee': '.265', 'recurringFee': '165.5' }, 'template': {'startCpus': 4} }, { 'itemPrice': { 'hourlyRecurringFee': '.209', 'recurringFee': '139', 'dedicatedHostInstanceFlag': False, 'item': { 'description': '1 x 2.0 GHz Cores (Dedicated)' } }, 'template': { 'dedicatedAccountHostOnlyFlag': True, 'startCpus': 1 } }, { 'itemPrice': { 'hourlyRecurringFee': '0', 'recurringFee': '0', 'dedicatedHostInstanceFlag': True, 'item': { 'description': '56 x 2.0 GHz Cores (Dedicated Host)' } }, 'template': { 'startCpus': 56, 'dedicatedHost': { 'id': None } } }, { 'itemPrice': { 'hourlyRecurringFee': '0', 'recurringFee': '0', 'dedicatedHostInstanceFlag': True, 'item': { 'description': '4 x 2.0 GHz Cores (Dedicated Host)' } }, 'template': { 'startCpus': 4, 'dedicatedHost': { 'id': None } } }, ], 'memory': [ { 'itemPrice': { 'item': {'description': '1 GB'}, 'hourlyRecurringFee': '.03', 'recurringFee': '21' }, 'template': {'maxMemory': 1024} }, { 'itemPrice': { 'item': {'description': '2 GB'}, 'hourlyRecurringFee': '.06', 'recurringFee': '42' }, 'template': {'maxMemory': 2048} }, { 'itemPrice': { 'item': {'description': '3 GB'}, 'hourlyRecurringFee': '.085', 'recurringFee': '59.5'}, 'template': {'maxMemory': 3072} }, { 'itemPrice': { 'item': {'description': '4 GB'}, 'hourlyRecurringFee': '.11', 'recurringFee': '77' }, 'template': {'maxMemory': 4096} }, { 'itemPrice': { 'hourlyRecurringFee': '0', 'recurringFee': '0', 'dedicatedHostInstanceFlag': True, 'item': { 'description': '64 GB (Dedicated Host)' } }, 'template': { 'maxMemory': 65536 } }, { 'itemPrice': { 'hourlyRecurringFee': '0', 'recurringFee': '0', 'dedicatedHostInstanceFlag': True, 'item': { 'description': '8 GB (Dedicated Host)' } }, 'template': { 'maxMemory': 8192 } }, ], 'blockDevices': [ { 'itemPrice': { 'item': {'description': '25 GB (LOCAL)'}, 'hourlyRecurringFee': '0', 'recurringFee': '0'}, 'template': { 'blockDevices': [ {'device': '0', 'diskImage': {'capacity': 25}} ], 'localDiskFlag': True } }, { 'itemPrice': { 'item': {'description': '100 GB (LOCAL)'}, 'hourlyRecurringFee': '.01', 'recurringFee': '7' }, 'template': { 'blockDevices': [ {'device': '0', 'diskImage': {'capacity': 100}} ], 'localDiskFlag': True } }, ], 'operatingSystems': [ { 'itemPrice': { 'item': { 'description': 'CentOS 6.0 - Minimal Install (64 bit)' }, 'hourlyRecurringFee': '0', 'recurringFee': '0' }, 'template': { 'operatingSystemReferenceCode': 'CENTOS_6_64' } }, { 'itemPrice': { 'item': { 'description': 'Debian GNU/Linux 7.0 Wheezy/Stable -' ' Minimal Install (64 bit)' }, 'hourlyRecurringFee': '0', 'recurringFee': '0' }, 'template': { 'operatingSystemReferenceCode': 'DEBIAN_7_64' } }, { 'itemPrice': { 'item': { 'description': 'Ubuntu Linux 12.04 LTS Precise' ' Pangolin - Minimal Install (64 bit)' }, 'hourlyRecurringFee': '0', 'recurringFee': '0' }, 'template': { 'operatingSystemReferenceCode': 'UBUNTU_12_64' } }, ], 'networkComponents': [ { 'itemPrice': { 'item': { 'description': '10 Mbps Public & Private Networks' }, 'hourlyRecurringFee': '0', 'recurringFee': '0'}, 'template': { 'networkComponents': [{'maxSpeed': 10}] } }, { 'itemPrice': { 'item': {'description': '100 Mbps Private Network'}, 'hourlyRecurringFee': '0', 'recurringFee': '0'}, 'template': { 'networkComponents': [{'maxSpeed': 100}] } }, { 'itemPrice': { 'item': {'description': '1 Gbps Private Network'}, 'hourlyRecurringFee': '.02', 'recurringFee': '10' }, 'template': { 'networkComponents': [{'maxSpeed': 1000}] } }, { 'itemPrice': { 'hourlyRecurringFee': '0', 'recurringFee': '0', 'dedicatedHostInstanceFlag': True, 'item': { 'description': '1 Gbps Public & Private Network Uplinks (Dedicated Host)' } }, 'template': { 'networkComponents': [ { 'maxSpeed': 1000 } ], 'privateNetworkOnlyFlag': False } }, ], 'datacenters': [ {'template': {'datacenter': {'name': 'ams01'}}}, {'template': {'datacenter': {'name': 'dal05'}}}, ], } getReverseDomainRecords = [{ 'networkAddress': '12.34.56.78', 'name': '12.34.56.78.in-addr.arpa', 'resourceRecords': [{'data': 'test.softlayer.com.', 'id': 987654}], 'updateDate': '2013-09-11T14:36:57-07:00', 'serial': 1234665663, 'id': 123456, }] editObject = True deleteObject = True setPrivateNetworkInterfaceSpeed = True setPublicNetworkInterfaceSpeed = True createObject = getObject createObjects = [getObject] generateOrderTemplate = {} setUserMetadata = ['meta'] reloadOperatingSystem = 'OK' setTags = True createArchiveTransaction = {} executeRescueLayer = True getUpgradeItemPrices = [ { 'id': 1007, 'categories': [{'id': 80, 'name': 'Computing Instance', 'categoryCode': 'guest_core'}], 'item': { 'capacity': '4', 'units': 'PRIVATE_CORE', 'description': 'Computing Instance (Dedicated)', } }, { 'id': 1144, 'locationGroupId': None, 'categories': [{'id': 80, 'name': 'Computing Instance', 'categoryCode': 'guest_core'}], 'item': { 'capacity': '4', 'units': 'CORE', 'description': 'Computing Instance', } }, { 'id': 332211, 'locationGroupId': 1, 'categories': [{'id': 80, 'name': 'Computing Instance', 'categoryCode': 'guest_core'}], 'item': { 'capacity': '4', 'units': 'CORE', 'description': 'Computing Instance', } }, { 'id': 1122, 'categories': [{'id': 26, 'name': 'Uplink Port Speeds', 'categoryCode': 'port_speed'}], 'item': { 'capacity': '1000', 'description': 'Public & Private Networks', } }, { 'id': 1144, 'categories': [{'id': 26, 'name': 'Uplink Port Speeds', 'categoryCode': 'port_speed'}], 'item': { 'capacity': '1000', 'description': 'Private Networks', } }, { 'id': 1133, 'categories': [{'id': 3, 'name': 'RAM', 'categoryCode': 'ram'}], 'item': { 'capacity': '2', 'description': 'RAM', } }, ] DEDICATED_GET_UPGRADE_ITEM_PRICES = [ { 'id': 115566, 'categories': [{'id': 80, 'name': 'Computing Instance', 'categoryCode': 'guest_core'}], 'item': { 'capacity': '4', 'units': 'DEDICATED_CORE', 'description': 'Computing Instance (Dedicated Host)', } }, ] softlayer-python-5.4.2/SoftLayer/fixtures/SoftLayer_Virtual_Guest_Block_Device_Template_Group.py000066400000000000000000000014641324365065500334070ustar00rootroot00000000000000IMAGES = [{ 'accountId': 1234, 'blockDevices': [], 'createDate': '2013-12-05T21:53:03-06:00', 'globalIdentifier': '0B5DEAF4-643D-46CA-A695-CECBE8832C9D', 'id': 100, 'name': 'test_image', 'parentId': '', 'publicFlag': True, }, { 'accountId': 1234, 'blockDevices': [], 'createDate': '2013-12-05T21:53:03-06:00', 'globalIdentifier': 'EB38414C-2AB3-47F3-BBBD-56A5F689620B', 'id': 101, 'name': 'test_image2', 'parentId': '', 'publicFlag': True, }] getObject = IMAGES[0] getPublicImages = IMAGES deleteObject = {} editObject = True setTags = True createFromExternalSource = [{ 'createDate': '2013-12-05T21:53:03-06:00', 'globalIdentifier': '0B5DEAF4-643D-46CA-A695-CECBE8832C9D', 'id': 100, 'name': 'test_image', }] copyToExternalSource = True softlayer-python-5.4.2/SoftLayer/fixtures/__init__.py000066400000000000000000000000001324365065500227420ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/fixtures/empty.conf000066400000000000000000000000001324365065500226360ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/fixtures/full.conf000066400000000000000000000001151324365065500224510ustar00rootroot00000000000000[softlayer] username=myusername api_key=myapi_key endpoint_url=myendpoint_urlsoftlayer-python-5.4.2/SoftLayer/fixtures/id_rsa.pub000066400000000000000000000000471324365065500226150ustar00rootroot00000000000000ssh-rsa AAAAB3N...pa67 user@example.comsoftlayer-python-5.4.2/SoftLayer/fixtures/no_options.conf000066400000000000000000000000131324365065500236730ustar00rootroot00000000000000[softlayer]softlayer-python-5.4.2/SoftLayer/fixtures/realtest.com000066400000000000000000000015261324365065500231720ustar00rootroot00000000000000$ORIGIN realtest.com. $TTL 86400 @ IN SOA ns1.softlayer.com. support.softlayer.com. ( 2014052300 ; Serial 7200 ; Refresh 600 ; Retry 1728000 ; Expire 43200) ; Minimum @ 86400 IN NS ns1.softlayer.com. @ 86400 IN NS ns2.softlayer.com. IN MX 10 test.realtest.com. testing 86400 IN A 127.0.0.1 testing1 86400 IN A 12.12.0.1 server2 IN A 1.0.3.4 ftp IN CNAME server2 dev.realtest.com IN TXT "This is just a test of the txt record" IN AAAA 2001:db8:10::1 spf IN TXT "v=spf1 ip4:192.0.2.0/24 ip4:198.51.100.123 a -all" softlayer-python-5.4.2/SoftLayer/fixtures/sample_vs_template.conf000066400000000000000000000002271324365065500253770ustar00rootroot00000000000000hostname = myhost domain = example.com datacenter = dal05 cpu = 4 memory = 1024 os = DEBIAN_7_64 network = 100 hourly = true monthly= false disk=50,100softlayer-python-5.4.2/SoftLayer/managers/000077500000000000000000000000001324365065500205675ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/managers/__init__.py000066400000000000000000000033571324365065500227100ustar00rootroot00000000000000""" SoftLayer.managers ~~~~~~~~~~~~~~~~~~ Managers mask out a lot of the complexities of using the API into classes that provide a simpler interface to various services. These are higher-level interfaces to the SoftLayer API. :license: MIT, see LICENSE for more details. """ from SoftLayer.managers.block import BlockStorageManager from SoftLayer.managers.cdn import CDNManager from SoftLayer.managers.dedicated_host import DedicatedHostManager from SoftLayer.managers.dns import DNSManager from SoftLayer.managers.file import FileStorageManager from SoftLayer.managers.firewall import FirewallManager from SoftLayer.managers.hardware import HardwareManager from SoftLayer.managers.image import ImageManager from SoftLayer.managers.ipsec import IPSECManager from SoftLayer.managers.load_balancer import LoadBalancerManager from SoftLayer.managers.messaging import MessagingManager from SoftLayer.managers.metadata import MetadataManager from SoftLayer.managers.network import NetworkManager from SoftLayer.managers.object_storage import ObjectStorageManager from SoftLayer.managers.ordering import OrderingManager from SoftLayer.managers.sshkey import SshKeyManager from SoftLayer.managers.ssl import SSLManager from SoftLayer.managers.ticket import TicketManager from SoftLayer.managers.vs import VSManager __all__ = [ 'BlockStorageManager', 'CDNManager', 'DedicatedHostManager', 'DNSManager', 'FileStorageManager', 'FirewallManager', 'HardwareManager', 'ImageManager', 'IPSECManager', 'LoadBalancerManager', 'MessagingManager', 'MetadataManager', 'NetworkManager', 'ObjectStorageManager', 'OrderingManager', 'SshKeyManager', 'SSLManager', 'TicketManager', 'VSManager', ] softlayer-python-5.4.2/SoftLayer/managers/block.py000066400000000000000000000560671324365065500222510ustar00rootroot00000000000000""" SoftLayer.block ~~~~~~~~~~~~~~~ Block Storage Manager :license: MIT, see LICENSE for more details. """ from SoftLayer import exceptions from SoftLayer.managers import storage_utils from SoftLayer import utils # pylint: disable=too-many-public-methods class BlockStorageManager(utils.IdentifierMixin, object): """Manages SoftLayer Block Storage volumes. See product information here: http://www.softlayer.com/block-storage :param SoftLayer.API.BaseClient client: the client instance """ def __init__(self, client): self.configuration = {} self.client = client def list_block_volumes(self, datacenter=None, username=None, storage_type=None, **kwargs): """Returns a list of block volumes. :param datacenter: Datacenter short name (e.g.: dal09) :param username: Name of volume. :param storage_type: Type of volume: Endurance or Performance :param kwargs: :return: Returns a list of block volumes. """ if 'mask' not in kwargs: items = [ 'id', 'username', 'lunId', 'capacityGb', 'bytesUsed', 'serviceResource.datacenter[name]', 'serviceResourceBackendIpAddress', 'activeTransactionCount', 'replicationPartnerCount' ] kwargs['mask'] = ','.join(items) _filter = utils.NestedDict(kwargs.get('filter') or {}) _filter['iscsiNetworkStorage']['serviceResource']['type']['type'] = \ (utils.query_filter('!~ ISCSI')) _filter['iscsiNetworkStorage']['storageType']['keyName'] = ( utils.query_filter('*BLOCK_STORAGE*')) if storage_type: _filter['iscsiNetworkStorage']['storageType']['keyName'] = ( utils.query_filter('%s_BLOCK_STORAGE*' % storage_type.upper())) if datacenter: _filter['iscsiNetworkStorage']['serviceResource']['datacenter'][ 'name'] = (utils.query_filter(datacenter)) if username: _filter['iscsiNetworkStorage']['username'] = \ (utils.query_filter(username)) kwargs['filter'] = _filter.to_dict() return self.client.call('Account', 'getIscsiNetworkStorage', **kwargs) def get_block_volume_details(self, volume_id, **kwargs): """Returns details about the specified volume. :param volume_id: ID of volume. :param kwargs: :return: Returns details about the specified volume. """ if 'mask' not in kwargs: items = [ 'id', 'username', 'password', 'capacityGb', 'snapshotCapacityGb', 'parentVolume.snapshotSizeBytes', 'storageType.keyName', 'serviceResource.datacenter[name]', 'serviceResourceBackendIpAddress', 'storageTierLevel', 'provisionedIops', 'lunId', 'originalVolumeName', 'originalSnapshotName', 'originalVolumeSize', 'activeTransactionCount', 'activeTransactions.transactionStatus[friendlyName]', 'replicationPartnerCount', 'replicationStatus', 'replicationPartners[id,username,' 'serviceResourceBackendIpAddress,' 'serviceResource[datacenter[name]],' 'replicationSchedule[type[keyname]]]', ] kwargs['mask'] = ','.join(items) return self.client.call('Network_Storage', 'getObject', id=volume_id, **kwargs) def get_block_volume_access_list(self, volume_id, **kwargs): """Returns a list of authorized hosts for a specified volume. :param volume_id: ID of volume. :param kwargs: :return: Returns a list of authorized hosts for a specified volume. """ if 'mask' not in kwargs: items = [ 'id', 'allowedVirtualGuests[allowedHost[credential, sourceSubnet]]', 'allowedHardware[allowedHost[credential]]', 'allowedSubnets[allowedHost[credential]]', 'allowedIpAddresses[allowedHost[credential]]', ] kwargs['mask'] = ','.join(items) return self.client.call('Network_Storage', 'getObject', id=volume_id, **kwargs) def get_block_volume_snapshot_list(self, volume_id, **kwargs): """Returns a list of snapshots for the specified volume. :param volume_id: ID of volume. :param kwargs: :return: Returns a list of snapshots for the specified volume. """ if 'mask' not in kwargs: items = [ 'id', 'notes', 'snapshotSizeBytes', 'storageType[keyName]', 'snapshotCreationTimestamp', 'intervalSchedule', 'hourlySchedule', 'dailySchedule', 'weeklySchedule' ] kwargs['mask'] = ','.join(items) return self.client.call('Network_Storage', 'getSnapshots', id=volume_id, **kwargs) def authorize_host_to_volume(self, volume_id, hardware_ids=None, virtual_guest_ids=None, ip_address_ids=None, **kwargs): """Authorizes hosts to Block Storage Volumes :param volume_id: The Block volume to authorize hosts to :param hardware_ids: A List of SoftLayer_Hardware ids :param virtual_guest_ids: A List of SoftLayer_Virtual_Guest ids :param ip_address_ids: A List of SoftLayer_Network_Subnet_IpAddress ids :return: Returns an array of SoftLayer_Network_Storage_Allowed_Host objects which now have access to the given Block volume """ host_templates = [] storage_utils.populate_host_templates(host_templates, hardware_ids, virtual_guest_ids, ip_address_ids, None) return self.client.call('Network_Storage', 'allowAccessFromHostList', host_templates, id=volume_id, **kwargs) def deauthorize_host_to_volume(self, volume_id, hardware_ids=None, virtual_guest_ids=None, ip_address_ids=None, **kwargs): """Revokes authorization of hosts to Block Storage Volumes :param volume_id: The Block volume to deauthorize hosts to :param hardware_ids: A List of SoftLayer_Hardware ids :param virtual_guest_ids: A List of SoftLayer_Virtual_Guest ids :param ip_address_ids: A List of SoftLayer_Network_Subnet_IpAddress ids :return: Returns an array of SoftLayer_Network_Storage_Allowed_Host objects which have access to the given Block volume """ host_templates = [] storage_utils.populate_host_templates(host_templates, hardware_ids, virtual_guest_ids, ip_address_ids, None) return self.client.call('Network_Storage', 'removeAccessFromHostList', host_templates, id=volume_id, **kwargs) def get_replication_partners(self, volume_id): """Acquires list of replicant volumes pertaining to the given volume. :param volume_id: The ID of the primary volume to be replicated :return: Returns an array of SoftLayer_Location objects """ return self.client.call('Network_Storage', 'getReplicationPartners', id=volume_id) def get_replication_locations(self, volume_id): """Acquires list of the datacenters to which a volume can be replicated. :param volume_id: The ID of the primary volume to be replicated :return: Returns an array of SoftLayer_Network_Storage objects """ return self.client.call('Network_Storage', 'getValidReplicationTargetDatacenterLocations', id=volume_id) def order_replicant_volume(self, volume_id, snapshot_schedule, location, tier=None, os_type=None): """Places an order for a replicant block volume. :param volume_id: The ID of the primary volume to be replicated :param snapshot_schedule: The primary volume's snapshot schedule to use for replication :param location: The location for the ordered replicant volume :param tier: The tier (IOPS per GB) of the primary volume :param os_type: The OS type of the primary volume :return: Returns a SoftLayer_Container_Product_Order_Receipt """ block_mask = 'billingItem[activeChildren,hourlyFlag],'\ 'storageTierLevel,osType,staasVersion,'\ 'hasEncryptionAtRest,snapshotCapacityGb,schedules,'\ 'intervalSchedule,hourlySchedule,dailySchedule,'\ 'weeklySchedule,storageType[keyName],provisionedIops' block_volume = self.get_block_volume_details(volume_id, mask=block_mask) if os_type is None: if isinstance(utils.lookup(block_volume, 'osType', 'keyName'), str): os_type = block_volume['osType']['keyName'] else: raise exceptions.SoftLayerError( "Cannot find primary volume's os-type " "automatically; must specify manually") order = storage_utils.prepare_replicant_order_object( self, snapshot_schedule, location, tier, block_volume, 'block' ) order['osFormatType'] = {'keyName': os_type} return self.client.call('Product_Order', 'placeOrder', order) def order_duplicate_volume(self, origin_volume_id, origin_snapshot_id=None, duplicate_size=None, duplicate_iops=None, duplicate_tier_level=None, duplicate_snapshot_size=None, hourly_billing_flag=False): """Places an order for a duplicate block volume. :param origin_volume_id: The ID of the origin volume to be duplicated :param origin_snapshot_id: Origin snapshot ID to use for duplication :param duplicate_size: Size/capacity for the duplicate volume :param duplicate_iops: The IOPS per GB for the duplicate volume :param duplicate_tier_level: Tier level for the duplicate volume :param duplicate_snapshot_size: Snapshot space size for the duplicate :param hourly_billing_flag: Billing type, monthly (False) or hourly (True), default to monthly. :return: Returns a SoftLayer_Container_Product_Order_Receipt """ block_mask = 'id,billingItem[location,hourlyFlag],snapshotCapacityGb,'\ 'storageType[keyName],capacityGb,originalVolumeSize,'\ 'provisionedIops,storageTierLevel,osType[keyName],'\ 'staasVersion,hasEncryptionAtRest' origin_volume = self.get_block_volume_details(origin_volume_id, mask=block_mask) if isinstance(utils.lookup(origin_volume, 'osType', 'keyName'), str): os_type = origin_volume['osType']['keyName'] else: raise exceptions.SoftLayerError( "Cannot find origin volume's os-type") order = storage_utils.prepare_duplicate_order_object( self, origin_volume, duplicate_iops, duplicate_tier_level, duplicate_size, duplicate_snapshot_size, 'block', hourly_billing_flag ) order['osFormatType'] = {'keyName': os_type} if origin_snapshot_id is not None: order['duplicateOriginSnapshotId'] = origin_snapshot_id return self.client.call('Product_Order', 'placeOrder', order) def order_modified_volume(self, volume_id, new_size=None, new_iops=None, new_tier_level=None): """Places an order for modifying an existing block volume. :param volume_id: The ID of the volume to be modified :param new_size: The new size/capacity for the volume :param new_iops: The new IOPS for the volume :param new_tier_level: The new tier level for the volume :return: Returns a SoftLayer_Container_Product_Order_Receipt """ mask_items = [ 'id', 'billingItem', 'storageType[keyName]', 'capacityGb', 'provisionedIops', 'storageTierLevel', 'staasVersion', 'hasEncryptionAtRest', ] block_mask = ','.join(mask_items) volume = self.get_block_volume_details(volume_id, mask=block_mask) order = storage_utils.prepare_modify_order_object( self, volume, new_iops, new_tier_level, new_size ) return self.client.call('Product_Order', 'placeOrder', order) def delete_snapshot(self, snapshot_id): """Deletes the specified snapshot object. :param snapshot_id: The ID of the snapshot object to delete. """ return self.client.call('Network_Storage', 'deleteObject', id=snapshot_id) def order_block_volume(self, storage_type, location, size, os_type, iops=None, tier_level=None, snapshot_size=None, service_offering='storage_as_a_service', hourly_billing_flag=False): """Places an order for a block volume. :param storage_type: 'performance' or 'endurance' :param location: Datacenter in which to order iSCSI volume :param size: Size of the desired volume, in GB :param os_type: OS Type to use for volume alignment, see help for list :param iops: Number of IOPs for a "Performance" order :param tier_level: Tier level to use for an "Endurance" order :param snapshot_size: The size of optional snapshot space, if snapshot space should also be ordered (None if not ordered) :param service_offering: Requested offering package to use in the order ('storage_as_a_service', 'enterprise', or 'performance') :param hourly_billing_flag: Billing type, monthly (False) or hourly (True), default to monthly. """ order = storage_utils.prepare_volume_order_object( self, storage_type, location, size, iops, tier_level, snapshot_size, service_offering, 'block', hourly_billing_flag ) order['osFormatType'] = {'keyName': os_type} return self.client.call('Product_Order', 'placeOrder', order) def create_snapshot(self, volume_id, notes='', **kwargs): """Creates a snapshot on the given block volume. :param integer volume_id: The id of the volume :param string notes: The notes or "name" to assign the snapshot :return: Returns the id of the new snapshot """ return self.client.call('Network_Storage', 'createSnapshot', notes, id=volume_id, **kwargs) def order_snapshot_space(self, volume_id, capacity, tier, upgrade, **kwargs): """Orders snapshot space for the given block volume. :param integer volume_id: The id of the volume :param integer capacity: The capacity to order, in GB :param float tier: The tier level of the block volume, in IOPS per GB :param boolean upgrade: Flag to indicate if this order is an upgrade :return: Returns a SoftLayer_Container_Product_Order_Receipt """ block_mask = 'id,billingItem[location,hourlyFlag],'\ 'storageType[keyName],storageTierLevel,provisionedIops,'\ 'staasVersion,hasEncryptionAtRest' block_volume = self.get_block_volume_details(volume_id, mask=block_mask, **kwargs) order = storage_utils.prepare_snapshot_order_object( self, block_volume, capacity, tier, upgrade) return self.client.call('Product_Order', 'placeOrder', order) def cancel_snapshot_space(self, volume_id, reason='No longer needed', immediate=False): """Cancels snapshot space for a given volume. :param integer volume_id: The volume ID :param string reason: The reason for cancellation :param boolean immediate_flag: Cancel immediately or on anniversary date """ block_volume = self.get_block_volume_details( volume_id, mask='mask[id,billingItem[activeChildren,hourlyFlag]]') if 'activeChildren' not in block_volume['billingItem']: raise exceptions.SoftLayerError( 'No snapshot space found to cancel') children_array = block_volume['billingItem']['activeChildren'] billing_item_id = None for child in children_array: if child['categoryCode'] == 'storage_snapshot_space': billing_item_id = child['id'] break if not billing_item_id: raise exceptions.SoftLayerError( 'No snapshot space found to cancel') if utils.lookup(block_volume, 'billingItem', 'hourlyFlag'): immediate = True return self.client['Billing_Item'].cancelItem( immediate, True, reason, id=billing_item_id) def enable_snapshots(self, volume_id, schedule_type, retention_count, minute, hour, day_of_week, **kwargs): """Enables snapshots for a specific block volume at a given schedule :param integer volume_id: The id of the volume :param string schedule_type: 'HOURLY'|'DAILY'|'WEEKLY' :param integer retention_count: Number of snapshots to be kept :param integer minute: Minute when to take snapshot :param integer hour: Hour when to take snapshot :param string day_of_week: Day when to take snapshot :return: Returns whether successfully scheduled or not """ return self.client.call('Network_Storage', 'enableSnapshots', schedule_type, retention_count, minute, hour, day_of_week, id=volume_id, **kwargs) def disable_snapshots(self, volume_id, schedule_type): """Disables snapshots for a specific block volume at a given schedule :param integer volume_id: The id of the volume :param string schedule_type: 'HOURLY'|'DAILY'|'WEEKLY' :return: Returns whether successfully disabled or not """ return self.client.call('Network_Storage', 'disableSnapshots', schedule_type, id=volume_id) def list_volume_schedules(self, volume_id): """Lists schedules for a given volume :param integer volume_id: The id of the volume :return: Returns list of schedules assigned to a given volume """ volume_detail = self.client.call( 'Network_Storage', 'getObject', id=volume_id, mask='schedules[type,properties[type]]') return utils.lookup(volume_detail, 'schedules') def restore_from_snapshot(self, volume_id, snapshot_id): """Restores a specific volume from a snapshot :param integer volume_id: The id of the volume :param integer snapshot_id: The id of the restore point :return: Returns whether succesfully restored or not """ return self.client.call('Network_Storage', 'restoreFromSnapshot', snapshot_id, id=volume_id) def cancel_block_volume(self, volume_id, reason='No longer needed', immediate=False): """Cancels the given block storage volume. :param integer volume_id: The volume ID :param string reason: The reason for cancellation :param boolean immediate_flag: Cancel immediately or on anniversary date """ block_volume = self.get_block_volume_details( volume_id, mask='mask[id,billingItem[id,hourlyFlag]]') billing_item_id = block_volume['billingItem']['id'] if utils.lookup(block_volume, 'billingItem', 'hourlyFlag'): immediate = True return self.client['Billing_Item'].cancelItem( immediate, True, reason, id=billing_item_id) def failover_to_replicant(self, volume_id, replicant_id, immediate=False): """Failover to a volume replicant. :param integer volume_id: The id of the volume :param integer replicant_id: ID of replicant to failover to :param boolean immediate: Flag indicating if failover is immediate :return: Returns whether failover was successful or not """ return self.client.call('Network_Storage', 'failoverToReplicant', replicant_id, immediate, id=volume_id) def failback_from_replicant(self, volume_id, replicant_id): """Failback from a volume replicant. :param integer volume_id: The id of the volume :param integer replicant_id: ID of replicant to failback from :return: Returns whether failback was successful or not """ return self.client.call('Network_Storage', 'failbackFromReplicant', replicant_id, id=volume_id) def set_credential_password(self, access_id, password): """Sets the password for an access host :param integer access_id: id of the access host :param string password: password to set """ return self.client.call('Network_Storage_Allowed_Host', 'setCredentialPassword', password, id=access_id) def create_or_update_lun_id(self, volume_id, lun_id): """Set the LUN ID on a volume. :param integer volume_id: The id of the volume :param integer lun_id: LUN ID to set on the volume :return: a SoftLayer_Network_Storage_Property object """ return self.client.call('Network_Storage', 'createOrUpdateLunId', lun_id, id=volume_id) softlayer-python-5.4.2/SoftLayer/managers/cdn.py000066400000000000000000000125541324365065500217140ustar00rootroot00000000000000""" SoftLayer.cdn ~~~~~~~~~~~~~ CDN Manager/helpers :license: MIT, see LICENSE for more details. """ import six from SoftLayer import utils MAX_URLS_PER_LOAD = 5 MAX_URLS_PER_PURGE = 5 class CDNManager(utils.IdentifierMixin, object): """Manage CDN accounts and content. See product information here: http://www.softlayer.com/content-delivery-network :param SoftLayer.API.BaseClient client: the client instance """ def __init__(self, client): self.client = client self.account = self.client['Network_ContentDelivery_Account'] def list_accounts(self): """Lists CDN accounts for the active user.""" account = self.client['Account'] mask = 'cdnAccounts[%s]' % ', '.join(['id', 'createDate', 'cdnAccountName', 'cdnSolutionName', 'cdnAccountNote', 'status']) return account.getObject(mask=mask).get('cdnAccounts', []) def get_account(self, account_id, **kwargs): """Retrieves a CDN account with the specified account ID. :param account_id int: the numeric ID associated with the CDN account. :param dict \\*\\*kwargs: additional arguments to include in the object mask. """ if 'mask' not in kwargs: kwargs['mask'] = 'status' return self.account.getObject(id=account_id, **kwargs) def get_origins(self, account_id, **kwargs): """Retrieves list of origin pull mappings for a specified CDN account. :param account_id int: the numeric ID associated with the CDN account. :param dict \\*\\*kwargs: additional arguments to include in the object mask. """ return self.account.getOriginPullMappingInformation(id=account_id, **kwargs) def add_origin(self, account_id, media_type, origin_url, cname=None, secure=False): """Adds an original pull mapping to an origin-pull. :param int account_id: the numeric ID associated with the CDN account. :param string media_type: the media type/protocol associated with this origin pull mapping; valid values are HTTP, FLASH, and WM. :param string origin_url: the base URL from which content should be pulled. :param string cname: an optional CNAME that should be associated with this origin pull rule; only the hostname should be included (i.e., no 'http://', directories, etc.). :param boolean secure: specifies whether this is an SSL origin pull rule, if SSL is enabled on your account (defaults to false). """ config = {'mediaType': media_type, 'originUrl': origin_url, 'isSecureContent': secure} if cname: config['cname'] = cname return self.account.createOriginPullMapping(config, id=account_id) def remove_origin(self, account_id, origin_id): """Removes an origin pull mapping with the given origin pull ID. :param int account_id: the CDN account ID from which the mapping should be deleted. :param int origin_id: the origin pull mapping ID to delete. """ return self.account.deleteOriginPullRule(origin_id, id=account_id) def load_content(self, account_id, urls): """Prefetches one or more URLs to the CDN edge nodes. :param int account_id: the CDN account ID into which content should be preloaded. :param urls: a string or a list of strings representing the CDN URLs that should be pre-loaded. :returns: true if all load requests were successfully submitted; otherwise, returns the first error encountered. """ if isinstance(urls, six.string_types): urls = [urls] for i in range(0, len(urls), MAX_URLS_PER_LOAD): result = self.account.loadContent(urls[i:i + MAX_URLS_PER_LOAD], id=account_id) if not result: return result return True def purge_content(self, account_id, urls): """Purges one or more URLs from the CDN edge nodes. :param int account_id: the CDN account ID from which content should be purged. :param urls: a string or a list of strings representing the CDN URLs that should be purged. :returns: true if all purge requests were successfully submitted; otherwise, returns the first error encountered. """ if isinstance(urls, six.string_types): urls = [urls] for i in range(0, len(urls), MAX_URLS_PER_PURGE): result = self.account.purgeCache(urls[i:i + MAX_URLS_PER_PURGE], id=account_id) if not result: return result return True softlayer-python-5.4.2/SoftLayer/managers/dedicated_host.py000066400000000000000000000304171324365065500241110ustar00rootroot00000000000000""" SoftLayer.dedicatedhost ~~~~~~~~~~~~~~~~~~~~~~~ DH Manager/helpers :license: MIT, see License for more details. """ import logging import SoftLayer from SoftLayer.managers import ordering from SoftLayer import utils # Invalid names are ignored due to long method names and short argument names # pylint: disable=invalid-name, no-self-use LOGGER = logging.getLogger(__name__) class DedicatedHostManager(utils.IdentifierMixin, object): """Manages SoftLayer Dedicated Hosts. See product information here https://www.ibm.com/cloud/dedicated :param SoftLayer.API.BaseClient client: the client instance :param SoftLayer.managers.OrderingManager ordering_manager: an optional manager to handle ordering. If none is provided, one will be auto initialized. """ def __init__(self, client, ordering_manager=None): self.client = client self.account = client['Account'] self.host = client['Virtual_DedicatedHost'] if ordering_manager is None: self.ordering_manager = ordering.OrderingManager(client) def list_instances(self, tags=None, cpus=None, memory=None, hostname=None, disk=None, datacenter=None, **kwargs): """Retrieve a list of all dedicated hosts on the account :param list tags: filter based on list of tags :param integer cpus: filter based on number of CPUS :param integer memory: filter based on amount of memory :param string hostname: filter based on hostname :param string disk: filter based on disk :param string datacenter: filter based on datacenter :param dict \\*\\*kwargs: response-level options (mask, limit, etc.) :returns: Returns a list of dictionaries representing the matching dedicated host. """ if 'mask' not in kwargs: items = [ 'id', 'name', 'cpuCount', 'diskCapacity', 'memoryCapacity', 'datacenter', 'guestCount', ] kwargs['mask'] = "mask[%s]" % ','.join(items) _filter = utils.NestedDict(kwargs.get('filter') or {}) if tags: _filter['dedicatedHosts']['tagReferences']['tag']['name'] = { 'operation': 'in', 'options': [{'name': 'data', 'value': tags}], } if hostname: _filter['dedicatedHosts']['name'] = ( utils.query_filter(hostname) ) if cpus: _filter['dedicatedHosts']['cpuCount'] = utils.query_filter(cpus) if disk: _filter['dedicatedHosts']['diskCapacity'] = ( utils.query_filter(disk)) if memory: _filter['dedicatedHosts']['memoryCapacity'] = ( utils.query_filter(memory)) if datacenter: _filter['dedicatedHosts']['datacenter']['name'] = ( utils.query_filter(datacenter)) kwargs['filter'] = _filter.to_dict() return self.account.getDedicatedHosts(**kwargs) def get_host(self, host_id, **kwargs): """Get details about a dedicated host. :param integer : the host ID :returns: A dictionary containing host information. Example:: # Print out host ID 12345. dh = mgr.get_host(12345) print dh # Print out only name and backendRouter for instance 12345 object_mask = "mask[name,backendRouter[id]]" dh = mgr.get_host(12345, mask=mask) print dh """ if 'mask' not in kwargs: kwargs['mask'] = (''' id, name, cpuCount, memoryCapacity, diskCapacity, createDate, modifyDate, backendRouter[ id, hostname, domain ], billingItem[ id, nextInvoiceTotalRecurringAmount, children[ categoryCode, nextInvoiceTotalRecurringAmount ], orderItem[ id, order.userRecord[ username ] ] ], datacenter[ id, name, longName ], guests[ id, hostname, domain, uuid ], guestCount ''') return self.host.getObject(id=host_id, **kwargs) def place_order(self, hostname, domain, location, flavor, hourly, router=None): """Places an order for a dedicated host. See get_create_options() for valid arguments. :param string hostname: server hostname :param string domain: server domain name :param string location: location (datacenter) name :param boolean hourly: True if using hourly pricing (default). False for monthly. :param int router: an optional value for selecting a backend router """ create_options = self._generate_create_dict(hostname=hostname, router=router, domain=domain, flavor=flavor, datacenter=location, hourly=hourly) return self.client['Product_Order'].placeOrder(create_options) def verify_order(self, hostname, domain, location, hourly, flavor, router=None): """Verifies an order for a dedicated host. See :func:`place_order` for a list of available options. """ create_options = self._generate_create_dict(hostname=hostname, router=router, domain=domain, flavor=flavor, datacenter=location, hourly=hourly) return self.client['Product_Order'].verifyOrder(create_options) def _generate_create_dict(self, hostname=None, domain=None, flavor=None, router=None, datacenter=None, hourly=True): """Translates args into a dictionary for creating a dedicated host.""" package = self._get_package() item = self._get_item(package, flavor) location = self._get_location(package['regions'], datacenter) price = self._get_price(item) routers = self._get_backend_router( location['location']['locationPackageDetails'], item) router = self._get_default_router(routers, router) hardware = { 'hostname': hostname, 'domain': domain, 'primaryBackendNetworkComponent': { 'router': { 'id': router } } } complex_type = "SoftLayer_Container_Product_Order_Virtual_DedicatedHost" order = { "complexType": complex_type, "quantity": 1, 'location': location['keyname'], 'packageId': package['id'], 'prices': [{'id': price}], 'hardware': [hardware], 'useHourlyPricing': hourly, } return order def _get_package(self): """Get the package related to simple dedicated host ordering.""" mask = ''' items[ id, description, prices, capacity, keyName, itemCategory[categoryCode], bundleItems[capacity, categories[categoryCode]] ], regions[location[location[priceGroups]]] ''' package_keyname = 'DEDICATED_HOST' package = self.ordering_manager.get_package_by_key(package_keyname, mask=mask) return package def _get_location(self, regions, datacenter): """Get the longer key with a short location(datacenter) name.""" for region in regions: # list of locations if region['location']['location']['name'] == datacenter: return region raise SoftLayer.SoftLayerError("Could not find valid location for: '%s'" % datacenter) def get_create_options(self): """Returns valid options for ordering a dedicated host.""" package = self._get_package() # Locations locations = [] for region in package['regions']: locations.append({ 'name': region['location']['location']['longName'], 'key': region['location']['location']['name'], }) # flavors dedicated_host = [] for item in package['items']: if item['itemCategory']['categoryCode'] == \ 'dedicated_virtual_hosts': dedicated_host.append({ 'name': item['description'], 'key': item['keyName'], }) return {'locations': locations, 'dedicated_host': dedicated_host} def _get_price(self, package): """Returns valid price for ordering a dedicated host.""" for price in package['prices']: if not price.get('locationGroupId'): return price['id'] raise SoftLayer.SoftLayerError("Could not find valid price") def _get_item(self, package, flavor): """Returns the item for ordering a dedicated host.""" for item in package['items']: if item['keyName'] == flavor: return item raise SoftLayer.SoftLayerError("Could not find valid item for: '%s'" % flavor) def _get_backend_router(self, locations, item): """Returns valid router options for ordering a dedicated host.""" mask = ''' id, hostname ''' cpu_count = item['capacity'] for capacity in item['bundleItems']: for category in capacity['categories']: if category['categoryCode'] == 'dedicated_host_ram': mem_capacity = capacity['capacity'] if category['categoryCode'] == 'dedicated_host_disk': disk_capacity = capacity['capacity'] if locations is not None: for location in locations: if location['locationId'] is not None: loc_id = location['locationId'] host = { 'cpuCount': cpu_count, 'memoryCapacity': mem_capacity, 'diskCapacity': disk_capacity, 'datacenter': { 'id': loc_id } } routers = self.host.getAvailableRouters(host, mask=mask) return routers raise SoftLayer.SoftLayerError("Could not find available routers") def _get_default_router(self, routers, router_name=None): """Returns the default router for ordering a dedicated host.""" if router_name is None: for router in routers: if router['id'] is not None: return router['id'] else: for router in routers: if router['hostname'] == router_name: return router['id'] raise SoftLayer.SoftLayerError("Could not find valid default router") def get_router_options(self, datacenter=None, flavor=None): """Returns available backend routers for the dedicated host.""" package = self._get_package() location = self._get_location(package['regions'], datacenter) item = self._get_item(package, flavor) return self._get_backend_router(location['location']['locationPackageDetails'], item) softlayer-python-5.4.2/SoftLayer/managers/dns.py000066400000000000000000000121621324365065500217270ustar00rootroot00000000000000""" SoftLayer.dns ~~~~~~~~~~~~~ DNS Manager/helpers :license: MIT, see LICENSE for more details. """ import time from SoftLayer import utils class DNSManager(utils.IdentifierMixin, object): """Manage SoftLayer DNS. See product information here: http://www.softlayer.com/DOMAIN-SERVICES :param SoftLayer.API.BaseClient client: the client instance """ def __init__(self, client): self.client = client self.service = self.client['Dns_Domain'] self.record = self.client['Dns_Domain_ResourceRecord'] self.resolvers = [self._get_zone_id_from_name] def _get_zone_id_from_name(self, name): """Return zone ID based on a zone.""" results = self.client['Account'].getDomains( filter={"domains": {"name": utils.query_filter(name)}}) return [x['id'] for x in results] def list_zones(self, **kwargs): """Retrieve a list of all DNS zones. :param dict \\*\\*kwargs: response-level options (mask, limit, etc.) :returns: A list of dictionaries representing the matching zones. """ return self.client['Account'].getDomains(**kwargs) def get_zone(self, zone_id, records=True): """Get a zone and its records. :param zone: the zone name :returns: A dictionary containing a large amount of information about the specified zone. """ mask = None if records: mask = 'resourceRecords' return self.service.getObject(id=zone_id, mask=mask) def create_zone(self, zone, serial=None): """Create a zone for the specified zone. :param zone: the zone name to create :param serial: serial value on the zone (default: strftime(%Y%m%d01)) """ return self.service.createObject({ 'name': zone, 'serial': serial or time.strftime('%Y%m%d01'), "resourceRecords": {}}) def delete_zone(self, zone_id): """Delete a zone by its ID. :param integer zone_id: the zone ID to delete """ return self.service.deleteObject(id=zone_id) def edit_zone(self, zone): """Update an existing zone with the options provided. The provided dict must include an 'id' key and value corresponding to the zone that should be updated. :param dict zone: the zone to update """ self.service.editObject(zone) def create_record(self, zone_id, record, record_type, data, ttl=60): """Create a resource record on a domain. :param integer id: the zone's ID :param record: the name of the record to add :param record_type: the type of record (A, AAAA, CNAME, MX, TXT, etc.) :param data: the record's value :param integer ttl: the TTL or time-to-live value (default: 60) """ return self.record.createObject({ 'domainId': zone_id, 'ttl': ttl, 'host': record, 'type': record_type, 'data': data}) def delete_record(self, record_id): """Delete a resource record by its ID. :param integer id: the record's ID """ self.record.deleteObject(id=record_id) def get_record(self, record_id): """Get a DNS record. :param integer id: the record's ID """ return self.record.getObject(id=record_id) def get_records(self, zone_id, ttl=None, data=None, host=None, record_type=None): """List, and optionally filter, records within a zone. :param zone: the zone name in which to search. :param int ttl: time in seconds :param str data: the records data :param str host: record's host :param str record_type: the type of record :returns: A list of dictionaries representing the matching records within the specified zone. """ _filter = utils.NestedDict() if ttl: _filter['resourceRecords']['ttl'] = utils.query_filter(ttl) if host: _filter['resourceRecords']['host'] = utils.query_filter(host) if data: _filter['resourceRecords']['data'] = utils.query_filter(data) if record_type: _filter['resourceRecords']['type'] = utils.query_filter( record_type.lower()) results = self.service.getResourceRecords( id=zone_id, mask='id,expire,domainId,host,minimum,refresh,retry,' 'mxPriority,ttl,type,data,responsiblePerson', filter=_filter.to_dict(), ) return results def edit_record(self, record): """Update an existing record with the options provided. The provided dict must include an 'id' key and value corresponding to the record that should be updated. :param dict record: the record to update """ self.record.editObject(record, id=record['id']) def dump_zone(self, zone_id): """Retrieve a zone dump in BIND format. :param integer id: The zone ID to dump """ return self.service.getZoneFileContents(id=zone_id) softlayer-python-5.4.2/SoftLayer/managers/file.py000066400000000000000000000530221324365065500220620ustar00rootroot00000000000000""" SoftLayer.file ~~~~~~~~~~~~~~~ File Storage Manager :license: MIT, see LICENSE for more details. """ from SoftLayer import exceptions from SoftLayer.managers import storage_utils from SoftLayer import utils # pylint: disable=too-many-public-methods class FileStorageManager(utils.IdentifierMixin, object): """Manages file Storage volumes.""" def __init__(self, client): self.configuration = {} self.client = client def list_file_volumes(self, datacenter=None, username=None, storage_type=None, **kwargs): """Returns a list of file volumes. :param datacenter: Datacenter short name (e.g.: dal09) :param username: Name of volume. :param storage_type: Type of volume: Endurance or Performance :param kwargs: :return: Returns a list of file volumes. """ if 'mask' not in kwargs: items = [ 'id', 'username', 'capacityGb', 'bytesUsed', 'serviceResource.datacenter[name]', 'serviceResourceBackendIpAddress', 'activeTransactionCount', 'fileNetworkMountAddress', 'replicationPartnerCount' ] kwargs['mask'] = ','.join(items) _filter = utils.NestedDict(kwargs.get('filter') or {}) _filter['nasNetworkStorage']['serviceResource']['type']['type'] = \ (utils.query_filter('!~ NAS')) _filter['nasNetworkStorage']['storageType']['keyName'] = ( utils.query_filter('*FILE_STORAGE*')) if storage_type: _filter['nasNetworkStorage']['storageType']['keyName'] = ( utils.query_filter('%s_FILE_STORAGE*' % storage_type.upper())) if datacenter: _filter['nasNetworkStorage']['serviceResource']['datacenter'][ 'name'] = (utils.query_filter(datacenter)) if username: _filter['nasNetworkStorage']['username'] = \ (utils.query_filter(username)) kwargs['filter'] = _filter.to_dict() return self.client.call('Account', 'getNasNetworkStorage', **kwargs) def get_file_volume_details(self, volume_id, **kwargs): """Returns details about the specified volume. :param volume_id: ID of volume. :param kwargs: :return: Returns details about the specified volume. """ if 'mask' not in kwargs: items = [ 'id', 'username', 'password', 'capacityGb', 'bytesUsed', 'snapshotCapacityGb', 'parentVolume.snapshotSizeBytes', 'storageType.keyName', 'serviceResource.datacenter[name]', 'serviceResourceBackendIpAddress', 'fileNetworkMountAddress', 'storageTierLevel', 'provisionedIops', 'lunId', 'originalVolumeName', 'originalSnapshotName', 'originalVolumeSize', 'activeTransactionCount', 'activeTransactions.transactionStatus[friendlyName]', 'replicationPartnerCount', 'replicationStatus', 'replicationPartners[id,username,' 'serviceResourceBackendIpAddress,' 'serviceResource[datacenter[name]],' 'replicationSchedule[type[keyname]]]', ] kwargs['mask'] = ','.join(items) return self.client.call('Network_Storage', 'getObject', id=volume_id, **kwargs) def get_file_volume_access_list(self, volume_id, **kwargs): """Returns a list of authorized hosts for a specified volume. :param volume_id: ID of volume. :param kwargs: :return: Returns a list of authorized hosts for a specified volume. """ if 'mask' not in kwargs: items = [ 'id', 'allowedVirtualGuests[allowedHost[credential, sourceSubnet]]', 'allowedHardware[allowedHost[credential]]', 'allowedSubnets[allowedHost[credential]]', 'allowedIpAddresses[allowedHost[credential]]', ] kwargs['mask'] = ','.join(items) return self.client.call('Network_Storage', 'getObject', id=volume_id, **kwargs) def get_file_volume_snapshot_list(self, volume_id, **kwargs): """Returns a list of snapshots for the specified volume. :param volume_id: ID of volume. :param kwargs: :return: Returns a list of snapshots for the specified volume. """ if 'mask' not in kwargs: items = [ 'id', 'notes', 'snapshotSizeBytes', 'storageType[keyName]', 'snapshotCreationTimestamp', 'intervalSchedule', 'hourlySchedule', 'dailySchedule', 'weeklySchedule' ] kwargs['mask'] = ','.join(items) return self.client.call('Network_Storage', 'getSnapshots', id=volume_id, **kwargs) def authorize_host_to_volume(self, volume_id, hardware_ids=None, virtual_guest_ids=None, ip_address_ids=None, subnet_ids=None, **kwargs): """Authorizes hosts to File Storage Volumes :param volume_id: The File volume to authorize hosts to :param hardware_ids: A List of SoftLayer_Hardware ids :param virtual_guest_ids: A List of SoftLayer_Virtual_Guest ids :param ip_address_ids: A List of SoftLayer_Network_Subnet_IpAddress ids :param subnet_ids: A List of SoftLayer_Network_Subnet ids :return: Returns an array of SoftLayer_Network_Storage_Allowed_Host objects which now have access to the given File volume """ host_templates = [] storage_utils.populate_host_templates(host_templates, hardware_ids, virtual_guest_ids, ip_address_ids, subnet_ids) return self.client.call('Network_Storage', 'allowAccessFromHostList', host_templates, id=volume_id, **kwargs) def deauthorize_host_to_volume(self, volume_id, hardware_ids=None, virtual_guest_ids=None, ip_address_ids=None, subnet_ids=None, **kwargs): """Revokes authorization of hosts to File Storage Volumes :param volume_id: The File volume to deauthorize hosts to :param hardware_ids: A List of SoftLayer_Hardware ids :param virtual_guest_ids: A List of SoftLayer_Virtual_Guest ids :param ip_address_ids: A List of SoftLayer_Network_Subnet_IpAddress ids :param subnet_ids: A List of SoftLayer_Network_Subnet ids :return: Returns an array of SoftLayer_Network_Storage_Allowed_Host objects which have access to the given File volume """ host_templates = [] storage_utils.populate_host_templates(host_templates, hardware_ids, virtual_guest_ids, ip_address_ids, subnet_ids) return self.client.call('Network_Storage', 'removeAccessFromHostList', host_templates, id=volume_id, **kwargs) def order_replicant_volume(self, volume_id, snapshot_schedule, location, tier=None): """Places an order for a replicant file volume. :param volume_id: The ID of the primary volume to be replicated :param snapshot_schedule: The primary volume's snapshot schedule to use for replication :param location: The location for the ordered replicant volume :param tier: The tier (IOPS per GB) of the primary volume :return: Returns a SoftLayer_Container_Product_Order_Receipt """ file_mask = 'billingItem[activeChildren,hourlyFlag],'\ 'storageTierLevel,osType,staasVersion,'\ 'hasEncryptionAtRest,snapshotCapacityGb,schedules,'\ 'intervalSchedule,hourlySchedule,dailySchedule,'\ 'weeklySchedule,storageType[keyName],provisionedIops' file_volume = self.get_file_volume_details(volume_id, mask=file_mask) order = storage_utils.prepare_replicant_order_object( self, snapshot_schedule, location, tier, file_volume, 'file' ) return self.client.call('Product_Order', 'placeOrder', order) def get_replication_partners(self, volume_id): """Acquires list of replicant volumes pertaining to the given volume. :param volume_id: The ID of the primary volume to be replicated :return: Returns an array of SoftLayer_Location objects """ return self.client.call('Network_Storage', 'getReplicationPartners', id=volume_id) def get_replication_locations(self, volume_id): """Acquires list of the datacenters to which a volume can be replicated. :param volume_id: The ID of the primary volume to be replicated :return: Returns an array of SoftLayer_Network_Storage objects """ return self.client.call('Network_Storage', 'getValidReplicationTargetDatacenterLocations', id=volume_id) def order_duplicate_volume(self, origin_volume_id, origin_snapshot_id=None, duplicate_size=None, duplicate_iops=None, duplicate_tier_level=None, duplicate_snapshot_size=None, hourly_billing_flag=False): """Places an order for a duplicate file volume. :param origin_volume_id: The ID of the origin volume to be duplicated :param origin_snapshot_id: Origin snapshot ID to use for duplication :param duplicate_size: Size/capacity for the duplicate volume :param duplicate_iops: The IOPS per GB for the duplicate volume :param duplicate_tier_level: Tier level for the duplicate volume :param duplicate_snapshot_size: Snapshot space size for the duplicate :param hourly_billing_flag: Billing type, monthly (False) or hourly (True), default to monthly. :return: Returns a SoftLayer_Container_Product_Order_Receipt """ file_mask = 'id,billingItem[location,hourlyFlag],snapshotCapacityGb,'\ 'storageType[keyName],capacityGb,originalVolumeSize,'\ 'provisionedIops,storageTierLevel,'\ 'staasVersion,hasEncryptionAtRest' origin_volume = self.get_file_volume_details(origin_volume_id, mask=file_mask) order = storage_utils.prepare_duplicate_order_object( self, origin_volume, duplicate_iops, duplicate_tier_level, duplicate_size, duplicate_snapshot_size, 'file', hourly_billing_flag ) if origin_snapshot_id is not None: order['duplicateOriginSnapshotId'] = origin_snapshot_id return self.client.call('Product_Order', 'placeOrder', order) def order_modified_volume(self, volume_id, new_size=None, new_iops=None, new_tier_level=None): """Places an order for modifying an existing file volume. :param volume_id: The ID of the volume to be modified :param new_size: The new size/capacity for the volume :param new_iops: The new IOPS for the volume :param new_tier_level: The new tier level for the volume :return: Returns a SoftLayer_Container_Product_Order_Receipt """ mask_items = [ 'id', 'billingItem', 'storageType[keyName]', 'capacityGb', 'provisionedIops', 'storageTierLevel', 'staasVersion', 'hasEncryptionAtRest', ] file_mask = ','.join(mask_items) volume = self.get_file_volume_details(volume_id, mask=file_mask) order = storage_utils.prepare_modify_order_object( self, volume, new_iops, new_tier_level, new_size ) return self.client.call('Product_Order', 'placeOrder', order) def delete_snapshot(self, snapshot_id): """Deletes the specified snapshot object. :param snapshot_id: The ID of the snapshot object to delete. """ return self.client.call('Network_Storage', 'deleteObject', id=snapshot_id) def order_file_volume(self, storage_type, location, size, iops=None, tier_level=None, snapshot_size=None, service_offering='storage_as_a_service', hourly_billing_flag=False): """Places an order for a file volume. :param storage_type: 'performance' or 'endurance' :param location: Name of the datacenter in which to order the volume :param size: Size of the desired volume, in GB :param iops: Number of IOPs for a "Performance" order :param tier_level: Tier level to use for an "Endurance" order :param snapshot_size: The size of optional snapshot space, if snapshot space should also be ordered (None if not ordered) :param service_offering: Requested offering package to use in the order ('storage_as_a_service', 'enterprise', or 'performance') :param hourly_billing_flag: Billing type, monthly (False) or hourly (True), default to monthly. """ order = storage_utils.prepare_volume_order_object( self, storage_type, location, size, iops, tier_level, snapshot_size, service_offering, 'file', hourly_billing_flag ) return self.client.call('Product_Order', 'placeOrder', order) def create_snapshot(self, volume_id, notes='', **kwargs): """Creates a snapshot on the given file volume. :param integer volume_id: The id of the volume :param string notes: The notes or "name" to assign the snapshot :return: Returns the id of the new snapshot """ return self.client.call('Network_Storage', 'createSnapshot', notes, id=volume_id, **kwargs) def enable_snapshots(self, volume_id, schedule_type, retention_count, minute, hour, day_of_week, **kwargs): """Enables snapshots for a specific file volume at a given schedule :param integer volume_id: The id of the volume :param string schedule_type: 'HOURLY'|'DAILY'|'WEEKLY' :param integer retention_count: The number of snapshots to attempt to retain in this schedule :param integer minute: The minute of the hour at which HOURLY, DAILY, and WEEKLY snapshots should be taken :param integer hour: The hour of the day at which DAILY and WEEKLY snapshots should be taken :param string|integer day_of_week: The day of the week on which WEEKLY snapshots should be taken, either as a string ('SUNDAY') or integer ('0' is Sunday) :return: Returns whether successfully scheduled or not """ return self.client.call('Network_Storage', 'enableSnapshots', schedule_type, retention_count, minute, hour, day_of_week, id=volume_id, **kwargs) def disable_snapshots(self, volume_id, schedule_type): """Disables snapshots for a specific file volume at a given schedule :param integer volume_id: The id of the volume :param string schedule_type: 'HOURLY'|'DAILY'|'WEEKLY' :return: Returns whether successfully disabled or not """ return self.client.call('Network_Storage', 'disableSnapshots', schedule_type, id=volume_id) def list_volume_schedules(self, volume_id): """Lists schedules for a given volume :param integer volume_id: The id of the volume :return: Returns list of schedules assigned to a given volume """ volume_detail = self.client.call( 'Network_Storage', 'getObject', id=volume_id, mask='schedules[type,properties[type]]') return utils.lookup(volume_detail, 'schedules') def order_snapshot_space(self, volume_id, capacity, tier, upgrade, **kwargs): """Orders snapshot space for the given file volume. :param integer volume_id: The ID of the volume :param integer capacity: The capacity to order, in GB :param float tier: The tier level of the file volume, in IOPS per GB :param boolean upgrade: Flag to indicate if this order is an upgrade :return: Returns a SoftLayer_Container_Product_Order_Receipt """ file_mask = 'id,billingItem[location,hourlyFlag],'\ 'storageType[keyName],storageTierLevel,provisionedIops,'\ 'staasVersion,hasEncryptionAtRest' file_volume = self.get_file_volume_details(volume_id, mask=file_mask, **kwargs) order = storage_utils.prepare_snapshot_order_object( self, file_volume, capacity, tier, upgrade) return self.client.call('Product_Order', 'placeOrder', order) def cancel_snapshot_space(self, volume_id, reason='No longer needed', immediate=False): """Cancels snapshot space for a given volume. :param integer volume_id: The volume ID :param string reason: The reason for cancellation :param boolean immediate: Cancel immediately or on anniversary date """ file_volume = self.get_file_volume_details( volume_id, mask='mask[id,billingItem[activeChildren,hourlyFlag]]') if 'activeChildren' not in file_volume['billingItem']: raise exceptions.SoftLayerError( 'No snapshot space found to cancel') children_array = file_volume['billingItem']['activeChildren'] billing_item_id = None for child in children_array: if child['categoryCode'] == 'storage_snapshot_space': billing_item_id = child['id'] break if not billing_item_id: raise exceptions.SoftLayerError( 'No snapshot space found to cancel') if utils.lookup(file_volume, 'billingItem', 'hourlyFlag'): immediate = True return self.client['Billing_Item'].cancelItem( immediate, True, reason, id=billing_item_id) def restore_from_snapshot(self, volume_id, snapshot_id): """Restores a specific volume from a snapshot :param integer volume_id: The ID of the volume :param integer snapshot_id: The id of the restore point :return: Returns whether successfully restored or not """ return self.client.call('Network_Storage', 'restoreFromSnapshot', snapshot_id, id=volume_id) def cancel_file_volume(self, volume_id, reason='No longer needed', immediate=False): """Cancels the given file storage volume. :param integer volume_id: The volume ID :param string reason: The reason for cancellation :param boolean immediate: Cancel immediately or on anniversary date """ file_volume = self.get_file_volume_details( volume_id, mask='mask[id,billingItem[id,hourlyFlag]]') billing_item_id = file_volume['billingItem']['id'] if utils.lookup(file_volume, 'billingItem', 'hourlyFlag'): immediate = True return self.client['Billing_Item'].cancelItem( immediate, True, reason, id=billing_item_id) def failover_to_replicant(self, volume_id, replicant_id, immediate=False): """Failover to a volume replicant. :param integer volume_id: The ID of the volume :param integer replicant_id: ID of replicant to failover to :param boolean immediate: Flag indicating if failover is immediate :return: Returns whether failover was successful or not """ return self.client.call('Network_Storage', 'failoverToReplicant', replicant_id, immediate, id=volume_id) def failback_from_replicant(self, volume_id, replicant_id): """Failback from a volume replicant. :param integer volume_id: The ID of the volume :param integer replicant_id: ID of replicant to failback from :return: Returns whether failback was successful or not """ return self.client.call('Network_Storage', 'failbackFromReplicant', replicant_id, id=volume_id) softlayer-python-5.4.2/SoftLayer/managers/firewall.py000066400000000000000000000262371324365065500227600ustar00rootroot00000000000000""" SoftLayer.firewall ~~~~~~~~~~~~~~~~~~ Firewall Manager/helpers :license: MIT, see LICENSE for more details. """ from SoftLayer import exceptions from SoftLayer import utils RULE_MASK = ('mask[orderValue,action,destinationIpAddress,' 'destinationIpSubnetMask,protocol,destinationPortRangeStart,' 'destinationPortRangeEnd,sourceIpAddress,sourceIpSubnetMask,' 'version,notes]') def has_firewall(vlan): """Helper to determine whether or not a VLAN has a firewall. :param dict vlan: A dictionary representing a VLAN :returns: True if the VLAN has a firewall, false if it doesn't. """ return bool( vlan.get('dedicatedFirewallFlag', None) or vlan.get('highAvailabilityFirewallFlag', None) or vlan.get('firewallInterfaces', None) or vlan.get('firewallNetworkComponents', None) or vlan.get('firewallGuestNetworkComponents', None) ) class FirewallManager(utils.IdentifierMixin, object): """Manages SoftLayer firewalls See product information here: http://www.softlayer.com/firewalls :param SoftLayer.API.BaseClient client: the client instance """ def __init__(self, client): self.client = client self.account = self.client['Account'] self.prod_pkg = self.client['Product_Package'] def get_standard_package(self, server_id, is_virt=True): """Retrieves the standard firewall package for the virtual server. :param int server_id: The ID of the server to create the firewall for :param bool is_virt: True if the ID provided is for a virtual server, False for a server :returns: A dictionary containing the standard virtual server firewall package """ firewall_port_speed = self._get_fwl_port_speed(server_id, is_virt) _value = "%s%s" % (firewall_port_speed, "Mbps Hardware Firewall") _filter = {'items': {'description': utils.query_filter(_value)}} return self.prod_pkg.getItems(id=0, filter=_filter) def get_dedicated_package(self, ha_enabled=False): """Retrieves the dedicated firewall package. :param bool ha_enabled: True if HA is to be enabled on the firewall False for No HA :returns: A dictionary containing the dedicated virtual server firewall package """ fwl_filter = 'Hardware Firewall (Dedicated)' ha_fwl_filter = 'Hardware Firewall (High Availability)' _filter = utils.NestedDict({}) if ha_enabled: _filter['items']['description'] = utils.query_filter(ha_fwl_filter) else: _filter['items']['description'] = utils.query_filter(fwl_filter) return self.prod_pkg.getItems(id=0, filter=_filter.to_dict()) def cancel_firewall(self, firewall_id, dedicated=False): """Cancels the specified firewall. :param int firewall_id: Firewall ID to be cancelled. :param bool dedicated: If true, the firewall instance is dedicated, otherwise, the firewall instance is shared. """ fwl_billing = self._get_fwl_billing_item(firewall_id, dedicated) billing_item_service = self.client['Billing_Item'] return billing_item_service.cancelService(id=fwl_billing['id']) def add_standard_firewall(self, server_id, is_virt=True): """Creates a firewall for the specified virtual/hardware server. :param int server_id: The ID of the server to create the firewall for :param bool is_virt: If true, will create the firewall for a virtual server, otherwise for a hardware server. :returns: A dictionary containing the standard virtual server firewall order """ package = self.get_standard_package(server_id, is_virt) if is_virt: product_order = { 'complexType': 'SoftLayer_Container_Product_Order_Network_' 'Protection_Firewall', 'quantity': 1, 'packageId': 0, 'virtualGuests': [{'id': server_id}], 'prices': [{'id': package[0]['prices'][0]['id']}] } else: product_order = { 'complexType': 'SoftLayer_Container_Product_Order_Network_' 'Protection_Firewall', 'quantity': 1, 'packageId': 0, 'hardware': [{'id': server_id}], 'prices': [{'id': package[0]['prices'][0]['id']}] } return self.client['Product_Order'].placeOrder(product_order) def add_vlan_firewall(self, vlan_id, ha_enabled=False): """Creates a firewall for the specified vlan. :param int vlan_id: The ID of the vlan to create the firewall for :param bool ha_enabled: If True, an HA firewall will be created :returns: A dictionary containing the VLAN firewall order """ package = self.get_dedicated_package(ha_enabled) product_order = { 'complexType': 'SoftLayer_Container_Product_Order_Network_' 'Protection_Firewall_Dedicated', 'quantity': 1, 'packageId': 0, 'vlanId': vlan_id, 'prices': [{'id': package[0]['prices'][0]['id']}] } return self.client['Product_Order'].placeOrder(product_order) def _get_fwl_billing_item(self, firewall_id, dedicated=False): """Retrieves the billing item of the firewall. :param int firewall_id: Firewall ID to get the billing item for :param bool dedicated: whether the firewall is dedicated or standard :returns: A dictionary of the firewall billing item. """ mask = 'mask[id,billingItem[id]]' if dedicated: firewall_service = self.client['Network_Vlan_Firewall'] else: firewall_service = self.client['Network_Component_Firewall'] firewall = firewall_service.getObject(id=firewall_id, mask=mask) if firewall is None: raise exceptions.SoftLayerError( "Unable to find firewall %d" % firewall_id) if firewall.get('billingItem') is None: raise exceptions.SoftLayerError( "Unable to find billing item for firewall %d" % firewall_id) return firewall['billingItem'] def _get_fwl_port_speed(self, server_id, is_virt=True): """Determines the appropriate speed for a firewall. :param int server_id: The ID of server the firewall is for :param bool is_virt: True if the server_id is for a virtual server :returns: a integer representing the Mbps speed of a firewall """ fwl_port_speed = 0 if is_virt: mask = ('primaryNetworkComponent[maxSpeed]') svc = self.client['Virtual_Guest'] primary = svc.getObject(mask=mask, id=server_id) fwl_port_speed = primary['primaryNetworkComponent']['maxSpeed'] else: mask = ('id,maxSpeed,networkComponentGroup.networkComponents') svc = self.client['Hardware_Server'] network_components = svc.getFrontendNetworkComponents( mask=mask, id=server_id) grouped = [interface['networkComponentGroup']['networkComponents'] for interface in network_components if 'networkComponentGroup' in interface] ungrouped = [interface for interface in network_components if 'networkComponentGroup' not in interface] # For each group, sum the maxSpeeds of each compoment in the # group. Put the sum for each in a new list group_speeds = [] for group in grouped: group_speed = 0 for interface in group: group_speed += interface['maxSpeed'] group_speeds.append(group_speed) # The max speed of all groups is the max of the list max_grouped_speed = max(group_speeds) max_ungrouped = 0 for interface in ungrouped: max_ungrouped = max(max_ungrouped, interface['maxSpeed']) fwl_port_speed = max(max_grouped_speed, max_ungrouped) return fwl_port_speed def get_firewalls(self): """Returns a list of all firewalls on the account. :returns: A list of firewalls on the current account. """ mask = ('firewallNetworkComponents,' 'networkVlanFirewall,' 'dedicatedFirewallFlag,' 'firewallGuestNetworkComponents,' 'firewallInterfaces,' 'firewallRules,' 'highAvailabilityFirewallFlag') return [firewall for firewall in self.account.getNetworkVlans(mask=mask) if has_firewall(firewall)] def get_standard_fwl_rules(self, firewall_id): """Get the rules of a standard firewall. :param integer firewall_id: the instance ID of the standard firewall :returns: A list of the rules. """ svc = self.client['Network_Component_Firewall'] return svc.getRules(id=firewall_id, mask=RULE_MASK) def get_dedicated_fwl_rules(self, firewall_id): """Get the rules of a dedicated firewall. :param integer firewall_id: the instance ID of the dedicated firewall :returns: A list of the rules. """ svc = self.client['Network_Vlan_Firewall'] return svc.getRules(id=firewall_id, mask=RULE_MASK) def edit_dedicated_fwl_rules(self, firewall_id, rules): """Edit the rules for dedicated firewall. :param integer firewall_id: the instance ID of the dedicated firewall :param list rules: the rules to be pushed on the firewall as defined by SoftLayer_Network_Firewall_Update_Request_Rule """ mask = ('mask[networkVlan[firewallInterfaces' '[firewallContextAccessControlLists]]]') svc = self.client['Network_Vlan_Firewall'] fwl = svc.getObject(id=firewall_id, mask=mask) network_vlan = fwl['networkVlan'] for fwl1 in network_vlan['firewallInterfaces']: if fwl1['name'] == 'inside': continue for control_list in fwl1['firewallContextAccessControlLists']: if control_list['direction'] == 'out': continue fwl_ctx_acl_id = control_list['id'] template = {'firewallContextAccessControlListId': fwl_ctx_acl_id, 'rules': rules} svc = self.client['Network_Firewall_Update_Request'] return svc.createObject(template) def edit_standard_fwl_rules(self, firewall_id, rules): """Edit the rules for standard firewall. :param integer firewall_id: the instance ID of the standard firewall :param dict rules: the rules to be pushed on the firewall """ rule_svc = self.client['Network_Firewall_Update_Request'] template = {'networkComponentFirewallId': firewall_id, 'rules': rules} return rule_svc.createObject(template) softlayer-python-5.4.2/SoftLayer/managers/hardware.py000066400000000000000000000704141324365065500227440ustar00rootroot00000000000000""" SoftLayer.hardware ~~~~~~~~~~~~~~~~~~ Hardware Manager/helpers :license: MIT, see LICENSE for more details. """ import logging import socket import time import SoftLayer from SoftLayer.decoration import retry from SoftLayer.managers import ordering from SoftLayer import utils LOGGER = logging.getLogger(__name__) # Invalid names are ignored due to long method names and short argument names # pylint: disable=invalid-name, no-self-use EXTRA_CATEGORIES = ['pri_ipv6_addresses', 'static_ipv6_addresses', 'sec_ip_addresses'] class HardwareManager(utils.IdentifierMixin, object): """Manage SoftLayer hardware servers. Example:: # Initialize the Manager. # env variables. These can also be specified in ~/.softlayer, # or passed directly to SoftLayer.Client() # SL_USERNAME = YOUR_USERNAME # SL_API_KEY = YOUR_API_KEY import SoftLayer client = SoftLayer.Client() mgr = SoftLayer.HardwareManager(client) See product information here: http://www.softlayer.com/bare-metal-servers :param SoftLayer.API.BaseClient client: the client instance :param SoftLayer.managers.OrderingManager ordering_manager: an optional manager to handle ordering. If none is provided, one will be auto initialized. """ def __init__(self, client, ordering_manager=None): self.client = client self.hardware = self.client['Hardware_Server'] self.account = self.client['Account'] self.resolvers = [self._get_ids_from_ip, self._get_ids_from_hostname] if ordering_manager is None: self.ordering_manager = ordering.OrderingManager(client) else: self.ordering_manager = ordering_manager def cancel_hardware(self, hardware_id, reason='unneeded', comment='', immediate=False): """Cancels the specified dedicated server. Example:: # Cancels hardware id 1234 result = mgr.cancel_hardware(hardware_id=1234) :param int hardware_id: The ID of the hardware to be cancelled. :param string reason: The reason code for the cancellation. This should come from :func:`get_cancellation_reasons`. :param string comment: An optional comment to include with the cancellation. """ # Get cancel reason reasons = self.get_cancellation_reasons() cancel_reason = reasons.get(reason, reasons['unneeded']) hw_billing = self.get_hardware(hardware_id, mask='mask[id, billingItem.id]') if 'billingItem' not in hw_billing: raise SoftLayer.SoftLayerError( "No billing item found for hardware") billing_id = hw_billing['billingItem']['id'] return self.client.call('Billing_Item', 'cancelItem', immediate, False, cancel_reason, comment, id=billing_id) @retry(logger=LOGGER) def list_hardware(self, tags=None, cpus=None, memory=None, hostname=None, domain=None, datacenter=None, nic_speed=None, public_ip=None, private_ip=None, **kwargs): """List all hardware (servers and bare metal computing instances). :param list tags: filter based on tags :param integer cpus: filter based on number of CPUS :param integer memory: filter based on amount of memory in gigabytes :param string hostname: filter based on hostname :param string domain: filter based on domain :param string datacenter: filter based on datacenter :param integer nic_speed: filter based on network speed (in MBPS) :param string public_ip: filter based on public ip address :param string private_ip: filter based on private ip address :param dict \\*\\*kwargs: response-level options (mask, limit, etc.) :returns: Returns a list of dictionaries representing the matching hardware. This list will contain both dedicated servers and bare metal computing instances Example:: # Using a custom object-mask. Will get ONLY what is specified # These will stem from the SoftLayer_Hardware_Server datatype object_mask = "mask[hostname,monitoringRobot[robotStatus]]" result = mgr.list_hardware(mask=object_mask) """ if 'mask' not in kwargs: hw_items = [ 'id', 'hostname', 'domain', 'hardwareStatusId', 'globalIdentifier', 'fullyQualifiedDomainName', 'processorPhysicalCoreAmount', 'memoryCapacity', 'primaryBackendIpAddress', 'primaryIpAddress', 'datacenter', ] server_items = [ 'activeTransaction[id, transactionStatus[friendlyName,name]]', ] kwargs['mask'] = ('[mask[%s],' ' mask(SoftLayer_Hardware_Server)[%s]]' % (','.join(hw_items), ','.join(server_items))) _filter = utils.NestedDict(kwargs.get('filter') or {}) if tags: _filter['hardware']['tagReferences']['tag']['name'] = { 'operation': 'in', 'options': [{'name': 'data', 'value': tags}], } if cpus: _filter['hardware']['processorPhysicalCoreAmount'] = ( utils.query_filter(cpus)) if memory: _filter['hardware']['memoryCapacity'] = utils.query_filter(memory) if hostname: _filter['hardware']['hostname'] = utils.query_filter(hostname) if domain: _filter['hardware']['domain'] = utils.query_filter(domain) if datacenter: _filter['hardware']['datacenter']['name'] = ( utils.query_filter(datacenter)) if nic_speed: _filter['hardware']['networkComponents']['maxSpeed'] = ( utils.query_filter(nic_speed)) if public_ip: _filter['hardware']['primaryIpAddress'] = ( utils.query_filter(public_ip)) if private_ip: _filter['hardware']['primaryBackendIpAddress'] = ( utils.query_filter(private_ip)) kwargs['filter'] = _filter.to_dict() return self.account.getHardware(**kwargs) @retry(logger=LOGGER) def get_hardware(self, hardware_id, **kwargs): """Get details about a hardware device. :param integer id: the hardware ID :returns: A dictionary containing a large amount of information about the specified server. Example:: object_mask = "mask[id,networkVlans[vlanNumber]]" # Object masks are optional result = mgr.get_hardware(hardware_id=1234,mask=object_mask) """ if 'mask' not in kwargs: kwargs['mask'] = ( 'id,' 'globalIdentifier,' 'fullyQualifiedDomainName,' 'hostname,' 'domain,' 'provisionDate,' 'hardwareStatus,' 'processorPhysicalCoreAmount,' 'memoryCapacity,' 'notes,' 'privateNetworkOnlyFlag,' 'primaryBackendIpAddress,' 'primaryIpAddress,' 'networkManagementIpAddress,' 'userData,' 'datacenter,' '''networkComponents[id, status, speed, maxSpeed, name, ipmiMacAddress, ipmiIpAddress, macAddress, primaryIpAddress, port, primarySubnet[id, netmask, broadcastAddress, networkIdentifier, gateway]],''' 'hardwareChassis[id,name],' 'activeTransaction[id, transactionStatus[friendlyName,name]],' '''operatingSystem[ softwareLicense[softwareDescription[manufacturer, name, version, referenceCode]], passwords[username,password]],''' 'billingItem[' 'id,nextInvoiceTotalRecurringAmount,' 'children[nextInvoiceTotalRecurringAmount],' 'orderItem.order.userRecord[username]' '],' 'hourlyBillingFlag,' 'tagReferences[id,tag[name,id]],' 'networkVlans[id,vlanNumber,networkSpace],' 'remoteManagementAccounts[username,password]' ) return self.hardware.getObject(id=hardware_id, **kwargs) def reload(self, hardware_id, post_uri=None, ssh_keys=None): """Perform an OS reload of a server with its current configuration. :param integer hardware_id: the instance ID to reload :param string post_url: The URI of the post-install script to run after reload :param list ssh_keys: The SSH keys to add to the root user """ config = {} if post_uri: config['customProvisionScriptUri'] = post_uri if ssh_keys: config['sshKeyIds'] = [key_id for key_id in ssh_keys] return self.hardware.reloadOperatingSystem('FORCE', config, id=hardware_id) def rescue(self, hardware_id): """Reboot a server into the a recsue kernel. :param integer instance_id: the server ID to rescue Example:: result = mgr.rescue(1234) """ return self.hardware.bootToRescueLayer(id=hardware_id) def change_port_speed(self, hardware_id, public, speed): """Allows you to change the port speed of a server's NICs. :param int hardware_id: The ID of the server :param bool public: Flag to indicate which interface to change. True (default) means the public interface. False indicates the private interface. :param int speed: The port speed to set. .. warning:: A port speed of 0 will disable the interface. Example:: #change the Public interface to 10Mbps on instance 12345 result = mgr.change_port_speed(hardware_id=12345, public=True, speed=10) # result will be True or an Exception """ if public: return self.client.call('Hardware_Server', 'setPublicNetworkInterfaceSpeed', speed, id=hardware_id) else: return self.client.call('Hardware_Server', 'setPrivateNetworkInterfaceSpeed', speed, id=hardware_id) def place_order(self, **kwargs): """Places an order for a piece of hardware. See get_create_options() for valid arguments. :param string size: server size name or presetId :param string hostname: server hostname :param string domain: server domain name :param string location: location (datacenter) name :param string os: operating system name :param int port_speed: Port speed in Mbps :param list ssh_keys: list of ssh key ids :param string post_uri: The URI of the post-install script to run after reload :param boolean hourly: True if using hourly pricing (default). False for monthly. :param boolean no_public: True if this server should only have private interfaces :param list extras: List of extra feature names """ create_options = self._generate_create_dict(**kwargs) return self.client['Product_Order'].placeOrder(create_options) def verify_order(self, **kwargs): """Verifies an order for a piece of hardware. See :func:`place_order` for a list of available options. """ create_options = self._generate_create_dict(**kwargs) return self.client['Product_Order'].verifyOrder(create_options) def get_cancellation_reasons(self): """Returns a dictionary of valid cancellation reasons. These can be used when cancelling a dedicated server via :func:`cancel_hardware`. """ return { 'unneeded': 'No longer needed', 'closing': 'Business closing down', 'cost': 'Server / Upgrade Costs', 'migrate_larger': 'Migrating to larger server', 'migrate_smaller': 'Migrating to smaller server', 'datacenter': 'Migrating to a different SoftLayer datacenter', 'performance': 'Network performance / latency', 'support': 'Support response / timing', 'sales': 'Sales process / upgrades', 'moving': 'Moving to competitor', } @retry(logger=LOGGER) def get_create_options(self): """Returns valid options for ordering hardware.""" package = self._get_package() # Locations locations = [] for region in package['regions']: locations.append({ 'name': region['location']['location']['longName'], 'key': region['location']['location']['name'], }) # Sizes sizes = [] for preset in package['activePresets']: sizes.append({ 'name': preset['description'], 'key': preset['keyName'] }) # Operating systems operating_systems = [] for item in package['items']: if item['itemCategory']['categoryCode'] == 'os': operating_systems.append({ 'name': item['softwareDescription']['longDescription'], 'key': item['softwareDescription']['referenceCode'], }) # Port speeds port_speeds = [] for item in package['items']: if all([item['itemCategory']['categoryCode'] == 'port_speed', # Hide private options not _is_private_port_speed_item(item), # Hide unbonded options _is_bonded(item)]): port_speeds.append({ 'name': item['description'], 'key': item['capacity'], }) # Extras extras = [] for item in package['items']: if item['itemCategory']['categoryCode'] in EXTRA_CATEGORIES: extras.append({ 'name': item['description'], 'key': item['keyName'] }) return { 'locations': locations, 'sizes': sizes, 'operating_systems': operating_systems, 'port_speeds': port_speeds, 'extras': extras, } @retry(logger=LOGGER) def _get_package(self): """Get the package related to simple hardware ordering.""" mask = ''' items[ keyName, capacity, description, attributes[id,attributeTypeKeyName], itemCategory[id,categoryCode], softwareDescription[id,referenceCode,longDescription], prices ], activePresets, regions[location[location[priceGroups]]] ''' package_keyname = 'BARE_METAL_SERVER' package = self.ordering_manager.get_package_by_key(package_keyname, mask=mask) return package def _generate_create_dict(self, size=None, hostname=None, domain=None, location=None, os=None, port_speed=None, ssh_keys=None, post_uri=None, hourly=True, no_public=False, extras=None): """Translates arguments into a dictionary for creating a server.""" extras = extras or [] package = self._get_package() location = _get_location(package, location) prices = [] for category in ['pri_ip_addresses', 'vpn_management', 'remote_management']: prices.append(_get_default_price_id(package['items'], option=category, hourly=hourly, location=location)) prices.append(_get_os_price_id(package['items'], os, location=location)) prices.append(_get_bandwidth_price_id(package['items'], hourly=hourly, no_public=no_public, location=location)) prices.append(_get_port_speed_price_id(package['items'], port_speed, no_public, location=location)) for extra in extras: prices.append(_get_extra_price_id(package['items'], extra, hourly, location=location)) hardware = { 'hostname': hostname, 'domain': domain, } order = { 'hardware': [hardware], 'location': location['keyname'], 'prices': [{'id': price} for price in prices], 'packageId': package['id'], 'presetId': _get_preset_id(package, size), 'useHourlyPricing': hourly, } if post_uri: order['provisionScripts'] = [post_uri] if ssh_keys: order['sshKeys'] = [{'sshKeyIds': ssh_keys}] return order def _get_ids_from_hostname(self, hostname): """Returns list of matching hardware IDs for a given hostname.""" results = self.list_hardware(hostname=hostname, mask="id") return [result['id'] for result in results] def _get_ids_from_ip(self, ip): # pylint: disable=inconsistent-return-statements """Returns list of matching hardware IDs for a given ip address.""" try: # Does it look like an ip address? socket.inet_aton(ip) except socket.error: return [] # Find the server via ip address. First try public ip, then private results = self.list_hardware(public_ip=ip, mask="id") if results: return [result['id'] for result in results] results = self.list_hardware(private_ip=ip, mask="id") if results: return [result['id'] for result in results] def edit(self, hardware_id, userdata=None, hostname=None, domain=None, notes=None, tags=None): """Edit hostname, domain name, notes, user data of the hardware. Parameters set to None will be ignored and not attempted to be updated. :param integer hardware_id: the instance ID to edit :param string userdata: user data on the hardware to edit. If none exist it will be created :param string hostname: valid hostname :param string domain: valid domain name :param string notes: notes about this particular hardware :param string tags: tags to set on the hardware as a comma separated list. Use the empty string to remove all tags. Example:: # Change the hostname on instance 12345 to 'something' result = mgr.edit(hardware_id=12345 , hostname="something") #result will be True or an Exception """ obj = {} if userdata: self.hardware.setUserMetadata([userdata], id=hardware_id) if tags is not None: self.hardware.setTags(tags, id=hardware_id) if hostname: obj['hostname'] = hostname if domain: obj['domain'] = domain if notes: obj['notes'] = notes if not obj: return True return self.hardware.editObject(obj, id=hardware_id) def update_firmware(self, hardware_id, ipmi=True, raid_controller=True, bios=True, hard_drive=True): """Update hardware firmware. This will cause the server to be unavailable for ~20 minutes. :param int hardware_id: The ID of the hardware to have its firmware updated. :param bool ipmi: Update the ipmi firmware. :param bool raid_controller: Update the raid controller firmware. :param bool bios: Update the bios firmware. :param bool hard_drive: Update the hard drive firmware. Example:: # Check the servers active transactions to see progress result = mgr.update_firmware(hardware_id=1234) """ return self.hardware.createFirmwareUpdateTransaction( bool(ipmi), bool(raid_controller), bool(bios), bool(hard_drive), id=hardware_id) def wait_for_ready(self, instance_id, limit=14400, delay=10, pending=False): """Determine if a Server is ready. A server is ready when no transactions are running on it. :param int instance_id: The instance ID with the pending transaction :param int limit: The maximum amount of seconds to wait. :param int delay: The number of seconds to sleep before checks. Defaults to 10. """ now = time.time() until = now + limit mask = "mask[id, lastOperatingSystemReload[id], activeTransaction, provisionDate]" instance = self.get_hardware(instance_id, mask=mask) while now <= until: if utils.is_ready(instance, pending): return True transaction = utils.lookup(instance, 'activeTransaction', 'transactionStatus', 'friendlyName') snooze = min(delay, until - now) LOGGER.info("%s - %d not ready. Auto retry in %ds", transaction, instance_id, snooze) time.sleep(snooze) instance = self.get_hardware(instance_id, mask=mask) now = time.time() LOGGER.info("Waiting for %d expired.", instance_id) return False def _get_extra_price_id(items, key_name, hourly, location): """Returns a price id attached to item with the given key_name.""" for item in items: if utils.lookup(item, 'keyName') != key_name: continue for price in item['prices']: if not _matches_billing(price, hourly): continue if not _matches_location(price, location): continue return price['id'] raise SoftLayer.SoftLayerError( "Could not find valid price for extra option, '%s'" % key_name) def _get_default_price_id(items, option, hourly, location): """Returns a 'free' price id given an option.""" for item in items: if utils.lookup(item, 'itemCategory', 'categoryCode') != option: continue for price in item['prices']: if all([float(price.get('hourlyRecurringFee', 0)) == 0.0, float(price.get('recurringFee', 0)) == 0.0, _matches_billing(price, hourly), _matches_location(price, location)]): return price['id'] raise SoftLayer.SoftLayerError( "Could not find valid price for '%s' option" % option) def _get_bandwidth_price_id(items, hourly=True, no_public=False, location=None): """Choose a valid price id for bandwidth.""" # Prefer pay-for-use data transfer with hourly for item in items: capacity = float(item.get('capacity', 0)) # Hourly and private only do pay-as-you-go bandwidth if any([utils.lookup(item, 'itemCategory', 'categoryCode') != 'bandwidth', (hourly or no_public) and capacity != 0.0, not (hourly or no_public) and capacity == 0.0]): continue for price in item['prices']: if not _matches_billing(price, hourly): continue if not _matches_location(price, location): continue return price['id'] raise SoftLayer.SoftLayerError( "Could not find valid price for bandwidth option") def _get_os_price_id(items, os, location): """Returns the price id matching.""" for item in items: if any([utils.lookup(item, 'itemCategory', 'categoryCode') != 'os', utils.lookup(item, 'softwareDescription', 'referenceCode') != os]): continue for price in item['prices']: if not _matches_location(price, location): continue return price['id'] raise SoftLayer.SoftLayerError("Could not find valid price for os: '%s'" % os) def _get_port_speed_price_id(items, port_speed, no_public, location): """Choose a valid price id for port speed.""" for item in items: if utils.lookup(item, 'itemCategory', 'categoryCode') != 'port_speed': continue # Check for correct capacity and if the item matches private only if any([int(utils.lookup(item, 'capacity')) != port_speed, _is_private_port_speed_item(item) != no_public, not _is_bonded(item)]): continue for price in item['prices']: if not _matches_location(price, location): continue return price['id'] raise SoftLayer.SoftLayerError( "Could not find valid price for port speed: '%s'" % port_speed) def _matches_billing(price, hourly): """Return True if the price object is hourly and/or monthly.""" return any([hourly and price.get('hourlyRecurringFee') is not None, not hourly and price.get('recurringFee') is not None]) def _matches_location(price, location): """Return True if the price object matches the location.""" # the price has no location restriction if not price.get('locationGroupId'): return True # Check to see if any of the location groups match the location group # of this price object for group in location['location']['location']['priceGroups']: if group['id'] == price['locationGroupId']: return True return False def _is_private_port_speed_item(item): """Determine if the port speed item is private network only.""" for attribute in item['attributes']: if attribute['attributeTypeKeyName'] == 'IS_PRIVATE_NETWORK_ONLY': return True return False def _is_bonded(item): """Determine if the item refers to a bonded port.""" for attribute in item['attributes']: if attribute['attributeTypeKeyName'] == 'NON_LACP': return False return True def _get_location(package, location): """Get the longer key with a short location name.""" for region in package['regions']: if region['location']['location']['name'] == location: return region raise SoftLayer.SoftLayerError("Could not find valid location for: '%s'" % location) def _get_preset_id(package, size): """Get the preset id given the keyName of the preset.""" for preset in package['activePresets']: if preset['keyName'] == size or preset['id'] == size: return preset['id'] raise SoftLayer.SoftLayerError("Could not find valid size for: '%s'" % size) softlayer-python-5.4.2/SoftLayer/managers/image.py000066400000000000000000000116071324365065500222300ustar00rootroot00000000000000""" SoftLayer.image ~~~~~~~~~~~~~~~ Image Manager/helpers :license: MIT, see LICENSE for more details. """ from SoftLayer import utils IMAGE_MASK = ('id,accountId,name,globalIdentifier,blockDevices,parentId,' 'createDate,transaction') class ImageManager(utils.IdentifierMixin, object): """Manages SoftLayer server images. See product information here: https://knowledgelayer.softlayer.com/topic/image-templates :param SoftLayer.API.BaseClient client: the client instance """ def __init__(self, client): self.client = client self.vgbdtg = self.client['Virtual_Guest_Block_Device_Template_Group'] self.resolvers = [self._get_ids_from_name_public, self._get_ids_from_name_private] def get_image(self, image_id, **kwargs): """Get details about an image. :param int image: The ID of the image. :param dict \\*\\*kwargs: response-level options (mask, limit, etc.) """ if 'mask' not in kwargs: kwargs['mask'] = IMAGE_MASK return self.vgbdtg.getObject(id=image_id, **kwargs) def delete_image(self, image_id): """Deletes the specified image. :param int image_id: The ID of the image. """ self.vgbdtg.deleteObject(id=image_id) def list_private_images(self, guid=None, name=None, **kwargs): """List all private images. :param string guid: filter based on GUID :param string name: filter based on name :param dict \\*\\*kwargs: response-level options (mask, limit, etc.) """ if 'mask' not in kwargs: kwargs['mask'] = IMAGE_MASK _filter = utils.NestedDict(kwargs.get('filter') or {}) if name: _filter['privateBlockDeviceTemplateGroups']['name'] = ( utils.query_filter(name)) if guid: _filter['privateBlockDeviceTemplateGroups']['globalIdentifier'] = ( utils.query_filter(guid)) kwargs['filter'] = _filter.to_dict() account = self.client['Account'] return account.getPrivateBlockDeviceTemplateGroups(**kwargs) def list_public_images(self, guid=None, name=None, **kwargs): """List all public images. :param string guid: filter based on GUID :param string name: filter based on name :param dict \\*\\*kwargs: response-level options (mask, limit, etc.) """ if 'mask' not in kwargs: kwargs['mask'] = IMAGE_MASK _filter = utils.NestedDict(kwargs.get('filter') or {}) if name: _filter['name'] = utils.query_filter(name) if guid: _filter['globalIdentifier'] = utils.query_filter(guid) kwargs['filter'] = _filter.to_dict() return self.vgbdtg.getPublicImages(**kwargs) def _get_ids_from_name_public(self, name): """Get public images which match the given name.""" results = self.list_public_images(name=name) return [result['id'] for result in results] def _get_ids_from_name_private(self, name): """Get private images which match the given name.""" results = self.list_private_images(name=name) return [result['id'] for result in results] def edit(self, image_id, name=None, note=None, tag=None): """Edit image related details. :param int image_id: The ID of the image :param string name: Name of the Image. :param string note: Note of the image. :param string tag: Tags of the image to be updated to. """ obj = {} if name: obj['name'] = name if note: obj['note'] = note if obj: self.vgbdtg.editObject(obj, id=image_id) if tag: self.vgbdtg.setTags(str(tag), id=image_id) return bool(name or note or tag) def import_image_from_uri(self, name, uri, os_code=None, note=None): """Import a new image from object storage. :param string name: Name of the new image :param string uri: The URI for an object storage object (.vhd/.iso file) of the format: swift://@// :param string os_code: The reference code of the operating system :param string note: Note to add to the image """ return self.vgbdtg.createFromExternalSource({ 'name': name, 'note': note, 'operatingSystemReferenceCode': os_code, 'uri': uri, }) def export_image_to_uri(self, image_id, uri): """Export image into the given object storage :param int image_id: The ID of the image :param string uri: The URI for object storage of the format swift://@// """ return self.vgbdtg.copyToExternalSource({'uri': uri}, id=image_id) softlayer-python-5.4.2/SoftLayer/managers/ipsec.py000066400000000000000000000332521324365065500222510ustar00rootroot00000000000000""" SoftLayer.ipsec ~~~~~~~~~~~~~~~~~~ IPSec VPN Manager :license: MIT, see LICENSE for more details. """ from SoftLayer.exceptions import SoftLayerAPIError from SoftLayer import utils class IPSECManager(utils.IdentifierMixin, object): """Manage SoftLayer IPSEC VPN tunnel contexts. This provides helpers to manage IPSEC contexts, private and remote subnets, and NAT translations. :param SoftLayer.API.BaseClient client: the client instance :param SoftLayer.API.BaseClient account: account service client :param SoftLayer.API.BaseClient context: tunnel context client :param SoftLayer.API.BaseClient customer_subnet: remote subnet client """ def __init__(self, client): self.client = client self.account = client['Account'] self.context = client['Network_Tunnel_Module_Context'] self.remote_subnet = client['Network_Customer_Subnet'] def add_internal_subnet(self, context_id, subnet_id): """Add an internal subnet to a tunnel context. :param int context_id: The id-value representing the context instance. :param int subnet_id: The id-value representing the internal subnet. :return bool: True if internal subnet addition was successful. """ return self.context.addPrivateSubnetToNetworkTunnel(subnet_id, id=context_id) def add_remote_subnet(self, context_id, subnet_id): """Adds a remote subnet to a tunnel context. :param int context_id: The id-value representing the context instance. :param int subnet_id: The id-value representing the remote subnet. :return bool: True if remote subnet addition was successful. """ return self.context.addCustomerSubnetToNetworkTunnel(subnet_id, id=context_id) def add_service_subnet(self, context_id, subnet_id): """Adds a service subnet to a tunnel context. :param int context_id: The id-value representing the context instance. :param int subnet_id: The id-value representing the service subnet. :return bool: True if service subnet addition was successful. """ return self.context.addServiceSubnetToNetworkTunnel(subnet_id, id=context_id) def apply_configuration(self, context_id): """Requests network configuration for a tunnel context. :param int context_id: The id-value representing the context instance. :return bool: True if the configuration request was successfully queued. """ return self.context.applyConfigurationsToDevice(id=context_id) def create_remote_subnet(self, account_id, identifier, cidr): """Creates a remote subnet on the given account. :param string account_id: The account identifier. :param string identifier: The network identifier of the remote subnet. :param string cidr: The CIDR value of the remote subnet. :return dict: Mapping of properties for the new remote subnet. """ return self.remote_subnet.createObject({ 'accountId': account_id, 'cidr': cidr, 'networkIdentifier': identifier }) def create_translation(self, context_id, static_ip, remote_ip, notes): """Creates an address translation on a tunnel context/ :param int context_id: The id-value representing the context instance. :param string static_ip: The IP address value representing the internal side of the translation entry, :param string remote_ip: The IP address value representing the remote side of the translation entry, :param string notes: The notes to supply with the translation entry, :return dict: Mapping of properties for the new translation entry. """ return self.context.createAddressTranslation({ 'customerIpAddress': remote_ip, 'internalIpAddress': static_ip, 'notes': notes }, id=context_id) def delete_remote_subnet(self, subnet_id): """Deletes a remote subnet from the current account. :param string subnet_id: The id-value representing the remote subnet. :return bool: True if subnet deletion was successful. """ return self.remote_subnet.deleteObject(id=subnet_id) def get_tunnel_context(self, context_id, **kwargs): """Retrieves the network tunnel context instance. :param int context_id: The id-value representing the context instance. :return dict: Mapping of properties for the tunnel context. :raise SoftLayerAPIError: If a context cannot be found. """ _filter = utils.NestedDict(kwargs.get('filter') or {}) _filter['networkTunnelContexts']['id'] = utils.query_filter(context_id) kwargs['filter'] = _filter.to_dict() contexts = self.account.getNetworkTunnelContexts(**kwargs) if len(contexts) == 0: raise SoftLayerAPIError('SoftLayer_Exception_ObjectNotFound', 'Unable to find object with id of \'{}\'' .format(context_id)) return contexts[0] def get_translation(self, context_id, translation_id): """Retrieves a translation entry for the given id values. :param int context_id: The id-value representing the context instance. :param int translation_id: The id-value representing the translation instance. :return dict: Mapping of properties for the translation entry. :raise SoftLayerAPIError: If a translation cannot be found. """ translation = next((x for x in self.get_translations(context_id) if x['id'] == translation_id), None) if translation is None: raise SoftLayerAPIError('SoftLayer_Exception_ObjectNotFound', 'Unable to find object with id of \'{}\'' .format(translation_id)) return translation def get_translations(self, context_id): """Retrieves all translation entries for a tunnel context. :param int context_id: The id-value representing the context instance. :return list(dict): Translations associated with the given context """ _mask = ('[mask[addressTranslations[customerIpAddressRecord,' 'internalIpAddressRecord]]]') context = self.get_tunnel_context(context_id, mask=_mask) # Pull the internal and remote IP addresses into the translation for translation in context.get('addressTranslations', []): remote_ip = translation.get('customerIpAddressRecord', {}) internal_ip = translation.get('internalIpAddressRecord', {}) translation['customerIpAddress'] = remote_ip.get('ipAddress', '') translation['internalIpAddress'] = internal_ip.get('ipAddress', '') translation.pop('customerIpAddressRecord', None) translation.pop('internalIpAddressRecord', None) return context['addressTranslations'] def get_tunnel_contexts(self, **kwargs): """Retrieves network tunnel module context instances. :return list(dict): Contexts associated with the current account. """ return self.account.getNetworkTunnelContexts(**kwargs) def remove_internal_subnet(self, context_id, subnet_id): """Remove an internal subnet from a tunnel context. :param int context_id: The id-value representing the context instance. :param int subnet_id: The id-value representing the internal subnet. :return bool: True if internal subnet removal was successful. """ return self.context.removePrivateSubnetFromNetworkTunnel(subnet_id, id=context_id) def remove_remote_subnet(self, context_id, subnet_id): """Removes a remote subnet from a tunnel context. :param int context_id: The id-value representing the context instance. :param int subnet_id: The id-value representing the remote subnet. :return bool: True if remote subnet removal was successful. """ return self.context.removeCustomerSubnetFromNetworkTunnel(subnet_id, id=context_id) def remove_service_subnet(self, context_id, subnet_id): """Removes a service subnet from a tunnel context. :param int context_id: The id-value representing the context instance. :param int subnet_id: The id-value representing the service subnet. :return bool: True if service subnet removal was successful. """ return self.context.removeServiceSubnetFromNetworkTunnel(subnet_id, id=context_id) def remove_translation(self, context_id, translation_id): """Removes a translation entry from a tunnel context. :param int context_id: The id-value representing the context instance. :param int translation_id: The id-value representing the translation. :return bool: True if translation entry removal was successful. """ return self.context.deleteAddressTranslation(translation_id, id=context_id) def update_translation(self, context_id, translation_id, static_ip=None, remote_ip=None, notes=None): """Updates an address translation entry using the given values. :param int context_id: The id-value representing the context instance. :param dict template: A key-value mapping of translation properties. :param string static_ip: The static IP address value to update. :param string remote_ip: The remote IP address value to update. :param string notes: The notes value to update. :return bool: True if the update was successful. """ translation = self.get_translation(context_id, translation_id) if static_ip is not None: translation['internalIpAddress'] = static_ip translation.pop('internalIpAddressId', None) if remote_ip is not None: translation['customerIpAddress'] = remote_ip translation.pop('customerIpAddressId', None) if notes is not None: translation['notes'] = notes # todo: Update this signature to return the updated translation # once internal and customer IP addresses can be fetched # and set on the translation object, i.e. that which is # currently being handled in get_translations self.context.editAddressTranslation(translation, id=context_id) return True def update_tunnel_context(self, context_id, friendly_name=None, remote_peer=None, preshared_key=None, phase1_auth=None, phase1_crypto=None, phase1_dh=None, phase1_key_ttl=None, phase2_auth=None, phase2_crypto=None, phase2_dh=None, phase2_forward_secrecy=None, phase2_key_ttl=None): """Updates a tunnel context using the given values. :param string context_id: The id-value representing the context. :param string friendly_name: The friendly name value to update. :param string remote_peer: The remote peer IP address value to update. :param string preshared_key: The preshared key value to update. :param string phase1_auth: The phase 1 authentication value to update. :param string phase1_crypto: The phase 1 encryption value to update. :param string phase1_dh: The phase 1 diffie hellman group value to update. :param string phase1_key_ttl: The phase 1 key life value to update. :param string phase2_auth: The phase 2 authentication value to update. :param string phase2_crypto: The phase 2 encryption value to update. :param string phase2_df: The phase 2 diffie hellman group value to update. :param string phase2_forward_secriecy: The phase 2 perfect forward secrecy value to update. :param string phase2_key_ttl: The phase 2 key life value to update. :return bool: True if the update was successful. """ context = self.get_tunnel_context(context_id) if friendly_name is not None: context['friendlyName'] = friendly_name if remote_peer is not None: context['customerPeerIpAddress'] = remote_peer if preshared_key is not None: context['presharedKey'] = preshared_key if phase1_auth is not None: context['phaseOneAuthentication'] = phase1_auth if phase1_crypto is not None: context['phaseOneEncryption'] = phase1_crypto if phase1_dh is not None: context['phaseOneDiffieHellmanGroup'] = phase1_dh if phase1_key_ttl is not None: context['phaseOneKeylife'] = phase1_key_ttl if phase2_auth is not None: context['phaseTwoAuthentication'] = phase2_auth if phase2_crypto is not None: context['phaseTwoEncryption'] = phase2_crypto if phase2_dh is not None: context['phaseTwoDiffieHellmanGroup'] = phase2_dh if phase2_forward_secrecy is not None: context['phaseTwoPerfectForwardSecrecy'] = phase2_forward_secrecy if phase2_key_ttl is not None: context['phaseTwoKeylife'] = phase2_key_ttl return self.context.editObject(context, id=context_id) softlayer-python-5.4.2/SoftLayer/managers/load_balancer.py000066400000000000000000000312531324365065500237130ustar00rootroot00000000000000""" SoftLayer.load_balancer ~~~~~~~~~~~~~~~~~~~~~~~ Load Balancer Manager/helpers :license: MIT, see LICENSE for more details. """ from SoftLayer import utils class LoadBalancerManager(utils.IdentifierMixin, object): """Manages SoftLayer load balancers. See product information here: http://www.softlayer.com/load-balancing :param SoftLayer.API.BaseClient client: the client instance """ def __init__(self, client): self.client = client self.account = self.client['Account'] self.prod_pkg = self.client['Product_Package'] self.lb_svc = self.client['Network_Application_Delivery_Controller_' 'LoadBalancer_VirtualIpAddress'] def get_lb_pkgs(self): """Retrieves the local load balancer packages. :returns: A dictionary containing the load balancer packages """ _filter = {'items': {'description': utils.query_filter('*Load Balancer*')}} packages = self.prod_pkg.getItems(id=0, filter=_filter) pkgs = [] for package in packages: if not package['description'].startswith('Global'): pkgs.append(package) return pkgs def get_hc_types(self): """Retrieves the health check type values. :returns: A dictionary containing the health check types """ svc = self.client['Network_Application_Delivery_Controller_' 'LoadBalancer_Health_Check_Type'] return svc.getAllObjects() def get_routing_methods(self): """Retrieves the load balancer routing methods. :returns: A dictionary containing the load balancer routing methods """ svc = self.client['Network_Application_Delivery_Controller_' 'LoadBalancer_Routing_Method'] return svc.getAllObjects() def get_routing_types(self): """Retrieves the load balancer routing types. :returns: A dictionary containing the load balancer routing types """ svc = self.client['Network_Application_Delivery_Controller_' 'LoadBalancer_Routing_Type'] return svc.getAllObjects() def _get_location(self, datacenter_name): """Returns the location of the specified datacenter. :param string datacenter_name: The datacenter to create the loadbalancer in :returns: the location id of the given datacenter """ datacenters = self.client['Location'].getDataCenters() for datacenter in datacenters: if datacenter['name'] == datacenter_name: return datacenter['id'] return 'FIRST_AVAILABLE' def cancel_lb(self, loadbal_id): """Cancels the specified load balancer. :param int loadbal_id: Load Balancer ID to be cancelled. """ lb_billing = self.lb_svc.getBillingItem(id=loadbal_id) billing_id = lb_billing['id'] billing_item = self.client['Billing_Item'] return billing_item.cancelService(id=billing_id) def add_local_lb(self, price_item_id, datacenter): """Creates a local load balancer in the specified data center. :param int price_item_id: The price item ID for the load balancer :param string datacenter: The datacenter to create the loadbalancer in :returns: A dictionary containing the product order """ product_order = { 'complexType': 'SoftLayer_Container_Product_Order_Network_' 'LoadBalancer', 'quantity': 1, 'packageId': 0, "location": self._get_location(datacenter), 'prices': [{'id': price_item_id}] } return self.client['Product_Order'].placeOrder(product_order) def get_local_lbs(self): """Returns a list of all local load balancers on the account. :returns: A list of all local load balancers on the current account. """ mask = 'loadBalancerHardware[datacenter],ipAddress' return self.account.getAdcLoadBalancers(mask=mask) def get_local_lb(self, loadbal_id, **kwargs): """Returns a specified local load balancer given the id. :param int loadbal_id: The id of the load balancer to retrieve :returns: A dictionary containing the details of the load balancer """ if 'mask' not in kwargs: kwargs['mask'] = ('loadBalancerHardware[datacenter], ' 'ipAddress, virtualServers[serviceGroups' '[routingMethod,routingType,services' '[healthChecks[type], groupReferences,' ' ipAddress]]]') return self.lb_svc.getObject(id=loadbal_id, **kwargs) def delete_service(self, service_id): """Deletes a service from the loadbal_id. :param int service_id: The id of the service to delete """ svc = self.client['Network_Application_Delivery_Controller_' 'LoadBalancer_Service'] return svc.deleteObject(id=service_id) def delete_service_group(self, group_id): """Deletes a service group from the loadbal_id. :param int group_id: The id of the service group to delete """ svc = self.client['Network_Application_Delivery_Controller_' 'LoadBalancer_VirtualServer'] return svc.deleteObject(id=group_id) def toggle_service_status(self, service_id): """Toggles the service status. :param int service_id: The id of the service to delete """ svc = self.client['Network_Application_Delivery_Controller_' 'LoadBalancer_Service'] return svc.toggleStatus(id=service_id) def edit_service(self, loadbal_id, service_id, ip_address_id=None, port=None, enabled=None, hc_type=None, weight=None): """Edits an existing service properties. :param int loadbal_id: The id of the loadbal where the service resides :param int service_id: The id of the service to edit :param string ip_address: The ip address of the service :param int port: the port of the service :param bool enabled: enable or disable the search :param int hc_type: The health check type :param int weight: the weight to give to the service """ _filter = { 'virtualServers': { 'serviceGroups': { 'services': {'id': utils.query_filter(service_id)}}}} mask = 'serviceGroups[services[groupReferences,healthChecks]]' virtual_servers = self.lb_svc.getVirtualServers(id=loadbal_id, filter=_filter, mask=mask) for service in virtual_servers[0]['serviceGroups'][0]['services']: if service['id'] == service_id: if enabled is not None: service['enabled'] = int(enabled) if port is not None: service['port'] = port if weight is not None: service['groupReferences'][0]['weight'] = weight if hc_type is not None: service['healthChecks'][0]['healthCheckTypeId'] = hc_type if ip_address_id is not None: service['ipAddressId'] = ip_address_id template = {'virtualServers': list(virtual_servers)} load_balancer = self.lb_svc.editObject(template, id=loadbal_id) return load_balancer def add_service(self, loadbal_id, service_group_id, ip_address_id, port=80, enabled=True, hc_type=21, weight=1): """Adds a new service to the service group. :param int loadbal_id: The id of the loadbal where the service resides :param int service_group_id: The group to add the service to :param int ip_address id: The ip address ID of the service :param int port: the port of the service :param bool enabled: Enable or disable the service :param int hc_type: The health check type :param int weight: the weight to give to the service """ kwargs = utils.NestedDict({}) kwargs['mask'] = ('virtualServers[' 'serviceGroups[services[groupReferences]]]') load_balancer = self.lb_svc.getObject(id=loadbal_id, **kwargs) virtual_servers = load_balancer['virtualServers'] for virtual_server in virtual_servers: if virtual_server['id'] == service_group_id: service_template = { 'enabled': int(enabled), 'port': port, 'ipAddressId': ip_address_id, 'healthChecks': [ { 'healthCheckTypeId': hc_type } ], 'groupReferences': [ { 'weight': weight } ] } services = virtual_server['serviceGroups'][0]['services'] services.append(service_template) return self.lb_svc.editObject(load_balancer, id=loadbal_id) def add_service_group(self, lb_id, allocation=100, port=80, routing_type=2, routing_method=10): """Adds a new service group to the load balancer. :param int loadbal_id: The id of the loadbal where the service resides :param int allocation: percent of connections to allocate toward the group :param int port: the port of the service group :param int routing_type: the routing type to set on the service group :param int routing_method: The routing method to set on the group """ mask = 'virtualServers[serviceGroups[services[groupReferences]]]' load_balancer = self.lb_svc.getObject(id=lb_id, mask=mask) service_template = { 'port': port, 'allocation': allocation, 'serviceGroups': [ { 'routingTypeId': routing_type, 'routingMethodId': routing_method } ] } load_balancer['virtualServers'].append(service_template) return self.lb_svc.editObject(load_balancer, id=lb_id) def edit_service_group(self, loadbal_id, group_id, allocation=None, port=None, routing_type=None, routing_method=None): """Edit an existing service group. :param int loadbal_id: The id of the loadbal where the service resides :param int group_id: The id of the service group :param int allocation: the % of connections to allocate to the group :param int port: the port of the service group :param int routing_type: the routing type to set on the service group :param int routing_method: The routing method to set on the group """ mask = 'virtualServers[serviceGroups[services[groupReferences]]]' load_balancer = self.lb_svc.getObject(id=loadbal_id, mask=mask) virtual_servers = load_balancer['virtualServers'] for virtual_server in virtual_servers: if virtual_server['id'] == group_id: service_group = virtual_server['serviceGroups'][0] if allocation is not None: virtual_server['allocation'] = allocation if port is not None: virtual_server['port'] = port if routing_type is not None: service_group['routingTypeId'] = routing_type if routing_method is not None: service_group['routingMethodId'] = routing_method break return self.lb_svc.editObject(load_balancer, id=loadbal_id) def reset_service_group(self, loadbal_id, group_id): """Resets all the connections on the service group. :param int loadbal_id: The id of the loadbal :param int group_id: The id of the service group to reset """ _filter = {'virtualServers': {'id': utils.query_filter(group_id)}} virtual_servers = self.lb_svc.getVirtualServers(id=loadbal_id, filter=_filter, mask='serviceGroups') actual_id = virtual_servers[0]['serviceGroups'][0]['id'] svc = self.client['Network_Application_Delivery_Controller' '_LoadBalancer_Service_Group'] return svc.kickAllConnections(id=actual_id) softlayer-python-5.4.2/SoftLayer/managers/messaging.py000066400000000000000000000310411324365065500231150ustar00rootroot00000000000000""" SoftLayer.messaging ~~~~~~~~~~~~~~~~~~~ Manager for the SoftLayer Message Queue service :license: MIT, see LICENSE for more details. """ import json import requests.auth from SoftLayer import consts from SoftLayer import exceptions # pylint: disable=no-self-use ENDPOINTS = { "dal05": { "public": "dal05.mq.softlayer.net", "private": "dal05.mq.service.networklayer.com" } } class QueueAuth(requests.auth.AuthBase): """SoftLayer Message Queue authentication for requests. :param endpoint: endpoint URL :param username: SoftLayer username :param api_key: SoftLayer API Key :param auth_token: (optional) Starting auth token """ def __init__(self, endpoint, username, api_key, auth_token=None): self.endpoint = endpoint self.username = username self.api_key = api_key self.auth_token = auth_token def auth(self): """Authenticate.""" headers = { 'X-Auth-User': self.username, 'X-Auth-Key': self.api_key } resp = requests.post(self.endpoint, headers=headers) if resp.ok: self.auth_token = resp.headers['X-Auth-Token'] else: raise exceptions.Unauthenticated("Error while authenticating: %s" % resp.status_code) def handle_error(self, resp, **_): """Handle errors.""" resp.request.deregister_hook('response', self.handle_error) if resp.status_code == 503: resp.connection.send(resp.request) elif resp.status_code == 401: self.auth() resp.request.headers['X-Auth-Token'] = self.auth_token resp.connection.send(resp.request) def __call__(self, resp): """Attach auth token to the request. Do authentication if an auth token isn't available """ if not self.auth_token: self.auth() resp.register_hook('response', self.handle_error) resp.headers['X-Auth-Token'] = self.auth_token return resp class MessagingManager(object): """Manage SoftLayer Message Queue accounts. See product information here: http://www.softlayer.com/message-queue :param SoftLayer.API.BaseClient client: the client instance """ def __init__(self, client): self.client = client def list_accounts(self, **kwargs): """List message queue accounts. :param dict \\*\\*kwargs: response-level options (mask, limit, etc.) """ if 'mask' not in kwargs: items = [ 'id', 'name', 'status', 'nodes', ] kwargs['mask'] = "mask[%s]" % ','.join(items) return self.client['Account'].getMessageQueueAccounts(**kwargs) def get_endpoint(self, datacenter=None, network=None): """Get a message queue endpoint based on datacenter/network type. :param datacenter: datacenter code :param network: network ('public' or 'private') """ if datacenter is None: datacenter = 'dal05' if network is None: network = 'public' try: host = ENDPOINTS[datacenter][network] return "https://%s" % host except KeyError: raise TypeError('Invalid endpoint %s/%s' % (datacenter, network)) def get_endpoints(self): """Get all known message queue endpoints.""" return ENDPOINTS def get_connection(self, account_id, datacenter=None, network=None): """Get connection to Message Queue Service. :param account_id: Message Queue Account id :param datacenter: Datacenter code :param network: network ('public' or 'private') """ if any([not self.client.auth, not getattr(self.client.auth, 'username', None), not getattr(self.client.auth, 'api_key', None)]): raise exceptions.SoftLayerError( 'Client instance auth must be BasicAuthentication.') client = MessagingConnection( account_id, endpoint=self.get_endpoint(datacenter, network)) client.authenticate(self.client.auth.username, self.client.auth.api_key) return client def ping(self, datacenter=None, network=None): """Ping a message queue endpoint.""" resp = requests.get('%s/v1/ping' % self.get_endpoint(datacenter, network)) resp.raise_for_status() return True class MessagingConnection(object): """Message Queue Service Connection. :param account_id: Message Queue Account id :param endpoint: Endpoint URL """ def __init__(self, account_id, endpoint=None): self.account_id = account_id self.endpoint = endpoint self.auth = None def _make_request(self, method, path, **kwargs): """Make request. Generally not called directly. :param method: HTTP Method :param path: resource Path :param dict \\*\\*kwargs: extra request arguments """ headers = { 'Content-Type': 'application/json', 'User-Agent': consts.USER_AGENT, } headers.update(kwargs.get('headers', {})) kwargs['headers'] = headers kwargs['auth'] = self.auth url = '/'.join((self.endpoint, 'v1', self.account_id, path)) resp = requests.request(method, url, **kwargs) try: resp.raise_for_status() except requests.HTTPError as ex: content = json.loads(ex.response.content) raise exceptions.SoftLayerAPIError(ex.response.status_code, content['message']) return resp def authenticate(self, username, api_key, auth_token=None): """Authenticate this connection using the given credentials. :param username: SoftLayer username :param api_key: SoftLayer API Key :param auth_token: (optional) Starting auth token """ auth_endpoint = '/'.join((self.endpoint, 'v1', self.account_id, 'auth')) auth = QueueAuth(auth_endpoint, username, api_key, auth_token=auth_token) auth.auth() self.auth = auth def stats(self, period='hour'): """Get account stats. :param period: 'hour', 'day', 'week', 'month' """ resp = self._make_request('get', 'stats/%s' % period) return resp.json() # QUEUE METHODS def get_queues(self, tags=None): """Get listing of queues. :param list tags: (optional) list of tags to filter by """ params = {} if tags: params['tags'] = ','.join(tags) resp = self._make_request('get', 'queues', params=params) return resp.json() def create_queue(self, queue_name, **kwargs): """Create Queue. :param queue_name: Queue Name :param dict \\*\\*kwargs: queue options """ queue = {} queue.update(kwargs) data = json.dumps(queue) resp = self._make_request('put', 'queues/%s' % queue_name, data=data) return resp.json() def modify_queue(self, queue_name, **kwargs): """Modify Queue. :param queue_name: Queue Name :param dict \\*\\*kwargs: queue options """ return self.create_queue(queue_name, **kwargs) def get_queue(self, queue_name): """Get queue details. :param queue_name: Queue Name """ resp = self._make_request('get', 'queues/%s' % queue_name) return resp.json() def delete_queue(self, queue_name, force=False): """Delete Queue. :param queue_name: Queue Name :param force: (optional) Force queue to be deleted even if there are pending messages """ params = {} if force: params['force'] = 1 self._make_request('delete', 'queues/%s' % queue_name, params=params) return True def push_queue_message(self, queue_name, body, **kwargs): """Create Queue Message. :param queue_name: Queue Name :param body: Message body :param dict \\*\\*kwargs: Message options """ message = {'body': body} message.update(kwargs) resp = self._make_request('post', 'queues/%s/messages' % queue_name, data=json.dumps(message)) return resp.json() def pop_messages(self, queue_name, count=1): """Pop messages from a queue. :param queue_name: Queue Name :param count: (optional) number of messages to retrieve """ resp = self._make_request('get', 'queues/%s/messages' % queue_name, params={'batch': count}) return resp.json() def pop_message(self, queue_name): """Pop a single message from a queue. If no messages are returned this returns None :param queue_name: Queue Name """ messages = self.pop_messages(queue_name, count=1) if messages['item_count'] > 0: return messages['items'][0] else: return None def delete_message(self, queue_name, message_id): """Delete a message. :param queue_name: Queue Name :param message_id: Message id """ self._make_request('delete', 'queues/%s/messages/%s' % (queue_name, message_id)) return True # TOPIC METHODS def get_topics(self, tags=None): """Get listing of topics. :param list tags: (optional) list of tags to filter by """ params = {} if tags: params['tags'] = ','.join(tags) resp = self._make_request('get', 'topics', params=params) return resp.json() def create_topic(self, topic_name, **kwargs): """Create Topic. :param topic_name: Topic Name :param dict \\*\\*kwargs: Topic options """ data = json.dumps(kwargs) resp = self._make_request('put', 'topics/%s' % topic_name, data=data) return resp.json() def modify_topic(self, topic_name, **kwargs): """Modify Topic. :param topic_name: Topic Name :param dict \\*\\*kwargs: Topic options """ return self.create_topic(topic_name, **kwargs) def get_topic(self, topic_name): """Get topic details. :param topic_name: Topic Name """ resp = self._make_request('get', 'topics/%s' % topic_name) return resp.json() def delete_topic(self, topic_name, force=False): """Delete Topic. :param topic_name: Topic Name :param force: (optional) Force topic to be deleted even if there are attached subscribers """ params = {} if force: params['force'] = 1 self._make_request('delete', 'topics/%s' % topic_name, params=params) return True def push_topic_message(self, topic_name, body, **kwargs): """Create Topic Message. :param topic_name: Topic Name :param body: Message body :param dict \\*\\*kwargs: Topic message options """ message = {'body': body} message.update(kwargs) resp = self._make_request('post', 'topics/%s/messages' % topic_name, data=json.dumps(message)) return resp.json() def get_subscriptions(self, topic_name): """Listing of subscriptions on a topic. :param topic_name: Topic Name """ resp = self._make_request('get', 'topics/%s/subscriptions' % topic_name) return resp.json() def create_subscription(self, topic_name, subscription_type, **kwargs): """Create Subscription. :param topic_name: Topic Name :param subscription_type: type ('queue' or 'http') :param dict \\*\\*kwargs: Subscription options """ resp = self._make_request( 'post', 'topics/%s/subscriptions' % topic_name, data=json.dumps({ 'endpoint_type': subscription_type, 'endpoint': kwargs})) return resp.json() def delete_subscription(self, topic_name, subscription_id): """Delete a subscription. :param topic_name: Topic Name :param subscription_id: Subscription id """ self._make_request('delete', 'topics/%s/subscriptions/%s' % (topic_name, subscription_id)) return True softlayer-python-5.4.2/SoftLayer/managers/metadata.py000066400000000000000000000113131324365065500227200ustar00rootroot00000000000000""" SoftLayer.metadata ~~~~~~~~~~~~~~~~~~ Metadata Manager/helpers :license: MIT, see LICENSE for more details. """ import SoftLayer from SoftLayer import consts from SoftLayer import exceptions from SoftLayer import transports METADATA_MAPPING = { 'backend_mac': {'call': 'getBackendMacAddresses'}, 'datacenter': {'call': 'getDatacenter'}, 'datacenter_id': {'call': 'getDatacenterId'}, 'domain': {'call': 'getDomain'}, 'frontend_mac': {'call': 'getFrontendMacAddresses'}, 'fqdn': {'call': 'getFullyQualifiedDomainName'}, 'hostname': {'call': 'getHostname'}, 'id': {'call': 'getId'}, 'primary_backend_ip': {'call': 'getPrimaryBackendIpAddress'}, 'primary_ip': {'call': 'getPrimaryIpAddress'}, 'primary_frontend_ip': {'call': 'getPrimaryIpAddress'}, 'provision_state': {'call': 'getProvisionState'}, 'router': {'call': 'getRouter', 'param_req': True}, 'tags': {'call': 'getTags'}, 'user_data': {'call': 'getUserMetadata'}, 'user_metadata': {'call': 'getUserMetadata'}, 'vlan_ids': {'call': 'getVlanIds', 'param_req': True}, 'vlans': {'call': 'getVlans', 'param_req': True}, } METADATA_ATTRIBUTES = METADATA_MAPPING.keys() class MetadataManager(object): """Provides an interface for the SoftLayer metadata service. See product information here: http://sldn.softlayer.com/reference/services/SoftLayer_Resource_Metadata This provides metadata about the resourse it is called from. See `METADATA_ATTRIBUTES` for full list of attributes. Usage: >>> import SoftLayer >>> client = SoftLayer.create_client_from_env() >>> from SoftLayer import MetadataManager >>> meta = MetadataManager(client) >>> meta.get('datacenter') 'dal05' >>> meta.get('fqdn') 'test.example.com' :param SoftLayer.API.BaseClient client: the client instance """ attribs = METADATA_MAPPING def __init__(self, client=None, timeout=5): if client is None: transport = transports.RestTransport( timeout=timeout, endpoint_url=consts.API_PRIVATE_ENDPOINT_REST, ) client = SoftLayer.BaseClient(transport=transport) self.client = client def get(self, name, param=None): """Retreive a metadata attribute. :param string name: name of the attribute to retrieve. See `attribs` :param param: Required parameter for some attributes """ if name not in self.attribs: raise exceptions.SoftLayerError('Unknown metadata attribute.') call_details = self.attribs[name] if call_details.get('param_req'): if not param: raise exceptions.SoftLayerError( 'Parameter required to get this attribute.') params = tuple() if param is not None: params = (param,) try: return self.client.call('Resource_Metadata', self.attribs[name]['call'], *params) except exceptions.SoftLayerAPIError as ex: if ex.faultCode == 404: return None raise ex def _get_network(self, kind, router=True, vlans=True, vlan_ids=True): """Wrapper for getting details about networks. :param string kind: network kind. Typically 'public' or 'private' :param boolean router: flag to include router information :param boolean vlans: flag to include vlan information :param boolean vlan_ids: flag to include vlan_ids """ network = {} macs = self.get('%s_mac' % kind) network['mac_addresses'] = macs if len(macs) == 0: return network if router: network['router'] = self.get('router', macs[0]) if vlans: network['vlans'] = self.get('vlans', macs[0]) if vlan_ids: network['vlan_ids'] = self.get('vlan_ids', macs[0]) return network def public_network(self, **kwargs): """Returns details about the public network. :param boolean router: True to return router details :param boolean vlans: True to return vlan details :param boolean vlan_ids: True to return vlan_ids """ return self._get_network('frontend', **kwargs) def private_network(self, **kwargs): """Returns details about the private network. :param boolean router: True to return router details :param boolean vlans: True to return vlan details :param boolean vlan_ids: True to return vlan_ids """ return self._get_network('backend', **kwargs) softlayer-python-5.4.2/SoftLayer/managers/network.py000066400000000000000000000621451324365065500226420ustar00rootroot00000000000000""" SoftLayer.network ~~~~~~~~~~~~~~~~~ Network Manager/helpers :license: MIT, see LICENSE for more details. """ import collections from SoftLayer import exceptions from SoftLayer import utils DEFAULT_SUBNET_MASK = ','.join(['hardware', 'datacenter', 'ipAddressCount', 'virtualGuests', 'networkVlan[id,networkSpace]']) DEFAULT_VLAN_MASK = ','.join([ 'firewallInterfaces', 'hardwareCount', 'primaryRouter[id, fullyQualifiedDomainName, datacenter]', 'subnetCount', 'totalPrimaryIpAddressCount', 'virtualGuestCount', 'networkSpace', ]) DEFAULT_GET_VLAN_MASK = ','.join([ 'firewallInterfaces', 'primaryRouter[id, fullyQualifiedDomainName, datacenter]', 'totalPrimaryIpAddressCount', 'networkSpace', 'hardware', 'subnets', 'virtualGuests', ]) class NetworkManager(object): """Manage SoftLayer network objects: VLANs, subnets, IPs and rwhois See product information here: http://www.softlayer.com/networking :param SoftLayer.API.BaseClient client: the client instance """ def __init__(self, client): self.client = client self.account = client['Account'] self.vlan = client['Network_Vlan'] self.subnet = client['Network_Subnet'] self.network_storage = self.client['Network_Storage'] self.security_group = self.client['Network_SecurityGroup'] def add_global_ip(self, version=4, test_order=False): """Adds a global IP address to the account. :param int version: Specifies whether this is IPv4 or IPv6 :param bool test_order: If true, this will only verify the order. """ # This method is here to improve the public interface from a user's # perspective since ordering a single global IP through the subnet # interface is not intuitive. return self.add_subnet('global', version=version, test_order=test_order) def add_securitygroup_rule(self, group_id, remote_ip=None, remote_group=None, direction=None, ethertype=None, port_max=None, port_min=None, protocol=None): """Add a rule to a security group :param int group_id: The ID of the security group to add this rule to :param str remote_ip: The remote IP or CIDR to enforce the rule on :param int remote_group: The remote security group ID to enforce the rule on :param str direction: The direction to enforce (egress or ingress) :param str ethertype: The ethertype to enforce (IPv4 or IPv6) :param int port_max: The upper port bound to enforce (icmp code if the protocol is icmp) :param int port_min: The lower port bound to enforce (icmp type if the protocol is icmp) :param str protocol: The protocol to enforce (icmp, udp, tcp) """ rule = {'direction': direction} if ethertype is not None: rule['ethertype'] = ethertype if port_max is not None: rule['portRangeMax'] = port_max if port_min is not None: rule['portRangeMin'] = port_min if protocol is not None: rule['protocol'] = protocol if remote_ip is not None: rule['remoteIp'] = remote_ip if remote_group is not None: rule['remoteGroupId'] = remote_group return self.add_securitygroup_rules(group_id, [rule]) def add_securitygroup_rules(self, group_id, rules): """Add rules to a security group :param int group_id: The ID of the security group to add the rules to :param list rules: The list of rule dictionaries to add """ if not isinstance(rules, list): raise TypeError("The rules provided must be a list of dictionaries") return self.security_group.addRules(rules, id=group_id) def add_subnet(self, subnet_type, quantity=None, vlan_id=None, version=4, test_order=False): """Orders a new subnet :param str subnet_type: Type of subnet to add: private, public, global :param int quantity: Number of IPs in the subnet :param int vlan_id: VLAN id for the subnet to be placed into :param int version: 4 for IPv4, 6 for IPv6 :param bool test_order: If true, this will only verify the order. """ package = self.client['Product_Package'] category = 'sov_sec_ip_addresses_priv' desc = '' if version == 4: if subnet_type == 'global': quantity = 0 category = 'global_ipv4' elif subnet_type == 'public': category = 'sov_sec_ip_addresses_pub' else: category = 'static_ipv6_addresses' if subnet_type == 'global': quantity = 0 category = 'global_ipv6' desc = 'Global' elif subnet_type == 'public': desc = 'Portable' # In the API, every non-server item is contained within package ID 0. # This means that we need to get all of the items and loop through them # looking for the items we need based upon the category, quantity, and # item description. price_id = None quantity_str = str(quantity) for item in package.getItems(id=0, mask='itemCategory'): category_code = utils.lookup(item, 'itemCategory', 'categoryCode') if all([category_code == category, item.get('capacity') == quantity_str, version == 4 or (version == 6 and desc in item['description'])]): price_id = item['prices'][0]['id'] break if not price_id: raise TypeError('Invalid combination specified for ordering a' ' subnet.') order = { 'packageId': 0, 'prices': [{'id': price_id}], 'quantity': 1, # This is necessary in order for the XML-RPC endpoint to select the # correct order container 'complexType': 'SoftLayer_Container_Product_Order_Network_Subnet', } if subnet_type != 'global': order['endPointVlanId'] = vlan_id if test_order: return self.client['Product_Order'].verifyOrder(order) else: return self.client['Product_Order'].placeOrder(order) def assign_global_ip(self, global_ip_id, target): """Assigns a global IP address to a specified target. :param int global_ip_id: The ID of the global IP being assigned :param string target: The IP address to assign """ return self.client['Network_Subnet_IpAddress_Global'].route( target, id=global_ip_id) def attach_securitygroup_component(self, group_id, component_id): """Attaches a network component to a security group. :param int group_id: The ID of the security group :param int component_id: The ID of the network component to attach """ return self.attach_securitygroup_components(group_id, [component_id]) def attach_securitygroup_components(self, group_id, component_ids): """Attaches network components to a security group. :param int group_id: The ID of the security group :param list component_ids: The IDs of the network components to attach """ return self.security_group.attachNetworkComponents(component_ids, id=group_id) def cancel_global_ip(self, global_ip_id): """Cancels the specified global IP address. :param int id: The ID of the global IP to be cancelled. """ service = self.client['Network_Subnet_IpAddress_Global'] ip_address = service.getObject(id=global_ip_id, mask='billingItem') billing_id = ip_address['billingItem']['id'] return self.client['Billing_Item'].cancelService(id=billing_id) def cancel_subnet(self, subnet_id): """Cancels the specified subnet. :param int subnet_id: The ID of the subnet to be cancelled. """ subnet = self.get_subnet(subnet_id, mask='id, billingItem.id') if "billingItem" not in subnet: raise exceptions.SoftLayerError("subnet %s can not be cancelled" " " % subnet_id) billing_id = subnet['billingItem']['id'] return self.client['Billing_Item'].cancelService(id=billing_id) def create_securitygroup(self, name=None, description=None): """Creates a security group. :param string name: The name of the security group :param string description: The description of the security group """ create_dict = {'name': name, 'description': description} return self.security_group.createObject(create_dict) def delete_securitygroup(self, group_id): """Deletes the specified security group. :param int group_id: The ID of the security group """ return self.security_group.deleteObject(id=group_id) def detach_securitygroup_component(self, group_id, component_id): """Detaches a network component from a security group. :param int group_id: The ID of the security group :param int component_id: The ID of the component to detach """ return self.detach_securitygroup_components(group_id, [component_id]) def detach_securitygroup_components(self, group_id, component_ids): """Detaches network components from a security group. :param int group_id: The ID of the security group :param list component_ids: The IDs of the network components to detach """ return self.security_group.detachNetworkComponents(component_ids, id=group_id) def edit_rwhois(self, abuse_email=None, address1=None, address2=None, city=None, company_name=None, country=None, first_name=None, last_name=None, postal_code=None, private_residence=None, state=None): """Edit rwhois record.""" update = {} for key, value in [('abuseEmail', abuse_email), ('address1', address1), ('address2', address2), ('city', city), ('companyName', company_name), ('country', country), ('firstName', first_name), ('lastName', last_name), ('privateResidenceFlag', private_residence), ('state', state), ('postalCode', postal_code)]: if value is not None: update[key] = value # If there's anything to update, update it if update: rwhois = self.get_rwhois() return self.client['Network_Subnet_Rwhois_Data'].editObject( update, id=rwhois['id']) return True def edit_securitygroup(self, group_id, name=None, description=None): """Edit security group details. :param int group_id: The ID of the security group :param string name: The name of the security group :param string description: The description of the security group """ successful = False obj = {} if name: obj['name'] = name if description: obj['description'] = description if obj: successful = self.security_group.editObject(obj, id=group_id) return successful def edit_securitygroup_rule(self, group_id, rule_id, remote_ip=None, remote_group=None, direction=None, ethertype=None, port_max=None, port_min=None, protocol=None): """Edit a security group rule. :param int group_id: The ID of the security group the rule belongs to :param int rule_id: The ID of the rule to edit :param str remote_ip: The remote IP or CIDR to enforce the rule on :param int remote_group: The remote security group ID to enforce the rule on :param str direction: The direction to enforce (egress or ingress) :param str ethertype: The ethertype to enforce (IPv4 or IPv6) :param str port_max: The upper port bound to enforce :param str port_min: The lower port bound to enforce :param str protocol: The protocol to enforce (icmp, udp, tcp) """ successful = False obj = {} if remote_ip is not None: obj['remoteIp'] = remote_ip if remote_group is not None: obj['remoteGroupId'] = remote_group if direction is not None: obj['direction'] = direction if ethertype is not None: obj['ethertype'] = ethertype if port_max is not None: obj['portRangeMax'] = port_max if port_min is not None: obj['portRangeMin'] = port_min if protocol is not None: obj['protocol'] = protocol if obj: obj['id'] = rule_id successful = self.security_group.editRules([obj], id=group_id) return successful def ip_lookup(self, ip_address): """Looks up an IP address and returns network information about it. :param string ip_address: An IP address. Can be IPv4 or IPv6 :returns: A dictionary of information about the IP """ obj = self.client['Network_Subnet_IpAddress'] return obj.getByIpAddress(ip_address, mask='hardware, virtualGuest') def get_rwhois(self): """Returns the RWhois information about the current account. :returns: A dictionary containing the account's RWhois information. """ return self.account.getRwhoisData() def get_securitygroup(self, group_id, **kwargs): """Returns the information about the given security group. :param string id: The ID for the security group :returns: A diction of information about the security group """ if 'mask' not in kwargs: kwargs['mask'] = ( 'id,' 'name,' 'description,' '''rules[id, remoteIp, remoteGroupId, direction, ethertype, portRangeMin, portRangeMax, protocol],''' '''networkComponentBindings[ networkComponent[ id, port, guest[ id, hostname, primaryBackendIpAddress, primaryIpAddress ] ] ]''' ) return self.security_group.getObject(id=group_id, **kwargs) def get_subnet(self, subnet_id, **kwargs): """Returns information about a single subnet. :param string id: Either the ID for the subnet or its network identifier :returns: A dictionary of information about the subnet """ if 'mask' not in kwargs: kwargs['mask'] = DEFAULT_SUBNET_MASK return self.subnet.getObject(id=subnet_id, **kwargs) def get_vlan(self, vlan_id): """Returns information about a single VLAN. :param int id: The unique identifier for the VLAN :returns: A dictionary containing a large amount of information about the specified VLAN. """ return self.vlan.getObject(id=vlan_id, mask=DEFAULT_GET_VLAN_MASK) def list_global_ips(self, version=None, identifier=None, **kwargs): """Returns a list of all global IP address records on the account. :param int version: Only returns IPs of this version (4 or 6) :param string identifier: If specified, the list will only contain the global ips matching this network identifier. """ if 'mask' not in kwargs: mask = ['destinationIpAddress[hardware, virtualGuest]', 'ipAddress'] kwargs['mask'] = ','.join(mask) _filter = utils.NestedDict({}) if version: ver = utils.query_filter(version) _filter['globalIpRecords']['ipAddress']['subnet']['version'] = ver if identifier: subnet_filter = _filter['globalIpRecords']['ipAddress']['subnet'] subnet_filter['networkIdentifier'] = utils.query_filter(identifier) kwargs['filter'] = _filter.to_dict() return self.account.getGlobalIpRecords(**kwargs) def list_subnets(self, identifier=None, datacenter=None, version=0, subnet_type=None, network_space=None, **kwargs): """Display a list of all subnets on the account. This provides a quick overview of all subnets including information about data center residence and the number of devices attached. :param string identifier: If specified, the list will only contain the subnet matching this network identifier. :param string datacenter: If specified, the list will only contain subnets in the specified data center. :param int version: Only returns subnets of this version (4 or 6). :param string subnet_type: If specified, it will only returns subnets of this type. :param string network_space: If specified, it will only returns subnets with the given address space label. :param dict \\*\\*kwargs: response-level options (mask, limit, etc.) """ if 'mask' not in kwargs: kwargs['mask'] = DEFAULT_SUBNET_MASK _filter = utils.NestedDict(kwargs.get('filter') or {}) if identifier: _filter['subnets']['networkIdentifier'] = ( utils.query_filter(identifier)) if datacenter: _filter['subnets']['datacenter']['name'] = ( utils.query_filter(datacenter)) if version: _filter['subnets']['version'] = utils.query_filter(version) if subnet_type: _filter['subnets']['subnetType'] = utils.query_filter(subnet_type) else: # This filters out global IPs from the subnet listing. _filter['subnets']['subnetType'] = {'operation': '!= GLOBAL_IP'} if network_space: _filter['subnets']['networkVlan']['networkSpace'] = ( utils.query_filter(network_space)) kwargs['filter'] = _filter.to_dict() return self.account.getSubnets(**kwargs) def list_vlans(self, datacenter=None, vlan_number=None, name=None, **kwargs): """Display a list of all VLANs on the account. This provides a quick overview of all VLANs including information about data center residence and the number of devices attached. :param string datacenter: If specified, the list will only contain VLANs in the specified data center. :param int vlan_number: If specified, the list will only contain the VLAN matching this VLAN number. :param int name: If specified, the list will only contain the VLAN matching this VLAN name. :param dict \\*\\*kwargs: response-level options (mask, limit, etc.) """ _filter = utils.NestedDict(kwargs.get('filter') or {}) if vlan_number: _filter['networkVlans']['vlanNumber'] = ( utils.query_filter(vlan_number)) if name: _filter['networkVlans']['name'] = utils.query_filter(name) if datacenter: _filter['networkVlans']['primaryRouter']['datacenter']['name'] = ( utils.query_filter(datacenter)) kwargs['filter'] = _filter.to_dict() if 'mask' not in kwargs: kwargs['mask'] = DEFAULT_VLAN_MASK return self.account.getNetworkVlans(**kwargs) def list_securitygroups(self, **kwargs): """List security groups.""" return self.security_group.getAllObjects(**kwargs) def list_securitygroup_rules(self, group_id): """List security group rules associated with a security group. :param int group_id: The security group to list rules for """ return self.security_group.getRules(id=group_id) def remove_securitygroup_rule(self, group_id, rule_id): """Remove a rule from a security group. :param int group_id: The ID of the security group :param int rule_id: The ID of the rule to remove """ return self.remove_securitygroup_rules(group_id, [rule_id]) def remove_securitygroup_rules(self, group_id, rules): """Remove rules from a security group. :param int group_id: The ID of the security group :param list rules: The list of IDs to remove """ return self.security_group.removeRules(rules, id=group_id) def resolve_global_ip_ids(self, identifier): """Resolve global ip ids.""" return utils.resolve_ids(identifier, [self._list_global_ips_by_identifier]) def resolve_subnet_ids(self, identifier): """Resolve subnet ids.""" return utils.resolve_ids(identifier, [self._list_subnets_by_identifier]) def resolve_vlan_ids(self, identifier): """Resolve VLAN ids.""" return utils.resolve_ids(identifier, [self._list_vlans_by_name]) def summary_by_datacenter(self): """Summary of the networks on the account, grouped by data center. The resultant dictionary is primarily useful for statistical purposes. It contains count information rather than raw data. If you want raw information, see the :func:`list_vlans` method instead. :returns: A dictionary keyed by data center with the data containing a set of counts for subnets, hardware, virtual servers, and other objects residing within that data center. """ datacenters = collections.defaultdict(lambda: { 'hardware_count': 0, 'public_ip_count': 0, 'subnet_count': 0, 'virtual_guest_count': 0, 'vlan_count': 0, }) for vlan in self.list_vlans(): name = utils.lookup(vlan, 'primaryRouter', 'datacenter', 'name') datacenters[name]['vlan_count'] += 1 datacenters[name]['public_ip_count'] += ( vlan['totalPrimaryIpAddressCount']) datacenters[name]['subnet_count'] += vlan['subnetCount'] # NOTE(kmcdonald): Only count hardware/guests once if vlan.get('networkSpace') == 'PRIVATE': datacenters[name]['hardware_count'] += ( vlan['hardwareCount']) datacenters[name]['virtual_guest_count'] += ( vlan['virtualGuestCount']) return dict(datacenters) def unassign_global_ip(self, global_ip_id): """Unassigns a global IP address from a target. :param int id: The ID of the global IP being unassigned """ return self.client['Network_Subnet_IpAddress_Global'].unroute( id=global_ip_id) def _list_global_ips_by_identifier(self, identifier): """Returns a list of IDs of the global IP matching the identifier. :param string identifier: The identifier to look up :returns: List of matching IDs """ results = self.list_global_ips(identifier=identifier, mask='id') return [result['id'] for result in results] def _list_subnets_by_identifier(self, identifier): """Returns a list of IDs of the subnet matching the identifier. :param string identifier: The identifier to look up :returns: List of matching IDs """ identifier = identifier.split('/', 1)[0] results = self.list_subnets(identifier=identifier, mask='id') return [result['id'] for result in results] def _list_vlans_by_name(self, name): """Returns a list of IDs of VLANs which match the given VLAN name. :param string name: a VLAN name :returns: List of matching IDs """ results = self.list_vlans(name=name, mask='id') return [result['id'] for result in results] def get_nas_credentials(self, identifier, **kwargs): """Returns a list of IDs of VLANs which match the given VLAN name. :param integer instance_id: the instance ID :returns: A dictionary containing a large amount of information about the specified instance. """ result = self.network_storage.getObject(id=identifier, **kwargs) return result softlayer-python-5.4.2/SoftLayer/managers/object_storage.py000066400000000000000000000035331324365065500241370ustar00rootroot00000000000000""" SoftLayer.object_storage ~~~~~~~~~~~~~~~~~~~~~~~~ Object Storage Manager/helpers :license: MIT, see LICENSE for more details. """ LIST_ACCOUNTS_MASK = '''mask(SoftLayer_Network_Storage_Hub_Swift)[ id,username,notes ]''' ENDPOINT_MASK = '''mask(SoftLayer_Network_Storage_Hub_Swift)[ id,storageNodes[id,backendIpAddress,frontendIpAddress,datacenter] ]''' class ObjectStorageManager(object): """Manager for SoftLayer Object Storage accounts. See product information here: http://www.softlayer.com/object-storage :param SoftLayer.API.BaseClient client: the client instance """ def __init__(self, client): self.client = client def list_accounts(self): """Lists your object storage accounts.""" _filter = { 'hubNetworkStorage': {'vendorName': {'operation': 'Swift'}}, } return self.client.call('Account', 'getHubNetworkStorage', mask=LIST_ACCOUNTS_MASK, filter=_filter) def list_endpoints(self): """Lists the known object storage endpoints.""" _filter = { 'hubNetworkStorage': {'vendorName': {'operation': 'Swift'}}, } endpoints = [] network_storage = self.client.call('Account', 'getHubNetworkStorage', mask=ENDPOINT_MASK, limit=1, filter=_filter) if network_storage: for node in network_storage['storageNodes']: endpoints.append({ 'datacenter': node['datacenter'], 'public': node['frontendIpAddress'], 'private': node['backendIpAddress'], }) return endpoints softlayer-python-5.4.2/SoftLayer/managers/ordering.py000066400000000000000000000470261324365065500227630ustar00rootroot00000000000000""" SoftLayer.ordering ~~~~~~~~~~~~~~~~~~ Ordering Manager :license: MIT, see LICENSE for more details. """ # pylint: disable=no-self-use from SoftLayer import exceptions CATEGORY_MASK = '''id, isRequired, itemCategory[id, name, categoryCode] ''' ITEM_MASK = '''id, keyName, description, itemCategory, categories''' PACKAGE_MASK = '''id, name, keyName, isActive, type''' PRESET_MASK = '''id, name, keyName, description''' class OrderingManager(object): """Manager to help ordering via the SoftLayer API. :param SoftLayer.API.BaseClient client: the client instance """ def __init__(self, client): self.client = client self.package_svc = client['Product_Package'] self.order_svc = client['Product_Order'] self.billing_svc = client['Billing_Order'] def get_packages_of_type(self, package_types, mask=None): """Get packages that match a certain type. Each ordering package has a type, so return all packages that match the types we are looking for :param list package_types: List of strings representing the package type keynames we are interested in. :param string mask: Mask to specify the properties we want to retrieve """ _filter = { 'type': { 'keyName': { 'operation': 'in', 'options': [ {'name': 'data', 'value': package_types} ], }, }, } packages = self.package_svc.getAllObjects(mask=mask, filter=_filter) packages = self.filter_outlet_packages(packages) return packages @staticmethod def filter_outlet_packages(packages): """Remove packages designated as OUTLET. Those type of packages must be handled in a different way, and they are not supported at the moment. :param packages: Dictionary of packages. Name and description keys must be present in each of them. """ non_outlet_packages = [] for package in packages: if all(['OUTLET' not in package.get('description', '').upper(), 'OUTLET' not in package.get('name', '').upper()]): non_outlet_packages.append(package) return non_outlet_packages @staticmethod def get_only_active_packages(packages): """Return only active packages. If a package is active, it is eligible for ordering This will inspect the 'isActive' property on the provided packages :param packages: Dictionary of packages, isActive key must be present """ active_packages = [] for package in packages: if package['isActive']: active_packages.append(package) return active_packages def get_package_by_type(self, package_type, mask=None): """Get a single package of a given type. Syntactic sugar to retrieve a single package of a given type. If multiple packages share the given type, this will return the first one returned by the API. If no packages are found, returns None :param string package_type: representing the package type key name we are interested in """ packages = self.get_packages_of_type([package_type], mask) if len(packages) == 0: return None else: return packages.pop() def get_package_id_by_type(self, package_type): """Return the package ID of a Product Package with a given type. :param string package_type: representing the package type key name we are interested in :raises ValueError: when no package of the given type is found """ mask = "mask[id, name, description, isActive, type[keyName]]" package = self.get_package_by_type(package_type, mask) if package: return package['id'] else: raise ValueError("No package found for type: " + package_type) def get_quotes(self): """Retrieve a list of quotes. :returns: a list of SoftLayer_Billing_Order_Quote """ quotes = self.client['Account'].getActiveQuotes() return quotes def get_quote_details(self, quote_id): """Retrieve quote details. :param quote_id: ID number of target quote """ quote = self.client['Billing_Order_Quote'].getObject(id=quote_id) return quote def get_order_container(self, quote_id): """Generate an order container from a quote object. :param quote_id: ID number of target quote """ quote = self.client['Billing_Order_Quote'] container = quote.getRecalculatedOrderContainer(id=quote_id) return container['orderContainers'][0] def generate_order_template(self, quote_id, extra, quantity=1): """Generate a complete order template. :param int quote_id: ID of target quote :param list extra: List of dictionaries that have extra details about the order such as hostname or domain names for virtual servers or hardware nodes :param int quantity: Number of ~things~ to order """ container = self.get_order_container(quote_id) container['quantity'] = quantity # NOTE(kmcdonald): This will only work with virtualGuests and hardware. # There has to be a better way, since this is based on # an existing quote that supposedly knows about this # detail if container['packageId'] == 46: product_type = 'virtualGuests' else: product_type = 'hardware' if len(extra) != quantity: raise ValueError("You must specify extra for each server in the quote") container[product_type] = [] for extra_details in extra: container[product_type].append(extra_details) container['presetId'] = None return container def verify_quote(self, quote_id, extra, quantity=1): """Verifies that a quote order is valid. :param int quote_id: ID for the target quote :param list hostnames: hostnames of the servers :param string domain: domain of the new servers :param int quantity: Quantity to override default """ container = self.generate_order_template(quote_id, extra, quantity=quantity) return self.order_svc.verifyOrder(container) def order_quote(self, quote_id, extra, quantity=1): """Places an order using a quote :param int quote_id: ID for the target quote :param list hostnames: hostnames of the servers :param string domain: domain of the new server :param int quantity: Quantity to override default """ container = self.generate_order_template(quote_id, extra, quantity=quantity) return self.order_svc.placeOrder(container) def get_package_by_key(self, package_keyname, mask=None): """Get a single package with a given key. If no packages are found, returns None :param package_keyname: string representing the package key name we are interested in. :param string mask: Mask to specify the properties we want to retrieve """ _filter = {'keyName': {'operation': package_keyname}} packages = self.package_svc.getAllObjects(mask=mask, filter=_filter) if len(packages) == 0: raise exceptions.SoftLayerError("Package {} does not exist".format(package_keyname)) return packages.pop() def list_categories(self, package_keyname, **kwargs): """List the categories for the given package. :param str package_keyname: The package for which to get the categories. :returns: List of categories associated with the package """ get_kwargs = {} get_kwargs['mask'] = kwargs.get('mask', CATEGORY_MASK) if 'filter' in kwargs: get_kwargs['filter'] = kwargs['filter'] package = self.get_package_by_key(package_keyname, mask='id') categories = self.package_svc.getConfiguration(id=package['id'], **get_kwargs) return categories def list_items(self, package_keyname, **kwargs): """List the items for the given package. :param str package_keyname: The package for which to get the items. :returns: List of items in the package """ get_kwargs = {} get_kwargs['mask'] = kwargs.get('mask', ITEM_MASK) if 'filter' in kwargs: get_kwargs['filter'] = kwargs['filter'] package = self.get_package_by_key(package_keyname, mask='id') items = self.package_svc.getItems(id=package['id'], **get_kwargs) return items def list_packages(self, **kwargs): """List active packages. :returns: List of active packages. """ get_kwargs = {} get_kwargs['mask'] = kwargs.get('mask', PACKAGE_MASK) if 'filter' in kwargs: get_kwargs['filter'] = kwargs['filter'] packages = self.package_svc.getAllObjects(**get_kwargs) return [package for package in packages if package['isActive']] def list_presets(self, package_keyname, **kwargs): """Gets active presets for the given package. :param str package_keyname: The package for which to get presets :returns: A list of package presets that can be used for ordering """ get_kwargs = {} get_kwargs['mask'] = kwargs.get('mask', PRESET_MASK) if 'filter' in kwargs: get_kwargs['filter'] = kwargs['filter'] package = self.get_package_by_key(package_keyname, mask='id') acc_presets = self.package_svc.getAccountRestrictedActivePresets(id=package['id'], **get_kwargs) active_presets = self.package_svc.getActivePresets(id=package['id'], **get_kwargs) return active_presets + acc_presets def get_preset_by_key(self, package_keyname, preset_keyname, mask=None): """Gets a single preset with the given key.""" preset_operation = '_= %s' % preset_keyname _filter = {'activePresets': {'keyName': {'operation': preset_operation}}} presets = self.list_presets(package_keyname, mask=mask, filter=_filter) if len(presets) == 0: raise exceptions.SoftLayerError( "Preset {} does not exist in package {}".format(preset_keyname, package_keyname)) return presets[0] def get_price_id_list(self, package_keyname, item_keynames): """Converts a list of item keynames to a list of price IDs. This function is used to convert a list of item keynames into a list of price IDs that are used in the Product_Order verifyOrder() and placeOrder() functions. :param str package_keyname: The package associated with the prices :param list item_keynames: A list of item keyname strings :returns: A list of price IDs associated with the given item keynames in the given package """ mask = 'id, keyName, prices' items = self.list_items(package_keyname, mask=mask) prices = [] for item_keyname in item_keynames: try: # Need to find the item in the package that has a matching # keyName with the current item we are searching for matching_item = [i for i in items if i['keyName'] == item_keyname][0] except IndexError: raise exceptions.SoftLayerError( "Item {} does not exist for package {}".format(item_keyname, package_keyname)) # we want to get the price ID that has no location attached to it, # because that is the most generic price. verifyOrder/placeOrder # can take that ID and create the proper price for us in the location # in which the order is made price_id = [p['id'] for p in matching_item['prices'] if p['locationGroupId'] == ''][0] prices.append(price_id) return prices def verify_order(self, package_keyname, location, item_keynames, complex_type=None, hourly=True, preset_keyname=None, extras=None, quantity=1): """Verifies an order with the given package and prices. This function takes in parameters needed for an order and verifies the order to ensure the given items are compatible with the given package. :param str package_keyname: The keyname for the package being ordered :param str location: The datacenter location string for ordering (Ex: DALLAS13) :param list item_keynames: The list of item keyname strings to order. To see list of possible keynames for a package, use list_items() (or `slcli order item-list`) :param str complex_type: The complex type to send with the order. Typically begins with `SoftLayer_Container_Product_Order_`. :param bool hourly: If true, uses hourly billing, otherwise uses monthly billing :param string preset_keyname: If needed, specifies a preset to use for that package. To see a list of possible keynames for a package, use list_preset() (or `slcli order preset-list`) :param dict extras: The extra data for the order in dictionary format. Example: A VSI order requires hostname and domain to be set, so extras will look like the following: 'virtualGuests': [{'hostname': 'test', 'domain': 'softlayer.com'}]} :param int quantity: The number of resources to order """ order = self.generate_order(package_keyname, location, item_keynames, complex_type=complex_type, hourly=hourly, preset_keyname=preset_keyname, extras=extras, quantity=quantity) return self.order_svc.verifyOrder(order) def place_order(self, package_keyname, location, item_keynames, complex_type=None, hourly=True, preset_keyname=None, extras=None, quantity=1): """Places an order with the given package and prices. This function takes in parameters needed for an order and places the order. :param str package_keyname: The keyname for the package being ordered :param str location: The datacenter location string for ordering (Ex: DALLAS13) :param list item_keynames: The list of item keyname strings to order. To see list of possible keynames for a package, use list_items() (or `slcli order item-list`) :param str complex_type: The complex type to send with the order. Typically begins with `SoftLayer_Container_Product_Order_`. :param bool hourly: If true, uses hourly billing, otherwise uses monthly billing :param string preset_keyname: If needed, specifies a preset to use for that package. To see a list of possible keynames for a package, use list_preset() (or `slcli order preset-list`) :param dict extras: The extra data for the order in dictionary format. Example: A VSI order requires hostname and domain to be set, so extras will look like the following: {'virtualGuests': [{'hostname': 'test', domain': 'softlayer.com'}]} :param int quantity: The number of resources to order """ order = self.generate_order(package_keyname, location, item_keynames, complex_type=complex_type, hourly=hourly, preset_keyname=preset_keyname, extras=extras, quantity=quantity) return self.order_svc.placeOrder(order) def generate_order(self, package_keyname, location, item_keynames, complex_type=None, hourly=True, preset_keyname=None, extras=None, quantity=1): """Generates an order with the given package and prices. This function takes in parameters needed for an order and generates an order dictionary. This dictionary can then be used in either verify or placeOrder(). :param str package_keyname: The keyname for the package being ordered :param str location: The datacenter location string for ordering (Ex: DALLAS13) :param list item_keynames: The list of item keyname strings to order. To see list of possible keynames for a package, use list_items() (or `slcli order item-list`) :param str complex_type: The complex type to send with the order. Typically begins with `SoftLayer_Container_Product_Order_`. :param bool hourly: If true, uses hourly billing, otherwise uses monthly billing :param string preset_keyname: If needed, specifies a preset to use for that package. To see a list of possible keynames for a package, use list_preset() (or `slcli order preset-list`) :param dict extras: The extra data for the order in dictionary format. Example: A VSI order requires hostname and domain to be set, so extras will look like the following: {'virtualGuests': [{'hostname': 'test', 'domain': 'softlayer.com'}]} :param int quantity: The number of resources to order """ order = {} extras = extras or {} package = self.get_package_by_key(package_keyname, mask='id') # if there was extra data given for the order, add it to the order # example: VSIs require hostname and domain set on the order, so # extras will be {'virtualGuests': [{'hostname': 'test', # 'domain': 'softlayer.com'}]} order.update(extras) order['packageId'] = package['id'] order['location'] = location order['quantity'] = quantity order['useHourlyPricing'] = hourly if preset_keyname: preset_id = self.get_preset_by_key(package_keyname, preset_keyname)['id'] order['presetId'] = preset_id if not complex_type: raise exceptions.SoftLayerError("A complex type must be specified with the order") order['complexType'] = complex_type price_ids = self.get_price_id_list(package_keyname, item_keynames) order['prices'] = [{'id': price_id} for price_id in price_ids] return order def package_locations(self, package_keyname): """List datacenter locations for a package keyname :param str package_keyname: The package for which to get the items. :returns: List of locations a package is orderable in """ mask = "mask[description, keyname, locations]" package = self.get_package_by_key(package_keyname, mask='id') regions = self.package_svc.getRegions(id=package['id'], mask=mask) return regions softlayer-python-5.4.2/SoftLayer/managers/sshkey.py000066400000000000000000000053561324365065500224600ustar00rootroot00000000000000""" SoftLayer.sshkey ~~~~~~~~~~~~~~~~ SSH Key Manager/helpers :license: MIT, see LICENSE for more details. """ from SoftLayer import utils class SshKeyManager(utils.IdentifierMixin, object): """Manages account SSH keys in SoftLayer. See product information here: https://knowledgelayer.softlayer.com/procedure/ssh-keys :param SoftLayer.API.BaseClient client: the client instance """ def __init__(self, client): self.client = client self.sshkey = client['Security_Ssh_Key'] self.resolvers = [self._get_ids_from_label] def add_key(self, key, label, notes=None): """Adds a new SSH key to the account. :param string key: The SSH key to add :param string label: The label for the key :param string notes: Additional notes for the key :returns: A dictionary of the new key's information. """ order = { 'key': key, 'label': label, 'notes': notes, } return self.sshkey.createObject(order) def delete_key(self, key_id): """Permanently deletes an SSH key from the account. :param int key_id: The ID of the key to delete """ return self.sshkey.deleteObject(id=key_id) def edit_key(self, key_id, label=None, notes=None): """Edits information about an SSH key. :param int key_id: The ID of the key to edit :param string label: The new label for the key :param string notes: Notes to set or change on the key :returns: A Boolean indicating success or failure """ data = {} if label: data['label'] = label if notes: data['notes'] = notes return self.sshkey.editObject(data, id=key_id) def get_key(self, key_id): """Returns full information about a single SSH key. :param int key_id: The ID of the key to retrieve :returns: A dictionary of information about the key """ return self.sshkey.getObject(id=key_id) def list_keys(self, label=None): """Lists all SSH keys on the account. :param string label: Filter list based on SSH key label :returns: A list of dictionaries with information about each key """ _filter = utils.NestedDict({}) if label: _filter['sshKeys']['label'] = utils.query_filter(label) return self.client['Account'].getSshKeys(filter=_filter.to_dict()) def _get_ids_from_label(self, label): """Return sshkey IDs which match the given label.""" keys = self.list_keys() results = [] for key in keys: if key['label'] == label: results.append(key['id']) return results softlayer-python-5.4.2/SoftLayer/managers/ssl.py000066400000000000000000000062611324365065500217470ustar00rootroot00000000000000""" SoftLayer.ssl ~~~~~~~~~~~~~ SSL Manager/helpers :license: MIT, see LICENSE for more details. """ class SSLManager(object): """Manages SSL certificates in SoftLayer. See product information here: http://www.softlayer.com/ssl-certificates Example:: # Initialize the Manager. # env variables. These can also be specified in ~/.softlayer, # or passed directly to SoftLayer.Client() # SL_USERNAME = YOUR_USERNAME # SL_API_KEY = YOUR_API_KEY import SoftLayer client = SoftLayer.Client() mgr = SoftLayer.SSLManager(client) :param SoftLayer.API.BaseClient client: the client instance """ def __init__(self, client): self.client = client self.ssl = self.client['Security_Certificate'] def list_certs(self, method='all'): """List all certificates. :param string method: The type of certificates to list. Options are 'all', 'expired', and 'valid'. :returns: A list of dictionaries representing the requested SSL certs. Example:: # Get all valid SSL certs certs = mgr.list_certs(method='valid') print certs """ ssl = self.client['Account'] methods = { 'all': 'getSecurityCertificates', 'expired': 'getExpiredSecurityCertificates', 'valid': 'getValidSecurityCertificates' } mask = "mask[id, commonName, validityDays, notes]" func = getattr(ssl, methods[method]) return func(mask=mask) def add_certificate(self, certificate): """Creates a new certificate. :param dict certificate: A dictionary representing the parts of the certificate. See developer.softlayer.com for more info. Example:: cert = ?? result = mgr.add_certificate(certificate=cert) """ return self.ssl.createObject(certificate) def remove_certificate(self, cert_id): """Removes a certificate. :param integer cert_id: a certificate ID to remove Example:: # Removes certificate with id 1234 result = mgr.remove_certificate(cert_id = 1234) """ return self.ssl.deleteObject(id=cert_id) def edit_certificate(self, certificate): """Updates a certificate with the included options. The provided dict must include an 'id' key and value corresponding to the certificate ID that should be updated. :param dict certificate: the certificate to update. Example:: # Updates the cert id 1234 cert['id'] = 1234 cert['certificate'] = ?? result = mgr.edit_certificate(certificate=cert) """ return self.ssl.editObject(certificate, id=certificate['id']) def get_certificate(self, cert_id): """Gets a certificate with the ID specified. :param integer cert_id: the certificate ID to retrieve Example:: cert = mgr.get_certificate(cert_id=1234) print(cert) """ return self.ssl.getObject(id=cert_id) softlayer-python-5.4.2/SoftLayer/managers/storage_utils.py000066400000000000000000001267761324365065500240500ustar00rootroot00000000000000""" SoftLayer.storage_utils ~~~~~~~~~~~~~~~ Utility functions used by File and Block Storage Managers :license: MIT, see LICENSE for more details. """ from SoftLayer import exceptions from SoftLayer import utils # pylint: disable=too-many-lines ENDURANCE_TIERS = { 0.25: 100, 2: 200, 4: 300, 10: 1000, } def populate_host_templates(host_templates, hardware_ids=None, virtual_guest_ids=None, ip_address_ids=None, subnet_ids=None): """Populate the given host_templates array with the IDs provided :param host_templates: The array to which host templates will be added :param hardware_ids: A List of SoftLayer_Hardware ids :param virtual_guest_ids: A List of SoftLayer_Virtual_Guest ids :param ip_address_ids: A List of SoftLayer_Network_Subnet_IpAddress ids :param subnet_ids: A List of SoftLayer_Network_Subnet ids """ if hardware_ids is not None: for hardware_id in hardware_ids: host_templates.append({ 'objectType': 'SoftLayer_Hardware', 'id': hardware_id }) if virtual_guest_ids is not None: for virtual_guest_id in virtual_guest_ids: host_templates.append({ 'objectType': 'SoftLayer_Virtual_Guest', 'id': virtual_guest_id }) if ip_address_ids is not None: for ip_address_id in ip_address_ids: host_templates.append({ 'objectType': 'SoftLayer_Network_Subnet_IpAddress', 'id': ip_address_id }) if subnet_ids is not None: for subnet_id in subnet_ids: host_templates.append({ 'objectType': 'SoftLayer_Network_Subnet', 'id': subnet_id }) def get_package(manager, category_code): """Returns a product package based on type of storage. :param manager: The storage manager which calls this function. :param category_code: Category code of product package. :return: Returns a packaged based on type of storage. """ _filter = utils.NestedDict({}) _filter['categories']['categoryCode'] = ( utils.query_filter(category_code)) _filter['statusCode'] = (utils.query_filter('ACTIVE')) packages = manager.client.call( 'Product_Package', 'getAllObjects', filter=_filter.to_dict(), mask='id,name,items[prices[categories],attributes]' ) if len(packages) == 0: raise ValueError('No packages were found for %s' % category_code) if len(packages) > 1: raise ValueError('More than one package was found for %s' % category_code) return packages[0] def get_location_id(manager, location): """Returns location id :param manager: The storage manager which calls this function. :param location: Datacenter short name :return: Returns location id """ loc_svc = manager.client['Location_Datacenter'] datacenters = loc_svc.getDatacenters(mask='mask[longName,id,name]') for datacenter in datacenters: if datacenter['name'] == location: location = datacenter['id'] return location raise ValueError('Invalid datacenter name specified.') def find_price_by_category(package, price_category): """Find the price in the given package that has the specified category :param package: The AsAService, Enterprise, or Performance product package :param price_category: The price category code to search for :return: Returns the price for the given category, or an error if not found """ for item in package['items']: for price in item['prices']: if price['locationGroupId'] != '': continue if not _has_category(price['categories'], price_category): continue return {'id': price['id']} raise ValueError("Could not find price with the category, %s" % price_category) def find_ent_space_price(package, category, size, tier_level): """Find the space price for the given category, size, and tier :param package: The Enterprise (Endurance) product package :param category: The category of space (endurance, replication, snapshot) :param size: The size for which a price is desired :param tier_level: The endurance tier for which a price is desired :return: Returns the matching price, or an error if not found """ if category == 'snapshot': category_code = 'storage_snapshot_space' elif category == 'replication': category_code = 'performance_storage_replication' else: # category == 'endurance' category_code = 'performance_storage_space' for item in package['items']: if int(item['capacity']) != size: continue for price in item['prices']: # Only collect prices from valid location groups. if price['locationGroupId'] != '': continue level = ENDURANCE_TIERS.get(tier_level) if price['capacityRestrictionType'] != 'STORAGE_TIER_LEVEL'\ or level < int(price['capacityRestrictionMinimum'])\ or level > int(price['capacityRestrictionMaximum']): continue if not _has_category(price['categories'], category_code): continue return {'id': price['id']} raise ValueError("Could not find price for %s storage space" % category) def find_ent_endurance_tier_price(package, tier_level): """Find the price in the given package with the specified tier level :param package: The Enterprise (Endurance) product package :param tier_level: The endurance tier for which a price is desired :return: Returns the price for the given tier, or an error if not found """ for item in package['items']: for attribute in item.get('attributes', []): if int(attribute['value']) == ENDURANCE_TIERS.get(tier_level): break else: continue for price in item['prices']: # Only collect prices from valid location groups. if price['locationGroupId'] != '': continue if not _has_category(price['categories'], 'storage_tier_level'): continue return {'id': price['id']} raise ValueError("Could not find price for endurance tier level") def find_endurance_tier_iops_per_gb(volume): """Find the tier for the given endurance volume (IOPS per GB) :param volume: The volume for which the tier level is desired :return: Returns a float value indicating the IOPS per GB for the volume """ tier = volume['storageTierLevel'] iops_per_gb = 0.25 if tier == "LOW_INTENSITY_TIER": iops_per_gb = 0.25 elif tier == "READHEAVY_TIER": iops_per_gb = 2 elif tier == "WRITEHEAVY_TIER": iops_per_gb = 4 elif tier == "10_IOPS_PER_GB": iops_per_gb = 10 else: raise ValueError("Could not find tier IOPS per GB for this volume") return iops_per_gb def find_perf_space_price(package, size): """Find the price in the given package with the specified size :param package: The Performance product package :param size: The storage space size for which a price is desired :return: Returns the price for the given size, or an error if not found """ for item in package['items']: if int(item['capacity']) != size: continue for price in item['prices']: # Only collect prices from valid location groups. if price['locationGroupId'] != '': continue if not _has_category(price['categories'], 'performance_storage_space'): continue return {'id': price['id']} raise ValueError("Could not find performance space price for this volume") def find_perf_iops_price(package, size, iops): """Find the price in the given package with the specified size and iops :param package: The Performance product package :param size: The size of storage space for which an IOPS price is desired :param iops: The number of IOPS for which a price is desired :return: Returns the price for the size and IOPS, or an error if not found """ for item in package['items']: if int(item['capacity']) != int(iops): continue for price in item['prices']: # Only collect prices from valid location groups. if price['locationGroupId'] != '': continue if not _has_category(price['categories'], 'performance_storage_iops'): continue if price['capacityRestrictionType'] != 'STORAGE_SPACE'\ or size < int(price['capacityRestrictionMinimum'])\ or size > int(price['capacityRestrictionMaximum']): continue return {'id': price['id']} raise ValueError("Could not find price for iops for the given volume") def find_saas_endurance_space_price(package, size, tier_level): """Find the SaaS endurance storage space price for the size and tier :param package: The Storage As A Service product package :param size: The volume size for which a price is desired :param tier_level: The endurance tier for which a price is desired :return: Returns the price for the size and tier, or an error if not found """ if tier_level != 0.25: tier_level = int(tier_level) key_name = 'STORAGE_SPACE_FOR_{0}_IOPS_PER_GB'.format(tier_level) key_name = key_name.replace(".", "_") for item in package['items']: if item['keyName'] != key_name: continue if 'capacityMinimum' not in item or 'capacityMaximum' not in item: continue capacity_minimum = int(item['capacityMinimum']) capacity_maximum = int(item['capacityMaximum']) if size < capacity_minimum or size > capacity_maximum: continue for price in item['prices']: # Only collect prices from valid location groups. if price['locationGroupId'] != '': continue if not _has_category(price['categories'], 'performance_storage_space'): continue return {'id': price['id']} raise ValueError("Could not find price for endurance storage space") def find_saas_endurance_tier_price(package, tier_level): """Find the SaaS storage tier level price for the specified tier level :param package: The Storage As A Service product package :param tier_level: The endurance tier for which a price is desired :return: Returns the price for the given tier, or an error if not found """ target_capacity = ENDURANCE_TIERS.get(tier_level) for item in package['items']: if 'itemCategory' not in item\ or 'categoryCode' not in item['itemCategory']\ or item['itemCategory']['categoryCode']\ != 'storage_tier_level': continue if int(item['capacity']) != target_capacity: continue for price in item['prices']: # Only collect prices from valid location groups. if price['locationGroupId'] != '': continue if not _has_category(price['categories'], 'storage_tier_level'): continue return {'id': price['id']} raise ValueError("Could not find price for endurance tier level") def find_saas_perform_space_price(package, size): """Find the SaaS performance storage space price for the given size :param package: The Storage As A Service product package :param size: The volume size for which a price is desired :return: Returns the price for the size and tier, or an error if not found """ for item in package['items']: if 'itemCategory' not in item\ or 'categoryCode' not in item['itemCategory']\ or item['itemCategory']['categoryCode']\ != 'performance_storage_space': continue if 'capacityMinimum' not in item or 'capacityMaximum' not in item: continue capacity_minimum = int(item['capacityMinimum']) capacity_maximum = int(item['capacityMaximum']) if size < capacity_minimum or size > capacity_maximum: continue key_name = '{0}_{1}_GBS'.format(capacity_minimum, capacity_maximum) if item['keyName'] != key_name: continue for price in item['prices']: # Only collect prices from valid location groups. if price['locationGroupId'] != '': continue if not _has_category(price['categories'], 'performance_storage_space'): continue return {'id': price['id']} raise ValueError("Could not find price for performance storage space") def find_saas_perform_iops_price(package, size, iops): """Find the SaaS IOPS price for the specified size and iops :param package: The Storage As A Service product package :param size: The volume size for which a price is desired :param iops: The number of IOPS for which a price is desired :return: Returns the price for the size and IOPS, or an error if not found """ for item in package['items']: if 'itemCategory' not in item\ or 'categoryCode' not in item['itemCategory']\ or item['itemCategory']['categoryCode']\ != 'performance_storage_iops': continue if 'capacityMinimum' not in item or 'capacityMaximum' not in item: continue capacity_minimum = int(item['capacityMinimum']) capacity_maximum = int(item['capacityMaximum']) if iops < capacity_minimum or iops > capacity_maximum: continue for price in item['prices']: # Only collect prices from valid location groups. if price['locationGroupId'] != '': continue if not _has_category(price['categories'], 'performance_storage_iops'): continue if price['capacityRestrictionType'] != 'STORAGE_SPACE'\ or size < int(price['capacityRestrictionMinimum'])\ or size > int(price['capacityRestrictionMaximum']): continue return {'id': price['id']} raise ValueError("Could not find price for iops for the given volume") def find_saas_snapshot_space_price(package, size, tier=None, iops=None): """Find the price in the SaaS package for the desired snapshot space size :param package: The product package of the endurance storage type :param size: The snapshot space size for which a price is desired :param tier: The tier of the volume for which space is being ordered :param iops: The IOPS of the volume for which space is being ordered :return: Returns the price for the given size, or an error if not found """ if tier is not None: target_value = ENDURANCE_TIERS.get(tier) target_restriction_type = 'STORAGE_TIER_LEVEL' else: target_value = iops target_restriction_type = 'IOPS' for item in package['items']: if int(item['capacity']) != size: continue for price in item['prices']: # Only collect prices from valid location groups. if price['locationGroupId'] != '': continue if target_restriction_type != price['capacityRestrictionType']\ or target_value < int(price['capacityRestrictionMinimum'])\ or target_value > int(price['capacityRestrictionMaximum']): continue if not _has_category(price['categories'], 'storage_snapshot_space'): continue return {'id': price['id']} raise ValueError("Could not find price for snapshot space") def find_saas_replication_price(package, tier=None, iops=None): """Find the price in the given package for the desired replicant volume :param package: The product package of the endurance storage type :param tier: The tier of the primary storage volume :param iops: The IOPS of the primary storage volume :return: Returns the replication price, or an error if not found """ if tier is not None: target_value = ENDURANCE_TIERS.get(tier) target_item_keyname = 'REPLICATION_FOR_TIERBASED_PERFORMANCE' target_restriction_type = 'STORAGE_TIER_LEVEL' else: target_value = iops target_item_keyname = 'REPLICATION_FOR_IOPSBASED_PERFORMANCE' target_restriction_type = 'IOPS' for item in package['items']: if item['keyName'] != target_item_keyname: continue for price in item['prices']: # Only collect prices from valid location groups. if price['locationGroupId'] != '': continue if target_restriction_type != price['capacityRestrictionType']\ or target_value < int(price['capacityRestrictionMinimum'])\ or target_value > int(price['capacityRestrictionMaximum']): continue if not _has_category(price['categories'], 'performance_storage_replication'): continue return {'id': price['id']} raise ValueError("Could not find price for replicant volume") def find_snapshot_schedule_id(volume, snapshot_schedule_keyname): """Find the snapshot schedule ID for the given volume and keyname :param volume: The volume for which the snapshot ID is desired :param snapshot_schedule_keyname: The keyname of the snapshot schedule :return: Returns an int value indicating the volume's snapshot schedule ID """ for schedule in volume['schedules']: if 'type' in schedule and 'keyname' in schedule['type']: if schedule['type']['keyname'] == snapshot_schedule_keyname: return schedule['id'] raise ValueError("The given snapshot schedule ID was not found for " "the given storage volume") def prepare_snapshot_order_object(manager, volume, capacity, tier, upgrade): """Prepare the snapshot space order object for the placeOrder() method :param manager: The File or Block manager calling this function :param integer volume: The volume for which snapshot space is ordered :param integer capacity: The snapshot space size to order, in GB :param float tier: The tier level of the volume, in IOPS per GB (optional) :param boolean upgrade: Flag to indicate if this order is an upgrade :return: Returns the order object for the Product_Order service's placeOrder() method """ # Ensure the storage volume has not been cancelled if 'billingItem' not in volume: raise exceptions.SoftLayerError( 'This volume has been cancelled; unable to order snapshot space') # Determine and validate the storage volume's billing item category billing_item_category_code = volume['billingItem']['categoryCode'] if billing_item_category_code == 'storage_as_a_service': order_type_is_saas = True elif billing_item_category_code == 'storage_service_enterprise': order_type_is_saas = False else: raise exceptions.SoftLayerError( "Snapshot space cannot be ordered for a primary volume with a " "billing item category code of '%s'" % billing_item_category_code) # Use the volume's billing item category code to get the product package package = get_package(manager, billing_item_category_code) # Find prices based on the volume's type and billing item category if order_type_is_saas: # 'storage_as_a_service' package volume_storage_type = volume['storageType']['keyName'] if 'ENDURANCE' in volume_storage_type: if tier is None: tier = find_endurance_tier_iops_per_gb(volume) prices = [find_saas_snapshot_space_price( package, capacity, tier=tier)] elif 'PERFORMANCE' in volume_storage_type: if not _staas_version_is_v2_or_above(volume): raise exceptions.SoftLayerError( "Snapshot space cannot be ordered for this performance " "volume since it does not support Encryption at Rest.") iops = int(volume['provisionedIops']) prices = [find_saas_snapshot_space_price( package, capacity, iops=iops)] else: raise exceptions.SoftLayerError( "Storage volume does not have a valid storage type " "(with an appropriate keyName to indicate the " "volume is a PERFORMANCE or an ENDURANCE volume)") else: # 'storage_service_enterprise' package if tier is None: tier = find_endurance_tier_iops_per_gb(volume) prices = [find_ent_space_price(package, 'snapshot', capacity, tier)] # Currently, these types are valid for snapshot space orders, whether # the base volume's order container was Enterprise or AsAService if upgrade: complex_type = 'SoftLayer_Container_Product_Order_'\ 'Network_Storage_Enterprise_SnapshotSpace_Upgrade' else: complex_type = 'SoftLayer_Container_Product_Order_'\ 'Network_Storage_Enterprise_SnapshotSpace' # Determine if hourly billing should be used hourly_billing_flag = utils.lookup(volume, 'billingItem', 'hourlyFlag') if hourly_billing_flag is None: hourly_billing_flag = False # Build and return the order object snapshot_space_order = { 'complexType': complex_type, 'packageId': package['id'], 'prices': prices, 'quantity': 1, 'location': volume['billingItem']['location']['id'], 'volumeId': volume['id'], 'useHourlyPricing': hourly_billing_flag } return snapshot_space_order def prepare_volume_order_object(manager, storage_type, location, size, iops, tier, snapshot_size, service_offering, volume_type, hourly_billing_flag=False): """Prepare the order object which is submitted to the placeOrder() method :param manager: The File or Block manager calling this function :param storage_type: "performance" or "endurance" :param location: Requested datacenter location name for the ordered volume :param size: Desired size of the volume, in GB :param iops: Number of IOPs for a "Performance" volume order :param tier: Tier level to use for an "Endurance" volume order :param snapshot_size: The size of snapshot space for the volume (optional) :param service_offering: Requested offering package to use for the order :param volume_type: The type of the volume to order ('file' or 'block') :param hourly_billing_flag: Billing type, monthly (False) or hourly (True) :return: Returns the order object for the Product_Order service's placeOrder() method """ # Ensure the volume storage type is valid if storage_type != 'performance' and storage_type != 'endurance': raise exceptions.SoftLayerError( "Volume storage type must be either performance or endurance") # Find the ID for the requested location try: location_id = get_location_id(manager, location) except ValueError: raise exceptions.SoftLayerError( "Invalid datacenter name specified. " "Please provide the lower case short name (e.g.: dal09)") # Determine the category code to use for the order (and product package) order_type_is_saas, order_category_code = _get_order_type_and_category( service_offering, storage_type, volume_type ) # Get the product package for the given category code package = get_package(manager, order_category_code) # Based on the storage type and product package, build up the complex type # and array of price codes to include in the order object base_type_name = 'SoftLayer_Container_Product_Order_Network_' if order_type_is_saas: complex_type = base_type_name + 'Storage_AsAService' if storage_type == 'performance': prices = [ find_price_by_category(package, order_category_code), find_price_by_category(package, 'storage_' + volume_type), find_saas_perform_space_price(package, size), find_saas_perform_iops_price(package, size, iops) ] if snapshot_size is not None: prices.append(find_saas_snapshot_space_price( package, snapshot_size, iops=iops)) else: # storage_type == 'endurance' prices = [ find_price_by_category(package, order_category_code), find_price_by_category(package, 'storage_' + volume_type), find_saas_endurance_space_price(package, size, tier), find_saas_endurance_tier_price(package, tier) ] if snapshot_size is not None: prices.append(find_saas_snapshot_space_price( package, snapshot_size, tier=tier)) else: # offering package is enterprise or performance if storage_type == 'performance': if volume_type == 'block': complex_type = base_type_name + 'PerformanceStorage_Iscsi' else: complex_type = base_type_name + 'PerformanceStorage_Nfs' prices = [ find_price_by_category(package, order_category_code), find_perf_space_price(package, size), find_perf_iops_price(package, size, iops), ] else: # storage_type == 'endurance' complex_type = base_type_name + 'Storage_Enterprise' prices = [ find_price_by_category(package, order_category_code), find_price_by_category(package, 'storage_' + volume_type), find_ent_space_price(package, 'endurance', size, tier), find_ent_endurance_tier_price(package, tier), ] if snapshot_size is not None: prices.append(find_ent_space_price( package, 'snapshot', snapshot_size, tier)) # Build and return the order object order = { 'complexType': complex_type, 'packageId': package['id'], 'prices': prices, 'quantity': 1, 'location': location_id, 'useHourlyPricing': hourly_billing_flag } if order_type_is_saas: order['volumeSize'] = size if storage_type == 'performance': order['iops'] = iops return order def _get_order_type_and_category(service_offering, storage_type, volume_type): if service_offering == 'storage_as_a_service': order_type_is_saas = True order_category_code = 'storage_as_a_service' elif service_offering == 'enterprise': order_type_is_saas = False if storage_type == 'endurance': order_category_code = 'storage_service_enterprise' else: raise exceptions.SoftLayerError( "The requested offering package, '%s', is not available for " "the '%s' storage type." % (service_offering, storage_type)) elif service_offering == 'performance': order_type_is_saas = False if storage_type == 'performance': if volume_type == 'block': order_category_code = 'performance_storage_iscsi' else: order_category_code = 'performance_storage_nfs' else: raise exceptions.SoftLayerError( "The requested offering package, '%s', is not available for " "the '%s' storage type." % (service_offering, storage_type)) else: raise exceptions.SoftLayerError( "The requested service offering package is not valid. " "Please check the available options and try again.") return order_type_is_saas, order_category_code def prepare_replicant_order_object(manager, snapshot_schedule, location, tier, volume, volume_type): """Prepare the order object which is submitted to the placeOrder() method :param manager: The File or Block manager calling this function :param snapshot_schedule: The primary volume's snapshot schedule to use for replication :param location: The location for the ordered replicant volume :param tier: The tier (IOPS per GB) of the primary volume :param volume: The primary volume as a SoftLayer_Network_Storage object :param volume_type: The type of the primary volume ('file' or 'block') :return: Returns the order object for the Product_Order service's placeOrder() method """ # Ensure the primary volume and snapshot space are not set for cancellation if 'billingItem' not in volume\ or volume['billingItem']['cancellationDate'] != '': raise exceptions.SoftLayerError( 'This volume is set for cancellation; ' 'unable to order replicant volume') for child in volume['billingItem']['activeChildren']: if child['categoryCode'] == 'storage_snapshot_space'\ and child['cancellationDate'] != '': raise exceptions.SoftLayerError( 'The snapshot space for this volume is set for ' 'cancellation; unable to order replicant volume') # Find the ID for the requested location try: location_id = get_location_id(manager, location) except ValueError: raise exceptions.SoftLayerError( "Invalid datacenter name specified. " "Please provide the lower case short name (e.g.: dal09)") # Get sizes and properties needed for the order volume_size = int(volume['capacityGb']) billing_item_category_code = volume['billingItem']['categoryCode'] if billing_item_category_code == 'storage_as_a_service': order_type_is_saas = True elif billing_item_category_code == 'storage_service_enterprise': order_type_is_saas = False else: raise exceptions.SoftLayerError( "A replicant volume cannot be ordered for a primary volume with a " "billing item category code of '%s'" % billing_item_category_code) if 'snapshotCapacityGb' in volume: snapshot_size = int(volume['snapshotCapacityGb']) else: raise exceptions.SoftLayerError( "Snapshot capacity not found for the given primary volume") snapshot_schedule_id = find_snapshot_schedule_id( volume, 'SNAPSHOT_' + snapshot_schedule ) # Use the volume's billing item category code to get the product package package = get_package(manager, billing_item_category_code) # Find prices based on the primary volume's type and billing item category if order_type_is_saas: # 'storage_as_a_service' package complex_type = 'SoftLayer_Container_Product_Order_'\ 'Network_Storage_AsAService' volume_storage_type = volume['storageType']['keyName'] if 'ENDURANCE' in volume_storage_type: volume_is_performance = False if tier is None: tier = find_endurance_tier_iops_per_gb(volume) prices = [ find_price_by_category(package, billing_item_category_code), find_price_by_category(package, 'storage_' + volume_type), find_saas_endurance_space_price(package, volume_size, tier), find_saas_endurance_tier_price(package, tier), find_saas_snapshot_space_price( package, snapshot_size, tier=tier), find_saas_replication_price(package, tier=tier) ] elif 'PERFORMANCE' in volume_storage_type: if not _staas_version_is_v2_or_above(volume): raise exceptions.SoftLayerError( "A replica volume cannot be ordered for this performance " "volume since it does not support Encryption at Rest.") volume_is_performance = True iops = int(volume['provisionedIops']) prices = [ find_price_by_category(package, billing_item_category_code), find_price_by_category(package, 'storage_' + volume_type), find_saas_perform_space_price(package, volume_size), find_saas_perform_iops_price(package, volume_size, iops), find_saas_snapshot_space_price( package, snapshot_size, iops=iops), find_saas_replication_price(package, iops=iops) ] else: raise exceptions.SoftLayerError( "Storage volume does not have a valid storage type " "(with an appropriate keyName to indicate the " "volume is a PERFORMANCE or an ENDURANCE volume)") else: # 'storage_service_enterprise' package complex_type = 'SoftLayer_Container_Product_Order_'\ 'Network_Storage_Enterprise' volume_is_performance = False if tier is None: tier = find_endurance_tier_iops_per_gb(volume) prices = [ find_price_by_category(package, billing_item_category_code), find_price_by_category(package, 'storage_' + volume_type), find_ent_space_price(package, 'endurance', volume_size, tier), find_ent_endurance_tier_price(package, tier), find_ent_space_price(package, 'snapshot', snapshot_size, tier), find_ent_space_price(package, 'replication', volume_size, tier) ] # Determine if hourly billing should be used hourly_billing_flag = utils.lookup(volume, 'billingItem', 'hourlyFlag') if hourly_billing_flag is None: hourly_billing_flag = False # Build and return the order object replicant_order = { 'complexType': complex_type, 'packageId': package['id'], 'prices': prices, 'quantity': 1, 'location': location_id, 'originVolumeId': volume['id'], 'originVolumeScheduleId': snapshot_schedule_id, 'useHourlyPricing': hourly_billing_flag } if order_type_is_saas: replicant_order['volumeSize'] = volume_size if volume_is_performance: replicant_order['iops'] = iops return replicant_order def prepare_duplicate_order_object(manager, origin_volume, iops, tier, duplicate_size, duplicate_snapshot_size, volume_type, hourly_billing_flag=False): """Prepare the duplicate order to submit to SoftLayer_Product::placeOrder() :param manager: The File or Block manager calling this function :param origin_volume: The origin volume which is being duplicated :param iops: The IOPS for the duplicate volume (performance) :param tier: The tier level for the duplicate volume (endurance) :param duplicate_size: The requested size for the duplicate volume :param duplicate_snapshot_size: The size for the duplicate snapshot space :param volume_type: The type of the origin volume ('file' or 'block') :param hourly_billing_flag: Billing type, monthly (False) or hourly (True) :return: Returns the order object to be passed to the placeOrder() method of the Product_Order service """ # Verify that the origin volume has not been cancelled if 'billingItem' not in origin_volume: raise exceptions.SoftLayerError( "The origin volume has been cancelled; " "unable to order duplicate volume") # Verify that the origin volume has snapshot space (needed for duplication) if isinstance(utils.lookup(origin_volume, 'snapshotCapacityGb'), str): origin_snapshot_size = int(origin_volume['snapshotCapacityGb']) else: raise exceptions.SoftLayerError( "Snapshot space not found for the origin volume. " "Origin snapshot space is needed for duplication.") # Obtain the datacenter location ID for the duplicate if isinstance(utils.lookup(origin_volume, 'billingItem', 'location', 'id'), int): location_id = origin_volume['billingItem']['location']['id'] else: raise exceptions.SoftLayerError( "Cannot find origin volume's location") # Ensure the origin volume is STaaS v2 or higher # and supports Encryption at Rest if not _staas_version_is_v2_or_above(origin_volume): raise exceptions.SoftLayerError( "This volume cannot be duplicated since it " "does not support Encryption at Rest.") # If no specific snapshot space was requested for the duplicate, # use the origin snapshot space size if duplicate_snapshot_size is None: duplicate_snapshot_size = origin_snapshot_size # Use the origin volume size if no size was specified for the duplicate if duplicate_size is None: duplicate_size = origin_volume['capacityGb'] # Get the appropriate package for the order # ('storage_as_a_service' is currently used for duplicate volumes) package = get_package(manager, 'storage_as_a_service') # Determine the IOPS or tier level for the duplicate volume, along with # the type and prices for the order origin_storage_type = origin_volume['storageType']['keyName'] if 'PERFORMANCE' in origin_storage_type: volume_is_performance = True if iops is None: iops = int(origin_volume.get('provisionedIops', 0)) if iops <= 0: raise exceptions.SoftLayerError("Cannot find origin volume's provisioned IOPS") # Set up the price array for the order prices = [ find_price_by_category(package, 'storage_as_a_service'), find_price_by_category(package, 'storage_' + volume_type), find_saas_perform_space_price(package, duplicate_size), find_saas_perform_iops_price(package, duplicate_size, iops), ] # Add the price code for snapshot space as well, unless 0 GB was given if duplicate_snapshot_size > 0: prices.append(find_saas_snapshot_space_price( package, duplicate_snapshot_size, iops=iops)) elif 'ENDURANCE' in origin_storage_type: volume_is_performance = False if tier is None: tier = find_endurance_tier_iops_per_gb(origin_volume) # Set up the price array for the order prices = [ find_price_by_category(package, 'storage_as_a_service'), find_price_by_category(package, 'storage_' + volume_type), find_saas_endurance_space_price(package, duplicate_size, tier), find_saas_endurance_tier_price(package, tier), ] # Add the price code for snapshot space as well, unless 0 GB was given if duplicate_snapshot_size > 0: prices.append(find_saas_snapshot_space_price( package, duplicate_snapshot_size, tier=tier)) else: raise exceptions.SoftLayerError( "Origin volume does not have a valid storage type " "(with an appropriate keyName to indicate the " "volume is a PERFORMANCE or an ENDURANCE volume)") duplicate_order = { 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_Storage_AsAService', 'packageId': package['id'], 'prices': prices, 'volumeSize': duplicate_size, 'quantity': 1, 'location': location_id, 'duplicateOriginVolumeId': origin_volume['id'], 'useHourlyPricing': hourly_billing_flag } if volume_is_performance: duplicate_order['iops'] = iops return duplicate_order def prepare_modify_order_object(manager, volume, new_iops, new_tier, new_size): """Prepare the modification order to submit to SoftLayer_Product::placeOrder() :param manager: The File or Block manager calling this function :param volume: The volume which is being modified :param new_iops: The new IOPS for the volume (performance) :param new_tier: The new tier level for the volume (endurance) :param new_size: The requested new size for the volume :return: Returns the order object to be passed to the placeOrder() method of the Product_Order service """ # Verify that the origin volume has not been cancelled if 'billingItem' not in volume: raise exceptions.SoftLayerError("The volume has been cancelled; unable to modify volume.") # Ensure the origin volume is STaaS v2 or higher and supports Encryption at Rest if not _staas_version_is_v2_or_above(volume): raise exceptions.SoftLayerError("This volume cannot be modified since it does not support Encryption at Rest.") # Get the appropriate package for the order ('storage_as_a_service' is currently used for modifying volumes) package = get_package(manager, 'storage_as_a_service') # Based on volume storage type, ensure at least one volume property is being modified, # use current values if some are not specified, and lookup price codes for the order volume_storage_type = volume['storageType']['keyName'] if 'PERFORMANCE' in volume_storage_type: volume_is_performance = True if new_size is None and new_iops is None: raise exceptions.SoftLayerError("A size or IOPS value must be given to modify this performance volume.") if new_size is None: new_size = volume['capacityGb'] elif new_iops is None: new_iops = int(volume.get('provisionedIops', 0)) if new_iops <= 0: raise exceptions.SoftLayerError("Cannot find volume's provisioned IOPS.") # Set up the prices array for the order prices = [ find_price_by_category(package, 'storage_as_a_service'), find_saas_perform_space_price(package, new_size), find_saas_perform_iops_price(package, new_size, new_iops), ] elif 'ENDURANCE' in volume_storage_type: volume_is_performance = False if new_size is None and new_tier is None: raise exceptions.SoftLayerError("A size or tier value must be given to modify this endurance volume.") if new_size is None: new_size = volume['capacityGb'] elif new_tier is None: new_tier = find_endurance_tier_iops_per_gb(volume) # Set up the prices array for the order prices = [ find_price_by_category(package, 'storage_as_a_service'), find_saas_endurance_space_price(package, new_size, new_tier), find_saas_endurance_tier_price(package, new_tier), ] else: raise exceptions.SoftLayerError("Volume does not have a valid storage type (with an appropriate " "keyName to indicate the volume is a PERFORMANCE or an ENDURANCE volume).") modify_order = { 'complexType': 'SoftLayer_Container_Product_Order_Network_Storage_AsAService_Upgrade', 'packageId': package['id'], 'prices': prices, 'volume': {'id': volume['id']}, 'volumeSize': new_size } if volume_is_performance: modify_order['iops'] = new_iops return modify_order def _has_category(categories, category_code): return any( True for category in categories if category['categoryCode'] == category_code ) def _staas_version_is_v2_or_above(volume): return int(volume['staasVersion']) > 1 and volume['hasEncryptionAtRest'] softlayer-python-5.4.2/SoftLayer/managers/ticket.py000066400000000000000000000117771324365065500224410ustar00rootroot00000000000000""" SoftLayer.ticket ~~~~~~~~~~~~~~~~ Ticket Manager/helpers :license: MIT, see LICENSE for more details. """ from SoftLayer import utils class TicketManager(utils.IdentifierMixin, object): """Manages SoftLayer support tickets. See product information here: http://www.softlayer.com/support :param SoftLayer.API.BaseClient client: the client instance """ def __init__(self, client): self.client = client self.account = self.client['Account'] self.ticket = self.client['Ticket'] def list_tickets(self, open_status=True, closed_status=True): """List all tickets. :param boolean open_status: include open tickets :param boolean closed_status: include closed tickets """ mask = ('id, title, assignedUser[firstName, lastName],' 'createDate,lastEditDate,accountId,status') call = 'getTickets' if not all([open_status, closed_status]): if open_status: call = 'getOpenTickets' elif closed_status: call = 'getClosedTickets' return self.client.call('Account', call, mask=mask) def list_subjects(self): """List all ticket subjects.""" return self.client['Ticket_Subject'].getAllObjects() def get_ticket(self, ticket_id): """Get details about a ticket. :param integer ticket_id: the ticket ID :returns: dict -- information about the specified ticket """ mask = ('id, title, assignedUser[firstName, lastName],status,' 'createDate,lastEditDate,updates[entry,editor],updateCount') return self.ticket.getObject(id=ticket_id, mask=mask) def create_ticket(self, title=None, body=None, subject=None): """Create a new ticket. :param string title: title for the new ticket :param string body: body for the new ticket :param integer subject: id of the subject to be assigned to the ticket """ current_user = self.account.getCurrentUser() new_ticket = { 'subjectId': subject, 'contents': body, 'assignedUserId': current_user['id'], 'title': title, } created_ticket = self.ticket.createStandardTicket(new_ticket, body) return created_ticket def update_ticket(self, ticket_id=None, body=None): """Update a ticket. :param integer ticket_id: the id of the ticket to update :param string body: entry to update in the ticket """ return self.ticket.addUpdate({'entry': body}, id=ticket_id) def upload_attachment(self, ticket_id=None, file_path=None, file_name=None): """Upload an attachment to a ticket. :param integer ticket_id: the id of the ticket to upload the attachment to :param string file_path: The path of the attachment to be uploaded :param string file_name: The name of the attachment shown in the ticket :returns: dict -- The uploaded attachment """ file_content = None with open(file_path, 'rb') as attached_file: file_content = attached_file.read() file_object = { "filename": file_name, "data": file_content } return self.ticket.addAttachedFile(file_object, id=ticket_id) def attach_hardware(self, ticket_id=None, hardware_id=None): """Attach hardware to a ticket. :param integer ticket_id: the id of the ticket to attach to :param integer hardware_id: the id of the hardware to attach :returns: dict -- The new ticket attachment """ return self.ticket.addAttachedHardware(hardware_id, id=ticket_id) def attach_virtual_server(self, ticket_id=None, virtual_id=None): """Attach a virtual server to a ticket. :param integer ticket_id: the id of the ticket to attach to :param integer virtual_id: the id of the virtual server to attach :returns: dict -- The new ticket attachment """ return self.ticket.addAttachedVirtualGuest(virtual_id, id=ticket_id) def detach_hardware(self, ticket_id=None, hardware_id=None): """Detach hardware from a ticket. :param ticket_id: the id of the ticket to detach from :param hardware_id: the id of the hardware to detach :returns: bool -- Whether the detachment was successful """ return self.ticket.removeAttachedHardware(hardware_id, id=ticket_id) def detach_virtual_server(self, ticket_id=None, virtual_id=None): """Detach a virtual server from a ticket. :param ticket_id: the id of the ticket to detach from :param virtual_id: the id of the virtual server to detach :returns: bool -- Whether the detachment was successful """ return self.ticket.removeAttachedVirtualGuest(virtual_id, id=ticket_id) softlayer-python-5.4.2/SoftLayer/managers/vs.py000066400000000000000000001107401324365065500215740ustar00rootroot00000000000000""" SoftLayer.vs ~~~~~~~~~~~~ VS Manager/helpers :license: MIT, see LICENSE for more details. """ import datetime import logging import socket import time import warnings from SoftLayer.decoration import retry from SoftLayer import exceptions from SoftLayer.managers import ordering from SoftLayer import utils LOGGER = logging.getLogger(__name__) # pylint: disable=no-self-use class VSManager(utils.IdentifierMixin, object): """Manages SoftLayer Virtual Servers. See product information here: http://www.softlayer.com/virtual-servers Example:: # Initialize the VSManager. # env variables. These can also be specified in ~/.softlayer, # or passed directly to SoftLayer.Client() # SL_USERNAME = YOUR_USERNAME # SL_API_KEY = YOUR_API_KEY import SoftLayer client = SoftLayer.Client() mgr = SoftLayer.VSManager(client) :param SoftLayer.API.BaseClient client: the client instance :param SoftLayer.managers.OrderingManager ordering_manager: an optional manager to handle ordering. If none is provided, one will be auto initialized. """ def __init__(self, client, ordering_manager=None): self.client = client self.account = client['Account'] self.guest = client['Virtual_Guest'] self.resolvers = [self._get_ids_from_ip, self._get_ids_from_hostname] if ordering_manager is None: self.ordering_manager = ordering.OrderingManager(client) else: self.ordering_manager = ordering_manager @retry(logger=LOGGER) def list_instances(self, hourly=True, monthly=True, tags=None, cpus=None, memory=None, hostname=None, domain=None, local_disk=None, datacenter=None, nic_speed=None, public_ip=None, private_ip=None, **kwargs): """Retrieve a list of all virtual servers on the account. Example:: # Print out a list of hourly instances in the DAL05 data center. for vsi in mgr.list_instances(hourly=True, datacenter='dal05'): print vsi['fullyQualifiedDomainName'], vsi['primaryIpAddress'] # Using a custom object-mask. Will get ONLY what is specified object_mask = "mask[hostname,monitoringRobot[robotStatus]]" for vsi in mgr.list_instances(mask=object_mask,hourly=True): print vsi :param boolean hourly: include hourly instances :param boolean monthly: include monthly instances :param list tags: filter based on list of tags :param integer cpus: filter based on number of CPUS :param integer memory: filter based on amount of memory :param string hostname: filter based on hostname :param string domain: filter based on domain :param string local_disk: filter based on local_disk :param string datacenter: filter based on datacenter :param integer nic_speed: filter based on network speed (in MBPS) :param string public_ip: filter based on public ip address :param string private_ip: filter based on private ip address :param dict \\*\\*kwargs: response-level options (mask, limit, etc.) :returns: Returns a list of dictionaries representing the matching virtual servers """ if 'mask' not in kwargs: items = [ 'id', 'globalIdentifier', 'hostname', 'domain', 'fullyQualifiedDomainName', 'primaryBackendIpAddress', 'primaryIpAddress', 'lastKnownPowerState.name', 'powerState', 'maxCpu', 'maxMemory', 'datacenter', 'activeTransaction.transactionStatus[friendlyName,name]', 'status', ] kwargs['mask'] = "mask[%s]" % ','.join(items) call = 'getVirtualGuests' if not all([hourly, monthly]): if hourly: call = 'getHourlyVirtualGuests' elif monthly: call = 'getMonthlyVirtualGuests' _filter = utils.NestedDict(kwargs.get('filter') or {}) if tags: _filter['virtualGuests']['tagReferences']['tag']['name'] = { 'operation': 'in', 'options': [{'name': 'data', 'value': tags}], } if cpus: _filter['virtualGuests']['maxCpu'] = utils.query_filter(cpus) if memory: _filter['virtualGuests']['maxMemory'] = utils.query_filter(memory) if hostname: _filter['virtualGuests']['hostname'] = utils.query_filter(hostname) if domain: _filter['virtualGuests']['domain'] = utils.query_filter(domain) if local_disk is not None: _filter['virtualGuests']['localDiskFlag'] = ( utils.query_filter(bool(local_disk))) if datacenter: _filter['virtualGuests']['datacenter']['name'] = ( utils.query_filter(datacenter)) if nic_speed: _filter['virtualGuests']['networkComponents']['maxSpeed'] = ( utils.query_filter(nic_speed)) if public_ip: _filter['virtualGuests']['primaryIpAddress'] = ( utils.query_filter(public_ip)) if private_ip: _filter['virtualGuests']['primaryBackendIpAddress'] = ( utils.query_filter(private_ip)) kwargs['filter'] = _filter.to_dict() func = getattr(self.account, call) return func(**kwargs) @retry(logger=LOGGER) def get_instance(self, instance_id, **kwargs): """Get details about a virtual server instance. :param integer instance_id: the instance ID :returns: A dictionary containing a large amount of information about the specified instance. Example:: # Print out instance ID 12345. vsi = mgr.get_instance(12345) print vsi # Print out only FQDN and primaryIP for instance 12345 object_mask = "mask[fullyQualifiedDomainName,primaryIpAddress]" vsi = mgr.get_instance(12345, mask=mask) print vsi """ if 'mask' not in kwargs: kwargs['mask'] = ( 'id,' 'globalIdentifier,' 'fullyQualifiedDomainName,' 'hostname,' 'domain,' 'createDate,' 'modifyDate,' 'provisionDate,' 'notes,' 'dedicatedAccountHostOnlyFlag,' 'privateNetworkOnlyFlag,' 'primaryBackendIpAddress,' 'primaryIpAddress,' '''networkComponents[id, status, speed, maxSpeed, name, macAddress, primaryIpAddress, port, primarySubnet, securityGroupBindings[ securityGroup[id, name]]],''' 'lastKnownPowerState.name,' 'powerState,' 'status,' 'maxCpu,' 'maxMemory,' 'datacenter,' 'activeTransaction[id, transactionStatus[friendlyName,name]],' 'lastOperatingSystemReload.id,' 'blockDevices,' 'blockDeviceTemplateGroup[id, name, globalIdentifier],' 'postInstallScriptUri,' '''operatingSystem[passwords[username,password], softwareLicense.softwareDescription[ manufacturer,name,version, referenceCode]],''' '''softwareComponents[ passwords[username,password,notes], softwareLicense[softwareDescription[ manufacturer,name,version, referenceCode]]],''' 'hourlyBillingFlag,' 'userData,' '''billingItem[id,nextInvoiceTotalRecurringAmount, children[categoryCode,nextInvoiceTotalRecurringAmount], orderItem[id, order.userRecord[username], preset.keyName]],''' 'tagReferences[id,tag[name,id]],' 'networkVlans[id,vlanNumber,networkSpace],' 'dedicatedHost.id' ) return self.guest.getObject(id=instance_id, **kwargs) @retry(logger=LOGGER) def get_create_options(self): """Retrieves the available options for creating a VS. :returns: A dictionary of creation options. Example:: # Prints out the create option dictionary options = mgr.get_create_options() print(options) """ return self.guest.getCreateObjectOptions() def cancel_instance(self, instance_id): """Cancel an instance immediately, deleting all its data. :param integer instance_id: the instance ID to cancel Example:: # Cancels instance 12345 mgr.cancel_instance(12345) """ return self.guest.deleteObject(id=instance_id) def reload_instance(self, instance_id, post_uri=None, ssh_keys=None, image_id=None): """Perform an OS reload of an instance. :param integer instance_id: the instance ID to reload :param string post_url: The URI of the post-install script to run after reload :param list ssh_keys: The SSH keys to add to the root user :param int image_id: The ID of the image to load onto the server .. warning:: This will reformat the primary drive. Post-provision script MUST be HTTPS for it to be executed. Example:: # Reload instance ID 12345 then run a custom post-provision script. # Post-provision script MUST be HTTPS for it to be executed. post_uri = 'https://somehost.com/bootstrap.sh' vsi = mgr.reload_instance(12345, post_uri=post_url) """ config = {} if post_uri: config['customProvisionScriptUri'] = post_uri if ssh_keys: config['sshKeyIds'] = [key_id for key_id in ssh_keys] if image_id: config['imageTemplateId'] = image_id return self.client.call('Virtual_Guest', 'reloadOperatingSystem', 'FORCE', config, id=instance_id) def _generate_create_dict( self, cpus=None, memory=None, hourly=True, hostname=None, domain=None, local_disk=True, datacenter=None, os_code=None, image_id=None, dedicated=False, public_vlan=None, private_vlan=None, userdata=None, nic_speed=None, disks=None, post_uri=None, private=False, ssh_keys=None, public_security_groups=None, private_security_groups=None, boot_mode=None, **kwargs): """Returns a dict appropriate to pass into Virtual_Guest::createObject See :func:`create_instance` for a list of available options. """ required = [hostname, domain] flavor = kwargs.get('flavor', None) host_id = kwargs.get('host_id', None) mutually_exclusive = [ {'os_code': os_code, 'image_id': image_id}, {'cpu': cpus, 'flavor': flavor}, {'memory': memory, 'flavor': flavor}, {'flavor': flavor, 'dedicated': dedicated}, {'flavor': flavor, 'host_id': host_id} ] if not all(required): raise ValueError("hostname, and domain are required") for mu_ex in mutually_exclusive: if all(mu_ex.values()): raise ValueError( 'Can only specify one of: %s' % (','.join(mu_ex.keys()))) data = { "startCpus": cpus, "maxMemory": memory, "hostname": hostname, "domain": domain, "localDiskFlag": local_disk, "hourlyBillingFlag": hourly, "supplementalCreateObjectOptions": { "bootMode": boot_mode } } if flavor: data["supplementalCreateObjectOptions"]["flavorKeyName"] = flavor if dedicated and not host_id: data["dedicatedAccountHostOnlyFlag"] = dedicated if host_id: data["dedicatedHost"] = {"id": host_id} if private: data['privateNetworkOnlyFlag'] = private if image_id: data["blockDeviceTemplateGroup"] = {"globalIdentifier": image_id} elif os_code: data["operatingSystemReferenceCode"] = os_code if datacenter: data["datacenter"] = {"name": datacenter} if public_vlan: data.update({ 'primaryNetworkComponent': { "networkVlan": {"id": int(public_vlan)}}}) if private_vlan: data.update({ "primaryBackendNetworkComponent": { "networkVlan": {"id": int(private_vlan)}}}) if public_security_groups: secgroups = [{'securityGroup': {'id': int(sg)}} for sg in public_security_groups] pnc = data.get('primaryNetworkComponent', {}) pnc['securityGroupBindings'] = secgroups data.update({'primaryNetworkComponent': pnc}) if private_security_groups: secgroups = [{'securityGroup': {'id': int(sg)}} for sg in private_security_groups] pbnc = data.get('primaryBackendNetworkComponent', {}) pbnc['securityGroupBindings'] = secgroups data.update({'primaryBackendNetworkComponent': pbnc}) if userdata: data['userData'] = [{'value': userdata}] if nic_speed: data['networkComponents'] = [{'maxSpeed': nic_speed}] if disks: data['blockDevices'] = [ {"device": "0", "diskImage": {"capacity": disks[0]}} ] for dev_id, disk in enumerate(disks[1:], start=2): data['blockDevices'].append( { "device": str(dev_id), "diskImage": {"capacity": disk} } ) if post_uri: data['postInstallScriptUri'] = post_uri if ssh_keys: data['sshKeys'] = [{'id': key_id} for key_id in ssh_keys] return data @retry(logger=LOGGER) def wait_for_transaction(self, instance_id, limit, delay=10): """Waits on a VS transaction for the specified amount of time. This is really just a wrapper for wait_for_ready(pending=True). Provided for backwards compatibility. :param int instance_id: The instance ID with the pending transaction :param int limit: The maximum amount of time to wait. :param int delay: The number of seconds to sleep before checks. Defaults to 10. """ return self.wait_for_ready(instance_id, limit, delay=delay, pending=True) def wait_for_ready(self, instance_id, limit=3600, delay=10, pending=False): """Determine if a VS is ready and available. In some cases though, that can mean that no transactions are running. The default arguments imply a VS is operational and ready for use by having network connectivity and remote access is available. Setting ``pending=True`` will ensure future API calls against this instance will not error due to pending transactions such as OS Reloads and cancellations. :param int instance_id: The instance ID with the pending transaction :param int limit: The maximum amount of seconds to wait. :param int delay: The number of seconds to sleep before checks. Defaults to 10. :param bool pending: Wait for pending transactions not related to provisioning or reloads such as monitoring. Example:: # Will return once vsi 12345 is ready, or after 10 checks ready = mgr.wait_for_ready(12345, 10) """ now = time.time() until = now + limit mask = "mask[id, lastOperatingSystemReload[id], activeTransaction, provisionDate]" while now <= until: instance = self.get_instance(instance_id, mask=mask) if utils.is_ready(instance, pending): return True transaction = utils.lookup(instance, 'activeTransaction', 'transactionStatus', 'friendlyName') snooze = min(delay, until - now) LOGGER.info("%s - %d not ready. Auto retry in %ds", transaction, instance_id, snooze) time.sleep(snooze) now = time.time() LOGGER.info("Waiting for %d expired.", instance_id) return False def verify_create_instance(self, **kwargs): """Verifies an instance creation command. Without actually placing an order. See :func:`create_instance` for a list of available options. Example:: new_vsi = { 'domain': u'test01.labs.sftlyr.ws', 'hostname': u'minion05', 'datacenter': u'hkg02', 'dedicated': False, 'private': False, 'cpus': 1, 'os_code' : u'UBUNTU_LATEST', 'hourly': True, 'ssh_keys': [1234], 'disks': ('100','25'), 'local_disk': True, 'memory': 1024 } vsi = mgr.verify_create_instance(**new_vsi) # vsi will be a SoftLayer_Container_Product_Order_Virtual_Guest # if your order is correct. Otherwise you will get an exception print vsi """ kwargs.pop('tags', None) create_options = self._generate_create_dict(**kwargs) return self.guest.generateOrderTemplate(create_options) def create_instance(self, **kwargs): """Creates a new virtual server instance. .. warning:: This will add charges to your account Example:: new_vsi = { 'domain': u'test01.labs.sftlyr.ws', 'hostname': u'minion05', 'datacenter': u'hkg02', 'dedicated': False, 'private': False, 'cpus': 1, 'os_code' : u'UBUNTU_LATEST', 'hourly': True, 'ssh_keys': [1234], 'disks': ('100','25'), 'local_disk': True, 'memory': 1024, 'tags': 'test, pleaseCancel', 'public_security_groups': [12, 15] } vsi = mgr.create_instance(**new_vsi) # vsi will have the newly created vsi details if done properly. print vsi :param int cpus: The number of virtual CPUs to include in the instance. :param int memory: The amount of RAM to order. :param bool hourly: Flag to indicate if this server should be billed hourly (default) or monthly. :param string hostname: The hostname to use for the new server. :param string domain: The domain to use for the new server. :param bool local_disk: Flag to indicate if this should be a local disk (default) or a SAN disk. :param string datacenter: The short name of the data center in which the VS should reside. :param string os_code: The operating system to use. Cannot be specified if image_id is specified. :param int image_id: The ID of the image to load onto the server. Cannot be specified if os_code is specified. :param bool dedicated: Flag to indicate if this should be housed on adedicated or shared host (default). This will incur a fee on your account. :param int public_vlan: The ID of the public VLAN on which you want this VS placed. :param list public_security_groups: The list of security group IDs to apply to the public interface :param list private_security_groups: The list of security group IDs to apply to the private interface :param int private_vlan: The ID of the private VLAN on which you want this VS placed. :param list disks: A list of disk capacities for this server. :param string post_uri: The URI of the post-install script to run after reload :param bool private: If true, the VS will be provisioned only with access to the private network. Defaults to false :param list ssh_keys: The SSH keys to add to the root user :param int nic_speed: The port speed to set :param string tags: tags to set on the VS as a comma separated list :param string flavor: The key name of the public virtual server flavor being ordered. :param int host_id: The host id of a dedicated host to provision a dedicated host virtual server on. """ tags = kwargs.pop('tags', None) inst = self.guest.createObject(self._generate_create_dict(**kwargs)) if tags is not None: self.set_tags(tags, guest_id=inst['id']) return inst @retry(logger=LOGGER) def set_tags(self, tags, guest_id): """Sets tags on a guest with a retry decorator Just calls guest.setTags, but if it fails from an APIError will retry """ self.guest.setTags(tags, id=guest_id) def create_instances(self, config_list): """Creates multiple virtual server instances. This takes a list of dictionaries using the same arguments as create_instance(). .. warning:: This will add charges to your account Example:: # Define the instance we want to create. new_vsi = { 'domain': u'test01.labs.sftlyr.ws', 'hostname': u'multi-test', 'datacenter': u'hkg02', 'dedicated': False, 'private': False, 'cpus': 1, 'os_code' : u'UBUNTU_LATEST', 'hourly': True, 'ssh_keys': [87634], 'disks': ('100','25'), 'local_disk': True, 'memory': 1024, 'tags': 'test, pleaseCancel', 'public_security_groups': [12, 15] } # using .copy() so we can make changes to individual nodes instances = [new_vsi.copy(), new_vsi.copy(), new_vsi.copy()] # give each its own hostname, not required. instances[0]['hostname'] = "multi-test01" instances[1]['hostname'] = "multi-test02" instances[2]['hostname'] = "multi-test03" vsi = mgr.create_instances(config_list=instances) #vsi will be a dictionary of all the new virtual servers print vsi """ tags = [conf.pop('tags', None) for conf in config_list] resp = self.guest.createObjects([self._generate_create_dict(**kwargs) for kwargs in config_list]) for instance, tag in zip(resp, tags): if tag is not None: self.set_tags(tag, guest_id=instance['id']) return resp def change_port_speed(self, instance_id, public, speed): """Allows you to change the port speed of a virtual server's NICs. Example:: #change the Public interface to 10Mbps on instance 12345 result = mgr.change_port_speed(instance_id=12345, public=True, speed=10) # result will be True or an Exception :param int instance_id: The ID of the VS :param bool public: Flag to indicate which interface to change. True (default) means the public interface. False indicates the private interface. :param int speed: The port speed to set. .. warning:: A port speed of 0 will disable the interface. """ if public: return self.client.call('Virtual_Guest', 'setPublicNetworkInterfaceSpeed', speed, id=instance_id) else: return self.client.call('Virtual_Guest', 'setPrivateNetworkInterfaceSpeed', speed, id=instance_id) def _get_ids_from_hostname(self, hostname): """List VS ids which match the given hostname.""" results = self.list_instances(hostname=hostname, mask="id") return [result['id'] for result in results] def _get_ids_from_ip(self, ip_address): # pylint: disable=inconsistent-return-statements """List VS ids which match the given ip address.""" try: # Does it look like an ip address? socket.inet_aton(ip_address) except socket.error: return [] # Find the VS via ip address. First try public ip, then private results = self.list_instances(public_ip=ip_address, mask="id") if results: return [result['id'] for result in results] results = self.list_instances(private_ip=ip_address, mask="id") if results: return [result['id'] for result in results] def edit(self, instance_id, userdata=None, hostname=None, domain=None, notes=None, tags=None): """Edit hostname, domain name, notes, and/or the user data of a VS. Parameters set to None will be ignored and not attempted to be updated. :param integer instance_id: the instance ID to edit :param string userdata: user data on VS to edit. If none exist it will be created :param string hostname: valid hostname :param string domain: valid domain namem :param string notes: notes about this particular VS :param string tags: tags to set on the VS as a comma separated list. Use the empty string to remove all tags. :returns: bool -- True or an Exception Example:: # Change the hostname on instance 12345 to 'something' result = mgr.edit(instance_id=12345 , hostname="something") #result will be True or an Exception """ obj = {} if userdata: self.guest.setUserMetadata([userdata], id=instance_id) if tags is not None: self.set_tags(tags, guest_id=instance_id) if hostname: obj['hostname'] = hostname if domain: obj['domain'] = domain if notes: obj['notes'] = notes if not obj: return True return self.guest.editObject(obj, id=instance_id) def rescue(self, instance_id): """Reboot a VSI into the Xen recsue kernel. :param integer instance_id: the instance ID to rescue :returns: bool -- True or an Exception Example:: # Puts instance 12345 into rescue mode result = mgr.rescue(instance_id=12345) """ return self.guest.executeRescueLayer(id=instance_id) def capture(self, instance_id, name, additional_disks=False, notes=None): """Capture one or all disks from a VS to a SoftLayer image. Parameters set to None will be ignored and not attempted to be updated. :param integer instance_id: the instance ID to edit :param string name: name assigned to the image :param bool additional_disks: set to true to include all additional attached storage devices :param string notes: notes about this particular image :returns: dictionary -- information about the capture transaction. Example:: name = "Testing Images" notes = "Some notes about this image" result = mgr.capture(instance_id=12345, name=name, notes=notes) """ vsi = self.client.call( 'Virtual_Guest', 'getObject', id=instance_id, mask="""id, blockDevices[id,device,mountType, diskImage[id,metadataFlag,type[keyName]]]""") disks_to_capture = [] for block_device in vsi['blockDevices']: # We never want metadata disks if utils.lookup(block_device, 'diskImage', 'metadataFlag'): continue # We never want swap devices type_name = utils.lookup(block_device, 'diskImage', 'type', 'keyName') if type_name == 'SWAP': continue # We never want CD images if block_device['mountType'] == 'CD': continue # Only use the first block device if we don't want additional disks if not additional_disks and str(block_device['device']) != '0': continue disks_to_capture.append(block_device) return self.guest.createArchiveTransaction( name, disks_to_capture, notes, id=instance_id) def upgrade(self, instance_id, cpus=None, memory=None, nic_speed=None, public=True): """Upgrades a VS instance. Example:: # Upgrade instance 12345 to 4 CPUs and 4 GB of memory import SoftLayer client = SoftLayer.create_client_from_env() mgr = SoftLayer.VSManager(client) mgr.upgrade(12345, cpus=4, memory=4) :param int instance_id: Instance id of the VS to be upgraded :param int cpus: The number of virtual CPUs to upgrade to of a VS instance. :param int memory: RAM of the VS to be upgraded to. :param int nic_speed: The port speed to set :param bool public: CPU will be in Private/Public Node. :returns: bool """ upgrade_prices = self._get_upgrade_prices(instance_id) prices = [] for option, value in {'cpus': cpus, 'memory': memory, 'nic_speed': nic_speed}.items(): if not value: continue price_id = self._get_price_id_for_upgrade_option(upgrade_prices, option, value, public) if not price_id: # Every option provided is expected to have a price raise exceptions.SoftLayerError( "Unable to find %s option with value %s" % (option, value)) prices.append({'id': price_id}) maintenance_window = datetime.datetime.now(utils.UTC()) order = { 'complexType': 'SoftLayer_Container_Product_Order_Virtual_Guest_' 'Upgrade', 'prices': prices, 'properties': [{ 'name': 'MAINTENANCE_WINDOW', 'value': maintenance_window.strftime("%Y-%m-%d %H:%M:%S%z") }], 'virtualGuests': [{'id': int(instance_id)}], } if prices: self.client['Product_Order'].placeOrder(order) return True return False def _get_package_items(self): """Following Method gets all the item ids related to VS. Deprecated in favor of _get_upgrade_prices() """ warnings.warn("use _get_upgrade_prices() instead", DeprecationWarning) mask = [ 'description', 'capacity', 'units', 'prices[id,locationGroupId,categories[name,id,categoryCode]]' ] mask = "mask[%s]" % ','.join(mask) package_keyname = "CLOUD_SERVER" package = self.ordering_manager.get_package_by_key(package_keyname) package_service = self.client['Product_Package'] return package_service.getItems(id=package['id'], mask=mask) def _get_upgrade_prices(self, instance_id, include_downgrade_options=True): """Following Method gets all the price ids related to upgrading a VS. :param int instance_id: Instance id of the VS to be upgraded :returns: list """ mask = [ 'id', 'locationGroupId', 'categories[name,id,categoryCode]', 'item[description,capacity,units]' ] mask = "mask[%s]" % ','.join(mask) return self.guest.getUpgradeItemPrices(include_downgrade_options, id=instance_id, mask=mask) # pylint: disable=inconsistent-return-statements def _get_price_id_for_upgrade_option(self, upgrade_prices, option, value, public=True): """Find the price id for the option and value to upgrade. This :param list upgrade_prices: Contains all the prices related to a VS upgrade :param string option: Describes type of parameter to be upgraded :param int value: The value of the parameter to be upgraded :param bool public: CPU will be in Private/Public Node. """ option_category = { 'memory': 'ram', 'cpus': 'guest_core', 'nic_speed': 'port_speed' } category_code = option_category.get(option) for price in upgrade_prices: if price.get('categories') is None or price.get('item') is None: continue product = price.get('item') is_private = (product.get('units') == 'PRIVATE_CORE' or product.get('units') == 'DEDICATED_CORE') for category in price.get('categories'): if not (category.get('categoryCode') == category_code and str(product.get('capacity')) == str(value)): continue if option == 'cpus': # Public upgrade and public guest_core price if public and not is_private: return price.get('id') # Private upgrade and private guest_core price elif not public and is_private: return price.get('id') elif option == 'nic_speed': if 'Public' in product.get('description'): return price.get('id') else: return price.get('id') # pylint: disable=inconsistent-return-statements def _get_price_id_for_upgrade(self, package_items, option, value, public=True): """Find the price id for the option and value to upgrade. Deprecated in favor of _get_price_id_for_upgrade_option() :param list package_items: Contains all the items related to an VS :param string option: Describes type of parameter to be upgraded :param int value: The value of the parameter to be upgraded :param bool public: CPU will be in Private/Public Node. """ warnings.warn("use _get_price_id_for_upgrade_option() instead", DeprecationWarning) option_category = { 'memory': 'ram', 'cpus': 'guest_core', 'nic_speed': 'port_speed' } category_code = option_category[option] for item in package_items: is_private = (item.get('units') == 'PRIVATE_CORE') for price in item['prices']: if 'locationGroupId' in price and price['locationGroupId']: # Skip location based prices continue if 'categories' not in price: continue categories = price['categories'] for category in categories: if not (category['categoryCode'] == category_code and str(item['capacity']) == str(value)): continue if option == 'cpus': if public and not is_private: return price['id'] elif not public and is_private: return price['id'] elif option == 'nic_speed': if 'Public' in item['description']: return price['id'] else: return price['id'] softlayer-python-5.4.2/SoftLayer/shell/000077500000000000000000000000001324365065500201015ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/shell/__init__.py000066400000000000000000000000001324365065500222000ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/shell/cmd_env.py000066400000000000000000000007151324365065500220710ustar00rootroot00000000000000"""Print environment variables.""" # :license: MIT, see LICENSE for more details. import click from SoftLayer.CLI import environment from SoftLayer.CLI import formatting @click.command() @environment.pass_env def cli(env): """Print environment variables.""" filtered_vars = dict([(k, v) for k, v in env.vars.items() if not k.startswith('_')]) env.fout(formatting.iter_to_table(filtered_vars)) softlayer-python-5.4.2/SoftLayer/shell/cmd_exit.py000066400000000000000000000003101324365065500222410ustar00rootroot00000000000000"""Exit the shell.""" # :license: MIT, see LICENSE for more details. import click from SoftLayer.shell import core @click.command() def cli(): """Exit the shell.""" raise core.ShellExit() softlayer-python-5.4.2/SoftLayer/shell/cmd_help.py000066400000000000000000000017721324365065500222350ustar00rootroot00000000000000"""Print help text.""" # :license: MIT, see LICENSE for more details. import click from click import formatting from SoftLayer.CLI import core as cli_core from SoftLayer.CLI import environment from SoftLayer.shell import routes @click.command() @environment.pass_env @click.pass_context def cli(ctx, env): """Print shell help text.""" env.out("Welcome to the SoftLayer shell.") env.out("") formatter = formatting.HelpFormatter() commands = [] shell_commands = [] for name in cli_core.cli.list_commands(ctx): command = cli_core.cli.get_command(ctx, name) details = (name, command.short_help) if name in dict(routes.ALL_ROUTES): shell_commands.append(details) else: commands.append(details) with formatter.section('Shell Commands'): formatter.write_dl(shell_commands) with formatter.section('Commands'): formatter.write_dl(commands) for line in formatter.buffer: env.out(line, newline=False) softlayer-python-5.4.2/SoftLayer/shell/completer.py000066400000000000000000000045031324365065500224470ustar00rootroot00000000000000""" SoftLayer.CLI.shell.completer ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Click completer for prompt_toolkit :license: MIT, see LICENSE for more details. """ import itertools import shlex import click from prompt_toolkit import completion as completion class ShellCompleter(completion.Completer): """Completer for the shell.""" def __init__(self, click_root): self.root = click_root def get_completions(self, document, complete_event): """Returns an iterator of completions for the shell.""" return _click_autocomplete(self.root, document.text_before_cursor) # pylint: disable=stop-iteration-return def _click_autocomplete(root, text): """Completer generator for click applications.""" try: parts = shlex.split(text) except ValueError: raise StopIteration location, incomplete = _click_resolve_command(root, parts) if not text.endswith(' ') and not incomplete and text: raise StopIteration if incomplete and not incomplete[0:2].isalnum(): for param in location.params: if not isinstance(param, click.Option): continue for opt in itertools.chain(param.opts, param.secondary_opts): if opt.startswith(incomplete): yield completion.Completion(opt, -len(incomplete), display_meta=param.help) elif isinstance(location, (click.MultiCommand, click.core.Group)): ctx = click.Context(location) commands = location.list_commands(ctx) for command in commands: if command.startswith(incomplete): cmd = location.get_command(ctx, command) yield completion.Completion(command, -len(incomplete), display_meta=cmd.short_help) def _click_resolve_command(root, parts): """Return the click command and the left over text given some vargs.""" location = root incomplete = '' for part in parts: incomplete = part if not part[0:2].isalnum(): continue try: next_location = location.get_command(click.Context(location), part) if next_location is not None: location = next_location incomplete = '' except AttributeError: break return location, incomplete softlayer-python-5.4.2/SoftLayer/shell/core.py000066400000000000000000000071311324365065500214050ustar00rootroot00000000000000""" SoftLayer.CLI.shell.core ~~~~~~~~~~~~~~~~~~~~~~~~ An interactive shell which exposes the CLI :license: MIT, see LICENSE for more details. """ from __future__ import print_function import copy import os import shlex import sys import traceback import click from prompt_toolkit import auto_suggest as p_auto_suggest from prompt_toolkit import history as p_history from prompt_toolkit import shortcuts as p_shortcuts from pygments import token from SoftLayer.CLI import core from SoftLayer.CLI import environment from SoftLayer.shell import completer from SoftLayer.shell import routes # pylint: disable=broad-except class ShellExit(Exception): """Exception raised to quit the shell.""" pass @click.command() @environment.pass_env @click.pass_context def cli(ctx, env): """Enters a shell for slcli.""" # Set up the environment env = copy.deepcopy(env) env.load_modules_from_python(routes.ALL_ROUTES) env.aliases.update(routes.ALL_ALIASES) env.vars['global_args'] = ctx.parent.params env.vars['is_shell'] = True env.vars['last_exit_code'] = 0 # Set up prompt_toolkit settings app_path = click.get_app_dir('softlayer_shell') if not os.path.exists(app_path): os.makedirs(app_path) history = p_history.FileHistory(os.path.join(app_path, 'history')) complete = completer.ShellCompleter(core.cli) while True: def get_prompt_tokens(_): """Returns tokens for the command prompt""" tokens = [] try: tokens.append((token.Token.Username, env.client.auth.username)) tokens.append((token.Token.At, "@")) except AttributeError: pass tokens.append((token.Token.Host, "slcli-shell")) if env.vars['last_exit_code']: tokens.append((token.Token.ErrorPrompt, '> ')) else: tokens.append((token.Token.Prompt, '> ')) return tokens try: line = p_shortcuts.prompt( completer=complete, history=history, auto_suggest=p_auto_suggest.AutoSuggestFromHistory(), get_prompt_tokens=get_prompt_tokens, ) # Parse arguments try: args = shlex.split(line) except ValueError as ex: print("Invalid Command: %s" % ex) continue if not args: continue # Run Command try: # Reset client so that the client gets refreshed env.client = None core.main(args=list(get_env_args(env)) + args, obj=env, prog_name="", reraise_exceptions=True) except SystemExit as ex: env.vars['last_exit_code'] = ex.code except EOFError: return except ShellExit: return except Exception as ex: env.vars['last_exit_code'] = 1 traceback.print_exc(file=sys.stderr) except KeyboardInterrupt: env.vars['last_exit_code'] = 130 def get_env_args(env): """Yield options to inject into the slcli command from the environment.""" for arg, val in env.vars.get('global_args', {}).items(): if val is True: yield '--%s' % arg elif isinstance(val, int): for _ in range(val): yield '--%s' % arg elif val is None: continue else: yield '--%s=%s' % (arg, val) softlayer-python-5.4.2/SoftLayer/shell/routes.py000066400000000000000000000006121324365065500217730ustar00rootroot00000000000000""" SoftLayer.CLI.routes ~~~~~~~~~~~~~~~~~~~ Routes for shell-specific commands :license: MIT, see LICENSE for more details. """ ALL_ROUTES = [ ('exit', 'SoftLayer.shell.cmd_exit:cli'), ('shell-help', 'SoftLayer.shell.cmd_help:cli'), ('env', 'SoftLayer.shell.cmd_env:cli'), ] ALL_ALIASES = { '?': 'shell-help', 'help': 'shell-help', 'quit': 'exit', } softlayer-python-5.4.2/SoftLayer/testing/000077500000000000000000000000001324365065500204475ustar00rootroot00000000000000softlayer-python-5.4.2/SoftLayer/testing/__init__.py000066400000000000000000000132451324365065500225650ustar00rootroot00000000000000""" SoftLayer.testing ~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ # Disable pylint import error and too many methods error # pylint: disable=invalid-name from __future__ import print_function import logging import os.path from click import testing import mock import testtools import SoftLayer from SoftLayer.CLI import core from SoftLayer.CLI import environment from SoftLayer.testing import xmlrpc FIXTURE_PATH = os.path.abspath(os.path.join(__file__, '..', '..', 'fixtures')) class MockableTransport(object): """Transport which is able to mock out specific API calls.""" def __init__(self, transport): self.calls = [] self.mocked = {} self.transport = transport def __call__(self, call): self._record_call(call) key = _mock_key(call.service, call.method) if key in self.mocked: return self.mocked[key](call) # Fall back to another transport (usually with fixtures) return self.transport(call) def set_mock(self, service, method): """Create a mock and return the mock object for the specific API call. :param service: API service to mock :param method: API method to mock """ _mock = mock.MagicMock() self.mocked[_mock_key(service, method)] = _mock return _mock def clear(self): """Clear out mocks and call history.""" self.calls = [] self.mocked = {} def _record_call(self, call): """Record and log the API call (for later assertions).""" self.calls.append(call) details = [] for prop in ['identifier', 'args', 'mask', 'filter', 'limit', 'offset']: details.append('%s=%r' % (prop, getattr(call, prop))) logging.info('%s::%s called; %s', call.service, call.method, '; '.join(details)) def _mock_key(service, method): """Key to address a mock object in MockableTransport.""" return '%s::%s' % (service, method) class TestCase(testtools.TestCase): """Testcase class with PEP-8 compatable method names.""" @classmethod def setUpClass(cls): """Stand up fixtured/mockable XML-RPC server.""" cls.mocks = MockableTransport(SoftLayer.FixtureTransport()) cls.server = xmlrpc.create_test_server(cls.mocks) host, port = cls.server.socket.getsockname()[:2] cls.endpoint_url = "http://%s:%s" % (host, port) @classmethod def tearDownClass(cls): """Clean up the http server.""" cls.server.shutdown() def set_up(self): """Aliased from setUp.""" pass def tear_down(self): """Aliased from tearDown.""" pass def setUp(self): # NOQA testtools.TestCase.setUp(self) self.mocks.clear() transport = SoftLayer.XmlRpcTransport(endpoint_url=self.endpoint_url) wrapped_transport = SoftLayer.TimingTransport(transport) self.client = SoftLayer.BaseClient(transport=wrapped_transport) self.env = environment.Environment() self.env.client = self.client self.set_up() def tearDown(self): # NOQA testtools.TestCase.tearDown(self) self.tear_down() self.mocks.clear() def calls(self, service=None, method=None, **props): """Return all API calls made during the current test.""" conditions = [] if service is not None: conditions.append(lambda call: call.service == service) if method is not None: conditions.append(lambda call: call.method == method) if props: conditions.append(lambda call: call_has_props(call, props)) return [call for call in self.mocks.calls if all(cond(call) for cond in conditions)] def assert_called_with(self, service, method, **props): """Used to assert that API calls were called with given properties. Props are properties of the given transport.Request object. """ if self.calls(service, method, **props): return raise AssertionError('%s::%s was not called with given properties: %s' % (service, method, props)) def assert_no_fail(self, result): """Fail when a failing click result has an error""" if result.exception: print(result.output) raise result.exception self.assertEqual(result.exit_code, 0) def set_mock(self, service, method): """Set and return mock on the current client.""" return self.mocks.set_mock(service, method) def run_command(self, args=None, env=None, fixtures=True, fmt='json'): """A helper that runs a SoftLayer CLI command. This returns a click.testing.Result object. """ args = args or [] if fixtures is True: args.insert(0, '--demo') args.insert(0, '--format=%s' % fmt) runner = testing.CliRunner() return runner.invoke(core.cli, args=args, obj=env or self.env) def call_has_props(call, props): """Check if a call has matching properties of a given props dictionary.""" for prop, expected_value in props.items(): actual_value = getattr(call, prop) if actual_value != expected_value: logging.info( '%s::%s property mismatch, %s: expected=%r; actual=%r', call.service, call.method, prop, expected_value, actual_value) return False return True softlayer-python-5.4.2/SoftLayer/testing/xmlrpc.py000066400000000000000000000072741324365065500223400ustar00rootroot00000000000000""" SoftLayer.testing.xmlrpc ~~~~~~~~~~~~~~~~~~~~~~~~ XMP-RPC server which can use a transport to proxy requests for testing. :license: MIT, see LICENSE for more details. """ import logging import threading import six import SoftLayer from SoftLayer import transports from SoftLayer import utils # pylint: disable=invalid-name, broad-except, arguments-differ class TestServer(six.moves.BaseHTTPServer.HTTPServer): """Test HTTP server which holds a given transport.""" def __init__(self, transport, *args, **kw): six.moves.BaseHTTPServer.HTTPServer.__init__(self, *args, **kw) self.transport = transport class TestHandler(six.moves.BaseHTTPServer.BaseHTTPRequestHandler): """Test XML-RPC Handler which converts XML-RPC to transport requests.""" def do_POST(self): """Handle XML-RPC POSTs.""" try: length = int(self.headers['Content-Length']) data = self.rfile.read(length).decode('utf-8') args, method = utils.xmlrpc_client.loads(data) headers = args[0].get('headers', {}) # Form Request for the transport req = transports.Request() req.service = self.path.lstrip('/') req.method = method req.limit = utils.lookup(headers, 'resultLimit', 'limit') req.offset = utils.lookup(headers, 'resultLimit', 'offset') req.args = args[1:] req.filter = _item_by_key_postfix(headers, 'ObjectFilter') or None req.mask = _item_by_key_postfix(headers, 'ObjectMask').get('mask') req.identifier = _item_by_key_postfix(headers, 'InitParameters').get('id') req.transport_headers = dict(((k.lower(), v) for k, v in self.headers.items())) req.headers = headers # Get response response = self.server.transport(req) response_body = utils.xmlrpc_client.dumps((response,), allow_none=True, methodresponse=True) self.send_response(200) self.send_header("Content-type", "application/xml; charset=UTF-8") self.end_headers() try: self.wfile.write(response_body.encode('utf-8')) except UnicodeDecodeError: self.wfile.write(response_body) except SoftLayer.SoftLayerAPIError as ex: self.send_response(200) self.end_headers() response = utils.xmlrpc_client.Fault(ex.faultCode, str(ex.reason)) response_body = utils.xmlrpc_client.dumps(response, allow_none=True, methodresponse=True) self.wfile.write(response_body.encode('utf-8')) except Exception as ex: self.send_response(500) logging.exception("Error while handling request") def log_message(self, fmt, *args): """Override log_message.""" pass def _item_by_key_postfix(dictionary, key_prefix): """Get item from a dictionary which begins with the given prefix.""" for key, value in dictionary.items(): if key.endswith(key_prefix): return value return {} def create_test_server(transport, host='localhost', port=0): """Create a test XML-RPC server in a new thread.""" server = TestServer(transport, (host, port), TestHandler) thread = threading.Thread(target=server.serve_forever, kwargs={'poll_interval': 0.01}) thread.start() return server softlayer-python-5.4.2/SoftLayer/transports.py000066400000000000000000000320671324365065500215730ustar00rootroot00000000000000""" SoftLayer.transports ~~~~~~~~~~~~~~~~~~~~ XML-RPC transport layer that uses the requests library. :license: MIT, see LICENSE for more details. """ import importlib import json import logging import time import requests from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry from SoftLayer import consts from SoftLayer import exceptions from SoftLayer import utils LOGGER = logging.getLogger(__name__) # transports.Request does have a lot of instance attributes. :( # pylint: disable=too-many-instance-attributes __all__ = [ 'Request', 'XmlRpcTransport', 'RestTransport', 'TimingTransport', 'FixtureTransport', 'SoftLayerListResult', ] REST_SPECIAL_METHODS = { 'deleteObject': 'DELETE', 'createObject': 'POST', 'createObjects': 'POST', 'editObject': 'PUT', 'editObjects': 'PUT', } def get_session(user_agent): """Sets up urllib sessions""" client = requests.Session() client.headers.update({ 'Content-Type': 'application/json', 'User-Agent': user_agent, }) retry = Retry(connect=3, backoff_factor=3) adapter = HTTPAdapter(max_retries=retry) client.mount('https://', adapter) return client class Request(object): """Transport request object.""" def __init__(self): #: API service name. E.G. SoftLayer_Account self.service = None #: API method name. E.G. getObject self.method = None #: API Parameters. self.args = tuple() #: API headers, used for authentication, masks, limits, offsets, etc. self.headers = {} #: Transport user. self.transport_user = None #: Transport password. self.transport_password = None #: Transport headers. self.transport_headers = {} #: Boolean specifying if the server certificate should be verified. self.verify = None #: Client certificate file path. self.cert = None #: InitParameter/identifier of an object. self.identifier = None #: SoftLayer mask (dict or string). self.mask = None #: SoftLayer Filter (dict). self.filter = None #: Integer result limit. self.limit = None #: Integer result offset. self.offset = None class SoftLayerListResult(list): """A SoftLayer API list result.""" def __init__(self, items, total_count): #: total count of items that exist on the server. This is useful when #: paginating through a large list of objects. self.total_count = total_count super(SoftLayerListResult, self).__init__(items) class XmlRpcTransport(object): """XML-RPC transport.""" def __init__(self, endpoint_url=None, timeout=None, proxy=None, user_agent=None, verify=True): self.endpoint_url = (endpoint_url or consts.API_PUBLIC_ENDPOINT).rstrip('/') self.timeout = timeout or None self.proxy = proxy self.user_agent = user_agent or consts.USER_AGENT self.verify = verify self._client = None @property def client(self): """Returns client session object""" if self._client is None: self._client = get_session(self.user_agent) return self._client def __call__(self, request): """Makes a SoftLayer API call against the XML-RPC endpoint. :param request request: Request object """ largs = list(request.args) headers = request.headers if request.identifier is not None: header_name = request.service + 'InitParameters' headers[header_name] = {'id': request.identifier} if request.mask is not None: headers.update(_format_object_mask_xmlrpc(request.mask, request.service)) if request.filter is not None: headers['%sObjectFilter' % request.service] = request.filter if request.limit: headers['resultLimit'] = { 'limit': request.limit, 'offset': request.offset or 0, } largs.insert(0, {'headers': headers}) request.transport_headers.setdefault('Content-Type', 'application/xml') request.transport_headers.setdefault('User-Agent', self.user_agent) url = '/'.join([self.endpoint_url, request.service]) payload = utils.xmlrpc_client.dumps(tuple(largs), methodname=request.method, allow_none=True) # Prefer the request setting, if it's not None verify = request.verify if verify is None: verify = self.verify LOGGER.debug("=== REQUEST ===") LOGGER.debug('POST %s', url) LOGGER.debug(request.transport_headers) LOGGER.debug(payload) try: resp = self.client.request('POST', url, data=payload, headers=request.transport_headers, timeout=self.timeout, verify=verify, cert=request.cert, proxies=_proxies_dict(self.proxy)) LOGGER.debug("=== RESPONSE ===") LOGGER.debug(resp.headers) LOGGER.debug(resp.content) resp.raise_for_status() result = utils.xmlrpc_client.loads(resp.content)[0][0] if isinstance(result, list): return SoftLayerListResult( result, int(resp.headers.get('softlayer-total-items', 0))) else: return result except utils.xmlrpc_client.Fault as ex: # These exceptions are formed from the XML-RPC spec # http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php error_mapping = { '-32700': exceptions.NotWellFormed, '-32701': exceptions.UnsupportedEncoding, '-32702': exceptions.InvalidCharacter, '-32600': exceptions.SpecViolation, '-32601': exceptions.MethodNotFound, '-32602': exceptions.InvalidMethodParameters, '-32603': exceptions.InternalError, '-32500': exceptions.ApplicationError, '-32400': exceptions.RemoteSystemError, '-32300': exceptions.TransportError, } _ex = error_mapping.get(ex.faultCode, exceptions.SoftLayerAPIError) raise _ex(ex.faultCode, ex.faultString) except requests.HTTPError as ex: raise exceptions.TransportError(ex.response.status_code, str(ex)) except requests.RequestException as ex: raise exceptions.TransportError(0, str(ex)) class RestTransport(object): """REST transport. REST calls should mostly work, but is not fully tested. XML-RPC should be used when in doubt """ def __init__(self, endpoint_url=None, timeout=None, proxy=None, user_agent=None, verify=True): self.endpoint_url = (endpoint_url or consts.API_PUBLIC_ENDPOINT_REST).rstrip('/') self.timeout = timeout or None self.proxy = proxy self.user_agent = user_agent or consts.USER_AGENT self.verify = verify self._client = None @property def client(self): """Returns client session object""" if self._client is None: self._client = get_session(self.user_agent) return self._client def __call__(self, request): """Makes a SoftLayer API call against the REST endpoint. REST calls should mostly work, but is not fully tested. XML-RPC should be used when in doubt :param request request: Request object """ params = request.headers.copy() if request.mask: params['objectMask'] = _format_object_mask(request.mask) if request.limit: params['limit'] = request.limit if request.offset: params['offset'] = request.offset if request.filter: params['objectFilter'] = json.dumps(request.filter) auth = None if request.transport_user: auth = requests.auth.HTTPBasicAuth( request.transport_user, request.transport_password, ) method = REST_SPECIAL_METHODS.get(request.method) if method is None: method = 'GET' body = {} if request.args: # NOTE(kmcdonald): force POST when there are arguments because # the request body is ignored otherwise. method = 'POST' body['parameters'] = request.args raw_body = None if body: raw_body = json.dumps(body) url_parts = [self.endpoint_url, request.service] if request.identifier is not None: url_parts.append(str(request.identifier)) if request.method is not None: url_parts.append(request.method) url = '%s.%s' % ('/'.join(url_parts), 'json') # Prefer the request setting, if it's not None verify = request.verify if verify is None: verify = self.verify LOGGER.debug("=== REQUEST ===") LOGGER.debug(url) LOGGER.debug(request.transport_headers) LOGGER.debug(raw_body) try: resp = self.client.request(method, url, auth=auth, headers=request.transport_headers, params=params, data=raw_body, timeout=self.timeout, verify=verify, cert=request.cert, proxies=_proxies_dict(self.proxy)) LOGGER.debug("=== RESPONSE ===") LOGGER.debug(resp.headers) LOGGER.debug(resp.text) resp.raise_for_status() result = json.loads(resp.text) if isinstance(result, list): return SoftLayerListResult( result, int(resp.headers.get('softlayer-total-items', 0))) else: return result except requests.HTTPError as ex: message = json.loads(ex.response.text)['error'] raise exceptions.SoftLayerAPIError(ex.response.status_code, message) except requests.RequestException as ex: raise exceptions.TransportError(0, str(ex)) class TimingTransport(object): """Transport that records API call timings.""" def __init__(self, transport): self.transport = transport self.last_calls = [] def __call__(self, call): """See Client.call for documentation.""" start_time = time.time() result = self.transport(call) end_time = time.time() self.last_calls.append((call, start_time, end_time - start_time)) return result def get_last_calls(self): """Retrieves the last_calls property. This property will contain a list of tuples in the form (Request, initiated_utc_timestamp, execution_time) """ last_calls = self.last_calls self.last_calls = [] return last_calls class FixtureTransport(object): """Implements a transport which returns fixtures.""" def __call__(self, call): """Load fixture from the default fixture path.""" try: module_path = 'SoftLayer.fixtures.%s' % call.service module = importlib.import_module(module_path) except ImportError: raise NotImplementedError('%s fixture is not implemented' % call.service) try: return getattr(module, call.method) except AttributeError: raise NotImplementedError('%s::%s fixture is not implemented' % (call.service, call.method)) def _proxies_dict(proxy): """Makes a proxy dict appropriate to pass to requests.""" if not proxy: return None return {'http': proxy, 'https': proxy} def _format_object_mask_xmlrpc(objectmask, service): """Format new and old style object masks into proper headers. :param objectmask: a string- or dict-based object mask :param service: a SoftLayer API service name """ if isinstance(objectmask, dict): mheader = '%sObjectMask' % service else: mheader = 'SoftLayer_ObjectMask' objectmask = _format_object_mask(objectmask) return {mheader: {'mask': objectmask}} def _format_object_mask(objectmask): """Format the new style object mask. This wraps the user mask with mask[USER_MASK] if it does not already have one. This makes it slightly easier for users. :param objectmask: a string-based object mask """ objectmask = objectmask.strip() if (not objectmask.startswith('mask') and not objectmask.startswith('[')): objectmask = "mask[%s]" % objectmask return objectmask softlayer-python-5.4.2/SoftLayer/utils.py000066400000000000000000000130061324365065500205040ustar00rootroot00000000000000""" SoftLayer.utils ~~~~~~~~~~~~~~~ Utility function/classes. :license: MIT, see LICENSE for more details. """ import datetime import re import six # pylint: disable=no-member, invalid-name UUID_RE = re.compile(r'^[0-9a-f\-]{36}$', re.I) KNOWN_OPERATIONS = ['<=', '>=', '<', '>', '~', '!~', '*=', '^=', '$=', '_='] configparser = six.moves.configparser string_types = six.string_types StringIO = six.StringIO xmlrpc_client = six.moves.xmlrpc_client def lookup(dic, key, *keys): """A generic dictionary access helper. This helps simplify code that uses heavily nested dictionaries. It will return None if any of the keys in *keys do not exist. :: >>> lookup({'this': {'is': 'nested'}}, 'this', 'is') nested >>> lookup({}, 'this', 'is') None """ if keys: return lookup(dic.get(key, {}), keys[0], *keys[1:]) return dic.get(key) class NestedDict(dict): """This helps with accessing a heavily nested dictionary. Dictionary where accessing keys that don't exist will return another NestedDict object. """ def __getitem__(self, key): if key in self: return self.get(key) return self.setdefault(key, NestedDict()) def to_dict(self): """Converts a NestedDict instance into a real dictionary. This is needed for places where strict type checking is done. """ return {key: val.to_dict() if isinstance(val, NestedDict) else val for key, val in self.items()} def query_filter(query): """Translate a query-style string to a 'filter'. Query can be the following formats: Case Insensitive 'value' OR '*= value' Contains 'value*' OR '^= value' Begins with value '*value' OR '$= value' Ends with value '*value*' OR '_= value' Contains value Case Sensitive '~ value' Contains '!~ value' Does not contain '> value' Greater than value '< value' Less than value '>= value' Greater than or equal to value '<= value' Less than or equal to value :param string query: query string """ try: return {'operation': int(query)} except ValueError: pass if isinstance(query, string_types): query = query.strip() for operation in KNOWN_OPERATIONS: if query.startswith(operation): query = "%s %s" % (operation, query[len(operation):].strip()) return {'operation': query} if query.startswith('*') and query.endswith('*'): query = "*= %s" % query.strip('*') elif query.startswith('*'): query = "$= %s" % query.strip('*') elif query.endswith('*'): query = "^= %s" % query.strip('*') else: query = "_= %s" % query return {'operation': query} def query_filter_date(start, end): """Query filters given start and end date. :param start:YY-MM-DD :param end: YY-MM-DD """ sdate = datetime.datetime.strptime(start, "%Y-%m-%d") edate = datetime.datetime.strptime(end, "%Y-%m-%d") startdate = "%s/%s/%s" % (sdate.month, sdate.day, sdate.year) enddate = "%s/%s/%s" % (edate.month, edate.day, edate.year) return { 'operation': 'betweenDate', 'options': [ {'name': 'startDate', 'value': [startdate+' 0:0:0']}, {'name': 'endDate', 'value': [enddate+' 0:0:0']} ] } class IdentifierMixin(object): """Mixin used to resolve ids from other names of objects. This mixin provides an interface to provide multiple methods for converting an 'indentifier' to an id """ resolvers = [] def resolve_ids(self, identifier): """Takes a string and tries to resolve to a list of matching ids. What exactly 'identifier' can be depends on the resolvers :param string identifier: identifying string :returns list: """ return resolve_ids(identifier, self.resolvers) def resolve_ids(identifier, resolvers): """Resolves IDs given a list of functions. :param string identifier: identifier string :param list resolvers: a list of functions :returns list: """ # Before doing anything, let's see if this is an integer try: return [int(identifier)] except ValueError: pass # It was worth a shot # This looks like a globalIdentifier (UUID) if len(identifier) == 36 and UUID_RE.match(identifier): return [identifier] for resolver in resolvers: ids = resolver(identifier) if ids: return ids return [] class UTC(datetime.tzinfo): """UTC timezone.""" def utcoffset(self, _): return datetime.timedelta(0) def tzname(self, _): return "UTC" def dst(self, _): return datetime.timedelta(0) def is_ready(instance, pending=False): """Returns True if instance is ready to be used :param Object instance: Hardware or Virt with transaction data retrieved from the API :param bool pending: Wait for ALL transactions to finish? :returns bool: """ last_reload = lookup(instance, 'lastOperatingSystemReload', 'id') active_transaction = lookup(instance, 'activeTransaction', 'id') reloading = all(( active_transaction, last_reload, last_reload == active_transaction, )) outstanding = False if pending: outstanding = active_transaction if instance.get('provisionDate') and not reloading and not outstanding: return True return False softlayer-python-5.4.2/docs/000077500000000000000000000000001324365065500160125ustar00rootroot00000000000000softlayer-python-5.4.2/docs/Makefile000066400000000000000000000130041324365065500174500ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build 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) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* 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." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/SoftLayerAPIPythonClient.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/SoftLayerAPIPythonClient.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/SoftLayerAPIPythonClient" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/SoftLayerAPIPythonClient" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." softlayer-python-5.4.2/docs/_static/000077500000000000000000000000001324365065500174405ustar00rootroot00000000000000softlayer-python-5.4.2/docs/_static/style.css000066400000000000000000000001731324365065500213130ustar00rootroot00000000000000@import url("nature.css"); p.deprecated { background-color: #ffe4e4; border: 1px solid #f66; padding: 0.2em; }softlayer-python-5.4.2/docs/api/000077500000000000000000000000001324365065500165635ustar00rootroot00000000000000softlayer-python-5.4.2/docs/api/client.rst000066400000000000000000000125761324365065500206060ustar00rootroot00000000000000.. _client: API Documentation ================= This is the primary API client to make API calls. It deals with constructing and executing XML-RPC calls against the SoftLayer API. Below are some links that will help to use the SoftLayer API. * `SoftLayer API Documentation `_ * `Source on GitHub `_ :: >>> import SoftLayer >>> client = SoftLayer.create_client_from_env(username="username", api_key="api_key") >>> resp = client.call('Account', 'getObject') >>> resp['companyName'] 'Your Company' Getting Started --------------- You can pass in your username and api_key when creating a SoftLayer client instance. However, you can also set these in the environmental variables 'SL_USERNAME' and 'SL_API_KEY'. Creating a client instance by passing in the username/api_key: :: import SoftLayer client = SoftLayer.create_client_from_env(username='YOUR_USERNAME', api_key='YOUR_API_KEY') Creating a client instance with environmental variables set: :: $ export SL_USERNAME=YOUR_USERNAME $ export SL_API_KEY=YOUR_API_KEY $ python >>> import SoftLayer >>> client = SoftLayer.create_client_from_env() Below is an example of creating a client instance with more options. This will create a client with the private API endpoint (only accessible from the SoftLayer private network) and a timeout of 4 minutes. :: client = SoftLayer.create_client_from_env(username='YOUR_USERNAME', api_key='YOUR_API_KEY' endpoint_url=SoftLayer.API_PRIVATE_ENDPOINT, timeout=240) Managers -------- For day-to-day operation, most users will find the managers to be the most convenient means for interacting with the API. Managers abstract a lot of the complexities of using the API into classes that provide a simpler interface to various services. These are higher-level interfaces to the SoftLayer API. :: from SoftLayer import VSManager, Client client = Client(...) vs = VSManager(client) vs.list_instances() [...] **Available managers**: .. toctree:: :maxdepth: 1 :glob: managers/* If you need more power or functionality than the managers provide, you can make direct API calls as well. Making API Calls ---------------- For full control over your account and services, you can directly call the SoftLayer API. The SoftLayer API client for python leverages SoftLayer's XML-RPC API. It supports authentication, object masks, object filters, limits, offsets, and retrieving objects by id. The following section assumes you have an initialized client named 'client'. The best way to test our setup is to call the `getObject `_ method on the `SoftLayer_Account `_ service. :: client.call('Account', 'getObject') For a more complex example we'll retrieve a support ticket with id 123456 along with the ticket's updates, the user it's assigned to, the servers attached to it, and the datacenter those servers are in. To retrieve our extra information using an `object mask `_. Retrieve a ticket using object masks. :: ticket = client.call('Ticket', 'getObject', id=123456, mask="updates, assignedUser, attachedHardware.datacenter") Now add an update to the ticket with `Ticket.addUpdate `_. This uses a parameter, which translate to positional arguments in the order that they appear in the API docs. :: update = client.call('Ticket', 'addUpdate', {'entry' : 'Hello!'}, id=123456) Let's get a listing of virtual guests using the domain example.com :: client.call('Account', 'getVirtualGuests', filter={'virtualGuests': {'domain': {'operation': 'example.com'}}}) This call gets tickets created between the beginning of March 1, 2013 and March 15, 2013. :: client.call('Account', 'getTickets', filter={ 'tickets': { 'createDate': { 'operation': 'betweenDate', 'options': [ {'name': 'startDate', 'value': ['03/01/2013 0:0:0']}, {'name': 'endDate', 'value': ['03/15/2013 23:59:59']} ] } } } ) SoftLayer's XML-RPC API also allows for pagination. :: client.call('Account', 'getVirtualGuests', limit=10, offset=0) # Page 1 client.call('Account', 'getVirtualGuests', limit=10, offset=10) # Page 2 Here's how to create a new Cloud Compute Instance using `SoftLayer_Virtual_Guest.createObject `_. Be warned, this call actually creates an hourly virtual server so this will have billing implications. :: client.call('Virtual_Guest', 'createObject', { 'hostname': 'myhostname', 'domain': 'example.com', 'startCpus': 1, 'maxMemory': 1024, 'hourlyBillingFlag': 'true', 'operatingSystemReferenceCode': 'UBUNTU_LATEST', 'localDiskFlag': 'false' }) API Reference ------------- .. automodule:: SoftLayer :members: softlayer-python-5.4.2/docs/api/managers/000077500000000000000000000000001324365065500203605ustar00rootroot00000000000000softlayer-python-5.4.2/docs/api/managers/block.rst000066400000000000000000000001301324365065500221760ustar00rootroot00000000000000.. _block: .. automodule:: SoftLayer.managers.block :members: :inherited-members:softlayer-python-5.4.2/docs/api/managers/cdn.rst000066400000000000000000000001251324365065500216540ustar00rootroot00000000000000.. _cdn: .. automodule:: SoftLayer.managers.cdn :members: :inherited-members: softlayer-python-5.4.2/docs/api/managers/dedicated_host.rst000066400000000000000000000001531324365065500240540ustar00rootroot00000000000000.. _dedicated_host: .. automodule:: SoftLayer.managers.dedicated_host :members: :inherited-members: softlayer-python-5.4.2/docs/api/managers/dns.rst000066400000000000000000000001251324365065500216740ustar00rootroot00000000000000.. _dns: .. automodule:: SoftLayer.managers.dns :members: :inherited-members: softlayer-python-5.4.2/docs/api/managers/file.rst000066400000000000000000000001261324365065500220300ustar00rootroot00000000000000.. _file: .. automodule:: SoftLayer.managers.file :members: :inherited-members:softlayer-python-5.4.2/docs/api/managers/firewall.rst000066400000000000000000000001371324365065500227200ustar00rootroot00000000000000.. _firewall: .. automodule:: SoftLayer.managers.firewall :members: :inherited-members: softlayer-python-5.4.2/docs/api/managers/hardware.rst000066400000000000000000000001361324365065500227070ustar00rootroot00000000000000.. _hardware: .. automodule:: SoftLayer.managers.hardware :members: :inherited-members:softlayer-python-5.4.2/docs/api/managers/image.rst000066400000000000000000000001311324365065500221670ustar00rootroot00000000000000.. _image: .. automodule:: SoftLayer.managers.image :members: :inherited-members: softlayer-python-5.4.2/docs/api/managers/ipsec.rst000066400000000000000000000001311324365065500222100ustar00rootroot00000000000000.. _ipsec: .. automodule:: SoftLayer.managers.ipsec :members: :inherited-members: softlayer-python-5.4.2/docs/api/managers/load_balancer.rst000066400000000000000000000001511324365065500236550ustar00rootroot00000000000000.. _load_balancer: .. automodule:: SoftLayer.managers.load_balancer :members: :inherited-members: softlayer-python-5.4.2/docs/api/managers/messaging.rst000066400000000000000000000001411324365065500230630ustar00rootroot00000000000000.. _messaging: .. automodule:: SoftLayer.managers.messaging :members: :inherited-members: softlayer-python-5.4.2/docs/api/managers/metadata.rst000066400000000000000000000002421324365065500226700ustar00rootroot00000000000000.. _metadata: .. automodule:: SoftLayer.managers.metadata :members: :inherited-members: .. autoattribute:: SoftLayer.managers.metadata.METADATA_ATTRIBUTESsoftlayer-python-5.4.2/docs/api/managers/network.rst000066400000000000000000000001351324365065500226020ustar00rootroot00000000000000.. _network: .. automodule:: SoftLayer.managers.network :members: :inherited-members: softlayer-python-5.4.2/docs/api/managers/ordering.rst000066400000000000000000000001371324365065500227240ustar00rootroot00000000000000.. _ordering: .. automodule:: SoftLayer.managers.ordering :members: :inherited-members: softlayer-python-5.4.2/docs/api/managers/sshkey.rst000066400000000000000000000001331324365065500224150ustar00rootroot00000000000000.. _sshkey: .. automodule:: SoftLayer.managers.sshkey :members: :inherited-members: softlayer-python-5.4.2/docs/api/managers/ssl.rst000066400000000000000000000001251324365065500217110ustar00rootroot00000000000000.. _ssl: .. automodule:: SoftLayer.managers.ssl :members: :inherited-members: softlayer-python-5.4.2/docs/api/managers/ticket.rst000066400000000000000000000001331324365065500223720ustar00rootroot00000000000000.. _ticket: .. automodule:: SoftLayer.managers.ticket :members: :inherited-members: softlayer-python-5.4.2/docs/api/managers/vs.rst000066400000000000000000000001231324365065500215360ustar00rootroot00000000000000.. _vs: .. automodule:: SoftLayer.managers.vs :members: :inherited-members: softlayer-python-5.4.2/docs/cli.rst000066400000000000000000000164451324365065500173250ustar00rootroot00000000000000.. _cli: Command-line Interface ====================== The SoftLayer command line interface is available via the `slcli` command available in your `PATH`. The `slcli` command is a reference implementation of SoftLayer API bindings for python and how to efficiently make API calls. See the :ref:`usage-examples` section to see how to discover all of the functionality not fully documented here. .. toctree:: :maxdepth: 2 cli/ipsec cli/vs cli/ordering .. _config_setup: Configuration Setup ------------------- To update the configuration, you can use `slcli setup`. :: $ slcli setup Username []: username API Key or Password []: Endpoint (public|private|custom): public :..............:..................................................................: : Name : Value : :..............:..................................................................: : Username : username : : API Key : oyVmeipYQCNrjVS4rF9bHWV7D75S6pa1fghFl384v7mwRCbHTfuJ8qRORIqoVnha : : Endpoint URL : https://api.softlayer.com/xmlrpc/v3/ : :..............:..................................................................: Are you sure you want to write settings to "/home/me/.softlayer"? [y/N]: y To check the configuration, you can use `slcli config show`. :: $ slcli config show :..............:..................................................................: : Name : Value : :..............:..................................................................: : Username : username : : API Key : oyVmeipYQCNrjVS4rF9bHWV7D75S6pa1fghFl384v7mwRCbHTfuJ8qRORIqoVnha : : Endpoint URL : https://api.softlayer.com/xmlrpc/v3/ : :..............:..................................................................: To see more about the config file format, see :ref:`config_file`. .. _usage-examples: Usage Examples -------------- To discover the available commands, simply type `slcli`. :: $ slcli Usage: slcli [OPTIONS] COMMAND [ARGS]... SoftLayer Command-line Client Options: --format [table|raw|json|jsonraw] Output format [default: table] -C, --config PATH Config file location [default: ~/.softlayer] -v, --verbose Sets the debug noise level, specify multiple times for more verbosity. --proxy TEXT HTTP[S] proxy to be use to make API calls -y, --really / --not-really Confirm all prompt actions --demo / --no-demo Use demo data instead of actually making API calls --version Show the version and exit. -h, --help Show this message and exit. Commands: block Block Storage. call-api Call arbitrary API endpoints. cdn Content Delivery Network. config CLI configuration. dns Domain Name System. file File Storage. firewall Firewalls. globalip Global IP addresses. hardware Hardware servers. image Compute images. loadbal Load balancers. messaging Message queue service. metadata Find details about this machine. nas Network Attached Storage. object-storage Object Storage. report Reports. rwhois Referral Whois. setup Edit configuration. shell Enters a shell for slcli. sshkey SSH Keys. ssl SSL Certificates. subnet Network subnets. summary Account summary. ticket Support tickets. virtual Virtual Servers. vlan Network VLANs. To use most commands your SoftLayer username and api_key need to be configured. The easiest way to do that is to use: 'slcli setup' As you can see, there are a number of commands/sections. To look at the list of subcommands for virtual servers type `slcli vs`. For example: :: $ slcli vs Usage: slcli vs [OPTIONS] COMMAND [ARGS]... Virtual Servers. Options: --help Show this message and exit. Commands: cancel Cancel virtual servers. capture Capture SoftLayer image. create Order/create virtual servers. create-options Virtual server order options. credentials List virtual server credentials. detail Get details for a virtual server. dns-sync Sync DNS records. edit Edit a virtual server's details. list List virtual servers. network Manage network settings. pause Pauses an active virtual server. power_off Power off an active virtual server. power_on Power on a virtual server. ready Check if a virtual server is ready. reboot Reboot an active virtual server. reload Reload operating system on a virtual server. rescue Reboot into a rescue image. resume Resumes a paused virtual server. upgrade Upgrade a virtual server. Finally, we can make an actual call. Let's list out the virtual servers on our account by using `slcli vs list`. :: $ slcli vs list :.........:............:....................:.......:........:................:..............:....................: : id : datacenter : host : cores : memory : primary_ip : backend_ip : active_transaction : :.........:............:....................:.......:........:................:..............:....................: : 1234567 : sjc01 : test.example.com : 4 : 4G : 12.34.56 : 65.43.21 : - : :.........:............:....................:.......:........:................:..............:....................: Most commands will take in additional options/arguments. To see all available actions, use `--help`. :: $ slcli vs list --help Usage: slcli vs list [OPTIONS] List virtual servers. Options: --sortby [guid|hostname|primary_ip|backend_ip|datacenter] Column to sort by -c, --cpu INTEGER Number of CPU cores -D, --domain TEXT Domain portion of the FQDN -d, --datacenter TEXT Datacenter shortname -H, --hostname TEXT Host portion of the FQDN -m, --memory INTEGER Memory in mebibytes -n, --network TEXT Network port speed in Mbps --hourly Show only hourly instances --monthly Show only monthly instances --tags TEXT Show instances that have one of these comma- separated tags --help Show this message and exit. softlayer-python-5.4.2/docs/cli/000077500000000000000000000000001324365065500165615ustar00rootroot00000000000000softlayer-python-5.4.2/docs/cli/ipsec.rst000066400000000000000000000257731324365065500204340ustar00rootroot00000000000000.. _cli_ipsec: Interacting with IPSEC Tunnels ============================== The IPSEC :ref:`cli` commands can be used to configure an existing IPSEC tunnel context. Subnets in the SoftLayer private network can be associated to the tunnel context along with user-defined remote subnets. Address translation entries may also be defined to provide NAT functionality from static subnet IP addresses associated with the tunnel context to user-defined remote subnet IP addresses. .. note:: Most CLI actions that affect an IPSEC tunnel context do not result in configuration changes to SoftLayer network devices. A separate *configure* command is available to issue a device configuration request. To see more information about the IPSEC tunnel context module and API internaction, see :doc:`IPSEC Module<../api/managers/ipsec>` documentation. .. _cli_ipsec_list: ipsec list ---------- A list of all IPSEC tunnel contexts associated with the current user's account can be retrieved via the ``ipsec list`` command. This provides a brief overview of all tunnel contexts and can be used to retrieve an individual context's identifier, which all other CLI commands require. :: $ slcli ipsec list :.....:..........:...............:..........................:........................:...........................: : id : name : friendly name : internal peer IP address : remote peer IP address : created : :.....:..........:...............:..........................:........................:...........................: : 445 : ipsec038 : ipsec tunnel : 173.192.250.79 : 158.85.80.22 : 2012-03-05T14:07:34-06:00 : :.....:..........:...............:..........................:........................:...........................: .. _cli_ipsec_detail: ipsec detail ------------ More detailed information can be retrieved for an individual context using the ``ipsec detail`` command. Using the detail command, information about associated internal subnets, remote subnets, static subnets, service subnets and address translations may also be retrieved using multiple instances of the ``-i|--include`` option. :: $ slcli ipsec detail 445 -i at -i is -i rs -i sr -i ss Context Details: :.................................:...........................: : name : value : :.................................:...........................: : id : 445 : : name : ipsec038 : : friendly name : ipsec tunnel : : internal peer IP address : 173.192.250.79 : : remote peer IP address : 158.85.80.22 : : advanced configuration flag : 0 : : preshared key : secret : : phase 1 authentication : MD5 : : phase 1 diffie hellman group : 0 : : phase 1 encryption : DES : : phase 1 key life : 240 : : phase 2 authentication : MD5 : : phase 2 diffie hellman group : 1 : : phase 2 encryption : DES : : phase 2 key life : 240 : : phase 2 perfect forward secrecy : 1 : : created : 2012-03-05T14:07:34-06:00 : : modified : 2017-05-17T12:01:33-06:00 : :.................................:...........................: Address Translations: :.......:...................:......................:...................:......................:.................: : id : static IP address : static IP address id : remote IP address : remote IP address id : note : :.......:...................:......................:...................:......................:.................: : 15920 : 10.1.249.86 : 9791681 : 158.85.80.22 : 98828 : windows server : : 15918 : 10.1.249.84 : 9791679 : 158.85.80.20 : 98824 : unix server : :.......:...................:......................:...................:......................:.................: Internal Subnets: :........:....................:......:......: : id : network identifier : cidr : note : :........:....................:......:......: : 180767 : 10.28.67.128 : 26 : : :........:....................:......:......: Remote Subnets: :......:....................:......:......: : id : network identifier : cidr : note : :......:....................:......:......: : 7852 : 158.85.80.20 : 30 : : :......:....................:......:......: Static Subnets: :........:....................:......:......: : id : network identifier : cidr : note : :........:....................:......:......: : 231807 : 10.1.249.84 : 30 : : :........:....................:......:......: Service Subnets: :........:....................:......:......: : id : network identifier : cidr : note : :........:....................:......:......: : 162079 : 10.0.80.0 : 25 : : :........:....................:......:......: .. _cli_ipsec_update: ipsec update ------------ Most values listed in the tunnel context detail printout can be modified using the ``ipsec update`` command. The following is given when executing with the ``-h|--help`` option and highlights all properties that may be modified. :: $ slcli ipsec update -h Usage: slcli ipsec update [OPTIONS] CONTEXT_ID Update tunnel context properties. Updates are made atomically, so either all are accepted or none are. Key life values must be in the range 120-172800. Phase 2 perfect forward secrecy must be in the range 0-1. A separate configuration request should be made to realize changes on network devices. Options: --friendly-name TEXT Friendly name value --remote-peer TEXT Remote peer IP address value --preshared-key TEXT Preshared key value --p1-auth, --phase1-auth [MD5|SHA1|SHA256] Phase 1 authentication value --p1-crypto, --phase1-crypto [DES|3DES|AES128|AES192|AES256] Phase 1 encryption value --p1-dh, --phase1-dh [0|1|2|5] Phase 1 diffie hellman group value --p1-key-ttl, --phase1-key-ttl INTEGER RANGE Phase 1 key life value --p2-auth, --phase2-auth [MD5|SHA1|SHA256] Phase 2 authentication value --p2-crypto, --phase2-crypto [DES|3DES|AES128|AES192|AES256] Phase 2 encryption value --p2-dh, --phase2-dh [0|1|2|5] Phase 2 diffie hellman group value --p2-forward-secrecy, --phase2-forward-secrecy INTEGER RANGE Phase 2 perfect forward secrecy value --p2-key-ttl, --phase2-key-ttl INTEGER RANGE Phase 2 key life value -h, --help Show this message and exit. .. _cli_ipsec_configure: ipsec configure --------------- A request to configure SoftLayer network devices for a given tunnel context can be issued using the ``ipsec configure`` command. .. note:: Once a configuration request is received, the IPSEC tunnel context will be placed into an unmodifiable state, and further changes against the tunnel context will be prevented. Once configuration changes have been made, the tunnel context may again be modified. The unmodifiable state of a tunnel context is indicated by an *advanced configuration flag* value of 1. .. _cli_ipsec_subnet_add: ipsec subnet-add ---------------- Internal, remote and service subnets can be associated to an IPSEC tunnel context using the ``ipsec subnet-add`` command. Additionally, remote subnets can be created using this same command, which will then be associated to the targeted tunnel context. .. note:: The targeted subnet type must be specified. A subnet id must be provided when associating internal and service subnets. Either a subnet id or a network identifier must be provided when associating remote subnets. If a network identifier is provided when associating a remote subnet, that subnet will first be created and then associated to the tunnel context. The following is an exmaple of associating an internal subnet to a tunnel context. :: $ slcli ipsec subnet-add 445 --subnet-id 180767 --subnet-type internal Added internal subnet #180767 The following is an example of creating and associating a remote subnet to a tunnel context. :: $ slcli ipsec subnet-add 445 --subnet-type remote --network 50.100.0.0/26 Created subnet 50.100.0.0/26 #21268 Added remote subnet #21268 .. _cli_ipsec_subnet_remove: ipsec subnet-remove ------------------- Internal, remote and service subnets can be disassociated from an IPSEC tunnel context via the ``ipsec subnet-remove`` command. .. note:: The targeted subnet id and type must be specified. When disassociating remote subnets, that subnet record will also be deleted. The following is an example of disassociating an internal subnet from a tunnel context. :: $ slcli ipsec subnet-remove 445 --subnet-id 180767 --subnet-type internal Removed internal subnet #180767 .. _cli_ipsec_translation_add: ipsec translation-add --------------------- Address translation entries can be added to a tunnel context to provide NAT functionality from a statically routed subnet associated with the tunnel context to a remote subnet. This action is performed with the ``ipsec translation-add`` command. .. note:: Both static and remote IP address values must be specified. An optional note value may also be provided. The following is an example of adding a new address translation entry. :: $ slcli ipsec translation-add 445 --static-ip 10.1.249.87 --remote-ip 50.100.0.10 --note 'email server' Created translation from 10.1.249.87 to 50.100.0.10 #15922 .. _cli_ipsec_translation_remove: ipsec translation-remove ------------------------ Address translation entries can be removed using the ``ipsec translation-remove`` command. The following is an example of removing an address translation entry. :: $ slcli ipsec translation-remove 445 --translation-id 15922 Removed translation #15922 .. _cli_ipsec_translation_update: ipsec translation-update ------------------------ Address translation entries may also be modified using the ``ipsec translation-update`` command. The following is an example of updating an existing address translation entry. :: $ slcli ipsec translation-update 445 --translation-id 15924 --static-ip 10.1.249.86 --remote-ip 50.100.0.8 --note 'new email server' Updated translation #15924 softlayer-python-5.4.2/docs/cli/ordering.rst000066400000000000000000000110151324365065500211220ustar00rootroot00000000000000.. _cli_order: Ordering ========== The Order :ref:`cli` commands can be used to build an order for any product in the SoftLayer catalog. The basic flow for ordering goes something like this... #. package-list #. category-list #. item-list #. place .. _cli_ordering_package_list: order package-list ------------------ This command will list all of the packages that are available to be ordered. This is the starting point for placing any order. Find the package keyName you want to order, and use it for the next steps. .. note:: * CLOUD_SERVER: These are Virtual Servers * BARE_METAL_INSTANCE: Hourly Bare Metal * BARE_METAL_SERVER: Other monthly server types * `#_PROC_#_DRIVES`: Packages in this format will contain only this CPU model and Drive bays * ADDITIONAL_PRODUCTS: Additional IPs, Vlans, SSL certs and other things are in here * NETWORK_GATEWAY_APPLIANCE: Vyattas Bluemix services listed here may still need to be ordered through the Bluemix CLI/Portal .. _cli_ordering_category_list: order category-list ------------------- Shows all the available categories for a certain package, useful in finding the required categories. Categories that are required will need to have a corresponding item included with any orders These are all the required categories for ``BARE_METAL_SERVER`` :: $ slcli order category-list BARE_METAL_SERVER :........................................:.......................:............: : name : categoryCode : isRequired : :........................................:.......................:............: : Server : server : Y : : Operating System : os : Y : : RAM : ram : Y : : Disk Controller : disk_controller : Y : : First Hard Drive : disk0 : Y : : Public Bandwidth : bandwidth : Y : : Uplink Port Speeds : port_speed : Y : : Remote Management : remote_management : Y : : Primary IP Addresses : pri_ip_addresses : Y : : VPN Management - Private Network : vpn_management : Y : :........................................:.......................:............: .. _cli_ordering_item_list: order item-list --------------- Shows all the prices for a given package. Collect all the items you want included on your server. Don't forget to include the required category items. If forgotten, ``order place`` will tell you about it. .. _cli_ordering_preset_list: order preset-list ----------------- Some packages have presets which makes ordering significantly simpler. These will have set CPU / RAM / Disk allotments. You still need to specify required items .. _cli_ordering_place: order place ----------- Now that you have the package you want, the prices needed, and found a location, it is time to place an order. order place ^^^^^^^^^^^^^^^^^^^^ :: $ slcli --really order place --preset D2620V4_64GB_2X1TB_SATA_RAID_1 BARE_METAL_SERVER TORONTO \ OS_UBUNTU_16_04_LTS_XENIAL_XERUS_64_BIT \ BANDWIDTH_0_GB_2 \ 1_GBPS_PRIVATE_NETWORK_UPLINK \ REBOOT_KVM_OVER_IP 1_IP_ADDRESS \ UNLIMITED_SSL_VPN_USERS_1_PPTP_VPN_USER_PER_ACCOUNT \ --extras '{"hardware": [{"hostname" : "testOrder", "domain": "cgallo.com"}]}' \ --complex-type SoftLayer_Container_Product_Order_Hardware_Server order place ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: $ slcli order place --billing hourly CLOUD_SERVER DALLAS13 \ GUEST_CORES_4 \ RAM_16_GB \ REBOOT_REMOTE_CONSOLE \ 1_GBPS_PUBLIC_PRIVATE_NETWORK_UPLINKS \ BANDWIDTH_0_GB_2 \ 1_IP_ADDRESS \ GUEST_DISK_100_GB_SAN \ OS_UBUNTU_16_04_LTS_XENIAL_XERUS_MINIMAL_64_BIT_FOR_VSI \ MONITORING_HOST_PING \ NOTIFICATION_EMAIL_AND_TICKET \ AUTOMATED_NOTIFICATION \ UNLIMITED_SSL_VPN_USERS_1_PPTP_VPN_USER_PER_ACCOUNT \ NESSUS_VULNERABILITY_ASSESSMENT_REPORTING \ --extras '{"virtualGuests": [{"hostname": "test", "domain": "softlayer.com"}]}' \ --complex-type SoftLayer_Container_Product_Order_Virtual_Guestsoftlayer-python-5.4.2/docs/cli/vs.rst000066400000000000000000000347151324365065500177550ustar00rootroot00000000000000.. _vs_user_docs: Working with Virtual Servers ============================ Using the SoftLayer portal to order virtual servers is fine, but for a number of reasons it's often more convenient to use the command line. For this, you can use SoftLayer's command-line client to make administrative tasks quicker and easier. This page gives an intro to working with SoftLayer virtual servers using SoftLayer's command-line client. .. note:: The following assumes that the client is already :ref:`configured with valid SoftLayer credentials`. First, let's list the current virtual servers with `slcli vs list`. :: $ slcli vs list :.....:............:.........................:.......:........:..............:.............:....................:........: : id : datacenter : host : cores : memory : primary_ip : backend_ip : active_transaction : owner : :.....:............:.........................:.......:........:..............:.............:....................:........: :.....:............:.........................:.......:........:..............:.............:....................:........: We don't have any virtual servers yet! Let's fix that. Before we can create a virtual server (VS), we need to know what options are available to us: RAM, CPU, operating systems, disk sizes, disk types, datacenters, and so on. Luckily, there's a simple command to show all options: `slcli vs create-options`. :: $ slcli vs create-options :.................:...........................................................................................: : Name : Value : :.................:...........................................................................................: : datacenter : ams01,dal01,dal05,dal06,dal09,hkg02,hou02,lon02,mel01,par01,sea01,sjc01,sng01,tor01,wdc01 : : cpus (private) : 1,2,4,8 : : cpus (standard) : 1,2,4,8,12,16 : : memory : 1024,2048,4096,6144,8192,12288,16384,32768,49152,65536 : : os (CENTOS) : CENTOS_5_32 : : : CENTOS_5_64 : : : CENTOS_6_32 : : : CENTOS_6_64 : : : CENTOS_7_64 : : : CENTOS_LATEST : : : CENTOS_LATEST_32 : : : CENTOS_LATEST_64 : : os (CLOUDLINUX) : CLOUDLINUX_5_32 : : : CLOUDLINUX_5_64 : : : CLOUDLINUX_6_32 : : : CLOUDLINUX_6_64 : : : CLOUDLINUX_LATEST : : : CLOUDLINUX_LATEST_32 : : : CLOUDLINUX_LATEST_64 : : os (DEBIAN) : DEBIAN_6_32 : : : DEBIAN_6_64 : : : DEBIAN_7_32 : : : DEBIAN_7_64 : : : DEBIAN_LATEST : : : DEBIAN_LATEST_32 : : : DEBIAN_LATEST_64 : : os (REDHAT) : REDHAT_5_32 : : : REDHAT_5_64 : : : REDHAT_6_32 : : : REDHAT_6_64 : : : REDHAT_LATEST : : : REDHAT_LATEST_32 : : : REDHAT_LATEST_64 : : os (UBUNTU) : UBUNTU_10_32 : : : UBUNTU_10_64 : : : UBUNTU_12_32 : : : UBUNTU_12_64 : : : UBUNTU_14_32 : : : UBUNTU_14_64 : : : UBUNTU_LATEST : : : UBUNTU_LATEST_32 : : : UBUNTU_LATEST_64 : : os (VYATTACE) : VYATTACE_6.5_64 : : : VYATTACE_6.6_64 : : : VYATTACE_LATEST : : : VYATTACE_LATEST_64 : : os (WIN) : WIN_2003-DC-SP2-1_32 : : : WIN_2003-DC-SP2-1_64 : : : WIN_2003-ENT-SP2-5_32 : : : WIN_2003-ENT-SP2-5_64 : : : WIN_2003-STD-SP2-5_32 : : : WIN_2003-STD-SP2-5_64 : : : WIN_2008-DC-R2_64 : : : WIN_2008-DC-SP2_64 : : : WIN_2008-ENT-R2_64 : : : WIN_2008-ENT-SP2_32 : : : WIN_2008-ENT-SP2_64 : : : WIN_2008-STD-R2-SP1_64 : : : WIN_2008-STD-R2_64 : : : WIN_2008-STD-SP2_32 : : : WIN_2008-STD-SP2_64 : : : WIN_2012-DC_64 : : : WIN_2012-STD_64 : : : WIN_LATEST : : : WIN_LATEST_32 : : : WIN_LATEST_64 : : local disk(0) : 25,100 : : local disk(2) : 25,100,150,200,300 : : san disk(0) : 25,100 : : san disk(2) : 10,20,25,30,40,50,75,100,125,150,175,200,250,300,350,400,500,750,1000,1500,2000 : : san disk(3) : 10,20,25,30,40,50,75,100,125,150,175,200,250,300,350,400,500,750,1000,1500,2000 : : san disk(4) : 10,20,25,30,40,50,75,100,125,150,175,200,250,300,350,400,500,750,1000,1500,2000 : : san disk(5) : 10,20,25,30,40,50,75,100,125,150,175,200,250,300,350,400,500,750,1000,1500,2000 : : nic : 10,100,1000 : :.................:...........................................................................................: Here's the command to create a 2-core virtual server with 1GiB memory, running Ubuntu 14.04 LTS, and that is billed on an hourly basis in the San Jose 1 datacenter using the command `slcli vs create`. :: $ slcli vs create --hostname=example --domain=softlayer.com --cpu 2 --memory 1024 -o UBUNTU_14_64 --datacenter=sjc01 --billing=hourly This action will incur charges on your account. Continue? [y/N]: y :.........:......................................: : name : value : :.........:......................................: : id : 1234567 : : created : 2013-06-13T08:29:44-06:00 : : guid : 6e013cde-a863-46ee-8s9a-f806dba97c89 : :.........:......................................: After the last command, the virtual server is now being built. It should instantly appear in your virtual server list now. :: $ slcli vs list :.........:............:.......................:.......:........:................:..............:....................: : id : datacenter : host : cores : memory : primary_ip : backend_ip : active_transaction : :.........:............:.......................:.......:........:................:..............:....................: : 1234567 : sjc01 : example.softlayer.com : 2 : 1G : 108.168.200.11 : 10.54.80.200 : Assign Host : :.........:............:.......................:.......:........:................:..............:....................: Cool. You may ask, "It's creating... but how do I know when it's done?" Well, here's how: :: $ slcli vs ready 'example' --wait=600 READY When the previous command returns, you'll know that the virtual server has finished the provisioning process and is ready to use. This is *very* useful for chaining commands together. Now that you have your virtual server, let's get access to it. To do that, use the `slcli vs detail` command. From the example below, you can see that the username is 'root' and password is 'ABCDEFGH'. .. warning:: Be careful when using the `--passwords` flag. This will print the virtual server's password on the screen. Make sure no one is looking over your shoulder. It's also advisable to change your root password soon after creating your virtual server, or to create a user with sudo access and disable SSH-based login directly to the root account. :: $ slcli vs detail example --passwords :..............:...........................: : Name : Value : :..............:...........................: : id : 1234567 : : hostname : example.softlayer.com : : status : Active : : state : Running : : datacenter : sjc01 : : cores : 2 : : memory : 1G : : public_ip : 108.168.200.11 : : private_ip : 10.54.80.200 : : os : Ubuntu : : private_only : False : : private_cpu : False : : created : 2013-06-13T08:29:44-06:00 : : modified : 2013-06-13T08:31:57-06:00 : : users : root ABCDEFGH : :..............:...........................: There are many other commands to help manage virtual servers. To see them all, use `slcli help vs`. :: $ slcli vs Usage: slcli vs [OPTIONS] COMMAND [ARGS]... Virtual Servers. Options: --help Show this message and exit. Commands: cancel Cancel virtual servers. capture Capture SoftLayer image. create Order/create virtual servers. create-options Virtual server order options. credentials List virtual server credentials. detail Get details for a virtual server. dns-sync Sync DNS records. edit Edit a virtual server's details. list List virtual servers. network Manage network settings. pause Pauses an active virtual server. power_off Power off an active virtual server. power_on Power on a virtual server. ready Check if a virtual server is ready. reboot Reboot an active virtual server. reload Reload operating system on a virtual server. rescue Reboot into a rescue image. resume Resumes a paused virtual server. upgrade Upgrade a virtual server. softlayer-python-5.4.2/docs/conf.py000066400000000000000000000204421324365065500173130ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # SoftLayer API Python Client documentation build configuration file, created # by sphinx-quickstart on Fri Mar 22 11:08:48 2013. # # 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 import os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # sys.path.insert(0, os.path.abspath('.')) sys.path.insert(0, os.path.abspath('..')) # -- General configuration ---------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.coverage', 'sphinx.ext.viewcode'] # 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-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'SoftLayer API Python Client' # Hack to avoid the "Redefining built-in 'copyright'" error from static # analysis tools globals()['copyright'] = u'2017, SoftLayer Technologies, Inc.' # 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 = 'latest' # The full version, including alpha/beta/rc tags. release = 'latest' # 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 patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # 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 = 'friendly' # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] # -- Options for HTML output -------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. on_rtd = os.environ.get('READTHEDOCS', None) == 'True' if on_rtd: html_theme = 'default' else: html_theme = 'nature' html_style = "style.css" # 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_domain_indices = 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, "Created using Sphinx" is shown in the HTML footer. Default is True. # html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'SoftLayerAPIPythonClientdoc' # -- Options for LaTeX output ------------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # 'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, # documentclass [howto/manual]). latex_documents = [ ('index', 'SoftLayerAPIPythonClient.tex', u'SoftLayer API Python Client Documentation', u'SoftLayer Technologies, Inc.', '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 # If true, show page references after internal links. # latex_show_pagerefs = False # If true, show URL addresses after external links. # latex_show_urls = False # Documents to append as an appendix to all manuals. # latex_appendices = [] # If false, no module index is generated. # latex_domain_indices = True # -- Options for manual page output ------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'softlayerapipythonclient', u'SoftLayer API Python Client Documentation', [u'SoftLayer Technologies, Inc.'], 1) ] # If true, show URL addresses after external links. # man_show_urls = False # -- Options for Texinfo output ----------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'SoftLayerAPIPythonClient', u'SoftLayer API Python Client Documentation', u'SoftLayer Technologies, Inc.', 'SoftLayerAPIPythonClient', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. # texinfo_appendices = [] # If false, no module index is generated. # texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. # texinfo_show_urls = 'footnote' softlayer-python-5.4.2/docs/config_file.rst000066400000000000000000000015671324365065500210210ustar00rootroot00000000000000.. _config_file: Configuration File ================== The SoftLayer API bindings load your settings from a number of different locations. * Input directly into SoftLayer.create_client_from_env(...) * Enviorment variables (`SL_USERNAME`, `SL_API_KEY`) * Config file locations (`~/.softlayer`, `/etc/softlayer.conf`) * Or argument (`-C/path/to/config` or `--config=/path/to/config`) The configuration file is INI-based and requires the `softlayer` section to be present. The only required fields are `username` and `api_key`. You can optionally supply the `endpoint_url` as well. This file is created automatically by the `slcli setup` command detailed here: :ref:`config_setup`. *Config Example* :: [softlayer] username = username api_key = oyVmeipYQCNrjVS4rF9bHWV7D75S6pa1fghFl384v7mwRCbHTfuJ8qRORIqoVnha endpoint_url = https://api.softlayer.com/xmlrpc/v3/ timeout = 40 softlayer-python-5.4.2/docs/dev/000077500000000000000000000000001324365065500165705ustar00rootroot00000000000000softlayer-python-5.4.2/docs/dev/cla-corporate.md000066400000000000000000000154551324365065500216570ustar00rootroot00000000000000#### International Business Machines, Inc. ##### Software Grant and Corporate Contributor License Agreement ("Agreement") https://github.com/softlayer/softlayer-python/ Thank you for your interest in IBM’s softlayer-python project (“the Project"). In order to clarify the intellectual property license granted with Contributions from any person or entity, IBM must have a Contributor License Agreement (CLA) on file that has been signed by each Contributor, indicating agreement to the license terms below. This license is for your protection as a Contributor as well as the protection of IBM and its users; it does not change your rights to use your own Contributions for any other purpose. This version of the Agreement allows an entity (the "Corporation") to submit Contributions to the Project, to authorize Contributions submitted by its designated employees to the Project, and to grant copyright and patent licenses thereto. If you have not already done so, please complete and sign, then scan and email a PDF file of this Agreement to pjackson@softlayer.com. Please read this document carefully before signing and keep a copy for your records. Corporation name: ________________________________________________ Corporation address: ________________________________________________ Point of Contact: ________________________________________________ E-Mail: ________________________________________________ Telephone: _____________________ You accept and agree to the following terms and conditions for Your present and future Contributions submitted to the Project. Except for the license granted herein to IBM and recipients of software distributed by IBM, You reserve all right, title, and interest in and to Your Contributions. 1. Definitions. "You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement with IBM. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. 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. "Contribution" shall mean the code, documentation or other original works of authorship expressly identified in Schedule B, as well as any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to IBM for inclusion in, or documentation of, the Project managed by IBM (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to IBM 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, IBM for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution." 2. Grant of Copyright License. Subject to the terms and conditions of this Agreement, You hereby grant to IBM and to recipients of software distributed by IBM 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 Your Contributions and such derivative works. 3. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to IBM and to recipients of software distributed by IBM 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 You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) were submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed. 4. You represent that You are legally entitled to grant the above license. You represent further that each employee of the Corporation designated on Schedule A below (or in a subsequent written modification to that Schedule) is authorized to submit Contributions on behalf of the Corporation. 5. You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others). 6. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your 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. 7. Should You wish to submit work that is not Your original creation, You may submit it to IBM separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [named here]". 8. It is your responsibility to notify IBM when any change is required to the list of designated employees authorized to submit Contributions on behalf of the Corporation, or to the Corporation's Point of Contact with IBM. Please sign: __________________________________ Date: _______________ Title: __________________________________ Corporation: __________________________________ Schedule A [Initial list of designated employees. NB: authorization is not tied to particular Contributions.] Schedule B [Identification of optional concurrent software grant. Would be left blank or omitted if there is no concurrent software grant.] softlayer-python-5.4.2/docs/dev/cla-individual.md000066400000000000000000000151021324365065500217760ustar00rootroot00000000000000#### International Business Machines, Inc. (IBM) ##### Individual Contributor License Agreement ("Agreement") https://github.com/softlayer/softlayer-python Thank you for your interest in the softlayer-python project ("the Project"). In order to clarify the intellectual property license granted with Contributions from any person or entity, IBM must have a Contributor License Agreement ("CLA") on file that has been signed by each Contributor, indicating agreement to the license terms below. This license is for your protection as a Contributor as well as the protection of IBM and its customers; it does not change your rights to use your own Contributions for any other purpose. If you have not already done so, please complete and sign, then scan and email a PDF file of this Agreement to pjackson@softlayer.com. Please read this document carefully before signing and keep a copy for your records. Full name: ______________________________________________________ (optional) Public name: _________________________________________ Mailing Address: ________________________________________________ Country: ______________________________________________________ Telephone: ______________________________________________________ E-Mail: ______________________________________________________ You accept and agree to the following terms and conditions for Your present and future Contributions submitted to the Project. Except for the license granted herein to IBM and recipients of software distributed by IBM, You reserve all right, title, and interest in and to Your Contributions. 1. Definitions. "You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement with IBM. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. 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. "Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to the Project for inclusion in, or documentation of, the Project (”the Work”). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Project 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 Project for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution." 2. Grant of Copyright License. Subject to the terms and conditions of this Agreement, You hereby grant to IBM and to recipients of software distributed by IBM 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 Your Contributions and such derivative works. 3. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to IBM and to recipients of software distributed by IBM 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 to which Your Contribution(s) were submitted, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed. 4. You represent that you are legally entitled to grant the above license. If your employer(s) has rights to intellectual property that you create that includes your Contributions, you represent that you have received permission to make Contributions on behalf of that employer, that your employer has waived such rights for your Contributions to the Project, or that your employer has executed a separate Corporate CLA with IBM. 5. You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others). You represent that Your Contribution submissions include complete details of any third-party license or other restriction (including, but not limited to, related patents and trademarks) of which you are personally aware and which are associated with any part of Your Contributions. 6. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your 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. 7. Should You wish to submit work that is not Your original creation, You may submit it to the Project separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [named here]". 8. You agree to notify IBM of any facts or circumstances of which you become aware that would make these representations inaccurate in any respect. Please sign: __________________________________ Date: ________________ softlayer-python-5.4.2/docs/dev/cli.rst000066400000000000000000000106301324365065500200710ustar00rootroot00000000000000.. _cli_dev: Command-Line Interface Developer Guide ====================================== The SoftLayer CLI can be used to manage many different SoftLayer services directly from the command line. The command line parsing is currently based on `click `_, which is a command parsing library along with some additions to dynamically load modules from a routes-like file and from `entry points `_. First Example ------------- For the first example, we can create `slcli table-example` by creating the following file at SoftLayer/CLI/table_example.py: :: """A formatting table example.""" from SoftLayer.CLI import environment from SoftLayer.CLI import formatting import click @click.command() @environment.pass_env def cli(env): """This returns an table that highlights how tables are output""" # create a table with two columns: col1, col2 table = formatting.Table(['col1', 'col2']) # align the data facing each other # valid values are r, c, l for right, center, left # note, these are suggestions based on the format chosen by the user table.align['col1'] = 'r' table.align['col2'] = 'l' # add rows table.add_row(['test', 'test']) table.add_row(['test2', 'test2']) env.fout(table) Then we need to register it so that `slcli table-example` will know to route to this new module. We do that by adding ALL_ROUTES in SoftLayer/CLI/routes.py to include the following: :: ... ('table-example', 'SoftLayer.CLI.table_example:cli'), ... Which gives us :: $ slcli table-example :.......:.......: : col1 : col2 : :.......:.......: : test : test : : test2 : test2 : :.......:.......: $ slcli --format=raw table-example test test test2 test2 Formatting of the data represented in the table is actually controlled upstream from the CLIRunnable's making supporting more data formats in the future easier. Arguments --------- A command usually isn't very useful without context or arguments of some kind. With click, you have a large array of argument and option types at your disposal. Additionally, with the SoftLayer CLI, we have global options and context which is stored in `SoftLayer.CLI.environment.Environment` and is attainable through a decorator located at `SoftLayer.CLI.environment.pass_env`. An example of options and the environment is shown below. It also shows how output should be done using `env.out` instead of printing. This is used for testing and to have a consistent way to print things onto the screen. :: from SoftLayer.CLI import environment import click @click.command() @click.option("--number", required=True, type=click.INT, help="print different output") @click.option("--choice", type=click.Choice(['this', 'that']), help="print different output") @click.option("--test", help="print different output") @environment.pass_env def cli(env, number, choice, test): """Argument parsing example""" if test: env.out("Just testing, move along...") else: env.out("This is fo'realz!") if choice == 'this': env.out("Selected this") elif choice == 'that': env.out("Selected that") env.out("This is a number: %d" % number) Refer to the click library documentation for more options. Accessing the API ----------------- A SoftLayer client is stood up for every command and is available through `SoftLayer.CLI.environment.Environment.client`. The example below shows how to make a simple API call to the SoftLayer_Account::getObject. :: from SoftLayer.CLI import environment import click @click.command() @environment.pass_env def cli(env): """Using the SoftLayer API client""" account = env.client['Account'].getObject() return account['companyName'] Aborting execution ------------------ When a confirmation fails, you probably want to stop execution and give a non-zero exit code. To do that, raise a `SoftLayer.CLI.exceptions.CLIAbort` exception with the message for the user as the first parameter. This will prevent any further execution and properly return the right error code. :: raise CLIAbort("Aborting. Failed confirmation") softlayer-python-5.4.2/docs/dev/example_module.rst000066400000000000000000000040271324365065500223250ustar00rootroot00000000000000.. _example_module: :orphan: Example CLI Module ================== :: """ usage: slcli example [] [...] [options] Example implementation of a CLI module Available commands are: parse parsing args example pretty formatted print example print print example """ from SoftLayer.CLI import ( CLIRunnable, Table, no_going_back, confirm) class ExampleAction(CLIRunnable): """ usage: slcli example print [options] Print example """ action = 'print' def execute(self, args): print "EXAMPLE!" class ExamplePretty(CLIRunnable): """ usage: slcli example pretty [options] Pretty output example """ action = 'pretty' def execute(self, args): # create a table with two columns: col1, col2 t = Table(['col1', 'col2']) # align the data facing each other # valid values are r, c, l for right, center, left # note, these are suggestions based on the format chosen by the user t.align['col1'] = 'r' t.align['col2'] = 'l' # add rows t.add_row(['test', 'test']) t.add_row(['test2', 'test2']) return t class ExampleArgs(CLIRunnable): """ usage: slcli example parse [--test] [--this=THIS|--that=THAT] (--one|--two) [options] Argument parsing example Options: --test Print different output """ action = 'parse' options = ['confirm'] def execute(self, args): if args.get('--test'): print "Just testing, move along..." else: print "This is fo'realz!" if args['--one']: print 1 elif args['--two']: print 2 if args.get('--this'): print "I gots", args['--this'] if args.get('--that'): print "you dont have", args['--that'] softlayer-python-5.4.2/docs/dev/index.rst000066400000000000000000000122531324365065500204340ustar00rootroot00000000000000.. _api_dev: Contribution Guide ================== This page explains how to get started contributing code to the SoftLayer API Python Bindings project. Code Organization ----------------- * **docs** - Where The source to this documentation lives. * **SoftLayer** - All the source lives under here. * **API** - Primary API client. * **CLI** - Code for the command-line interface. * **managers** - API Managers. Abstractions to help use the API. Setting Up A Dev Environment ---------------------------- Before working with the SoftLayer Python API client source, we strongly recommend that you know how to use Python's virtual environment, `virtualenv `_. Virtualenv allows you to create isolated Python environments that are individually tailored to particular development projects. Each environment can have its own set of libraries and even its own Python interpreter. This keeps them fully isolated, reducing the possibility of library conflicts between different projects. After you have virtualenv, you should set up a virtual environment and activate it whenever you are working on softlayer-python. The commands needed to setup an environment and activate it might look something like this: :: virtualenv --no-site-packages softlayer_env source softlayer_env/bin/activate Please refer to the virtualenv documentation for more information about creating, and working with virtual environments. Once you have an appropriate environment, you will then download the SoftLayer API Python Bindings source code by following the :ref:`installation instructions `. Change into softlayer-python source directory and run the following to install the pre-requisites that you'll need in order to run the test suites: :: pip install -r tools/test-requirements.txt Testing ------- The project has a mix of functional and unit tests. Before submitting changes to be integrated into the project, you should validate your code using `tox `_. Simply issue the tox command from the root of the source tree: :: tox In addition to testing different versions of Python, tox checks for common mistakes in the code using `Flake8 `_ and `pylint `_. You should eliminate the linting errors that are reported before submitting your code. You can run only the linting checks by using this command: :: tox -eanalysis The project's configuration instructs tox to test against many different versions of Python. A tox test will use as many of those as it can find on your local computer. Rather than installing all those versions, we recommend that you point the `Travis `_ continuous integration tool at your GitHub fork. Travis will run the test against the full suite of Python versions every time you push new code. Using tox to run tests in multiple environments can be very time consuming. If you wish to quickly run the tests in your own environment, you may do so using `py.test `_. The command to do that is: :: py.test tests Documentation ------------- The project is documented in `reStructuredText `_ and built using `Sphinx `_. If you have `fabric `_ installed, you simply need to run the following to build the docs: :: fab make_html The documentation will be built in `docs/_build/html`. If you don't have fabric, use the following commands. :: cd docs make html The primary docs are built at `Read the Docs `_. Style ----- This project tries to follow :pep:`8` and most of the style suggestions that pyflakes recommends. Run `Flake8 `_ regularly. Flake8, with project-specific exceptions, can be run by using tox: :: tox -e analysis Contributing ------------ Contributing to the Python API bindings follows the `fork-pull-request model `_ on `GitHub `_. The project uses GitHub's `issue tracker `_ and `pull requests `_ to manage source control, bug fixes and new feature development regarding the API bindings and the CLI. In order to contribute, we require that you sign a contributer agreemenet: * Sign our contributor agreement (CLA) You can find the :download:`CLA here `. * If you're contributing on behalf of your employer we'll need a signed copy of our corporate contributor agreement (CCLA) as well. You can find the :download:`CCLA here `. Developer Resources ------------------- .. toctree:: SoftLayer API Documentation Source on GitHub Issues Pull Requests PyPI Twitter #softlayer on freenode softlayer-python-5.4.2/docs/index.rst000066400000000000000000000026621324365065500176610ustar00rootroot00000000000000.. SoftLayer API Python Client documentation SoftLayer API Python Client |version| ======================================== `API Docs `_ ``|`` `GitHub `_ ``|`` `Issues `_ ``|`` `Pull Requests `_ ``|`` `PyPI `_ ``|`` `Twitter `_ ``|`` `#softlayer on freenode `_ This is the documentation to SoftLayer's Python API Bindings. These bindings use SoftLayer's `XML-RPC interface `_ in order to manage SoftLayer services. .. toctree:: :maxdepth: 3 :glob: install config_file api/* cli Contributing ------------ .. toctree:: :maxdepth: 1 :glob: dev/index dev/cli External Links -------------- * `SoftLayer API Documentation `_ * `Source on GitHub `_ * `Issues `_ * `Pull Requests `_ * `PyPI `_ * `Twitter `_ * `#softlayer on freenode `_ softlayer-python-5.4.2/docs/install.rst000066400000000000000000000033001324365065500202060ustar00rootroot00000000000000.. _install: Installation ============ What's Included --------------- When you install softlayer-python you you will get the following: * a python package called 'SoftLayer' (casing is important) available in your python path. * a command-line client placed in your system path named 'slcli'. Using Pip --------- Install via pip: :: $ pip install softlayer Debian/Ubuntu ------------- For Debian "jessie" (currently testing) and Ubuntu 14.04, official system packages are available. **These are typically a couple versions behind so it is recommended to install from pypi if problems are encountered.** :: $ sudo apt-get install python-softlayer .. _install_from_source: From Source ----------- The project is developed on GitHub, at `https://github.com/softlayer/softlayer-python `_. Install from source via pip (requires `git `_): :: $ pip install git+git://github.com/softlayer/softlayer-python.git You can clone the public repository:: $ git clone git@github.com:softlayer/softlayer-python.git Or, Download the `tarball `_: :: $ curl -OL https://github.com/softlayer/softlayer-python/tarball/master Or, download the `zipball `_: :: $ curl -OL https://github.com/softlayer/softlayer-python/zipball/master Once you have a copy of the source you can install it with one of the following commands: :: $ python setup.py install Or: :: $ pip install . For more information about working with the source, or contributing to the project, please see the :ref:`Contribution Guide `. softlayer-python-5.4.2/fabfile.py000066400000000000000000000021461324365065500170270ustar00rootroot00000000000000import os.path import shutil from fabric.api import local, lcd, puts, abort def make_html(): "Build HTML docs" with lcd('docs'): local('make html') def upload(): "Upload distribution to PyPi" local('python setup.py sdist upload') local('python setup.py bdist_wheel upload') def clean(): puts("* Cleaning Repo") directories = ['.tox', 'SoftLayer.egg-info', 'build', 'dist'] for directory in directories: if os.path.exists(directory) and os.path.isdir(directory): shutil.rmtree(directory) def release(version, force=False): """Perform a release. Example: $ fab release:3.0.0 """ if version.startswith("v"): abort("Version should not start with 'v'") version_str = "v%s" % version clean() local("pip install wheel") puts(" * Uploading to PyPI") upload() puts(" * Tagging Version %s" % version_str) force_option = 'f' if force else '' local("git tag -%sam \"%s\" %s" % (force_option, version_str, version_str)) puts(" * Pushing Tag to upstream") local("git push upstream %s" % version_str) softlayer-python-5.4.2/pkg/000077500000000000000000000000001324365065500156435ustar00rootroot00000000000000softlayer-python-5.4.2/pkg/softlayer-python.spec000066400000000000000000000030571324365065500220530ustar00rootroot00000000000000# sitelib for noarch packages, sitearch for others (remove the unneeded one) %{!?python_sitelib: %global python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")} %{!?python_sitearch: %global python_sitearch %(%{__python} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib(1))")} %global commit master %global shortcommit %(c=%{commit}; echo ${c:0:7}) Name: softlayer-python Version: %{commit} Release: 1%{?dist} Summary: softlayer python interface License: MIT URL: https://github.com/softlayer/softlayer-python Source: https://github.com/softlayer/softlayer-python/archive/%{commit}/softlayer-python-%{commit}.tar.gz #BuildArch: BuildRequires: python-devel, python-setuptools Requires: python-requests, python-click, python-prettytable >= 0.7.0 Requires: python-importlib, python-six >= 1.6.1 %description %prep %setup -q %build # Remove CFLAGS=... for noarch packages (unneeded) CFLAGS="$RPM_OPT_FLAGS" %{__python} setup.py build %install rm -rf $RPM_BUILD_ROOT %{__python} setup.py install -O1 --skip-build --root $RPM_BUILD_ROOT %files %doc /usr/bin/sl # For noarch packages: sitelib %{python_sitelib}/* # For arch-specific packages: sitearch #%{python_sitearch}/* %changelog * Thu Oct 23 2014 Christopher Gallo - master-2 - Changed Source to a proper github url, added python-setuptool build requirement * Wed Mar 19 2014 Andy Bakun 773ab17-1 - initial packaging softlayer-python-5.4.2/setup.cfg000066400000000000000000000000751324365065500167050ustar00rootroot00000000000000[tool:pytest] python_files = *_tests.py [wheel] universal=1 softlayer-python-5.4.2/setup.py000066400000000000000000000035651324365065500166050ustar00rootroot00000000000000from __future__ import print_function import codecs import os from setuptools import setup, find_packages DESCRIPTION = "A library for SoftLayer's API" if os.path.exists('README.rst'): with codecs.open('README.rst', 'r', 'utf-8') as readme_file: LONG_DESCRIPTION = readme_file.read() else: LONG_DESCRIPTION = DESCRIPTION setup( name='SoftLayer', version='5.4.2', description=DESCRIPTION, long_description=LONG_DESCRIPTION, author='SoftLayer Technologies, Inc.', author_email='sldn@softlayer.com', packages=find_packages(exclude=['tests']), license='MIT', zip_safe=False, url='http://github.com/softlayer/softlayer-python', entry_points={ 'console_scripts': [ 'slcli = SoftLayer.CLI.core:main', 'sl = SoftLayer.CLI.deprecated:main', ], }, install_requires=[ 'six >= 1.7.0', 'prettytable >= 0.7.0', 'click >= 5', 'requests >= 2.18.4', 'prompt_toolkit >= 0.53', 'pygments >= 2.0.0', 'urllib3 >= 1.22' ], keywords=['softlayer', 'cloud'], classifiers=[ 'Environment :: Console', 'Environment :: Web Environment', 'Intended Audience :: Developers', 'Intended Audience :: System Administrators', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Topic :: Software Development :: Libraries :: Python Modules', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', ], ) softlayer-python-5.4.2/tests/000077500000000000000000000000001324365065500162245ustar00rootroot00000000000000softlayer-python-5.4.2/tests/CLI/000077500000000000000000000000001324365065500166335ustar00rootroot00000000000000softlayer-python-5.4.2/tests/CLI/__init__.py000066400000000000000000000000001324365065500207320ustar00rootroot00000000000000softlayer-python-5.4.2/tests/CLI/core_tests.py000066400000000000000000000076561324365065500213750ustar00rootroot00000000000000""" SoftLayer.tests.CLI.core_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import logging import SoftLayer from SoftLayer.CLI import core from SoftLayer.CLI import environment from SoftLayer import testing from SoftLayer import utils import click import mock class CoreTests(testing.TestCase): def test_load_all(self): for path, cmd in recursive_subcommand_loader(core.cli, current_path='root'): try: cmd.main(args=['--help']) except SystemExit as ex: if ex.code != 0: self.fail("Non-zero exit code for command: %s" % path) def test_verbose_max(self): with mock.patch('logging.getLogger') as log_mock: result = self.run_command(['-vvv', 'vs', 'list']) self.assert_no_fail(result) log_mock().addHandler.assert_called_with(mock.ANY) log_mock().setLevel.assert_called_with(logging.DEBUG) def test_build_client(self): env = environment.Environment() result = self.run_command(['vs', 'list'], env=env) self.assert_no_fail(result) self.assertIsNotNone(env.client) def test_diagnostics(self): result = self.run_command(['-v', 'vs', 'list']) self.assert_no_fail(result) self.assertIn('SoftLayer_Account::getVirtualGuests', result.output) self.assertIn('"execution_time"', result.output) self.assertIn('"api_calls"', result.output) self.assertIn('"version"', result.output) self.assertIn('"python_version"', result.output) self.assertIn('"library_location"', result.output) class CoreMainTests(testing.TestCase): @mock.patch('SoftLayer.CLI.core.cli.main') @mock.patch('sys.stdout', new_callable=utils.StringIO) def test_unexpected_error(self, stdoutmock, climock): climock.side_effect = AttributeError('Attribute foo does not exist') self.assertRaises(SystemExit, core.main) self.assertIn("Feel free to report this error as it is likely a bug", stdoutmock.getvalue()) self.assertIn("Traceback (most recent call last)", stdoutmock.getvalue()) self.assertIn("AttributeError: Attribute foo does not exist", stdoutmock.getvalue()) @mock.patch('SoftLayer.CLI.core.cli.main') @mock.patch('sys.stdout', new_callable=utils.StringIO) def test_sl_error(self, stdoutmock, climock): ex = SoftLayer.SoftLayerAPIError('SoftLayer_Exception', 'Not found') climock.side_effect = ex self.assertRaises(SystemExit, core.main) self.assertIn("SoftLayerAPIError(SoftLayer_Exception): Not found", stdoutmock.getvalue()) @mock.patch('SoftLayer.CLI.core.cli.main') @mock.patch('sys.stdout', new_callable=utils.StringIO) def test_auth_error(self, stdoutmock, climock): ex = SoftLayer.SoftLayerAPIError('SoftLayer_Exception', 'Invalid API token.') climock.side_effect = ex self.assertRaises(SystemExit, core.main) self.assertIn("Authentication Failed:", stdoutmock.getvalue()) self.assertIn("use 'slcli config setup'", stdoutmock.getvalue()) def recursive_subcommand_loader(root, current_path=''): """Recursively load and list every command.""" if getattr(root, 'list_commands', None) is None: return ctx = click.Context(root) for command in root.list_commands(ctx): new_path = '%s:%s' % (current_path, command) logging.info("loading %s", new_path) new_root = root.get_command(ctx, command) if new_root is None: raise Exception('Could not load command: %s' % command) for path, cmd in recursive_subcommand_loader(new_root, current_path=new_path): yield path, cmd yield current_path, new_root softlayer-python-5.4.2/tests/CLI/custom_types_tests.py000066400000000000000000000021521324365065500231650ustar00rootroot00000000000000""" SoftLayer.tests.CLI.custom_types_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import click from SoftLayer.CLI.custom_types import NetworkParamType from SoftLayer import testing class CustomTypesTests(testing.TestCase): def test_network_param_convert(self): param = NetworkParamType() (ip_address, cidr) = param.convert('10.0.0.0/24', None, None) self.assertEqual(ip_address, '10.0.0.0') self.assertEqual(cidr, 24) def test_network_param_convert_fails(self): param = NetworkParamType() self.assertRaises(click.exceptions.BadParameter, lambda: param.convert('10.0.0.0//24', None, None)) self.assertRaises(click.exceptions.BadParameter, lambda: param.convert('10.0.0.0', None, None)) self.assertRaises(click.exceptions.BadParameter, lambda: param.convert('what is it', None, None)) self.assertRaises(click.exceptions.BadParameter, lambda: param.convert('10.0.0.0/hi', None, None)) softlayer-python-5.4.2/tests/CLI/deprecated_tests.py000066400000000000000000000020131324365065500225230ustar00rootroot00000000000000""" SoftLayer.tests.CLI.deprecated_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import mock from SoftLayer.CLI import deprecated from SoftLayer import testing from SoftLayer import utils class EnvironmentTests(testing.TestCase): def test_main(self): with mock.patch('sys.stderr', new=utils.StringIO()) as fake_out: ex = self.assertRaises(SystemExit, deprecated.main) self.assertEqual(ex.code, -1) self.assertIn("ERROR: Use the 'slcli' command instead.", fake_out.getvalue()) def test_with_args(self): with mock.patch('sys.stderr', new=utils.StringIO()) as fake_out: with mock.patch('sys.argv', new=['sl', 'module', 'subcommand']): ex = self.assertRaises(SystemExit, deprecated.main) self.assertEqual(ex.code, -1) self.assertIn("ERROR: Use the 'slcli' command instead.", fake_out.getvalue()) softlayer-python-5.4.2/tests/CLI/environment_tests.py000066400000000000000000000035071324365065500230000ustar00rootroot00000000000000""" SoftLayer.tests.CLI.environment_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import click import mock from SoftLayer.CLI import environment from SoftLayer import testing @click.command() def fixture_command(): pass class EnvironmentTests(testing.TestCase): def set_up(self): self.env = environment.Environment() def test_list_commands(self): self.env.load() actions = self.env.list_commands() self.assertIn('virtual', actions) self.assertIn('dns', actions) def test_get_command_invalid(self): cmd = self.env.get_command('invalid', 'command') self.assertEqual(cmd, None) def test_get_command(self): fixture_loader = environment.ModuleLoader( 'tests.CLI.environment_tests', 'fixture_command', ) self.env.commands = {'fixture:run': fixture_loader} command = self.env.get_command('fixture', 'run') self.assertIsInstance(command, click.Command) @mock.patch('click.prompt') def test_input(self, prompt_mock): r = self.env.input('input') prompt_mock.assert_called_with('input', default=None, show_default=True) self.assertEqual(prompt_mock(), r) @mock.patch('click.prompt') def test_getpass(self, prompt_mock): r = self.env.getpass('input') prompt_mock.assert_called_with('input', default=None, hide_input=True) self.assertEqual(prompt_mock(), r) def test_resolve_alias(self): self.env.aliases = {'aliasname': 'realname'} r = self.env.resolve_alias('aliasname') self.assertEqual(r, 'realname') r = self.env.resolve_alias('realname') self.assertEqual(r, 'realname') softlayer-python-5.4.2/tests/CLI/helper_tests.py000066400000000000000000000406561324365065500217210ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ SoftLayer.tests.CLI.helper_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import json import os import sys import tempfile import click import mock import six from SoftLayer.CLI import core from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting from SoftLayer.CLI import helpers from SoftLayer.CLI import template from SoftLayer import testing class CLIJSONEncoderTest(testing.TestCase): def test_default(self): out = json.dumps({ 'formattedItem': formatting.FormattedItem('normal', 'formatted') }, cls=formatting.CLIJSONEncoder) self.assertEqual(out, '{"formattedItem": "normal"}') out = json.dumps({'normal': 'string'}, cls=formatting.CLIJSONEncoder) self.assertEqual(out, '{"normal": "string"}') def test_fail(self): self.assertRaises( TypeError, json.dumps, {'test': object()}, cls=formatting.CLIJSONEncoder) class PromptTests(testing.TestCase): @mock.patch('click.prompt') def test_do_or_die(self, prompt_mock): confirmed = '37347373737' prompt_mock.return_value = confirmed result = formatting.no_going_back(confirmed) self.assertTrue(result) # no_going_back should cast int's to str() confirmed = '4712309182309' prompt_mock.return_value = confirmed result = formatting.no_going_back(int(confirmed)) self.assertTrue(result) confirmed = None prompt_mock.return_value = '' result = formatting.no_going_back(confirmed) self.assertFalse(result) @mock.patch('click.prompt') def test_confirmation(self, prompt_mock): prompt_mock.return_value = 'Y' res = formatting.confirm('Confirm?', default=False) self.assertTrue(res) prompt_mock.return_value = 'N' res = formatting.confirm('Confirm?', default=False) self.assertFalse(res) prompt_mock.return_value = 'Y' res = formatting.confirm('Confirm?', default=True) self.assertTrue(res) prompt_mock.assert_called_with('Confirm? [Y/n]', default='y', show_default=False) prompt_mock.return_value = 'N' res = formatting.confirm('Confirm?', default=False) self.assertFalse(res) prompt_mock.assert_called_with('Confirm? [y/N]', default='n', show_default=False) class FormattedItemTests(testing.TestCase): def test_init(self): item = formatting.FormattedItem('test', 'test_formatted') self.assertEqual('test', item.original) self.assertEqual('test_formatted', item.formatted) self.assertEqual('test', str(item)) item = formatting.FormattedItem('test') self.assertEqual('test', item.original) self.assertEqual('test', item.formatted) self.assertEqual('test', str(item)) def test_unicode(self): if six.PY2: item = formatting.FormattedItem(u'\u32423', u'\u32423') self.assertEqual(u'\u32423', item.original) self.assertEqual(u'\u32423', item.formatted) self.assertEqual('invalid', str(item)) def test_mb_to_gb(self): item = formatting.mb_to_gb(1024) self.assertEqual(1024, item.original) self.assertEqual('1G', item.formatted) item = formatting.mb_to_gb('1024') self.assertEqual('1024', item.original) self.assertEqual('1G', item.formatted) item = formatting.mb_to_gb('1025.0') self.assertEqual('1025.0', item.original) self.assertEqual('1G', item.formatted) self.assertRaises(ValueError, formatting.mb_to_gb, '1024string') def test_gb(self): item = formatting.gb(2) self.assertEqual(2048, item.original) self.assertEqual('2G', item.formatted) item = formatting.gb('2') self.assertEqual(2048, item.original) self.assertEqual('2G', item.formatted) item = formatting.gb('2.0') self.assertEqual(2048, item.original) self.assertEqual('2G', item.formatted) def test_blank(self): item = formatting.blank() self.assertEqual(None, item.original) self.assertEqual('-', item.formatted) self.assertEqual('NULL', str(item)) def test_sort_mixed(self): blank = formatting.blank() items = [10, blank] sorted_items = sorted(items) self.assertEqual(sorted_items, [blank, 10]) items = [blank, 10] sorted_items = sorted(items) self.assertEqual(sorted_items, [blank, 10]) items = [blank, "10"] sorted_items = sorted(items) self.assertEqual(sorted_items, [blank, "10"]) def test_sort(self): items = [10, formatting.FormattedItem(20), formatting.FormattedItem(5)] sorted_items = sorted(items) self.assertEqual(sorted_items, [formatting.FormattedItem(5), 10, formatting.FormattedItem(20)]) class FormattedListTests(testing.TestCase): def test_init(self): l = formatting.listing([1, 'two'], separator=':') self.assertEqual([1, 'two'], list(l)) self.assertEqual(':', l.separator) l = formatting.listing([]) self.assertEqual(',', l.separator) def test_to_python(self): l = formatting.listing([1, 'two']) result = l.to_python() self.assertEqual([1, 'two'], result) l = formatting.listing(x for x in [1, 'two']) result = l.to_python() self.assertEqual([1, 'two'], result) def test_str(self): l = formatting.listing([1, 'two']) result = str(l) self.assertEqual('1,two', result) l = formatting.listing((x for x in [1, 'two']), separator=':') result = str(l) self.assertEqual('1:two', result) class FormattedTxnTests(testing.TestCase): def test_active_txn_empty(self): result = formatting.active_txn({}) self.assertEqual(str(result), 'NULL') def test_active_txn(self): result = formatting.active_txn({ 'activeTransaction': { 'transactionStatus': { 'name': 'a', 'friendlyName': 'b' } } }) self.assertEqual(result.original, 'a') self.assertEqual(result.formatted, 'b') self.assertIsInstance(result, formatting.FormattedItem) def test_active_txn_missing(self): # A dict with activeTransaction but not transactionStatus # should return blank() instead of raising an exception b = formatting.blank() result = formatting.active_txn({ 'activeTransaction': {} }) self.assertIsInstance(result, formatting.FormattedItem) self.assertEqual(result.original, b.original) def test_transaction_status(self): result = formatting.transaction_status({ 'transactionStatus': { 'name': 'a', 'friendlyName': 'b' } }) self.assertEqual(result.original, 'a') self.assertEqual(result.formatted, 'b') self.assertIsInstance(result, formatting.FormattedItem) def test_transaction_status_missing(self): b = formatting.blank() result = formatting.transaction_status({ 'transactionStatus': {} }) self.assertIsInstance(result, formatting.FormattedItem) self.assertEqual(result.original, b.original) class CLIAbortTests(testing.TestCase): def test_init(self): e = exceptions.CLIAbort("something") self.assertEqual(2, e.code) self.assertEqual("something", e.message) self.assertIsInstance(e, exceptions.CLIHalt) class ResolveIdTests(testing.TestCase): def test_resolve_id_one(self): resolver = lambda r: [12345] self.assertEqual(helpers.resolve_id(resolver, 'test'), 12345) def test_resolve_id_none(self): resolver = lambda r: [] self.assertRaises( exceptions.CLIAbort, helpers.resolve_id, resolver, 'test') def test_resolve_id_multiple(self): resolver = lambda r: [12345, 54321] self.assertRaises( exceptions.CLIAbort, helpers.resolve_id, resolver, 'test') class TestTable(testing.TestCase): def test_table_with_duplicated_columns(self): self.assertRaises(exceptions.CLIHalt, formatting.Table, ['col', 'col']) class TestFormatOutput(testing.TestCase): def test_format_output_string(self): t = formatting.format_output('just a string', 'raw') self.assertEqual('just a string', t) t = formatting.format_output(b'just a string', 'raw') self.assertEqual(b'just a string', t) def test_format_output_raw(self): t = formatting.Table(['nothing']) t.align['nothing'] = 'c' t.add_row(['testdata']) t.sortby = 'nothing' ret = formatting.format_output(t, 'raw') self.assertNotIn('nothing', str(ret)) self.assertIn('testdata', str(ret)) def test_format_output_json(self): t = formatting.Table(['nothing']) t.align['nothing'] = 'c' t.add_row(['testdata']) t.add_row([formatting.blank()]) t.sortby = 'nothing' ret = formatting.format_output(t, 'json') # This uses json.dumps due to slight changes in the output between # py3.3 and py3.4 expected = json.dumps([{'nothing': 'testdata'}, {'nothing': None}], indent=4) self.assertEqual(expected, ret) ret = formatting.format_output('test', 'json') self.assertEqual('"test"', ret) def test_format_output_jsonraw(self): t = formatting.Table(['nothing']) t.align['nothing'] = 'c' t.add_row(['testdata']) t.add_row([formatting.blank()]) t.sortby = 'nothing' ret = formatting.format_output(t, 'jsonraw') # This uses json.dumps due to slight changes in the output between # py3.3 and py3.4 expected = json.dumps([{'nothing': 'testdata'}, {'nothing': None}]) self.assertEqual(expected, ret) ret = formatting.format_output('test', 'json') self.assertEqual('"test"', ret) def test_format_output_json_keyvaluetable(self): t = formatting.KeyValueTable(['key', 'value']) t.add_row(['nothing', formatting.blank()]) t.sortby = 'nothing' ret = formatting.format_output(t, 'json') self.assertEqual('''{ "nothing": null }''', ret) def test_format_output_jsonraw_keyvaluetable(self): t = formatting.KeyValueTable(['key', 'value']) t.add_row(['nothing', formatting.blank()]) t.sortby = 'nothing' ret = formatting.format_output(t, 'jsonraw') self.assertEqual('''{"nothing": null}''', ret) def test_format_output_json_string(self): ret = formatting.format_output("test", 'json') self.assertEqual('"test"', ret) def test_format_output_jsonraw_string(self): ret = formatting.format_output("test", 'jsonraw') self.assertEqual('"test"', ret) def test_format_output_formatted_item(self): item = formatting.FormattedItem('test', 'test_formatted') ret = formatting.format_output(item, 'table') self.assertEqual('test_formatted', ret) def test_format_output_list(self): item = ['this', 'is', 'a', 'list'] ret = formatting.format_output(item, 'table') self.assertEqual(os.linesep.join(item), ret) def test_format_output_table(self): t = formatting.Table(['nothing']) t.align['nothing'] = 'c' t.add_row(['testdata']) t.sortby = 'nothing' ret = formatting.format_output(t, 'table') self.assertIn('nothing', str(ret)) self.assertIn('testdata', str(ret)) def test_unknown(self): t = formatting.format_output({}, 'raw') self.assertEqual({}, t) def test_sequentialoutput(self): # specifying the separator prevents windows from using \n\r t = formatting.SequentialOutput(separator="\n") self.assertTrue(hasattr(t, 'append')) t.append('This is a test') t.append('') t.append('More tests') output = formatting.format_output(t) self.assertEqual("This is a test\nMore tests", output) t.separator = ',' output = formatting.format_output(t) self.assertEqual("This is a test,More tests", output) def test_format_output_python(self): t = formatting.format_output('just a string', 'python') self.assertEqual('just a string', t) t = formatting.format_output(['just a string'], 'python') self.assertEqual(['just a string'], t) t = formatting.format_output({'test_key': 'test_value'}, 'python') self.assertEqual({'test_key': 'test_value'}, t) def test_format_output_python_keyvaluetable(self): t = formatting.KeyValueTable(['key', 'value']) t.add_row(['nothing', formatting.blank()]) t.sortby = 'nothing' ret = formatting.format_output(t, 'python') self.assertEqual({'nothing': None}, ret) def test_format_output_unicode(self): t = formatting.format_output('☃', 'raw') self.assertEqual('☃', t) item = formatting.FormattedItem('raw ☃', '☃') t = formatting.format_output(item) self.assertEqual('☃', t) t = formatting.format_output(item, 'raw') self.assertEqual('raw ☃', t) def test_format_output_table_invalid_sort(self): t = formatting.Table(['nothing']) t.align['nothing'] = 'c' t.add_row(['testdata']) t.sortby = 'DOES NOT EXIST' self.assertRaises( exceptions.CLIHalt, formatting.format_output, t, 'table', ) class TestTemplateArgs(testing.TestCase): def test_no_template_option(self): ctx = click.Context(core.cli) template.TemplateCallback()(ctx, None, None) self.assertIsNone(ctx.default_map) def test_template_options(self): ctx = click.Context(core.cli) path = os.path.join(testing.FIXTURE_PATH, 'sample_vs_template.conf') template.TemplateCallback(list_args=['disk'])(ctx, None, path) self.assertEqual(ctx.default_map, { 'cpu': '4', 'datacenter': 'dal05', 'domain': 'example.com', 'hostname': 'myhost', 'hourly': 'true', 'memory': '1024', 'monthly': 'false', 'network': '100', 'os': 'DEBIAN_7_64', 'disk': ['50', '100'], }) class TestExportToTemplate(testing.TestCase): def test_export_to_template(self): if(sys.platform.startswith("win")): self.skipTest("Test doesn't work in Windows") # Tempfile creation is wonky on windows with tempfile.NamedTemporaryFile() as tmp: template.export_to_template(tmp.name, { 'os': None, 'datacenter': 'ams01', 'disk': ('disk1', 'disk2'), # The following should get stripped out 'config': 'no', 'really': 'no', 'format': 'no', 'debug': 'no', # exclude list 'test': 'test', }, exclude=['test']) with open(tmp.name) as f: data = f.read() self.assertEqual(len(data.splitlines()), 2) self.assertIn('datacenter=ams01\n', data) self.assertIn('disk=disk1,disk2\n', data) class IterToTableTests(testing.TestCase): def test_format_api_dict(self): result = formatting._format_dict({'key': 'value'}) self.assertIsInstance(result, formatting.Table) self.assertEqual(result.columns, ['name', 'value']) self.assertEqual(result.rows, [['key', 'value']]) def test_format_api_list(self): result = formatting._format_list([{'key': 'value'}]) self.assertIsInstance(result, formatting.Table) self.assertEqual(result.columns, ['key']) self.assertEqual(result.rows, [['value']]) def test_format_api_list_non_objects(self): result = formatting._format_list(['a', 'b', 'c']) self.assertIsInstance(result, formatting.Table) self.assertEqual(result.columns, ['value']) self.assertEqual(result.rows, [['a'], ['b'], ['c']]) softlayer-python-5.4.2/tests/CLI/modules/000077500000000000000000000000001324365065500203035ustar00rootroot00000000000000softlayer-python-5.4.2/tests/CLI/modules/__init__.py000066400000000000000000000000001324365065500224020ustar00rootroot00000000000000softlayer-python-5.4.2/tests/CLI/modules/block_tests.py000066400000000000000000000705731324365065500232050ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.block_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ from SoftLayer import exceptions from SoftLayer import testing import json import mock class BlockTests(testing.TestCase): def test_access_list(self): result = self.run_command(['block', 'access-list', '1234']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Network_Storage', 'getObject') def test_volume_cancel(self): result = self.run_command([ '--really', 'block', 'volume-cancel', '1234']) self.assert_no_fail(result) self.assertEqual('Block volume with id 1234 has been marked' ' for cancellation\n', result.output) self.assert_called_with('SoftLayer_Billing_Item', 'cancelItem', args=(False, True, None)) def test_volume_set_lun_id_in_range(self): lun_mock = self.set_mock('SoftLayer_Network_Storage', 'createOrUpdateLunId') lun_mock.return_value = dict(volumeId=1234, value='42') result = self.run_command('block volume-set-lun-id 1234 42'.split()) self.assert_no_fail(result) self.assertEqual('Block volume with id 1234 is reporting LUN ID 42\n', result.output) def test_volume_set_lun_id_in_range_missing_value(self): lun_mock = self.set_mock('SoftLayer_Network_Storage', 'createOrUpdateLunId') lun_mock.return_value = dict(volumeId=1234) result = self.run_command('block volume-set-lun-id 1234 42'.split()) self.assert_no_fail(result) self.assertEqual('Failed to confirm the new LUN ID on volume 1234\n', result.output) def test_volume_set_lun_id_not_in_range(self): value = '-1' lun_mock = self.set_mock('SoftLayer_Network_Storage', 'createOrUpdateLunId') lun_mock.side_effect = exceptions.SoftLayerAPIError( 'SoftLayer_Exception_Network_Storage_Iscsi_InvalidLunId', 'The LUN ID specified is out of the valid range: %s [min: 0 max: 4095]' % (value)) result = self.run_command('block volume-set-lun-id 1234 42'.split()) self.assertIsNotNone(result.exception) self.assertIn('The LUN ID specified is out of the valid range', result.exception.faultString) def test_volume_detail(self): result = self.run_command(['block', 'volume-detail', '1234']) self.assert_no_fail(result) self.assertEqual({ 'Username': 'username', 'LUN Id': '2', 'Endurance Tier': 'READHEAVY_TIER', 'IOPs': 1000, 'Snapshot Capacity (GB)': '10', 'Snapshot Used (Bytes)': 1024, 'Capacity (GB)': '20GB', 'Target IP': '10.1.2.3', 'Data Center': 'dal05', 'Type': 'ENDURANCE', 'ID': 100, '# of Active Transactions': '1', 'Ongoing Transaction': 'This is a buffer time in which the customer may cancel the server', 'Replicant Count': '1', 'Replication Status': 'Replicant Volume Provisioning ' 'has completed.', 'Replicant Volumes': [[ {'Replicant ID': 'Volume Name', '1784': 'TEST_REP_1'}, {'Replicant ID': 'Target IP', '1784': '10.3.174.79'}, {'Replicant ID': 'Data Center', '1784': 'wdc01'}, {'Replicant ID': 'Schedule', '1784': 'REPLICATION_HOURLY'}, ], [ {'Replicant ID': 'Volume Name', '1785': 'TEST_REP_2'}, {'Replicant ID': 'Target IP', '1785': '10.3.177.84'}, {'Replicant ID': 'Data Center', '1785': 'dal01'}, {'Replicant ID': 'Schedule', '1785': 'REPLICATION_DAILY'}, ]], 'Original Volume Properties': [ {'Property': 'Original Volume Size', 'Value': '20'}, {'Property': 'Original Volume Name', 'Value': 'test-original-volume-name'}, {'Property': 'Original Snapshot Name', 'Value': 'test-original-snapshot-name'} ] }, json.loads(result.output)) def test_volume_list(self): result = self.run_command(['block', 'volume-list']) self.assert_no_fail(result) self.assertEqual([ { 'bytes_used': None, 'capacity_gb': 20, 'datacenter': 'dal05', 'id': 100, 'ip_addr': '10.1.2.3', 'lunId': None, 'rep_partner_count': None, 'storage_type': 'ENDURANCE', 'username': 'username', 'active_transactions': None }], json.loads(result.output)) @mock.patch('SoftLayer.BlockStorageManager.list_block_volumes') def test_volume_count(self, list_mock): list_mock.return_value = [ {'serviceResource': {'datacenter': {'name': 'dal05'}}}, {'serviceResource': {'datacenter': {'name': 'ams01'}}}, {'serviceResource': {'datacenter': {'name': 'dal05'}}} ] result = self.run_command(['block', 'volume-count']) self.assert_no_fail(result) self.assertEqual( { 'dal05': 2, 'ams01': 1 }, json.loads(result.output)) def test_volume_order_performance_iops_not_given(self): result = self.run_command(['block', 'volume-order', '--storage-type=performance', '--size=20', '--os-type=linux', '--location=dal05']) self.assertEqual(2, result.exit_code) def test_volume_order_performance_iops_not_multiple_of_100(self): result = self.run_command(['block', 'volume-order', '--storage-type=performance', '--size=20', '--iops=122', '--os-type=linux', '--location=dal05']) self.assertEqual(2, result.exit_code) def test_volume_order_performance_snapshot_error(self): result = self.run_command(['block', 'volume-order', '--storage-type=performance', '--size=20', '--iops=100', '--os-type=linux', '--location=dal05', '--snapshot-size=10', '--service-offering=performance']) self.assertEqual(2, result.exit_code) @mock.patch('SoftLayer.BlockStorageManager.order_block_volume') def test_volume_order_performance(self, order_mock): order_mock.return_value = { 'placedOrder': { 'id': 478, 'items': [ {'description': 'Performance Storage'}, {'description': 'Block Storage'}, {'description': '0.25 IOPS per GB'}, {'description': '20 GB Storage Space'}, {'description': '10 GB Storage Space (Snapshot Space)'}] } } result = self.run_command(['block', 'volume-order', '--storage-type=performance', '--size=20', '--iops=100', '--os-type=linux', '--location=dal05', '--snapshot-size=10']) self.assert_no_fail(result) self.assertEqual(result.output, 'Order #478 placed successfully!\n' ' > Performance Storage\n > Block Storage\n' ' > 0.25 IOPS per GB\n > 20 GB Storage Space\n' ' > 10 GB Storage Space (Snapshot Space)\n') def test_volume_order_endurance_tier_not_given(self): result = self.run_command(['block', 'volume-order', '--storage-type=endurance', '--size=20', '--os-type=linux', '--location=dal05']) self.assertEqual(2, result.exit_code) @mock.patch('SoftLayer.BlockStorageManager.order_block_volume') def test_volume_order_endurance(self, order_mock): order_mock.return_value = { 'placedOrder': { 'id': 478, 'items': [ {'description': 'Endurance Storage'}, {'description': 'Block Storage'}, {'description': '0.25 IOPS per GB'}, {'description': '20 GB Storage Space'}, {'description': '10 GB Storage Space (Snapshot Space)'}] } } result = self.run_command(['block', 'volume-order', '--storage-type=endurance', '--size=20', '--tier=0.25', '--os-type=linux', '--location=dal05', '--snapshot-size=10']) self.assert_no_fail(result) self.assertEqual(result.output, 'Order #478 placed successfully!\n' ' > Endurance Storage\n > Block Storage\n' ' > 0.25 IOPS per GB\n > 20 GB Storage Space\n' ' > 10 GB Storage Space (Snapshot Space)\n') @mock.patch('SoftLayer.BlockStorageManager.order_block_volume') def test_volume_order_order_not_placed(self, order_mock): order_mock.return_value = {} result = self.run_command(['block', 'volume-order', '--storage-type=endurance', '--size=20', '--tier=0.25', '--os-type=linux', '--location=dal05']) self.assert_no_fail(result) self.assertEqual(result.output, 'Order could not be placed! Please verify ' 'your options and try again.\n') def test_volume_order_hourly_billing_not_available(self): result = self.run_command(['block', 'volume-order', '--storage-type=endurance', '--size=20', '--tier=0.25', '--os-type=linux', '--location=dal10', '--billing=hourly', '--service-offering=enterprise']) self.assertEqual(2, result.exit_code) @mock.patch('SoftLayer.BlockStorageManager.order_block_volume') def test_volume_order_hourly_billing(self, order_mock): order_mock.return_value = { 'placedOrder': { 'id': 10983647, 'items': [ {'description': 'Storage as a Service'}, {'description': 'Block Storage'}, {'description': '20 GB Storage Space'}, {'description': '200 IOPS'}] } } result = self.run_command(['block', 'volume-order', '--storage-type=endurance', '--size=20', '--tier=0.25', '--os-type=linux', '--location=dal10', '--billing=hourly', '--service-offering=storage_as_a_service']) self.assert_no_fail(result) self.assertEqual(result.output, 'Order #10983647 placed successfully!\n' ' > Storage as a Service\n' ' > Block Storage\n' ' > 20 GB Storage Space\n' ' > 200 IOPS\n') @mock.patch('SoftLayer.BlockStorageManager.order_block_volume') def test_volume_order_performance_manager_error(self, order_mock): order_mock.side_effect = ValueError('failure!') result = self.run_command(['block', 'volume-order', '--storage-type=performance', '--size=20', '--iops=100', '--os-type=linux', '--location=dal05']) self.assertEqual(2, result.exit_code) self.assertEqual('Argument Error: failure!', result.exception.message) @mock.patch('SoftLayer.BlockStorageManager.order_block_volume') def test_volume_order_endurance_manager_error(self, order_mock): order_mock.side_effect = ValueError('failure!') result = self.run_command(['block', 'volume-order', '--storage-type=endurance', '--size=20', '--tier=0.25', '--os-type=linux', '--location=dal05']) self.assertEqual(2, result.exit_code) self.assertEqual('Argument Error: failure!', result.exception.message) def test_enable_snapshots(self): result = self.run_command(['block', 'snapshot-enable', '12345678', '--schedule-type=HOURLY', '--minute=10', '--retention-count=5']) self.assert_no_fail(result) def test_disable_snapshots(self): result = self.run_command(['block', 'snapshot-disable', '12345678', '--schedule-type=HOURLY']) self.assert_no_fail(result) def test_list_volume_schedules(self): result = self.run_command([ 'block', 'snapshot-schedule-list', '12345678']) self.assert_no_fail(result) self.assertEqual([ { "week": None, "maximum_snapshots": None, "hour": None, "day_of_week": None, "day": None, "replication": None, "date_of_month": None, "month_of_year": None, "active": "", "date_created": "", "type": "WEEKLY", "id": 978, "minute": '30' }, { "week": None, "maximum_snapshots": None, "hour": None, "day_of_week": None, "day": None, "replication": '*', "date_of_month": None, "month_of_year": None, "active": "", "date_created": "", "type": "INTERVAL", "id": 988, "minute": '*' } ], json.loads(result.output)) def test_create_snapshot(self): result = self.run_command(['block', 'snapshot-create', '12345678']) self.assert_no_fail(result) @mock.patch('SoftLayer.BlockStorageManager.create_snapshot') def test_create_snapshot_unsuccessful(self, snapshot_mock): snapshot_mock.return_value = [] result = self.run_command(['block', 'snapshot-create', '8', '-n=note']) self.assertEqual('Error occurred while creating snapshot.\n' 'Ensure volume is not failed over or in another ' 'state which prevents taking snapshots.\n', result.output) def test_snapshot_list(self): result = self.run_command(['block', 'snapshot-list', '12345678']) self.assert_no_fail(result) self.assertEqual([ { 'id': 470, 'name': 'unit_testing_note', 'created': '2016-07-06T07:41:19-05:00', 'size_bytes': '42', }], json.loads(result.output)) def test_snapshot_cancel(self): result = self.run_command(['--really', 'block', 'snapshot-cancel', '1234']) self.assert_no_fail(result) self.assertEqual('Block volume with id 1234 has been marked' ' for snapshot cancellation\n', result.output) self.assert_called_with('SoftLayer_Billing_Item', 'cancelItem', args=(False, True, None)) def test_snapshot_restore(self): result = self.run_command(['block', 'snapshot-restore', '12345678', '--snapshot-id=87654321']) self.assert_no_fail(result) self.assertEqual(result.output, 'Block volume 12345678 is being' ' restored using snapshot 87654321\n') @mock.patch('SoftLayer.BlockStorageManager.order_snapshot_space') def test_snapshot_order_order_not_placed(self, order_mock): order_mock.return_value = {} result = self.run_command(['block', 'snapshot-order', '1234', '--capacity=10', '--tier=0.25']) self.assert_no_fail(result) self.assertEqual(result.output, 'Order could not be placed! Please verify ' 'your options and try again.\n') @mock.patch('SoftLayer.BlockStorageManager.order_snapshot_space') def test_snapshot_order_performance_manager_error(self, order_mock): order_mock.side_effect = ValueError('failure!') result = self.run_command(['block', 'snapshot-order', '1234', '--capacity=10', '--tier=0.25']) self.assertEqual(2, result.exit_code) self.assertEqual('Argument Error: failure!', result.exception.message) @mock.patch('SoftLayer.BlockStorageManager.order_snapshot_space') def test_snapshot_order(self, order_mock): order_mock.return_value = { 'placedOrder': { 'id': 8702, 'items': [{'description': '10 GB Storage Space (Snapshot Space)'}], 'status': 'PENDING_APPROVAL', } } result = self.run_command(['block', 'snapshot-order', '1234', '--capacity=10', '--tier=0.25']) self.assert_no_fail(result) self.assertEqual(result.output, 'Order #8702 placed successfully!\n' ' > 10 GB Storage Space (Snapshot Space)\n' ' > Order status: PENDING_APPROVAL\n') def test_authorize_host_to_volume(self): result = self.run_command(['block', 'access-authorize', '12345678', '--hardware-id=100', '--virtual-id=10', '--ip-address-id=192', '--ip-address=192.3.2.1']) self.assert_no_fail(result) def test_deauthorize_host_to_volume(self): result = self.run_command(['block', 'access-revoke', '12345678', '--hardware-id=100', '--virtual-id=10', '--ip-address-id=192', '--ip-address=192.3.2.1']) self.assert_no_fail(result) def test_replicant_failover(self): result = self.run_command(['block', 'replica-failover', '12345678', '--replicant-id=5678', '--immediate']) self.assert_no_fail(result) self.assertEqual('Failover to replicant is now in progress.\n', result.output) def test_replication_locations(self): result = self.run_command(['block', 'replica-locations', '1234']) self.assert_no_fail(result) self.assertEqual( { '12345': 'Dallas 05', }, json.loads(result.output)) @mock.patch('SoftLayer.BlockStorageManager.get_replication_locations') def test_replication_locations_unsuccessful(self, locations_mock): locations_mock.return_value = False result = self.run_command(['block', 'replica-locations', '1234']) self.assert_no_fail(result) self.assertEqual('No data centers compatible for replication.\n', result.output) def test_replication_partners(self): result = self.run_command(['block', 'replica-partners', '1234']) self.assert_no_fail(result) self.assertEqual([ { 'ID': 1784, 'Account ID': 3000, 'Capacity (GB)': 20, 'Host ID': None, 'Guest ID': None, 'Hardware ID': None, 'Username': 'TEST_REP_1', }, { 'ID': 1785, 'Account ID': 3001, 'Host ID': None, 'Guest ID': None, 'Hardware ID': None, 'Capacity (GB)': 20, 'Username': 'TEST_REP_2', }], json.loads(result.output)) @mock.patch('SoftLayer.BlockStorageManager.get_replication_partners') def test_replication_partners_unsuccessful(self, partners_mock): partners_mock.return_value = False result = self.run_command(['block', 'replica-partners', '1234']) self.assertEqual( 'There are no replication partners for the given volume.\n', result.output) @mock.patch('SoftLayer.BlockStorageManager.failover_to_replicant') def test_replicant_failover_unsuccessful(self, failover_mock): failover_mock.return_value = False result = self.run_command(['block', 'replica-failover', '12345678', '--replicant-id=5678']) self.assertEqual('Failover operation could not be initiated.\n', result.output) def test_replicant_failback(self): result = self.run_command(['block', 'replica-failback', '12345678', '--replicant-id=5678']) self.assert_no_fail(result) self.assertEqual('Failback from replicant is now in progress.\n', result.output) @mock.patch('SoftLayer.BlockStorageManager.failback_from_replicant') def test_replicant_failback_unsuccessful(self, failback_mock): failback_mock.return_value = False result = self.run_command(['block', 'replica-failback', '12345678', '--replicant-id=5678']) self.assertEqual('Failback operation could not be initiated.\n', result.output) @mock.patch('SoftLayer.BlockStorageManager.order_replicant_volume') def test_replicant_order_order_not_placed(self, order_mock): order_mock.return_value = {} result = self.run_command(['block', 'replica-order', '100', '--snapshot-schedule=DAILY', '--location=dal05']) self.assert_no_fail(result) self.assertEqual(result.output, 'Order could not be placed! Please verify ' 'your options and try again.\n') @mock.patch('SoftLayer.BlockStorageManager.order_replicant_volume') def test_replicant_order(self, order_mock): order_mock.return_value = { 'placedOrder': { 'id': 91604, 'items': [ {'description': 'Endurance Storage'}, {'description': '2 IOPS per GB'}, {'description': 'Block Storage'}, {'description': '20 GB Storage Space'}, {'description': '10 GB Storage Space (Snapshot Space)'}, {'description': '20 GB Storage Space Replicant of: TEST'}, ], } } result = self.run_command(['block', 'replica-order', '100', '--snapshot-schedule=DAILY', '--location=dal05', '--tier=2']) self.assert_no_fail(result) self.assertEqual(result.output, 'Order #91604 placed successfully!\n' ' > Endurance Storage\n' ' > 2 IOPS per GB\n' ' > Block Storage\n' ' > 20 GB Storage Space\n' ' > 10 GB Storage Space (Snapshot Space)\n' ' > 20 GB Storage Space Replicant of: TEST\n') @mock.patch('SoftLayer.BlockStorageManager.order_duplicate_volume') def test_duplicate_order_exception_caught(self, order_mock): order_mock.side_effect = ValueError('order attempt failed, oh noooo!') result = self.run_command(['block', 'volume-duplicate', '102']) self.assertEqual(2, result.exit_code) self.assertEqual('Argument Error: order attempt failed, oh noooo!', result.exception.message) @mock.patch('SoftLayer.BlockStorageManager.order_duplicate_volume') def test_duplicate_order_order_not_placed(self, order_mock): order_mock.return_value = {} result = self.run_command(['block', 'volume-duplicate', '102', '--duplicate-iops=1400']) self.assert_no_fail(result) self.assertEqual(result.output, 'Order could not be placed! Please verify ' 'your options and try again.\n') @mock.patch('SoftLayer.BlockStorageManager.order_duplicate_volume') def test_duplicate_order(self, order_mock): order_mock.return_value = { 'placedOrder': { 'id': 24601, 'items': [{'description': 'Storage as a Service'}] } } result = self.run_command(['block', 'volume-duplicate', '102', '--origin-snapshot-id=470', '--duplicate-size=250', '--duplicate-tier=2', '--duplicate-snapshot-size=20']) self.assert_no_fail(result) self.assertEqual(result.output, 'Order #24601 placed successfully!\n' ' > Storage as a Service\n') @mock.patch('SoftLayer.BlockStorageManager.order_duplicate_volume') def test_duplicate_order_hourly_billing(self, order_mock): order_mock.return_value = { 'placedOrder': { 'id': 24602, 'items': [{'description': 'Storage as a Service'}] } } result = self.run_command(['block', 'volume-duplicate', '100', '--origin-snapshot-id=470', '--duplicate-size=250', '--duplicate-tier=2', '--billing=hourly', '--duplicate-snapshot-size=20']) order_mock.assert_called_with('100', origin_snapshot_id=470, duplicate_size=250, duplicate_iops=None, duplicate_tier_level=2, duplicate_snapshot_size=20, hourly_billing_flag=True) self.assert_no_fail(result) self.assertEqual(result.output, 'Order #24602 placed successfully!\n' ' > Storage as a Service\n') @mock.patch('SoftLayer.BlockStorageManager.order_modified_volume') def test_modify_order_exception_caught(self, order_mock): order_mock.side_effect = ValueError('order attempt failed, noooo!') result = self.run_command(['block', 'volume-modify', '102', '--new-size=1000']) self.assertEqual(2, result.exit_code) self.assertEqual('Argument Error: order attempt failed, noooo!', result.exception.message) @mock.patch('SoftLayer.BlockStorageManager.order_modified_volume') def test_modify_order_order_not_placed(self, order_mock): order_mock.return_value = {} result = self.run_command(['block', 'volume-modify', '102', '--new-iops=1400']) self.assert_no_fail(result) self.assertEqual('Order could not be placed! Please verify your options and try again.\n', result.output) @mock.patch('SoftLayer.BlockStorageManager.order_modified_volume') def test_modify_order(self, order_mock): order_mock.return_value = {'placedOrder': {'id': 24602, 'items': [{'description': 'Storage as a Service'}, {'description': '1000 GBs'}, {'description': '4 IOPS per GB'}]}} result = self.run_command(['block', 'volume-modify', '102', '--new-size=1000', '--new-tier=4']) order_mock.assert_called_with('102', new_size=1000, new_iops=None, new_tier_level=4) self.assert_no_fail(result) self.assertEqual('Order #24602 placed successfully!\n > Storage as a Service\n > 1000 GBs\n > 4 IOPS per GB\n', result.output) def test_set_password(self): result = self.run_command(['block', 'access-password', '1234', '--password=AAAAA']) self.assert_no_fail(result) softlayer-python-5.4.2/tests/CLI/modules/call_api_tests.py000066400000000000000000000201331324365065500236420ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.call_api_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import json from SoftLayer.CLI import call_api from SoftLayer.CLI import exceptions from SoftLayer import testing import pytest def test_filter_empty(): assert call_api._build_filters([]) == {} def test_filter_basic(): result = call_api._build_filters(['property=value']) assert result == {'property': {'operation': '_= value'}} def test_filter_nested(): result = call_api._build_filters(['nested.property=value']) assert result == {'nested': {'property': {'operation': '_= value'}}} def test_filter_multi(): result = call_api._build_filters(['prop1=value1', 'prop2=prop2']) assert result == { 'prop1': {'operation': '_= value1'}, 'prop2': {'operation': '_= prop2'}, } def test_filter_in(): result = call_api._build_filters(['prop IN value1,value2']) assert result == { 'prop': { 'operation': 'in', 'options': [{'name': 'data', 'value': ['value1', 'value2']}], } } def test_filter_in_multi(): result = call_api._build_filters([ 'prop_a IN a_val1,a_val2', 'prop_b IN b_val1,b_val2', ]) assert result == { 'prop_a': { 'operation': 'in', 'options': [{'name': 'data', 'value': ['a_val1', 'a_val2']}], }, 'prop_b': { 'operation': 'in', 'options': [{'name': 'data', 'value': ['b_val1', 'b_val2']}], }, } def test_filter_in_with_whitespace(): result = call_api._build_filters(['prop IN value1 , value2 ']) assert result == { 'prop': { 'operation': 'in', 'options': [{'name': 'data', 'value': ['value1', 'value2']}], } } def test_filter_invalid_operation(): with pytest.raises(exceptions.CLIAbort): call_api._build_filters(['prop N/A value1']) def test_filter_only_whitespace(): with pytest.raises(exceptions.CLIAbort): call_api._build_filters([' ']) class CallCliTests(testing.TestCase): def test_python_output(self): result = self.run_command(['call-api', 'Service', 'method', '--mask=some.mask', '--limit=20', '--offset=40', '--id=100', '-f nested.property=5432', '--output-python']) self.assert_no_fail(result) # NOTE(kmcdonald): Python 3 no longer inserts 'u' before unicode # string literals but python 2 does. These are stripped out to make # this test pass on both python versions. stripped_output = result.output.replace("u'", "'") self.assertIsNotNone(stripped_output, """import SoftLayer client = SoftLayer.create_client_from_env() result = client.call(u'Service', u'method', filter={u'nested': {u'property': {'operation': 5432}}}, id=u'100', limit=20, mask=u'some.mask', offset=40) """) self.assertEqual(self.calls(), [], "no API calls were made") def test_options(self): mock = self.set_mock('SoftLayer_Service', 'method') mock.return_value = 'test' result = self.run_command(['call-api', 'Service', 'method', '--mask=some.mask', '--limit=20', '--offset=40', '--id=100', '-f property=1234', '-f nested.property=5432']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), 'test') self.assert_called_with('SoftLayer_Service', 'method', mask='mask[some.mask]', limit=20, offset=40, identifier='100', filter={ 'property': {'operation': 1234}, 'nested': {'property': {'operation': 5432}} }) def test_object(self): mock = self.set_mock('SoftLayer_Service', 'method') mock.return_value = {'string': 'string', 'int': 10, 'float': 1.0, 'None': None, 'Bool': True} result = self.run_command(['call-api', 'Service', 'method']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), {'string': 'string', 'int': 10, 'float': 1.0, 'None': None, 'Bool': True}) def test_object_table(self): mock = self.set_mock('SoftLayer_Service', 'method') mock.return_value = {'string': 'string', 'int': 10, 'float': 1.0, 'None': None, 'Bool': True} result = self.run_command(['call-api', 'Service', 'method'], fmt='table') self.assert_no_fail(result) # NOTE(kmcdonald): Order is not guaranteed self.assertIn(":........:........:", result.output) self.assertIn(": name : value :", result.output) self.assertIn(": int : 10 :", result.output) self.assertIn(": None : None :", result.output) self.assertIn(": float : 1.0 :", result.output) self.assertIn(": Bool : True :", result.output) self.assertIn(": string : string :", result.output) self.assertIn(":........:........:", result.output) def test_object_nested(self): mock = self.set_mock('SoftLayer_Service', 'method') mock.return_value = {'this': {'is': [{'pretty': 'nested'}]}} result = self.run_command(['call-api', 'Service', 'method']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), {'this': {'is': [{'pretty': 'nested'}]}}) def test_list(self): mock = self.set_mock('SoftLayer_Service', 'method') mock.return_value = [{'string': 'string', 'int': 10, 'float': 1.0, 'None': None, 'Bool': True}] result = self.run_command(['call-api', 'Service', 'method']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), [{'string': 'string', 'int': 10, 'float': 1.0, 'None': None, 'Bool': True}]) def test_list_table(self): mock = self.set_mock('SoftLayer_Service', 'method') mock.return_value = [{'string': 'string', 'int': 10, 'float': 1.0, 'None': None, 'Bool': True}] result = self.run_command(['call-api', 'Service', 'method'], fmt='table') self.assert_no_fail(result) self.assertEqual(result.output, """:......:......:.......:.....:........: : Bool : None : float : int : string : :......:......:.......:.....:........: : True : None : 1.0 : 10 : string : :......:......:.......:.....:........: """) def test_parameters(self): mock = self.set_mock('SoftLayer_Service', 'method') mock.return_value = {} result = self.run_command(['call-api', 'Service', 'method', 'arg1', '1234']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Service', 'method', args=('arg1', '1234')) softlayer-python-5.4.2/tests/CLI/modules/cdn_tests.py000066400000000000000000000054311324365065500226460ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.cdn_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ from SoftLayer import testing import json class CdnTests(testing.TestCase): def test_list_accounts(self): result = self.run_command(['cdn', 'list']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), [{'notes': None, 'created': '2012-06-25T14:05:28-07:00', 'type': 'ORIGIN_PULL', 'id': 1234, 'account_name': '1234a'}, {'notes': None, 'created': '2012-07-24T13:34:25-07:00', 'type': 'POP_PULL', 'id': 1234, 'account_name': '1234a'}]) def test_detail_account(self): result = self.run_command(['cdn', 'detail', '1245']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), {'notes': None, 'created': '2012-06-25T14:05:28-07:00', 'type': 'ORIGIN_PULL', 'status': 'ACTIVE', 'id': 1234, 'account_name': '1234a'}) def test_load_content(self): result = self.run_command(['cdn', 'load', '1234', 'http://example.com']) self.assert_no_fail(result) self.assertEqual(result.output, "") def test_purge_content(self): result = self.run_command(['cdn', 'purge', '1234', 'http://example.com']) self.assert_no_fail(result) self.assertEqual(result.output, "") def test_list_origins(self): result = self.run_command(['cdn', 'origin-list', '1234']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), [ {'media_type': 'FLASH', 'origin_url': 'http://ams01.objectstorage.softlayer.net:80', 'cname': None, 'id': '12345'}, {'media_type': 'FLASH', 'origin_url': 'http://sng01.objectstorage.softlayer.net:80', 'cname': None, 'id': '12345'}]) def test_add_origin(self): result = self.run_command(['cdn', 'origin-add', '1234', 'http://example.com']) self.assert_no_fail(result) self.assertEqual(result.output, "") def test_remove_origin(self): result = self.run_command(['cdn', 'origin-remove', '1234', 'http://example.com']) self.assert_no_fail(result) self.assertEqual(result.output, "") softlayer-python-5.4.2/tests/CLI/modules/config_tests.py000066400000000000000000000115761324365065500233560ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.config_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import json import sys import tempfile import mock import SoftLayer from SoftLayer import auth from SoftLayer.CLI.config import setup as config from SoftLayer.CLI import exceptions from SoftLayer import consts from SoftLayer import testing from SoftLayer import transports class TestHelpShow(testing.TestCase): def set_up(self): transport = transports.XmlRpcTransport( endpoint_url='http://endpoint-url', ) self.env.client = SoftLayer.BaseClient( transport=transport, auth=auth.BasicAuthentication('username', 'api-key')) def test_show(self): result = self.run_command(['config', 'show']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), {'Username': 'username', 'API Key': 'api-key', 'Endpoint URL': 'http://endpoint-url', 'Timeout': 'not set'}) class TestHelpSetup(testing.TestCase): def set_up(self): super(TestHelpSetup, self).set_up() # NOTE(kmcdonald): since the endpoint_url is changed with the client # in these commands, we need to ensure that a fixtured transport is # used. transport = testing.MockableTransport(SoftLayer.FixtureTransport()) self.env.client = SoftLayer.BaseClient(transport=transport) @mock.patch('SoftLayer.CLI.formatting.confirm') @mock.patch('SoftLayer.CLI.environment.Environment.getpass') @mock.patch('SoftLayer.CLI.environment.Environment.input') def test_setup(self, mocked_input, getpass, confirm_mock): if(sys.platform.startswith("win")): self.skipTest("Test doesn't work in Windows") with tempfile.NamedTemporaryFile() as config_file: confirm_mock.return_value = True getpass.return_value = 'A' * 64 mocked_input.side_effect = ['user', 'public', 0] result = self.run_command(['--config=%s' % config_file.name, 'config', 'setup']) self.assert_no_fail(result) self.assertTrue('Configuration Updated Successfully' in result.output) contents = config_file.read().decode("utf-8") self.assertTrue('[softlayer]' in contents) self.assertTrue('username = user' in contents) self.assertTrue('api_key = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAA' in contents) self.assertTrue('endpoint_url = %s' % consts.API_PUBLIC_ENDPOINT in contents) @mock.patch('SoftLayer.CLI.formatting.confirm') @mock.patch('SoftLayer.CLI.environment.Environment.getpass') @mock.patch('SoftLayer.CLI.environment.Environment.input') def test_setup_cancel(self, mocked_input, getpass, confirm_mock): with tempfile.NamedTemporaryFile() as config_file: confirm_mock.return_value = False getpass.return_value = 'A' * 64 mocked_input.side_effect = ['user', 'public', 0] result = self.run_command(['--config=%s' % config_file.name, 'config', 'setup']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.CLIAbort) @mock.patch('SoftLayer.CLI.environment.Environment.getpass') @mock.patch('SoftLayer.CLI.environment.Environment.input') def test_get_user_input_private(self, mocked_input, getpass): getpass.return_value = 'A' * 64 mocked_input.side_effect = ['user', 'private', 0] username, secret, endpoint_url, timeout = ( config.get_user_input(self.env)) self.assertEqual(username, 'user') self.assertEqual(secret, 'A' * 64) self.assertEqual(endpoint_url, consts.API_PRIVATE_ENDPOINT) self.assertEqual(timeout, 0) @mock.patch('SoftLayer.CLI.environment.Environment.getpass') @mock.patch('SoftLayer.CLI.environment.Environment.input') def test_get_user_input_custom(self, mocked_input, getpass): getpass.return_value = 'A' * 64 mocked_input.side_effect = ['user', 'custom', 'custom-endpoint', 0] _, _, endpoint_url, _ = config.get_user_input(self.env) self.assertEqual(endpoint_url, 'custom-endpoint') @mock.patch('SoftLayer.CLI.environment.Environment.getpass') @mock.patch('SoftLayer.CLI.environment.Environment.input') def test_get_user_input_default(self, mocked_input, getpass): self.env.getpass.return_value = 'A' * 64 mocked_input.side_effect = ['user', 'public', 0] _, _, endpoint_url, _ = config.get_user_input(self.env) self.assertEqual(endpoint_url, consts.API_PUBLIC_ENDPOINT) softlayer-python-5.4.2/tests/CLI/modules/dedicatedhost_tests.py000066400000000000000000000331351324365065500247100ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.dedicatedhosts_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import json import mock import os import SoftLayer from SoftLayer.CLI import exceptions from SoftLayer.fixtures import SoftLayer_Product_Package from SoftLayer.fixtures import SoftLayer_Virtual_DedicatedHost from SoftLayer import testing class DedicatedHostsTests(testing.TestCase): def set_up(self): self.dedicated_host = SoftLayer.DedicatedHostManager(self.client) def test_list_dedicated_hosts(self): result = self.run_command(['dedicatedhost', 'list']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), [{ 'cpuCount': 56, 'datacenter': 'dal05', 'diskCapacity': 1200, 'guestCount': 1, 'id': 44701, 'memoryCapacity': 242, 'name': 'khnguyendh' }] ) def tear_down(self): if os.path.exists("test.txt"): os.remove("test.txt") def test_details(self): mock = self.set_mock('SoftLayer_Virtual_DedicatedHost', 'getObject') mock.return_value = SoftLayer_Virtual_DedicatedHost.getObjectById result = self.run_command(['dedicatedhost', 'detail', '44701', '--price', '--guests']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), { 'cpu count': 56, 'create date': '2017-11-02T11:40:56-07:00', 'datacenter': 'dal05', 'disk capacity': 1200, 'guest count': 1, 'guests': [{ 'domain': 'Softlayer.com', 'hostname': 'khnguyenDHI', 'id': 43546081, 'uuid': '806a56ec-0383-4c2e-e6a9-7dc89c4b29a2' }], 'id': 44701, 'memory capacity': 242, 'modify date': '2017-11-06T11:38:20-06:00', 'name': 'khnguyendh', 'owner': '232298_khuong', 'price_rate': 1515.556, 'router hostname': 'bcr01a.dal05', 'router id': 51218} ) def test_details_no_owner(self): mock = self.set_mock('SoftLayer_Virtual_DedicatedHost', 'getObject') retVal = SoftLayer_Virtual_DedicatedHost.getObjectById retVal['billingItem'] = {} mock.return_value = retVal result = self.run_command( ['dedicatedhost', 'detail', '44701', '--price', '--guests']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), {'cpu count': 56, 'create date': '2017-11-02T11:40:56-07:00', 'datacenter': 'dal05', 'disk capacity': 1200, 'guest count': 1, 'guests': [{ 'domain': 'Softlayer.com', 'hostname': 'khnguyenDHI', 'id': 43546081, 'uuid': '806a56ec-0383-4c2e-e6a9-7dc89c4b29a2'}], 'id': 44701, 'memory capacity': 242, 'modify date': '2017-11-06T11:38:20-06:00', 'name': 'khnguyendh', 'owner': None, 'price_rate': 0, 'router hostname': 'bcr01a.dal05', 'router id': 51218} ) def test_create_options(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = SoftLayer_Product_Package.getAllObjectsDH result = self.run_command(['dh', 'create-options']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), [[ { 'datacenter': 'Dallas 5', 'value': 'dal05' }], [{ 'Dedicated Virtual Host Flavor(s)': '56 Cores X 242 RAM X 1.2 TB', 'value': '56_CORES_X_242_RAM_X_1_4_TB' } ]] ) def test_create_options_with_only_datacenter(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = SoftLayer_Product_Package.getAllObjectsDH result = self.run_command(['dh', 'create-options', '-d=dal05']) self.assertIsInstance(result.exception, exceptions.ArgumentError) def test_create_options_get_routers(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = SoftLayer_Product_Package.getAllObjectsDH result = self.run_command(['dh', 'create-options', '--datacenter=dal05', '--flavor=56_CORES_X_242_RAM_X_1_4_TB']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), [[ { "Available Backend Routers": "bcr01a.dal05" }, { "Available Backend Routers": "bcr02a.dal05" }, { "Available Backend Routers": "bcr03a.dal05" }, { "Available Backend Routers": "bcr04a.dal05" } ]] ) def test_create(self): SoftLayer.CLI.formatting.confirm = mock.Mock() SoftLayer.CLI.formatting.confirm.return_value = True mock_package_obj = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock_package_obj.return_value = SoftLayer_Product_Package.getAllObjectsDH result = self.run_command(['dedicatedhost', 'create', '--hostname=host', '--domain=example.com', '--datacenter=dal05', '--flavor=56_CORES_X_242_RAM_X_1_4_TB', '--billing=hourly']) self.assert_no_fail(result) args = ({ 'hardware': [{ 'domain': 'example.com', 'primaryBackendNetworkComponent': { 'router': { 'id': 51218 } }, 'hostname': 'host' }], 'prices': [{ 'id': 200269 }], 'location': 'DALLAS05', 'packageId': 813, 'complexType': 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost', 'useHourlyPricing': True, 'quantity': 1}, ) self.assert_called_with('SoftLayer_Product_Order', 'placeOrder', args=args) def test_create_verify(self): SoftLayer.CLI.formatting.confirm = mock.Mock() SoftLayer.CLI.formatting.confirm.return_value = True mock_package_obj = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock_package_obj.return_value = SoftLayer_Product_Package.getAllObjectsDH mock_package = self.set_mock('SoftLayer_Product_Order', 'verifyOrder') mock_package.return_value = SoftLayer_Product_Package.verifyOrderDH result = self.run_command(['dedicatedhost', 'create', '--verify', '--hostname=host', '--domain=example.com', '--datacenter=dal05', '--flavor=56_CORES_X_242_RAM_X_1_4_TB', '--billing=hourly']) self.assert_no_fail(result) args = ({ 'useHourlyPricing': True, 'hardware': [{ 'hostname': 'host', 'domain': 'example.com', 'primaryBackendNetworkComponent': { 'router': { 'id': 51218 } } }], 'packageId': 813, 'prices': [{'id': 200269}], 'location': 'DALLAS05', 'complexType': 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost', 'quantity': 1},) self.assert_called_with('SoftLayer_Product_Order', 'verifyOrder', args=args) result = self.run_command(['dh', 'create', '--verify', '--hostname=host', '--domain=example.com', '--datacenter=dal05', '--flavor=56_CORES_X_242_RAM_X_1_4_TB', '--billing=monthly']) self.assert_no_fail(result) args = ({ 'useHourlyPricing': True, 'hardware': [{ 'hostname': 'host', 'domain': 'example.com', 'primaryBackendNetworkComponent': { 'router': { 'id': 51218 } } }], 'packageId': 813, 'prices': [{'id': 200269}], 'location': 'DALLAS05', 'complexType': 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost', 'quantity': 1},) self.assert_called_with('SoftLayer_Product_Order', 'verifyOrder', args=args) def test_create_aborted(self): SoftLayer.CLI.formatting.confirm = mock.Mock() SoftLayer.CLI.formatting.confirm.return_value = False mock_package_obj = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock_package_obj.return_value = SoftLayer_Product_Package.getAllObjectsDH result = self.run_command(['dh', 'create', '--hostname=host', '--domain=example.com', '--datacenter=dal05', '--flavor=56_CORES_X_242_RAM_X_1_4_TB', '--billing=monthly']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.CLIAbort) def test_create_export(self): mock_package_obj = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock_package_obj.return_value = SoftLayer_Product_Package.getAllObjectsDH mock_package = self.set_mock('SoftLayer_Product_Order', 'verifyOrder') mock_package.return_value = SoftLayer_Product_Package.verifyOrderDH self.run_command(['dedicatedhost', 'create', '--verify', '--hostname=host', '--domain=example.com', '--datacenter=dal05', '--flavor=56_CORES_X_242_RAM_X_1_4_TB', '--billing=hourly', '--export=test.txt']) self.assertEqual(os.path.exists("test.txt"), True) def test_create_verify_no_price_or_more_than_one(self): mock_package_obj = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock_package_obj.return_value = SoftLayer_Product_Package.getAllObjectsDH mock_package = self.set_mock('SoftLayer_Product_Order', 'verifyOrder') ret_val = SoftLayer_Product_Package.verifyOrderDH ret_val['prices'] = [] mock_package.return_value = ret_val result = self.run_command(['dedicatedhost', 'create', '--verify', '--hostname=host', '--domain=example.com', '--datacenter=dal05', '--flavor=56_CORES_X_242_RAM_X_1_4_TB', '--billing=hourly']) self.assertIsInstance(result.exception, exceptions.ArgumentError) args = ({ 'hardware': [{ 'domain': 'example.com', 'primaryBackendNetworkComponent': { 'router': { 'id': 51218 } }, 'hostname': 'host' }], 'prices': [{ 'id': 200269 }], 'location': 'DALLAS05', 'packageId': 813, 'complexType': 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost', 'useHourlyPricing': True, 'quantity': 1},) self.assert_called_with('SoftLayer_Product_Order', 'verifyOrder', args=args) softlayer-python-5.4.2/tests/CLI/modules/dns_tests.py000066400000000000000000000215741324365065500226740ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.dns_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import json import os.path import mock from SoftLayer.CLI.dns import zone_import from SoftLayer.CLI import exceptions from SoftLayer import testing class DnsTests(testing.TestCase): def test_zone_print(self): result = self.run_command(['dns', 'zone-print', '1234']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), "lots of text") def test_create_zone(self): result = self.run_command(['dns', 'zone-create', 'example.com']) self.assert_no_fail(result) self.assertEqual(result.output, "") @mock.patch('SoftLayer.CLI.formatting.no_going_back') def test_delete_zone(self, no_going_back_mock): no_going_back_mock.return_value = True result = self.run_command(['dns', 'zone-delete', '1234']) self.assert_no_fail(result) self.assertEqual(result.output, "") no_going_back_mock.return_value = False result = self.run_command(['--really', 'dns', 'zone-delete', '1234']) self.assert_no_fail(result) self.assertEqual(result.output, "") @mock.patch('SoftLayer.CLI.formatting.no_going_back') def test_delete_zone_abort(self, no_going_back_mock): no_going_back_mock.return_value = False result = self.run_command(['dns', 'zone-delete', '1234']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.CLIAbort) def test_list_zones(self): result = self.run_command(['dns', 'zone-list']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), [{'serial': 2014030728, 'updated': '2014-03-07T13:52:31-06:00', 'id': 12345, 'zone': 'example.com'}]) def test_list_records(self): result = self.run_command(['dns', 'record-list', '1234']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output)[0], {'record': 'a', 'type': 'CNAME', 'id': 1, 'data': 'd', 'ttl': 7200}) def test_add_record(self): result = self.run_command(['dns', 'record-add', '1234', 'hostname', 'A', 'd', '--ttl=100']) self.assert_no_fail(result) self.assertEqual(result.output, "") @mock.patch('SoftLayer.CLI.formatting.no_going_back') def test_delete_record(self, no_going_back_mock): no_going_back_mock.return_value = True result = self.run_command(['dns', 'record-remove', '1234']) self.assert_no_fail(result) self.assertEqual(result.output, "") @mock.patch('SoftLayer.CLI.formatting.no_going_back') def test_delete_record_abort(self, no_going_back_mock): no_going_back_mock.return_value = False result = self.run_command(['dns', 'record-remove', '1234']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.CLIAbort) def test_parse_zone_file(self): zone_file = """$ORIGIN realtest.com. $TTL 86400 @ IN SOA ns1.softlayer.com. support.softlayer.com. ( 2014052300 ; Serial 7200 ; Refresh 600 ; Retry 1728000 ; Expire 43200) ; Minimum @ 86400 IN NS ns1.softlayer.com. @ 86400 IN NS ns2.softlayer.com. IN MX 10 test.realtest.com. testing 86400 IN A 127.0.0.1 testing1 86400 IN A 12.12.0.1 server2 IN A 1.0.3.4 ftp IN CNAME server2 dev.realtest.com IN TXT "This is just a test of the txt record" IN AAAA 2001:db8:10::1 spf IN TXT "v=spf1 ip4:192.0.2.0/24 ip4:198.51.100.123 a" *.testing 86400 IN A 127.0.0.2 * 86400 IN A 127.0.0.3 """ expected = [{'data': 'ns1.softlayer.com.', 'record': '@', 'type': 'NS', 'ttl': '86400'}, {'data': 'ns2.softlayer.com.', 'record': '@', 'type': 'NS', 'ttl': '86400'}, {'data': '127.0.0.1', 'record': 'testing', 'type': 'A', 'ttl': '86400'}, {'data': '12.12.0.1', 'record': 'testing1', 'type': 'A', 'ttl': '86400'}, {'data': '1.0.3.4', 'record': 'server2', 'type': 'A', 'ttl': None}, {'data': 'server2', 'record': 'ftp', 'type': 'CNAME', 'ttl': None}, {'data': '"This is just a test of the txt record"', 'record': 'dev.realtest.com', 'type': 'TXT', 'ttl': None}, {'data': '"v=spf1 ip4:192.0.2.0/24 ip4:198.51.100.123 a"', 'record': 'spf', 'type': 'TXT', 'ttl': None}, {'data': '127.0.0.2', 'record': '*.testing', 'type': 'A', 'ttl': '86400'}, {'data': '127.0.0.3', 'record': '*', 'type': 'A', 'ttl': '86400'}] zone, records, bad_lines = zone_import.parse_zone_details(zone_file) self.assertEqual(zone, 'realtest.com') self.assertEqual(records, expected) self.assertEqual(len(bad_lines), 13) def test_import_zone_dry_run(self): path = os.path.join(testing.FIXTURE_PATH, 'realtest.com') result = self.run_command(['dns', 'import', path, '--dry-run']) self.assertIn("Parsed: zone=realtest.com", result.output) self.assertIn( "Parsed: type=NS, record=@, data=ns1.softlayer.com., ttl=86400", result.output) self.assertIn("Unparsed: $TTL 86400", result.output) def test_import_zone(self): path = os.path.join(testing.FIXTURE_PATH, 'realtest.com') result = self.run_command(['dns', 'import', path]) self.assertEqual(self.calls('SoftLayer_Dns_Domain', 'createObject'), []) calls = self.calls('SoftLayer_Dns_Domain_ResourceRecord', 'createObject') expected_calls = [{'data': 'ns1.softlayer.com.', 'host': '@', 'domainId': 12345, 'type': 'NS', 'ttl': '86400'}, {'data': 'ns2.softlayer.com.', 'host': '@', 'domainId': 12345, 'type': 'NS', 'ttl': '86400'}, {'data': '127.0.0.1', 'host': 'testing', 'domainId': 12345, 'type': 'A', 'ttl': '86400'}, {'data': '12.12.0.1', 'host': 'testing1', 'domainId': 12345, 'type': 'A', 'ttl': '86400'}, {'data': '1.0.3.4', 'host': 'server2', 'domainId': 12345, 'type': 'A', 'ttl': None}, {'data': 'server2', 'host': 'ftp', 'domainId': 12345, 'type': 'CNAME', 'ttl': None}, {'data': '"This is just a test of the txt record"', 'host': 'dev.realtest.com', 'domainId': 12345, 'type': 'TXT', 'ttl': None}, {'data': '"v=spf1 ip4:192.0.2.0/24 ' 'ip4:198.51.100.123 a -all"', 'host': 'spf', 'domainId': 12345, 'type': 'TXT', 'ttl': None}] self.assertEqual(len(calls), len(expected_calls)) for call, expected_call in zip(calls, expected_calls): self.assertEqual(call.args[0], expected_call) self.assertIn("Finished", result.output) softlayer-python-5.4.2/tests/CLI/modules/file_tests.py000066400000000000000000000654631324365065500230340ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.file_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ from SoftLayer import testing import json import mock class FileTests(testing.TestCase): def test_access_list(self): result = self.run_command(['file', 'access-list', '1234']) self.assert_no_fail(result) def test_authorize_host_to_volume(self): result = self.run_command(['file', 'access-authorize', '12345678', '--hardware-id=100', '--virtual-id=10', '--ip-address-id=192', '--ip-address=192.3.2.1', '--subnet-id=200']) self.assert_no_fail(result) def test_deauthorize_host_to_volume(self): result = self.run_command(['file', 'access-revoke', '12345678', '--hardware-id=100', '--virtual-id=10', '--ip-address-id=192', '--ip-address=192.3.2.1', '--subnet-id=200']) self.assert_no_fail(result) def test_volume_list(self): result = self.run_command(['file', 'volume-list']) self.assert_no_fail(result) self.assertEqual([ { 'bytes_used': None, 'capacity_gb': 10, 'datacenter': 'Dallas', 'id': 1, 'ip_addr': '127.0.0.1', 'storage_type': 'ENDURANCE', 'username': 'user', 'active_transactions': None, 'mount_addr': '127.0.0.1:/TEST', 'rep_partner_count': None }], json.loads(result.output)) @mock.patch('SoftLayer.FileStorageManager.list_file_volumes') def test_volume_count(self, list_mock): list_mock.return_value = [ {'serviceResource': {'datacenter': {'name': 'dal09'}}}, {'serviceResource': {'datacenter': {'name': 'ams01'}}}, {'serviceResource': {'datacenter': {'name': 'ams01'}}} ] result = self.run_command(['file', 'volume-count']) self.assert_no_fail(result) self.assertEqual( { 'ams01': 2, 'dal09': 1 }, json.loads(result.output)) def test_snapshot_list(self): result = self.run_command(['file', 'snapshot-list', '1234']) self.assert_no_fail(result) self.assertEqual([ { 'id': 470, 'name': 'unit_testing_note', 'created': '2016-07-06T07:41:19-05:00', 'size_bytes': '42', }], json.loads(result.output)) def test_volume_cancel(self): result = self.run_command([ '--really', 'file', 'volume-cancel', '1234']) self.assert_no_fail(result) self.assertEqual('File volume with id 1234 has been marked' ' for cancellation\n', result.output) self.assert_called_with('SoftLayer_Billing_Item', 'cancelItem', args=(False, True, None)) def test_volume_detail(self): result = self.run_command(['file', 'volume-detail', '1234']) self.assert_no_fail(result) self.assertEqual({ 'Username': 'username', 'Used Space': '0B', 'Endurance Tier': 'READHEAVY_TIER', 'IOPs': 1000, 'Mount Address': '127.0.0.1:/TEST', 'Snapshot Capacity (GB)': '10', 'Snapshot Used (Bytes)': 1024, 'Capacity (GB)': '20GB', 'Target IP': '10.1.2.3', 'Data Center': 'dal05', 'Type': 'ENDURANCE', 'ID': 100, '# of Active Transactions': '1', 'Ongoing Transaction': 'This is a buffer time in which the customer may cancel the server', 'Replicant Count': '1', 'Replication Status': 'Replicant Volume Provisioning ' 'has completed.', 'Replicant Volumes': [[ {'Replicant ID': 'Volume Name', '1784': 'TEST_REP_1'}, {'Replicant ID': 'Target IP', '1784': '10.3.174.79'}, {'Replicant ID': 'Data Center', '1784': 'wdc01'}, {'Replicant ID': 'Schedule', '1784': 'REPLICATION_HOURLY'}, ], [ {'Replicant ID': 'Volume Name', '1785': 'TEST_REP_2'}, {'Replicant ID': 'Target IP', '1785': '10.3.177.84'}, {'Replicant ID': 'Data Center', '1785': 'dal01'}, {'Replicant ID': 'Schedule', '1785': 'REPLICATION_DAILY'}, ]], 'Original Volume Properties': [ {'Property': 'Original Volume Size', 'Value': '20'}, {'Property': 'Original Volume Name', 'Value': 'test-original-volume-name'}, {'Property': 'Original Snapshot Name', 'Value': 'test-original-snapshot-name'} ] }, json.loads(result.output)) def test_volume_order_performance_iops_not_given(self): result = self.run_command(['file', 'volume-order', '--storage-type=performance', '--size=20', '--location=dal05']) self.assertEqual(2, result.exit_code) def test_volume_order_performance_iops_not_multiple_of_100(self): result = self.run_command(['file', 'volume-order', '--storage-type=performance', '--size=20', '--iops=122', '--location=dal05']) self.assertEqual(2, result.exit_code) def test_volume_order_performance_snapshot_error(self): result = self.run_command(['file', 'volume-order', '--storage-type=performance', '--size=20', '--iops=100', '--location=dal05', '--snapshot-size=10', '--service-offering=performance']) self.assertEqual(2, result.exit_code) @mock.patch('SoftLayer.FileStorageManager.order_file_volume') def test_volume_order_performance(self, order_mock): order_mock.return_value = { 'placedOrder': { 'id': 478, 'items': [ {'description': 'Performance Storage'}, {'description': 'File Storage'}, {'description': '0.25 IOPS per GB'}, {'description': '20 GB Storage Space'}, {'description': '10 GB Storage Space (Snapshot Space)'}] } } result = self.run_command(['file', 'volume-order', '--storage-type=performance', '--size=20', '--iops=100', '--location=dal05', '--snapshot-size=10']) self.assert_no_fail(result) self.assertEqual(result.output, 'Order #478 placed successfully!\n' ' > Performance Storage\n > File Storage\n' ' > 0.25 IOPS per GB\n > 20 GB Storage Space\n' ' > 10 GB Storage Space (Snapshot Space)\n') def test_volume_order_endurance_tier_not_given(self): result = self.run_command(['file', 'volume-order', '--storage-type=endurance', '--size=20', '--location=dal05']) self.assertEqual(2, result.exit_code) @mock.patch('SoftLayer.FileStorageManager.order_file_volume') def test_volume_order_endurance(self, order_mock): order_mock.return_value = { 'placedOrder': { 'id': 478, 'items': [ {'description': 'Endurance Storage'}, {'description': 'File Storage'}, {'description': '0.25 IOPS per GB'}, {'description': '20 GB Storage Space'}, {'description': '10 GB Storage Space (Snapshot Space)'}] } } result = self.run_command(['file', 'volume-order', '--storage-type=endurance', '--size=20', '--tier=0.25', '--location=dal05', '--snapshot-size=10']) self.assert_no_fail(result) self.assertEqual(result.output, 'Order #478 placed successfully!\n' ' > Endurance Storage\n > File Storage\n' ' > 0.25 IOPS per GB\n > 20 GB Storage Space\n' ' > 10 GB Storage Space (Snapshot Space)\n') @mock.patch('SoftLayer.FileStorageManager.order_file_volume') def test_volume_order_order_not_placed(self, order_mock): order_mock.return_value = {} result = self.run_command(['file', 'volume-order', '--storage-type=endurance', '--size=20', '--tier=0.25', '--location=dal05']) self.assert_no_fail(result) self.assertEqual(result.output, 'Order could not be placed! Please verify ' 'your options and try again.\n') def test_volume_order_hourly_billing_not_available(self): result = self.run_command(['file', 'volume-order', '--storage-type=endurance', '--size=20', '--tier=0.25', '--location=dal10', '--billing=hourly', '--service-offering=enterprise']) self.assertEqual(2, result.exit_code) @mock.patch('SoftLayer.FileStorageManager.order_file_volume') def test_volume_order_hourly_billing(self, order_mock): order_mock.return_value = { 'placedOrder': { 'id': 479, 'items': [ {'description': 'Storage as a Service'}, {'description': 'File Storage'}, {'description': '20 GB Storage Space'}, {'description': '0.25 IOPS per GB'}, {'description': '10 GB Storage Space (Snapshot Space)'}] } } result = self.run_command(['file', 'volume-order', '--storage-type=endurance', '--size=20', '--tier=0.25', '--location=dal05', '--service-offering=storage_as_a_service', '--billing=hourly', '--snapshot-size=10']) self.assert_no_fail(result) self.assertEqual(result.output, 'Order #479 placed successfully!\n' ' > Storage as a Service\n' ' > File Storage\n' ' > 20 GB Storage Space\n' ' > 0.25 IOPS per GB\n' ' > 10 GB Storage Space (Snapshot Space)\n') @mock.patch('SoftLayer.FileStorageManager.order_file_volume') def test_volume_order_performance_manager_error(self, order_mock): order_mock.side_effect = ValueError('failure!') result = self.run_command(['file', 'volume-order', '--storage-type=performance', '--size=20', '--iops=100', '--location=dal05']) self.assertEqual(2, result.exit_code) self.assertEqual('Argument Error: failure!', result.exception.message) @mock.patch('SoftLayer.FileStorageManager.order_file_volume') def test_volume_order_endurance_manager_error(self, order_mock): order_mock.side_effect = ValueError('failure!') result = self.run_command(['file', 'volume-order', '--storage-type=endurance', '--size=20', '--tier=0.25', '--location=dal05']) self.assertEqual(2, result.exit_code) self.assertEqual('Argument Error: failure!', result.exception.message) def test_enable_snapshots(self): result = self.run_command(['file', 'snapshot-enable', '12345678', '--schedule-type=HOURLY', '--minute=10', '--retention-count=5']) self.assert_no_fail(result) def test_disable_snapshots(self): result = self.run_command(['file', 'snapshot-disable', '12345678', '--schedule-type=HOURLY']) self.assert_no_fail(result) def test_list_volume_schedules(self): result = self.run_command([ 'file', 'snapshot-schedule-list', '12345678']) self.assert_no_fail(result) self.assertEqual([ { "week": None, "maximum_snapshots": None, "hour": None, "day_of_week": None, "day": None, "replication": None, "date_of_month": None, "month_of_year": None, "active": "", "date_created": "", "type": "WEEKLY", "id": 978, "minute": '30' }, { "week": None, "maximum_snapshots": None, "hour": None, "day_of_week": None, "day": None, "replication": '*', "date_of_month": None, "month_of_year": None, "active": "", "date_created": "", "type": "INTERVAL", "id": 988, "minute": '*' } ], json.loads(result.output)) def test_create_snapshot(self): result = self.run_command(['file', 'snapshot-create', '12345678']) self.assert_no_fail(result) self.assertEqual('New snapshot created with id: 449\n', result.output) @mock.patch('SoftLayer.FileStorageManager.create_snapshot') def test_create_snapshot_unsuccessful(self, snapshot_mock): snapshot_mock.return_value = [] result = self.run_command(['file', 'snapshot-create', '8', '-n=note']) self.assertEqual('Error occurred while creating snapshot.\n' 'Ensure volume is not failed over or in another ' 'state which prevents taking snapshots.\n', result.output) def test_snapshot_restore(self): result = self.run_command(['file', 'snapshot-restore', '12345678', '--snapshot-id=87654321']) self.assert_no_fail(result) self.assertEqual(result.output, 'File volume 12345678 is being' ' restored using snapshot 87654321\n') def test_delete_snapshot(self): result = self.run_command(['file', 'snapshot-delete', '12345678']) self.assert_no_fail(result) @mock.patch('SoftLayer.FileStorageManager.order_snapshot_space') def test_snapshot_order_order_not_placed(self, order_mock): order_mock.return_value = {} result = self.run_command(['file', 'snapshot-order', '1234', '--capacity=10', '--tier=0.25']) self.assert_no_fail(result) self.assertEqual(result.output, 'Order could not be placed! Please verify ' 'your options and try again.\n') @mock.patch('SoftLayer.FileStorageManager.order_snapshot_space') def test_snapshot_order_performance_manager_error(self, order_mock): order_mock.side_effect = ValueError('failure!') result = self.run_command(['file', 'snapshot-order', '1234', '--capacity=10', '--tier=0.25']) self.assertEqual(2, result.exit_code) self.assertEqual('Argument Error: failure!', result.exception.message) @mock.patch('SoftLayer.FileStorageManager.order_snapshot_space') def test_snapshot_order(self, order_mock): order_mock.return_value = { 'placedOrder': { 'id': 8702, 'items': [{'description': '10 GB Storage Space (Snapshot Space)'}], 'status': 'PENDING_APPROVAL', } } result = self.run_command(['file', 'snapshot-order', '1234', '--capacity=10', '--tier=0.25']) self.assert_no_fail(result) self.assertEqual(result.output, 'Order #8702 placed successfully!\n' ' > 10 GB Storage Space (Snapshot Space)\n' ' > Order status: PENDING_APPROVAL\n') def test_snapshot_cancel(self): result = self.run_command(['--really', 'file', 'snapshot-cancel', '1234']) self.assert_no_fail(result) self.assertEqual('File volume with id 1234 has been marked' ' for snapshot cancellation\n', result.output) self.assert_called_with('SoftLayer_Billing_Item', 'cancelItem', args=(False, True, None)) def test_replicant_failover(self): result = self.run_command(['file', 'replica-failover', '12345678', '--replicant-id=5678', '--immediate']) self.assert_no_fail(result) self.assertEqual('Failover to replicant is now in progress.\n', result.output) @mock.patch('SoftLayer.FileStorageManager.failover_to_replicant') def test_replicant_failover_unsuccessful(self, failover_mock): failover_mock.return_value = False result = self.run_command(['file', 'replica-failover', '12345678', '--replicant-id=5678']) self.assertEqual('Failover operation could not be initiated.\n', result.output) def test_replicant_failback(self): result = self.run_command(['file', 'replica-failback', '12345678', '--replicant-id=5678']) self.assert_no_fail(result) self.assertEqual('Failback from replicant is now in progress.\n', result.output) @mock.patch('SoftLayer.FileStorageManager.failback_from_replicant') def test_replicant_failback_unsuccessful(self, failback_mock): failback_mock.return_value = False result = self.run_command(['file', 'replica-failback', '12345678', '--replicant-id=5678']) self.assertEqual('Failback operation could not be initiated.\n', result.output) @mock.patch('SoftLayer.FileStorageManager.order_replicant_volume') def test_replicant_order_order_not_placed(self, order_mock): order_mock.return_value = {} result = self.run_command(['file', 'replica-order', '100', '--snapshot-schedule=DAILY', '--location=dal05']) self.assert_no_fail(result) self.assertEqual(result.output, 'Order could not be placed! Please verify ' 'your options and try again.\n') @mock.patch('SoftLayer.FileStorageManager.order_replicant_volume') def test_replicant_order(self, order_mock): order_mock.return_value = { 'placedOrder': { 'id': 77309, 'items': [ {'description': 'Endurance Storage'}, {'description': '2 IOPS per GB'}, {'description': 'File Storage'}, {'description': '20 GB Storage Space'}, {'description': '10 GB Storage Space (Snapshot Space)'}, {'description': '20 GB Storage Space Replicant of: TEST'}, ], } } result = self.run_command(['file', 'replica-order', '100', '--snapshot-schedule=DAILY', '--location=dal05', '--tier=2']) self.assert_no_fail(result) self.assertEqual(result.output, 'Order #77309 placed successfully!\n' ' > Endurance Storage\n' ' > 2 IOPS per GB\n' ' > File Storage\n' ' > 20 GB Storage Space\n' ' > 10 GB Storage Space (Snapshot Space)\n' ' > 20 GB Storage Space Replicant of: TEST\n') def test_replication_locations(self): result = self.run_command(['file', 'replica-locations', '1234']) self.assert_no_fail(result) self.assertEqual( { '12345': 'Dallas 05', }, json.loads(result.output)) @mock.patch('SoftLayer.FileStorageManager.get_replication_locations') def test_replication_locations_unsuccessful(self, locations_mock): locations_mock.return_value = False result = self.run_command(['file', 'replica-locations', '1234']) self.assert_no_fail(result) self.assertEqual('No data centers compatible for replication.\n', result.output) def test_replication_partners(self): result = self.run_command(['file', 'replica-partners', '1234']) self.assert_no_fail(result) self.assertEqual([ { 'ID': 1784, 'Account ID': 3000, 'Capacity (GB)': 20, 'Host ID': None, 'Guest ID': None, 'Hardware ID': None, 'Username': 'TEST_REP_1', }, { 'ID': 1785, 'Account ID': 3001, 'Host ID': None, 'Guest ID': None, 'Hardware ID': None, 'Capacity (GB)': 20, 'Username': 'TEST_REP_2', }], json.loads(result.output)) @mock.patch('SoftLayer.FileStorageManager.get_replication_partners') def test_replication_partners_unsuccessful(self, partners_mock): partners_mock.return_value = False result = self.run_command(['file', 'replica-partners', '1234']) self.assertEqual( 'There are no replication partners for the given volume.\n', result.output) @mock.patch('SoftLayer.FileStorageManager.order_duplicate_volume') def test_duplicate_order_exception_caught(self, order_mock): order_mock.side_effect = ValueError('order attempt failed, oh noooo!') result = self.run_command(['file', 'volume-duplicate', '100']) self.assertEqual(2, result.exit_code) self.assertEqual('Argument Error: order attempt failed, oh noooo!', result.exception.message) @mock.patch('SoftLayer.FileStorageManager.order_duplicate_volume') def test_duplicate_order_order_not_placed(self, order_mock): order_mock.return_value = {} result = self.run_command(['file', 'volume-duplicate', '100', '--duplicate-iops=1400']) self.assert_no_fail(result) self.assertEqual(result.output, 'Order could not be placed! Please verify ' 'your options and try again.\n') @mock.patch('SoftLayer.FileStorageManager.order_duplicate_volume') def test_duplicate_order(self, order_mock): order_mock.return_value = { 'placedOrder': { 'id': 24602, 'items': [{'description': 'Storage as a Service'}] } } result = self.run_command(['file', 'volume-duplicate', '100', '--origin-snapshot-id=470', '--duplicate-size=250', '--duplicate-tier=2', '--duplicate-snapshot-size=20']) self.assert_no_fail(result) self.assertEqual(result.output, 'Order #24602 placed successfully!\n' ' > Storage as a Service\n') @mock.patch('SoftLayer.FileStorageManager.order_duplicate_volume') def test_duplicate_order_hourly_billing(self, order_mock): order_mock.return_value = { 'placedOrder': { 'id': 24602, 'items': [{'description': 'Storage as a Service'}] } } result = self.run_command(['file', 'volume-duplicate', '100', '--origin-snapshot-id=470', '--duplicate-size=250', '--duplicate-tier=2', '--billing=hourly', '--duplicate-snapshot-size=20']) order_mock.assert_called_with('100', origin_snapshot_id=470, duplicate_size=250, duplicate_iops=None, duplicate_tier_level=2, duplicate_snapshot_size=20, hourly_billing_flag=True) self.assert_no_fail(result) self.assertEqual(result.output, 'Order #24602 placed successfully!\n' ' > Storage as a Service\n') @mock.patch('SoftLayer.FileStorageManager.order_modified_volume') def test_modify_order_exception_caught(self, order_mock): order_mock.side_effect = ValueError('order attempt failed, noooo!') result = self.run_command(['file', 'volume-modify', '102', '--new-size=1000']) self.assertEqual(2, result.exit_code) self.assertEqual('Argument Error: order attempt failed, noooo!', result.exception.message) @mock.patch('SoftLayer.FileStorageManager.order_modified_volume') def test_modify_order_order_not_placed(self, order_mock): order_mock.return_value = {} result = self.run_command(['file', 'volume-modify', '102', '--new-iops=1400']) self.assert_no_fail(result) self.assertEqual('Order could not be placed! Please verify your options and try again.\n', result.output) @mock.patch('SoftLayer.FileStorageManager.order_modified_volume') def test_modify_order(self, order_mock): order_mock.return_value = {'placedOrder': {'id': 24602, 'items': [{'description': 'Storage as a Service'}, {'description': '1000 GBs'}, {'description': '4 IOPS per GB'}]}} result = self.run_command(['file', 'volume-modify', '102', '--new-size=1000', '--new-tier=4']) order_mock.assert_called_with('102', new_size=1000, new_iops=None, new_tier_level=4) self.assert_no_fail(result) self.assertEqual('Order #24602 placed successfully!\n > Storage as a Service\n > 1000 GBs\n > 4 IOPS per GB\n', result.output) softlayer-python-5.4.2/tests/CLI/modules/firewall_tests.py000066400000000000000000000020201324365065500236760ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.firewall_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import json from SoftLayer import testing class FirewallTests(testing.TestCase): def test_list_firewalls(self): result = self.run_command(['firewall', 'list']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), [{'type': 'VLAN - dedicated', 'server/vlan id': 1, 'features': ['HA'], 'firewall id': 'vlan:1234'}, {'features': '-', 'firewall id': 'vs:1234', 'server/vlan id': 1, 'type': 'Virtual Server - standard'}, {'features': '-', 'firewall id': 'server:1234', 'server/vlan id': 1, 'type': 'Server - standard'}]) softlayer-python-5.4.2/tests/CLI/modules/globalip_tests.py000066400000000000000000000045301324365065500236720ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.globalip_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import mock from SoftLayer.CLI import exceptions from SoftLayer import testing import json class DnsTests(testing.TestCase): def test_ip_assign(self): result = self.run_command(['globalip', 'assign', '1', '127.0.0.1']) self.assert_no_fail(result) self.assertEqual(result.output, "") @mock.patch('SoftLayer.CLI.formatting.no_going_back') def test_ip_cancel(self, no_going_back_mock): # Test using --really flag result = self.run_command(['--really', 'globalip', 'cancel', '1']) self.assert_no_fail(result) self.assertEqual(result.output, "") # Test with confirmation no_going_back_mock.return_value = True result = self.run_command(['globalip', 'cancel', '1']) self.assert_no_fail(result) self.assertEqual(result.output, "") # Test with confirmation and responding negatively no_going_back_mock.return_value = False result = self.run_command(['globalip', 'cancel', '1']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.CLIAbort) def test_ip_list(self): result = self.run_command(['globalip', 'list', '--ip-version=v4']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), [{'assigned': 'Yes', 'id': '200', 'ip': '127.0.0.1', 'target': '127.0.0.1 (example.com)'}, {'assigned': 'Yes', 'id': '201', 'ip': '127.0.0.1', 'target': '127.0.0.1 (example.com)'}]) result = self.run_command(['globalip', 'list', '--ip-version=v6']) self.assertEqual(json.loads(result.output), [{'assigned': 'Yes', 'id': '200', 'ip': '127.0.0.1', 'target': '127.0.0.1 (example.com)'}, {'assigned': 'Yes', 'id': '201', 'ip': '127.0.0.1', 'target': '127.0.0.1 (example.com)'}]) softlayer-python-5.4.2/tests/CLI/modules/ipsec_tests.py000066400000000000000000000616721324365065500232160ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.ipsec_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import json from SoftLayer.CLI.exceptions import ArgumentError from SoftLayer.CLI.exceptions import CLIHalt from SoftLayer import testing class IPSECTests(testing.TestCase): def test_ipsec_configure(self): mock_account = self.set_mock('SoftLayer_Account', 'getNetworkTunnelContexts') mock_account.return_value = [{'id': 445}] mock_tunnel = self.set_mock('SoftLayer_Network_Tunnel_Module_Context', 'applyConfigurationsToDevice') mock_tunnel.return_value = True result = self.run_command(['ipsec', 'configure', '445']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Network_Tunnel_Module_Context', 'applyConfigurationsToDevice', identifier=445) self.assertEqual('Configuration request received for context #445\n', result.output) def test_ipsec_configure_fails(self): mock_account = self.set_mock('SoftLayer_Account', 'getNetworkTunnelContexts') mock_account.return_value = [{'id': 445}] mock_tunnel = self.set_mock('SoftLayer_Network_Tunnel_Module_Context', 'applyConfigurationsToDevice') mock_tunnel.return_value = False result = self.run_command(['ipsec', 'configure', '445']) self.assertIsInstance(result.exception, CLIHalt) self.assert_called_with('SoftLayer_Network_Tunnel_Module_Context', 'applyConfigurationsToDevice', identifier=445) self.assertEqual(('Failed to enqueue configuration request for ' 'context #445\n'), result.output) def test_ipsec_detail(self): _mask = ('[mask[id,accountId,advancedConfigurationFlag,createDate,' 'customerPeerIpAddress,modifyDate,name,friendlyName,' 'internalPeerIpAddress,phaseOneAuthentication,' 'phaseOneDiffieHellmanGroup,phaseOneEncryption,' 'phaseOneKeylife,phaseTwoAuthentication,' 'phaseTwoDiffieHellmanGroup,phaseTwoEncryption,' 'phaseTwoKeylife,phaseTwoPerfectForwardSecrecy,presharedKey,' 'addressTranslations[internalIpAddressRecord[ipAddress],' 'customerIpAddressRecord[ipAddress]],internalSubnets,' 'customerSubnets,staticRouteSubnets,serviceSubnets]]') mock = self.set_mock('SoftLayer_Account', 'getNetworkTunnelContexts') mock.return_value = [{ 'id': 445, 'name': 'der tunnel', 'friendlyName': 'the tunnel', 'internalPeerIpAddress': '10.0.0.1', 'customerPeerIpAddress': '50.0.0.1', 'advancedConfigurationFlag': 0, 'presharedKey': 'secret', 'phaseOneAuthentication': 'MD5', 'phaseOneDiffieHellmanGroup': 1, 'phaseOneEncryption': 'DES', 'phaseOneKeylife': 600, 'phaseTwoAuthentication': 'MD5', 'phaseTwoDiffieHellmanGroup': 1, 'phaseTwoEncryption': 'DES', 'phaseTwoKeylife': 600, 'phaseTwoPerfectForwardSecrecy': 0, 'createDate': '2017-05-17T12:00:00-06:00', 'modifyDate': '2017-05-17T12:01:00-06:00', 'addressTranslations': [{ 'id': 872341, 'internalIpAddressId': 982341, 'internalIpAddressRecord': {'ipAddress': '10.0.0.1'}, 'customerIpAddressId': 872422, 'customerIpAddressRecord': {'ipAddress': '50.0.0.1'}, 'notes': 'surprise!' }], 'internalSubnets': [{ 'id': 324113, 'networkIdentifier': '10.20.0.0', 'cidr': 29, 'note': 'Private Network' }], 'customerSubnets': [{ 'id': 873411, 'networkIdentifier': '50.0.0.0', 'cidr': 26, 'note': 'Offsite Network' }], 'serviceSubnets': [{ 'id': 565312, 'networkIdentifier': '100.10.0.0', 'cidr': 26, 'note': 'Service Network' }], 'staticRouteSubnets': [{ 'id': 998232, 'networkIdentifier': '50.50.0.0', 'cidr': 29, 'note': 'Static Network' }] }] result = self.run_command(['ipsec', 'detail', '445', '-iat', '-iis', '-irs', '-isr', '-iss']) empty, output = result.output.split('Context Details:\n') context, output = output.split('Address Translations:\n') translations, output = output.split('Internal Subnets:\n') internal_subnets, output = output.split('Remote Subnets:\n') remote_subnets, output = output.split('Static Subnets:\n') static_subnets, service_subnets = output.split('Service Subnets:\n') self.assert_no_fail(result) self.assert_called_with('SoftLayer_Account', 'getNetworkTunnelContexts', mask=_mask) self.assertEqual({'id': 445, 'name': 'der tunnel', 'friendly name': 'the tunnel', 'internal peer IP address': '10.0.0.1', 'remote peer IP address': '50.0.0.1', 'advanced configuration flag': 0, 'preshared key': 'secret', 'phase 1 authentication': 'MD5', 'phase 1 diffie hellman group': 1, 'phase 1 encryption': 'DES', 'phase 1 key life': 600, 'phase 2 authentication': 'MD5', 'phase 2 diffie hellman group': 1, 'phase 2 encryption': 'DES', 'phase 2 key life': 600, 'phase 2 perfect forward secrecy': 0, 'created': '2017-05-17T12:00:00-06:00', 'modified': '2017-05-17T12:01:00-06:00'}, json.loads(context)) self.assertEqual([{'id': 872341, 'remote IP address': '50.0.0.1', 'remote IP address id': 872422, 'static IP address': '10.0.0.1', 'static IP address id': 982341, 'note': 'surprise!'}], json.loads(translations)) self.assertEqual([{'id': 324113, 'network identifier': '10.20.0.0', 'cidr': 29, 'note': 'Private Network'}], json.loads(internal_subnets)) self.assertEqual([{'id': 873411, 'network identifier': '50.0.0.0', 'cidr': 26, 'note': 'Offsite Network'}], json.loads(remote_subnets)) self.assertEqual([{'id': 998232, 'network identifier': '50.50.0.0', 'cidr': 29, 'note': 'Static Network'}], json.loads(static_subnets)) self.assertEqual([{'id': 565312, 'network identifier': '100.10.0.0', 'cidr': 26, 'note': 'Service Network'}], json.loads(service_subnets)) def test_ipsec_list(self): mock = self.set_mock('SoftLayer_Account', 'getNetworkTunnelContexts') mock.return_value = [{'id': 445, 'name': 'der tunnel', 'friendlyName': 'the tunnel', 'internalPeerIpAddress': '10.0.0.1', 'customerPeerIpAddress': '50.0.0.1', 'advancedConfigurationFlag': 0, 'presharedKey': 'secret', 'phaseOneAuthentication': 'MD5', 'phaseOneDiffieHellmanGroup': 1, 'phaseOneEncryption': 'DES', 'phaseOneKeylife': 600, 'phaseTwoAuthentication': 'MD5', 'phaseTwoDiffieHellmanGroup': 1, 'phaseTwoEncryption': 'DES', 'phaseTwoKeylife': 600, 'phaseTwoPerfectForwardSecrecy': 0, 'createDate': '2017-05-17T12:00:00-06:00', 'modifyDate': '2017-05-17T12:01:00-06:00'}] result = self.run_command(['ipsec', 'list']) self.assert_no_fail(result) self.assertEqual([{ 'id': 445, 'name': 'der tunnel', 'friendly name': 'the tunnel', 'internal peer IP address': '10.0.0.1', 'remote peer IP address': '50.0.0.1', 'created': '2017-05-17T12:00:00-06:00' }], json.loads(result.output)) def test_ipsec_update(self): account_mock = self.set_mock('SoftLayer_Account', 'getNetworkTunnelContexts') account_mock.return_value = [{'id': 445, 'name': 'der tunnel', 'friendlyName': 'the tunnel', 'internalPeerIpAddress': '10.0.0.1', 'customerPeerIpAddress': '50.0.0.1', 'advancedConfigurationFlag': 0, 'presharedKey': 'secret', 'phaseOneAuthentication': 'MD5', 'phaseOneDiffieHellmanGroup': 1, 'phaseOneEncryption': 'DES', 'phaseOneKeylife': 600, 'phaseTwoAuthentication': 'MD5', 'phaseTwoDiffieHellmanGroup': 1, 'phaseTwoEncryption': 'DES', 'phaseTwoKeylife': 600, 'phaseTwoPerfectForwardSecrecy': 0}] tunnel_mock = self.set_mock('SoftLayer_Network_Tunnel_Module_Context', 'editObject') tunnel_mock.return_value = True result = self.run_command(['ipsec', 'update', '445', '--friendly-name=ipsec tunnel', '--remote-peer=50.0.0.2', '--preshared-key=enigma', '--p1-auth=SHA256', '--p1-crypto=AES256', '--p1-dh=5', '--p1-key-ttl=120', '--p2-auth=SHA1', '--p2-crypto=AES192', '--p2-dh=2', '--p2-forward-secrecy=1', '--p2-key-ttl=240']) self.assert_no_fail(result) self.assertEqual(result.output, 'Updated context #445\n') self.assert_called_with('SoftLayer_Network_Tunnel_Module_Context', 'editObject', identifier=445, args=({'id': 445, 'name': 'der tunnel', 'friendlyName': 'ipsec tunnel', 'internalPeerIpAddress': '10.0.0.1', 'customerPeerIpAddress': '50.0.0.2', 'advancedConfigurationFlag': 0, 'presharedKey': 'enigma', 'phaseOneAuthentication': 'SHA256', 'phaseOneDiffieHellmanGroup': '5', 'phaseOneEncryption': 'AES256', 'phaseOneKeylife': 120, 'phaseTwoAuthentication': 'SHA1', 'phaseTwoDiffieHellmanGroup': '2', 'phaseTwoEncryption': 'AES192', 'phaseTwoKeylife': 240, 'phaseTwoPerfectForwardSecrecy': 1},)) def test_ipsec_update_fails(self): account_mock = self.set_mock('SoftLayer_Account', 'getNetworkTunnelContexts') account_mock.return_value = [{'id': 445}] tunnel_mock = self.set_mock('SoftLayer_Network_Tunnel_Module_Context', 'editObject') tunnel_mock.return_value = False result = self.run_command(['ipsec', 'update', '445']) self.assertIsInstance(result.exception, CLIHalt) self.assertEqual('Failed to update context #445\n', result.output) def test_ipsec_subnet_add_internal(self): account_mock = self.set_mock('SoftLayer_Account', 'getNetworkTunnelContexts') account_mock.return_value = [{'id': 445}] tunnel_mock = self.set_mock('SoftLayer_Network_Tunnel_Module_Context', 'addPrivateSubnetToNetworkTunnel') tunnel_mock.return_value = True result = self.run_command(['ipsec', 'subnet-add', '445', '-tinternal', '-s234716']) self.assert_no_fail(result) self.assertEqual(result.output, 'Added internal subnet #234716\n') self.assert_called_with('SoftLayer_Network_Tunnel_Module_Context', 'addPrivateSubnetToNetworkTunnel', identifier=445, args=(234716,)) def test_ipsec_subnet_add_remote(self): account_mock = self.set_mock('SoftLayer_Account', 'getNetworkTunnelContexts') account_mock.return_value = [{'id': 445, 'accountId': 999000}] tunnel_mock = self.set_mock('SoftLayer_Network_Tunnel_Module_Context', 'addCustomerSubnetToNetworkTunnel') tunnel_mock.return_value = True subnet_mock = self.set_mock('SoftLayer_Network_Customer_Subnet', 'createObject') subnet_mock.return_value = {'id': 234716} result = self.run_command(['ipsec', 'subnet-add', '445', '-tremote', '-n50.0.0.0/26']) self.assert_no_fail(result) self.assertEqual(result.output, ('Created subnet 50.0.0.0/26 #234716\n' 'Added remote subnet #234716\n')) self.assert_called_with('SoftLayer_Network_Tunnel_Module_Context', 'addCustomerSubnetToNetworkTunnel', identifier=445, args=(234716,)) self.assert_called_with('SoftLayer_Network_Customer_Subnet', 'createObject', args=({'networkIdentifier': '50.0.0.0', 'cidr': 26, 'accountId': 999000},)) def test_ipsec_subnet_add_service(self): account_mock = self.set_mock('SoftLayer_Account', 'getNetworkTunnelContexts') account_mock.return_value = [{'id': 445}] tunnel_mock = self.set_mock('SoftLayer_Network_Tunnel_Module_Context', 'addServiceSubnetToNetworkTunnel') tunnel_mock.return_value = True result = self.run_command(['ipsec', 'subnet-add', '445', '-tservice', '-s234716']) self.assert_no_fail(result) self.assertEqual(result.output, 'Added service subnet #234716\n') self.assert_called_with('SoftLayer_Network_Tunnel_Module_Context', 'addServiceSubnetToNetworkTunnel', identifier=445, args=(234716,)) def test_ipsec_subnet_add_without_id_or_network(self): result = self.run_command(['ipsec', 'subnet-add', '445', '-tinternal']) self.assertIsInstance(result.exception, ArgumentError) def test_ipsec_subnet_add_internal_with_network(self): result = self.run_command(['ipsec', 'subnet-add', '445', '-tinternal', '-n50.0.0.0/26']) self.assertIsInstance(result.exception, ArgumentError) def test_ipsec_subnet_add_fails(self): account_mock = self.set_mock('SoftLayer_Account', 'getNetworkTunnelContexts') account_mock.return_value = [{'id': 445}] tunnel_mock = self.set_mock('SoftLayer_Network_Tunnel_Module_Context', 'addPrivateSubnetToNetworkTunnel') tunnel_mock.return_value = False result = self.run_command(['ipsec', 'subnet-add', '445', '-tinternal', '-s234716']) self.assertIsInstance(result.exception, CLIHalt) self.assertEqual(result.output, 'Failed to add internal subnet #234716\n') def test_ipsec_subnet_remove_internal(self): account_mock = self.set_mock('SoftLayer_Account', 'getNetworkTunnelContexts') account_mock.return_value = [{'id': 445}] tunnel_mock = self.set_mock('SoftLayer_Network_Tunnel_Module_Context', 'removePrivateSubnetFromNetworkTunnel') tunnel_mock.return_value = True result = self.run_command(['ipsec', 'subnet-remove', '445', '-tinternal', '-s234716']) self.assert_no_fail(result) self.assertEqual(result.output, 'Removed internal subnet #234716\n') self.assert_called_with('SoftLayer_Network_Tunnel_Module_Context', 'removePrivateSubnetFromNetworkTunnel', identifier=445, args=(234716,)) def test_ipsec_subnet_remove_remote(self): account_mock = self.set_mock('SoftLayer_Account', 'getNetworkTunnelContexts') account_mock.return_value = [{'id': 445}] tunnel_mock = self.set_mock('SoftLayer_Network_Tunnel_Module_Context', 'removeCustomerSubnetFromNetworkTunnel') tunnel_mock.return_value = True result = self.run_command(['ipsec', 'subnet-remove', '445', '-tremote', '-s234716']) self.assert_no_fail(result) self.assertEqual(result.output, 'Removed remote subnet #234716\n') self.assert_called_with('SoftLayer_Network_Tunnel_Module_Context', 'removeCustomerSubnetFromNetworkTunnel', identifier=445, args=(234716,)) def test_ipsec_subnet_remove_service(self): account_mock = self.set_mock('SoftLayer_Account', 'getNetworkTunnelContexts') account_mock.return_value = [{'id': 445}] tunnel_mock = self.set_mock('SoftLayer_Network_Tunnel_Module_Context', 'removeServiceSubnetFromNetworkTunnel') tunnel_mock.return_value = True result = self.run_command(['ipsec', 'subnet-remove', '445', '-tservice', '-s234716']) self.assert_no_fail(result) self.assertEqual(result.output, 'Removed service subnet #234716\n') self.assert_called_with('SoftLayer_Network_Tunnel_Module_Context', 'removeServiceSubnetFromNetworkTunnel', identifier=445, args=(234716,)) def test_ipsec_subnet_remove_fails(self): account_mock = self.set_mock('SoftLayer_Account', 'getNetworkTunnelContexts') account_mock.return_value = [{'id': 445}] tunnel_mock = self.set_mock('SoftLayer_Network_Tunnel_Module_Context', 'removePrivateSubnetFromNetworkTunnel') tunnel_mock.return_value = False result = self.run_command(['ipsec', 'subnet-remove', '445', '-tinternal', '-s234716']) self.assertIsInstance(result.exception, CLIHalt) self.assertEqual(result.output, 'Failed to remove internal subnet #234716\n') def test_ipsec_translation_add(self): account_mock = self.set_mock('SoftLayer_Account', 'getNetworkTunnelContexts') account_mock.return_value = [{'id': 445}] tunnel_mock = self.set_mock('SoftLayer_Network_Tunnel_Module_Context', 'createAddressTranslation') tunnel_mock.return_value = {'id': 872843} result = self.run_command(['ipsec', 'translation-add', '445', '-s10.50.0.0', '-r50.50.0.0', '-nlost']) self.assert_no_fail(result) self.assertEqual(result.output, ('Created translation from 10.50.0.0 to 50.50.0.0 ' '#872843\n')) self.assert_called_with('SoftLayer_Network_Tunnel_Module_Context', 'createAddressTranslation', identifier=445, args=({'customerIpAddress': '50.50.0.0', 'internalIpAddress': '10.50.0.0', 'notes': 'lost'},)) def test_ipsec_translation_remove(self): account_mock = self.set_mock('SoftLayer_Account', 'getNetworkTunnelContexts') account_mock.return_value = [{'id': 445, 'addressTranslations': [{'id': 872843}]}] tunnel_mock = self.set_mock('SoftLayer_Network_Tunnel_Module_Context', 'deleteAddressTranslation') tunnel_mock.return_value = True result = self.run_command(['ipsec', 'translation-remove', '445', '-t872843']) self.assert_no_fail(result) self.assertEqual(result.output, 'Removed translation #872843\n') self.assert_called_with('SoftLayer_Network_Tunnel_Module_Context', 'deleteAddressTranslation', identifier=445, args=(872843,)) def test_ipsec_translation_remove_fails(self): account_mock = self.set_mock('SoftLayer_Account', 'getNetworkTunnelContexts') account_mock.return_value = [{'id': 445, 'addressTranslations': [{'id': 872843}]}] tunnel_mock = self.set_mock('SoftLayer_Network_Tunnel_Module_Context', 'deleteAddressTranslation') tunnel_mock.return_value = False result = self.run_command(['ipsec', 'translation-remove', '445', '-t872843']) self.assertIsInstance(result.exception, CLIHalt) self.assertEqual(result.output, 'Failed to remove translation #872843\n') def test_ipsec_translation_update(self): account_mock = self.set_mock('SoftLayer_Account', 'getNetworkTunnelContexts') account_mock.return_value = [{'id': 445, 'addressTranslations': [{'id': 872843}]}] tunnel_mock = self.set_mock('SoftLayer_Network_Tunnel_Module_Context', 'editAddressTranslation') tunnel_mock.return_value = {'id': 872843} result = self.run_command(['ipsec', 'translation-update', '445', '-t872843', '-s10.50.0.1', '-r50.50.0.1', '-nlost']) self.assert_no_fail(result) self.assertEqual(result.output, 'Updated translation #872843\n') self.assert_called_with('SoftLayer_Network_Tunnel_Module_Context', 'editAddressTranslation', identifier=445, args=({'id': 872843, 'internalIpAddress': '10.50.0.1', 'customerIpAddress': '50.50.0.1', 'notes': 'lost'},)) softlayer-python-5.4.2/tests/CLI/modules/nas_tests.py000066400000000000000000000010731324365065500226610ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.nas_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ from SoftLayer import testing import json class RWhoisTests(testing.TestCase): def test_list_nas(self): result = self.run_command(['nas', 'list']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), [{'datacenter': 'Dallas', 'server': '127.0.0.1', 'id': 1, 'size': 10}]) softlayer-python-5.4.2/tests/CLI/modules/object_storage_tests.py000066400000000000000000000024161324365065500250740ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.object_storage_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import json from SoftLayer import testing class ObjectStorageTests(testing.TestCase): def test_list_accounts(self): result = self.run_command(['object-storage', 'accounts']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), [{'id': 12345, 'name': 'SLOS12345-1'}, {'id': 12346, 'name': 'SLOS12345-2'}]) def test_list_endpoints(self): accounts = self.set_mock('SoftLayer_Account', 'getHubNetworkStorage') accounts.return_value = { 'storageNodes': [{ 'datacenter': {'name': 'dal05'}, 'frontendIpAddress': 'https://dal05/auth/v1.0/', 'backendIpAddress': 'https://dal05/auth/v1.0/'} ], } result = self.run_command(['object-storage', 'endpoints']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), [{'datacenter': 'dal05', 'private': 'https://dal05/auth/v1.0/', 'public': 'https://dal05/auth/v1.0/'}]) softlayer-python-5.4.2/tests/CLI/modules/order_tests.py000066400000000000000000000260601324365065500232160ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.order_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import json from SoftLayer import testing class OrderTests(testing.TestCase): def test_category_list(self): cat1 = {'itemCategory': {'name': 'cat1', 'categoryCode': 'code1'}, 'isRequired': 1} cat2 = {'itemCategory': {'name': 'cat2', 'categoryCode': 'code2'}, 'isRequired': 0} p_mock = self.set_mock('SoftLayer_Product_Package', 'getConfiguration') p_mock.return_value = [cat1, cat2] result = self.run_command(['order', 'category-list', 'package']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Product_Package', 'getConfiguration') self.assertEqual([{'name': 'cat1', 'categoryCode': 'code1', 'isRequired': 'Y'}, {'name': 'cat2', 'categoryCode': 'code2', 'isRequired': 'N'}], json.loads(result.output)) def test_item_list(self): category = {'categoryCode': 'testing'} item1 = {'keyName': 'item1', 'description': 'description1', 'itemCategory': category} item2 = {'keyName': 'item2', 'description': 'description2', 'itemCategory': category} p_mock = self.set_mock('SoftLayer_Product_Package', 'getItems') p_mock.return_value = [item1, item2] result = self.run_command(['order', 'item-list', 'package']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Product_Package', 'getItems') expected_results = [{'category': 'testing', 'keyName': 'item1', 'description': 'description1'}, {'category': 'testing', 'keyName': 'item2', 'description': 'description2'}] self.assertEqual(expected_results, json.loads(result.output)) def test_package_list(self): item1 = {'name': 'package1', 'keyName': 'PACKAGE1', 'type': {'keyName': 'BARE_METAL_CPU'}, 'isActive': 1} item2 = {'name': 'package2', 'keyName': 'PACKAGE2', 'type': {'keyName': 'BARE_METAL_CPU'}, 'isActive': 1} item3 = {'name': 'package2', 'keyName': 'PACKAGE2', 'type': {'keyName': 'BARE_METAL_CPU'}, 'isActive': 0} p_mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') p_mock.return_value = [item1, item2, item3] _filter = {'type': {'keyName': {'operation': '!= BLUEMIX_SERVICE'}}} result = self.run_command(['order', 'package-list']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Product_Package', 'getAllObjects', filter=_filter) expected_results = [{'name': 'package1', 'keyName': 'PACKAGE1', 'type': 'BARE_METAL_CPU'}, {'name': 'package2', 'keyName': 'PACKAGE2', 'type': 'BARE_METAL_CPU'}] self.assertEqual(expected_results, json.loads(result.output)) def test_package_list_keyword(self): item1 = {'name': 'package1', 'keyName': 'PACKAGE1', 'type': {'keyName': 'BARE_METAL_CPU'}, 'isActive': 1} item2 = {'name': 'package2', 'keyName': 'PACKAGE2', 'type': {'keyName': 'BARE_METAL_CPU'}, 'isActive': 1} p_mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') p_mock.return_value = [item1, item2] _filter = {'type': {'keyName': {'operation': '!= BLUEMIX_SERVICE'}}} _filter['name'] = {'operation': '*= package1'} result = self.run_command(['order', 'package-list', '--keyword', 'package1']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Product_Package', 'getAllObjects', filter=_filter) expected_results = [{'name': 'package1', 'keyName': 'PACKAGE1', 'type': 'BARE_METAL_CPU'}, {'name': 'package2', 'keyName': 'PACKAGE2', 'type': 'BARE_METAL_CPU'}] self.assertEqual(expected_results, json.loads(result.output)) def test_package_list_type(self): item1 = {'name': 'package1', 'keyName': 'PACKAGE1', 'type': {'keyName': 'BARE_METAL_CPU'}, 'isActive': 1} item2 = {'name': 'package2', 'keyName': 'PACKAGE2', 'type': {'keyName': 'BARE_METAL_CPU'}, 'isActive': 1} p_mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') p_mock.return_value = [item1, item2] _filter = {'type': {'keyName': {'operation': 'BARE_METAL_CPU'}}} result = self.run_command(['order', 'package-list', '--package_type', 'BARE_METAL_CPU']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Product_Package', 'getAllObjects', filter=_filter) expected_results = [{'name': 'package1', 'keyName': 'PACKAGE1', 'type': 'BARE_METAL_CPU'}, {'name': 'package2', 'keyName': 'PACKAGE2', 'type': 'BARE_METAL_CPU'}] self.assertEqual(expected_results, json.loads(result.output)) def test_place(self): order_date = '2017-04-04 07:39:20' order = {'orderId': 1234, 'orderDate': order_date, 'placedOrder': {'status': 'APPROVED'}} verify_mock = self.set_mock('SoftLayer_Product_Order', 'verifyOrder') place_mock = self.set_mock('SoftLayer_Product_Order', 'placeOrder') items_mock = self.set_mock('SoftLayer_Product_Package', 'getItems') verify_mock.return_value = self._get_verified_order_return() place_mock.return_value = order items_mock.return_value = self._get_order_items() result = self.run_command(['-y', 'order', 'place', 'package', 'DALLAS13', 'ITEM1', '--complex-type', 'SoftLayer_Container_Product_Order_Thing']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Product_Order', 'placeOrder') self.assertEqual({'id': 1234, 'created': order_date, 'status': 'APPROVED'}, json.loads(result.output)) def test_verify_hourly(self): order_date = '2017-04-04 07:39:20' order = {'orderId': 1234, 'orderDate': order_date, 'placedOrder': {'status': 'APPROVED'}} verify_mock = self.set_mock('SoftLayer_Product_Order', 'verifyOrder') items_mock = self.set_mock('SoftLayer_Product_Package', 'getItems') order = self._get_verified_order_return() verify_mock.return_value = order items_mock.return_value = self._get_order_items() result = self.run_command(['order', 'place', '--billing', 'hourly', '--verify', '--complex-type', 'SoftLayer_Container_Product_Order_Thing', 'package', 'DALLAS13', 'ITEM1', 'ITEM2']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Product_Order', 'verifyOrder') expected_results = [] for price in order['prices']: expected_results.append({'keyName': price['item']['keyName'], 'description': price['item']['description'], 'cost': price['hourlyRecurringFee']}) self.assertEqual(expected_results, json.loads(result.output)) def test_verify_monthly(self): order_date = '2017-04-04 07:39:20' order = {'orderId': 1234, 'orderDate': order_date, 'placedOrder': {'status': 'APPROVED'}} verify_mock = self.set_mock('SoftLayer_Product_Order', 'verifyOrder') items_mock = self.set_mock('SoftLayer_Product_Package', 'getItems') order = self._get_verified_order_return() verify_mock.return_value = order items_mock.return_value = self._get_order_items() result = self.run_command(['order', 'place', '--billing', 'monthly', '--verify', '--complex-type', 'SoftLayer_Container_Product_Order_Thing', 'package', 'DALLAS13', 'ITEM1', 'ITEM2']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Product_Order', 'verifyOrder') expected_results = [] for price in order['prices']: expected_results.append({'keyName': price['item']['keyName'], 'description': price['item']['description'], 'cost': price['recurringFee']}) self.assertEqual(expected_results, json.loads(result.output)) def test_preset_list(self): active_preset1 = {'name': 'active1', 'keyName': 'PRESET1', 'description': 'description1'} active_preset2 = {'name': 'active2', 'keyName': 'PRESET2', 'description': 'description2'} acc_preset = {'name': 'account1', 'keyName': 'PRESET3', 'description': 'description3'} active_mock = self.set_mock('SoftLayer_Product_Package', 'getActivePresets') account_mock = self.set_mock('SoftLayer_Product_Package', 'getAccountRestrictedActivePresets') active_mock.return_value = [active_preset1, active_preset2] account_mock.return_value = [acc_preset] result = self.run_command(['order', 'preset-list', 'package']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Product_Package', 'getActivePresets') self.assert_called_with('SoftLayer_Product_Package', 'getAccountRestrictedActivePresets') self.assertEqual([{'name': 'active1', 'keyName': 'PRESET1', 'description': 'description1'}, {'name': 'active2', 'keyName': 'PRESET2', 'description': 'description2'}, {'name': 'account1', 'keyName': 'PRESET3', 'description': 'description3'}], json.loads(result.output)) def test_location_list(self): result = self.run_command(['order', 'package-locations', 'package']) self.assert_no_fail(result) expected_results = [ {'id': 2017603, 'dc': 'wdc07', 'description': 'WDC07 - Washington, DC', 'keyName': 'WASHINGTON07'} ] print("FUCK") print(result.output) self.assertEqual(expected_results, json.loads(result.output)) def _get_order_items(self): item1 = {'keyName': 'ITEM1', 'description': 'description1', 'prices': [{'id': 1111, 'locationGroupId': ''}]} item2 = {'keyName': 'ITEM2', 'description': 'description2', 'prices': [{'id': 2222, 'locationGroupId': ''}]} return [item1, item2] def _get_verified_order_return(self): item1, item2 = self._get_order_items() price1 = {'item': item1, 'hourlyRecurringFee': '0.04', 'recurringFee': '120'} price2 = {'item': item2, 'hourlyRecurringFee': '0.05', 'recurringFee': '150'} return {'prices': [price1, price2]} softlayer-python-5.4.2/tests/CLI/modules/report_tests.py000066400000000000000000000146561324365065500234260ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.report_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ from SoftLayer import testing import json class ReportTests(testing.TestCase): def test_bandwidth_invalid_date(self): result = self.run_command( [ 'report', 'bandwidth', '--start=welp', '--end=2016-01-01', ], ) self.assertTrue('Invalid value for "--start"', result.output) result = self.run_command( [ 'report', 'bandwidth', '--start=2016-01-01', '--end=welp', ], ) self.assertTrue('Invalid value for "--end"', result.output) def test_bandwidth_report(self): racks = self.set_mock('SoftLayer_Account', 'getVirtualDedicatedRacks') racks.return_value = [{ 'id': 1, 'name': 'pool1', 'metricTrackingObjectId': 1, }, { 'id': 2, 'name': 'pool2', }, { 'id': 3, 'name': 'pool3', 'metricTrackingObjectId': 3, }] hardware = self.set_mock('SoftLayer_Account', 'getHardware') hardware.return_value = [{ 'id': 101, 'metricTrackingObject': {'id': 101}, 'hostname': 'host1', }, { 'id': 102, 'hostname': 'host2', 'virtualRack': {'id': 1, 'bandwidthAllotmentTypeId': 2}, }, { 'id': 103, 'metricTrackingObject': {'id': 103}, 'hostname': 'host3', 'virtualRack': {'id': 1, 'bandwidthAllotmentTypeId': 2}, }] guests = self.set_mock('SoftLayer_Account', 'getVirtualGuests') guests.return_value = [{ 'id': 201, 'metricTrackingObjectId': 201, 'hostname': 'host1', }, { 'id': 202, 'hostname': 'host2', 'virtualRack': {'id': 2, 'bandwidthAllotmentTypeId': 2}, }, { 'id': 203, 'metricTrackingObjectId': 203, 'hostname': 'host3', 'virtualRack': {'id': 2, 'bandwidthAllotmentTypeId': 2}, }] summary_data = self.set_mock('SoftLayer_Metric_Tracking_Object', 'getSummaryData') summary_data.return_value = [ {'type': 'publicIn_net_octet', 'counter': 10}, {'type': 'publicOut_net_octet', 'counter': 20}, {'type': 'privateIn_net_octet', 'counter': 30}, {'type': 'privateOut_net_octet', 'counter': 40}, ] result = self.run_command([ 'report', 'bandwidth', '--start=2016-02-04', '--end=2016-03-04 12:34:56', ]) self.assert_no_fail(result) stripped_output = '[' + result.output.split('[', 1)[1] self.assertEqual([ { 'name': 'pool1', 'pool': None, 'private_in': 30, 'private_out': 40, 'public_in': 10, 'public_out': 20, 'type': 'pool', }, { 'name': 'pool3', 'pool': None, 'private_in': 30, 'private_out': 40, 'public_in': 10, 'public_out': 20, 'type': 'pool', }, { 'name': 'host1', 'pool': None, 'private_in': 30, 'private_out': 40, 'public_in': 10, 'public_out': 20, 'type': 'virtual', }, { 'name': 'host3', 'pool': 2, 'private_in': 30, 'private_out': 40, 'public_in': 10, 'public_out': 20, 'type': 'virtual', }, { 'name': 'host1', 'pool': None, 'private_in': 30, 'private_out': 40, 'public_in': 10, 'public_out': 20, 'type': 'hardware', }, { 'name': 'host3', 'pool': None, 'private_in': 30, 'private_out': 40, 'public_in': 10, 'public_out': 20, 'type': 'hardware', }], json.loads(stripped_output), ) self.assertEqual( 6, len(self.calls('SoftLayer_Metric_Tracking_Object', 'getSummaryData')), ) self.assert_called_with('SoftLayer_Metric_Tracking_Object', 'getSummaryData', identifier=1) self.assert_called_with('SoftLayer_Metric_Tracking_Object', 'getSummaryData', identifier=3) self.assert_called_with('SoftLayer_Metric_Tracking_Object', 'getSummaryData', identifier=101) self.assert_called_with('SoftLayer_Metric_Tracking_Object', 'getSummaryData', identifier=103) self.assert_called_with('SoftLayer_Metric_Tracking_Object', 'getSummaryData', identifier=201) self.assert_called_with('SoftLayer_Metric_Tracking_Object', 'getSummaryData', identifier=203) call = self.calls('SoftLayer_Metric_Tracking_Object', 'getSummaryData', identifier=1)[0] expected_args = ( '2016-02-04 00:00:00 ', '2016-03-04 12:34:56 ', [{ 'keyName': 'PUBLICIN', 'name': 'publicIn', 'summaryType': 'sum', }, { 'keyName': 'PUBLICOUT', 'name': 'publicOut', 'summaryType': 'sum', }, { 'keyName': 'PRIVATEIN', 'name': 'privateIn', 'summaryType': 'sum', }, { 'keyName': 'PRIVATEOUT', 'name': 'privateOut', 'summaryType': 'sum', }], 300, ) self.assertEqual(expected_args, call.args) softlayer-python-5.4.2/tests/CLI/modules/rwhois_tests.py000066400000000000000000000062721324365065500234210ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.rwhois_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ from SoftLayer.CLI import exceptions from SoftLayer import testing import json class RWhoisTests(testing.TestCase): def test_edit_nothing(self): result = self.run_command(['rwhois', 'edit']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.CLIAbort) def test_edit(self): result = self.run_command(['rwhois', 'edit', '--abuse=abuse@site.com', '--address1=address line 1', '--address2=address line 2', '--company=Company, Inc', '--city=Dallas', '--country=United States', '--firstname=John', '--lastname=Smith', '--postal=12345', '--state=TX', '--state=TX', '--private']) self.assert_no_fail(result) self.assertEqual(result.output, "") self.assert_called_with('SoftLayer_Network_Subnet_Rwhois_Data', 'editObject', args=({'city': 'Dallas', 'firstName': 'John', 'companyName': 'Company, Inc', 'address1': 'address line 1', 'address2': 'address line 2', 'lastName': 'Smith', 'abuseEmail': 'abuse@site.com', 'state': 'TX', 'country': 'United States', 'postalCode': '12345', 'privateResidenceFlag': True},), identifier='id') def test_edit_public(self): result = self.run_command(['rwhois', 'edit', '--public']) self.assert_no_fail(result) self.assertEqual(result.output, "") self.assert_called_with('SoftLayer_Network_Subnet_Rwhois_Data', 'editObject', args=({'privateResidenceFlag': False},), identifier='id') def test_show(self): self.maxDiff = 100000 result = self.run_command(['rwhois', 'show']) expected = {'Abuse Email': 'abuseEmail', 'Address 1': 'address1', 'Address 2': 'address2', 'City': 'city', 'Company': 'companyName', 'Country': 'country', 'Name': 'firstName lastName', 'Postal Code': 'postalCode', 'State': '-', 'Private Residence': True} self.assert_no_fail(result) self.assertEqual(json.loads(result.output), expected) softlayer-python-5.4.2/tests/CLI/modules/securitygroup_tests.py000066400000000000000000000230751324365065500250320ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.securitygroup_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import json from SoftLayer import testing class SecurityGroupTests(testing.TestCase): def test_list_securitygroup(self): result = self.run_command(['sg', 'list']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Network_SecurityGroup', 'getAllObjects') self.assertEqual([{'id': 100, 'name': 'secgroup1', 'description': 'Securitygroup1'}, {'id': 104, 'name': 'secgroup2', 'description': None}, {'id': 110, 'name': None, 'description': None}], json.loads(result.output)) def test_securitygroup_detail(self): result = self.run_command(['sg', 'detail', '100']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Network_SecurityGroup', 'getObject', identifier='100') priv_server_dict = {'id': 5000, 'hostname': 'test', 'interface': 'PRIVATE', 'ipAddress': '10.3.4.5'} pub_server_dict = {'id': 5000, 'hostname': 'test', 'interface': 'PUBLIC', 'ipAddress': '169.23.123.43'} self.assertEqual({'id': 100, 'name': 'secgroup1', 'description': 'Securitygroup1', 'rules': [{'id': 100, 'direction': 'egress', 'ethertype': 'IPv4', 'remoteIp': None, 'remoteGroupId': None, 'protocol': None, 'portRangeMin': None, 'portRangeMax': None}], 'servers': [priv_server_dict, pub_server_dict]}, json.loads(result.output)) def test_securitygroup_create(self): result = self.run_command(['sg', 'create', '--name=secgroup1', '--description=Securitygroup1']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Network_SecurityGroup', 'createObject', args=({'name': 'secgroup1', 'description': 'Securitygroup1'},)) self.assertEqual({'id': 100, 'name': 'secgroup1', 'description': 'Securitygroup1', 'created': '2017-05-05T12:44:43-06:00'}, json.loads(result.output)) def test_securitygroup_edit(self): result = self.run_command(['sg', 'edit', '104', '--name=foo']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Network_SecurityGroup', 'editObject', identifier='104', args=({'name': 'foo'},)) def test_securitygroup_edit_fail(self): fixture = self.set_mock('SoftLayer_Network_SecurityGroup', 'editObject') fixture.return_value = False result = self.run_command(['sg', 'edit', '100', '--name=foo']) self.assertEqual(result.exit_code, 2) def test_securitygroup_delete(self): result = self.run_command(['sg', 'delete', '104']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Network_SecurityGroup', 'deleteObject', identifier='104') def test_securitygroup_delete_fail(self): fixture = self.set_mock('SoftLayer_Network_SecurityGroup', 'deleteObject') fixture.return_value = False result = self.run_command(['sg', 'delete', '100']) self.assertEqual(result.exit_code, 2) def test_securitygroup_rule_list(self): result = self.run_command(['sg', 'rule-list', '100']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Network_SecurityGroup', 'getRules', identifier='100') self.assertEqual([{'id': 100, 'direction': 'egress', 'ethertype': 'IPv4', 'remoteIp': None, 'remoteGroupId': None, 'protocol': None, 'portRangeMin': None, 'portRangeMax': None}], json.loads(result.output)) def test_securitygroup_rule_add(self): result = self.run_command(['sg', 'rule-add', '100', '--direction=ingress']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Network_SecurityGroup', 'addRules', identifier='100', args=([{'direction': 'ingress'}],)) def test_securitygroup_rule_add_fail(self): fixture = self.set_mock('SoftLayer_Network_SecurityGroup', 'addRules') fixture.return_value = False result = self.run_command(['sg', 'rule-add', '100', '--direction=ingress']) self.assertEqual(result.exit_code, 2) def test_securitygroup_rule_edit(self): result = self.run_command(['sg', 'rule-edit', '100', '520', '--direction=ingress']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Network_SecurityGroup', 'editRules', identifier='100', args=([{'id': '520', 'direction': 'ingress'}],)) def test_securitygroup_rule_edit_fail(self): fixture = self.set_mock('SoftLayer_Network_SecurityGroup', 'editRules') fixture.return_value = False result = self.run_command(['sg', 'rule-edit', '100', '520', '--direction=ingress']) self.assertEqual(result.exit_code, 2) def test_securitygroup_rule_remove(self): result = self.run_command(['sg', 'rule-remove', '100', '520']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Network_SecurityGroup', 'removeRules', identifier='100', args=(['520'],)) def test_securitygroup_rule_remove_fail(self): fixture = self.set_mock('SoftLayer_Network_SecurityGroup', 'removeRules') fixture.return_value = False result = self.run_command(['sg', 'rule-remove', '100', '520']) self.assertEqual(result.exit_code, 2) def test_securitygroup_interface_list(self): result = self.run_command(['sg', 'interface-list', '100']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Network_SecurityGroup', 'getObject', identifier='100') self.assertEqual([{'hostname': 'test', 'interface': 'PRIVATE', 'ipAddress': '10.3.4.5', 'networkComponentId': 1000, 'virtualServerId': 5000}, {'hostname': 'test', 'interface': 'PUBLIC', 'ipAddress': '169.23.123.43', 'networkComponentId': 1001, 'virtualServerId': 5000}], json.loads(result.output)) def test_securitygroup_interface_add(self): result = self.run_command(['sg', 'interface-add', '100', '--network-component=1000']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Network_SecurityGroup', 'attachNetworkComponents', identifier='100', args=(['1000'],)) def test_securitygroup_interface_add_fail(self): fixture = self.set_mock('SoftLayer_Network_SecurityGroup', 'attachNetworkComponents') fixture.return_value = False result = self.run_command(['sg', 'interface-add', '100', '--network-component=500']) self.assertEqual(result.exit_code, 2) def test_securitygroup_interface_remove(self): result = self.run_command(['sg', 'interface-remove', '100', '--network-component=500']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Network_SecurityGroup', 'detachNetworkComponents', identifier='100', args=(['500'],)) def test_securitygroup_interface_remove_fail(self): fixture = self.set_mock('SoftLayer_Network_SecurityGroup', 'detachNetworkComponents') fixture.return_value = False result = self.run_command(['sg', 'interface-remove', '100', '--network-component=500']) self.assertEqual(result.exit_code, 2) softlayer-python-5.4.2/tests/CLI/modules/server_tests.py000066400000000000000000000501201324365065500234030ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.server_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This is a series of integration tests designed to test the complete command line interface. :license: MIT, see LICENSE for more details. """ import mock import sys from SoftLayer.CLI import exceptions from SoftLayer import testing import json import tempfile class ServerCLITests(testing.TestCase): def test_server_cancel_reasons(self): result = self.run_command(['server', 'cancel-reasons']) output = json.loads(result.output) self.assert_no_fail(result) self.assertEqual(len(output), 10) def test_server_details(self): result = self.run_command(['server', 'detail', '1234', '--passwords', '--price']) expected = { 'cores': 2, 'created': '2013-08-01 15:23:45', 'datacenter': 'TEST00', 'guid': '1a2b3c-1701', 'domain': 'test.sftlyr.ws', 'hostname': 'hardware-test1', 'fqdn': 'hardware-test1.test.sftlyr.ws', 'id': 1000, 'ipmi_ip': '10.1.0.3', 'memory': 2048, 'notes': 'These are test notes.', 'os': 'Ubuntu', 'os_version': 'Ubuntu 12.04 LTS', 'owner': 'chechu', 'prices': [{'Item': 'Total', 'Recurring Price': 16.08}, {'Item': 'test', 'Recurring Price': 1}], 'private_ip': '10.1.0.2', 'public_ip': '172.16.1.100', 'remote users': [{'password': 'abc123', 'ipmi_username': 'root'}], 'status': 'ACTIVE', 'tags': ['test_tag'], 'users': [{'password': 'abc123', 'username': 'root'}], 'vlans': [{'id': 9653, 'number': 1800, 'type': 'PRIVATE'}, {'id': 19082, 'number': 3672, 'type': 'PUBLIC'}] } self.assert_no_fail(result) self.assertEqual(expected, json.loads(result.output)) def test_detail_vs_empty_tag(self): mock = self.set_mock('SoftLayer_Hardware_Server', 'getObject') mock.return_value = { 'id': 100, 'processorPhysicalCoreAmount': 2, 'memoryCapacity': 2, 'tagReferences': [ {'tag': {'name': 'example-tag'}}, {}, ], } result = self.run_command(['server', 'detail', '100']) self.assert_no_fail(result) self.assertEqual( json.loads(result.output)['tags'], ['example-tag'], ) def test_list_servers(self): result = self.run_command(['server', 'list', '--tag=openstack']) expected = [ { 'datacenter': 'TEST00', 'primary_ip': '172.16.1.100', 'hostname': 'hardware-test1', 'id': 1000, 'backend_ip': '10.1.0.2', 'action': 'TXN_NAME', }, { 'datacenter': 'TEST00', 'primary_ip': '172.16.4.94', 'hostname': 'hardware-test2', 'id': 1001, 'backend_ip': '10.1.0.3', 'action': None, }, { 'datacenter': 'TEST00', 'primary_ip': '172.16.4.95', 'hostname': 'hardware-bad-memory', 'id': 1002, 'backend_ip': '10.1.0.4', 'action': None, }, { 'action': None, 'backend_ip': None, 'datacenter': None, 'hostname': None, 'id': 1003, 'primary_ip': None, }, ] self.assert_no_fail(result) self.assertEqual(expected, json.loads(result.output)) @mock.patch('SoftLayer.CLI.formatting.no_going_back') @mock.patch('SoftLayer.HardwareManager.reload') def test_server_reload(self, reload_mock, ngb_mock): ngb_mock.return_value = False # Check the positive case result = self.run_command(['--really', 'server', 'reload', '12345', '--key=4567']) self.assert_no_fail(result) reload_mock.assert_called_with(12345, None, [4567]) # Now check to make sure we properly call CLIAbort in the negative case result = self.run_command(['server', 'reload', '12345']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.CLIAbort) @mock.patch('SoftLayer.CLI.formatting.no_going_back') @mock.patch('SoftLayer.HardwareManager.cancel_hardware') def test_cancel_server(self, cancel_mock, ngb_mock): ngb_mock.return_value = False # Check the positive case result = self.run_command(['--really', 'server', 'cancel', '12345', '--reason=Test', '--comment=Test']) self.assert_no_fail(result) cancel_mock.assert_called_with(12345, "Test", "Test", False) # Test result = self.run_command(['server', 'cancel', '12345', '--reason=Test', '--comment=Test']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.CLIAbort) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_server_power_off(self, confirm_mock): # Check the positive case result = self.run_command(['--really', 'server', 'power-off', '12345']) self.assert_called_with('SoftLayer_Hardware_Server', 'powerOff', identifier=12345) # Now check to make sure we properly call CLIAbort in the negative case confirm_mock.return_value = False result = self.run_command(['server', 'power-off', '12345']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.CLIAbort) def test_server_reboot_default(self): result = self.run_command(['--really', 'server', 'reboot', '12345']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Hardware_Server', 'rebootDefault', identifier=12345) def test_server_reboot_soft(self): result = self.run_command(['--really', 'server', 'reboot', '12345', '--soft']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Hardware_Server', 'rebootSoft', identifier=12345) def test_server_reboot_hard(self): result = self.run_command(['--really', 'server', 'reboot', '12345', '--hard']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Hardware_Server', 'rebootHard', identifier=12345) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_server_reboot_negative(self, confirm_mock): confirm_mock.return_value = False result = self.run_command(['server', 'reboot', '12345']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.CLIAbort) def test_server_power_on(self): result = self.run_command(['--really', 'server', 'power-on', '12345']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Hardware_Server', 'powerOn', identifier=12345) def test_server_power_cycle(self): result = self.run_command(['--really', 'server', 'power-cycle', '12345']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Hardware_Server', 'powerCycle', identifier=12345) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_server_power_cycle_negative(self, confirm_mock): confirm_mock.return_value = False result = self.run_command(['server', 'power-cycle', '12345']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.CLIAbort) @mock.patch('SoftLayer.HardwareManager.verify_order') def test_create_server_test_flag(self, verify_mock): verify_mock.return_value = { 'prices': [ { 'recurringFee': 0.0, 'setupFee': 0.0, 'item': {'description': 'First Item'}, }, { 'recurringFee': 25.0, 'setupFee': 0.0, 'item': {'description': 'Second Item'}, } ] } result = self.run_command(['--really', 'server', 'create', '--size=S1270_8GB_2X1TBSATA_NORAID', '--hostname=test', '--domain=example.com', '--datacenter=TEST00', '--port-speed=100', '--os=UBUNTU_12_64', '--test'], fmt='raw') self.assert_no_fail(result) self.assertIn("First Item", result.output) self.assertIn("Second Item", result.output) self.assertIn("Total monthly cost", result.output) def test_create_options(self): result = self.run_command(['server', 'create-options']) self.assert_no_fail(result) expected = [ [{'datacenter': 'Washington 1', 'value': 'wdc01'}], [{'size': 'Single Xeon 1270, 8GB Ram, 2x1TB SATA disks, Non-RAID', 'value': 'S1270_8GB_2X1TBSATA_NORAID'}], [{'operating_system': 'Ubuntu / 14.04-64', 'value': 'UBUNTU_14_64'}], [{'port_speed': '10 Mbps Public & Private Network Uplinks', 'value': '10'}], [{'extras': '1 IPv6 Address', 'value': '1_IPV6_ADDRESS'}]] self.assertEqual(json.loads(result.output), expected) @mock.patch('SoftLayer.HardwareManager.place_order') def test_create_server(self, order_mock): order_mock.return_value = { 'orderId': 98765, 'orderDate': '2013-08-02 15:23:47' } result = self.run_command(['--really', 'server', 'create', '--size=S1270_8GB_2X1TBSATA_NORAID', '--hostname=test', '--domain=example.com', '--datacenter=TEST00', '--port-speed=100', '--os=UBUNTU_12_64', '--no-public', '--key=10', ]) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), {'id': 98765, 'created': '2013-08-02 15:23:47'}) def test_create_server_missing_required(self): # This is missing a required argument result = self.run_command(['server', 'create', # Note: no chassis id '--hostname=test', '--domain=example.com', '--datacenter=TEST00', '--network=100', '--os=UBUNTU_12_64_MINIMAL', ]) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, SystemExit) @mock.patch('SoftLayer.CLI.template.export_to_template') def test_create_server_with_export(self, export_mock): if(sys.platform.startswith("win")): self.skipTest("Test doesn't work in Windows") result = self.run_command(['--really', 'server', 'create', '--size=S1270_8GB_2X1TBSATA_NORAID', '--hostname=test', '--domain=example.com', '--datacenter=TEST00', '--port-speed=100', '--os=UBUNTU_12_64', '--no-public', '--export=/path/to/test_file.txt'], fmt='raw') self.assert_no_fail(result) self.assertIn("Successfully exported options to a template file.", result.output) export_mock.assert_called_with('/path/to/test_file.txt', {'billing': 'hourly', 'datacenter': 'TEST00', 'domain': 'example.com', 'extra': (), 'hostname': 'test', 'key': (), 'os': 'UBUNTU_12_64', 'port_speed': 100, 'postinstall': None, 'size': 'S1270_8GB_2X1TBSATA_NORAID', 'test': False, 'no_public': True, 'wait': None, 'template': None}, exclude=['wait', 'test']) def test_edit_server_userdata_and_file(self): # Test both userdata and userfile at once with tempfile.NamedTemporaryFile() as userfile: result = self.run_command(['server', 'edit', '1000', '--hostname=hardware-test1', '--domain=test.sftlyr.ws', '--userdata=My data', '--userfile=%s' % userfile.name]) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.ArgumentError) def test_edit_server_userdata(self): result = self.run_command(['server', 'edit', '1000', '--hostname=hardware-test1', '--domain=test.sftlyr.ws', '--userdata=My data']) self.assert_no_fail(result) self.assertEqual(result.output, "") self.assert_called_with('SoftLayer_Hardware_Server', 'editObject', args=({'domain': 'test.sftlyr.ws', 'hostname': 'hardware-test1'},), identifier=1000) @mock.patch('SoftLayer.HardwareManager.edit') def test_edit_server_failed(self, edit_mock): edit_mock.return_value = False result = self.run_command(['server', 'edit', '1000', '--hostname=hardware-test1', '--domain=test.sftlyr.ws', '--userdata=My data']) self.assertEqual(result.exit_code, 2) self.assertEqual(result.output, "") edit_mock.assert_called_with(1000, userdata='My data', domain='test.sftlyr.ws', hostname='hardware-test1') def test_edit_server_userfile(self): if(sys.platform.startswith("win")): self.skipTest("Test doesn't work in Windows") with tempfile.NamedTemporaryFile() as userfile: userfile.write(b"some data") userfile.flush() result = self.run_command(['server', 'edit', '1000', '--userfile=%s' % userfile.name]) self.assert_no_fail(result) self.assertEqual(result.output, "") self.assert_called_with('SoftLayer_Hardware_Server', 'setUserMetadata', args=(['some data'],), identifier=1000) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_update_firmware(self, confirm_mock): confirm_mock.return_value = True result = self.run_command(['server', 'update-firmware', '1000']) self.assert_no_fail(result) self.assertEqual(result.output, "") self.assert_called_with('SoftLayer_Hardware_Server', 'createFirmwareUpdateTransaction', args=((1, 1, 1, 1)), identifier=1000) def test_edit(self): result = self.run_command(['server', 'edit', '--domain=example.com', '--hostname=host', '--userdata="testdata"', '--tag=dev', '--tag=green', '--public-speed=10', '--private-speed=100', '100']) self.assert_no_fail(result) self.assertEqual(result.output, '') self.assert_called_with( 'SoftLayer_Hardware_Server', 'editObject', args=({'domain': 'example.com', 'hostname': 'host'},), identifier=100, ) self.assert_called_with( 'SoftLayer_Hardware_Server', 'setUserMetadata', args=(['"testdata"'],), identifier=100, ) self.assert_called_with( 'SoftLayer_Hardware_Server', 'setPublicNetworkInterfaceSpeed', args=(10,), identifier=100, ) self.assert_called_with( 'SoftLayer_Hardware_Server', 'setPrivateNetworkInterfaceSpeed', args=(100,), identifier=100, ) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_rescue(self, confirm_mock): confirm_mock.return_value = True result = self.run_command(['server', 'rescue', '1000']) self.assert_no_fail(result) self.assertEqual(result.output, "") self.assert_called_with('SoftLayer_Hardware_Server', 'bootToRescueLayer', identifier=1000) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_server_rescue_negative(self, confirm_mock): confirm_mock.return_value = False result = self.run_command(['server', 'rescue', '1000']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.CLIAbort) def test_ready(self): mock = self.set_mock('SoftLayer_Hardware_Server', 'getObject') mock.return_value = { "provisionDate": "2017-10-17T11:21:53-07:00", "id": 41957081 } result = self.run_command(['hw', 'ready', '100']) self.assert_no_fail(result) self.assertEqual(result.output, '"READY"\n') def test_not_ready(self): mock = self.set_mock('SoftLayer_Hardware_Server', 'getObject') not_ready = { 'activeTransaction': { 'transactionStatus': {'friendlyName': 'Attach Primary Disk'} }, 'provisionDate': '', 'id': 47392219 } ready = { "provisionDate": "2017-10-17T11:21:53-07:00", "id": 41957081 } mock.side_effect = [not_ready, ready] result = self.run_command(['hw', 'ready', '100']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.CLIAbort) @mock.patch('time.sleep') def test_going_ready(self, _sleep): mock = self.set_mock('SoftLayer_Hardware_Server', 'getObject') not_ready = { 'activeTransaction': { 'transactionStatus': {'friendlyName': 'Attach Primary Disk'} }, 'provisionDate': '', 'id': 47392219 } ready = { "provisionDate": "2017-10-17T11:21:53-07:00", "id": 41957081 } mock.side_effect = [not_ready, ready] result = self.run_command(['hw', 'ready', '100', '--wait=100']) self.assert_no_fail(result) self.assertEqual(result.output, '"READY"\n') softlayer-python-5.4.2/tests/CLI/modules/sshkey_tests.py000066400000000000000000000116211324365065500234060ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.sshkey_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import json import os.path import sys import tempfile import mock from SoftLayer.CLI import exceptions from SoftLayer import testing class SshKeyTests(testing.TestCase): def test_add_without_key_errors(self): result = self.run_command(['sshkey', 'add', 'key1']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.ArgumentError) def test_add_with_key_file_and_key_argument_errors(self): path = os.path.join(testing.FIXTURE_PATH, 'id_rsa.pub') result = self.run_command(['sshkey', 'add', 'key1', '--key=some_key', '--in-file=%s' % path]) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.ArgumentError) def test_add_by_option(self): service = self.client['Security_Ssh_Key'] mock_key = service.getObject()['key'] result = self.run_command(['sshkey', 'add', 'key1', '--key=%s' % mock_key, '--note=my key']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), "SSH key added: aa:bb:cc:dd") self.assert_called_with('SoftLayer_Security_Ssh_Key', 'createObject', args=({'notes': 'my key', 'key': mock_key, 'label': 'key1'},)) def test_add_by_file(self): path = os.path.join(testing.FIXTURE_PATH, 'id_rsa.pub') result = self.run_command(['sshkey', 'add', 'key1', '--in-file=%s' % path]) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), "SSH key added: aa:bb:cc:dd") service = self.client['Security_Ssh_Key'] mock_key = service.getObject()['key'] self.assert_called_with('SoftLayer_Security_Ssh_Key', 'createObject', args=({'notes': None, 'key': mock_key, 'label': 'key1'},)) def test_remove_key(self): result = self.run_command(['--really', 'sshkey', 'remove', '1234']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Security_Ssh_Key', 'deleteObject', identifier=1234) @mock.patch('SoftLayer.CLI.formatting.no_going_back') def test_remove_key_fail(self, ngb_mock): ngb_mock.return_value = False result = self.run_command(['sshkey', 'remove', '1234']) self.assertEqual(result.exit_code, 2) def test_edit_key(self): result = self.run_command(['sshkey', 'edit', '1234', '--label=key1', '--note=my key']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Security_Ssh_Key', 'editObject', args=({'notes': 'my key', 'label': 'key1'},), identifier=1234) def test_edit_key_fail(self): fixture = self.set_mock('SoftLayer_Security_Ssh_Key', 'editObject') fixture.return_value = False result = self.run_command(['sshkey', 'edit', '1234', '--label=key1', '--note=my key']) self.assertEqual(result.exit_code, 2) def test_list_keys(self): result = self.run_command(['sshkey', 'list']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), [{'notes': '-', 'fingerprint': None, 'id': '100', 'label': 'Test 1'}, {'notes': 'my key', 'fingerprint': None, 'id': '101', 'label': 'Test 2'}]) def test_print_key(self): result = self.run_command(['sshkey', 'print', '1234']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), {'id': 1234, 'label': 'label', 'notes': 'notes'}) def test_print_key_file(self): if(sys.platform.startswith("win")): self.skipTest("Test doesn't work in Windows") with tempfile.NamedTemporaryFile() as sshkey_file: service = self.client['Security_Ssh_Key'] mock_key = service.getObject()['key'] result = self.run_command(['sshkey', 'print', '1234', '--out-file=%s' % sshkey_file.name]) self.assert_no_fail(result) self.assertEqual(mock_key, sshkey_file.read().decode("utf-8")) softlayer-python-5.4.2/tests/CLI/modules/subnet_tests.py000066400000000000000000000021011324365065500233710ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.subnet_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ from SoftLayer import testing import json class SubnetTests(testing.TestCase): def test_detail(self): result = self.run_command(['subnet', 'detail', '1234']) self.assert_no_fail(result) self.assertEqual( { 'id': 1234, 'identifier': '1.2.3.4/26', 'subnet type': 'ADDITIONAL_PRIMARY', 'network space': 'PUBLIC', 'gateway': '1.2.3.254', 'broadcast': '1.2.3.255', 'datacenter': 'dal10', 'vs': [ { 'hostname': 'hostname0', 'domain': 'sl.test', 'public_ip': '1.2.3.10', 'private_ip': '10.0.1.2' } ], 'hardware': 'none', 'usable ips': 22 }, json.loads(result.output)) softlayer-python-5.4.2/tests/CLI/modules/summary_tests.py000066400000000000000000000012321324365065500235720ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.summary_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ from SoftLayer import testing import json class SummaryTests(testing.TestCase): def test_summary(self): result = self.run_command(['summary']) expected = [ { 'datacenter': 'dal00', 'subnets': 0, 'hardware': 1, 'public_ips': 6, 'virtual_servers': 1, 'vlans': 3 } ] self.assert_no_fail(result) self.assertEqual(json.loads(result.output), expected) softlayer-python-5.4.2/tests/CLI/modules/ticket_tests.py000066400000000000000000000176001324365065500233660ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.ticket_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import json import mock from SoftLayer.CLI import exceptions from SoftLayer import testing class TicketTests(testing.TestCase): def test_list(self): result = self.run_command(['ticket', 'list']) expected = [{ 'assigned_user': 'John Smith', 'id': 102, 'last_edited': '2013-08-01T14:16:47-07:00', 'status': 'Open', 'title': 'Cloud Instance Cancellation - 08/01/13'}] self.assert_no_fail(result) self.assertEqual(json.loads(result.output), expected) def test_detail(self): result = self.run_command(['ticket', 'detail', '1']) expected = { 'created': '2013-08-01T14:14:04-07:00', 'edited': '2013-08-01T14:16:47-07:00', 'id': 100, 'status': 'Closed', 'title': 'Cloud Instance Cancellation - 08/01/13', 'update 1': 'a bot says something', 'update 2': 'By John Smith\nuser says something', 'update 3': 'By emp1 (Employee)\nemployee says something', } self.assert_no_fail(result) self.assertEqual(json.loads(result.output), expected) def test_create(self): result = self.run_command(['ticket', 'create', '--title=Test', '--subject-id=1000', '--body=ticket body']) self.assert_no_fail(result) args = ({'subjectId': 1000, 'contents': 'ticket body', 'assignedUserId': 12345, 'title': 'Test'}, 'ticket body') self.assert_called_with('SoftLayer_Ticket', 'createStandardTicket', args=args) def test_create_and_attach(self): result = self.run_command(['ticket', 'create', '--title=Test', '--subject-id=1000', '--body=ticket body', '--hardware=234', '--virtual=567']) self.assert_no_fail(result) args = ({'subjectId': 1000, 'contents': 'ticket body', 'assignedUserId': 12345, 'title': 'Test'}, 'ticket body') self.assert_called_with('SoftLayer_Ticket', 'createStandardTicket', args=args) self.assert_called_with('SoftLayer_Ticket', 'addAttachedHardware', args=(234,), identifier=100) self.assert_called_with('SoftLayer_Ticket', 'addAttachedVirtualGuest', args=(567,), identifier=100) @mock.patch('click.edit') def test_create_no_body(self, edit_mock): edit_mock.return_value = 'ticket body' result = self.run_command(['ticket', 'create', '--title=Test', '--subject-id=1000']) self.assert_no_fail(result) args = ({'subjectId': 1000, 'contents': 'ticket body', 'assignedUserId': 12345, 'title': 'Test'}, 'ticket body') self.assert_called_with('SoftLayer_Ticket', 'createStandardTicket', args=args) def test_subjects(self): list_expected_ids = [1001, 1002, 1003, 1004, 1005] result = self.run_command(['ticket', 'subjects']) self.assert_no_fail(result) results = json.loads(result.output) for result in results: self.assertIn(result['id'], list_expected_ids) def test_attach_no_identifier(self): result = self.run_command(['ticket', 'attach', '1']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.ArgumentError) def test_attach_two_identifiers(self): result = self.run_command(['ticket', 'attach', '1', '--hardware=100', '--virtual=100']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.ArgumentError) def test_ticket_attach_hardware(self): result = self.run_command(['ticket', 'attach', '1', '--hardware=100']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Ticket', 'addAttachedHardware', args=(100,), identifier=1) def test_ticket_attach_virtual_server(self): result = self.run_command(['ticket', 'attach', '1', '--virtual=100']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Ticket', 'addAttachedVirtualGuest', args=(100,), identifier=1) def test_detach_no_identifier(self): result = self.run_command(['ticket', 'detach', '1']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.ArgumentError) def test_detach_two_identifiers(self): result = self.run_command(['ticket', 'detach', '1', '--hardware=100', '--virtual=100']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.ArgumentError) def test_ticket_detach_hardware(self): result = self.run_command(['ticket', 'detach', '1', '--hardware=100']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Ticket', 'removeAttachedHardware', args=(100,), identifier=1) def test_ticket_detach_virtual_server(self): result = self.run_command(['ticket', 'detach', '1', '--virtual=100']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Ticket', 'removeAttachedVirtualGuest', args=(100,), identifier=1) def test_ticket_upload_no_path(self): result = self.run_command(['ticket', 'upload', '1']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.ArgumentError) def test_ticket_upload_invalid_path(self): result = self.run_command(['ticket', 'upload', '1', '--path=tests/resources/nonexistent_file', '--name=a_file_name']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.ArgumentError) def test_ticket_upload_no_name(self): result = self.run_command(['ticket', 'upload', '1', '--path=tests/resources/attachment_upload']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Ticket', 'addAttachedFile', args=({"filename": "attachment_upload", "data": b"ticket attached data"},), identifier=1) def test_ticket_upload(self): result = self.run_command(['ticket', 'upload', '1', '--path=tests/resources/attachment_upload', '--name=a_file_name']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Ticket', 'addAttachedFile', args=({"filename": "a_file_name", "data": b"ticket attached data"},), identifier=1) softlayer-python-5.4.2/tests/CLI/modules/vs_tests.py000066400000000000000000000674521324365065500225450ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.vs_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import json import mock from SoftLayer.CLI import exceptions from SoftLayer import SoftLayerAPIError from SoftLayer import testing class VirtTests(testing.TestCase): def test_list_vs(self): result = self.run_command(['vs', 'list', '--tag=tag']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), [{'datacenter': 'TEST00', 'primary_ip': '172.16.240.2', 'hostname': 'vs-test1', 'action': None, 'id': 100, 'backend_ip': '10.45.19.37'}, {'datacenter': 'TEST00', 'primary_ip': '172.16.240.7', 'hostname': 'vs-test2', 'action': None, 'id': 104, 'backend_ip': '10.45.19.35'}]) def test_detail_vs(self): result = self.run_command(['vs', 'detail', '100', '--passwords', '--price']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), {'active_transaction': None, 'cores': 2, 'created': '2013-08-01 15:23:45', 'datacenter': 'TEST00', 'dedicated_host': 'test-dedicated', 'dedicated_host_id': 37401, 'hostname': 'vs-test1', 'domain': 'test.sftlyr.ws', 'fqdn': 'vs-test1.test.sftlyr.ws', 'id': 100, 'guid': '1a2b3c-1701', 'memory': 1024, 'modified': {}, 'os': 'Ubuntu', 'os_version': '12.04-64 Minimal for VSI', 'notes': 'notes', 'price_rate': 6.54, 'tags': ['production'], 'private_cpu': {}, 'private_ip': '10.45.19.37', 'private_only': {}, 'ptr': 'test.softlayer.com.', 'public_ip': '172.16.240.2', 'state': 'RUNNING', 'status': 'ACTIVE', 'users': [{'software': 'Ubuntu', 'password': 'pass', 'username': 'user'}], 'vlans': [{'type': 'PUBLIC', 'number': 23, 'id': 1}], 'owner': 'chechu'}) def test_detail_vs_empty_tag(self): mock = self.set_mock('SoftLayer_Virtual_Guest', 'getObject') mock.return_value = { 'id': 100, 'maxCpu': 2, 'maxMemory': 1024, 'tagReferences': [ {'tag': {'name': 'example-tag'}}, {}, ], } result = self.run_command(['vs', 'detail', '100']) self.assert_no_fail(result) self.assertEqual( json.loads(result.output)['tags'], ['example-tag'], ) def test_detail_vs_dedicated_host_not_found(self): ex = SoftLayerAPIError('SoftLayer_Exception', 'Not found') mock = self.set_mock('SoftLayer_Virtual_DedicatedHost', 'getObject') mock.side_effect = ex result = self.run_command(['vs', 'detail', '100']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output)['dedicated_host_id'], 37401) self.assertIsNone(json.loads(result.output)['dedicated_host']) def test_detail_vs_no_dedicated_host_hostname(self): mock = self.set_mock('SoftLayer_Virtual_DedicatedHost', 'getObject') mock.return_value = {'this_is_a_fudged_Virtual_DedicatedHost': True, 'name_is_not_provided': ''} result = self.run_command(['vs', 'detail', '100']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output)['dedicated_host_id'], 37401) self.assertIsNone(json.loads(result.output)['dedicated_host']) def test_create_options(self): result = self.run_command(['vs', 'create-options']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), {'cpus (dedicated host)': [4, 56], 'cpus (dedicated)': [1], 'cpus (standard)': [1, 2, 3, 4], 'datacenter': ['ams01', 'dal05'], 'flavors (balanced)': ['B1_1X2X25', 'B1_1X2X100'], 'flavors (balanced local - hdd)': ['BL1_1X2X100'], 'flavors (balanced local - ssd)': ['BL2_1X2X100'], 'flavors (compute)': ['C1_1X2X25'], 'flavors (memory)': ['M1_1X2X100'], 'flavors (GPU)': ['AC1_1X2X100', 'ACL1_1X2X100'], 'local disk(0)': ['25', '100'], 'memory': [1024, 2048, 3072, 4096], 'memory (dedicated host)': [8192, 65536], 'nic': ['10', '100', '1000'], 'nic (dedicated host)': ['1000'], 'os (CENTOS)': 'CENTOS_6_64', 'os (DEBIAN)': 'DEBIAN_7_64', 'os (UBUNTU)': 'UBUNTU_12_64'}) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create(self, confirm_mock): confirm_mock.return_value = True result = self.run_command(['vs', 'create', '--cpu=2', '--domain=example.com', '--hostname=host', '--os=UBUNTU_LATEST', '--memory=1', '--network=100', '--billing=hourly', '--datacenter=dal05', '--tag=dev', '--tag=green']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), {'guid': '1a2b3c-1701', 'id': 100, 'created': '2013-08-01 15:23:45'}) args = ({'datacenter': {'name': 'dal05'}, 'domain': 'example.com', 'hourlyBillingFlag': True, 'localDiskFlag': True, 'maxMemory': 1024, 'hostname': 'host', 'startCpus': 2, 'operatingSystemReferenceCode': 'UBUNTU_LATEST', 'networkComponents': [{'maxSpeed': '100'}], 'supplementalCreateObjectOptions': {'bootMode': None}},) self.assert_called_with('SoftLayer_Virtual_Guest', 'createObject', args=args) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create_with_integer_image_id(self, confirm_mock): confirm_mock.return_value = True result = self.run_command(['vs', 'create', '--cpu=2', '--domain=example.com', '--hostname=host', '--image=12345', '--memory=1', '--network=100', '--billing=hourly', '--datacenter=dal05']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), {'guid': '1a2b3c-1701', 'id': 100, 'created': '2013-08-01 15:23:45'}) args = ({ 'datacenter': {'name': 'dal05'}, 'domain': 'example.com', 'hourlyBillingFlag': True, 'localDiskFlag': True, 'maxMemory': 1024, 'hostname': 'host', 'startCpus': 2, 'blockDeviceTemplateGroup': { 'globalIdentifier': '0B5DEAF4-643D-46CA-A695-CECBE8832C9D', }, 'networkComponents': [{'maxSpeed': '100'}], 'supplementalCreateObjectOptions': {'bootMode': None} },) self.assert_called_with('SoftLayer_Virtual_Guest', 'createObject', args=args) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create_with_flavor(self, confirm_mock): confirm_mock.return_value = True result = self.run_command(['vs', 'create', '--domain=example.com', '--hostname=host', '--os=UBUNTU_LATEST', '--network=100', '--billing=hourly', '--datacenter=dal05', '--flavor=B1_1X2X25']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), {'guid': '1a2b3c-1701', 'id': 100, 'created': '2013-08-01 15:23:45'}) args = ({'datacenter': {'name': 'dal05'}, 'domain': 'example.com', 'hourlyBillingFlag': True, 'hostname': 'host', 'startCpus': None, 'maxMemory': None, 'localDiskFlag': None, 'supplementalCreateObjectOptions': { 'bootMode': None, 'flavorKeyName': 'B1_1X2X25'}, 'operatingSystemReferenceCode': 'UBUNTU_LATEST', 'networkComponents': [{'maxSpeed': '100'}]},) self.assert_called_with('SoftLayer_Virtual_Guest', 'createObject', args=args) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create_with_host_id(self, confirm_mock): confirm_mock.return_value = True result = self.run_command(['vs', 'create', '--cpu=2', '--domain=example.com', '--hostname=host', '--os=UBUNTU_LATEST', '--memory=1', '--network=100', '--billing=hourly', '--datacenter=dal05', '--dedicated', '--host-id=123']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), {'guid': '1a2b3c-1701', 'id': 100, 'created': '2013-08-01 15:23:45'}) args = ({'datacenter': {'name': 'dal05'}, 'domain': 'example.com', 'hourlyBillingFlag': True, 'localDiskFlag': True, 'maxMemory': 1024, 'hostname': 'host', 'startCpus': 2, 'operatingSystemReferenceCode': 'UBUNTU_LATEST', 'networkComponents': [{'maxSpeed': '100'}], 'dedicatedHost': {'id': 123}, 'supplementalCreateObjectOptions': {'bootMode': None}},) self.assert_called_with('SoftLayer_Virtual_Guest', 'createObject', args=args) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create_like(self, confirm_mock): mock = self.set_mock('SoftLayer_Virtual_Guest', 'getObject') mock.return_value = { 'hostname': 'vs-test-like', 'domain': 'test.sftlyr.ws', 'maxCpu': 2, 'maxMemory': 1024, 'datacenter': {'name': 'dal05'}, 'networkComponents': [{'maxSpeed': 100}], 'dedicatedAccountHostOnlyFlag': False, 'privateNetworkOnlyFlag': False, 'billingItem': {'orderItem': {'preset': {}}}, 'operatingSystem': {'softwareLicense': { 'softwareDescription': {'referenceCode': 'UBUNTU_LATEST'} }}, 'hourlyBillingFlag': False, 'localDiskFlag': True, 'userData': {} } confirm_mock.return_value = True result = self.run_command(['vs', 'create', '--like=123', '--san', '--billing=hourly']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), {'guid': '1a2b3c-1701', 'id': 100, 'created': '2013-08-01 15:23:45'}) args = ({'datacenter': {'name': 'dal05'}, 'domain': 'test.sftlyr.ws', 'hourlyBillingFlag': True, 'hostname': 'vs-test-like', 'startCpus': 2, 'maxMemory': 1024, 'localDiskFlag': False, 'operatingSystemReferenceCode': 'UBUNTU_LATEST', 'networkComponents': [{'maxSpeed': 100}], 'supplementalCreateObjectOptions': {'bootMode': None}},) self.assert_called_with('SoftLayer_Virtual_Guest', 'createObject', args=args) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create_like_flavor(self, confirm_mock): mock = self.set_mock('SoftLayer_Virtual_Guest', 'getObject') mock.return_value = { 'hostname': 'vs-test-like', 'domain': 'test.sftlyr.ws', 'maxCpu': 2, 'maxMemory': 1024, 'datacenter': {'name': 'dal05'}, 'networkComponents': [{'maxSpeed': 100}], 'dedicatedAccountHostOnlyFlag': False, 'privateNetworkOnlyFlag': False, 'billingItem': {'orderItem': {'preset': {'keyName': 'B1_1X2X25'}}}, 'operatingSystem': {'softwareLicense': { 'softwareDescription': {'referenceCode': 'UBUNTU_LATEST'} }}, 'hourlyBillingFlag': True, 'localDiskFlag': False, 'userData': {} } confirm_mock.return_value = True result = self.run_command(['vs', 'create', '--like=123']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), {'guid': '1a2b3c-1701', 'id': 100, 'created': '2013-08-01 15:23:45'}) args = ({'datacenter': {'name': 'dal05'}, 'domain': 'test.sftlyr.ws', 'hourlyBillingFlag': True, 'hostname': 'vs-test-like', 'startCpus': None, 'maxMemory': None, 'localDiskFlag': None, 'supplementalCreateObjectOptions': { 'bootMode': None, 'flavorKeyName': 'B1_1X2X25'}, 'operatingSystemReferenceCode': 'UBUNTU_LATEST', 'networkComponents': [{'maxSpeed': 100}]},) self.assert_called_with('SoftLayer_Virtual_Guest', 'createObject', args=args) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_dns_sync_both(self, confirm_mock): confirm_mock.return_value = True getReverseDomainRecords = self.set_mock('SoftLayer_Virtual_Guest', 'getReverseDomainRecords') getReverseDomainRecords.return_value = [{ 'networkAddress': '172.16.240.2', 'name': '2.240.16.172.in-addr.arpa', 'resourceRecords': [{'data': 'test.softlayer.com.', 'id': 100, 'host': '12'}], 'updateDate': '2013-09-11T14:36:57-07:00', 'serial': 1234665663, 'id': 123456, }] getResourceRecords = self.set_mock('SoftLayer_Dns_Domain', 'getResourceRecords') getResourceRecords.return_value = [] createAargs = ({ 'type': 'a', 'host': 'vs-test1', 'domainId': 98765, 'data': '172.16.240.2', 'ttl': 7200 },) createPTRargs = ({ 'type': 'ptr', 'host': '2', 'domainId': 123456, 'data': 'vs-test1.test.sftlyr.ws', 'ttl': 7200 },) result = self.run_command(['vs', 'dns-sync', '100']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Dns_Domain', 'getResourceRecords') self.assert_called_with('SoftLayer_Virtual_Guest', 'getReverseDomainRecords') self.assert_called_with('SoftLayer_Dns_Domain_ResourceRecord', 'createObject', args=createAargs) self.assert_called_with('SoftLayer_Dns_Domain_ResourceRecord', 'createObject', args=createPTRargs) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_dns_sync_v6(self, confirm_mock): confirm_mock.return_value = True getResourceRecords = self.set_mock('SoftLayer_Dns_Domain', 'getResourceRecords') getResourceRecords.return_value = [] guest = self.set_mock('SoftLayer_Virtual_Guest', 'getObject') test_guest = { 'id': 100, 'hostname': 'vs-test1', 'domain': 'sftlyr.ws', 'primaryIpAddress': '172.16.240.2', 'fullyQualifiedDomainName': 'vs-test1.sftlyr.ws', "primaryNetworkComponent": {} } guest.return_value = test_guest result = self.run_command(['vs', 'dns-sync', '--aaaa-record', '100']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.CLIAbort) test_guest['primaryNetworkComponent'] = { 'primaryVersion6IpAddressRecord': { 'ipAddress': '2607:f0d0:1b01:0023:0000:0000:0000:0004' } } createV6args = ({ 'type': 'aaaa', 'host': 'vs-test1', 'domainId': 98765, 'data': '2607:f0d0:1b01:0023:0000:0000:0000:0004', 'ttl': 7200 },) guest.return_value = test_guest result = self.run_command(['vs', 'dns-sync', '--aaaa-record', '100']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Dns_Domain_ResourceRecord', 'createObject', args=createV6args) v6Record = { 'id': 1, 'ttl': 7200, 'data': '2607:f0d0:1b01:0023:0000:0000:0000:0004', 'host': 'vs-test1', 'type': 'aaaa' } getResourceRecords = self.set_mock('SoftLayer_Dns_Domain', 'getResourceRecords') getResourceRecords.return_value = [v6Record] editArgs = (v6Record,) result = self.run_command(['vs', 'dns-sync', '--aaaa-record', '100']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Dns_Domain_ResourceRecord', 'editObject', args=editArgs) getResourceRecords = self.set_mock('SoftLayer_Dns_Domain', 'getResourceRecords') getResourceRecords.return_value = [v6Record, v6Record] result = self.run_command(['vs', 'dns-sync', '--aaaa-record', '100']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.CLIAbort) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_dns_sync_edit_a(self, confirm_mock): confirm_mock.return_value = True getResourceRecords = self.set_mock('SoftLayer_Dns_Domain', 'getResourceRecords') getResourceRecords.return_value = [ {'id': 1, 'ttl': 7200, 'data': '1.1.1.1', 'host': 'vs-test1', 'type': 'a'} ] editArgs = ( {'type': 'a', 'host': 'vs-test1', 'data': '172.16.240.2', 'id': 1, 'ttl': 7200}, ) result = self.run_command(['vs', 'dns-sync', '-a', '100']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Dns_Domain_ResourceRecord', 'editObject', args=editArgs) getResourceRecords = self.set_mock('SoftLayer_Dns_Domain', 'getResourceRecords') getResourceRecords.return_value = [ {'id': 1, 'ttl': 7200, 'data': '1.1.1.1', 'host': 'vs-test1', 'type': 'a'}, {'id': 2, 'ttl': 7200, 'data': '1.1.1.1', 'host': 'vs-test1', 'type': 'a'} ] result = self.run_command(['vs', 'dns-sync', '-a', '100']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.CLIAbort) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_dns_sync_edit_ptr(self, confirm_mock): confirm_mock.return_value = True getReverseDomainRecords = self.set_mock('SoftLayer_Virtual_Guest', 'getReverseDomainRecords') getReverseDomainRecords.return_value = [{ 'networkAddress': '172.16.240.2', 'name': '2.240.16.172.in-addr.arpa', 'resourceRecords': [{'data': 'test.softlayer.com.', 'id': 100, 'host': '2'}], 'updateDate': '2013-09-11T14:36:57-07:00', 'serial': 1234665663, 'id': 123456, }] editArgs = ({'host': '2', 'data': 'vs-test1.test.sftlyr.ws', 'id': 100, 'ttl': 7200},) result = self.run_command(['vs', 'dns-sync', '--ptr', '100']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Dns_Domain_ResourceRecord', 'editObject', args=editArgs) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_dns_sync_misc_exception(self, confirm_mock): confirm_mock.return_value = False result = self.run_command(['vs', 'dns-sync', '-a', '100']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.CLIAbort) guest = self.set_mock('SoftLayer_Virtual_Guest', 'getObject') test_guest = { 'id': 100, 'primaryIpAddress': '', 'hostname': 'vs-test1', 'domain': 'sftlyr.ws', 'fullyQualifiedDomainName': 'vs-test1.sftlyr.ws', "primaryNetworkComponent": {} } guest.return_value = test_guest result = self.run_command(['vs', 'dns-sync', '-a', '100']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.CLIAbort) def test_upgrade_no_options(self, ): result = self.run_command(['vs', 'upgrade', '100']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.ArgumentError) def test_upgrade_private_no_cpu(self): result = self.run_command(['vs', 'upgrade', '100', '--private', '--memory=1024']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.ArgumentError) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_upgrade_aborted(self, confirm_mock): confirm_mock.return_value = False result = self.run_command(['vs', 'upgrade', '100', '--cpu=1']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.CLIAbort) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_upgrade(self, confirm_mock): confirm_mock.return_value = True result = self.run_command(['vs', 'upgrade', '100', '--cpu=4', '--memory=2048', '--network=1000']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Product_Order', 'placeOrder') call = self.calls('SoftLayer_Product_Order', 'placeOrder')[0] order_container = call.args[0] self.assertIn({'id': 1144}, order_container['prices']) self.assertIn({'id': 1133}, order_container['prices']) self.assertIn({'id': 1122}, order_container['prices']) self.assertEqual(order_container['virtualGuests'], [{'id': 100}]) def test_edit(self): result = self.run_command(['vs', 'edit', '--domain=example.com', '--hostname=host', '--userdata="testdata"', '--tag=dev', '--tag=green', '--public-speed=10', '--private-speed=100', '100']) self.assert_no_fail(result) self.assertEqual(result.output, '') self.assert_called_with( 'SoftLayer_Virtual_Guest', 'editObject', args=({'domain': 'example.com', 'hostname': 'host'},), identifier=100, ) self.assert_called_with( 'SoftLayer_Virtual_Guest', 'setUserMetadata', args=(['"testdata"'],), identifier=100, ) self.assert_called_with( 'SoftLayer_Virtual_Guest', 'setPublicNetworkInterfaceSpeed', args=(10,), identifier=100, ) self.assert_called_with( 'SoftLayer_Virtual_Guest', 'setPrivateNetworkInterfaceSpeed', args=(100,), identifier=100, ) def test_ready(self): mock = self.set_mock('SoftLayer_Virtual_Guest', 'getObject') mock.return_value = { "provisionDate": "2017-10-17T11:21:53-07:00", "id": 41957081 } result = self.run_command(['vs', 'ready', '100']) self.assert_no_fail(result) self.assertEqual(result.output, '"READY"\n') def test_not_ready(self): mock = self.set_mock('SoftLayer_Virtual_Guest', 'getObject') not_ready = { 'activeTransaction': { 'transactionStatus': {'friendlyName': 'Attach Primary Disk'} }, 'provisionDate': '', 'id': 47392219 } ready = { "provisionDate": "2017-10-17T11:21:53-07:00", "id": 41957081 } mock.side_effect = [not_ready, ready] result = self.run_command(['vs', 'ready', '100']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.CLIAbort) @mock.patch('time.sleep') def test_going_ready(self, _sleep): mock = self.set_mock('SoftLayer_Virtual_Guest', 'getObject') not_ready = { 'activeTransaction': { 'transactionStatus': {'friendlyName': 'Attach Primary Disk'} }, 'provisionDate': '', 'id': 47392219 } ready = { "provisionDate": "2017-10-17T11:21:53-07:00", "id": 41957081 } mock.side_effect = [not_ready, ready] result = self.run_command(['vs', 'ready', '100', '--wait=100']) self.assert_no_fail(result) self.assertEqual(result.output, '"READY"\n') softlayer-python-5.4.2/tests/__init__.py000066400000000000000000000000001324365065500203230ustar00rootroot00000000000000softlayer-python-5.4.2/tests/api_tests.py000066400000000000000000000255061324365065500206010ustar00rootroot00000000000000""" SoftLayer.tests.api_tests ~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import mock import SoftLayer import SoftLayer.API from SoftLayer import testing from SoftLayer import transports class Inititialization(testing.TestCase): def test_init(self): client = SoftLayer.Client(username='doesnotexist', api_key='issurelywrong', timeout=10, endpoint_url='http://example.com/v3/xmlrpc/') self.assertIsInstance(client.auth, SoftLayer.BasicAuthentication) self.assertEqual(client.auth.username, 'doesnotexist') self.assertEqual(client.auth.api_key, 'issurelywrong') self.assertIsNotNone(client.transport) self.assertIsInstance(client.transport, transports.XmlRpcTransport) self.assertEqual(client.transport.timeout, 10) def test_init_with_rest_url(self): client = SoftLayer.Client(username='doesnotexist', api_key='issurelywrong', timeout=10, endpoint_url='http://example.com/v3/rest/') self.assertIsInstance(client.auth, SoftLayer.BasicHTTPAuthentication) self.assertEqual(client.auth.username, 'doesnotexist') self.assertEqual(client.auth.api_key, 'issurelywrong') self.assertIsNotNone(client.transport) self.assertIsInstance(client.transport, transports.RestTransport) self.assertEqual(client.transport.endpoint_url, 'http://example.com/v3/rest') self.assertEqual(client.transport.timeout, 10) @mock.patch('SoftLayer.config.get_client_settings') def test_env(self, get_client_settings): auth = mock.Mock() get_client_settings.return_value = { 'timeout': 10, 'endpoint_url': 'http://endpoint_url/', } client = SoftLayer.Client(auth=auth) self.assertEqual(client.auth.get_headers(), auth.get_headers()) self.assertEqual(client.transport.timeout, 10) self.assertEqual(client.transport.endpoint_url, 'http://endpoint_url') class ClientMethods(testing.TestCase): def test_repr(self): client = SoftLayer.Client( username='doesnotexist', api_key='issurelywrong' ) self.assertIn("Client", repr(client)) def test_service_repr(self): client = SoftLayer.Client( username='doesnotexist', api_key='issurelywrong' ) self.assertIn("Service", repr(client['SERVICE'])) def test_len(self): client = SoftLayer.Client( username='doesnotexist', api_key='issurelywrong' ) self.assertEqual(len(client), 0) class APIClient(testing.TestCase): def test_simple_call(self): mock = self.set_mock('SoftLayer_SERVICE', 'METHOD') mock.return_value = {"test": "result"} resp = self.client['SERVICE'].METHOD() self.assertEqual(resp, {"test": "result"}) self.assert_called_with('SoftLayer_SERVICE', 'METHOD', mask=None, filter=None, identifier=None, args=tuple(), limit=None, offset=None, ) def test_verify_request_false(self): client = SoftLayer.BaseClient(transport=self.mocks) mock = self.set_mock('SoftLayer_SERVICE', 'METHOD') mock.return_value = {"test": "result"} resp = client.call('SERVICE', 'METHOD', verify=False) self.assertEqual(resp, {"test": "result"}) self.assert_called_with('SoftLayer_SERVICE', 'METHOD', verify=False) def test_verify_request_true(self): client = SoftLayer.BaseClient(transport=self.mocks) mock = self.set_mock('SoftLayer_SERVICE', 'METHOD') mock.return_value = {"test": "result"} resp = client.call('SERVICE', 'METHOD', verify=True) self.assertEqual(resp, {"test": "result"}) self.assert_called_with('SoftLayer_SERVICE', 'METHOD', verify=True) def test_verify_request_not_specified(self): client = SoftLayer.BaseClient(transport=self.mocks) mock = self.set_mock('SoftLayer_SERVICE', 'METHOD') mock.return_value = {"test": "result"} resp = client.call('SERVICE', 'METHOD') self.assertEqual(resp, {"test": "result"}) self.assert_called_with('SoftLayer_SERVICE', 'METHOD', verify=None) @mock.patch('SoftLayer.API.BaseClient.iter_call') def test_iterate(self, _iter_call): self.client['SERVICE'].METHOD(iter=True) _iter_call.assert_called_with('SERVICE', 'METHOD') @mock.patch('SoftLayer.API.BaseClient.iter_call') def test_service_iter_call(self, _iter_call): self.client['SERVICE'].iter_call('METHOD', 'ARG') _iter_call.assert_called_with('SERVICE', 'METHOD', 'ARG') @mock.patch('SoftLayer.API.BaseClient.iter_call') def test_service_iter_call_with_chunk(self, _iter_call): self.client['SERVICE'].iter_call('METHOD', 'ARG', chunk=2) _iter_call.assert_called_with('SERVICE', 'METHOD', 'ARG', chunk=2) @mock.patch('SoftLayer.API.BaseClient.call') def test_iter_call(self, _call): # chunk=100, no limit _call.side_effect = [list(range(100)), list(range(100, 125))] result = list(self.client.iter_call('SERVICE', 'METHOD', iter=True)) self.assertEqual(list(range(125)), result) _call.assert_has_calls([ mock.call('SERVICE', 'METHOD', limit=100, iter=False, offset=0), mock.call('SERVICE', 'METHOD', limit=100, iter=False, offset=100), ]) _call.reset_mock() # chunk=100, no limit. Requires one extra request. _call.side_effect = [list(range(100)), list(range(100, 200)), []] result = list(self.client.iter_call('SERVICE', 'METHOD', iter=True)) self.assertEqual(list(range(200)), result) _call.assert_has_calls([ mock.call('SERVICE', 'METHOD', limit=100, iter=False, offset=0), mock.call('SERVICE', 'METHOD', limit=100, iter=False, offset=100), mock.call('SERVICE', 'METHOD', limit=100, iter=False, offset=200), ]) _call.reset_mock() # chunk=25, limit=30 _call.side_effect = [list(range(0, 25)), list(range(25, 30))] result = list(self.client.iter_call( 'SERVICE', 'METHOD', iter=True, limit=30, chunk=25)) self.assertEqual(list(range(30)), result) _call.assert_has_calls([ mock.call('SERVICE', 'METHOD', iter=False, limit=25, offset=0), mock.call('SERVICE', 'METHOD', iter=False, limit=5, offset=25), ]) _call.reset_mock() # A non-list was returned _call.side_effect = ["test"] result = list(self.client.iter_call('SERVICE', 'METHOD', iter=True)) self.assertEqual(["test"], result) _call.assert_has_calls([ mock.call('SERVICE', 'METHOD', iter=False, limit=100, offset=0), ]) _call.reset_mock() # chunk=25, limit=30, offset=12 _call.side_effect = [list(range(0, 25)), list(range(25, 30))] result = list(self.client.iter_call('SERVICE', 'METHOD', 'ARG', iter=True, limit=30, chunk=25, offset=12)) self.assertEqual(list(range(30)), result) _call.assert_has_calls([ mock.call('SERVICE', 'METHOD', 'ARG', iter=False, limit=25, offset=12), mock.call('SERVICE', 'METHOD', 'ARG', iter=False, limit=5, offset=37), ]) # Chunk size of 0 is invalid self.assertRaises( AttributeError, lambda: list(self.client.iter_call('SERVICE', 'METHOD', iter=True, chunk=0))) def test_call_invalid_arguments(self): self.assertRaises( TypeError, self.client.call, 'SERVICE', 'METHOD', invalid_kwarg='invalid') def test_call_compression_disabled(self): mocked = self.set_mock('SoftLayer_SERVICE', 'METHOD') mocked.return_value = {} self.client['SERVICE'].METHOD(compress=False) calls = self.calls('SoftLayer_SERVICE', 'METHOD') self.assertEqual(len(calls), 1) headers = calls[0].transport_headers self.assertEqual(headers.get('accept-encoding'), 'identity') def test_call_compression_enabled(self): mocked = self.set_mock('SoftLayer_SERVICE', 'METHOD') mocked.return_value = {} self.client['SERVICE'].METHOD(compress=True) calls = self.calls('SoftLayer_SERVICE', 'METHOD') self.assertEqual(len(calls), 1) headers = calls[0].transport_headers self.assertEqual(headers.get('accept-encoding'), 'gzip, deflate, compress') def test_call_compression_override(self): # raw_headers should override compress=False mocked = self.set_mock('SoftLayer_SERVICE', 'METHOD') mocked.return_value = {} self.client['SERVICE'].METHOD( compress=False, raw_headers={'Accept-Encoding': 'gzip'}) calls = self.calls('SoftLayer_SERVICE', 'METHOD') self.assertEqual(len(calls), 1) headers = calls[0].transport_headers self.assertEqual(headers.get('accept-encoding'), 'gzip') class UnauthenticatedAPIClient(testing.TestCase): def set_up(self): self.client = SoftLayer.Client(endpoint_url="ENDPOINT") @mock.patch('SoftLayer.config.get_client_settings') def test_init(self, get_client_settings): get_client_settings.return_value = {} client = SoftLayer.Client() self.assertIsNone(client.auth) @mock.patch('SoftLayer.config.get_client_settings') def test_init_with_proxy(self, get_client_settings): get_client_settings.return_value = {'proxy': 'http://localhost:3128'} client = SoftLayer.Client() self.assertEqual(client.transport.proxy, 'http://localhost:3128') @mock.patch('SoftLayer.API.BaseClient.call') def test_authenticate_with_password(self, _call): _call.return_value = { 'userId': 12345, 'hash': 'TOKEN', } self.client.authenticate_with_password('USERNAME', 'PASSWORD') _call.assert_called_with( 'User_Customer', 'getPortalLoginToken', 'USERNAME', 'PASSWORD', None, None) self.assertIsNotNone(self.client.auth) self.assertEqual(self.client.auth.user_id, 12345) self.assertEqual(self.client.auth.auth_token, 'TOKEN') softlayer-python-5.4.2/tests/auth_tests.py000066400000000000000000000050301324365065500207570ustar00rootroot00000000000000""" SoftLayer.tests.auth_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ from SoftLayer import auth from SoftLayer import testing from SoftLayer import transports class TestAuthenticationBase(testing.TestCase): def test_get_request(self): auth_base = auth.AuthenticationBase() self.assertEqual(auth_base.get_request({}), {}) self.assertEqual(auth_base.get_headers(), {}) class TestBasicAuthentication(testing.TestCase): def set_up(self): self.auth = auth.BasicAuthentication('USERNAME', 'APIKEY') def test_attribs(self): self.assertEqual(self.auth.username, 'USERNAME') self.assertEqual(self.auth.api_key, 'APIKEY') def test_get_request(self): req = transports.Request() authed_req = self.auth.get_request(req) self.assertEqual(authed_req.headers, { 'authenticate': { 'username': 'USERNAME', 'apiKey': 'APIKEY', } }) def test_repr(self): s = repr(self.auth) self.assertIn('BasicAuthentication', s) self.assertIn('USERNAME', s) class TestTokenAuthentication(testing.TestCase): def set_up(self): self.auth = auth.TokenAuthentication(12345, 'TOKEN') def test_attribs(self): self.assertEqual(self.auth.user_id, 12345) self.assertEqual(self.auth.auth_token, 'TOKEN') def test_get_request(self): req = transports.Request() authed_req = self.auth.get_request(req) self.assertEqual(authed_req.headers, { 'authenticate': { 'complexType': 'PortalLoginToken', 'userId': 12345, 'authToken': 'TOKEN', } }) def test_repr(self): s = repr(self.auth) self.assertIn('TokenAuthentication', s) self.assertIn('12345', s) class TestBasicHTTPAuthentication(testing.TestCase): def set_up(self): self.auth = auth.BasicHTTPAuthentication('USERNAME', 'APIKEY') def test_attribs(self): self.assertEqual(self.auth.username, 'USERNAME') self.assertEqual(self.auth.api_key, 'APIKEY') def test_get_request(self): req = transports.Request() authed_req = self.auth.get_request(req) self.assertEqual(authed_req.transport_user, 'USERNAME') self.assertEqual(authed_req.transport_password, 'APIKEY') def test_repr(self): s = repr(self.auth) self.assertIn('BasicHTTPAuthentication', s) self.assertIn('USERNAME', s) softlayer-python-5.4.2/tests/basic_tests.py000066400000000000000000000134021324365065500211010ustar00rootroot00000000000000""" SoftLayer.tests.basic_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Tests shared code :license: MIT, see LICENSE for more details. """ import datetime import SoftLayer from SoftLayer import testing class TestExceptions(testing.TestCase): def test_softlayer_api_error(self): e = SoftLayer.SoftLayerAPIError('fault code', 'fault string') self.assertEqual(e.faultCode, 'fault code') self.assertEqual(e.faultString, 'fault string') self.assertEqual(e.reason, 'fault string') self.assertEqual( repr(e), "") self.assertEqual( str(e), "SoftLayerAPIError(fault code): fault string") def test_parse_error(self): e = SoftLayer.ParseError('fault code', 'fault string') self.assertEqual(e.faultCode, 'fault code') self.assertEqual(e.faultString, 'fault string') self.assertEqual(e.reason, 'fault string') self.assertEqual( repr(e), "") self.assertEqual( str(e), "ParseError(fault code): fault string") class TestUtils(testing.TestCase): def test_query_filter(self): result = SoftLayer.utils.query_filter('test') self.assertEqual({'operation': '_= test'}, result) result = SoftLayer.utils.query_filter('~ test') self.assertEqual({'operation': '~ test'}, result) result = SoftLayer.utils.query_filter('*test') self.assertEqual({'operation': '$= test'}, result) result = SoftLayer.utils.query_filter('test*') self.assertEqual({'operation': '^= test'}, result) result = SoftLayer.utils.query_filter('*test*') self.assertEqual({'operation': '*= test'}, result) result = SoftLayer.utils.query_filter('> 10') self.assertEqual({'operation': '> 10'}, result) result = SoftLayer.utils.query_filter('>10') self.assertEqual({'operation': '> 10'}, result) result = SoftLayer.utils.query_filter(10) self.assertEqual({'operation': 10}, result) def test_query_filter_date(self): result = SoftLayer.utils.query_filter_date("2018-01-01", "2018-01-02") expected = { 'operation': 'betweenDate', 'options': [ {'name': 'startDate', 'value': ['1/1/2018 0:0:0']}, {'name': 'endDate', 'value': ['1/2/2018 0:0:0']} ] } self.assertEqual(expected, result) def test_timezone(self): utc = SoftLayer.utils.UTC() time = datetime.datetime(2018, 1, 1, tzinfo=utc) self.assertEqual('2018-01-01 00:00:00+00:00', time.__str__()) self.assertEqual('UTC', time.tzname()) self.assertEqual(datetime.timedelta(0), time.dst()) self.assertEqual(datetime.timedelta(0), time.utcoffset()) class TestNestedDict(testing.TestCase): def test_basic(self): n = SoftLayer.utils.NestedDict() self.assertEqual(n['test'], SoftLayer.utils.NestedDict()) n['test_set'] = 1 self.assertEqual(n['test_set'], 1) d = { 'test': { 'nested': 1 }} n = SoftLayer.utils.NestedDict(d) self.assertEqual(d, n) self.assertEqual(n['test']['nested'], 1) # new default top level elements should return a new NestedDict() self.assertEqual(n['not']['nested'], SoftLayer.utils.NestedDict()) # NestedDict doesn't convert dict children, just the top level dict # so you can't assume the same behavior with children self.assertRaises(KeyError, lambda: n['test']['not']['nested']) def test_to_dict(self): n = SoftLayer.utils.NestedDict() n['test']['test1']['test2']['test3'] = {} d = n.to_dict() self.assertEqual({ 'test': {'test1': {'test2': {'test3': {}}}} }, d) self.assertEqual(dict, type(d)) self.assertEqual(dict, type(d['test'])) self.assertEqual(dict, type(d['test']['test1'])) self.assertEqual(dict, type(d['test']['test1']['test2'])) self.assertEqual(dict, type(d['test']['test1']['test2']['test3'])) class TestLookup(testing.TestCase): def test_lookup(self): d = {'test': {'nested': 1}} val = SoftLayer.utils.lookup(d, 'test') self.assertEqual(val, {'nested': 1}) val = SoftLayer.utils.lookup(d, 'test', 'nested') self.assertEqual(val, 1) val = SoftLayer.utils.lookup(d, 'test1') self.assertEqual(val, None) val = SoftLayer.utils.lookup(d, 'test1', 'nested1') self.assertEqual(val, None) def is_a(string): if string == 'a': return ['this', 'is', 'a'] def is_b(string): if string == 'b': return ['this', 'is', 'b'] class IdentifierFixture(SoftLayer.utils.IdentifierMixin): resolvers = [is_a, is_b] class TestIdentifierMixin(testing.TestCase): def set_up(self): self.fixture = IdentifierFixture() def test_integer(self): ids = self.fixture.resolve_ids(1234) self.assertEqual(ids, [1234]) def test_a(self): ids = self.fixture.resolve_ids('a') self.assertEqual(ids, ['this', 'is', 'a']) def test_b(self): ids = self.fixture.resolve_ids('b') self.assertEqual(ids, ['this', 'is', 'b']) def test_not_found(self): ids = self.fixture.resolve_ids('something') self.assertEqual(ids, []) def test_globalidentifier(self): ids = self.fixture.resolve_ids('9d888bc2-7c9a-4dba-bbd8-6bd688687bae') self.assertEqual(ids, ['9d888bc2-7c9a-4dba-bbd8-6bd688687bae']) def test_globalidentifier_upper(self): ids = self.fixture.resolve_ids('B534EF96-55C4-4891-B51A-63866411B58E') self.assertEqual(ids, ['B534EF96-55C4-4891-B51A-63866411B58E']) softlayer-python-5.4.2/tests/config_tests.py000066400000000000000000000071321324365065500212700ustar00rootroot00000000000000""" SoftLayer.tests.config_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import mock from SoftLayer import config from SoftLayer import testing class TestGetClientSettings(testing.TestCase): @mock.patch('SoftLayer.config.SETTING_RESOLVERS', []) def test_no_resolvers(self): result = config.get_client_settings() self.assertEqual(result, {}) def test_resolve_one(self): resolvers = [mock.Mock() for i in range(1)] resolvers[0].return_value = {'auth': 'AUTH HANDLER'} with mock.patch('SoftLayer.config.SETTING_RESOLVERS', resolvers): result = config.get_client_settings() self.assertEqual(result, {'auth': 'AUTH HANDLER'}) def test_inherit(self): # This tests the inheritting properties of the list of resolvers. # Values should be preferred on earlier resolvers except where their # value is false-ish resolvers = [mock.Mock() for i in range(4)] resolvers[0].return_value = {'timeout': 20} resolvers[1].return_value = {'timeout': 10, 'auth': None} resolvers[2].return_value = None resolvers[3].return_value = {'auth': 'AUTH HANDLER'} with mock.patch('SoftLayer.config.SETTING_RESOLVERS', resolvers): result = config.get_client_settings() self.assertEqual(result, {'auth': 'AUTH HANDLER', 'timeout': 20}) class TestGetClientSettingsArgs(testing.TestCase): def test_username_api_key(self): result = config.get_client_settings_args( username='username', api_key='api_key', endpoint_url='http://endpoint/', timeout=10, proxy='https://localhost:3128') self.assertEqual(result['endpoint_url'], 'http://endpoint/') self.assertEqual(result['timeout'], 10) self.assertEqual(result['username'], 'username') self.assertEqual(result['api_key'], 'api_key') self.assertEqual(result['proxy'], 'https://localhost:3128') class TestGetClientSettingsEnv(testing.TestCase): @mock.patch.dict('os.environ', {'SL_USERNAME': 'username', 'SL_API_KEY': 'api_key', 'https_proxy': 'https://localhost:3128'}) def test_username_api_key(self): result = config.get_client_settings_env() self.assertEqual(result['username'], 'username') self.assertEqual(result['api_key'], 'api_key') class TestGetClientSettingsConfigFile(testing.TestCase): @mock.patch('six.moves.configparser.RawConfigParser') def test_username_api_key(self, config_parser): result = config.get_client_settings_config_file() self.assertEqual(result['endpoint_url'], config_parser().get()) self.assertEqual(result['timeout'], config_parser().getfloat()) self.assertEqual(result['proxy'], config_parser().get()) self.assertEqual(result['username'], config_parser().get()) self.assertEqual(result['api_key'], config_parser().get()) @mock.patch('six.moves.configparser.RawConfigParser') def test_no_section(self, config_parser): config_parser().has_section.return_value = False result = config.get_client_settings_config_file() self.assertIsNone(result) @mock.patch('six.moves.configparser.RawConfigParser') def test_config_file(config_parser): config.get_client_settings_config_file(config_file='path/to/config') config_parser().read.assert_called_with([mock.ANY, mock.ANY, 'path/to/config']) softlayer-python-5.4.2/tests/conftest.py000066400000000000000000000000711324365065500204210ustar00rootroot00000000000000import logging logging.basicConfig(level=logging.DEBUG) softlayer-python-5.4.2/tests/decoration_tests.py000066400000000000000000000051641324365065500221550ustar00rootroot00000000000000""" SoftLayer.tests.decoration_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import logging import mock from SoftLayer.decoration import retry from SoftLayer import exceptions from SoftLayer import testing class TestDecoration(testing.TestCase): def setUp(self): super(TestDecoration, self).setUp() self.patcher = mock.patch('SoftLayer.decoration.sleep') self.patcher.return_value = False self.patcher.start() self.addCleanup(self.patcher.stop) self.counter = 0 def test_no_retry_required(self): @retry(exceptions.SoftLayerError, tries=4) def succeeds(): self.counter += 1 return 'success' r = succeeds() self.assertEqual(r, 'success') self.assertEqual(self.counter, 1) @mock.patch('SoftLayer.decoration.randint') def test_retries_once(self, _random): _random.side_effect = [0, 0, 0, 0] @retry(exceptions.SoftLayerError, tries=4, logger=logging.getLogger(__name__)) def fails_once(): self.counter += 1 if self.counter < 2: raise exceptions.SoftLayerError('failed') else: return 'success' with self.assertLogs(__name__, level='WARNING') as log: r = fails_once() self.assertEqual(log.output, ["WARNING:tests.decoration_tests:failed, Retrying in 5 seconds..."]) self.assertEqual(r, 'success') self.assertEqual(self.counter, 2) def test_limit_is_reached(self): @retry(exceptions.SoftLayerError, tries=4) def always_fails(): self.counter += 1 raise exceptions.SoftLayerError('failed!') self.assertRaises(exceptions.SoftLayerError, always_fails) self.assertEqual(self.counter, 4) def test_multiple_exception_types(self): @retry((exceptions.SoftLayerError, TypeError), tries=4) def raise_multiple_exceptions(): self.counter += 1 if self.counter == 1: raise exceptions.SoftLayerError('a retryable error') elif self.counter == 2: raise TypeError('another retryable error') else: return 'success' r = raise_multiple_exceptions() self.assertEqual(r, 'success') self.assertEqual(self.counter, 3) def test_unexpected_exception_does_not_retry(self): @retry(exceptions.SoftLayerError, tries=4) def raise_unexpected_error(): raise TypeError('unexpected error') self.assertRaises(TypeError, raise_unexpected_error) softlayer-python-5.4.2/tests/functional_tests.py000066400000000000000000000066101324365065500221650ustar00rootroot00000000000000""" SoftLayer.tests.functional_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import os import SoftLayer from SoftLayer import testing from SoftLayer import transports class FunctionalTest(testing.TestCase): def _get_creds(self): for key in 'SL_USERNAME SL_API_KEY'.split(): if key not in os.environ: raise self.skipTest('SL_USERNAME and SL_API_KEY environmental ' 'variables not set') return { 'endpoint': (os.environ.get('SL_API_ENDPOINT') or SoftLayer.API_PUBLIC_ENDPOINT), 'username': os.environ['SL_USERNAME'], 'api_key': os.environ['SL_API_KEY'] } class UnauthedUser(FunctionalTest): def test_failed_auth(self): client = SoftLayer.Client( username='doesnotexist', api_key='issurelywrong', timeout=20) self.assertRaises( SoftLayer.SoftLayerAPIError, client['SoftLayer_User_Customer'].getPortalLoginToken) def test_no_hostname(self): try: request = transports.Request() request.service = 'SoftLayer_Account' request.method = 'getObject' request.id = 1234 # This test will fail if 'notvalidsoftlayer.com' becomes a thing transport = transports.XmlRpcTransport( endpoint_url='http://notvalidsoftlayer.com', ) transport(request) except SoftLayer.TransportError as ex: self.assertEqual(ex.faultCode, 0) else: self.fail('Transport Error Exception Not Raised') class AuthedUser(FunctionalTest): def test_service_does_not_exist(self): creds = self._get_creds() client = SoftLayer.Client( username=creds['username'], api_key=creds['api_key'], endpoint_url=creds['endpoint'], timeout=20) try: client["SoftLayer_DOESNOTEXIST"].getObject() except SoftLayer.SoftLayerAPIError as e: self.assertEqual(e.faultCode, '-32601') self.assertEqual(e.faultString, 'Service does not exist') self.assertEqual(e.reason, 'Service does not exist') else: self.fail('No Exception Raised') def test_get_users(self): creds = self._get_creds() client = SoftLayer.Client( username=creds['username'], api_key=creds['api_key'], endpoint_url=creds['endpoint'], timeout=20) found = False results = client["Account"].getUsers() for user in results: if user.get('username') == creds['username']: found = True self.assertTrue(found) def test_result_types(self): creds = self._get_creds() client = SoftLayer.Client( username=creds['username'], api_key=creds['api_key'], endpoint_url=creds['endpoint'], timeout=20) result = client['SoftLayer_User_Security_Question'].getAllObjects() self.assertIsInstance(result, list) self.assertIsInstance(result[0], dict) self.assertIsInstance(result[0]['viewable'], int) self.assertIsInstance(result[0]['question'], str) self.assertIsInstance(result[0]['id'], int) self.assertIsInstance(result[0]['displayOrder'], int) softlayer-python-5.4.2/tests/managers/000077500000000000000000000000001324365065500200215ustar00rootroot00000000000000softlayer-python-5.4.2/tests/managers/__init__.py000066400000000000000000000000001324365065500221200ustar00rootroot00000000000000softlayer-python-5.4.2/tests/managers/block_tests.py000066400000000000000000000773731324365065500227300ustar00rootroot00000000000000""" SoftLayer.tests.managers.block_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import copy import SoftLayer from SoftLayer import exceptions from SoftLayer import fixtures from SoftLayer import testing class BlockTests(testing.TestCase): def set_up(self): self.block = SoftLayer.BlockStorageManager(self.client) def test_cancel_block_volume_immediately(self): self.block.cancel_block_volume(123, immediate=True) self.assert_called_with( 'SoftLayer_Billing_Item', 'cancelItem', args=(True, True, 'No longer needed'), identifier=449, ) def test_cancel_block_volume_immediately_hourly_billing(self): mock = self.set_mock('SoftLayer_Network_Storage', 'getObject') mock.return_value = { 'billingItem': {'hourlyFlag': True, 'id': 449}, } self.block.cancel_block_volume(123, immediate=False) self.assert_called_with( 'SoftLayer_Billing_Item', 'cancelItem', args=(True, True, 'No longer needed'), identifier=449, ) def test_get_block_volume_details(self): result = self.block.get_block_volume_details(100) self.assertEqual(fixtures.SoftLayer_Network_Storage.getObject, result) expected_mask = 'id,'\ 'username,'\ 'password,'\ 'capacityGb,'\ 'snapshotCapacityGb,'\ 'parentVolume.snapshotSizeBytes,'\ 'storageType.keyName,'\ 'serviceResource.datacenter[name],'\ 'serviceResourceBackendIpAddress,'\ 'storageTierLevel,'\ 'provisionedIops,'\ 'lunId,'\ 'originalVolumeName,'\ 'originalSnapshotName,'\ 'originalVolumeSize,'\ 'activeTransactionCount,'\ 'activeTransactions.transactionStatus[friendlyName],'\ 'replicationPartnerCount,'\ 'replicationStatus,'\ 'replicationPartners[id,username,'\ 'serviceResourceBackendIpAddress,'\ 'serviceResource[datacenter[name]],'\ 'replicationSchedule[type[keyname]]]' self.assert_called_with( 'SoftLayer_Network_Storage', 'getObject', identifier=100, mask='mask[%s]' % expected_mask ) def test_list_block_volumes(self): result = self.block.list_block_volumes() self.assertEqual(fixtures.SoftLayer_Account.getIscsiNetworkStorage, result) expected_filter = { 'iscsiNetworkStorage': { 'storageType': { 'keyName': {'operation': '*= BLOCK_STORAGE'} }, 'serviceResource': { 'type': { 'type': {'operation': '!~ ISCSI'} } } } } expected_mask = 'id,'\ 'username,'\ 'lunId,'\ 'capacityGb,'\ 'bytesUsed,'\ 'serviceResource.datacenter[name],'\ 'serviceResourceBackendIpAddress,'\ 'activeTransactionCount,'\ 'replicationPartnerCount' self.assert_called_with( 'SoftLayer_Account', 'getIscsiNetworkStorage', filter=expected_filter, mask='mask[%s]' % expected_mask ) def test_list_block_volumes_with_additional_filters(self): result = self.block.list_block_volumes(datacenter="dal09", storage_type="Endurance", username="username") self.assertEqual(fixtures.SoftLayer_Account.getIscsiNetworkStorage, result) expected_filter = { 'iscsiNetworkStorage': { 'storageType': { 'keyName': {'operation': '^= ENDURANCE_BLOCK_STORAGE'} }, 'username': {'operation': u'_= username'}, 'serviceResource': { 'datacenter': { 'name': {'operation': u'_= dal09'} }, 'type': { 'type': {'operation': '!~ ISCSI'} } } } } expected_mask = 'id,'\ 'username,'\ 'lunId,'\ 'capacityGb,'\ 'bytesUsed,'\ 'serviceResource.datacenter[name],'\ 'serviceResourceBackendIpAddress,'\ 'activeTransactionCount,'\ 'replicationPartnerCount' self.assert_called_with( 'SoftLayer_Account', 'getIscsiNetworkStorage', filter=expected_filter, mask='mask[%s]' % expected_mask ) def test_get_block_volume_access_list(self): result = self.block.get_block_volume_access_list(100) self.assertEqual(fixtures.SoftLayer_Network_Storage.getObject, result) self.assert_called_with( 'SoftLayer_Network_Storage', 'getObject', identifier=100) def test_get_block_volume_snapshot_list(self): result = self.block.get_block_volume_snapshot_list(100) self.assertEqual(fixtures.SoftLayer_Network_Storage.getSnapshots, result) self.assert_called_with( 'SoftLayer_Network_Storage', 'getSnapshots', identifier=100) def test_delete_snapshot(self): result = self.block.delete_snapshot(100) self.assertEqual(fixtures.SoftLayer_Network_Storage.deleteObject, result) self.assert_called_with( 'SoftLayer_Network_Storage', 'deleteObject', identifier=100) def test_cancel_snapshot_immediately(self): self.block.cancel_snapshot_space(1234, immediate=True) self.assert_called_with( 'SoftLayer_Billing_Item', 'cancelItem', args=(True, True, 'No longer needed'), identifier=123, ) def test_cancel_snapshot_hourly_billing_immediate_true(self): mock = self.set_mock('SoftLayer_Network_Storage', 'getObject') mock.return_value = { 'billingItem': { 'activeChildren': [ {'categoryCode': 'storage_snapshot_space', 'id': 417} ], 'hourlyFlag': True, 'id': 449 }, } self.block.cancel_snapshot_space(1234, immediate=True) self.assert_called_with( 'SoftLayer_Billing_Item', 'cancelItem', args=(True, True, 'No longer needed'), identifier=417, ) def test_cancel_snapshot_hourly_billing_immediate_false(self): mock = self.set_mock('SoftLayer_Network_Storage', 'getObject') mock.return_value = { 'billingItem': { 'activeChildren': [ {'categoryCode': 'storage_snapshot_space', 'id': 417} ], 'hourlyFlag': True, 'id': 449 }, } self.block.cancel_snapshot_space(1234, immediate=False) self.assert_called_with( 'SoftLayer_Billing_Item', 'cancelItem', args=(True, True, 'No longer needed'), identifier=417, ) def test_cancel_snapshot_exception_no_billing_item_active_children(self): mock = self.set_mock('SoftLayer_Network_Storage', 'getObject') mock.return_value = { 'capacityGb': 20, 'snapshotCapacityGb': '10', 'schedules': [{ 'id': 7770, 'type': {'keyname': 'SNAPSHOT_WEEKLY'} }], 'billingItem': { 'categoryCode': 'storage_service_enterprise', 'cancellationDate': '2016-09-04T22:00:00-07:00' } } exception = self.assertRaises( exceptions.SoftLayerError, self.block.cancel_snapshot_space, 12345, immediate=True ) self.assertEqual( 'No snapshot space found to cancel', str(exception) ) def test_cancel_snapshot_exception_snapshot_billing_item_not_found(self): mock = self.set_mock('SoftLayer_Network_Storage', 'getObject') mock.return_value = { 'capacityGb': 20, 'snapshotCapacityGb': '10', 'schedules': [{ 'id': 7770, 'type': {'keyname': 'SNAPSHOT_WEEKLY'} }], 'billingItem': { 'activeChildren': [] } } exception = self.assertRaises( exceptions.SoftLayerError, self.block.cancel_snapshot_space, 12345, immediate=True ) self.assertEqual( 'No snapshot space found to cancel', str(exception) ) def test_replicant_failover(self): result = self.block.failover_to_replicant(1234, 5678, immediate=True) self.assertEqual( fixtures.SoftLayer_Network_Storage.failoverToReplicant, result) self.assert_called_with( 'SoftLayer_Network_Storage', 'failoverToReplicant', args=(5678, True), identifier=1234, ) def test_replicant_failback(self): result = self.block.failback_from_replicant(1234, 5678) self.assertEqual( fixtures.SoftLayer_Network_Storage.failbackFromReplicant, result) self.assert_called_with( 'SoftLayer_Network_Storage', 'failbackFromReplicant', args=(5678,), identifier=1234, ) def test_get_replication_partners(self): self.block.get_replication_partners(1234) self.assert_called_with( 'SoftLayer_Network_Storage', 'getReplicationPartners', identifier=1234, ) def test_get_replication_locations(self): self.block.get_replication_locations(1234) self.assert_called_with( 'SoftLayer_Network_Storage', 'getValidReplicationTargetDatacenterLocations', identifier=1234, ) def test_order_block_volume_performance(self): mock = self.set_mock('SoftLayer_Location_Datacenter', 'getDatacenters') mock.return_value = [{'id': 449494, 'name': 'dal09'}] mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['storageType']['keyName'] = 'PERFORMANCE_BLOCK_STORAGE' mock = self.set_mock('SoftLayer_Network_Storage', 'getObject') mock.return_value = mock_volume result = self.block.order_block_volume( 'performance', 'dal09', 1000, 'LINUX', iops=2000, service_offering='storage_as_a_service' ) self.assertEqual(fixtures.SoftLayer_Product_Order.placeOrder, result) self.assert_called_with( 'SoftLayer_Product_Order', 'placeOrder', args=({ 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_Storage_AsAService', 'packageId': 759, 'prices': [ {'id': 189433}, {'id': 189443}, {'id': 190113}, {'id': 190173} ], 'volumeSize': 1000, 'quantity': 1, 'location': 449494, 'iops': 2000, 'useHourlyPricing': False, 'osFormatType': {'keyName': 'LINUX'} },) ) def test_order_block_volume_endurance(self): mock = self.set_mock('SoftLayer_Location_Datacenter', 'getDatacenters') mock.return_value = [{'id': 449494, 'name': 'dal09'}] mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME mock = self.set_mock('SoftLayer_Network_Storage', 'getObject') mock.return_value = mock_volume result = self.block.order_block_volume( 'endurance', 'dal09', 1000, 'LINUX', tier_level=4, service_offering='storage_as_a_service' ) self.assertEqual(fixtures.SoftLayer_Product_Order.placeOrder, result) self.assert_called_with( 'SoftLayer_Product_Order', 'placeOrder', args=({ 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_Storage_AsAService', 'packageId': 759, 'prices': [ {'id': 189433}, {'id': 189443}, {'id': 194763}, {'id': 194703} ], 'volumeSize': 1000, 'quantity': 1, 'location': 449494, 'useHourlyPricing': False, 'osFormatType': {'keyName': 'LINUX'} },) ) def test_authorize_host_to_volume(self): result = self.block.authorize_host_to_volume( 50, hardware_ids=[100], virtual_guest_ids=[200], ip_address_ids=[300]) self.assertEqual(fixtures.SoftLayer_Network_Storage. allowAccessFromHostList, result) self.assert_called_with( 'SoftLayer_Network_Storage', 'allowAccessFromHostList', identifier=50) def test_deauthorize_host_to_volume(self): result = self.block.deauthorize_host_to_volume( 50, hardware_ids=[100], virtual_guest_ids=[200], ip_address_ids=[300]) self.assertEqual(fixtures.SoftLayer_Network_Storage. removeAccessFromHostList, result) self.assert_called_with( 'SoftLayer_Network_Storage', 'removeAccessFromHostList', identifier=50) def test_create_snapshot(self): result = self.block.create_snapshot(123, 'hello world') self.assertEqual(fixtures.SoftLayer_Network_Storage.createSnapshot, result) self.assert_called_with( 'SoftLayer_Network_Storage', 'createSnapshot', identifier=123) def test_snapshot_restore(self): result = self.block.restore_from_snapshot(12345678, 87654321) self.assertEqual( fixtures.SoftLayer_Network_Storage.restoreFromSnapshot, result) self.assert_called_with( 'SoftLayer_Network_Storage', 'restoreFromSnapshot', identifier=12345678) def test_enable_snapshots(self): result = self.block.enable_snapshots(12345678, 'WEEKLY', 10, 47, 16, 'FRIDAY') self.assertEqual(fixtures.SoftLayer_Network_Storage.enableSnapshots, result) self.assert_called_with( 'SoftLayer_Network_Storage', 'enableSnapshots', identifier=12345678) def test_disable_snapshots(self): result = self.block.disable_snapshots(12345678, 'HOURLY') self.assertEqual(fixtures.SoftLayer_Network_Storage.disableSnapshots, result) self.assert_called_with( 'SoftLayer_Network_Storage', 'disableSnapshots', identifier=12345678) def test_list_volume_schedules(self): result = self.block.list_volume_schedules(12345678) self.assertEqual( fixtures.SoftLayer_Network_Storage.listVolumeSchedules, result) expected_mask = 'schedules[type,properties[type]]' self.assert_called_with( 'SoftLayer_Network_Storage', 'getObject', identifier=12345678, mask='mask[%s]' % expected_mask ) def test_order_block_snapshot_space_upgrade(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['storageType']['keyName'] = 'ENDURANCE_BLOCK_STORAGE' mock = self.set_mock('SoftLayer_Network_Storage', 'getObject') mock.return_value = mock_volume result = self.block.order_snapshot_space(102, 20, None, True) self.assertEqual(fixtures.SoftLayer_Product_Order.placeOrder, result) self.assert_called_with( 'SoftLayer_Product_Order', 'placeOrder', args=({ 'complexType': 'SoftLayer_Container_Product_Order_Network_' 'Storage_Enterprise_SnapshotSpace_Upgrade', 'packageId': 759, 'prices': [ {'id': 193853} ], 'quantity': 1, 'location': 449500, 'volumeId': 102, 'useHourlyPricing': False },) ) def test_order_block_snapshot_space(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME mock = self.set_mock('SoftLayer_Network_Storage', 'getObject') mock.return_value = mock_volume result = self.block.order_snapshot_space(102, 10, None, False) self.assertEqual(fixtures.SoftLayer_Product_Order.placeOrder, result) self.assert_called_with( 'SoftLayer_Product_Order', 'placeOrder', args=({ 'complexType': 'SoftLayer_Container_Product_Order_Network_' 'Storage_Enterprise_SnapshotSpace', 'packageId': 759, 'prices': [ {'id': 193613} ], 'quantity': 1, 'location': 449500, 'volumeId': 102, 'useHourlyPricing': False },) ) def test_order_block_replicant_os_type_not_found(self): mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) del mock_volume['osType'] mock = self.set_mock('SoftLayer_Network_Storage', 'getObject') mock.return_value = mock_volume exception = self.assertRaises( exceptions.SoftLayerError, self.block.order_replicant_volume, 102, 'WEEKLY', 'dal09' ) self.assertEqual( "Cannot find primary volume's os-type " "automatically; must specify manually", str(exception) ) def test_order_block_replicant_performance_os_type_given(self): mock = self.set_mock('SoftLayer_Location_Datacenter', 'getDatacenters') mock.return_value = [{'id': 449494, 'name': 'dal09'}] mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['storageType']['keyName'] = 'PERFORMANCE_BLOCK_STORAGE' mock = self.set_mock('SoftLayer_Network_Storage', 'getObject') mock.return_value = mock_volume result = self.block.order_replicant_volume( 102, 'WEEKLY', 'dal09', os_type='XEN' ) self.assertEqual(fixtures.SoftLayer_Product_Order.placeOrder, result) self.assert_called_with( 'SoftLayer_Product_Order', 'placeOrder', args=({ 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_Storage_AsAService', 'packageId': 759, 'prices': [ {'id': 189433}, {'id': 189443}, {'id': 189993}, {'id': 190053}, {'id': 191193}, {'id': 192033} ], 'volumeSize': 500, 'quantity': 1, 'location': 449494, 'iops': 1000, 'originVolumeId': 102, 'originVolumeScheduleId': 978, 'useHourlyPricing': False, 'osFormatType': {'keyName': 'XEN'} },) ) def test_order_block_replicant_endurance(self): mock = self.set_mock('SoftLayer_Location_Datacenter', 'getDatacenters') mock.return_value = [{'id': 449494, 'name': 'dal09'}] mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME mock = self.set_mock('SoftLayer_Network_Storage', 'getObject') mock.return_value = mock_volume result = self.block.order_replicant_volume(102, 'WEEKLY', 'dal09') self.assertEqual(fixtures.SoftLayer_Product_Order.placeOrder, result) self.assert_called_with( 'SoftLayer_Product_Order', 'placeOrder', args=({ 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_Storage_AsAService', 'packageId': 759, 'prices': [ {'id': 189433}, {'id': 189443}, {'id': 193433}, {'id': 193373}, {'id': 193613}, {'id': 194693} ], 'volumeSize': 500, 'quantity': 1, 'location': 449494, 'originVolumeId': 102, 'originVolumeScheduleId': 978, 'useHourlyPricing': False, 'osFormatType': {'keyName': 'LINUX'} },) ) def test_order_block_duplicate_origin_os_type_not_found(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) del mock_volume['osType'] mock = self.set_mock('SoftLayer_Network_Storage', 'getObject') mock.return_value = mock_volume exception = self.assertRaises( exceptions.SoftLayerError, self.block.order_duplicate_volume, 102 ) self.assertEqual(str(exception), "Cannot find origin volume's os-type") def test_order_block_duplicate_performance_no_duplicate_snapshot(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['storageType']['keyName'] = 'PERFORMANCE_BLOCK_STORAGE' mock = self.set_mock('SoftLayer_Network_Storage', 'getObject') mock.return_value = mock_volume result = self.block.order_duplicate_volume( 102, duplicate_snapshot_size=0) self.assertEqual(fixtures.SoftLayer_Product_Order.placeOrder, result) self.assert_called_with( 'SoftLayer_Product_Order', 'placeOrder', args=({ 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_Storage_AsAService', 'packageId': 759, 'prices': [ {'id': 189433}, {'id': 189443}, {'id': 189993}, {'id': 190053} ], 'volumeSize': 500, 'quantity': 1, 'location': 449500, 'duplicateOriginVolumeId': 102, 'osFormatType': {'keyName': 'LINUX'}, 'iops': 1000, 'useHourlyPricing': False },)) def test_order_block_duplicate_performance(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['storageType']['keyName'] = 'PERFORMANCE_BLOCK_STORAGE' mock = self.set_mock('SoftLayer_Network_Storage', 'getObject') mock.return_value = mock_volume result = self.block.order_duplicate_volume( 102, origin_snapshot_id=470, duplicate_size=1000, duplicate_iops=2000, duplicate_tier_level=None, duplicate_snapshot_size=10 ) self.assertEqual(fixtures.SoftLayer_Product_Order.placeOrder, result) self.assert_called_with( 'SoftLayer_Product_Order', 'placeOrder', args=({ 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_Storage_AsAService', 'packageId': 759, 'prices': [ {'id': 189433}, {'id': 189443}, {'id': 190113}, {'id': 190173}, {'id': 191193} ], 'volumeSize': 1000, 'quantity': 1, 'location': 449500, 'duplicateOriginVolumeId': 102, 'osFormatType': {'keyName': 'LINUX'}, 'duplicateOriginSnapshotId': 470, 'iops': 2000, 'useHourlyPricing': False },)) def test_order_block_duplicate_endurance_no_duplicate_snapshot(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME mock = self.set_mock('SoftLayer_Network_Storage', 'getObject') mock.return_value = mock_volume result = self.block.order_duplicate_volume( 102, duplicate_snapshot_size=0) self.assertEqual(fixtures.SoftLayer_Product_Order.placeOrder, result) self.assert_called_with( 'SoftLayer_Product_Order', 'placeOrder', args=({ 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_Storage_AsAService', 'packageId': 759, 'prices': [ {'id': 189433}, {'id': 189443}, {'id': 193433}, {'id': 193373} ], 'volumeSize': 500, 'quantity': 1, 'location': 449500, 'duplicateOriginVolumeId': 102, 'osFormatType': {'keyName': 'LINUX'}, 'useHourlyPricing': False },)) def test_order_block_duplicate_endurance(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME mock = self.set_mock('SoftLayer_Network_Storage', 'getObject') mock.return_value = mock_volume result = self.block.order_duplicate_volume( 102, origin_snapshot_id=470, duplicate_size=1000, duplicate_iops=None, duplicate_tier_level=4, duplicate_snapshot_size=10 ) self.assertEqual(fixtures.SoftLayer_Product_Order.placeOrder, result) self.assert_called_with( 'SoftLayer_Product_Order', 'placeOrder', args=({ 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_Storage_AsAService', 'packageId': 759, 'prices': [ {'id': 189433}, {'id': 189443}, {'id': 194763}, {'id': 194703}, {'id': 194943} ], 'volumeSize': 1000, 'quantity': 1, 'location': 449500, 'duplicateOriginVolumeId': 102, 'osFormatType': {'keyName': 'LINUX'}, 'duplicateOriginSnapshotId': 470, 'useHourlyPricing': False },)) def test_order_block_modified_performance(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['storageType']['keyName'] = 'PERFORMANCE_BLOCK_STORAGE' mock = self.set_mock('SoftLayer_Network_Storage', 'getObject') mock.return_value = mock_volume result = self.block.order_modified_volume(102, new_size=1000, new_iops=2000, new_tier_level=None) self.assertEqual(fixtures.SoftLayer_Product_Order.placeOrder, result) self.assert_called_with( 'SoftLayer_Product_Order', 'placeOrder', args=({'complexType': 'SoftLayer_Container_Product_Order_Network_Storage_AsAService_Upgrade', 'packageId': 759, 'prices': [{'id': 189433}, {'id': 190113}, {'id': 190173}], 'volume': {'id': 102}, 'volumeSize': 1000, 'iops': 2000},) ) def test_order_block_modified_endurance(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME mock = self.set_mock('SoftLayer_Network_Storage', 'getObject') mock.return_value = mock_volume result = self.block.order_modified_volume(102, new_size=1000, new_iops=None, new_tier_level=4) self.assertEqual(fixtures.SoftLayer_Product_Order.placeOrder, result) self.assert_called_with( 'SoftLayer_Product_Order', 'placeOrder', args=({'complexType': 'SoftLayer_Container_Product_Order_Network_Storage_AsAService_Upgrade', 'packageId': 759, 'prices': [{'id': 189433}, {'id': 194763}, {'id': 194703}], 'volume': {'id': 102}, 'volumeSize': 1000},) ) def test_setCredentialPassword(self): mock = self.set_mock('SoftLayer_Network_Storage_Allowed_Host', 'setCredentialPassword') mock.return_value = True result = self.block.set_credential_password(access_id=102, password='AAAaaa') self.assertEqual(True, result) self.assert_called_with('SoftLayer_Network_Storage_Allowed_Host', 'setCredentialPassword') softlayer-python-5.4.2/tests/managers/cdn_tests.py000066400000000000000000000117011324365065500223610ustar00rootroot00000000000000""" SoftLayer.tests.managers.cdn_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import math from SoftLayer import fixtures from SoftLayer.managers import cdn from SoftLayer import testing class CDNTests(testing.TestCase): def set_up(self): self.cdn_client = cdn.CDNManager(self.client) def test_list_accounts(self): accounts = self.cdn_client.list_accounts() self.assertEqual(accounts, fixtures.SoftLayer_Account.getCdnAccounts) def test_get_account(self): account = self.cdn_client.get_account(12345) self.assertEqual( account, fixtures.SoftLayer_Network_ContentDelivery_Account.getObject) def test_get_origins(self): origins = self.cdn_client.get_origins(12345) self.assertEqual( origins, fixtures.SoftLayer_Network_ContentDelivery_Account. getOriginPullMappingInformation) def test_add_origin(self): self.cdn_client.add_origin(12345, 'http', 'http://localhost/', 'self.local', False) args = ({ 'mediaType': 'http', 'originUrl': 'http://localhost/', 'cname': 'self.local', 'isSecureContent': False },) self.assert_called_with('SoftLayer_Network_ContentDelivery_Account', 'createOriginPullMapping', args=args, identifier=12345) def test_remove_origin(self): self.cdn_client.remove_origin(12345, 12345) self.assert_called_with('SoftLayer_Network_ContentDelivery_Account', 'deleteOriginPullRule', args=(12345,), identifier=12345) def test_load_content(self): urls = ['http://a/img/0x001.png', 'http://b/img/0x002.png', 'http://c/img/0x004.png', 'http://d/img/0x008.png', 'http://e/img/0x010.png', 'http://e/img/0x020.png'] self.cdn_client.load_content(12345, urls) calls = self.calls('SoftLayer_Network_ContentDelivery_Account', 'loadContent') self.assertEqual(len(calls), math.ceil(len(urls) / float(cdn.MAX_URLS_PER_LOAD))) def test_load_content_single(self): url = 'http://geocities.com/Area51/Meteor/12345/under_construction.gif' self.cdn_client.load_content(12345, url) self.assert_called_with('SoftLayer_Network_ContentDelivery_Account', 'loadContent', args=([url],), identifier=12345) def test_load_content_failure(self): urls = ['http://z/img/0x004.png', 'http://y/img/0x002.png', 'http://x/img/0x001.png'] service = self.client['SoftLayer_Network_ContentDelivery_Account'] service.loadContent.return_value = False self.cdn_client.load_content(12345, urls) calls = self.calls('SoftLayer_Network_ContentDelivery_Account', 'loadContent') self.assertEqual(len(calls), math.ceil(len(urls) / float(cdn.MAX_URLS_PER_LOAD))) def test_purge_content(self): urls = ['http://z/img/0x020.png', 'http://y/img/0x010.png', 'http://x/img/0x008.png', 'http://w/img/0x004.png', 'http://v/img/0x002.png', 'http://u/img/0x001.png'] self.cdn_client.purge_content(12345, urls) calls = self.calls('SoftLayer_Network_ContentDelivery_Account', 'purgeCache') self.assertEqual(len(calls), math.ceil(len(urls) / float(cdn.MAX_URLS_PER_PURGE))) def test_purge_content_failure(self): urls = ['http://z/img/0x004.png', 'http://y/img/0x002.png', 'http://x/img/0x001.png'] mock = self.set_mock('SoftLayer_Network_ContentDelivery_Account', 'purgeCache') mock.return_value = False self.cdn_client.purge_content(12345, urls) calls = self.calls('SoftLayer_Network_ContentDelivery_Account', 'purgeCache') self.assertEqual(len(calls), math.ceil(len(urls) / float(cdn.MAX_URLS_PER_PURGE))) def test_purge_content_single(self): url = 'http://geocities.com/Area51/Meteor/12345/under_construction.gif' self.cdn_client.purge_content(12345, url) self.assert_called_with('SoftLayer_Network_ContentDelivery_Account', 'purgeCache', args=([url],), identifier=12345) softlayer-python-5.4.2/tests/managers/dedicated_host_tests.py000066400000000000000000000445551324365065500245750ustar00rootroot00000000000000""" SoftLayer.tests.managers.dedicated_host_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import mock import SoftLayer from SoftLayer import exceptions from SoftLayer import fixtures from SoftLayer import testing class DedicatedHostTests(testing.TestCase): def set_up(self): self.dedicated_host = SoftLayer.DedicatedHostManager(self.client) def test_list_instances(self): results = self.dedicated_host.list_instances() self.assertEqual(results, fixtures.SoftLayer_Account.getDedicatedHosts) self.assert_called_with('SoftLayer_Account', 'getDedicatedHosts') def test_list_instances_with_filters(self): results = self.dedicated_host.list_instances( tags=['tag1', 'tag2'], cpus=2, memory=1, hostname='hostname', datacenter='dal05', disk=1 ) self.assertEqual(results, fixtures.SoftLayer_Account.getDedicatedHosts) def test_get_host(self): self.dedicated_host.host = mock.Mock() self.dedicated_host.host.getObject.return_value = 'test' self.dedicated_host.get_host(12345) mask = (''' id, name, cpuCount, memoryCapacity, diskCapacity, createDate, modifyDate, backendRouter[ id, hostname, domain ], billingItem[ id, nextInvoiceTotalRecurringAmount, children[ categoryCode, nextInvoiceTotalRecurringAmount ], orderItem[ id, order.userRecord[ username ] ] ], datacenter[ id, name, longName ], guests[ id, hostname, domain, uuid ], guestCount ''') self.dedicated_host.host.getObject.assert_called_once_with(id=12345, mask=mask) def test_place_order(self): create_dict = self.dedicated_host._generate_create_dict = mock.Mock() values = { 'hardware': [ { 'primaryBackendNetworkComponent': { 'router': { 'id': 51218 } }, 'domain': u'test.com', 'hostname': u'test' } ], 'useHourlyPricing': True, 'location': 'AMSTERDAM', 'packageId': 813, 'complexType': 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost', 'prices': [ { 'id': 200269 } ], 'quantity': 1 } create_dict.return_value = values location = 'dal05' hostname = 'test' domain = 'test.com' hourly = True flavor = '56_CORES_X_242_RAM_X_1_4_TB' self.dedicated_host.place_order(hostname=hostname, domain=domain, location=location, flavor=flavor, hourly=hourly) create_dict.assert_called_once_with(hostname=hostname, router=None, domain=domain, datacenter=location, flavor=flavor, hourly=True) self.assert_called_with('SoftLayer_Product_Order', 'placeOrder', args=(values,)) def test_verify_order(self): create_dict = self.dedicated_host._generate_create_dict = mock.Mock() values = { 'hardware': [ { 'primaryBackendNetworkComponent': { 'router': { 'id': 51218 } }, 'domain': 'test.com', 'hostname': 'test' } ], 'useHourlyPricing': True, 'location': 'AMSTERDAM', 'packageId': 813, 'complexType': 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost', 'prices': [ { 'id': 200269 } ], 'quantity': 1 } create_dict.return_value = values location = 'dal05' hostname = 'test' domain = 'test.com' hourly = True flavor = '56_CORES_X_242_RAM_X_1_4_TB' self.dedicated_host.verify_order(hostname=hostname, domain=domain, location=location, flavor=flavor, hourly=hourly) create_dict.assert_called_once_with(hostname=hostname, router=None, domain=domain, datacenter=location, flavor=flavor, hourly=True) self.assert_called_with('SoftLayer_Product_Order', 'verifyOrder', args=(values,)) def test_generate_create_dict_without_router(self): self.dedicated_host._get_package = mock.MagicMock() self.dedicated_host._get_package.return_value = self._get_package() self.dedicated_host._get_backend_router = mock.Mock() self.dedicated_host._get_backend_router.return_value = self \ ._get_routers_sample() location = 'dal05' hostname = 'test' domain = 'test.com' hourly = True flavor = '56_CORES_X_242_RAM_X_1_4_TB' results = self.dedicated_host._generate_create_dict(hostname=hostname, domain=domain, datacenter=location, flavor=flavor, hourly=hourly) testResults = { 'hardware': [ { 'primaryBackendNetworkComponent': { 'router': { 'id': 51218 } }, 'domain': 'test.com', 'hostname': 'test' } ], 'useHourlyPricing': True, 'location': 'DALLAS05', 'packageId': 813, 'complexType': 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost', 'prices': [ { 'id': 200269 } ], 'quantity': 1 } self.assertEqual(results, testResults) def test_generate_create_dict_with_router(self): self.dedicated_host._get_package = mock.MagicMock() self.dedicated_host._get_package.return_value = self._get_package() self.dedicated_host._get_default_router = mock.Mock() self.dedicated_host._get_default_router.return_value = 51218 location = 'dal05' router = 51218 hostname = 'test' domain = 'test.com' hourly = True flavor = '56_CORES_X_242_RAM_X_1_4_TB' results = self.dedicated_host._generate_create_dict( hostname=hostname, router=router, domain=domain, datacenter=location, flavor=flavor, hourly=hourly) testResults = { 'hardware': [ { 'primaryBackendNetworkComponent': { 'router': { 'id': 51218 } }, 'domain': 'test.com', 'hostname': 'test' } ], 'useHourlyPricing': True, 'location': 'DALLAS05', 'packageId': 813, 'complexType': 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost', 'prices': [ { 'id': 200269 } ], 'quantity': 1 } self.assertEqual(results, testResults) def test_get_package(self): mask = ''' items[ id, description, prices, capacity, keyName, itemCategory[categoryCode], bundleItems[capacity, categories[categoryCode]] ], regions[location[location[priceGroups]]] ''' self.dedicated_host.ordering_manager = mock.Mock() self.dedicated_host.ordering_manager.get_package_by_key.return_value = \ "test" package = self.dedicated_host._get_package() package_keyname = 'DEDICATED_HOST' self.assertEqual('test', package) self.dedicated_host.ordering_manager.get_package_by_key. \ assert_called_once_with(package_keyname, mask=mask) def test_get_package_no_package_found(self): packages = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') packages.return_value = [] self.assertRaises(exceptions.SoftLayerError, self.dedicated_host._get_package) def test_get_location(self): regions = [{ "location": { "location": { "name": "dal05", } } }] region = { 'location': { 'location': { 'name': 'dal05', } } } testing = self.dedicated_host._get_location(regions, 'dal05') self.assertEqual(testing, region) def test_get_location_no_location_found(self): regions = [{ "location": { "location": { "name": "dal05", } } }] self.assertRaises(exceptions.SoftLayerError, self.dedicated_host._get_location, regions, 'dal10') def test_get_create_options(self): self.dedicated_host._get_package = mock.MagicMock() self.dedicated_host._get_package.return_value = self._get_package() results = { 'dedicated_host': [{ 'key': '56_CORES_X_242_RAM_X_1_4_TB', 'name': '56 Cores X 242 RAM X 1.2 TB' }], 'locations': [ { 'key': 'ams01', 'name': 'Amsterdam 1' }, { 'key': 'dal05', 'name': 'Dallas 5' } ] } self.assertEqual(self.dedicated_host.get_create_options(), results) def test_get_price(self): package = self._get_package() item = package['items'][0] price_id = 200269 self.assertEqual(self.dedicated_host._get_price(item), price_id) def test_get_price_no_price_found(self): package = self._get_package() package['items'][0]['prices'][0]['locationGroupId'] = 33 item = package['items'][0] self.assertRaises(exceptions.SoftLayerError, self.dedicated_host._get_price, item) def test_get_item(self): """Returns the item for ordering a dedicated host.""" package = self._get_package() flavor = '56_CORES_X_242_RAM_X_1_4_TB' item = { 'bundleItems': [{ 'capacity': '1200', 'categories': [{ 'categoryCode': 'dedicated_host_disk' }] }, { 'capacity': '242', 'categories': [{ 'categoryCode': 'dedicated_host_ram' }] }], 'capacity': '56', 'description': '56 Cores X 242 RAM X 1.2 TB', 'id': 10195, 'itemCategory': { 'categoryCode': 'dedicated_virtual_hosts' }, 'keyName': '56_CORES_X_242_RAM_X_1_4_TB', 'prices': [{ 'hourlyRecurringFee': '3.164', 'id': 200269, 'itemId': 10195, 'recurringFee': '2099', }] } self.assertEqual(self.dedicated_host._get_item(package, flavor), item) def test_get_item_no_item_found(self): package = self._get_package() flavor = '56_CORES_X_242_RAM_X_1_4_TB' package['items'][0]['keyName'] = 'not found' self.assertRaises(exceptions.SoftLayerError, self.dedicated_host._get_item, package, flavor) def test_get_backend_router(self): location = [ { 'isAvailable': 1, 'locationId': 138124, 'packageId': 813 } ] locId = location[0]['locationId'] mask = ''' id, hostname ''' host = { 'cpuCount': '56', 'memoryCapacity': '242', 'diskCapacity': '1200', 'datacenter': { 'id': locId } } self.dedicated_host.host = mock.Mock() routers = self.dedicated_host.host.getAvailableRouters.return_value = \ self._get_routers_sample() item = self._get_package()['items'][0] routers_test = self.dedicated_host._get_backend_router(location, item) self.assertEqual(routers, routers_test) self.dedicated_host.host.getAvailableRouters.assert_called_once_with(host, mask=mask) def test_get_backend_router_no_routers_found(self): location = [] self.dedicated_host.host = mock.Mock() routers_test = self.dedicated_host._get_backend_router item = self._get_package()['items'][0] self.assertRaises(exceptions.SoftLayerError, routers_test, location, item) def test_get_default_router(self): routers = self._get_routers_sample() router = 51218 router_test = self.dedicated_host._get_default_router(routers, 'bcr01a.dal05') self.assertEqual(router_test, router) def test_get_default_router_no_router_found(self): routers = [] self.assertRaises(exceptions.SoftLayerError, self.dedicated_host._get_default_router, routers, 'notFound') def _get_routers_sample(self): routers = [ { 'hostname': 'bcr01a.dal05', 'id': 51218 }, { 'hostname': 'bcr02a.dal05', 'id': 83361 }, { 'hostname': 'bcr03a.dal05', 'id': 122762 }, { 'hostname': 'bcr04a.dal05', 'id': 147566 } ] return routers def _get_package(self): package = { "items": [ { "capacity": "56", "description": "56 Cores X 242 RAM X 1.2 TB", "bundleItems": [ { "capacity": "1200", "categories": [ { "categoryCode": "dedicated_host_disk" } ] }, { "capacity": "242", "categories": [ { "categoryCode": "dedicated_host_ram" } ] } ], "prices": [ { "itemId": 10195, "recurringFee": "2099", "hourlyRecurringFee": "3.164", "id": 200269, } ], "keyName": "56_CORES_X_242_RAM_X_1_4_TB", "id": 10195, "itemCategory": { "categoryCode": "dedicated_virtual_hosts" }, } ], "regions": [ { "location": { "locationPackageDetails": [ { "locationId": 265592, "packageId": 813 } ], "location": { "id": 265592, "name": "ams01", "longName": "Amsterdam 1" } }, "keyname": "AMSTERDAM", "description": "AMS01 - Amsterdam", "sortOrder": 0 }, { "location": { "locationPackageDetails": [ { "isAvailable": 1, "locationId": 138124, "packageId": 813 } ], "location": { "id": 138124, "name": "dal05", "longName": "Dallas 5" } }, "keyname": "DALLAS05", "description": "DAL05 - Dallas", } ], "id": 813, "description": "Dedicated Host" } return package softlayer-python-5.4.2/tests/managers/dns_tests.py000066400000000000000000000123111324365065500223770ustar00rootroot00000000000000""" SoftLayer.tests.managers.dns_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import SoftLayer from SoftLayer import fixtures from SoftLayer import testing class DNSTests(testing.TestCase): def set_up(self): self.dns_client = SoftLayer.DNSManager(self.client) def test_list_zones(self): zones = self.dns_client.list_zones() self.assertEqual(zones, fixtures.SoftLayer_Account.getDomains) def test_get_zone(self): # match, with defaults res = self.dns_client.get_zone(12345) self.assertEqual(res, fixtures.SoftLayer_Dns_Domain.getObject) self.assert_called_with('SoftLayer_Dns_Domain', 'getObject', identifier=12345, mask='mask[resourceRecords]') def test_get_zone_without_records(self): self.dns_client.get_zone(12345, records=False) self.assert_called_with('SoftLayer_Dns_Domain', 'getObject', identifier=12345, mask=None) def test_resolve_zone_name(self): res = self.dns_client._get_zone_id_from_name('example.com') self.assertEqual([12345], res) _filter = {"domains": {"name": {"operation": "_= example.com"}}} self.assert_called_with('SoftLayer_Account', 'getDomains', filter=_filter) def test_resolve_zone_name_no_matches(self): mock = self.set_mock('SoftLayer_Account', 'getDomains') mock.return_value = [] res = self.dns_client._get_zone_id_from_name('example.com') self.assertEqual([], res) _filter = {"domains": {"name": {"operation": "_= example.com"}}} self.assert_called_with('SoftLayer_Account', 'getDomains', filter=_filter) def test_create_zone(self): res = self.dns_client.create_zone('example.com', serial='2014110201') args = ({'serial': '2014110201', 'name': 'example.com', 'resourceRecords': {}},) self.assert_called_with('SoftLayer_Dns_Domain', 'createObject', args=args) self.assertEqual(res, {'name': 'example.com'}) def test_delete_zone(self): self.dns_client.delete_zone(1) self.assert_called_with('SoftLayer_Dns_Domain', 'deleteObject', identifier=1) def test_edit_zone(self): self.dns_client.edit_zone('example.com') self.assert_called_with('SoftLayer_Dns_Domain', 'editObject', args=('example.com',)) def test_create_record(self): res = self.dns_client.create_record(1, 'test', 'TXT', 'testing', ttl=1200) self.assert_called_with('SoftLayer_Dns_Domain_ResourceRecord', 'createObject', args=({ 'domainId': 1, 'ttl': 1200, 'host': 'test', 'type': 'TXT', 'data': 'testing' },)) self.assertEqual(res, {'name': 'example.com'}) def test_delete_record(self): self.dns_client.delete_record(1) self.assert_called_with('SoftLayer_Dns_Domain_ResourceRecord', 'deleteObject', identifier=1) def test_edit_record(self): self.dns_client.edit_record({'id': 1, 'name': 'test', 'ttl': '1800'}) self.assert_called_with('SoftLayer_Dns_Domain_ResourceRecord', 'editObject', args=({'id': 1, 'name': 'test', 'ttl': '1800'},), identifier=1) def test_dump_zone(self): self.dns_client.dump_zone(1) self.assert_called_with('SoftLayer_Dns_Domain', 'getZoneFileContents', identifier=1) def test_get_record(self): # maybe valid domain, but no records matching mock = self.set_mock('SoftLayer_Dns_Domain', 'getResourceRecords') mock.return_value = [] self.assertEqual(self.dns_client.get_records(12345), []) mock.reset_mock() records = fixtures.SoftLayer_Dns_Domain.getResourceRecords mock.return_value = [records[0]] self.dns_client.get_records(12345, record_type='a', host='hostname', data='a', ttl='86400') _filter = {'resourceRecords': {'type': {'operation': '_= a'}, 'host': {'operation': '_= hostname'}, 'data': {'operation': '_= a'}, 'ttl': {'operation': 86400}}} self.assert_called_with('SoftLayer_Dns_Domain', 'getResourceRecords', identifier=12345, filter=_filter) softlayer-python-5.4.2/tests/managers/file_tests.py000066400000000000000000000730541324365065500225450ustar00rootroot00000000000000""" SoftLayer.tests.managers.file_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import copy import SoftLayer from SoftLayer import exceptions from SoftLayer import fixtures from SoftLayer import testing class FileTests(testing.TestCase): def set_up(self): self.file = SoftLayer.FileStorageManager(self.client) def test_cancel_file_volume_immediately(self): self.file.cancel_file_volume(123, immediate=True) self.assert_called_with( 'SoftLayer_Billing_Item', 'cancelItem', args=(True, True, 'No longer needed'), identifier=449, ) def test_cancel_file_volume_immediately_hourly_billing(self): mock = self.set_mock('SoftLayer_Network_Storage', 'getObject') mock.return_value = { 'billingItem': {'hourlyFlag': True, 'id': 449}, } self.file.cancel_file_volume(123, immediate=False) self.assert_called_with( 'SoftLayer_Billing_Item', 'cancelItem', args=(True, True, 'No longer needed'), identifier=449, ) def test_authorize_host_to_volume(self): result = self.file.authorize_host_to_volume( 50, hardware_ids=[100], virtual_guest_ids=[200], ip_address_ids=[300], subnet_ids=[400]) self.assertEqual(fixtures.SoftLayer_Network_Storage. allowAccessFromHostList, result) self.assert_called_with( 'SoftLayer_Network_Storage', 'allowAccessFromHostList', identifier=50) def test_deauthorize_host_to_volume(self): result = self.file.deauthorize_host_to_volume( 50, hardware_ids=[100], virtual_guest_ids=[200], ip_address_ids=[300], subnet_ids=[400]) self.assertEqual(fixtures.SoftLayer_Network_Storage. removeAccessFromHostList, result) self.assert_called_with( 'SoftLayer_Network_Storage', 'removeAccessFromHostList', identifier=50) def test_get_file_volume_access_list(self): self.file.get_file_volume_access_list(100) self.assert_called_with( 'SoftLayer_Network_Storage', 'getObject', identifier=100) def test_enable_snapshots(self): result = self.file.enable_snapshots(12345678, 'WEEKLY', 10, 47, 16, 'FRIDAY') self.assertEqual(fixtures.SoftLayer_Network_Storage.enableSnapshots, result) self.assert_called_with( 'SoftLayer_Network_Storage', 'enableSnapshots', identifier=12345678) def test_disable_snapshots(self): result = self.file.disable_snapshots(12345678, 'HOURLY') self.assertEqual(fixtures.SoftLayer_Network_Storage.disableSnapshots, result) self.assert_called_with( 'SoftLayer_Network_Storage', 'disableSnapshots', identifier=12345678) def test_snapshot_restore(self): result = self.file.restore_from_snapshot(12345678, 87654321) self.assertEqual( fixtures.SoftLayer_Network_Storage.restoreFromSnapshot, result) self.assert_called_with( 'SoftLayer_Network_Storage', 'restoreFromSnapshot', identifier=12345678) def test_get_file_volume_details(self): result = self.file.get_file_volume_details(100) self.assertEqual(fixtures.SoftLayer_Network_Storage.getObject, result) expected_mask = 'id,'\ 'username,'\ 'password,'\ 'capacityGb,'\ 'bytesUsed,'\ 'snapshotCapacityGb,'\ 'parentVolume.snapshotSizeBytes,'\ 'storageType.keyName,'\ 'serviceResource.datacenter[name],'\ 'serviceResourceBackendIpAddress,'\ 'fileNetworkMountAddress,'\ 'storageTierLevel,'\ 'provisionedIops,'\ 'lunId,'\ 'originalVolumeName,'\ 'originalSnapshotName,'\ 'originalVolumeSize,'\ 'activeTransactionCount,'\ 'activeTransactions.transactionStatus[friendlyName],'\ 'replicationPartnerCount,'\ 'replicationStatus,'\ 'replicationPartners[id,username,'\ 'serviceResourceBackendIpAddress,'\ 'serviceResource[datacenter[name]],'\ 'replicationSchedule[type[keyname]]]' self.assert_called_with( 'SoftLayer_Network_Storage', 'getObject', identifier=100, mask='mask[%s]' % expected_mask) def test_get_file_volume_snapshot_list(self): result = self.file.get_file_volume_snapshot_list(100) self.assertEqual(fixtures.SoftLayer_Network_Storage.getSnapshots, result) self.assert_called_with( 'SoftLayer_Network_Storage', 'getSnapshots', identifier=100) def test_create_snapshot(self): result = self.file.create_snapshot(123, 'hello world') self.assertEqual(fixtures.SoftLayer_Network_Storage.createSnapshot, result) self.assert_called_with( 'SoftLayer_Network_Storage', 'createSnapshot', identifier=123) def test_cancel_snapshot_immediately(self): self.file.cancel_snapshot_space(1234, immediate=True) self.assert_called_with( 'SoftLayer_Billing_Item', 'cancelItem', args=(True, True, 'No longer needed'), identifier=123, ) def test_cancel_snapshot_hourly_billing_immediate_true(self): mock = self.set_mock('SoftLayer_Network_Storage', 'getObject') mock.return_value = { 'billingItem': { 'activeChildren': [ {'categoryCode': 'storage_snapshot_space', 'id': 417} ], 'hourlyFlag': True, 'id': 449 }, } self.file.cancel_snapshot_space(1234, immediate=True) self.assert_called_with( 'SoftLayer_Billing_Item', 'cancelItem', args=(True, True, 'No longer needed'), identifier=417, ) def test_cancel_snapshot_hourly_billing_immediate_false(self): mock = self.set_mock('SoftLayer_Network_Storage', 'getObject') mock.return_value = { 'billingItem': { 'activeChildren': [ {'categoryCode': 'storage_snapshot_space', 'id': 417} ], 'hourlyFlag': True, 'id': 449 }, } self.file.cancel_snapshot_space(1234, immediate=False) self.assert_called_with( 'SoftLayer_Billing_Item', 'cancelItem', args=(True, True, 'No longer needed'), identifier=417, ) def test_cancel_snapshot_exception_no_billing_item_active_children(self): mock = self.set_mock('SoftLayer_Network_Storage', 'getObject') mock.return_value = { 'capacityGb': 20, 'snapshotCapacityGb': '10', 'schedules': [{ 'id': 7770, 'type': {'keyname': 'SNAPSHOT_WEEKLY'} }], 'billingItem': { 'categoryCode': 'storage_service_enterprise', 'cancellationDate': '2016-09-04T22:00:00-07:00' } } exception = self.assertRaises( exceptions.SoftLayerError, self.file.cancel_snapshot_space, 12345, immediate=True ) self.assertEqual( 'No snapshot space found to cancel', str(exception) ) def test_cancel_snapshot_exception_snapshot_billing_item_not_found(self): mock = self.set_mock('SoftLayer_Network_Storage', 'getObject') mock.return_value = { 'capacityGb': 20, 'snapshotCapacityGb': '10', 'schedules': [{ 'id': 7770, 'type': {'keyname': 'SNAPSHOT_WEEKLY'} }], 'billingItem': { 'activeChildren': [] } } exception = self.assertRaises( exceptions.SoftLayerError, self.file.cancel_snapshot_space, 12345, immediate=True ) self.assertEqual( 'No snapshot space found to cancel', str(exception) ) def test_replicant_failover(self): result = self.file.failover_to_replicant(1234, 5678, immediate=True) self.assertEqual( fixtures.SoftLayer_Network_Storage.failoverToReplicant, result) self.assert_called_with( 'SoftLayer_Network_Storage', 'failoverToReplicant', args=(5678, True), identifier=1234, ) def test_replicant_failback(self): result = self.file.failback_from_replicant(1234, 5678) self.assertEqual( fixtures.SoftLayer_Network_Storage.failbackFromReplicant, result) self.assert_called_with( 'SoftLayer_Network_Storage', 'failbackFromReplicant', args=(5678,), identifier=1234, ) def test_get_replication_partners(self): self.file.get_replication_partners(1234) self.assert_called_with( 'SoftLayer_Network_Storage', 'getReplicationPartners', identifier=1234, ) def test_get_replication_locations(self): self.file.get_replication_locations(1234) self.assert_called_with( 'SoftLayer_Network_Storage', 'getValidReplicationTargetDatacenterLocations', identifier=1234, ) def test_delete_snapshot(self): result = self.file.delete_snapshot(100) self.assertEqual(fixtures.SoftLayer_Network_Storage.deleteObject, result) self.assert_called_with( 'SoftLayer_Network_Storage', 'deleteObject', identifier=100) def test_list_file_volumes(self): result = self.file.list_file_volumes() self.assertEqual(fixtures.SoftLayer_Account.getNasNetworkStorage, result) expected_filter = { 'nasNetworkStorage': { 'storageType': { 'keyName': {'operation': '*= FILE_STORAGE'} }, 'serviceResource': { 'type': { 'type': {'operation': '!~ NAS'} } } } } expected_mask = 'id,'\ 'username,'\ 'capacityGb,'\ 'bytesUsed,'\ 'serviceResource.datacenter[name],'\ 'serviceResourceBackendIpAddress,'\ 'activeTransactionCount,'\ 'fileNetworkMountAddress,'\ 'replicationPartnerCount' self.assert_called_with( 'SoftLayer_Account', 'getNasNetworkStorage', filter=expected_filter, mask='mask[%s]' % expected_mask ) def test_list_file_volumes_with_additional_filters(self): result = self.file.list_file_volumes(datacenter="dal09", storage_type="Endurance", username="username") self.assertEqual(fixtures.SoftLayer_Account.getNasNetworkStorage, result) expected_filter = { 'nasNetworkStorage': { 'storageType': { 'keyName': {'operation': '^= ENDURANCE_FILE_STORAGE'} }, 'username': {'operation': u'_= username'}, 'serviceResource': { 'datacenter': { 'name': {'operation': u'_= dal09'} }, 'type': { 'type': {'operation': '!~ NAS'} } } } } expected_mask = 'id,'\ 'username,'\ 'capacityGb,'\ 'bytesUsed,'\ 'serviceResource.datacenter[name],'\ 'serviceResourceBackendIpAddress,'\ 'activeTransactionCount,'\ 'fileNetworkMountAddress,'\ 'replicationPartnerCount' self.assert_called_with( 'SoftLayer_Account', 'getNasNetworkStorage', filter=expected_filter, mask='mask[%s]' % expected_mask ) def test_order_file_volume_performance(self): mock = self.set_mock('SoftLayer_Location_Datacenter', 'getDatacenters') mock.return_value = [{'id': 449494, 'name': 'dal09'}] mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['storageType']['keyName'] = 'PERFORMANCE_FILE_STORAGE' mock = self.set_mock('SoftLayer_Network_Storage', 'getObject') mock.return_value = mock_volume result = self.file.order_file_volume( 'performance', 'dal09', 1000, iops=2000, service_offering='storage_as_a_service' ) self.assertEqual(fixtures.SoftLayer_Product_Order.placeOrder, result) self.assert_called_with( 'SoftLayer_Product_Order', 'placeOrder', args=({ 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_Storage_AsAService', 'packageId': 759, 'prices': [ {'id': 189433}, {'id': 189453}, {'id': 190113}, {'id': 190173} ], 'volumeSize': 1000, 'quantity': 1, 'location': 449494, 'iops': 2000, 'useHourlyPricing': False },) ) def test_order_file_volume_endurance(self): mock = self.set_mock('SoftLayer_Location_Datacenter', 'getDatacenters') mock.return_value = [{'id': 449494, 'name': 'dal09'}] mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['storageType']['keyName'] = 'ENDURANCE_FILE_STORAGE' mock = self.set_mock('SoftLayer_Network_Storage', 'getObject') mock.return_value = mock_volume result = self.file.order_file_volume( 'endurance', 'dal09', 1000, tier_level=4, service_offering='storage_as_a_service' ) self.assertEqual(fixtures.SoftLayer_Product_Order.placeOrder, result) self.assert_called_with( 'SoftLayer_Product_Order', 'placeOrder', args=({ 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_Storage_AsAService', 'packageId': 759, 'prices': [ {'id': 189433}, {'id': 189453}, {'id': 194763}, {'id': 194703} ], 'volumeSize': 1000, 'quantity': 1, 'location': 449494, 'useHourlyPricing': False },) ) def test_order_file_snapshot_space_upgrade(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['storageType']['keyName'] = 'ENDURANCE_FILE_STORAGE' mock = self.set_mock('SoftLayer_Network_Storage', 'getObject') mock.return_value = mock_volume result = self.file.order_snapshot_space(102, 20, None, True) self.assertEqual(fixtures.SoftLayer_Product_Order.placeOrder, result) self.assert_called_with( 'SoftLayer_Product_Order', 'placeOrder', args=({ 'complexType': 'SoftLayer_Container_Product_Order_Network_' 'Storage_Enterprise_SnapshotSpace_Upgrade', 'packageId': 759, 'prices': [ {'id': 193853} ], 'quantity': 1, 'location': 449500, 'volumeId': 102, 'useHourlyPricing': False },) ) def test_order_file_snapshot_space(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['storageType']['keyName'] = 'ENDURANCE_FILE_STORAGE' mock = self.set_mock('SoftLayer_Network_Storage', 'getObject') mock.return_value = mock_volume result = self.file.order_snapshot_space(102, 10, None, False) self.assertEqual(fixtures.SoftLayer_Product_Order.placeOrder, result) self.assert_called_with( 'SoftLayer_Product_Order', 'placeOrder', args=({ 'complexType': 'SoftLayer_Container_Product_Order_Network_' 'Storage_Enterprise_SnapshotSpace', 'packageId': 759, 'prices': [ {'id': 193613} ], 'quantity': 1, 'location': 449500, 'volumeId': 102, 'useHourlyPricing': False },) ) def test_order_file_replicant_performance(self): mock = self.set_mock('SoftLayer_Location_Datacenter', 'getDatacenters') mock.return_value = [{'id': 449494, 'name': 'dal09'}] mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['storageType']['keyName'] = 'PERFORMANCE_FILE_STORAGE' mock = self.set_mock('SoftLayer_Network_Storage', 'getObject') mock.return_value = mock_volume result = self.file.order_replicant_volume(102, 'WEEKLY', 'dal09') self.assertEqual(fixtures.SoftLayer_Product_Order.placeOrder, result) self.assert_called_with( 'SoftLayer_Product_Order', 'placeOrder', args=({ 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_Storage_AsAService', 'packageId': 759, 'prices': [ {'id': 189433}, {'id': 189453}, {'id': 189993}, {'id': 190053}, {'id': 191193}, {'id': 192033} ], 'volumeSize': 500, 'quantity': 1, 'location': 449494, 'iops': 1000, 'originVolumeId': 102, 'originVolumeScheduleId': 978, 'useHourlyPricing': False },) ) def test_order_file_replicant_endurance(self): mock = self.set_mock('SoftLayer_Location_Datacenter', 'getDatacenters') mock.return_value = [{'id': 449494, 'name': 'dal09'}] mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['storageType']['keyName'] = 'ENDURANCE_FILE_STORAGE' mock = self.set_mock('SoftLayer_Network_Storage', 'getObject') mock.return_value = mock_volume result = self.file.order_replicant_volume(102, 'WEEKLY', 'dal09') self.assertEqual(fixtures.SoftLayer_Product_Order.placeOrder, result) self.assert_called_with( 'SoftLayer_Product_Order', 'placeOrder', args=({ 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_Storage_AsAService', 'packageId': 759, 'prices': [ {'id': 189433}, {'id': 189453}, {'id': 193433}, {'id': 193373}, {'id': 193613}, {'id': 194693} ], 'volumeSize': 500, 'quantity': 1, 'location': 449494, 'originVolumeId': 102, 'originVolumeScheduleId': 978, 'useHourlyPricing': False },) ) def test_order_file_duplicate_performance_no_duplicate_snapshot(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['storageType']['keyName'] = 'PERFORMANCE_FILE_STORAGE' mock = self.set_mock('SoftLayer_Network_Storage', 'getObject') mock.return_value = mock_volume result = self.file.order_duplicate_volume( 102, duplicate_snapshot_size=0) self.assertEqual(fixtures.SoftLayer_Product_Order.placeOrder, result) self.assert_called_with( 'SoftLayer_Product_Order', 'placeOrder', args=({ 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_Storage_AsAService', 'packageId': 759, 'prices': [ {'id': 189433}, {'id': 189453}, {'id': 189993}, {'id': 190053} ], 'volumeSize': 500, 'quantity': 1, 'location': 449500, 'duplicateOriginVolumeId': 102, 'iops': 1000, 'useHourlyPricing': False },)) def test_order_file_duplicate_performance(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['storageType']['keyName'] = 'PERFORMANCE_FILE_STORAGE' mock = self.set_mock('SoftLayer_Network_Storage', 'getObject') mock.return_value = mock_volume result = self.file.order_duplicate_volume( 102, origin_snapshot_id=470, duplicate_size=1000, duplicate_iops=2000, duplicate_tier_level=None, duplicate_snapshot_size=10 ) self.assertEqual(fixtures.SoftLayer_Product_Order.placeOrder, result) self.assert_called_with( 'SoftLayer_Product_Order', 'placeOrder', args=({ 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_Storage_AsAService', 'packageId': 759, 'prices': [ {'id': 189433}, {'id': 189453}, {'id': 190113}, {'id': 190173}, {'id': 191193} ], 'volumeSize': 1000, 'quantity': 1, 'location': 449500, 'duplicateOriginVolumeId': 102, 'duplicateOriginSnapshotId': 470, 'iops': 2000, 'useHourlyPricing': False },)) def test_order_file_duplicate_endurance_no_duplicate_snapshot(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['storageType']['keyName'] = 'ENDURANCE_FILE_STORAGE' mock = self.set_mock('SoftLayer_Network_Storage', 'getObject') mock.return_value = mock_volume result = self.file.order_duplicate_volume( 102, duplicate_snapshot_size=0) self.assertEqual(fixtures.SoftLayer_Product_Order.placeOrder, result) self.assert_called_with( 'SoftLayer_Product_Order', 'placeOrder', args=({ 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_Storage_AsAService', 'packageId': 759, 'prices': [ {'id': 189433}, {'id': 189453}, {'id': 193433}, {'id': 193373} ], 'volumeSize': 500, 'quantity': 1, 'location': 449500, 'duplicateOriginVolumeId': 102, 'useHourlyPricing': False },)) def test_order_file_duplicate_endurance(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['storageType']['keyName'] = 'ENDURANCE_FILE_STORAGE' mock = self.set_mock('SoftLayer_Network_Storage', 'getObject') mock.return_value = mock_volume result = self.file.order_duplicate_volume( 102, origin_snapshot_id=470, duplicate_size=1000, duplicate_iops=None, duplicate_tier_level=4, duplicate_snapshot_size=10 ) self.assertEqual(fixtures.SoftLayer_Product_Order.placeOrder, result) self.assert_called_with( 'SoftLayer_Product_Order', 'placeOrder', args=({ 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_Storage_AsAService', 'packageId': 759, 'prices': [ {'id': 189433}, {'id': 189453}, {'id': 194763}, {'id': 194703}, {'id': 194943} ], 'volumeSize': 1000, 'quantity': 1, 'location': 449500, 'duplicateOriginVolumeId': 102, 'duplicateOriginSnapshotId': 470, 'useHourlyPricing': False },)) def test_order_file_modified_performance(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['storageType']['keyName'] = 'PERFORMANCE_FILE_STORAGE' mock = self.set_mock('SoftLayer_Network_Storage', 'getObject') mock.return_value = mock_volume result = self.file.order_modified_volume(102, new_size=1000, new_iops=2000, new_tier_level=None) self.assertEqual(fixtures.SoftLayer_Product_Order.placeOrder, result) self.assert_called_with( 'SoftLayer_Product_Order', 'placeOrder', args=({'complexType': 'SoftLayer_Container_Product_Order_Network_Storage_AsAService_Upgrade', 'packageId': 759, 'prices': [{'id': 189433}, {'id': 190113}, {'id': 190173}], 'volume': {'id': 102}, 'volumeSize': 1000, 'iops': 2000},) ) def test_order_file_modified_endurance(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['storageType']['keyName'] = 'ENDURANCE_FILE_STORAGE' mock = self.set_mock('SoftLayer_Network_Storage', 'getObject') mock.return_value = mock_volume result = self.file.order_modified_volume(102, new_size=1000, new_iops=None, new_tier_level=4) self.assertEqual(fixtures.SoftLayer_Product_Order.placeOrder, result) self.assert_called_with( 'SoftLayer_Product_Order', 'placeOrder', args=({'complexType': 'SoftLayer_Container_Product_Order_Network_Storage_AsAService_Upgrade', 'packageId': 759, 'prices': [{'id': 189433}, {'id': 194763}, {'id': 194703}], 'volume': {'id': 102}, 'volumeSize': 1000},) ) softlayer-python-5.4.2/tests/managers/firewall_tests.py000066400000000000000000000305541324365065500234310ustar00rootroot00000000000000""" SoftLayer.tests.managers.firewall_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import SoftLayer from SoftLayer import exceptions from SoftLayer import fixtures from SoftLayer import testing class FirewallTests(testing.TestCase): def set_up(self): self.firewall = SoftLayer.FirewallManager(self.client) def test_get_firewalls(self): firewall_vlan = { 'id': 1, 'firewallNetworkComponents': [{'id': 1234}], 'networkVlanFirewall': {'id': 1234}, 'dedicatedFirewallFlag': True, 'firewallGuestNetworkComponents': [{'id': 1234}], 'firewallInterfaces': [{'id': 1234}], 'firewallRules': [{'id': 1234}], 'highAvailabilityFirewallFlag': True, } mock = self.set_mock('SoftLayer_Account', 'getNetworkVlans') mock.return_value = [firewall_vlan] firewalls = self.firewall.get_firewalls() self.assertEqual(firewalls, [firewall_vlan]) self.assert_called_with('SoftLayer_Account', 'getNetworkVlans') def test_get_standard_fwl_rules(self): rules = self.firewall.get_standard_fwl_rules(1234) self.assertEqual( rules, fixtures.SoftLayer_Network_Component_Firewall.getRules) self.assert_called_with('SoftLayer_Network_Component_Firewall', 'getRules', identifier=1234) def test_get_dedicated_fwl_rules(self): rules = self.firewall.get_dedicated_fwl_rules(1234) self.assertEqual(rules, fixtures.SoftLayer_Network_Vlan_Firewall.getRules) self.assert_called_with('SoftLayer_Network_Vlan_Firewall', 'getRules', identifier=1234) def test_get_standard_package_virtual_server(self): # test standard firewalls self.firewall.get_standard_package(server_id=1234, is_virt=True) self.assert_called_with('SoftLayer_Virtual_Guest', 'getObject', identifier=1234, mask='mask[primaryNetworkComponent[maxSpeed]]') _filter = { 'items': { 'description': { 'operation': '_= 100Mbps Hardware Firewall' } } } self.assert_called_with('SoftLayer_Product_Package', 'getItems', identifier=0, filter=_filter) def test_get_standard_package_bare_metal(self): self.firewall.get_standard_package(server_id=1234, is_virt=False) # we should ask for the frontEndNetworkComponents to get # the firewall port speed mask = 'mask[id,maxSpeed,networkComponentGroup.networkComponents]' self.assert_called_with('SoftLayer_Hardware_Server', 'getFrontendNetworkComponents', identifier=1234, mask=mask) # shiould call the product package for a 2000Mbps firwall _filter = { 'items': { 'description': { 'operation': '_= 2000Mbps Hardware Firewall' } } } self.assert_called_with('SoftLayer_Product_Package', 'getItems', identifier=0, filter=_filter) def test_get_dedicated_package_ha(self): # test dedicated HA firewalls self.firewall.get_dedicated_package(ha_enabled=True) _filter = { 'items': { 'description': { 'operation': '_= Hardware Firewall (High Availability)' } } } self.assert_called_with('SoftLayer_Product_Package', 'getItems', identifier=0, filter=_filter) def test_get_dedicated_package_pkg(self): # test dedicated HA firewalls self.firewall.get_dedicated_package(ha_enabled=False) _filter = { 'items': { 'description': { 'operation': '_= Hardware Firewall (Dedicated)' } } } self.assert_called_with('SoftLayer_Product_Package', 'getItems', identifier=0, filter=_filter) def test_cancel_firewall(self): # test standard firewalls result = self.firewall.cancel_firewall(6327, dedicated=False) self.assertEqual(result, fixtures.SoftLayer_Billing_Item.cancelService) self.assert_called_with('SoftLayer_Network_Component_Firewall', 'getObject', identifier=6327, mask='mask[id,billingItem[id]]') self.assert_called_with('SoftLayer_Billing_Item', 'cancelService', identifier=21370814) def test_cancel_firewall_no_firewall(self): mock = self.set_mock('SoftLayer_Network_Component_Firewall', 'getObject') mock.return_value = None self.assertRaises(exceptions.SoftLayerError, self.firewall.cancel_firewall, 6327, dedicated=False) def test_cancel_firewall_no_billing(self): mock = self.set_mock('SoftLayer_Network_Component_Firewall', 'getObject') mock.return_value = { 'id': 6327, 'billingItem': None } self.assertRaises(exceptions.SoftLayerError, self.firewall.cancel_firewall, 6327, dedicated=False) def test_cancel_dedicated_firewall(self): # test dedicated firewalls result = self.firewall.cancel_firewall(6327, dedicated=True) self.assertEqual(result, fixtures.SoftLayer_Billing_Item.cancelService) self.assert_called_with('SoftLayer_Network_Vlan_Firewall', 'getObject', identifier=6327, mask='mask[id,billingItem[id]]') self.assert_called_with('SoftLayer_Billing_Item', 'cancelService', identifier=21370815) def test_cancel_dedicated_firewall_no_firewall(self): mock = self.set_mock('SoftLayer_Network_Vlan_Firewall', 'getObject') mock.return_value = None self.assertRaises(exceptions.SoftLayerError, self.firewall.cancel_firewall, 6327, dedicated=True) def test_cancel_dedicated_firewall_no_billing(self): mock = self.set_mock('SoftLayer_Network_Vlan_Firewall', 'getObject') mock.return_value = { 'id': 6327, 'billingItem': None } self.assertRaises(exceptions.SoftLayerError, self.firewall.cancel_firewall, 6327, dedicated=True) def test_add_standard_firewall_virtual_server(self): # test standard firewalls for virtual servers self.firewall.add_standard_firewall(6327, is_virt=True) self.assert_called_with('SoftLayer_Virtual_Guest', 'getObject', mask='mask[primaryNetworkComponent[maxSpeed]]', identifier=6327) _filter = { 'items': { 'description': { 'operation': '_= 100Mbps Hardware Firewall' } } } self.assert_called_with('SoftLayer_Product_Package', 'getItems', filter=_filter, identifier=0) args = ({'prices': [{'id': 1122}], 'quantity': 1, 'virtualGuests': [{'id': 6327}], 'packageId': 0, 'complexType': 'SoftLayer_Container_Product_Order_Network_' 'Protection_Firewall'},) self.assert_called_with('SoftLayer_Product_Order', 'placeOrder', args=args) def test_add_standard_firewall_server(self): # test dedicated firewall for Servers self.firewall.add_standard_firewall(6327, is_virt=False) # We should query the product package for a 2000Mbps firewall _filter = { 'items': { 'description': { 'operation': '_= 2000Mbps Hardware Firewall' } } } self.assert_called_with('SoftLayer_Product_Package', 'getItems', identifier=0, filter=_filter) # we should ask for the frontEndNetworkComponents to get # the firewall port speed mask = 'mask[id,maxSpeed,networkComponentGroup.networkComponents]' self.assert_called_with('SoftLayer_Hardware_Server', 'getFrontendNetworkComponents', mask=mask, identifier=6327) args = ({'hardware': [{'id': 6327}], 'prices': [{'id': 1122}], 'quantity': 1, 'packageId': 0, 'complexType': 'SoftLayer_Container_Product_Order_Network_' 'Protection_Firewall'},) self.assert_called_with('SoftLayer_Product_Order', 'placeOrder', args=args) def test__get_fwl_port_speed_server(self): # Test the routine that calculates the speed of firewall # required for a server port_speed = self.firewall._get_fwl_port_speed(186908, False) self.assertEqual(port_speed, 2000) def test_add_vlan_firewall(self): # test dedicated firewall for Vlan self.firewall.add_vlan_firewall(6327, ha_enabled=False) _filter = { 'items': { 'description': { 'operation': '_= Hardware Firewall (Dedicated)' } } } self.assert_called_with('SoftLayer_Product_Package', 'getItems', identifier=0, filter=_filter) args = ({'prices': [{'id': 1122}], 'quantity': 1, 'vlanId': 6327, 'packageId': 0, 'complexType': 'SoftLayer_Container_Product_Order_Network_' 'Protection_Firewall_Dedicated'},) self.assert_called_with('SoftLayer_Product_Order', 'placeOrder', args=args) def test_add_vlan_firewall_ha(self): # test dedicated firewall for Vlan self.firewall.add_vlan_firewall(6327, ha_enabled=True) _filter = { 'items': { 'description': { 'operation': '_= Hardware Firewall (High Availability)' } } } self.assert_called_with('SoftLayer_Product_Package', 'getItems', identifier=0, filter=_filter) args = ({'prices': [{'id': 1122}], 'quantity': 1, 'vlanId': 6327, 'packageId': 0, 'complexType': 'SoftLayer_Container_Product_Order_Network_' 'Protection_Firewall_Dedicated'},) self.assert_called_with('SoftLayer_Product_Order', 'placeOrder', args=args) def test_edit_dedicated_fwl_rules(self): # test standard firewalls rules = fixtures.SoftLayer_Network_Vlan_Firewall.getRules self.firewall.edit_dedicated_fwl_rules(firewall_id=1234, rules=rules) args = ({'firewallContextAccessControlListId': 3142, 'rules': rules},) self.assert_called_with('SoftLayer_Network_Firewall_Update_Request', 'createObject', args=args) def test_edit_standard_fwl_rules(self): # test standard firewalls rules = fixtures.SoftLayer_Network_Component_Firewall.getRules self.firewall.edit_standard_fwl_rules(firewall_id=1234, rules=rules) args = ({"networkComponentFirewallId": 1234, "rules": rules},) self.assert_called_with('SoftLayer_Network_Firewall_Update_Request', 'createObject', args=args) softlayer-python-5.4.2/tests/managers/hardware_tests.py000066400000000000000000000375771324365065500234350ustar00rootroot00000000000000""" SoftLayer.tests.managers.hardware_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import copy import mock import SoftLayer from SoftLayer import fixtures from SoftLayer import managers from SoftLayer import testing MINIMAL_TEST_CREATE_ARGS = { 'size': 'S1270_8GB_2X1TBSATA_NORAID', 'hostname': 'unicorn', 'domain': 'giggles.woo', 'location': 'wdc01', 'os': 'UBUNTU_14_64', 'port_speed': 10, } class HardwareTests(testing.TestCase): def set_up(self): self.hardware = SoftLayer.HardwareManager(self.client) def test_init_with_ordering_manager(self): ordering_manager = SoftLayer.OrderingManager(self.client) mgr = SoftLayer.HardwareManager(self.client, ordering_manager) self.assertEqual(mgr.ordering_manager, ordering_manager) def test_list_hardware(self): results = self.hardware.list_hardware() self.assertEqual(results, fixtures.SoftLayer_Account.getHardware) self.assert_called_with('SoftLayer_Account', 'getHardware') def test_list_hardware_with_filters(self): results = self.hardware.list_hardware( tags=['tag1', 'tag2'], cpus=2, memory=1, hostname='hostname', domain='example.com', datacenter='dal05', nic_speed=100, public_ip='1.2.3.4', private_ip='4.3.2.1', ) self.assertEqual(results, fixtures.SoftLayer_Account.getHardware) _filter = { 'hardware': { 'datacenter': {'name': {'operation': '_= dal05'}}, 'domain': {'operation': '_= example.com'}, 'tagReferences': { 'tag': {'name': { 'operation': 'in', 'options': [ {'name': 'data', 'value': ['tag1', 'tag2']}] }} }, 'memoryCapacity': {'operation': 1}, 'processorPhysicalCoreAmount': {'operation': 2}, 'hostname': {'operation': '_= hostname'}, 'primaryIpAddress': {'operation': '_= 1.2.3.4'}, 'networkComponents': {'maxSpeed': {'operation': 100}}, 'primaryBackendIpAddress': {'operation': '_= 4.3.2.1'}} } self.assert_called_with('SoftLayer_Account', 'getHardware', filter=_filter) def test_resolve_ids_ip(self): _id = self.hardware._get_ids_from_ip('172.16.1.100') self.assertEqual(_id, [1000, 1001, 1002, 1003]) _id = self.hardware._get_ids_from_ip('nope') self.assertEqual(_id, []) # Now simulate a private IP test mock = self.set_mock('SoftLayer_Account', 'getHardware') mock.side_effect = [[], [{'id': 99}]] _id = self.hardware._get_ids_from_ip('10.0.1.87') self.assertEqual(_id, [99]) def test_resolve_ids_hostname(self): _id = self.hardware._get_ids_from_hostname('hardware-test1') self.assertEqual(_id, [1000, 1001, 1002, 1003]) def test_get_hardware(self): result = self.hardware.get_hardware(1000) self.assertEqual(fixtures.SoftLayer_Hardware_Server.getObject, result) self.assert_called_with('SoftLayer_Hardware_Server', 'getObject', identifier=1000) def test_reload(self): post_uri = 'http://test.sftlyr.ws/test.sh' result = self.hardware.reload(1, post_uri=post_uri, ssh_keys=[1701]) self.assertEqual(result, 'OK') self.assert_called_with('SoftLayer_Hardware_Server', 'reloadOperatingSystem', args=('FORCE', {'customProvisionScriptUri': post_uri, 'sshKeyIds': [1701]}), identifier=1) def test_get_create_options(self): options = self.hardware.get_create_options() expected = { 'extras': [{'key': '1_IPV6_ADDRESS', 'name': '1 IPv6 Address'}], 'locations': [{'key': 'wdc01', 'name': 'Washington 1'}], 'operating_systems': [{'key': 'UBUNTU_14_64', 'name': 'Ubuntu / 14.04-64'}], 'port_speeds': [{ 'key': '10', 'name': '10 Mbps Public & Private Network Uplinks' }], 'sizes': [{ 'key': 'S1270_8GB_2X1TBSATA_NORAID', 'name': 'Single Xeon 1270, 8GB Ram, 2x1TB SATA disks, Non-RAID' }] } self.assertEqual(options, expected) def test_get_create_options_package_missing(self): packages = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') packages.return_value = [] ex = self.assertRaises(SoftLayer.SoftLayerError, self.hardware.get_create_options) self.assertEqual("Package BARE_METAL_SERVER does not exist", str(ex)) def test_generate_create_dict_no_items(self): packages = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') packages_copy = copy.deepcopy( fixtures.SoftLayer_Product_Package.getAllObjects) packages_copy[0]['items'] = [] packages.return_value = packages_copy ex = self.assertRaises(SoftLayer.SoftLayerError, self.hardware._generate_create_dict, location="wdc01") self.assertIn("Could not find valid price", str(ex)) def test_generate_create_dict_no_regions(self): packages = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') packages_copy = copy.deepcopy( fixtures.SoftLayer_Product_Package.getAllObjects) packages_copy[0]['regions'] = [] packages.return_value = packages_copy ex = self.assertRaises(SoftLayer.SoftLayerError, self.hardware._generate_create_dict, **MINIMAL_TEST_CREATE_ARGS) self.assertIn("Could not find valid location for: 'wdc01'", str(ex)) def test_generate_create_dict_invalid_size(self): args = { 'size': 'UNKNOWN_SIZE', 'hostname': 'unicorn', 'domain': 'giggles.woo', 'location': 'wdc01', 'os': 'UBUNTU_14_64', 'port_speed': 10, } ex = self.assertRaises(SoftLayer.SoftLayerError, self.hardware._generate_create_dict, **args) self.assertIn("Could not find valid size for: 'UNKNOWN_SIZE'", str(ex)) def test_generate_create_dict(self): args = { 'size': 'S1270_8GB_2X1TBSATA_NORAID', 'hostname': 'unicorn', 'domain': 'giggles.woo', 'location': 'wdc01', 'os': 'UBUNTU_14_64', 'port_speed': 10, 'hourly': True, 'extras': ['1_IPV6_ADDRESS'], 'post_uri': 'http://example.com/script.php', 'ssh_keys': [10], } expected = { 'hardware': [{ 'domain': 'giggles.woo', 'hostname': 'unicorn', }], 'location': 'WASHINGTON_DC', 'packageId': 200, 'presetId': 64, 'prices': [{'id': 21}, {'id': 420}, {'id': 906}, {'id': 37650}, {'id': 1800}, {'id': 272}, {'id': 17129}], 'useHourlyPricing': True, 'provisionScripts': ['http://example.com/script.php'], 'sshKeys': [{'sshKeyIds': [10]}], } data = self.hardware._generate_create_dict(**args) self.assertEqual(expected, data) @mock.patch('SoftLayer.managers.hardware.HardwareManager' '._generate_create_dict') def test_verify_order(self, create_dict): create_dict.return_value = {'test': 1, 'verify': 1} self.hardware.verify_order(test=1, verify=1) create_dict.assert_called_once_with(test=1, verify=1) self.assert_called_with('SoftLayer_Product_Order', 'verifyOrder', args=({'test': 1, 'verify': 1},)) @mock.patch('SoftLayer.managers.hardware.HardwareManager' '._generate_create_dict') def test_place_order(self, create_dict): create_dict.return_value = {'test': 1, 'verify': 1} self.hardware.place_order(test=1, verify=1) create_dict.assert_called_once_with(test=1, verify=1) self.assert_called_with('SoftLayer_Product_Order', 'placeOrder', args=({'test': 1, 'verify': 1},)) def test_cancel_hardware_without_reason(self): mock = self.set_mock('SoftLayer_Hardware_Server', 'getObject') mock.return_value = {'id': 987, 'billingItem': {'id': 1234}} result = self.hardware.cancel_hardware(987) self.assertEqual(result, True) reasons = self.hardware.get_cancellation_reasons() args = (False, False, reasons['unneeded'], '') self.assert_called_with('SoftLayer_Billing_Item', 'cancelItem', identifier=1234, args=args) def test_cancel_hardware_with_reason_and_comment(self): mock = self.set_mock('SoftLayer_Hardware_Server', 'getObject') mock.return_value = {'id': 987, 'billingItem': {'id': 1234}} result = self.hardware.cancel_hardware(6327, reason='sales', comment='Test Comment') self.assertEqual(result, True) reasons = self.hardware.get_cancellation_reasons() args = (False, False, reasons['sales'], 'Test Comment') self.assert_called_with('SoftLayer_Billing_Item', 'cancelItem', identifier=1234, args=args) def test_cancel_hardware(self): result = self.hardware.cancel_hardware(6327) self.assertEqual(result, True) self.assert_called_with('SoftLayer_Billing_Item', 'cancelItem', identifier=6327, args=(False, False, 'No longer needed', '')) def test_cancel_hardware_no_billing_item(self): mock = self.set_mock('SoftLayer_Hardware_Server', 'getObject') mock.return_value = {'id': 987} ex = self.assertRaises(SoftLayer.SoftLayerError, self.hardware.cancel_hardware, 6327) self.assertEqual("No billing item found for hardware", str(ex)) def test_change_port_speed_public(self): self.hardware.change_port_speed(2, True, 100) self.assert_called_with('SoftLayer_Hardware_Server', 'setPublicNetworkInterfaceSpeed', identifier=2, args=(100,)) def test_change_port_speed_private(self): self.hardware.change_port_speed(2, False, 10) self.assert_called_with('SoftLayer_Hardware_Server', 'setPrivateNetworkInterfaceSpeed', identifier=2, args=(10,)) def test_edit_meta(self): # Test editing user data self.hardware.edit(100, userdata='my data') self.assert_called_with('SoftLayer_Hardware_Server', 'setUserMetadata', args=(['my data'],), identifier=100) def test_edit_blank(self): # Now test a blank edit self.assertTrue(self.hardware.edit, 100) self.assertEqual(self.calls(), []) def test_edit(self): # Finally, test a full edit self.hardware.edit(100, hostname='new-host', domain='new.sftlyr.ws', notes='random notes') self.assert_called_with('SoftLayer_Hardware_Server', 'editObject', args=({ 'hostname': 'new-host', 'domain': 'new.sftlyr.ws', 'notes': 'random notes', },), identifier=100) def test_rescue(self): result = self.hardware.rescue(1234) self.assertEqual(result, True) self.assert_called_with('SoftLayer_Hardware_Server', 'bootToRescueLayer', identifier=1234) def test_update_firmware(self): result = self.hardware.update_firmware(100) self.assertEqual(result, True) self.assert_called_with('SoftLayer_Hardware_Server', 'createFirmwareUpdateTransaction', identifier=100, args=(1, 1, 1, 1)) def test_update_firmware_selective(self): result = self.hardware.update_firmware(100, ipmi=False, hard_drive=False) self.assertEqual(result, True) self.assert_called_with('SoftLayer_Hardware_Server', 'createFirmwareUpdateTransaction', identifier=100, args=(0, 1, 1, 0)) class HardwareHelperTests(testing.TestCase): def test_get_extra_price_id_no_items(self): ex = self.assertRaises(SoftLayer.SoftLayerError, managers.hardware._get_extra_price_id, [], 'test', True, None) self.assertEqual("Could not find valid price for extra option, 'test'", str(ex)) def test_get_default_price_id_item_not_first(self): items = [{ 'itemCategory': {'categoryCode': 'unknown', 'id': 325}, 'keyName': 'UNKNOWN', 'prices': [{'accountRestrictions': [], 'currentPriceFlag': '', 'hourlyRecurringFee': '10.0', 'id': 1245172, 'recurringFee': '1.0'}], }] ex = self.assertRaises(SoftLayer.SoftLayerError, managers.hardware._get_default_price_id, items, 'unknown', True, None) self.assertEqual("Could not find valid price for 'unknown' option", str(ex)) def test_get_default_price_id_no_items(self): ex = self.assertRaises(SoftLayer.SoftLayerError, managers.hardware._get_default_price_id, [], 'test', True, None) self.assertEqual("Could not find valid price for 'test' option", str(ex)) def test_get_bandwidth_price_id_no_items(self): ex = self.assertRaises(SoftLayer.SoftLayerError, managers.hardware._get_bandwidth_price_id, [], hourly=True, no_public=False) self.assertEqual("Could not find valid price for bandwidth option", str(ex)) def test_get_os_price_id_no_items(self): ex = self.assertRaises(SoftLayer.SoftLayerError, managers.hardware._get_os_price_id, [], 'UBUNTU_14_64', None) self.assertEqual("Could not find valid price for os: 'UBUNTU_14_64'", str(ex)) def test_get_port_speed_price_id_no_items(self): ex = self.assertRaises(SoftLayer.SoftLayerError, managers.hardware._get_port_speed_price_id, [], 10, True, None) self.assertEqual("Could not find valid price for port speed: '10'", str(ex)) softlayer-python-5.4.2/tests/managers/image_tests.py000066400000000000000000000130111324365065500226730ustar00rootroot00000000000000""" SoftLayer.tests.managers.image_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import SoftLayer from SoftLayer import testing IMAGE_SERVICE = 'SoftLayer_Virtual_Guest_Block_Device_Template_Group' class ImageTests(testing.TestCase): def set_up(self): self.image = SoftLayer.ImageManager(self.client) self.vgbdtg = self.client[''] self.account = self.client['Account'] def test_get_image(self): result = self.image.get_image(100) self.assertEqual(result['id'], 100) self.assertEqual(result['name'], 'test_image') self.assertEqual(result['accountId'], 1234) self.assert_called_with(IMAGE_SERVICE, 'getObject', identifier=100) def test_delete_image(self): self.image.delete_image(100) self.assert_called_with(IMAGE_SERVICE, 'deleteObject', identifier=100) def test_list_private_images(self): results = self.image.list_private_images() self.assertEqual(len(results), 2) self.assert_called_with('SoftLayer_Account', 'getPrivateBlockDeviceTemplateGroups') def test_list_private_images_with_filters(self): results = self.image.list_private_images( guid='0FA9ECBD-CF7E-4A1F-1E36F8D27C2B', name='name') self.assertEqual(len(results), 2) _filter = { 'privateBlockDeviceTemplateGroups': { 'globalIdentifier': { 'operation': '_= 0FA9ECBD-CF7E-4A1F-1E36F8D27C2B'}, 'name': {'operation': '_= name'}} } self.assert_called_with('SoftLayer_Account', 'getPrivateBlockDeviceTemplateGroups', filter=_filter) def test_list_public_images(self): results = self.image.list_public_images() self.assertEqual(len(results), 2) self.assert_called_with(IMAGE_SERVICE, 'getPublicImages') def test_list_public_images_with_filters(self): results = self.image.list_public_images( guid='0FA9ECBD-CF7E-4A1F-1E36F8D27C2B', name='name') _filter = { 'globalIdentifier': { 'operation': '_= 0FA9ECBD-CF7E-4A1F-1E36F8D27C2B'}, 'name': {'operation': '_= name'} } self.assertEqual(len(results), 2) self.assert_called_with(IMAGE_SERVICE, 'getPublicImages', filter=_filter) def test_resolve_ids_guid(self): result = self.image.resolve_ids('3C1F3C68-0B67-4F5E-8907-D0FC84BF3F12') self.assertEqual(['3C1F3C68-0B67-4F5E-8907-D0FC84BF3F12'], result) def test_resolve_ids_name_public(self): public_mock = self.set_mock(IMAGE_SERVICE, 'getPublicImages') public_mock.return_value = [{'id': 100}] private_mock = self.set_mock('SoftLayer_Account', 'getPrivateBlockDeviceTemplateGroups') private_mock.return_value = [] self.account.getPrivateBlockDeviceTemplateGroups.return_value = [] result = self.image.resolve_ids('image_name') self.assertEqual([100], result) def test_resolve_ids_name_private(self): public_mock = self.set_mock(IMAGE_SERVICE, 'getPublicImages') public_mock.return_value = [] private_mock = self.set_mock('SoftLayer_Account', 'getPrivateBlockDeviceTemplateGroups') private_mock.return_value = [{'id': 100}] result = self.image.resolve_ids('private_image_name') self.assertEqual([100], result) def test_resolve_ids_not_found(self): public_mock = self.set_mock(IMAGE_SERVICE, 'getPublicImages') public_mock.return_value = [] private_mock = self.set_mock('SoftLayer_Account', 'getPrivateBlockDeviceTemplateGroups') private_mock.return_value = [] result = self.image.resolve_ids('unknown_name') self.assertEqual([], result) def test_edit_tags(self): # Test updating tags self.image.edit(1, tag="tag1,tag2") self.assert_called_with(IMAGE_SERVICE, 'setTags', identifier=1, args=("tag1,tag2",)) def test_edit_blank(self): # Test a blank edit result = self.image.edit(1) self.assertEqual(result, False) self.assertEqual(self.calls(IMAGE_SERVICE, 'setTags'), []) def test_edit_full(self): # Finally test a full edit self.image.edit(1, name='abc', note='xyz') self.assert_called_with(IMAGE_SERVICE, 'editObject', identifier=1, args=({'name': 'abc', 'note': 'xyz'},)) def test_import_image(self): self.image.import_image_from_uri(name='test_image', note='testimage', uri='someuri', os_code='UBUNTU_LATEST') self.assert_called_with( IMAGE_SERVICE, 'createFromExternalSource', args=({'name': 'test_image', 'note': 'testimage', 'uri': 'someuri', 'operatingSystemReferenceCode': 'UBUNTU_LATEST'},)) def test_export_image(self): self.image.export_image_to_uri(1234, 'someuri') self.assert_called_with( IMAGE_SERVICE, 'copyToExternalSource', args=({'uri': 'someuri'},), identifier=1234) softlayer-python-5.4.2/tests/managers/ipsec_tests.py000066400000000000000000000361461324365065500227320ustar00rootroot00000000000000""" SoftLayer.tests.managers.ipsec_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ from mock import MagicMock import SoftLayer from SoftLayer.exceptions import SoftLayerAPIError from SoftLayer import testing class IPSECTests(testing.TestCase): def set_up(self): self.ipsec = SoftLayer.IPSECManager(self.client) def test_add_internal_subnet(self): mock = self.set_mock('SoftLayer_Network_Tunnel_Module_Context', 'addPrivateSubnetToNetworkTunnel') mock.return_value = True self.assertEqual(self.ipsec.add_internal_subnet(445, 565787), True) self.assert_called_with('SoftLayer_Network_Tunnel_Module_Context', 'addPrivateSubnetToNetworkTunnel', args=(565787,), identifier=445) def test_add_remote_subnet(self): mock = self.set_mock('SoftLayer_Network_Tunnel_Module_Context', 'addCustomerSubnetToNetworkTunnel') mock.return_value = True self.assertEqual(self.ipsec.add_remote_subnet(445, 565787), True) self.assert_called_with('SoftLayer_Network_Tunnel_Module_Context', 'addCustomerSubnetToNetworkTunnel', args=(565787,), identifier=445) def test_add_service_subnet(self): mock = self.set_mock('SoftLayer_Network_Tunnel_Module_Context', 'addServiceSubnetToNetworkTunnel') mock.return_value = True self.assertEqual(self.ipsec.add_service_subnet(445, 565787), True) self.assert_called_with('SoftLayer_Network_Tunnel_Module_Context', 'addServiceSubnetToNetworkTunnel', args=(565787,), identifier=445) def test_apply_configuration(self): mock = self.set_mock('SoftLayer_Network_Tunnel_Module_Context', 'applyConfigurationsToDevice') mock.return_value = True self.assertEqual(self.ipsec.apply_configuration(445), True) self.assert_called_with('SoftLayer_Network_Tunnel_Module_Context', 'applyConfigurationsToDevice', args=(), identifier=445) def test_create_remote_subnet(self): mock = self.set_mock('SoftLayer_Network_Customer_Subnet', 'createObject') mock.return_value = {'id': 565787, 'networkIdentifier': '50.0.0.0', 'cidr': 29, 'accountId': 999000} result = self.ipsec.create_remote_subnet(999000, '50.0.0.0', 29) self.assertEqual(result, mock.return_value) self.assert_called_with('SoftLayer_Network_Customer_Subnet', 'createObject', args=({'networkIdentifier': '50.0.0.0', 'cidr': 29, 'accountId': 999000},)) def test_create_translation(self): mock = self.set_mock('SoftLayer_Network_Tunnel_Module_Context', 'createAddressTranslation') mock.return_value = {'id': 787989, 'customerIpAddress': '50.0.0.0', 'customerIpAddressId': 672634, 'internalIpAddress': '10.0.0.0', 'internalIpAddressId': 871231, 'notes': 'first translation'} result = self.ipsec.create_translation(445, '10.0.0.0', '50.0.0.0', 'first translation') self.assertEqual(result, mock.return_value) self.assert_called_with('SoftLayer_Network_Tunnel_Module_Context', 'createAddressTranslation', args=({'customerIpAddress': '50.0.0.0', 'internalIpAddress': '10.0.0.0', 'notes': 'first translation'},), identifier=445) def test_delete_remote_subnet(self): mock = self.set_mock('SoftLayer_Network_Customer_Subnet', 'deleteObject') mock.return_value = True self.assertEqual(self.ipsec.delete_remote_subnet(565787), True) self.assert_called_with('SoftLayer_Network_Customer_Subnet', 'deleteObject', identifier=565787) def test_get_tunnel_context(self): _filter = {'networkTunnelContexts': {'id': {'operation': 445}}} _mask = '[mask[id]]' mock = self.set_mock('SoftLayer_Account', 'getNetworkTunnelContexts') mock.return_value = [{'id': 445}] result = self.ipsec.get_tunnel_context(445, mask=_mask) self.assertEqual(result, mock.return_value[0]) self.assert_called_with('SoftLayer_Account', 'getNetworkTunnelContexts', filter=_filter, mask=_mask) def test_get_tunnel_context_raises_error(self): mock = self.set_mock('SoftLayer_Account', 'getNetworkTunnelContexts') mock.return_value = [] self.assertRaises(SoftLayerAPIError, self.ipsec.get_tunnel_context, 445) def test_get_translation(self): mock = self.set_mock('SoftLayer_Account', 'getNetworkTunnelContexts') mock.return_value = [{'id': 445, 'addressTranslations': [{'id': 234123}, {'id': 872341}]}] self.assertEqual(self.ipsec.get_translation(445, 872341), {'id': 872341, 'customerIpAddress': '', 'internalIpAddress': ''}) self.assert_called_with('SoftLayer_Account', 'getNetworkTunnelContexts') def test_get_translation_raises_error(self): mock = self.set_mock('SoftLayer_Account', 'getNetworkTunnelContexts') mock.return_value = [{'id': 445, 'addressTranslations': [{'id': 234123}]}] self.assertRaises(SoftLayerAPIError, self.ipsec.get_translation, 445, 872341) def test_get_translations(self): _mask = ('[mask[addressTranslations[customerIpAddressRecord,' 'internalIpAddressRecord]]]') _filter = {'networkTunnelContexts': {'id': {'operation': 445}}} mock = self.set_mock('SoftLayer_Account', 'getNetworkTunnelContexts') mock.return_value = [{'id': 445, 'addressTranslations': [{ 'id': 234123, 'customerIpAddressRecord': {'ipAddress': '50.0.0.0'}, 'customerIpAddressId': 234112, 'internalIpAddressRecord': {'ipAddress': '10.0.0.0'}, 'internalIpAddressId': 234442 }]}] self.assertEqual(self.ipsec.get_translations(445), [{'id': 234123, 'customerIpAddress': '50.0.0.0', 'customerIpAddressId': 234112, 'internalIpAddress': '10.0.0.0', 'internalIpAddressId': 234442}]) self.assert_called_with('SoftLayer_Account', 'getNetworkTunnelContexts', filter=_filter, mask=_mask) def test_get_tunnel_contexts(self): _mask = '[mask[addressTranslations]]' mock = self.set_mock('SoftLayer_Account', 'getNetworkTunnelContexts') mock.return_value = [{'id': 445}, {'id': 446}] self.assertEqual(self.ipsec.get_tunnel_contexts(mask=_mask), mock.return_value) self.assert_called_with('SoftLayer_Account', 'getNetworkTunnelContexts', mask=_mask) def test_remove_internal_subnet(self): mock = self.set_mock('SoftLayer_Network_Tunnel_Module_Context', 'removePrivateSubnetFromNetworkTunnel') mock.return_value = True self.assertEqual(self.ipsec.remove_internal_subnet(445, 565787), True) self.assert_called_with('SoftLayer_Network_Tunnel_Module_Context', 'removePrivateSubnetFromNetworkTunnel', args=(565787,), identifier=445) def test_remove_remote_subnet(self): mock = self.set_mock('SoftLayer_Network_Tunnel_Module_Context', 'removeCustomerSubnetFromNetworkTunnel') mock.return_value = True self.assertEqual(self.ipsec.remove_remote_subnet(445, 565787), True) self.assert_called_with('SoftLayer_Network_Tunnel_Module_Context', 'removeCustomerSubnetFromNetworkTunnel', args=(565787,), identifier=445) def test_remove_service_subnet(self): mock = self.set_mock('SoftLayer_Network_Tunnel_Module_Context', 'removeServiceSubnetFromNetworkTunnel') mock.return_value = True self.assertEqual(self.ipsec.remove_service_subnet(445, 565787), True) self.assert_called_with('SoftLayer_Network_Tunnel_Module_Context', 'removeServiceSubnetFromNetworkTunnel', args=(565787,), identifier=445) def test_remove_translation(self): mock = self.set_mock('SoftLayer_Network_Tunnel_Module_Context', 'deleteAddressTranslation') mock.return_value = True self.assertEqual(self.ipsec.remove_translation(445, 787547), True) self.assert_called_with('SoftLayer_Network_Tunnel_Module_Context', 'deleteAddressTranslation', args=(787547,), identifier=445) def test_update_translation(self): mock = self.set_mock('SoftLayer_Network_Tunnel_Module_Context', 'editAddressTranslation') mock.return_value = True translation = {'id': 234123, 'customerIpAddress': '50.0.0.0', 'customerIpAddressId': 234112, 'internalIpAddress': '10.0.0.0', 'internalIpAddressId': 234442} self.ipsec.get_translation = MagicMock(return_value=translation) result = self.ipsec.update_translation(445, 234123, static_ip='10.0.0.2', remote_ip='50.0.0.2', notes='do not touch') self.assertEqual(result, True) self.ipsec.get_translation.assert_called_with(445, 234123) self.assert_called_with('SoftLayer_Network_Tunnel_Module_Context', 'editAddressTranslation', args=({'id': 234123, 'customerIpAddress': '50.0.0.2', 'internalIpAddress': '10.0.0.2', 'notes': 'do not touch'},), identifier=445) def test_update_tunnel_context(self): mock = self.set_mock('SoftLayer_Network_Tunnel_Module_Context', 'editObject') mock.return_value = True context = {'id': 445, 'name': 'der tunnel', 'friendlyName': 'the tunnel', 'internalPeerIpAddress': '10.0.0.1', 'customerPeerIpAddress': '50.0.0.1', 'advancedConfigurationFlag': 0, 'presharedKey': 'secret', 'phaseOneAuthentication': 'MD5', 'phaseOneDiffieHellmanGroup': 1, 'phaseOneEncryption': 'DES', 'phaseOneKeylife': 600, 'phaseTwoAuthentication': 'MD5', 'phaseTwoDiffieHellmanGroup': 1, 'phaseTwoEncryption': 'DES', 'phaseTwoKeylife': 600, 'phaseTwoPerfectForwardSecrecy': 0} self.ipsec.get_tunnel_context = MagicMock(return_value=context) result = self.ipsec.update_tunnel_context(445, friendly_name='ipsec tunnel', remote_peer='50.0.0.2', preshared_key='enigma', phase1_auth='SHA256', phase1_dh=5, phase1_crypto='AES256', phase1_key_ttl=120, phase2_auth='SHA128', phase2_dh=2, phase2_crypto='AES192', phase2_key_ttl=240, phase2_forward_secrecy=1) self.assertEqual(result, True) self.ipsec.get_tunnel_context.assert_called_with(445) self.assert_called_with('SoftLayer_Network_Tunnel_Module_Context', 'editObject', args=({'id': 445, 'name': 'der tunnel', 'friendlyName': 'ipsec tunnel', 'internalPeerIpAddress': '10.0.0.1', 'customerPeerIpAddress': '50.0.0.2', 'advancedConfigurationFlag': 0, 'presharedKey': 'enigma', 'phaseOneAuthentication': 'SHA256', 'phaseOneDiffieHellmanGroup': 5, 'phaseOneEncryption': 'AES256', 'phaseOneKeylife': 120, 'phaseTwoAuthentication': 'SHA128', 'phaseTwoDiffieHellmanGroup': 2, 'phaseTwoEncryption': 'AES192', 'phaseTwoKeylife': 240, 'phaseTwoPerfectForwardSecrecy': 1},), identifier=445) softlayer-python-5.4.2/tests/managers/loadbal_tests.py000066400000000000000000000173661324365065500232300ustar00rootroot00000000000000""" SoftLayer.tests.managers.loadbal_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import SoftLayer from SoftLayer import testing VIRT_IP_SERVICE = ('SoftLayer_Network_Application_Delivery_Controller_' 'LoadBalancer_VirtualIpAddress') class LoadBalancerTests(testing.TestCase): def set_up(self): self.lb_mgr = SoftLayer.LoadBalancerManager(self.client) def test_get_lb_pkgs(self): result = self.lb_mgr.get_lb_pkgs() self.assertEqual(len(result), 13) _filter = { 'items': { 'description': { 'operation': '*= Load Balancer' } } } self.assert_called_with('SoftLayer_Product_Package', 'getItems', identifier=0, filter=_filter) def test_get_hc_types(self): result = self.lb_mgr.get_hc_types() self.assertEqual(len(result), 6) service = ('SoftLayer_Network_Application_Delivery_Controller_' 'LoadBalancer_Health_Check_Type') self.assert_called_with(service, 'getAllObjects') def test_get_routing_methods(self): result = self.lb_mgr.get_routing_methods() self.assertEqual(len(result), 12) service = ('SoftLayer_Network_Application_Delivery_Controller_' 'LoadBalancer_Routing_Method') self.assert_called_with(service, 'getAllObjects') def test_get_location(self): id1 = self.lb_mgr._get_location('sjc01') self.assertEqual(id1, 168642) id2 = self.lb_mgr._get_location('dal05') self.assertEqual(id2, 'FIRST_AVAILABLE') def test_get_routing_types(self): result = self.lb_mgr.get_routing_types() self.assertEqual(len(result), 6) service = ('SoftLayer_Network_Application_Delivery_Controller_' 'LoadBalancer_Routing_Type') self.assert_called_with(service, 'getAllObjects') def test_cancel_lb(self): result = self.lb_mgr.cancel_lb(6327) self.assertEqual(result, True) self.assert_called_with('SoftLayer_Billing_Item', 'cancelService', identifier=21370814) def test_add_local_lb(self): self.lb_mgr.add_local_lb(6327, 'sjc01') args = ({ 'complexType': 'SoftLayer_Container_Product_Order_Network_' 'LoadBalancer', 'quantity': 1, 'packageId': 0, "location": 168642, 'prices': [{'id': 6327}] },) self.assert_called_with('SoftLayer_Product_Order', 'placeOrder', args=args) def test_get_local_lbs(self): result = self.lb_mgr.get_local_lbs() self.assertEqual(len(result), 0) mask = 'mask[loadBalancerHardware[datacenter],ipAddress]' self.assert_called_with('SoftLayer_Account', 'getAdcLoadBalancers', mask=mask) def test_get_local_lb(self): result = self.lb_mgr.get_local_lb(22348) self.assertEqual(result['id'], 22348) mask = ('mask[' 'loadBalancerHardware[datacenter], ' 'ipAddress, virtualServers[serviceGroups' '[routingMethod,routingType,services' '[healthChecks[type], groupReferences,' ' ipAddress]]]]') self.assert_called_with(VIRT_IP_SERVICE, 'getObject', identifier=22348, mask=mask) def test_delete_service(self): result = self.lb_mgr.delete_service(1234) self.assertEqual(result, True) service = ('SoftLayer_Network_Application_Delivery_Controller_' 'LoadBalancer_Service') self.assert_called_with(service, 'deleteObject', identifier=1234) def test_delete_service_group(self): result = self.lb_mgr.delete_service_group(1234) self.assertEqual(result, True) service = ('SoftLayer_Network_Application_Delivery_Controller_' 'LoadBalancer_VirtualServer') self.assert_called_with(service, 'deleteObject', identifier=1234) def test_toggle_service_status(self): result = self.lb_mgr.toggle_service_status(1234) self.assertEqual(result, True) service = ('SoftLayer_Network_Application_Delivery_Controller_' 'LoadBalancer_Service') self.assert_called_with(service, 'toggleStatus', identifier=1234) def test_edit_service(self): self.lb_mgr.edit_service(12345, 1234, '9.9.9.9', 80, True, 21, 1) _filter = { 'virtualServers': { 'serviceGroups': { 'services': { 'id': { 'operation': 1234 } } } } } mask = 'mask[serviceGroups[services[groupReferences,healthChecks]]]' self.assert_called_with(VIRT_IP_SERVICE, 'getVirtualServers', identifier=12345, filter=_filter, mask=mask) self.assert_called_with(VIRT_IP_SERVICE, 'editObject') def test_add_service(self): self.lb_mgr.add_service(12345, 50718, 123, 80, True, 21, 1) mask = 'mask[virtualServers[serviceGroups[services[groupReferences]]]]' self.assert_called_with(VIRT_IP_SERVICE, 'getObject', mask=mask, identifier=12345) self.assert_called_with(VIRT_IP_SERVICE, 'editObject', identifier=12345) arg = self.calls(VIRT_IP_SERVICE, 'editObject')[0].args[0] self.assertEqual( len(arg['virtualServers'][0]['serviceGroups'][0]['services']), 2) def test_edit_service_group(self): self.lb_mgr.edit_service_group(12345, group_id=50718, allocation=100, port=80, routing_type=2, routing_method=10) mask = 'mask[virtualServers[serviceGroups[services[groupReferences]]]]' self.assert_called_with(VIRT_IP_SERVICE, 'getObject', identifier=12345, mask=mask) self.assert_called_with(VIRT_IP_SERVICE, 'getObject', identifier=12345) def test_add_service_group(self): self.lb_mgr.add_service_group(12345, 100, 80, 2, 10) mask = 'mask[virtualServers[serviceGroups[services[groupReferences]]]]' self.assert_called_with(VIRT_IP_SERVICE, 'getObject', mask=mask, identifier=12345) self.assert_called_with(VIRT_IP_SERVICE, 'editObject', identifier=12345) arg = self.calls(VIRT_IP_SERVICE, 'editObject')[0].args[0] self.assertEqual(len(arg['virtualServers']), 2) def test_reset_service_group(self): result = self.lb_mgr.reset_service_group(12345, group_id=50718) self.assertEqual(result, True) _filter = {'virtualServers': {'id': {'operation': 50718}}} self.assert_called_with(VIRT_IP_SERVICE, 'getVirtualServers', identifier=12345, filter=_filter, mask='mask[serviceGroups]') service = ('SoftLayer_Network_Application_Delivery_Controller_' 'LoadBalancer_Service_Group') self.assert_called_with(service, 'kickAllConnections', identifier=51758) softlayer-python-5.4.2/tests/managers/metadata_tests.py000066400000000000000000000060251324365065500234000ustar00rootroot00000000000000""" SoftLayer.tests.managers.metadata_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import SoftLayer from SoftLayer import testing class MetadataTests(testing.TestCase): def set_up(self): self.metadata = SoftLayer.MetadataManager(client=self.client) def test_get(self): mock = self.set_mock('SoftLayer_Resource_Metadata', 'getDatacenter') mock.return_value = 'dal01' resp = self.metadata.get('datacenter') self.assertEqual('dal01', resp) self.assert_called_with('SoftLayer_Resource_Metadata', 'getDatacenter', identifier=None) def test_no_param(self): resp = self.metadata.get('datacenter') self.assertEqual('dal01', resp) self.assert_called_with('SoftLayer_Resource_Metadata', 'getDatacenter', identifier=None, args=tuple()) def test_w_param(self): resp = self.metadata.get('vlans', '1:2:3:4:5') self.assertEqual([10, 124], resp) self.assert_called_with('SoftLayer_Resource_Metadata', 'getVlans', args=('1:2:3:4:5',)) def test_user_data(self): resp = self.metadata.get('user_data') self.assertEqual(resp, 'User-supplied data') self.assert_called_with('SoftLayer_Resource_Metadata', 'getUserMetadata', identifier=None) def test_return_none(self): mock = self.set_mock('SoftLayer_Resource_Metadata', 'getDatacenter') mock.return_value = None resp = self.metadata.get('datacenter') self.assertEqual(None, resp) def test_404(self): mock = self.set_mock('SoftLayer_Resource_Metadata', 'getUserMetadata') mock.side_effect = SoftLayer.SoftLayerAPIError(404, 'Not Found') resp = self.metadata.get('user_data') self.assertEqual(None, resp) def test_error(self): exception = SoftLayer.SoftLayerAPIError(500, 'Error') mock = self.set_mock('SoftLayer_Resource_Metadata', 'getUserMetadata') mock.side_effect = exception self.assertRaises(SoftLayer.SoftLayerAPIError, self.metadata.get, 'user_data') def test_w_param_error(self): self.assertRaises(SoftLayer.SoftLayerError, self.metadata.get, 'vlans') def test_not_exists(self): self.assertRaises(SoftLayer.SoftLayerError, self.metadata.get, 'something') def test_networks(self): r = self.metadata.public_network() self.assertEqual({ 'vlan_ids': [8384, 12446], 'router': 'brc01', 'vlans': [10, 124], 'mac_addresses': ['06-00-00-00-00-00'], }, r) r = self.metadata.private_network() self.assertEqual({ 'vlan_ids': [8384, 12446], 'router': 'brc01', 'vlans': [10, 124], 'mac_addresses': ['07-00-00-00-00-00'], }, r) softlayer-python-5.4.2/tests/managers/network_tests.py000066400000000000000000000443751324365065500233230ustar00rootroot00000000000000""" SoftLayer.tests.managers.network_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import SoftLayer from SoftLayer import fixtures from SoftLayer.managers import network from SoftLayer import testing class NetworkTests(testing.TestCase): def set_up(self): self.network = SoftLayer.NetworkManager(self.client) def test_ip_lookup(self): result = self.network.ip_lookup('10.0.1.37') expected = fixtures.SoftLayer_Network_Subnet_IpAddress.getByIpAddress self.assertEqual(result, expected) self.assert_called_with('SoftLayer_Network_Subnet_IpAddress', 'getByIpAddress', args=('10.0.1.37',)) def test_add_subnet_raises_exception_on_failure(self): self.assertRaises(TypeError, self.network.add_subnet, ('bad')) def test_add_global_ip(self): # Test a global IPv4 order result = self.network.add_global_ip(test_order=True) self.assertEqual(fixtures.SoftLayer_Product_Order.verifyOrder, result) def test_add_securitygroup_rule(self): result = self.network.add_securitygroup_rule(100, remote_ip='10.0.0.0/24', direction='ingress', ethertype='IPv4', port_min=95, port_max=100, protocol='tcp') self.assertTrue(result) self.assert_called_with('SoftLayer_Network_SecurityGroup', 'addRules', identifier=100, args=([{'remoteIp': '10.0.0.0/24', 'direction': 'ingress', 'ethertype': 'IPv4', 'portRangeMin': 95, 'portRangeMax': 100, 'protocol': 'tcp'}],)) def test_add_securitygroup_rules(self): rule1 = {'remoteIp': '10.0.0.0/24', 'direction': 'ingress', 'ethertype': 'IPv4', 'portRangeMin': 95, 'portRangeMax': 100, 'protocol': 'tcp'} rule2 = {'remoteGroupId': 102, 'direction': 'egress', 'ethertype': 'IPv4'} result = self.network.add_securitygroup_rules(100, [rule1, rule2]) self.assertTrue(result) self.assert_called_with('SoftLayer_Network_SecurityGroup', 'addRules', identifier=100, args=([rule1, rule2],)) def test_add_securitygroup_rules_with_dict_error(self): rule = {'remoteIp': '10.0.0.0/24', 'direction': 'ingress'} self.assertRaises(TypeError, self.network.add_securitygroup_rules, rule) def test_add_subnet_for_ipv4(self): # Test a four public address IPv4 order result = self.network.add_subnet('public', quantity=4, vlan_id=1234, version=4, test_order=True) self.assertEqual(fixtures.SoftLayer_Product_Order.verifyOrder, result) result = self.network.add_subnet('public', quantity=4, vlan_id=1234, version=4, test_order=False) self.assertEqual(fixtures.SoftLayer_Product_Order.verifyOrder, result) result = self.network.add_subnet('global', test_order=True) self.assertEqual(fixtures.SoftLayer_Product_Order.verifyOrder, result) def test_add_subnet_for_ipv6(self): # Test a public IPv6 order result = self.network.add_subnet('public', quantity=64, vlan_id=45678, version=6, test_order=True) self.assertEqual(fixtures.SoftLayer_Product_Order.verifyOrder, result) # Test a global IPv6 order result = self.network.add_subnet('global', version=6, test_order=True) self.assertEqual(fixtures.SoftLayer_Product_Order.verifyOrder, result) def test_assign_global_ip(self): result = self.network.assign_global_ip(9876, '172.16.24.76') self.assertEqual(result, True) self.assert_called_with('SoftLayer_Network_Subnet_IpAddress_Global', 'route', identifier=9876, args=('172.16.24.76',)) def test_attach_securitygroup_component(self): result = self.network.attach_securitygroup_component(100, 500) self.assertTrue(result) self.assert_called_with('SoftLayer_Network_SecurityGroup', 'attachNetworkComponents', identifier=100, args=([500],)) def test_attach_securitygroup_components(self): result = self.network.attach_securitygroup_components(100, [500, 600]) self.assertTrue(result) self.assert_called_with('SoftLayer_Network_SecurityGroup', 'attachNetworkComponents', identifier=100, args=([500, 600],)) def test_cancel_global_ip(self): result = self.network.cancel_global_ip(1234) self.assertEqual(result, True) self.assert_called_with('SoftLayer_Billing_Item', 'cancelService', identifier=1234) def test_cancel_subnet(self): result = self.network.cancel_subnet(1234) self.assertEqual(result, True) self.assert_called_with('SoftLayer_Billing_Item', 'cancelService', identifier=1056) def test_create_securitygroup(self): result = self.network.create_securitygroup(name='foo', description='bar') sg_fixture = fixtures.SoftLayer_Network_SecurityGroup self.assertEqual(sg_fixture.createObject, result) self.assert_called_with('SoftLayer_Network_SecurityGroup', 'createObject', args=({'name': 'foo', 'description': 'bar'},)) def test_delete_securitygroup(self): result = self.network.delete_securitygroup(100) self.assertTrue(result) self.assert_called_with('SoftLayer_Network_SecurityGroup', 'deleteObject', identifier=100) def test_detach_securitygroup_component(self): result = self.network.detach_securitygroup_component(100, 500) self.assertTrue(result) self.assert_called_with('SoftLayer_Network_SecurityGroup', 'detachNetworkComponents', identifier=100, args=([500],)) def test_detach_securitygroup_components(self): result = self.network.detach_securitygroup_components(100, [500, 600]) self.assertTrue(result) self.assert_called_with('SoftLayer_Network_SecurityGroup', 'detachNetworkComponents', identifier=100, args=([500, 600],)) def test_edit_rwhois(self): result = self.network.edit_rwhois( abuse_email='abuse@test.foo', address1='123 Test Street', address2='Apt. #31', city='Anywhere', company_name='TestLayer', country='US', first_name='Bob', last_name='Bobinson', postal_code='9ba62', private_residence=False, state='TX') self.assertEqual(result, True) expected = { 'abuseEmail': 'abuse@test.foo', 'address1': '123 Test Street', 'address2': 'Apt. #31', 'city': 'Anywhere', 'companyName': 'TestLayer', 'country': 'US', 'firstName': 'Bob', 'lastName': 'Bobinson', 'postalCode': '9ba62', 'privateResidenceFlag': False, 'state': 'TX', } self.assert_called_with('SoftLayer_Network_Subnet_Rwhois_Data', 'editObject', identifier='id', args=(expected,)) def test_edit_securitygroup(self): result = self.network.edit_securitygroup(100, name='foobar') self.assertTrue(result) self.assert_called_with('SoftLayer_Network_SecurityGroup', 'editObject', identifier=100, args=({'name': 'foobar'},)) def test_edit_securitygroup_rule(self): result = self.network.edit_securitygroup_rule(100, 500, direction='ingress') self.assertTrue(result) self.assert_called_with('SoftLayer_Network_SecurityGroup', 'editRules', identifier=100, args=([{'id': 500, 'direction': 'ingress'}],)) def test_edit_securitygroup_rule_unset(self): # Test calling edit rule with falsy values, which are used # to unset those values in the API result = self.network.edit_securitygroup_rule(100, 500, protocol='', port_min=-1, port_max=-1, ethertype='', remote_ip='') self.assertTrue(result) self.assert_called_with('SoftLayer_Network_SecurityGroup', 'editRules', identifier=100, args=([{'id': 500, 'protocol': '', 'portRangeMin': -1, 'portRangeMax': -1, 'ethertype': '', 'remoteIp': ''}],)) def test_get_rwhois(self): result = self.network.get_rwhois() self.assertEqual(result, fixtures.SoftLayer_Account.getRwhoisData) self.assert_called_with('SoftLayer_Account', 'getRwhoisData') def test_get_securitygroup(self): result = self.network.get_securitygroup(100) sg_fixture = fixtures.SoftLayer_Network_SecurityGroup self.assertEqual(sg_fixture.getObject, result) self.assert_called_with('SoftLayer_Network_SecurityGroup', 'getObject', identifier=100) def test_get_subnet(self): result = self.network.get_subnet(9876) self.assertEqual(result, fixtures.SoftLayer_Network_Subnet.getObject) self.assert_called_with('SoftLayer_Network_Subnet', 'getObject', identifier=9876) def test_get_vlan(self): result = self.network.get_vlan(1234) self.assertEqual(result, fixtures.SoftLayer_Network_Vlan.getObject) self.assert_called_with('SoftLayer_Network_Vlan', 'getObject', identifier=1234) def test_list_global_ips_default(self): result = self.network.list_global_ips() self.assertEqual(result, fixtures.SoftLayer_Account.getGlobalIpRecords) mask = 'mask[destinationIpAddress[hardware, virtualGuest],ipAddress]' self.assert_called_with('SoftLayer_Account', 'getGlobalIpRecords', mask=mask) def test_list_global_ips_with_filter(self): result = self.network.list_global_ips(version=4) self.assertEqual(result, fixtures.SoftLayer_Account.getGlobalIpRecords) mask = 'mask[destinationIpAddress[hardware, virtualGuest],ipAddress]' _filter = { 'globalIpRecords': { 'ipAddress': { 'subnet': { 'version': {'operation': 4}, } } } } mask = 'mask[destinationIpAddress[hardware, virtualGuest],ipAddress]' self.assert_called_with('SoftLayer_Account', 'getGlobalIpRecords', mask=mask, filter=_filter) def test_list_subnets_default(self): result = self.network.list_subnets() self.assertEqual(result, fixtures.SoftLayer_Account.getSubnets) _filter = {'subnets': {'subnetType': {'operation': '!= GLOBAL_IP'}}} self.assert_called_with('SoftLayer_Account', 'getSubnets', mask='mask[%s]' % network.DEFAULT_SUBNET_MASK, filter=_filter) def test_list_subnets_with_filters(self): result = self.network.list_subnets( identifier='10.0.0.1', datacenter='dal00', version=4, subnet_type='PRIMARY', network_space='PUBLIC', ) self.assertEqual(result, fixtures.SoftLayer_Account.getSubnets) _filter = { 'subnets': { 'networkIdentifier': {'operation': '_= 10.0.0.1'}, 'datacenter': { 'name': {'operation': '_= dal00'} }, 'version': {'operation': 4}, 'subnetType': {'operation': '_= PRIMARY'}, 'networkVlan': {'networkSpace': {'operation': '_= PUBLIC'}}, } } self.assert_called_with('SoftLayer_Account', 'getSubnets', mask='mask[%s]' % network.DEFAULT_SUBNET_MASK, filter=_filter) def test_list_vlans_default(self): result = self.network.list_vlans() self.assertEqual(result, fixtures.SoftLayer_Account.getNetworkVlans) self.assert_called_with('SoftLayer_Account', 'getNetworkVlans') def test_list_vlans_with_filters(self): result = self.network.list_vlans( vlan_number=5, datacenter='dal00', name='primary-vlan', ) self.assertEqual(result, fixtures.SoftLayer_Account.getNetworkVlans) _filter = { 'networkVlans': { 'primaryRouter': { 'datacenter': { 'name': {'operation': '_= dal00'}}, }, 'vlanNumber': {'operation': 5}, 'name': {'operation': '_= primary-vlan'}, }, } self.assert_called_with('SoftLayer_Account', 'getNetworkVlans', filter=_filter) def test_list_securitygroups(self): result = self.network.list_securitygroups() sg_fixture = fixtures.SoftLayer_Network_SecurityGroup self.assertEqual(sg_fixture.getAllObjects, result) def test_list_securitygroup_rules(self): result = self.network.list_securitygroup_rules(100) sg_fixture = fixtures.SoftLayer_Network_SecurityGroup self.assert_called_with('SoftLayer_Network_SecurityGroup', 'getRules', identifier=100) self.assertEqual(sg_fixture.getRules, result) def test_remove_securitygroup_rule(self): result = self.network.remove_securitygroup_rule(100, 500) self.assertTrue(result) self.assert_called_with('SoftLayer_Network_SecurityGroup', 'removeRules', identifier=100, args=([500],)) def test_remove_securitygroup_rules(self): result = self.network.remove_securitygroup_rules(100, [500, 600]) self.assertTrue(result) self.assert_called_with('SoftLayer_Network_SecurityGroup', 'removeRules', identifier=100, args=([500, 600],)) def test_summary_by_datacenter(self): result = self.network.summary_by_datacenter() expected = {'dal00': {'hardware_count': 1, 'virtual_guest_count': 1, 'subnet_count': 0, 'public_ip_count': 6, 'vlan_count': 3}} self.assertEqual(expected, result) def test_resolve_global_ip_ids(self): _id = self.network.resolve_global_ip_ids('10.0.0.1') self.assertEqual(_id, ['200', '201']) def test_resolve_global_ip_ids_no_results(self): mock = self.set_mock('SoftLayer_Account', 'getGlobalIpRecords') mock.return_value = [] _id = self.network.resolve_global_ip_ids('nope') self.assertEqual(_id, []) def test_resolve_subnet_ids(self): _id = self.network.resolve_subnet_ids('10.0.0.1/29') self.assertEqual(_id, ['100']) def test_resolve_subnet_ids_no_results(self): mock = self.set_mock('SoftLayer_Account', 'getSubnets') mock.return_value = [] _id = self.network.resolve_subnet_ids('nope') self.assertEqual(_id, []) def test_resolve_vlan_ids(self): mock = self.set_mock('SoftLayer_Account', 'getNetworkVlans') mock.return_value = [{'id': '100'}] _id = self.network.resolve_vlan_ids('vlan_name') self.assertEqual(_id, ['100']) def test_resolve_vlan_ids_no_results(self): mock = self.set_mock('SoftLayer_Account', 'getNetworkVlans') mock.return_value = [] _id = self.network.resolve_vlan_ids('nope') self.assertEqual(_id, []) def test_unassign_global_ip(self): result = self.network.unassign_global_ip(9876) self.assertEqual(result, True) self.assert_called_with('SoftLayer_Network_Subnet_IpAddress_Global', 'unroute', identifier=9876) softlayer-python-5.4.2/tests/managers/object_storage_tests.py000066400000000000000000000030411324365065500246050ustar00rootroot00000000000000""" SoftLayer.tests.managers.object_storage_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import SoftLayer from SoftLayer import fixtures from SoftLayer import testing class ObjectStorageTests(testing.TestCase): def set_up(self): self.object_storage = SoftLayer.ObjectStorageManager(self.client) def test_list_accounts(self): accounts = self.object_storage.list_accounts() self.assertEqual(accounts, fixtures.SoftLayer_Account.getHubNetworkStorage) def test_list_endpoints(self): accounts = self.set_mock('SoftLayer_Account', 'getHubNetworkStorage') accounts.return_value = { 'storageNodes': [{ 'datacenter': {'name': 'dal05'}, 'frontendIpAddress': 'https://dal05/auth/v1.0/', 'backendIpAddress': 'https://dal05/auth/v1.0/'} ], } endpoints = self.object_storage.list_endpoints() self.assertEqual(endpoints, [{'datacenter': {'name': 'dal05'}, 'private': 'https://dal05/auth/v1.0/', 'public': 'https://dal05/auth/v1.0/'}]) def test_list_endpoints_no_results(self): accounts = self.set_mock('SoftLayer_Account', 'getHubNetworkStorage') accounts.return_value = { 'storageNodes': [], } endpoints = self.object_storage.list_endpoints() self.assertEqual(endpoints, []) softlayer-python-5.4.2/tests/managers/ordering_tests.py000066400000000000000000000375471324365065500234460ustar00rootroot00000000000000""" SoftLayer.tests.managers.ordering_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import mock import SoftLayer from SoftLayer import exceptions from SoftLayer import fixtures from SoftLayer import testing class OrderingTests(testing.TestCase): def set_up(self): self.ordering = SoftLayer.OrderingManager(self.client) def test_get_package_by_type_returns_no_outlet_packages(self): packages = self._get_server_packages() filtered_packages = self.ordering.filter_outlet_packages(packages) for package_id in [27, 28]: self._assert_package_id_not_present(package_id, filtered_packages) def _get_server_packages(self): return self.ordering.get_packages_of_type(['BARE_METAL_CPU']) def _assert_package_id_not_present(self, package_id, packages): package_ids = [] for package in packages: package_ids.append(package['id']) self.assertNotIn(package_id, package_ids) def test_get_active_packages(self): packages = self._get_server_packages() filtered_packages = self.ordering.get_only_active_packages(packages) for package_id in [15]: self._assert_package_id_not_present(package_id, filtered_packages) def test_get_package_by_type_returns_if_found(self): package_type = "BARE_METAL_CORE" mask = "mask[id, name]" package = self.ordering.get_package_by_type(package_type, mask) self.assertIsNotNone(package) def test_get_package_by_type_returns_none_if_not_found(self): p_mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') p_mock.return_value = [] package = self.ordering.get_package_by_type("PIZZA_FLAVORED_SERVERS") self.assertIsNone(package) def test_get_package_id_by_type_returns_valid_id(self): p_mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') p_mock.return_value = [ {'id': 46, 'name': 'Virtual Servers', 'description': 'Virtual Server Instances', 'type': {'keyName': 'VIRTUAL_SERVER_INSTANCE'}, 'isActive': 1}, ] package_type = "VIRTUAL_SERVER_INSTANCE" package_id = self.ordering.get_package_id_by_type(package_type) self.assertEqual(46, package_id) def test_get_package_id_by_type_fails_for_nonexistent_package_type(self): p_mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') p_mock.return_value = [] self.assertRaises(ValueError, self.ordering.get_package_id_by_type, "STRAWBERRY_FLAVORED_SERVERS") def test_get_order_container(self): container = self.ordering.get_order_container(1234) quote = self.ordering.client['Billing_Order_Quote'] container_fixture = quote.getRecalculatedOrderContainer(id=1234) self.assertEqual(container, container_fixture['orderContainers'][0]) def test_get_quotes(self): quotes = self.ordering.get_quotes() quotes_fixture = self.ordering.client['Account'].getActiveQuotes() self.assertEqual(quotes, quotes_fixture) def test_get_quote_details(self): quote = self.ordering.get_quote_details(1234) quote_service = self.ordering.client['Billing_Order_Quote'] quote_fixture = quote_service.getObject(id=1234) self.assertEqual(quote, quote_fixture) def test_verify_quote(self): result = self.ordering.verify_quote(1234, [{'hostname': 'test1', 'domain': 'example.com'}], quantity=1) self.assertEqual(result, fixtures.SoftLayer_Product_Order.verifyOrder) self.assert_called_with('SoftLayer_Product_Order', 'verifyOrder') def test_order_quote(self): result = self.ordering.order_quote(1234, [{'hostname': 'test1', 'domain': 'example.com'}], quantity=1) self.assertEqual(result, fixtures.SoftLayer_Product_Order.placeOrder) self.assert_called_with('SoftLayer_Product_Order', 'placeOrder') def test_generate_order_template(self): result = self.ordering.generate_order_template( 1234, [{'hostname': 'test1', 'domain': 'example.com'}], quantity=1) self.assertEqual(result, {'presetId': None, 'hardware': [{'domain': 'example.com', 'hostname': 'test1'}], 'useHourlyPricing': '', 'packageId': 50, 'prices': [{'id': 1921}], 'quantity': 1}) def test_generate_order_template_extra_quantity(self): self.assertRaises(ValueError, self.ordering.generate_order_template, 1234, [], quantity=1) def test_get_package_by_key_returns_if_found(self): package_keyname = "BARE_METAL_SERVER" mask = "mask[id, name]" package = self.ordering.get_package_by_key(package_keyname, mask) self.assertIsNotNone(package) def test_get_package_by_key_returns_none_if_not_found(self): p_mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') p_mock.return_value = [] self.assertRaises(exceptions.SoftLayerError, self.ordering.get_package_by_key, 'WILLY_NILLY_SERVERS') def test_list_categories(self): p_mock = self.set_mock('SoftLayer_Product_Package', 'getConfiguration') p_mock.return_value = ['cat1', 'cat2'] with mock.patch.object(self.ordering, 'get_package_by_key') as mock_get_pkg: mock_get_pkg.return_value = {'id': 1234} cats = self.ordering.list_categories('PACKAGE_KEYNAME') mock_get_pkg.assert_called_once_with('PACKAGE_KEYNAME', mask='id') self.assertEqual(p_mock.return_value, cats) def test_list_items(self): p_mock = self.set_mock('SoftLayer_Product_Package', 'getItems') p_mock.return_value = ['item1', 'item2'] with mock.patch.object(self.ordering, 'get_package_by_key') as mock_get_pkg: mock_get_pkg.return_value = {'id': 1234} items = self.ordering.list_items('PACKAGE_KEYNAME') mock_get_pkg.assert_called_once_with('PACKAGE_KEYNAME', mask='id') self.assertEqual(p_mock.return_value, items) def test_list_packages(self): packages = [{'id': 1234, 'isActive': True}, {'id': 1235, 'isActive': True}] p_mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') p_mock.return_value = packages actual_pkgs = self.ordering.list_packages() self.assertEqual(packages, actual_pkgs) def test_list_packages_not_active(self): packages = [{'id': 1234, 'isActive': True}, {'id': 1235, 'isActive': False}] p_mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') p_mock.return_value = packages actual_pkgs = self.ordering.list_packages() # Make sure that the list returned only contained the package # that was active self.assertEqual([packages[0]], actual_pkgs) def test_list_presets(self): acct_presets = ['acctPreset1', 'acctPreset2'] active_presets = ['activePreset3', 'activePreset4'] acct_preset_mock = self.set_mock('SoftLayer_Product_Package', 'getAccountRestrictedActivePresets') active_preset_mock = self.set_mock('SoftLayer_Product_Package', 'getActivePresets') acct_preset_mock.return_value = acct_presets active_preset_mock.return_value = active_presets presets = self.ordering.list_presets('PACKAGE_KEYNAME') # Make sure the preset list returns both active presets and # account restricted presets self.assertEqual(active_presets + acct_presets, presets) def test_get_preset_by_key(self): keyname = 'PRESET_KEYNAME' preset_filter = {'activePresets': {'keyName': {'operation': '_= %s' % keyname}}} with mock.patch.object(self.ordering, 'list_presets') as list_mock: list_mock.return_value = ['preset1'] preset = self.ordering.get_preset_by_key('PACKAGE_KEYNAME', keyname) list_mock.assert_called_once_with('PACKAGE_KEYNAME', filter=preset_filter, mask=None) self.assertEqual(list_mock.return_value[0], preset) def test_get_preset_by_key_preset_not_found(self): keyname = 'PRESET_KEYNAME' preset_filter = {'activePresets': {'keyName': {'operation': '_= %s' % keyname}}} with mock.patch.object(self.ordering, 'list_presets') as list_mock: list_mock.return_value = [] exc = self.assertRaises(exceptions.SoftLayerError, self.ordering.get_preset_by_key, 'PACKAGE_KEYNAME', keyname) list_mock.assert_called_once_with('PACKAGE_KEYNAME', filter=preset_filter, mask=None) self.assertEqual('Preset {} does not exist in package {}'.format(keyname, 'PACKAGE_KEYNAME'), str(exc)) def test_get_price_id_list(self): price1 = {'id': 1234, 'locationGroupId': ''} item1 = {'id': 1111, 'keyName': 'ITEM1', 'prices': [price1]} price2 = {'id': 5678, 'locationGroupId': ''} item2 = {'id': 2222, 'keyName': 'ITEM2', 'prices': [price2]} with mock.patch.object(self.ordering, 'list_items') as list_mock: list_mock.return_value = [item1, item2] prices = self.ordering.get_price_id_list('PACKAGE_KEYNAME', ['ITEM1', 'ITEM2']) list_mock.assert_called_once_with('PACKAGE_KEYNAME', mask='id, keyName, prices') self.assertEqual([price1['id'], price2['id']], prices) def test_get_price_id_list_item_not_found(self): price1 = {'id': 1234, 'locationGroupId': ''} item1 = {'id': 1111, 'keyName': 'ITEM1', 'prices': [price1]} with mock.patch.object(self.ordering, 'list_items') as list_mock: list_mock.return_value = [item1] exc = self.assertRaises(exceptions.SoftLayerError, self.ordering.get_price_id_list, 'PACKAGE_KEYNAME', ['ITEM2']) list_mock.assert_called_once_with('PACKAGE_KEYNAME', mask='id, keyName, prices') self.assertEqual("Item ITEM2 does not exist for package PACKAGE_KEYNAME", str(exc)) def test_generate_no_complex_type(self): pkg = 'PACKAGE_KEYNAME' items = ['ITEM1', 'ITEM2'] exc = self.assertRaises(exceptions.SoftLayerError, self.ordering.generate_order, pkg, 'DALLAS13', items) self.assertEqual("A complex type must be specified with the order", str(exc)) def test_generate_order_with_preset(self): pkg = 'PACKAGE_KEYNAME' complex_type = 'SoftLayer_Container_Foo' items = ['ITEM1', 'ITEM2'] preset = 'PRESET_KEYNAME' expected_order = {'complexType': 'SoftLayer_Container_Foo', 'location': 'DALLAS13', 'packageId': 1234, 'presetId': 5678, 'prices': [{'id': 1111}, {'id': 2222}], 'quantity': 1, 'useHourlyPricing': True} mock_pkg, mock_preset, mock_get_ids = self._patch_for_generate() order = self.ordering.generate_order(pkg, 'DALLAS13', items, preset_keyname=preset, complex_type=complex_type) mock_pkg.assert_called_once_with(pkg, mask='id') mock_preset.assert_called_once_with(pkg, preset) mock_get_ids.assert_called_once_with(pkg, items) self.assertEqual(expected_order, order) def test_generate_order(self): pkg = 'PACKAGE_KEYNAME' items = ['ITEM1', 'ITEM2'] complex_type = 'My_Type' expected_order = {'complexType': 'My_Type', 'location': 'DALLAS13', 'packageId': 1234, 'prices': [{'id': 1111}, {'id': 2222}], 'quantity': 1, 'useHourlyPricing': True} mock_pkg, mock_preset, mock_get_ids = self._patch_for_generate() order = self.ordering.generate_order(pkg, 'DALLAS13', items, complex_type=complex_type) mock_pkg.assert_called_once_with(pkg, mask='id') mock_preset.assert_not_called() mock_get_ids.assert_called_once_with(pkg, items) self.assertEqual(expected_order, order) def test_verify_order(self): ord_mock = self.set_mock('SoftLayer_Product_Order', 'verifyOrder') ord_mock.return_value = {'id': 1234} pkg = 'PACKAGE_KEYNAME' location = 'DALLAS13' items = ['ITEM1', 'ITEM2'] hourly = True preset_keyname = 'PRESET' complex_type = 'Complex_Type' extras = {'foo': 'bar'} quantity = 1 with mock.patch.object(self.ordering, 'generate_order') as gen_mock: gen_mock.return_value = {'order': {}} order = self.ordering.verify_order(pkg, location, items, hourly=hourly, preset_keyname=preset_keyname, complex_type=complex_type, extras=extras, quantity=quantity) gen_mock.assert_called_once_with(pkg, location, items, hourly=hourly, preset_keyname=preset_keyname, complex_type=complex_type, extras=extras, quantity=quantity) self.assertEqual(ord_mock.return_value, order) def test_place_order(self): ord_mock = self.set_mock('SoftLayer_Product_Order', 'placeOrder') ord_mock.return_value = {'id': 1234} pkg = 'PACKAGE_KEYNAME' location = 'DALLAS13' items = ['ITEM1', 'ITEM2'] hourly = True preset_keyname = 'PRESET' complex_type = 'Complex_Type' extras = {'foo': 'bar'} quantity = 1 with mock.patch.object(self.ordering, 'generate_order') as gen_mock: gen_mock.return_value = {'order': {}} order = self.ordering.place_order(pkg, location, items, hourly=hourly, preset_keyname=preset_keyname, complex_type=complex_type, extras=extras, quantity=quantity) gen_mock.assert_called_once_with(pkg, location, items, hourly=hourly, preset_keyname=preset_keyname, complex_type=complex_type, extras=extras, quantity=quantity) self.assertEqual(ord_mock.return_value, order) def test_locations(self): locations = self.ordering.package_locations('BARE_METAL_CPU') self.assertEqual('WASHINGTON07', locations[0]['keyname']) def _patch_for_generate(self): # mock out get_package_by_key, get_preset_by_key, and get_price_id_list # with patchers mock_pkg = mock.patch.object(self.ordering, 'get_package_by_key') mock_preset = mock.patch.object(self.ordering, 'get_preset_by_key') mock_get_ids = mock.patch.object(self.ordering, 'get_price_id_list') # start each patcher, and set a cleanup to stop each patcher as well to_return = [] for mock_func in [mock_pkg, mock_preset, mock_get_ids]: to_return.append(mock_func.start()) self.addCleanup(mock_func.stop) # set the return values on each of the mocks to_return[0].return_value = {'id': 1234} to_return[1].return_value = {'id': 5678} to_return[2].return_value = [1111, 2222] return to_return softlayer-python-5.4.2/tests/managers/queue_tests.py000066400000000000000000000426241324365065500227510ustar00rootroot00000000000000""" SoftLayer.tests.managers.queue_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import mock import SoftLayer from SoftLayer import consts from SoftLayer.managers import messaging from SoftLayer import testing QUEUE_1 = { 'expiration': 40000, 'message_count': 0, 'name': 'example_queue', 'tags': ['tag1', 'tag2', 'tag3'], 'visibility_interval': 10, 'visible_message_count': 0} QUEUE_LIST = {'item_count': 1, 'items': [QUEUE_1]} MESSAGE_1 = { 'body': '', 'fields': {'field': 'value'}, 'id': 'd344a01133b61181f57d9950a852eb10', 'initial_entry_time': 1343402631.3917992, 'message': 'Object created', 'visibility_delay': 0, 'visibility_interval': 30000} MESSAGE_POP = { 'item_count': 1, 'items': [MESSAGE_1], } MESSAGE_POP_EMPTY = { 'item_count': 0, 'items': [] } TOPIC_1 = {'name': 'example_topic', 'tags': ['tag1', 'tag2', 'tag3']} TOPIC_LIST = {'item_count': 1, 'items': [TOPIC_1]} SUBSCRIPTION_1 = { 'endpoint': { 'account_id': 'test', 'queue_name': 'topic_subscription_queue'}, 'endpoint_type': 'queue', 'id': 'd344a01133b61181f57d9950a85704d4', 'message': 'Object created'} SUBSCRIPTION_LIST = {'item_count': 1, 'items': [SUBSCRIPTION_1]} def mocked_auth_call(self): self.auth_token = 'NEW_AUTH_TOKEN' class QueueAuthTests(testing.TestCase): def set_up(self): self.auth = messaging.QueueAuth( 'endpoint', 'username', 'api_key', auth_token='auth_token') def test_init(self): auth = SoftLayer.managers.messaging.QueueAuth( 'endpoint', 'username', 'api_key', auth_token='auth_token') self.assertEqual(auth.endpoint, 'endpoint') self.assertEqual(auth.username, 'username') self.assertEqual(auth.api_key, 'api_key') self.assertEqual(auth.auth_token, 'auth_token') @mock.patch('SoftLayer.managers.messaging.requests.post') def test_auth(self, post): post().headers = {'X-Auth-Token': 'NEW_AUTH_TOKEN'} post().ok = True self.auth.auth() self.auth.auth_token = 'NEW_AUTH_TOKEN' post().ok = False self.assertRaises(SoftLayer.Unauthenticated, self.auth.auth) @mock.patch('SoftLayer.managers.messaging.QueueAuth.auth', mocked_auth_call) def test_handle_error_200(self): # No op on no error request = mock.MagicMock() request.status_code = 200 self.auth.handle_error(request) self.assertEqual(self.auth.auth_token, 'auth_token') self.assertFalse(request.request.send.called) @mock.patch('SoftLayer.managers.messaging.QueueAuth.auth', mocked_auth_call) def test_handle_error_503(self): # Retry once more on 503 error request = mock.MagicMock() request.status_code = 503 self.auth.handle_error(request) self.assertEqual(self.auth.auth_token, 'auth_token') request.connection.send.assert_called_with(request.request) @mock.patch('SoftLayer.managers.messaging.QueueAuth.auth', mocked_auth_call) def test_handle_error_401(self): # Re-auth on 401 request = mock.MagicMock() request.status_code = 401 request.request.headers = {'X-Auth-Token': 'OLD_AUTH_TOKEN'} self.auth.handle_error(request) self.assertEqual(self.auth.auth_token, 'NEW_AUTH_TOKEN') request.connection.send.assert_called_with(request.request) @mock.patch('SoftLayer.managers.messaging.QueueAuth.auth', mocked_auth_call) def test_call_unauthed(self): request = mock.MagicMock() request.headers = {} self.auth.auth_token = None self.auth(request) self.assertEqual(self.auth.auth_token, 'NEW_AUTH_TOKEN') request.register_hook.assert_called_with( 'response', self.auth.handle_error) self.assertEqual(request.headers, {'X-Auth-Token': 'NEW_AUTH_TOKEN'}) class MessagingManagerTests(testing.TestCase): def set_up(self): self.client = mock.MagicMock() self.manager = SoftLayer.MessagingManager(self.client) def test_list_accounts(self): self.manager.list_accounts() self.client['Account'].getMessageQueueAccounts.assert_called_with( mask=mock.ANY) def test_get_endpoints(self): endpoints = self.manager.get_endpoints() self.assertEqual(endpoints, SoftLayer.managers.messaging.ENDPOINTS) @mock.patch('SoftLayer.managers.messaging.ENDPOINTS', { 'datacenter01': { 'private': 'private_endpoint', 'public': 'public_endpoint'}, 'dal05': { 'private': 'dal05_private', 'public': 'dal05_public'}}) def test_get_endpoint(self): # Defaults to dal05, public endpoint = self.manager.get_endpoint() self.assertEqual(endpoint, 'https://dal05_public') endpoint = self.manager.get_endpoint(network='private') self.assertEqual(endpoint, 'https://dal05_private') endpoint = self.manager.get_endpoint(datacenter='datacenter01') self.assertEqual(endpoint, 'https://public_endpoint') endpoint = self.manager.get_endpoint(datacenter='datacenter01', network='private') self.assertEqual(endpoint, 'https://private_endpoint') endpoint = self.manager.get_endpoint(datacenter='datacenter01', network='private') self.assertEqual(endpoint, 'https://private_endpoint') # ERROR CASES self.assertRaises( TypeError, self.manager.get_endpoint, datacenter='doesnotexist') self.assertRaises( TypeError, self.manager.get_endpoint, network='doesnotexist') @mock.patch('SoftLayer.managers.messaging.MessagingConnection') def test_get_connection(self, conn): queue_conn = self.manager.get_connection('QUEUE_ACCOUNT_ID') conn.assert_called_with( 'QUEUE_ACCOUNT_ID', endpoint='https://dal05.mq.softlayer.net') conn().authenticate.assert_called_with( self.client.auth.username, self.client.auth.api_key) self.assertEqual(queue_conn, conn()) def test_get_connection_no_auth(self): self.client.auth = None self.assertRaises(SoftLayer.SoftLayerError, self.manager.get_connection, 'QUEUE_ACCOUNT_ID') def test_get_connection_no_username(self): self.client.auth.username = None self.assertRaises(SoftLayer.SoftLayerError, self.manager.get_connection, 'QUEUE_ACCOUNT_ID') def test_get_connection_no_api_key(self): self.client.auth.api_key = None self.assertRaises(SoftLayer.SoftLayerError, self.manager.get_connection, 'QUEUE_ACCOUNT_ID') @mock.patch('SoftLayer.managers.messaging.requests.get') def test_ping(self, get): result = self.manager.ping() get.assert_called_with('https://dal05.mq.softlayer.net/v1/ping') get().raise_for_status.assert_called_with() self.assertTrue(result) class MessagingConnectionTests(testing.TestCase): def set_up(self): self.conn = SoftLayer.managers.messaging.MessagingConnection( 'acount_id', endpoint='endpoint') self.auth = mock.MagicMock() self.conn.auth = self.auth def test_init(self): self.assertEqual(self.conn.account_id, 'acount_id') self.assertEqual(self.conn.endpoint, 'endpoint') self.assertEqual(self.conn.auth, self.auth) @mock.patch('SoftLayer.managers.messaging.requests.request') def test_make_request(self, request): resp = self.conn._make_request('GET', 'path') request.assert_called_with( 'GET', 'endpoint/v1/acount_id/path', headers={ 'Content-Type': 'application/json', 'User-Agent': consts.USER_AGENT}, auth=self.auth) request().raise_for_status.assert_called_with() self.assertEqual(resp, request()) @mock.patch('SoftLayer.managers.messaging.QueueAuth') def test_authenticate(self, auth): self.conn.authenticate('username', 'api_key', auth_token='auth_token') auth.assert_called_with( 'endpoint/v1/acount_id/auth', 'username', 'api_key', auth_token='auth_token') auth().auth.assert_called_with() self.assertEqual(self.conn.auth, auth()) @mock.patch('SoftLayer.managers.messaging.MessagingConnection' '._make_request') def test_stats(self, make_request): content = { 'notifications': [{'key': [2012, 7, 27, 14, 31], 'value': 2}], 'requests': [{'key': [2012, 7, 27, 14, 31], 'value': 11}]} make_request().json.return_value = content result = self.conn.stats() make_request.assert_called_with('get', 'stats/hour') self.assertEqual(content, result) # Queue-based Tests @mock.patch('SoftLayer.managers.messaging.MessagingConnection' '._make_request') def test_get_queues(self, make_request): make_request().json.return_value = QUEUE_LIST result = self.conn.get_queues() make_request.assert_called_with('get', 'queues', params={}) self.assertEqual(QUEUE_LIST, result) # with tags result = self.conn.get_queues(tags=['tag1', 'tag2']) make_request.assert_called_with( 'get', 'queues', params={'tags': 'tag1,tag2'}) self.assertEqual(QUEUE_LIST, result) @mock.patch('SoftLayer.managers.messaging.MessagingConnection' '._make_request') def test_create_queue(self, make_request): make_request().json.return_value = QUEUE_1 result = self.conn.create_queue('example_queue') make_request.assert_called_with( 'put', 'queues/example_queue', data='{}') self.assertEqual(QUEUE_1, result) @mock.patch('SoftLayer.managers.messaging.MessagingConnection' '._make_request') def test_modify_queue(self, make_request): make_request().json.return_value = QUEUE_1 result = self.conn.modify_queue('example_queue') make_request.assert_called_with( 'put', 'queues/example_queue', data='{}') self.assertEqual(QUEUE_1, result) @mock.patch('SoftLayer.managers.messaging.MessagingConnection' '._make_request') def test_get_queue(self, make_request): make_request().json.return_value = QUEUE_1 result = self.conn.get_queue('example_queue') make_request.assert_called_with('get', 'queues/example_queue') self.assertEqual(QUEUE_1, result) @mock.patch('SoftLayer.managers.messaging.MessagingConnection' '._make_request') def test_delete_queue(self, make_request): result = self.conn.delete_queue('example_queue') make_request.assert_called_with( 'delete', 'queues/example_queue', params={}) self.assertTrue(result) # With Force result = self.conn.delete_queue('example_queue', force=True) make_request.assert_called_with( 'delete', 'queues/example_queue', params={'force': 1}) self.assertTrue(result) @mock.patch('SoftLayer.managers.messaging.MessagingConnection' '._make_request') def test_push_queue_message(self, make_request): make_request().json.return_value = MESSAGE_1 result = self.conn.push_queue_message('example_queue', '') make_request.assert_called_with( 'post', 'queues/example_queue/messages', data='{"body": ""}') self.assertEqual(MESSAGE_1, result) @mock.patch('SoftLayer.managers.messaging.MessagingConnection' '._make_request') def test_pop_messages(self, make_request): make_request().json.return_value = MESSAGE_POP result = self.conn.pop_messages('example_queue') make_request.assert_called_with( 'get', 'queues/example_queue/messages', params={'batch': 1}) self.assertEqual(MESSAGE_POP, result) @mock.patch('SoftLayer.managers.messaging.MessagingConnection' '._make_request') def test_pop_message(self, make_request): make_request().json.return_value = MESSAGE_POP result = self.conn.pop_message('example_queue') make_request.assert_called_with( 'get', 'queues/example_queue/messages', params={'batch': 1}) self.assertEqual(MESSAGE_1, result) @mock.patch('SoftLayer.managers.messaging.MessagingConnection' '._make_request') def test_pop_message_empty(self, make_request): make_request().json.return_value = MESSAGE_POP_EMPTY result = self.conn.pop_message('example_queue') make_request.assert_called_with( 'get', 'queues/example_queue/messages', params={'batch': 1}) self.assertEqual(None, result) @mock.patch('SoftLayer.managers.messaging.MessagingConnection' '._make_request') def test_delete_message(self, make_request): result = self.conn.delete_message('example_queue', MESSAGE_1['id']) make_request.assert_called_with( 'delete', 'queues/example_queue/messages/%s' % MESSAGE_1['id']) self.assertTrue(result) # Topic-based Tests @mock.patch('SoftLayer.managers.messaging.MessagingConnection' '._make_request') def test_get_topics(self, make_request): make_request().json.return_value = TOPIC_LIST result = self.conn.get_topics() make_request.assert_called_with('get', 'topics', params={}) self.assertEqual(TOPIC_LIST, result) # with tags result = self.conn.get_topics(tags=['tag1', 'tag2']) make_request.assert_called_with( 'get', 'topics', params={'tags': 'tag1,tag2'}) self.assertEqual(TOPIC_LIST, result) @mock.patch('SoftLayer.managers.messaging.MessagingConnection' '._make_request') def test_create_topic(self, make_request): make_request().json.return_value = TOPIC_1 result = self.conn.create_topic('example_topic') make_request.assert_called_with( 'put', 'topics/example_topic', data='{}') self.assertEqual(TOPIC_1, result) @mock.patch('SoftLayer.managers.messaging.MessagingConnection' '._make_request') def test_modify_topic(self, make_request): make_request().json.return_value = TOPIC_1 result = self.conn.modify_topic('example_topic') make_request.assert_called_with( 'put', 'topics/example_topic', data='{}') self.assertEqual(TOPIC_1, result) @mock.patch('SoftLayer.managers.messaging.MessagingConnection' '._make_request') def test_get_topic(self, make_request): make_request().json.return_value = TOPIC_1 result = self.conn.get_topic('example_topic') make_request.assert_called_with('get', 'topics/example_topic') self.assertEqual(TOPIC_1, result) @mock.patch('SoftLayer.managers.messaging.MessagingConnection' '._make_request') def test_delete_topic(self, make_request): result = self.conn.delete_topic('example_topic') make_request.assert_called_with( 'delete', 'topics/example_topic', params={}) self.assertTrue(result) # With Force result = self.conn.delete_topic('example_topic', force=True) make_request.assert_called_with( 'delete', 'topics/example_topic', params={'force': 1}) self.assertTrue(result) @mock.patch('SoftLayer.managers.messaging.MessagingConnection' '._make_request') def test_push_topic_message(self, make_request): make_request().json.return_value = MESSAGE_1 result = self.conn.push_topic_message('example_topic', '') make_request.assert_called_with( 'post', 'topics/example_topic/messages', data='{"body": ""}') self.assertEqual(MESSAGE_1, result) @mock.patch('SoftLayer.managers.messaging.MessagingConnection' '._make_request') def test_get_subscriptions(self, make_request): make_request().json.return_value = SUBSCRIPTION_LIST result = self.conn.get_subscriptions('example_topic') make_request.assert_called_with( 'get', 'topics/example_topic/subscriptions') self.assertEqual(SUBSCRIPTION_LIST, result) @mock.patch('SoftLayer.managers.messaging.MessagingConnection' '._make_request') def test_create_subscription(self, make_request): make_request().json.return_value = SUBSCRIPTION_1 endpoint_details = { 'account_id': 'test', 'queue_name': 'topic_subscription_queue'} result = self.conn.create_subscription( 'example_topic', 'queue', **endpoint_details) make_request.assert_called_with( 'post', 'topics/example_topic/subscriptions', data=mock.ANY) self.assertEqual(SUBSCRIPTION_1, result) @mock.patch('SoftLayer.managers.messaging.MessagingConnection.' '_make_request') def test_delete_subscription(self, make_request): make_request().json.return_value = SUBSCRIPTION_1 result = self.conn.delete_subscription( 'example_topic', SUBSCRIPTION_1['id']) make_request.assert_called_with( 'delete', 'topics/example_topic/subscriptions/%s' % SUBSCRIPTION_1['id']) self.assertTrue(result) softlayer-python-5.4.2/tests/managers/sshkey_tests.py000066400000000000000000000037511324365065500231310ustar00rootroot00000000000000""" SoftLayer.tests.managers.sshkey_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import SoftLayer from SoftLayer import testing class SshKeyTests(testing.TestCase): def set_up(self): self.sshkey = SoftLayer.SshKeyManager(self.client) def test_add_key(self): self.sshkey.add_key(key='pretend this is a public SSH key', label='Test label', notes='My notes') args = ({ 'key': 'pretend this is a public SSH key', 'label': 'Test label', 'notes': 'My notes', },) self.assert_called_with('SoftLayer_Security_Ssh_Key', 'createObject', args=args) def test_delete_key(self): self.sshkey.delete_key(1234) self.assert_called_with('SoftLayer_Security_Ssh_Key', 'deleteObject', identifier=1234) def test_edit_key(self): self.sshkey.edit_key(1234, label='Test label', notes='My notes') args = ({ 'label': 'Test label', 'notes': 'My notes', },) self.assert_called_with('SoftLayer_Security_Ssh_Key', 'editObject', identifier=1234, args=args) def test_get_key(self): self.sshkey.get_key(1234) self.assert_called_with('SoftLayer_Security_Ssh_Key', 'getObject', identifier=1234) def test_list_keys(self): self.sshkey.list_keys(label='some label') _filter = {'sshKeys': {'label': {'operation': '_= some label'}}} self.assert_called_with('SoftLayer_Account', 'getSshKeys', filter=_filter) def test_resolve_ids_label(self): _id = self.sshkey._get_ids_from_label('Test 1') self.assertEqual(_id, ['100']) _id = self.sshkey._get_ids_from_label('nope') self.assertEqual(_id, []) softlayer-python-5.4.2/tests/managers/ssl_tests.py000066400000000000000000000057541324365065500224310ustar00rootroot00000000000000""" SoftLayer.tests.managers.ssl_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import SoftLayer from SoftLayer import fixtures from SoftLayer import testing class SSLTests(testing.TestCase): def set_up(self): self.ssl = SoftLayer.SSLManager(self.client) self.test_id = 10 def test_list_certs_valid(self): result = self.ssl.list_certs('valid') self.assertEqual( result, fixtures.SoftLayer_Account.getValidSecurityCertificates) self.assert_called_with('SoftLayer_Account', 'getValidSecurityCertificates') def test_list_certs_expired(self): result = self.ssl.list_certs('expired') self.assertEqual( result, fixtures.SoftLayer_Account.getExpiredSecurityCertificates) self.assert_called_with('SoftLayer_Account', 'getExpiredSecurityCertificates') def test_list_certs_all(self): result = self.ssl.list_certs('all') self.assertEqual( result, fixtures.SoftLayer_Account.getSecurityCertificates) self.assert_called_with('SoftLayer_Account', 'getSecurityCertificates') def test_add_certificate(self): test_cert = { 'certificate': 'cert', 'privateKey': 'key', } result = self.ssl.add_certificate(test_cert) self.assertEqual(result, fixtures.SoftLayer_Security_Certificate.createObject) self.assert_called_with('SoftLayer_Security_Certificate', 'createObject', args=(test_cert,)) def test_remove_certificate(self): result = self.ssl.remove_certificate(self.test_id) self.assertEqual(result, True) self.assert_called_with('SoftLayer_Security_Certificate', 'deleteObject', identifier=self.test_id) def test_edit_certificate(self): test_cert = { 'id': self.test_id, 'certificate': 'cert', 'privateKey': 'key' } result = self.ssl.edit_certificate(test_cert) self.assertEqual(result, True) args = ({ 'id': self.test_id, 'certificate': 'cert', 'privateKey': 'key' },) self.assert_called_with('SoftLayer_Security_Certificate', 'editObject', args=args, identifier=self.test_id) def test_get_certificate(self): result = self.ssl.get_certificate(self.test_id) self.assertEqual(result, fixtures.SoftLayer_Security_Certificate.getObject) self.assert_called_with('SoftLayer_Security_Certificate', 'getObject', identifier=self.test_id) softlayer-python-5.4.2/tests/managers/storage_utils_tests.py000066400000000000000000004253431324365065500245140ustar00rootroot00000000000000""" SoftLayer.tests.managers.storage_utils_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import copy import SoftLayer from SoftLayer import exceptions from SoftLayer import fixtures from SoftLayer.managers import storage_utils from SoftLayer import testing class StorageUtilsTests(testing.TestCase): def set_up(self): self.block = SoftLayer.BlockStorageManager(self.client) self.file = SoftLayer.FileStorageManager(self.client) # --------------------------------------------------------------------- # Tests for populate_host_templates() # --------------------------------------------------------------------- def test_populate_host_templates_no_ids_given(self): host_templates = [] storage_utils.populate_host_templates(host_templates) self.assertEqual([], host_templates) def test_populate_host_templates_empty_arrays_given(self): host_templates = [] storage_utils.populate_host_templates( host_templates, hardware_ids=[], virtual_guest_ids=[], ip_address_ids=[], subnet_ids=[] ) self.assertEqual([], host_templates) def test_populate_host_templates(self): host_templates = [] storage_utils.populate_host_templates( host_templates, hardware_ids=[1111], virtual_guest_ids=[2222], ip_address_ids=[3333], subnet_ids=[4444, 5555] ) expected_result = [ {'objectType': 'SoftLayer_Hardware', 'id': 1111}, {'objectType': 'SoftLayer_Virtual_Guest', 'id': 2222}, {'objectType': 'SoftLayer_Network_Subnet_IpAddress', 'id': 3333}, {'objectType': 'SoftLayer_Network_Subnet', 'id': 4444}, {'objectType': 'SoftLayer_Network_Subnet', 'id': 5555} ] self.assertEqual(expected_result, host_templates) # --------------------------------------------------------------------- # Tests for get_package() # --------------------------------------------------------------------- def test_get_package_no_packages_found(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [] exception = self.assertRaises( ValueError, storage_utils.get_package, self.block, 'storage_as_a_service' ) self.assertEqual( "No packages were found for storage_as_a_service", str(exception) ) def test_get_package_more_than_one_package_found(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [ fixtures.SoftLayer_Product_Package.SAAS_PACKAGE, fixtures.SoftLayer_Product_Package.ENTERPRISE_PACKAGE ] exception = self.assertRaises( ValueError, storage_utils.get_package, self.block, 'storage_as_a_service' ) self.assertEqual( "More than one package was found for storage_as_a_service", str(exception) ) def test_get_package(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] result = storage_utils.get_package(self.block, 'storage_as_a_service') self.assertEqual( fixtures.SoftLayer_Product_Package.SAAS_PACKAGE, result ) expected_filter = { 'statusCode': {'operation': '_= ACTIVE'}, 'categories': { 'categoryCode': {'operation': '_= storage_as_a_service'} } } self.assert_called_with( 'SoftLayer_Product_Package', 'getAllObjects', filter=expected_filter, mask='mask[id,name,items[prices[categories],attributes]]' ) # --------------------------------------------------------------------- # Tests for get_location_id() # --------------------------------------------------------------------- def test_get_location_id_no_datacenters_in_collection(self): mock = self.set_mock('SoftLayer_Location_Datacenter', 'getDatacenters') mock.return_value = [] exception = self.assertRaises( ValueError, storage_utils.get_location_id, self.block, 'dal09' ) self.assertEqual("Invalid datacenter name specified.", str(exception)) def test_get_location_id_no_matching_location_name(self): mock = self.set_mock('SoftLayer_Location_Datacenter', 'getDatacenters') mock.return_value = [ {'id': 1414, 'name': 'hoth01'}, {'id': 1417, 'name': 'hoth04'} ] exception = self.assertRaises( ValueError, storage_utils.get_location_id, self.block, 'dal09' ) self.assertEqual("Invalid datacenter name specified.", str(exception)) def test_get_location_id(self): mock = self.set_mock('SoftLayer_Location_Datacenter', 'getDatacenters') mock.return_value = [{'id': 29, 'name': 'dal09'}] result = storage_utils.get_location_id(self.block, 'dal09') self.assertEqual(29, result) # --------------------------------------------------------------------- # Tests for find_price_by_category() # --------------------------------------------------------------------- def test_find_price_by_category_no_items_in_package(self): package = { 'items': []} exception = self.assertRaises( ValueError, storage_utils.find_price_by_category, package, 'storage_as_a_service' ) self.assertEqual(str(exception), "Could not find price with the category, " "storage_as_a_service") def test_find_price_by_category_no_prices_in_items(self): package = { 'items': [ {'capacity': '0', 'prices': []} ]} exception = self.assertRaises( ValueError, storage_utils.find_price_by_category, package, 'storage_as_a_service' ) self.assertEqual(str(exception), "Could not find price with the category, " "storage_as_a_service") def test_find_price_by_category_empty_location_not_found(self): package = { 'items': [ {'capacity': '0', 'prices': [ {'id': 189433, 'categories': [ {'categoryCode': 'storage_as_a_service'} ], 'locationGroupId': '77777777'} ]} ]} exception = self.assertRaises( ValueError, storage_utils.find_price_by_category, package, 'storage_as_a_service' ) self.assertEqual(str(exception), "Could not find price with the category, " "storage_as_a_service") def test_find_price_by_category_category_not_found(self): package = { 'items': [ {'capacity': '0', 'prices': [ {'id': 189433, 'categories': [ {'categoryCode': 'invalid_category_noooo'} ], 'locationGroupId': ''} ]} ]} exception = self.assertRaises( ValueError, storage_utils.find_price_by_category, package, 'storage_as_a_service' ) self.assertEqual(str(exception), "Could not find price with the category, " "storage_as_a_service") def test_find_price_by_category(self): package = { 'items': [ {'capacity': '0', 'prices': [ {'id': 189433, 'categories': [ {'categoryCode': 'storage_as_a_service'} ], 'locationGroupId': ''} ]} ]} result = storage_utils.find_price_by_category( package, 'storage_as_a_service') self.assertEqual({'id': 189433}, result) # --------------------------------------------------------------------- # Tests for find_ent_space_price() # --------------------------------------------------------------------- def test_find_ent_space_price_no_items_in_package(self): package = { 'items': [] } exception = self.assertRaises( ValueError, storage_utils.find_ent_space_price, package, 'snapshot', 10, 2 ) self.assertEqual( "Could not find price for snapshot storage space", str(exception) ) def test_find_ent_space_price_no_matching_capacity(self): package = { 'items': [ {'capacity': '-1'} ] } exception = self.assertRaises( ValueError, storage_utils.find_ent_space_price, package, 'snapshot', 10, 2 ) self.assertEqual( "Could not find price for snapshot storage space", str(exception) ) def test_find_ent_space_price_no_prices_in_items(self): package = { 'items': [ { 'capacity': '10', 'prices': [] } ] } exception = self.assertRaises( ValueError, storage_utils.find_ent_space_price, package, 'snapshot', 10, 2 ) self.assertEqual( "Could not find price for snapshot storage space", str(exception) ) def test_find_ent_space_price_empty_location_not_found(self): package = { 'items': [ { 'capacity': '10', 'prices': [ { 'capacityRestrictionMaximum': '200', 'capacityRestrictionMinimum': '200', 'capacityRestrictionType': 'STORAGE_TIER_LEVEL', 'categories': [ {'categoryCode': 'storage_snapshot_space'} ], 'id': 46160, 'locationGroupId': '77777777' } ] } ] } exception = self.assertRaises( ValueError, storage_utils.find_ent_space_price, package, 'snapshot', 10, 2 ) self.assertEqual( "Could not find price for snapshot storage space", str(exception) ) def test_find_ent_space_price_wrong_capacity_restriction(self): package = { 'items': [ { 'capacity': '10', 'prices': [ { 'capacityRestrictionMaximum': '200', 'capacityRestrictionMinimum': '200', 'capacityRestrictionType': 'WRONG_CATEGORY', 'categories': [ {'categoryCode': 'storage_snapshot_space'} ], 'id': 46160, 'locationGroupId': '' } ] } ] } exception = self.assertRaises( ValueError, storage_utils.find_ent_space_price, package, 'snapshot', 10, 2 ) self.assertEqual( "Could not find price for snapshot storage space", str(exception) ) def test_find_ent_space_price_target_value_below_capacity(self): package = { 'items': [ { 'capacity': '10', 'prices': [ { 'capacityRestrictionMaximum': '200', 'capacityRestrictionMinimum': '200', 'capacityRestrictionType': 'STORAGE_TIER_LEVEL', 'categories': [ {'categoryCode': 'storage_snapshot_space'} ], 'id': 46160, 'locationGroupId': '' } ] } ] } exception = self.assertRaises( ValueError, storage_utils.find_ent_space_price, package, 'snapshot', 10, 0.25 ) self.assertEqual( "Could not find price for snapshot storage space", str(exception) ) def test_find_ent_space_price_target_value_above_capacity(self): package = { 'items': [ { 'capacity': '10', 'prices': [ { 'capacityRestrictionMaximum': '200', 'capacityRestrictionMinimum': '200', 'capacityRestrictionType': 'STORAGE_TIER_LEVEL', 'categories': [ {'categoryCode': 'storage_snapshot_space'} ], 'id': 46160, 'locationGroupId': '' } ] } ] } exception = self.assertRaises( ValueError, storage_utils.find_ent_space_price, package, 'snapshot', 10, 4 ) self.assertEqual( "Could not find price for snapshot storage space", str(exception) ) def test_find_ent_space_price_category_not_found(self): package = { 'items': [ { 'capacity': '10', 'prices': [ { 'capacityRestrictionMaximum': '200', 'capacityRestrictionMinimum': '200', 'capacityRestrictionType': 'STORAGE_TIER_LEVEL', 'categories': [ {'categoryCode': 'invalid_category_noooooooo'} ], 'id': 46160, 'locationGroupId': '' } ] } ] } exception = self.assertRaises( ValueError, storage_utils.find_ent_space_price, package, 'snapshot', 10, 2 ) self.assertEqual( "Could not find price for snapshot storage space", str(exception) ) def test_find_ent_space_price_with_snapshot_category(self): package = { 'items': [ { 'capacity': '10', 'prices': [ { 'capacityRestrictionMaximum': '200', 'capacityRestrictionMinimum': '200', 'capacityRestrictionType': 'STORAGE_TIER_LEVEL', 'categories': [ {'categoryCode': 'storage_snapshot_space'} ], 'id': 46160, 'locationGroupId': '' } ] } ] } result = storage_utils.find_ent_space_price( package, 'snapshot', 10, 2 ) self.assertEqual({'id': 46160}, result) def test_find_ent_space_price_with_replication_category(self): package = { 'items': [ { 'capacity': '20', 'prices': [ { 'capacityRestrictionMaximum': '200', 'capacityRestrictionMinimum': '200', 'capacityRestrictionType': 'STORAGE_TIER_LEVEL', 'categories': [ {'categoryCode': 'performance_storage_replication'} ], 'id': 46659, 'locationGroupId': '' } ] } ] } result = storage_utils.find_ent_space_price( package, 'replication', 20, 2 ) self.assertEqual({'id': 46659}, result) def test_find_ent_space_price_with_endurance_category(self): package = { 'items': [ { 'capacity': '1000', 'prices': [ { 'capacityRestrictionMaximum': '300', 'capacityRestrictionMinimum': '300', 'capacityRestrictionType': 'STORAGE_TIER_LEVEL', 'categories': [ {'categoryCode': 'performance_storage_space'} ], 'id': 45318, 'locationGroupId': '' } ] } ] } result = storage_utils.find_ent_space_price( package, 'endurance', 1000, 4 ) self.assertEqual({'id': 45318}, result) # --------------------------------------------------------------------- # Tests for find_ent_endurance_tier_price() # --------------------------------------------------------------------- def test_find_ent_endurance_tier_price_no_items_in_package(self): package = { 'items': [] } exception = self.assertRaises( ValueError, storage_utils.find_ent_endurance_tier_price, package, 2 ) self.assertEqual( "Could not find price for endurance tier level", str(exception) ) def test_find_ent_endurance_tier_price_no_attributes_in_items(self): package = { 'items': [ {'attributes': []} ] } exception = self.assertRaises( ValueError, storage_utils.find_ent_endurance_tier_price, package, 2 ) self.assertEqual( "Could not find price for endurance tier level", str(exception) ) def test_find_ent_endurance_tier_price_no_matching_attribute_value(self): package = { 'items': [ { 'attributes': [ {'value': '-1'} ] } ] } exception = self.assertRaises( ValueError, storage_utils.find_ent_endurance_tier_price, package, 2 ) self.assertEqual( "Could not find price for endurance tier level", str(exception) ) def test_find_ent_endurance_tier_price_no_prices_in_items(self): package = { 'items': [ { 'attributes': [ {'value': '200'} ], 'prices': [] } ] } exception = self.assertRaises( ValueError, storage_utils.find_ent_endurance_tier_price, package, 2 ) self.assertEqual( "Could not find price for endurance tier level", str(exception) ) def test_find_ent_endurance_tier_price_empty_location_not_found(self): package = { 'items': [ { 'attributes': [ {'value': '200'} ], 'prices': [ { 'categories': [ {'categoryCode': 'storage_tier_level'} ], 'id': 45078, 'locationGroupId': '77777777' } ] } ] } exception = self.assertRaises( ValueError, storage_utils.find_ent_endurance_tier_price, package, 2 ) self.assertEqual( "Could not find price for endurance tier level", str(exception) ) def test_find_ent_endurance_tier_price_category_not_found(self): package = { 'items': [ { 'attributes': [ {'value': '200'} ], 'prices': [ { 'categories': [ {'categoryCode': 'invalid_category_noooo'} ], 'id': 45078, 'locationGroupId': '' } ] } ] } exception = self.assertRaises( ValueError, storage_utils.find_ent_endurance_tier_price, package, 2 ) self.assertEqual( "Could not find price for endurance tier level", str(exception) ) def test_find_ent_endurance_tier_price(self): package = { 'items': [ { 'attributes': [ {'value': '200'} ], 'prices': [ { 'categories': [ {'categoryCode': 'storage_tier_level'} ], 'id': 45078, 'locationGroupId': '' } ] } ] } result = storage_utils.find_ent_endurance_tier_price(package, 2) self.assertEqual({'id': 45078}, result) # --------------------------------------------------------------------- # Tests for find_endurance_tier_iops_per_gb() # --------------------------------------------------------------------- def test_find_endurance_tier_iops_per_gb_value_is_025(self): volume = {'storageTierLevel': 'LOW_INTENSITY_TIER'} result = storage_utils.find_endurance_tier_iops_per_gb(volume) self.assertEqual(0.25, result) def test_find_endurance_tier_iops_per_gb_value_is_2(self): volume = {'storageTierLevel': 'READHEAVY_TIER'} result = storage_utils.find_endurance_tier_iops_per_gb(volume) self.assertEqual(2, result) def test_find_endurance_tier_iops_per_gb_value_is_4(self): volume = {'storageTierLevel': 'WRITEHEAVY_TIER'} result = storage_utils.find_endurance_tier_iops_per_gb(volume) self.assertEqual(4, result) def test_find_endurance_tier_iops_per_gb_value_is_10(self): volume = {'storageTierLevel': '10_IOPS_PER_GB'} result = storage_utils.find_endurance_tier_iops_per_gb(volume) self.assertEqual(10, result) def test_find_endurance_tier_iops_per_gb_value_not_found(self): volume = {'storageTierLevel': 'INVALID_TIER_OH_NOOOO'} exception = self.assertRaises( ValueError, storage_utils.find_endurance_tier_iops_per_gb, volume ) self.assertEqual( "Could not find tier IOPS per GB for this volume", str(exception) ) # --------------------------------------------------------------------- # Tests for find_perf_space_price() # --------------------------------------------------------------------- def test_find_perf_space_price_no_items_in_package(self): package = { 'items': [] } exception = self.assertRaises( ValueError, storage_utils.find_perf_space_price, package, 1000 ) self.assertEqual( "Could not find performance space price for this volume", str(exception) ) def test_find_perf_space_price_no_matching_capacity(self): package = { 'items': [ {'capacity': '-1'} ] } exception = self.assertRaises( ValueError, storage_utils.find_perf_space_price, package, 1000 ) self.assertEqual( "Could not find performance space price for this volume", str(exception) ) def test_find_perf_space_price_no_prices_in_items(self): package = { 'items': [ { 'capacity': '1000', 'prices': [] } ] } exception = self.assertRaises( ValueError, storage_utils.find_perf_space_price, package, 1000 ) self.assertEqual( "Could not find performance space price for this volume", str(exception) ) def test_find_perf_space_price_empty_location_not_found(self): package = { 'items': [ { 'capacity': '1000', 'prices': [ { 'categories': [ {'categoryCode': 'performance_storage_space'} ], 'id': 40742, 'locationGroupId': '77777777' } ] } ] } exception = self.assertRaises( ValueError, storage_utils.find_perf_space_price, package, 1000 ) self.assertEqual( "Could not find performance space price for this volume", str(exception) ) def test_find_perf_space_price_category_not_found(self): package = { 'items': [ { 'capacity': '1000', 'prices': [ { 'categories': [ {'categoryCode': 'invalid_category_noooo'} ], 'id': 40742, 'locationGroupId': '' } ] } ] } exception = self.assertRaises( ValueError, storage_utils.find_perf_space_price, package, 1000 ) self.assertEqual( "Could not find performance space price for this volume", str(exception) ) def test_find_perf_space_price(self): package = { 'items': [ { 'capacity': '1000', 'prices': [ { 'categories': [ {'categoryCode': 'performance_storage_space'} ], 'id': 40742, 'locationGroupId': '' } ] } ] } result = storage_utils.find_perf_space_price( package, 1000 ) self.assertEqual({'id': 40742}, result) # --------------------------------------------------------------------- # Tests for find_perf_iops_price() # --------------------------------------------------------------------- def test_find_perf_iops_price_no_items_in_package(self): package = { 'items': [] } exception = self.assertRaises( ValueError, storage_utils.find_perf_iops_price, package, 500, 800 ) self.assertEqual( "Could not find price for iops for the given volume", str(exception) ) def test_find_perf_iops_price_no_matching_iops_value(self): package = { 'items': [ {'capacity': '-1'} ] } exception = self.assertRaises( ValueError, storage_utils.find_perf_iops_price, package, 500, 800 ) self.assertEqual( "Could not find price for iops for the given volume", str(exception) ) def test_find_perf_iops_price_no_prices_in_items(self): package = { 'items': [ { 'capacity': '800', 'keyName': '800_IOPS_4', 'prices': [] } ] } exception = self.assertRaises( ValueError, storage_utils.find_perf_iops_price, package, 500, 800 ) self.assertEqual( "Could not find price for iops for the given volume", str(exception) ) def test_find_perf_iops_price_empty_location_not_found(self): package = { 'items': [ { 'capacity': '800', 'keyName': '800_IOPS_4', 'prices': [ { 'capacityRestrictionMaximum': '1000', 'capacityRestrictionMinimum': '100', 'capacityRestrictionType': 'STORAGE_SPACE', 'categories': [ {'categoryCode': 'performance_storage_iops'} ], 'id': 41562, 'locationGroupId': '77777777' } ] } ] } exception = self.assertRaises( ValueError, storage_utils.find_perf_iops_price, package, 500, 800 ) self.assertEqual( "Could not find price for iops for the given volume", str(exception) ) def test_find_perf_iops_price_category_not_found(self): package = { 'items': [ { 'capacity': '800', 'keyName': '800_IOPS_4', 'prices': [ { 'capacityRestrictionMaximum': '1000', 'capacityRestrictionMinimum': '100', 'capacityRestrictionType': 'STORAGE_SPACE', 'categories': [ {'categoryCode': 'invalid_category_noooo'} ], 'id': 41562, 'locationGroupId': '' } ] } ] } exception = self.assertRaises( ValueError, storage_utils.find_perf_iops_price, package, 500, 800 ) self.assertEqual( "Could not find price for iops for the given volume", str(exception) ) def test_find_perf_iops_price_wrong_capacity_restriction(self): package = { 'items': [ { 'capacity': '800', 'keyName': '800_IOPS_4', 'prices': [ { 'capacityRestrictionMaximum': '1000', 'capacityRestrictionMinimum': '100', 'capacityRestrictionType': 'WRONG_TYPE_WOAH', 'categories': [ {'categoryCode': 'performance_storage_iops'} ], 'id': 41562, 'locationGroupId': '' } ] } ] } exception = self.assertRaises( ValueError, storage_utils.find_perf_iops_price, package, 500, 800 ) self.assertEqual( "Could not find price for iops for the given volume", str(exception) ) def test_find_perf_iops_price_volume_size_below_capacity(self): package = { 'items': [ { 'capacity': '800', 'keyName': '800_IOPS_4', 'prices': [ { 'capacityRestrictionMaximum': '1000', 'capacityRestrictionMinimum': '100', 'capacityRestrictionType': 'STORAGE_SPACE', 'categories': [ {'categoryCode': 'performance_storage_iops'} ], 'id': 41562, 'locationGroupId': '' } ] } ] } exception = self.assertRaises( ValueError, storage_utils.find_perf_iops_price, package, 80, 800 ) self.assertEqual( "Could not find price for iops for the given volume", str(exception) ) def test_find_perf_iops_price_volume_size_above_capacity(self): package = { 'items': [ { 'capacity': '800', 'keyName': '800_IOPS_4', 'prices': [ { 'capacityRestrictionMaximum': '1000', 'capacityRestrictionMinimum': '100', 'capacityRestrictionType': 'STORAGE_SPACE', 'categories': [ {'categoryCode': 'performance_storage_iops'} ], 'id': 41562, 'locationGroupId': '' } ] } ] } exception = self.assertRaises( ValueError, storage_utils.find_perf_iops_price, package, 2000, 800 ) self.assertEqual( "Could not find price for iops for the given volume", str(exception) ) def test_find_perf_iops_price(self): package = { 'items': [ { 'capacity': '800', 'keyName': '800_IOPS_4', 'prices': [ { 'capacityRestrictionMaximum': '1000', 'capacityRestrictionMinimum': '100', 'capacityRestrictionType': 'STORAGE_SPACE', 'categories': [ {'categoryCode': 'performance_storage_iops'} ], 'id': 41562, 'locationGroupId': '' } ] } ] } result = storage_utils.find_perf_iops_price( package, 500, 800 ) self.assertEqual({'id': 41562}, result) # --------------------------------------------------------------------- # Tests for find_saas_endurance_space_price() # --------------------------------------------------------------------- def test_find_saas_endurance_space_price_no_items_in_package(self): package = { 'items': []} exception = self.assertRaises( ValueError, storage_utils.find_saas_endurance_space_price, package, 8000, 0.25 ) self.assertEqual(str(exception), "Could not find price for endurance storage space") def test_find_saas_endurance_space_price_no_matching_keyname(self): package = { 'items': [ {'capacity': '0', 'keyName': 'STORAGE_SPACE_FOR_2_IOPS_PER_GB'} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_endurance_space_price, package, 8000, 0.25 ) self.assertEqual(str(exception), "Could not find price for endurance storage space") def test_find_saas_endurance_space_price_no_capacity_maximum(self): package = { 'items': [ {'capacity': '0', 'capacityMinimum': '1', 'keyName': 'STORAGE_SPACE_FOR_0_25_IOPS_PER_GB'} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_endurance_space_price, package, 8000, 0.25 ) self.assertEqual(str(exception), "Could not find price for endurance storage space") def test_find_saas_endurance_space_price_no_capacity_minimum(self): package = { 'items': [ {'capacity': '0', 'capacityMaximum': '12000', 'keyName': 'STORAGE_SPACE_FOR_0_25_IOPS_PER_GB'} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_endurance_space_price, package, 8000, 0.25 ) self.assertEqual(str(exception), "Could not find price for endurance storage space") def test_find_saas_endurance_space_price_size_below_capacity(self): package = { 'items': [ {'capacity': '0', 'capacityMaximum': '12000', 'capacityMinimum': '1', 'keyName': 'STORAGE_SPACE_FOR_0_25_IOPS_PER_GB'} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_endurance_space_price, package, 0, 0.25 ) self.assertEqual(str(exception), "Could not find price for endurance storage space") def test_find_saas_endurance_space_price_size_above_capacity(self): package = { 'items': [ {'capacity': '0', 'capacityMaximum': '12000', 'capacityMinimum': '1', 'keyName': 'STORAGE_SPACE_FOR_0_25_IOPS_PER_GB'} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_endurance_space_price, package, 12001, 0.25 ) self.assertEqual(str(exception), "Could not find price for endurance storage space") def test_find_saas_endurance_space_price_no_prices_in_items(self): package = { 'items': [ {'capacity': '0', 'capacityMaximum': '12000', 'capacityMinimum': '1', 'keyName': 'STORAGE_SPACE_FOR_0_25_IOPS_PER_GB', 'prices': []} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_endurance_space_price, package, 8000, 0.25 ) self.assertEqual(str(exception), "Could not find price for endurance storage space") def test_find_saas_endurance_space_price_empty_location_not_found(self): package = { 'items': [ {'capacity': '0', 'capacityMaximum': '12000', 'capacityMinimum': '1', 'keyName': 'STORAGE_SPACE_FOR_0_25_IOPS_PER_GB', 'prices': [ {'id': 192103, 'categories': [ {'categoryCode': 'performance_storage_space'} ], 'locationGroupId': '77777777'} ]} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_endurance_space_price, package, 8000, 0.25 ) self.assertEqual(str(exception), "Could not find price for endurance storage space") def test_find_saas_endurance_space_price_category_not_found(self): package = { 'items': [ {'capacity': '0', 'capacityMaximum': '12000', 'capacityMinimum': '1', 'keyName': 'STORAGE_SPACE_FOR_0_25_IOPS_PER_GB', 'prices': [ {'id': 192103, 'categories': [ {'categoryCode': 'invalid_category_noooo'} ], 'locationGroupId': ''} ]} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_endurance_space_price, package, 8000, 0.25 ) self.assertEqual(str(exception), "Could not find price for endurance storage space") def test_find_saas_endurance_space_price(self): package = { 'items': [ {'capacity': '0', 'capacityMaximum': '12000', 'capacityMinimum': '1', 'keyName': 'STORAGE_SPACE_FOR_0_25_IOPS_PER_GB', 'prices': [ {'id': 192103, 'categories': [ {'categoryCode': 'performance_storage_space'} ], 'locationGroupId': ''} ]} ]} result = storage_utils.find_saas_endurance_space_price( package, 8000, 0.25) self.assertEqual({'id': 192103}, result) # --------------------------------------------------------------------- # Tests for find_saas_endurance_tier_price() # --------------------------------------------------------------------- def test_find_saas_endurance_tier_price_no_items_in_package(self): package = { 'items': []} exception = self.assertRaises( ValueError, storage_utils.find_saas_endurance_tier_price, package, 2 ) self.assertEqual(str(exception), "Could not find price for endurance tier level") def test_find_saas_endurance_tier_price_no_itemCategory(self): package = { 'items': [ {'capacity': '200'} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_endurance_tier_price, package, 2 ) self.assertEqual(str(exception), "Could not find price for endurance tier level") def test_find_saas_endurance_tier_price_no_itemCategory_code(self): package = { 'items': [ {'capacity': '200', 'itemCategory': {}} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_endurance_tier_price, package, 2 ) self.assertEqual(str(exception), "Could not find price for endurance tier level") def test_find_saas_endurance_tier_price_no_matching_itemCategory(self): package = { 'items': [ {'capacity': '200', 'itemCategory': {'categoryCode': 'invalid_category_noooo'}} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_endurance_tier_price, package, 2 ) self.assertEqual(str(exception), "Could not find price for endurance tier level") def test_find_saas_endurance_tier_price_no_matching_capacity(self): package = { 'items': [ {'capacity': '200', 'itemCategory': {'categoryCode': 'storage_tier_level'}} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_endurance_tier_price, package, 10 ) self.assertEqual(str(exception), "Could not find price for endurance tier level") def test_find_saas_endurance_tier_price_no_prices_in_items(self): package = { 'items': [ {'capacity': '200', 'itemCategory': {'categoryCode': 'storage_tier_level'}, 'prices': []} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_endurance_tier_price, package, 2 ) self.assertEqual(str(exception), "Could not find price for endurance tier level") def test_find_saas_endurance_tier_price_empty_location_not_found(self): package = { 'items': [ {'capacity': '200', 'itemCategory': {'categoryCode': 'storage_tier_level'}, 'prices': [ {'id': 193373, 'categories': [ {'categoryCode': 'storage_tier_level'} ], 'locationGroupId': '77777777'} ]} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_endurance_tier_price, package, 2 ) self.assertEqual(str(exception), "Could not find price for endurance tier level") def test_find_saas_endurance_tier_price_category_not_found(self): package = { 'items': [ {'capacity': '200', 'itemCategory': {'categoryCode': 'storage_tier_level'}, 'prices': [ {'id': 193373, 'categories': [ {'categoryCode': 'invalid_category_noooo'} ], 'locationGroupId': ''} ]} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_endurance_tier_price, package, 2 ) self.assertEqual(str(exception), "Could not find price for endurance tier level") def test_find_saas_endurance_tier_price(self): package = { 'items': [ {'capacity': '200', 'itemCategory': {'categoryCode': 'storage_tier_level'}, 'prices': [ {'id': 193373, 'categories': [ {'categoryCode': 'storage_tier_level'} ], 'locationGroupId': ''} ]} ]} result = storage_utils.find_saas_endurance_tier_price( package, 2) self.assertEqual({'id': 193373}, result) # --------------------------------------------------------------------- # Tests for find_saas_perform_space_price() # --------------------------------------------------------------------- def test_find_saas_perform_space_price_no_items_in_package(self): package = { 'items': []} exception = self.assertRaises( ValueError, storage_utils.find_saas_perform_space_price, package, 500 ) self.assertEqual(str(exception), "Could not find price for performance storage space") def test_find_saas_perform_space_price_no_itemCategory(self): package = { 'items': [ {'capacity': '0'} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_perform_space_price, package, 500 ) self.assertEqual(str(exception), "Could not find price for performance storage space") def test_find_saas_perform_space_price_no_itemCategory_code(self): package = { 'items': [ {'capacity': '0', 'itemCategory': {}} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_perform_space_price, package, 500 ) self.assertEqual(str(exception), "Could not find price for performance storage space") def test_find_saas_perform_space_price_no_matching_itemCategory(self): package = { 'items': [ {'capacity': '0', 'itemCategory': {'categoryCode': 'invalid_category_noooo'}} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_perform_space_price, package, 500 ) self.assertEqual(str(exception), "Could not find price for performance storage space") def test_find_saas_perform_space_price_no_capacity_maximum(self): package = { 'items': [ {'capacity': '0', 'capacityMinimum': '500', 'itemCategory': {'categoryCode': 'performance_storage_space'}, 'keyName': '500_999_GBS'} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_perform_space_price, package, 500 ) self.assertEqual(str(exception), "Could not find price for performance storage space") def test_find_saas_perform_space_price_no_capacity_minimum(self): package = { 'items': [ {'capacity': '0', 'capacityMaximum': '999', 'itemCategory': {'categoryCode': 'performance_storage_space'}, 'keyName': '500_999_GBS'} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_perform_space_price, package, 500 ) self.assertEqual(str(exception), "Could not find price for performance storage space") def test_find_saas_perform_space_price_size_below_capacity(self): package = { 'items': [ {'capacity': '0', 'capacityMaximum': '999', 'capacityMinimum': '500', 'itemCategory': {'categoryCode': 'performance_storage_space'}, 'keyName': '500_999_GBS'} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_perform_space_price, package, 499 ) self.assertEqual(str(exception), "Could not find price for performance storage space") def test_find_saas_perform_space_price_size_above_capacity(self): package = { 'items': [ {'capacity': '0', 'capacityMaximum': '999', 'capacityMinimum': '500', 'itemCategory': {'categoryCode': 'performance_storage_space'}, 'keyName': '500_999_GBS'} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_perform_space_price, package, 1000 ) self.assertEqual(str(exception), "Could not find price for performance storage space") def test_find_saas_perform_space_price_no_matching_keyname(self): package = { 'items': [ {'capacity': '0', 'capacityMaximum': '999', 'capacityMinimum': '500', 'itemCategory': {'categoryCode': 'performance_storage_space'}, 'keyName': 'NOT_THE_CORRECT_KEYNAME'} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_perform_space_price, package, 500 ) self.assertEqual(str(exception), "Could not find price for performance storage space") def test_find_saas_perform_space_price_no_prices_in_items(self): package = { 'items': [ {'capacity': '0', 'capacityMaximum': '999', 'capacityMinimum': '500', 'itemCategory': {'categoryCode': 'performance_storage_space'}, 'keyName': '500_999_GBS', 'prices': []} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_perform_space_price, package, 500 ) self.assertEqual(str(exception), "Could not find price for performance storage space") def test_find_saas_perform_space_price_empty_location_not_found(self): package = { 'items': [ {'capacity': '0', 'capacityMaximum': '999', 'capacityMinimum': '500', 'itemCategory': {'categoryCode': 'performance_storage_space'}, 'keyName': '500_999_GBS', 'prices': [ {'id': 189993, 'categories': [ {'categoryCode': 'performance_storage_space'} ], 'locationGroupId': '77777777'} ]} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_perform_space_price, package, 500 ) self.assertEqual(str(exception), "Could not find price for performance storage space") def test_find_saas_perform_space_price_category_not_found(self): package = { 'items': [ {'capacity': '0', 'capacityMaximum': '999', 'capacityMinimum': '500', 'itemCategory': {'categoryCode': 'performance_storage_space'}, 'keyName': '500_999_GBS', 'prices': [ {'id': 189993, 'categories': [ {'categoryCode': 'invalid_category_noooo'} ], 'locationGroupId': ''} ]} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_perform_space_price, package, 500 ) self.assertEqual(str(exception), "Could not find price for performance storage space") def test_find_saas_perform_space_price(self): package = { 'items': [ {'capacity': '0', 'capacityMaximum': '999', 'capacityMinimum': '500', 'itemCategory': {'categoryCode': 'performance_storage_space'}, 'keyName': '500_999_GBS', 'prices': [ {'id': 189993, 'categories': [ {'categoryCode': 'performance_storage_space'} ], 'locationGroupId': ''} ]} ]} result = storage_utils.find_saas_perform_space_price( package, 500) self.assertEqual({'id': 189993}, result) # --------------------------------------------------------------------- # Tests for find_saas_perform_iops_price() # --------------------------------------------------------------------- def test_find_saas_perform_iops_price_no_items_in_package(self): package = { 'items': []} exception = self.assertRaises( ValueError, storage_utils.find_saas_perform_iops_price, package, 500, 1700 ) self.assertEqual(str(exception), "Could not find price for iops for the given volume") def test_find_saas_perform_iops_price_no_itemCategory(self): package = { 'items': [ {'capacity': '0'} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_perform_iops_price, package, 500, 1700 ) self.assertEqual(str(exception), "Could not find price for iops for the given volume") def test_find_saas_perform_iops_price_no_itemCategory_code(self): package = { 'items': [ {'capacity': '0', 'itemCategory': {}} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_perform_iops_price, package, 500, 1700 ) self.assertEqual(str(exception), "Could not find price for iops for the given volume") def test_find_saas_perform_iops_price_no_matching_itemCategory(self): package = { 'items': [ {'capacity': '0', 'itemCategory': {'categoryCode': 'invalid_category_noooo'}} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_perform_iops_price, package, 500, 1700 ) self.assertEqual(str(exception), "Could not find price for iops for the given volume") def test_find_saas_perform_iops_price_no_capacity_maximum(self): package = { 'items': [ {'capacity': '0', 'capacityMinimum': '100', 'itemCategory': {'categoryCode': 'performance_storage_iops'}} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_perform_iops_price, package, 500, 1700 ) self.assertEqual(str(exception), "Could not find price for iops for the given volume") def test_find_saas_perform_iops_price_no_capacity_minimum(self): package = { 'items': [ {'capacity': '0', 'capacityMaximum': '10000', 'itemCategory': {'categoryCode': 'performance_storage_iops'}} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_perform_iops_price, package, 500, 1700 ) self.assertEqual(str(exception), "Could not find price for iops for the given volume") def test_find_saas_perform_iops_price_iops_below_capacity(self): package = { 'items': [ {'capacity': '0', 'capacityMaximum': '10000', 'capacityMinimum': '100', 'itemCategory': {'categoryCode': 'performance_storage_iops'}} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_perform_iops_price, package, 500, 99 ) self.assertEqual(str(exception), "Could not find price for iops for the given volume") def test_find_saas_perform_iops_price_iops_above_capacity(self): package = { 'items': [ {'capacity': '0', 'capacityMaximum': '10000', 'capacityMinimum': '100', 'itemCategory': {'categoryCode': 'performance_storage_iops'}} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_perform_iops_price, package, 500, 10001 ) self.assertEqual(str(exception), "Could not find price for iops for the given volume") def test_find_saas_perform_iops_price_no_prices_in_items(self): package = { 'items': [ {'capacity': '0', 'capacityMaximum': '10000', 'capacityMinimum': '100', 'itemCategory': {'categoryCode': 'performance_storage_iops'}, 'prices': []} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_perform_iops_price, package, 500, 1700 ) self.assertEqual(str(exception), "Could not find price for iops for the given volume") def test_find_saas_perform_iops_price_empty_location_not_found(self): package = { 'items': [ {'capacity': '0', 'capacityMaximum': '10000', 'capacityMinimum': '100', 'itemCategory': {'categoryCode': 'performance_storage_iops'}, 'prices': [ {'capacityRestrictionMaximum': '999', 'capacityRestrictionMinimum': '500', 'capacityRestrictionType': 'STORAGE_SPACE', 'categories': [ {'categoryCode': 'performance_storage_iops'} ], 'id': 190053, 'locationGroupId': '77777777'} ]} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_perform_iops_price, package, 500, 1700 ) self.assertEqual(str(exception), "Could not find price for iops for the given volume") def test_find_saas_perform_iops_price_category_not_found(self): package = { 'items': [ {'capacity': '0', 'capacityMaximum': '10000', 'capacityMinimum': '100', 'itemCategory': {'categoryCode': 'performance_storage_iops'}, 'prices': [ {'capacityRestrictionMaximum': '999', 'capacityRestrictionMinimum': '500', 'capacityRestrictionType': 'STORAGE_SPACE', 'categories': [ {'categoryCode': 'invalid_category_noooo'} ], 'id': 190053, 'locationGroupId': ''} ]} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_perform_iops_price, package, 500, 1700 ) self.assertEqual(str(exception), "Could not find price for iops for the given volume") def test_find_saas_perform_iops_price_wrong_capacity_restriction(self): package = { 'items': [ {'capacity': '0', 'capacityMaximum': '10000', 'capacityMinimum': '100', 'itemCategory': {'categoryCode': 'performance_storage_iops'}, 'prices': [ {'capacityRestrictionMaximum': '999', 'capacityRestrictionMinimum': '500', 'capacityRestrictionType': 'NOT_THE_CORRECT_TYPE', 'categories': [ {'categoryCode': 'performance_storage_iops'} ], 'id': 190053, 'locationGroupId': ''} ]} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_perform_iops_price, package, 500, 1700 ) self.assertEqual(str(exception), "Could not find price for iops for the given volume") def test_find_saas_perform_iops_price_size_below_capacity(self): package = { 'items': [ {'capacity': '0', 'capacityMaximum': '10000', 'capacityMinimum': '100', 'itemCategory': {'categoryCode': 'performance_storage_iops'}, 'prices': [ {'capacityRestrictionMaximum': '999', 'capacityRestrictionMinimum': '500', 'capacityRestrictionType': 'STORAGE_SPACE', 'categories': [ {'categoryCode': 'performance_storage_iops'} ], 'id': 190053, 'locationGroupId': ''} ]} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_perform_iops_price, package, 499, 1700 ) self.assertEqual(str(exception), "Could not find price for iops for the given volume") def test_find_saas_perform_iops_price_size_above_capacity(self): package = { 'items': [ {'capacity': '0', 'capacityMaximum': '10000', 'capacityMinimum': '100', 'itemCategory': {'categoryCode': 'performance_storage_iops'}, 'prices': [ {'capacityRestrictionMaximum': '999', 'capacityRestrictionMinimum': '500', 'capacityRestrictionType': 'STORAGE_SPACE', 'categories': [ {'categoryCode': 'performance_storage_iops'} ], 'id': 190053, 'locationGroupId': ''} ]} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_perform_iops_price, package, 1000, 1700 ) self.assertEqual(str(exception), "Could not find price for iops for the given volume") def test_find_saas_perform_iops_price(self): package = { 'items': [ {'capacity': '0', 'capacityMaximum': '10000', 'capacityMinimum': '100', 'itemCategory': {'categoryCode': 'performance_storage_iops'}, 'prices': [ {'capacityRestrictionMaximum': '999', 'capacityRestrictionMinimum': '500', 'capacityRestrictionType': 'STORAGE_SPACE', 'categories': [ {'categoryCode': 'performance_storage_iops'} ], 'id': 190053, 'locationGroupId': ''} ]} ]} result = storage_utils.find_saas_perform_iops_price( package, 500, 1700) self.assertEqual({'id': 190053}, result) # --------------------------------------------------------------------- # Tests for find_saas_snapshot_space_price() # --------------------------------------------------------------------- def test_find_saas_snapshot_space_price_no_items_in_package(self): package = { 'items': []} exception = self.assertRaises( ValueError, storage_utils.find_saas_snapshot_space_price, package, 10, iops=2100 ) self.assertEqual(str(exception), "Could not find price for snapshot space") def test_find_saas_snapshot_space_price_no_matching_capacity(self): package = { 'items': [ {'capacity': '-1'} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_snapshot_space_price, package, 10, iops=2100 ) self.assertEqual(str(exception), "Could not find price for snapshot space") def test_find_saas_snapshot_space_price_no_prices_in_items(self): package = { 'items': [ {'capacity': '10', 'prices': []} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_snapshot_space_price, package, 10, iops=2100 ) self.assertEqual(str(exception), "Could not find price for snapshot space") def test_find_saas_snapshot_space_price_empty_location_not_found(self): package = { 'items': [ {'capacity': '10', 'prices': [ {'capacityRestrictionMaximum': '48000', 'capacityRestrictionMinimum': '100', 'capacityRestrictionType': 'IOPS', 'categories': [ {'categoryCode': 'storage_snapshot_space'} ], 'id': 191193, 'locationGroupId': '77777777'} ]} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_snapshot_space_price, package, 10, iops=2100 ) self.assertEqual(str(exception), "Could not find price for snapshot space") def test_find_saas_snapshot_space_price_wrong_capacity_restriction(self): package = { 'items': [ {'capacity': '10', 'prices': [ {'capacityRestrictionMaximum': '48000', 'capacityRestrictionMinimum': '100', 'capacityRestrictionType': 'NOT_THE_CORRECT_CATEGORY', 'categories': [ {'categoryCode': 'storage_snapshot_space'} ], 'id': 191193, 'locationGroupId': ''} ]} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_snapshot_space_price, package, 10, iops=2100 ) self.assertEqual(str(exception), "Could not find price for snapshot space") def test_find_saas_snapshot_space_price_target_value_below_capacity(self): package = { 'items': [ {'capacity': '10', 'prices': [ {'capacityRestrictionMaximum': '48000', 'capacityRestrictionMinimum': '100', 'capacityRestrictionType': 'IOPS', 'categories': [ {'categoryCode': 'storage_snapshot_space'} ], 'id': 191193, 'locationGroupId': ''} ]} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_snapshot_space_price, package, 10, iops=99 ) self.assertEqual(str(exception), "Could not find price for snapshot space") def test_find_saas_snapshot_space_price_target_value_above_capacity(self): package = { 'items': [ {'capacity': '10', 'prices': [ {'capacityRestrictionMaximum': '48000', 'capacityRestrictionMinimum': '100', 'capacityRestrictionType': 'IOPS', 'categories': [ {'categoryCode': 'storage_snapshot_space'} ], 'id': 191193, 'locationGroupId': ''} ]} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_snapshot_space_price, package, 10, iops=48001 ) self.assertEqual(str(exception), "Could not find price for snapshot space") def test_find_saas_snapshot_space_price_category_not_found(self): package = { 'items': [ {'capacity': '10', 'prices': [ {'capacityRestrictionMaximum': '48000', 'capacityRestrictionMinimum': '100', 'capacityRestrictionType': 'IOPS', 'categories': [ {'categoryCode': 'invalid_category_noooooooo'} ], 'id': 191193, 'locationGroupId': ''} ]} ]} exception = self.assertRaises( ValueError, storage_utils.find_saas_snapshot_space_price, package, 10, iops=2100 ) self.assertEqual(str(exception), "Could not find price for snapshot space") def test_find_saas_snapshot_space_price_with_iops(self): package = { 'items': [ {'capacity': '10', 'prices': [ {'capacityRestrictionMaximum': '48000', 'capacityRestrictionMinimum': '100', 'capacityRestrictionType': 'IOPS', 'categories': [ {'categoryCode': 'storage_snapshot_space'} ], 'id': 191193, 'locationGroupId': ''} ]} ]} result = storage_utils.find_saas_snapshot_space_price( package, 10, iops=2100) self.assertEqual({'id': 191193}, result) def test_find_saas_snapshot_space_price_with_tier_level(self): package = { 'items': [ {'capacity': '10', 'prices': [ {'capacityRestrictionMaximum': '200', 'capacityRestrictionMinimum': '200', 'capacityRestrictionType': 'STORAGE_TIER_LEVEL', 'categories': [ {'categoryCode': 'storage_snapshot_space'} ], 'id': 193613, 'locationGroupId': ''} ]} ]} result = storage_utils.find_saas_snapshot_space_price( package, 10, tier=2) self.assertEqual({'id': 193613}, result) # --------------------------------------------------------------------- # Tests for find_saas_replication_price () # --------------------------------------------------------------------- def test_find_saas_replication_price_no_items_in_package(self): package = { 'items': [] } exception = self.assertRaises( ValueError, storage_utils.find_saas_replication_price, package, tier=2 ) self.assertEqual( "Could not find price for replicant volume", str(exception) ) def test_find_saas_replication_price_no_matching_key_name(self): package = { 'items': [ {'keyName': 'THIS_IS_NOT_THE_ITEM_YOU_ARE_LOOKING_FOR'} ] } exception = self.assertRaises( ValueError, storage_utils.find_saas_replication_price, package, tier=2 ) self.assertEqual( "Could not find price for replicant volume", str(exception) ) def test_find_saas_replication_price_no_prices_in_items(self): package = { 'items': [ { 'keyName': 'REPLICATION_FOR_TIERBASED_PERFORMANCE', 'prices': [] } ] } exception = self.assertRaises( ValueError, storage_utils.find_saas_replication_price, package, tier=2 ) self.assertEqual( "Could not find price for replicant volume", str(exception) ) def test_find_saas_replication_price_empty_location_not_found(self): package = { 'items': [ { 'keyName': 'REPLICATION_FOR_TIERBASED_PERFORMANCE', 'prices': [ { 'capacityRestrictionMaximum': '200', 'capacityRestrictionMinimum': '200', 'capacityRestrictionType': 'STORAGE_TIER_LEVEL', 'categories': [ {'categoryCode': 'performance_storage_replication'} ], 'id': 194693, 'locationGroupId': '77777777' } ] } ] } exception = self.assertRaises( ValueError, storage_utils.find_saas_replication_price, package, tier=2 ) self.assertEqual( "Could not find price for replicant volume", str(exception) ) def test_find_saas_replication_price_wrong_capacity_restriction(self): package = { 'items': [ { 'keyName': 'REPLICATION_FOR_TIERBASED_PERFORMANCE', 'prices': [ { 'capacityRestrictionMaximum': '200', 'capacityRestrictionMinimum': '200', 'capacityRestrictionType': 'WRONG_TYPE_WOAH', 'categories': [ {'categoryCode': 'performance_storage_replication'} ], 'id': 194693, 'locationGroupId': '' } ] } ] } exception = self.assertRaises( ValueError, storage_utils.find_saas_replication_price, package, tier=2 ) self.assertEqual( "Could not find price for replicant volume", str(exception) ) def test_find_saas_replication_price_target_value_below_capacity(self): package = { 'items': [ { 'keyName': 'REPLICATION_FOR_TIERBASED_PERFORMANCE', 'prices': [ { 'capacityRestrictionMaximum': '200', 'capacityRestrictionMinimum': '200', 'capacityRestrictionType': 'STORAGE_TIER_LEVEL', 'categories': [ {'categoryCode': 'performance_storage_replication'} ], 'id': 194693, 'locationGroupId': '' } ] } ] } exception = self.assertRaises( ValueError, storage_utils.find_saas_replication_price, package, tier=0.25 ) self.assertEqual( "Could not find price for replicant volume", str(exception) ) def test_find_saas_replication_price_target_value_above_capacity(self): package = { 'items': [ { 'keyName': 'REPLICATION_FOR_TIERBASED_PERFORMANCE', 'prices': [ { 'capacityRestrictionMaximum': '200', 'capacityRestrictionMinimum': '200', 'capacityRestrictionType': 'STORAGE_TIER_LEVEL', 'categories': [ {'categoryCode': 'performance_storage_replication'} ], 'id': 194693, 'locationGroupId': '' } ] } ] } exception = self.assertRaises( ValueError, storage_utils.find_saas_replication_price, package, tier=4 ) self.assertEqual( "Could not find price for replicant volume", str(exception) ) def test_find_saas_replication_price_category_not_found(self): package = { 'items': [ { 'keyName': 'REPLICATION_FOR_TIERBASED_PERFORMANCE', 'prices': [ { 'capacityRestrictionMaximum': '200', 'capacityRestrictionMinimum': '200', 'capacityRestrictionType': 'STORAGE_TIER_LEVEL', 'categories': [ {'categoryCode': 'invalid_category_oh_noooo'} ], 'id': 194693, 'locationGroupId': '' } ] } ] } exception = self.assertRaises( ValueError, storage_utils.find_saas_replication_price, package, tier=2 ) self.assertEqual( "Could not find price for replicant volume", str(exception) ) def test_find_saas_replication_price_with_tier(self): package = { 'items': [ { 'keyName': 'REPLICATION_FOR_TIERBASED_PERFORMANCE', 'prices': [ { 'capacityRestrictionMaximum': '200', 'capacityRestrictionMinimum': '200', 'capacityRestrictionType': 'STORAGE_TIER_LEVEL', 'categories': [ {'categoryCode': 'performance_storage_replication'} ], 'id': 194693, 'locationGroupId': '' } ] } ] } result = storage_utils.find_saas_replication_price( package, tier=2 ) self.assertEqual({'id': 194693}, result) def test_find_saas_replication_price_with_iops(self): package = { 'items': [ { 'keyName': 'REPLICATION_FOR_IOPSBASED_PERFORMANCE', 'prices': [ { 'capacityRestrictionMaximum': '48000', 'capacityRestrictionMinimum': '1', 'capacityRestrictionType': 'IOPS', 'categories': [ {'categoryCode': 'performance_storage_replication'} ], 'id': 192033, 'locationGroupId': '' } ] } ] } result = storage_utils.find_saas_replication_price( package, iops=800 ) self.assertEqual({'id': 192033}, result) # --------------------------------------------------------------------- # Tests for find_snapshot_schedule_id() # --------------------------------------------------------------------- def test_find_snapshot_schedule_id_no_schedules(self): volume = { 'schedules': [] } exception = self.assertRaises( ValueError, storage_utils.find_snapshot_schedule_id, volume, 'SNAPSHOT_WEEKLY' ) self.assertEqual( "The given snapshot schedule ID was not found for " "the given storage volume", str(exception) ) def test_find_snapshot_schedule_id_no_type_in_schedule(self): volume = { 'schedules': [ {'id': 888} ] } exception = self.assertRaises( ValueError, storage_utils.find_snapshot_schedule_id, volume, 'SNAPSHOT_WEEKLY' ) self.assertEqual( "The given snapshot schedule ID was not found for " "the given storage volume", str(exception) ) def test_find_snapshot_schedule_id_no_keyname_in_schedule_type(self): volume = { 'schedules': [ { 'id': 888, 'type': {} } ] } exception = self.assertRaises( ValueError, storage_utils.find_snapshot_schedule_id, volume, 'SNAPSHOT_WEEKLY' ) self.assertEqual( "The given snapshot schedule ID was not found for " "the given storage volume", str(exception) ) def test_find_snapshot_schedule_id_no_matching_keyname(self): volume = { 'schedules': [ { 'id': 888, 'type': {'keyname': 'SNAPSHOT_DAILY'} } ] } exception = self.assertRaises( ValueError, storage_utils.find_snapshot_schedule_id, volume, 'SNAPSHOT_WEEKLY' ) self.assertEqual( "The given snapshot schedule ID was not found for " "the given storage volume", str(exception) ) def test_find_snapshot_schedule_id(self): volume = { 'schedules': [ { 'id': 888, 'type': {'keyname': 'SNAPSHOT_DAILY'} }, { 'id': 999, 'type': {'keyname': 'SNAPSHOT_WEEKLY'} } ] } result = storage_utils.find_snapshot_schedule_id( volume, 'SNAPSHOT_WEEKLY' ) self.assertEqual(999, result) # --------------------------------------------------------------------- # Tests for prepare_snapshot_order_object() # --------------------------------------------------------------------- def test_prep_snapshot_order_billing_item_cancelled(self): mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) del mock_volume['billingItem'] exception = self.assertRaises( exceptions.SoftLayerError, storage_utils.prepare_snapshot_order_object, self.block, mock_volume, 10, None, False ) self.assertEqual( "This volume has been cancelled; unable to order snapshot space", str(exception) ) def test_prep_snapshot_order_invalid_billing_item_category_code(self): mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['billingItem']['categoryCode'] = 'invalid_type_ninja_cat' exception = self.assertRaises( exceptions.SoftLayerError, storage_utils.prepare_snapshot_order_object, self.block, mock_volume, 10, None, False ) self.assertEqual( "Snapshot space cannot be ordered for a primary volume with a " "billing item category code of 'invalid_type_ninja_cat'", str(exception) ) def test_prep_snapshot_order_saas_endurance_tier_is_not_none(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME expected_object = { 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_Storage_Enterprise_SnapshotSpace', 'packageId': 759, 'prices': [{'id': 193613}], 'quantity': 1, 'location': 449500, 'volumeId': 102, 'useHourlyPricing': False } result = storage_utils.prepare_snapshot_order_object( self.block, mock_volume, 10, 2, False ) self.assertEqual(expected_object, result) def test_prep_snapshot_order_saas_endurance_upgrade(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME expected_object = { 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_Storage_Enterprise_SnapshotSpace_Upgrade', 'packageId': 759, 'prices': [{'id': 193853}], 'quantity': 1, 'location': 449500, 'volumeId': 102, 'useHourlyPricing': False } result = storage_utils.prepare_snapshot_order_object( self.block, mock_volume, 20, None, True ) self.assertEqual(expected_object, result) def test_prep_snapshot_order_saas_performance_volume_below_staas_v2(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['storageType']['keyName'] = 'PERFORMANCE_BLOCK_STORAGE' mock_volume['staasVersion'] = '1' exception = self.assertRaises( exceptions.SoftLayerError, storage_utils.prepare_snapshot_order_object, self.block, mock_volume, 10, None, False ) self.assertEqual( "Snapshot space cannot be ordered for this performance " "volume since it does not support Encryption at Rest.", str(exception) ) def test_prep_snapshot_order_saas_performance(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['storageType']['keyName'] = 'PERFORMANCE_BLOCK_STORAGE' expected_object = { 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_Storage_Enterprise_SnapshotSpace', 'packageId': 759, 'prices': [{'id': 191193}], 'quantity': 1, 'location': 449500, 'volumeId': 102, 'useHourlyPricing': False } result = storage_utils.prepare_snapshot_order_object( self.block, mock_volume, 10, None, False ) self.assertEqual(expected_object, result) def test_prep_snapshot_order_saas_invalid_storage_type(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['storageType']['keyName'] = 'TASTY_PASTA_STORAGE' exception = self.assertRaises( exceptions.SoftLayerError, storage_utils.prepare_snapshot_order_object, self.block, mock_volume, 10, None, False ) self.assertEqual( "Storage volume does not have a valid storage type " "(with an appropriate keyName to indicate the " "volume is a PERFORMANCE or an ENDURANCE volume)", str(exception) ) def test_prep_snapshot_order_enterprise_tier_is_not_none(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [ fixtures.SoftLayer_Product_Package.ENTERPRISE_PACKAGE ] mock_volume = fixtures.SoftLayer_Network_Storage.getObject expected_object = { 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_Storage_Enterprise_SnapshotSpace', 'packageId': 240, 'prices': [{'id': 46160}], 'quantity': 1, 'location': 449500, 'volumeId': 100, 'useHourlyPricing': False } result = storage_utils.prepare_snapshot_order_object( self.block, mock_volume, 10, 2, False ) self.assertEqual(expected_object, result) def test_prep_snapshot_order_enterprise(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [ fixtures.SoftLayer_Product_Package.ENTERPRISE_PACKAGE ] mock_volume = fixtures.SoftLayer_Network_Storage.getObject expected_object = { 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_Storage_Enterprise_SnapshotSpace', 'packageId': 240, 'prices': [{'id': 45860}], 'quantity': 1, 'location': 449500, 'volumeId': 100, 'useHourlyPricing': False } result = storage_utils.prepare_snapshot_order_object( self.block, mock_volume, 20, None, False ) self.assertEqual(expected_object, result) def test_prep_snapshot_order_hourly_billing(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['billingItem']['hourlyFlag'] = True expected_object = { 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_Storage_Enterprise_SnapshotSpace', 'packageId': 759, 'prices': [{'id': 193853}], 'quantity': 1, 'location': 449500, 'volumeId': 102, 'useHourlyPricing': True } result = storage_utils.prepare_snapshot_order_object( self.block, mock_volume, 20, None, False ) self.assertEqual(expected_object, result) # --------------------------------------------------------------------- # Tests for prepare_volume_order_object() # --------------------------------------------------------------------- def test_prep_volume_order_invalid_storage_type(self): exception = self.assertRaises( exceptions.SoftLayerError, storage_utils.prepare_volume_order_object, self.block, 'saxophone_cat', 'dal09', 1000, None, 4, None, 'enterprise', 'block' ) self.assertEqual( "Volume storage type must be either performance or endurance", str(exception) ) def test_prep_volume_order_invalid_location(self): mock = self.set_mock('SoftLayer_Location_Datacenter', 'getDatacenters') mock.return_value = [] exception = self.assertRaises( exceptions.SoftLayerError, storage_utils.prepare_volume_order_object, self.block, 'endurance', 'hoth01', 1000, None, 4, None, 'enterprise', 'block' ) self.assertEqual( "Invalid datacenter name specified. " "Please provide the lower case short name (e.g.: dal09)", str(exception) ) def test_prep_volume_order_enterprise_offering_invalid_storage_type(self): mock = self.set_mock('SoftLayer_Location_Datacenter', 'getDatacenters') mock.return_value = [{'id': 29, 'name': 'dal09'}] exception = self.assertRaises( exceptions.SoftLayerError, storage_utils.prepare_volume_order_object, self.block, 'performance', 'dal09', 1000, None, 4, None, 'enterprise', 'block' ) self.assertEqual( "The requested offering package, 'enterprise', is not " "available for the 'performance' storage type.", str(exception) ) def test_prep_volume_order_performance_offering_invalid_storage_type(self): mock = self.set_mock('SoftLayer_Location_Datacenter', 'getDatacenters') mock.return_value = [{'id': 29, 'name': 'dal09'}] exception = self.assertRaises( exceptions.SoftLayerError, storage_utils.prepare_volume_order_object, self.block, 'endurance', 'dal09', 1000, 800, None, None, 'performance', 'block' ) self.assertEqual( "The requested offering package, 'performance', is not " "available for the 'endurance' storage type.", str(exception) ) def test_prep_volume_order_invalid_offering(self): mock = self.set_mock('SoftLayer_Location_Datacenter', 'getDatacenters') mock.return_value = [{'id': 29, 'name': 'dal09'}] exception = self.assertRaises( exceptions.SoftLayerError, storage_utils.prepare_volume_order_object, self.block, 'endurance', 'dal09', 1000, None, 4, None, 'jazz_penguins', 'block' ) self.assertEqual( "The requested service offering package is not valid. " "Please check the available options and try again.", str(exception) ) def test_prep_volume_order_saas_performance(self): mock = self.set_mock('SoftLayer_Location_Datacenter', 'getDatacenters') mock.return_value = [{'id': 29, 'name': 'dal09'}] mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] expected_object = { 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_Storage_AsAService', 'packageId': 759, 'prices': [ {'id': 189433}, {'id': 189453}, {'id': 190113}, {'id': 190173} ], 'quantity': 1, 'location': 29, 'volumeSize': 1000, 'iops': 800, 'useHourlyPricing': False } result = storage_utils.prepare_volume_order_object( self.file, 'performance', 'dal09', 1000, 800, None, None, 'storage_as_a_service', 'file' ) self.assertEqual(expected_object, result) def test_prep_volume_order_saas_performance_with_snapshot(self): mock = self.set_mock('SoftLayer_Location_Datacenter', 'getDatacenters') mock.return_value = [{'id': 29, 'name': 'dal09'}] mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] expected_object = { 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_Storage_AsAService', 'packageId': 759, 'prices': [ {'id': 189433}, {'id': 189453}, {'id': 190113}, {'id': 190173}, {'id': 191193} ], 'quantity': 1, 'location': 29, 'volumeSize': 1000, 'iops': 800, 'useHourlyPricing': False } result = storage_utils.prepare_volume_order_object( self.file, 'performance', 'dal09', 1000, 800, None, 10, 'storage_as_a_service', 'file' ) self.assertEqual(expected_object, result) def test_prep_volume_order_saas_endurance(self): mock = self.set_mock('SoftLayer_Location_Datacenter', 'getDatacenters') mock.return_value = [{'id': 29, 'name': 'dal09'}] mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] expected_object = { 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_Storage_AsAService', 'packageId': 759, 'prices': [ {'id': 189433}, {'id': 189443}, {'id': 194763}, {'id': 194703} ], 'quantity': 1, 'location': 29, 'volumeSize': 1000, 'useHourlyPricing': False } result = storage_utils.prepare_volume_order_object( self.block, 'endurance', 'dal09', 1000, None, 4, None, 'storage_as_a_service', 'block' ) self.assertEqual(expected_object, result) def test_prep_volume_order_saas_endurance_with_snapshot(self): mock = self.set_mock('SoftLayer_Location_Datacenter', 'getDatacenters') mock.return_value = [{'id': 29, 'name': 'dal09'}] mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] expected_object = { 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_Storage_AsAService', 'packageId': 759, 'prices': [ {'id': 189433}, {'id': 189443}, {'id': 194763}, {'id': 194703}, {'id': 194943} ], 'quantity': 1, 'location': 29, 'volumeSize': 1000, 'useHourlyPricing': False } result = storage_utils.prepare_volume_order_object( self.block, 'endurance', 'dal09', 1000, None, 4, 10, 'storage_as_a_service', 'block' ) self.assertEqual(expected_object, result) def test_prep_volume_order_perf_performance_block(self): mock = self.set_mock('SoftLayer_Location_Datacenter', 'getDatacenters') mock.return_value = [{'id': 29, 'name': 'dal09'}] mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [ fixtures.SoftLayer_Product_Package.PERFORMANCE_PACKAGE ] expected_object = { 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_PerformanceStorage_Iscsi', 'packageId': 222, 'prices': [ {'id': 40672}, {'id': 40742}, {'id': 41562} ], 'quantity': 1, 'location': 29, 'useHourlyPricing': False } result = storage_utils.prepare_volume_order_object( self.block, 'performance', 'dal09', 1000, 800, None, None, 'performance', 'block' ) self.assertEqual(expected_object, result) def test_prep_volume_order_perf_performance_file(self): mock = self.set_mock('SoftLayer_Location_Datacenter', 'getDatacenters') mock.return_value = [{'id': 29, 'name': 'dal09'}] mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [ fixtures.SoftLayer_Product_Package.PERFORMANCE_PACKAGE ] expected_object = { 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_PerformanceStorage_Nfs', 'packageId': 222, 'prices': [ {'id': 40662}, {'id': 40742}, {'id': 41562} ], 'quantity': 1, 'location': 29, 'useHourlyPricing': False } result = storage_utils.prepare_volume_order_object( self.file, 'performance', 'dal09', 1000, 800, None, None, 'performance', 'file' ) self.assertEqual(expected_object, result) def test_prep_volume_order_ent_endurance(self): mock = self.set_mock('SoftLayer_Location_Datacenter', 'getDatacenters') mock.return_value = [{'id': 29, 'name': 'dal09'}] mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [ fixtures.SoftLayer_Product_Package.ENTERPRISE_PACKAGE ] expected_object = { 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_Storage_Enterprise', 'packageId': 240, 'prices': [ {'id': 45058}, {'id': 45108}, {'id': 45318}, {'id': 45088} ], 'quantity': 1, 'location': 29, 'useHourlyPricing': False } result = storage_utils.prepare_volume_order_object( self.file, 'endurance', 'dal09', 1000, None, 4, None, 'enterprise', 'file' ) self.assertEqual(expected_object, result) def test_prep_volume_order_ent_endurance_with_snapshot(self): mock = self.set_mock('SoftLayer_Location_Datacenter', 'getDatacenters') mock.return_value = [{'id': 29, 'name': 'dal09'}] mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [ fixtures.SoftLayer_Product_Package.ENTERPRISE_PACKAGE ] expected_object = { 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_Storage_Enterprise', 'packageId': 240, 'prices': [ {'id': 45058}, {'id': 45098}, {'id': 45318}, {'id': 45088}, {'id': 46170} ], 'quantity': 1, 'location': 29, 'useHourlyPricing': False } result = storage_utils.prepare_volume_order_object( self.block, 'endurance', 'dal09', 1000, None, 4, 10, 'enterprise', 'block' ) self.assertEqual(expected_object, result) # --------------------------------------------------------------------- # Tests for prepare_replicant_order_object() # --------------------------------------------------------------------- def test_prep_replicant_order_volume_cancelled(self): mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) del mock_volume['billingItem'] exception = self.assertRaises( exceptions.SoftLayerError, storage_utils.prepare_replicant_order_object, self.block, 'WEEKLY', 'wdc04', None, mock_volume, 'block' ) self.assertEqual( 'This volume is set for cancellation; ' 'unable to order replicant volume', str(exception) ) def test_prep_replicant_order_volume_cancellation_date_set(self): mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['billingItem']['cancellationDate'] = 'y2k, oh nooooo' exception = self.assertRaises( exceptions.SoftLayerError, storage_utils.prepare_replicant_order_object, self.block, 'WEEKLY', 'wdc04', None, mock_volume, 'block' ) self.assertEqual( 'This volume is set for cancellation; ' 'unable to order replicant volume', str(exception) ) def test_prep_replicant_order_snapshot_space_cancelled(self): mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) snapshot_billing_item = mock_volume['billingItem']['activeChildren'][0] snapshot_billing_item['cancellationDate'] = 'daylight saving time, no!' exception = self.assertRaises( exceptions.SoftLayerError, storage_utils.prepare_replicant_order_object, self.block, 'WEEKLY', 'wdc04', None, mock_volume, 'block' ) self.assertEqual( 'The snapshot space for this volume is set for ' 'cancellation; unable to order replicant volume', str(exception) ) def test_prep_replicant_order_invalid_location(self): mock = self.set_mock('SoftLayer_Location_Datacenter', 'getDatacenters') mock.return_value = [] mock_volume = fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME exception = self.assertRaises( exceptions.SoftLayerError, storage_utils.prepare_replicant_order_object, self.block, 'WEEKLY', 'hoth02', None, mock_volume, 'block' ) self.assertEqual( "Invalid datacenter name specified. " "Please provide the lower case short name (e.g.: dal09)", str(exception) ) def test_prep_replicant_order_enterprise_offering_invalid_type(self): mock = self.set_mock('SoftLayer_Location_Datacenter', 'getDatacenters') mock.return_value = [{'id': 51, 'name': 'wdc04'}] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['billingItem']['categoryCode'] = 'invalid_type_ninja_cat' exception = self.assertRaises( exceptions.SoftLayerError, storage_utils.prepare_replicant_order_object, self.block, 'WEEKLY', 'wdc04', None, mock_volume, 'block' ) self.assertEqual( "A replicant volume cannot be ordered for a primary volume with a " "billing item category code of 'invalid_type_ninja_cat'", str(exception) ) def test_prep_replicant_order_snapshot_capacity_not_found(self): mock = self.set_mock('SoftLayer_Location_Datacenter', 'getDatacenters') mock.return_value = [{'id': 51, 'name': 'wdc04'}] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) del mock_volume['snapshotCapacityGb'] exception = self.assertRaises( exceptions.SoftLayerError, storage_utils.prepare_replicant_order_object, self.block, 'WEEKLY', 'wdc04', None, mock_volume, 'block' ) self.assertEqual( "Snapshot capacity not found for the given primary volume", str(exception) ) def test_prep_replicant_order_saas_endurance(self): mock = self.set_mock('SoftLayer_Location_Datacenter', 'getDatacenters') mock.return_value = [{'id': 51, 'name': 'wdc04'}] mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME expected_object = { 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_Storage_AsAService', 'packageId': 759, 'prices': [ {'id': 189433}, {'id': 189443}, {'id': 193433}, {'id': 193373}, {'id': 193613}, {'id': 194693} ], 'quantity': 1, 'location': 51, 'originVolumeId': 102, 'originVolumeScheduleId': 978, 'volumeSize': 500, 'useHourlyPricing': False } result = storage_utils.prepare_replicant_order_object( self.block, 'WEEKLY', 'wdc04', None, mock_volume, 'block' ) self.assertEqual(expected_object, result) def test_prep_replicant_order_saas_endurance_tier_is_not_none(self): mock = self.set_mock('SoftLayer_Location_Datacenter', 'getDatacenters') mock.return_value = [{'id': 51, 'name': 'wdc04'}] mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME expected_object = { 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_Storage_AsAService', 'packageId': 759, 'prices': [ {'id': 189433}, {'id': 189443}, {'id': 193433}, {'id': 193373}, {'id': 193613}, {'id': 194693} ], 'quantity': 1, 'location': 51, 'originVolumeId': 102, 'originVolumeScheduleId': 978, 'volumeSize': 500, 'useHourlyPricing': False } result = storage_utils.prepare_replicant_order_object( self.block, 'WEEKLY', 'wdc04', 2, mock_volume, 'block' ) self.assertEqual(expected_object, result) def test_prep_replicant_order_saas_performance_volume_below_staas_v2(self): mock = self.set_mock('SoftLayer_Location_Datacenter', 'getDatacenters') mock.return_value = [{'id': 51, 'name': 'wdc04'}] mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['storageType']['keyName'] = 'PERFORMANCE_BLOCK_STORAGE' mock_volume['hasEncryptionAtRest'] = 0 exception = self.assertRaises( exceptions.SoftLayerError, storage_utils.prepare_replicant_order_object, self.block, 'WEEKLY', 'wdc04', None, mock_volume, 'block' ) self.assertEqual( "A replica volume cannot be ordered for this performance " "volume since it does not support Encryption at Rest.", str(exception) ) def test_prep_replicant_order_saas_performance(self): mock = self.set_mock('SoftLayer_Location_Datacenter', 'getDatacenters') mock.return_value = [{'id': 51, 'name': 'wdc04'}] mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['storageType']['keyName'] = 'PERFORMANCE_BLOCK_STORAGE' expected_object = { 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_Storage_AsAService', 'packageId': 759, 'prices': [ {'id': 189433}, {'id': 189443}, {'id': 189993}, {'id': 190053}, {'id': 191193}, {'id': 192033} ], 'quantity': 1, 'location': 51, 'originVolumeId': 102, 'originVolumeScheduleId': 978, 'volumeSize': 500, 'iops': 1000, 'useHourlyPricing': False } result = storage_utils.prepare_replicant_order_object( self.block, 'WEEKLY', 'wdc04', None, mock_volume, 'block' ) self.assertEqual(expected_object, result) def test_prep_replicant_order_saas_invalid_storage_type(self): mock = self.set_mock('SoftLayer_Location_Datacenter', 'getDatacenters') mock.return_value = [{'id': 51, 'name': 'wdc04'}] mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['storageType']['keyName'] = 'CATS_LIKE_PIANO_MUSIC' exception = self.assertRaises( exceptions.SoftLayerError, storage_utils.prepare_replicant_order_object, self.block, 'WEEKLY', 'wdc04', None, mock_volume, 'block' ) self.assertEqual( "Storage volume does not have a valid storage type " "(with an appropriate keyName to indicate the " "volume is a PERFORMANCE or an ENDURANCE volume)", str(exception) ) def test_prep_replicant_order_ent_endurance(self): mock = self.set_mock('SoftLayer_Location_Datacenter', 'getDatacenters') mock.return_value = [{'id': 51, 'name': 'wdc04'}] mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [ fixtures.SoftLayer_Product_Package.ENTERPRISE_PACKAGE ] mock_volume = fixtures.SoftLayer_Network_Storage.getObject expected_object = { 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_Storage_Enterprise', 'packageId': 240, 'prices': [ {'id': 45058}, {'id': 45098}, {'id': 45128}, {'id': 45078}, {'id': 46160}, {'id': 46659} ], 'quantity': 1, 'location': 51, 'originVolumeId': 100, 'originVolumeScheduleId': 978, 'useHourlyPricing': False } result = storage_utils.prepare_replicant_order_object( self.block, 'WEEKLY', 'wdc04', None, mock_volume, 'block' ) self.assertEqual(expected_object, result) def test_prep_replicant_order_ent_endurance_tier_is_not_none(self): mock = self.set_mock('SoftLayer_Location_Datacenter', 'getDatacenters') mock.return_value = [{'id': 51, 'name': 'wdc04'}] mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [ fixtures.SoftLayer_Product_Package.ENTERPRISE_PACKAGE ] mock_volume = fixtures.SoftLayer_Network_Storage.getObject expected_object = { 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_Storage_Enterprise', 'packageId': 240, 'prices': [ {'id': 45058}, {'id': 45098}, {'id': 45128}, {'id': 45078}, {'id': 46160}, {'id': 46659} ], 'quantity': 1, 'location': 51, 'originVolumeId': 100, 'originVolumeScheduleId': 978, 'useHourlyPricing': False } result = storage_utils.prepare_replicant_order_object( self.block, 'WEEKLY', 'wdc04', 2, mock_volume, 'block' ) self.assertEqual(expected_object, result) def test_prep_replicant_order_hourly_billing(self): mock = self.set_mock('SoftLayer_Location_Datacenter', 'getDatacenters') mock.return_value = [{'id': 51, 'name': 'wdc04'}] mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['billingItem']['hourlyFlag'] = True expected_object = { 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_Storage_AsAService', 'packageId': 759, 'prices': [ {'id': 189433}, {'id': 189443}, {'id': 193433}, {'id': 193373}, {'id': 193613}, {'id': 194693} ], 'quantity': 1, 'location': 51, 'originVolumeId': 102, 'originVolumeScheduleId': 978, 'volumeSize': 500, 'useHourlyPricing': True } result = storage_utils.prepare_replicant_order_object( self.block, 'WEEKLY', 'wdc04', None, mock_volume, 'block' ) self.assertEqual(expected_object, result) # --------------------------------------------------------------------- # Tests for prepare_duplicate_order_object() # --------------------------------------------------------------------- def test_prep_duplicate_order_origin_volume_cancelled(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) del mock_volume['billingItem'] exception = self.assertRaises( exceptions.SoftLayerError, storage_utils.prepare_duplicate_order_object, self.block, mock_volume, None, None, None, None, 'block' ) self.assertEqual(str(exception), "The origin volume has been cancelled; " "unable to order duplicate volume") def test_prep_duplicate_order_origin_snapshot_capacity_not_found(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) del mock_volume['snapshotCapacityGb'] exception = self.assertRaises( exceptions.SoftLayerError, storage_utils.prepare_duplicate_order_object, self.block, mock_volume, None, None, None, None, 'block' ) self.assertEqual(str(exception), "Snapshot space not found for the origin volume. " "Origin snapshot space is needed for duplication.") def test_prep_duplicate_order_origin_volume_location_not_found(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) del mock_volume['billingItem']['location'] exception = self.assertRaises( exceptions.SoftLayerError, storage_utils.prepare_duplicate_order_object, self.block, mock_volume, None, None, None, None, 'block' ) self.assertEqual(str(exception), "Cannot find origin volume's location") def test_prep_duplicate_order_origin_volume_staas_version_below_v2(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['staasVersion'] = 1 exception = self.assertRaises( exceptions.SoftLayerError, storage_utils.prepare_duplicate_order_object, self.block, mock_volume, None, None, None, None, 'block' ) self.assertEqual("This volume cannot be duplicated since it " "does not support Encryption at Rest.", str(exception)) def test_prep_duplicate_order_performance_origin_iops_not_found(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['storageType']['keyName'] = 'PERFORMANCE_BLOCK_STORAGE_REPLICANT' del mock_volume['provisionedIops'] exception = self.assertRaises( exceptions.SoftLayerError, storage_utils.prepare_duplicate_order_object, self.block, mock_volume, None, None, None, None, 'block' ) self.assertEqual(str(exception), "Cannot find origin volume's provisioned IOPS") def test_prep_duplicate_order_performance_use_default_origin_values(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['storageType']['keyName'] = 'PERFORMANCE_FILE_STORAGE_REPLICANT' expected_object = { 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_Storage_AsAService', 'packageId': 759, 'prices': [ {'id': 189433}, {'id': 189453}, {'id': 189993}, {'id': 190053}, {'id': 191193} ], 'volumeSize': 500, 'quantity': 1, 'location': 449500, 'duplicateOriginVolumeId': 102, 'iops': 1000, 'useHourlyPricing': False} result = storage_utils.prepare_duplicate_order_object( self.file, mock_volume, None, None, None, None, 'file') self.assertEqual(expected_object, result) def test_prep_duplicate_order_performance_block(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['storageType']['keyName'] = 'PERFORMANCE_BLOCK_STORAGE' expected_object = { 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_Storage_AsAService', 'packageId': 759, 'prices': [ {'id': 189433}, {'id': 189443}, {'id': 190113}, {'id': 190173}, {'id': 191193} ], 'volumeSize': 1000, 'quantity': 1, 'location': 449500, 'duplicateOriginVolumeId': 102, 'iops': 2000, 'useHourlyPricing': False} result = storage_utils.prepare_duplicate_order_object( self.block, mock_volume, 2000, None, 1000, 10, 'block') self.assertEqual(expected_object, result) def test_prep_duplicate_order_performance_file(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['storageType']['keyName'] = 'PERFORMANCE_FILE_STORAGE' expected_object = { 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_Storage_AsAService', 'packageId': 759, 'prices': [ {'id': 189433}, {'id': 189453}, {'id': 190113}, {'id': 190173}, {'id': 191193} ], 'volumeSize': 1000, 'quantity': 1, 'location': 449500, 'duplicateOriginVolumeId': 102, 'iops': 2000, 'useHourlyPricing': False} result = storage_utils.prepare_duplicate_order_object( self.file, mock_volume, 2000, None, 1000, 10, 'file') self.assertEqual(expected_object, result) def test_prep_duplicate_order_endurance_use_default_origin_values(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['storageType']['keyName'] = 'ENDURANCE_FILE_'\ 'STORAGE_REPLICANT' expected_object = { 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_Storage_AsAService', 'packageId': 759, 'prices': [ {'id': 189433}, {'id': 189453}, {'id': 193433}, {'id': 193373}, {'id': 193613} ], 'volumeSize': 500, 'quantity': 1, 'location': 449500, 'duplicateOriginVolumeId': 102, 'useHourlyPricing': False} result = storage_utils.prepare_duplicate_order_object( self.file, mock_volume, None, None, None, None, 'file') self.assertEqual(expected_object, result) def test_prep_duplicate_order_endurance_block(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME expected_object = { 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_Storage_AsAService', 'packageId': 759, 'prices': [ {'id': 189433}, {'id': 189443}, {'id': 194763}, {'id': 194703}, {'id': 194943} ], 'volumeSize': 1000, 'quantity': 1, 'location': 449500, 'duplicateOriginVolumeId': 102, 'useHourlyPricing': False} result = storage_utils.prepare_duplicate_order_object( self.block, mock_volume, None, 4.0, 1000, 10, 'block') self.assertEqual(expected_object, result) def test_prep_duplicate_order_endurance_file(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['storageType']['keyName'] = 'ENDURANCE_FILE_STORAGE' expected_object = { 'complexType': 'SoftLayer_Container_Product_Order_' 'Network_Storage_AsAService', 'packageId': 759, 'prices': [ {'id': 189433}, {'id': 189453}, {'id': 194763}, {'id': 194703}, {'id': 194943} ], 'volumeSize': 1000, 'quantity': 1, 'location': 449500, 'duplicateOriginVolumeId': 102, 'useHourlyPricing': False} result = storage_utils.prepare_duplicate_order_object( self.file, mock_volume, None, 4.0, 1000, 10, 'file') self.assertEqual(expected_object, result) def test_prep_duplicate_order_invalid_origin_storage_type(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['storageType']['keyName'] = 'NINJA_CATS' exception = self.assertRaises( exceptions.SoftLayerError, storage_utils.prepare_duplicate_order_object, self.block, mock_volume, None, None, None, None, 'block' ) self.assertEqual(str(exception), "Origin volume does not have a valid storage type " "(with an appropriate keyName to indicate the " "volume is a PERFORMANCE or an ENDURANCE volume)") # --------------------------------------------------------------------- # Tests for prepare_modify_order_object() # --------------------------------------------------------------------- def test_prep_modify_order_origin_volume_cancelled(self): mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) del mock_volume['billingItem'] exception = self.assertRaises(exceptions.SoftLayerError, storage_utils.prepare_modify_order_object, self.block, mock_volume, None, None, None) self.assertEqual("The volume has been cancelled; unable to modify volume.", str(exception)) def test_prep_modify_order_origin_volume_staas_version_below_v2(self): mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['staasVersion'] = 1 exception = self.assertRaises(exceptions.SoftLayerError, storage_utils.prepare_modify_order_object, self.block, mock_volume, None, None, None) self.assertEqual("This volume cannot be modified since it does not support Encryption at Rest.", str(exception)) def test_prep_modify_order_performance_values_not_given(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['storageType']['keyName'] = 'PERFORMANCE_BLOCK_STORAGE' exception = self.assertRaises(exceptions.SoftLayerError, storage_utils.prepare_modify_order_object, self.block, mock_volume, None, None, None) self.assertEqual("A size or IOPS value must be given to modify this performance volume.", str(exception)) def test_prep_modify_order_performance_iops_not_found(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['storageType']['keyName'] = 'PERFORMANCE_BLOCK_STORAGE' del mock_volume['provisionedIops'] exception = self.assertRaises(exceptions.SoftLayerError, storage_utils.prepare_modify_order_object, self.block, mock_volume, None, None, 40) self.assertEqual("Cannot find volume's provisioned IOPS.", str(exception)) def test_prep_modify_order_performance_use_existing_iops(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['storageType']['keyName'] = 'PERFORMANCE_FILE_STORAGE' expected_object = { 'complexType': 'SoftLayer_Container_Product_Order_Network_Storage_AsAService_Upgrade', 'packageId': 759, 'prices': [{'id': 189433}, {'id': 190113}, {'id': 190173}], 'volume': {'id': 102}, 'volumeSize': 1000, 'iops': 1000 } result = storage_utils.prepare_modify_order_object(self.file, mock_volume, None, None, 1000) self.assertEqual(expected_object, result) def test_prep_modify_order_performance_use_existing_size(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['storageType']['keyName'] = 'PERFORMANCE_BLOCK_STORAGE' expected_object = { 'complexType': 'SoftLayer_Container_Product_Order_Network_Storage_AsAService_Upgrade', 'packageId': 759, 'prices': [{'id': 189433}, {'id': 189993}, {'id': 190053}], 'volume': {'id': 102}, 'volumeSize': 500, 'iops': 2000 } result = storage_utils.prepare_modify_order_object(self.block, mock_volume, 2000, None, None) self.assertEqual(expected_object, result) def test_prep_modify_order_performance(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['storageType']['keyName'] = 'PERFORMANCE_FILE_STORAGE' expected_object = { 'complexType': 'SoftLayer_Container_Product_Order_Network_Storage_AsAService_Upgrade', 'packageId': 759, 'prices': [{'id': 189433}, {'id': 190113}, {'id': 190173}], 'volume': {'id': 102}, 'volumeSize': 1000, 'iops': 2000 } result = storage_utils.prepare_modify_order_object(self.file, mock_volume, 2000, None, 1000) self.assertEqual(expected_object, result) def test_prep_modify_order_endurance_values_not_given(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['storageType']['keyName'] = 'ENDURANCE_BLOCK_STORAGE' exception = self.assertRaises(exceptions.SoftLayerError, storage_utils.prepare_modify_order_object, self.block, mock_volume, None, None, None) self.assertEqual("A size or tier value must be given to modify this endurance volume.", str(exception)) def test_prep_modify_order_endurance_use_existing_tier(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['storageType']['keyName'] = 'ENDURANCE_FILE_STORAGE' expected_object = { 'complexType': 'SoftLayer_Container_Product_Order_Network_Storage_AsAService_Upgrade', 'packageId': 759, 'prices': [{'id': 189433}, {'id': 193433}, {'id': 193373}], 'volume': {'id': 102}, 'volumeSize': 1000 } result = storage_utils.prepare_modify_order_object(self.file, mock_volume, None, None, 1000) self.assertEqual(expected_object, result) def test_prep_modify_order_endurance_use_existing_size(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['storageType']['keyName'] = 'ENDURANCE_BLOCK_STORAGE' expected_object = { 'complexType': 'SoftLayer_Container_Product_Order_Network_Storage_AsAService_Upgrade', 'packageId': 759, 'prices': [{'id': 189433}, {'id': 194763}, {'id': 194703}], 'volume': {'id': 102}, 'volumeSize': 500 } result = storage_utils.prepare_modify_order_object(self.block, mock_volume, None, 4, None) self.assertEqual(expected_object, result) def test_prep_modify_order_endurance(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['storageType']['keyName'] = 'ENDURANCE_FILE_STORAGE' expected_object = { 'complexType': 'SoftLayer_Container_Product_Order_Network_Storage_AsAService_Upgrade', 'packageId': 759, 'prices': [{'id': 189433}, {'id': 194763}, {'id': 194703}], 'volume': {'id': 102}, 'volumeSize': 1000 } result = storage_utils.prepare_modify_order_object(self.file, mock_volume, None, 4, 1000) self.assertEqual(expected_object, result) def test_prep_modify_order_invalid_volume_storage_type(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [fixtures.SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(fixtures.SoftLayer_Network_Storage.STAAS_TEST_VOLUME) mock_volume['storageType']['keyName'] = 'NINJA_PENGUINS' exception = self.assertRaises(exceptions.SoftLayerError, storage_utils.prepare_modify_order_object, self.block, mock_volume, None, None, None) self.assertEqual("Volume does not have a valid storage type (with an appropriate " "keyName to indicate the volume is a PERFORMANCE or an ENDURANCE volume).", str(exception)) softlayer-python-5.4.2/tests/managers/ticket_tests.py000066400000000000000000000074711324365065500231110ustar00rootroot00000000000000""" SoftLayer.tests.managers.ticket_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import SoftLayer from SoftLayer import fixtures from SoftLayer import testing class TicketTests(testing.TestCase): def set_up(self): self.ticket = SoftLayer.TicketManager(self.client) def test_list_tickets(self): results = self.ticket.list_tickets() for result in results: self.assertIn(result['id'], [100, 101, 102]) self.assert_called_with('SoftLayer_Account', 'getTickets') def test_list_tickets_all(self): results = self.ticket.list_tickets(open_status=True, closed_status=True) for result in results: self.assertIn(result['id'], [100, 101, 102]) self.assert_called_with('SoftLayer_Account', 'getTickets') def test_list_tickets_open(self): results = self.ticket.list_tickets(open_status=True, closed_status=False) for result in results: self.assertIn(result['id'], [102]) self.assert_called_with('SoftLayer_Account', 'getOpenTickets') def test_list_tickets_closed(self): results = self.ticket.list_tickets(open_status=False, closed_status=True) for result in results: self.assertIn(result['id'], [100, 101]) self.assert_called_with('SoftLayer_Account', 'getClosedTickets') def test_list_subjects(self): list_expected_ids = [1001, 1002, 1003, 1004, 1005] results = self.ticket.list_subjects() for result in results: self.assertIn(result['id'], list_expected_ids) def test_get_instance(self): result = self.ticket.get_ticket(100) self.assertEqual(result, fixtures.SoftLayer_Ticket.getObject) self.assert_called_with('SoftLayer_Ticket', 'getObject', identifier=100) def test_create_ticket(self): self.ticket.create_ticket( title="Cloud Instance Cancellation - 08/01/13", body="body", subject=1004) args = ({"assignedUserId": 12345, "contents": "body", "subjectId": 1004, "title": "Cloud Instance Cancellation - 08/01/13"}, "body") self.assert_called_with('SoftLayer_Ticket', 'createStandardTicket', args=args) def test_update_ticket(self): # test a full update self.ticket.update_ticket(100, body='Update1') self.assert_called_with('SoftLayer_Ticket', 'addUpdate', args=({'entry': 'Update1'},), identifier=100) def test_attach_hardware(self): self.ticket.attach_hardware(100, 123) self.assert_called_with('SoftLayer_Ticket', 'addAttachedHardware', args=(123,), identifier=100) def test_attach_virtual_server(self): self.ticket.attach_virtual_server(100, 123) self.assert_called_with('SoftLayer_Ticket', 'addAttachedVirtualGuest', args=(123,), identifier=100) def test_detach_hardware(self): self.ticket.detach_hardware(100, 123) self.assert_called_with('SoftLayer_Ticket', 'removeAttachedHardware', args=(123,), identifier=100) def test_detach_virtual_server(self): self.ticket.detach_virtual_server(100, 123) self.assert_called_with('SoftLayer_Ticket', 'removeAttachedVirtualGuest', args=(123,), identifier=100) softlayer-python-5.4.2/tests/managers/vs_tests.py000066400000000000000000001010421324365065500222430ustar00rootroot00000000000000""" SoftLayer.tests.managers.vs_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import mock import SoftLayer from SoftLayer import exceptions from SoftLayer import fixtures from SoftLayer import testing class VSTests(testing.TestCase): def set_up(self): self.vs = SoftLayer.VSManager(self.client, SoftLayer.OrderingManager(self.client)) def test_list_instances(self): results = self.vs.list_instances(hourly=True, monthly=True) for result in results: self.assertIn(result['id'], [100, 104]) self.assert_called_with('SoftLayer_Account', 'getVirtualGuests') def test_list_instances_neither(self): results = self.vs.list_instances(hourly=False, monthly=False) for result in results: self.assertIn(result['id'], [100, 104]) self.assert_called_with('SoftLayer_Account', 'getVirtualGuests') def test_list_instances_monthly(self): results = self.vs.list_instances(hourly=False, monthly=True) for result in results: self.assertIn(result['id'], [100]) self.assert_called_with('SoftLayer_Account', 'getMonthlyVirtualGuests') def test_list_instances_hourly(self): results = self.vs.list_instances(hourly=True, monthly=False) for result in results: self.assertIn(result['id'], [104]) self.assert_called_with('SoftLayer_Account', 'getHourlyVirtualGuests') def test_list_instances_with_filters(self): self.vs.list_instances( hourly=True, monthly=True, tags=['tag1', 'tag2'], cpus=2, memory=1024, hostname='hostname', domain='example.com', local_disk=True, datacenter='dal05', nic_speed=100, public_ip='1.2.3.4', private_ip='4.3.2.1', ) _filter = { 'virtualGuests': { 'datacenter': { 'name': {'operation': '_= dal05'}}, 'domain': {'operation': '_= example.com'}, 'tagReferences': { 'tag': {'name': { 'operation': 'in', 'options': [{ 'name': 'data', 'value': ['tag1', 'tag2']}]}}}, 'maxCpu': {'operation': 2}, 'localDiskFlag': {'operation': True}, 'maxMemory': {'operation': 1024}, 'hostname': {'operation': '_= hostname'}, 'networkComponents': {'maxSpeed': {'operation': 100}}, 'primaryIpAddress': {'operation': '_= 1.2.3.4'}, 'primaryBackendIpAddress': {'operation': '_= 4.3.2.1'} } } self.assert_called_with('SoftLayer_Account', 'getVirtualGuests', filter=_filter) def test_resolve_ids_ip(self): _id = self.vs._get_ids_from_ip('172.16.240.2') self.assertEqual(_id, [100, 104]) def test_resolve_ids_ip_private(self): # Now simulate a private IP test mock = self.set_mock('SoftLayer_Account', 'getVirtualGuests') mock.side_effect = [[], [{'id': 99}]] _id = self.vs._get_ids_from_ip('10.0.1.87') self.assertEqual(_id, [99]) def test_resolve_ids_ip_invalid(self): _id = self.vs._get_ids_from_ip('nope') self.assertEqual(_id, []) def test_resolve_ids_hostname(self): _id = self.vs._get_ids_from_hostname('vs-test1') self.assertEqual(_id, [100, 104]) def test_get_instance(self): result = self.vs.get_instance(100) self.assertEqual(fixtures.SoftLayer_Virtual_Guest.getObject, result) self.assert_called_with('SoftLayer_Virtual_Guest', 'getObject', identifier=100) def test_get_create_options(self): results = self.vs.get_create_options() self.assertEqual( fixtures.SoftLayer_Virtual_Guest.getCreateObjectOptions, results) def test_cancel_instance(self): result = self.vs.cancel_instance(1) self.assertEqual(result, True) self.assert_called_with('SoftLayer_Virtual_Guest', 'deleteObject', identifier=1) def test_reload_instance(self): self.vs.reload_instance(1) self.assert_called_with('SoftLayer_Virtual_Guest', 'reloadOperatingSystem', args=('FORCE', {}), identifier=1) def test_reload_instance_posturi_sshkeys(self): post_uri = 'http://test.sftlyr.ws/test.sh' self.vs.reload_instance(1, post_uri=post_uri, ssh_keys=[1701]) args = ('FORCE', {'customProvisionScriptUri': post_uri, 'sshKeyIds': [1701]}) self.assert_called_with('SoftLayer_Virtual_Guest', 'reloadOperatingSystem', args=args, identifier=1) def test_reload_instance_with_new_os(self): self.vs.reload_instance(1, image_id=1234) args = ('FORCE', {'imageTemplateId': 1234}) self.assert_called_with('SoftLayer_Virtual_Guest', 'reloadOperatingSystem', args=args, identifier=1) @mock.patch('SoftLayer.managers.vs.VSManager._generate_create_dict') def test_create_verify(self, create_dict): create_dict.return_value = {'test': 1, 'verify': 1} self.vs.verify_create_instance(test=1, verify=1, tags=['test', 'tags']) create_dict.assert_called_once_with(test=1, verify=1) self.assert_called_with('SoftLayer_Virtual_Guest', 'generateOrderTemplate', args=({'test': 1, 'verify': 1},)) @mock.patch('SoftLayer.managers.vs.VSManager._generate_create_dict') def test_create_instance(self, create_dict): create_dict.return_value = {'test': 1, 'verify': 1} self.vs.create_instance(test=1, verify=1, tags='dev,green') create_dict.assert_called_once_with(test=1, verify=1) self.assert_called_with('SoftLayer_Virtual_Guest', 'createObject', args=({'test': 1, 'verify': 1},)) self.assert_called_with('SoftLayer_Virtual_Guest', 'setTags', args=('dev,green',), identifier=100) def test_create_instances(self): self.vs.create_instances([{'cpus': 1, 'memory': 1024, 'hostname': 'server', 'domain': 'example.com', 'tags': 'dev,green'}]) args = ([{'domain': 'example.com', 'hourlyBillingFlag': True, 'localDiskFlag': True, 'maxMemory': 1024, 'hostname': 'server', 'startCpus': 1, 'supplementalCreateObjectOptions': {'bootMode': None}}],) self.assert_called_with('SoftLayer_Virtual_Guest', 'createObjects', args=args) self.assert_called_with('SoftLayer_Virtual_Guest', 'setTags', args=('dev,green',), identifier=100) def test_generate_os_and_image(self): self.assertRaises( ValueError, self.vs._generate_create_dict, cpus=1, memory=1, hostname='test', domain='example.com', os_code=1, image_id=1, ) def test_generate_missing(self): self.assertRaises(ValueError, self.vs._generate_create_dict) self.assertRaises(ValueError, self.vs._generate_create_dict, cpus=1) def test_generate_basic(self): data = self.vs._generate_create_dict( cpus=1, memory=1, hostname='test', domain='example.com', os_code="STRING", ) assert_data = { 'startCpus': 1, 'maxMemory': 1, 'hostname': 'test', 'domain': 'example.com', 'localDiskFlag': True, 'operatingSystemReferenceCode': "STRING", 'hourlyBillingFlag': True, 'supplementalCreateObjectOptions': {'bootMode': None}, } self.assertEqual(data, assert_data) def test_generate_monthly(self): data = self.vs._generate_create_dict( cpus=1, memory=1, hostname='test', domain='example.com', os_code="STRING", hourly=False, ) assert_data = { 'hourlyBillingFlag': False, 'startCpus': 1, 'maxMemory': 1, 'hostname': 'test', 'domain': 'example.com', 'localDiskFlag': True, 'operatingSystemReferenceCode': "STRING", 'supplementalCreateObjectOptions': {'bootMode': None}, } self.assertEqual(data, assert_data) def test_generate_image_id(self): data = self.vs._generate_create_dict( cpus=1, memory=1, hostname='test', domain='example.com', image_id="45", ) assert_data = { 'startCpus': 1, 'maxMemory': 1, 'hostname': 'test', 'domain': 'example.com', 'localDiskFlag': True, 'blockDeviceTemplateGroup': {"globalIdentifier": "45"}, 'hourlyBillingFlag': True, 'supplementalCreateObjectOptions': {'bootMode': None}, } self.assertEqual(data, assert_data) def test_generate_dedicated(self): data = self.vs._generate_create_dict( cpus=1, memory=1, hostname='test', domain='example.com', os_code="STRING", dedicated=True, ) assert_data = { 'startCpus': 1, 'maxMemory': 1, 'hostname': 'test', 'domain': 'example.com', 'localDiskFlag': True, 'operatingSystemReferenceCode': "STRING", 'hourlyBillingFlag': True, 'dedicatedAccountHostOnlyFlag': True, 'supplementalCreateObjectOptions': {'bootMode': None}, } self.assertEqual(data, assert_data) def test_generate_datacenter(self): data = self.vs._generate_create_dict( cpus=1, memory=1, hostname='test', domain='example.com', os_code="STRING", datacenter="sng01", ) assert_data = { 'startCpus': 1, 'maxMemory': 1, 'hostname': 'test', 'domain': 'example.com', 'localDiskFlag': True, 'operatingSystemReferenceCode': "STRING", 'hourlyBillingFlag': True, 'datacenter': {"name": 'sng01'}, 'supplementalCreateObjectOptions': {'bootMode': None}, } self.assertEqual(data, assert_data) def test_generate_public_vlan(self): data = self.vs._generate_create_dict( cpus=1, memory=1, hostname='test', domain='example.com', os_code="STRING", public_vlan=1, ) assert_data = { 'startCpus': 1, 'maxMemory': 1, 'hostname': 'test', 'domain': 'example.com', 'localDiskFlag': True, 'operatingSystemReferenceCode': "STRING", 'hourlyBillingFlag': True, 'primaryNetworkComponent': {"networkVlan": {"id": 1}}, 'supplementalCreateObjectOptions': {'bootMode': None}, } self.assertEqual(data, assert_data) def test_generate_private_vlan(self): data = self.vs._generate_create_dict( cpus=1, memory=1, hostname='test', domain='example.com', os_code="STRING", private_vlan=1, ) assert_data = { 'startCpus': 1, 'maxMemory': 1, 'hostname': 'test', 'domain': 'example.com', 'localDiskFlag': True, 'operatingSystemReferenceCode': "STRING", 'hourlyBillingFlag': True, 'primaryBackendNetworkComponent': {"networkVlan": {"id": 1}}, 'supplementalCreateObjectOptions': {'bootMode': None}, } self.assertEqual(data, assert_data) def test_generate_userdata(self): data = self.vs._generate_create_dict( cpus=1, memory=1, hostname='test', domain='example.com', os_code="STRING", userdata="ICANHAZVSI", ) assert_data = { 'startCpus': 1, 'maxMemory': 1, 'hostname': 'test', 'domain': 'example.com', 'localDiskFlag': True, 'operatingSystemReferenceCode': "STRING", 'hourlyBillingFlag': True, 'userData': [{'value': "ICANHAZVSI"}], 'supplementalCreateObjectOptions': {'bootMode': None}, } self.assertEqual(data, assert_data) def test_generate_network(self): data = self.vs._generate_create_dict( cpus=1, memory=1, hostname='test', domain='example.com', os_code="STRING", nic_speed=9001, ) assert_data = { 'startCpus': 1, 'maxMemory': 1, 'hostname': 'test', 'domain': 'example.com', 'localDiskFlag': True, 'operatingSystemReferenceCode': "STRING", 'hourlyBillingFlag': True, 'networkComponents': [{'maxSpeed': 9001}], 'supplementalCreateObjectOptions': {'bootMode': None}, } self.assertEqual(data, assert_data) def test_generate_private_network_only(self): data = self.vs._generate_create_dict( cpus=1, memory=1, hostname='test', domain='example.com', os_code="STRING", nic_speed=9001, private=True ) assert_data = { 'startCpus': 1, 'maxMemory': 1, 'hostname': 'test', 'domain': 'example.com', 'localDiskFlag': True, 'operatingSystemReferenceCode': "STRING", 'privateNetworkOnlyFlag': True, 'hourlyBillingFlag': True, 'networkComponents': [{'maxSpeed': 9001}], 'supplementalCreateObjectOptions': {'bootMode': None}, } self.assertEqual(data, assert_data) def test_generate_post_uri(self): data = self.vs._generate_create_dict( cpus=1, memory=1, hostname='test', domain='example.com', os_code="STRING", post_uri='https://example.com/boostrap.sh', ) assert_data = { 'startCpus': 1, 'maxMemory': 1, 'hostname': 'test', 'domain': 'example.com', 'localDiskFlag': True, 'operatingSystemReferenceCode': "STRING", 'hourlyBillingFlag': True, 'postInstallScriptUri': 'https://example.com/boostrap.sh', 'supplementalCreateObjectOptions': {'bootMode': None}, } self.assertEqual(data, assert_data) def test_generate_sshkey(self): data = self.vs._generate_create_dict( cpus=1, memory=1, hostname='test', domain='example.com', os_code="STRING", ssh_keys=[543], ) assert_data = { 'startCpus': 1, 'maxMemory': 1, 'hostname': 'test', 'domain': 'example.com', 'localDiskFlag': True, 'operatingSystemReferenceCode': "STRING", 'hourlyBillingFlag': True, 'sshKeys': [{'id': 543}], 'supplementalCreateObjectOptions': {'bootMode': None}, } self.assertEqual(data, assert_data) def test_generate_no_disks(self): data = self.vs._generate_create_dict( cpus=1, memory=1, hostname='test', domain='example.com', os_code="STRING" ) self.assertEqual(data.get('blockDevices'), None) def test_generate_single_disk(self): data = self.vs._generate_create_dict( cpus=1, memory=1, hostname='test', domain='example.com', os_code="STRING", disks=[50] ) assert_data = { 'blockDevices': [ {"device": "0", "diskImage": {"capacity": 50}}] } self.assertTrue(data.get('blockDevices')) self.assertEqual(data['blockDevices'], assert_data['blockDevices']) def test_generate_multi_disk(self): data = self.vs._generate_create_dict( cpus=1, memory=1, hostname='test', domain='example.com', os_code="STRING", disks=[50, 70, 100] ) assert_data = { 'blockDevices': [ {"device": "0", "diskImage": {"capacity": 50}}, {"device": "2", "diskImage": {"capacity": 70}}, {"device": "3", "diskImage": {"capacity": 100}}] } self.assertTrue(data.get('blockDevices')) self.assertEqual(data['blockDevices'], assert_data['blockDevices']) def test_generate_boot_mode(self): data = self.vs._generate_create_dict( cpus=1, memory=1, hostname='test', domain='example.com', os_code="STRING", boot_mode="HVM" ) assert_data = { 'startCpus': 1, 'maxMemory': 1, 'hostname': 'test', 'domain': 'example.com', 'localDiskFlag': True, 'operatingSystemReferenceCode': "STRING", 'hourlyBillingFlag': True, 'supplementalCreateObjectOptions': {'bootMode': 'HVM'}, } self.assertEqual(data, assert_data) def test_change_port_speed_public(self): result = self.vs.change_port_speed(1, True, 100) self.assertEqual(result, True) self.assert_called_with('SoftLayer_Virtual_Guest', 'setPublicNetworkInterfaceSpeed', identifier=1, args=(100,)) def test_change_port_speed_private(self): result = self.vs.change_port_speed(2, False, 10) self.assertEqual(result, True) self.assert_called_with('SoftLayer_Virtual_Guest', 'setPrivateNetworkInterfaceSpeed', identifier=2, args=(10,)) def test_rescue(self): # Test rescue environment result = self.vs.rescue(1234) self.assertEqual(result, True) self.assert_called_with('SoftLayer_Virtual_Guest', 'executeRescueLayer', identifier=1234) def test_edit_metadata(self): # Test editing user data result = self.vs.edit(100, userdata='my data') self.assertEqual(result, True) self.assert_called_with('SoftLayer_Virtual_Guest', 'setUserMetadata', identifier=100, args=(['my data'],)) def test_edit_blank(self): # Now test a blank edit self.assertTrue(self.vs.edit, 100) def test_edit_full(self): result = self.vs.edit(100, hostname='new-host', domain='new.sftlyr.ws', notes='random notes') self.assertEqual(result, True) args = ({ 'hostname': 'new-host', 'domain': 'new.sftlyr.ws', 'notes': 'random notes', },) self.assert_called_with('SoftLayer_Virtual_Guest', 'editObject', identifier=100, args=args) def test_edit_tags(self): # Test tag support result = self.vs.edit(100, tags='dev,green') self.assertEqual(result, True) self.assert_called_with('SoftLayer_Virtual_Guest', 'setTags', identifier=100, args=('dev,green',)) def test_edit_tags_blank(self): result = self.vs.edit(100, tags='') self.assertEqual(result, True) self.assert_called_with('SoftLayer_Virtual_Guest', 'setTags', identifier=100, args=('',)) def test_captures(self): # capture only the OS disk result = self.vs.capture(1, 'a') expected = fixtures.SoftLayer_Virtual_Guest.createArchiveTransaction self.assertEqual(result, expected) args = ('a', [{'device': 0, 'uuid': 1, 'mountType': 'Disk'}], None) self.assert_called_with('SoftLayer_Virtual_Guest', 'createArchiveTransaction', args=args, identifier=1) def test_capture_additional_disks(self): # capture all the disks, minus the swap # make sure the data is carried along with it result = self.vs.capture(1, 'a', additional_disks=True) expected = fixtures.SoftLayer_Virtual_Guest.createArchiveTransaction self.assertEqual(result, expected) args = ('a', [{'device': 0, 'mountType': 'Disk', 'uuid': 1}, {'device': 3, 'mountType': 'Disk', 'uuid': 3}], None) self.assert_called_with('SoftLayer_Virtual_Guest', 'createArchiveTransaction', args=args, identifier=1) def test_upgrade(self): # test single upgrade result = self.vs.upgrade(1, cpus=4, public=False) self.assertEqual(result, True) self.assert_called_with('SoftLayer_Product_Order', 'placeOrder') call = self.calls('SoftLayer_Product_Order', 'placeOrder')[0] order_container = call.args[0] self.assertEqual(order_container['prices'], [{'id': 1007}]) self.assertEqual(order_container['virtualGuests'], [{'id': 1}]) def test_upgrade_blank(self): # Now test a blank upgrade result = self.vs.upgrade(1) self.assertEqual(result, False) self.assertEqual(self.calls('SoftLayer_Product_Order', 'placeOrder'), []) def test_upgrade_full(self): # Testing all parameters Upgrade result = self.vs.upgrade(1, cpus=4, memory=2, nic_speed=1000, public=True) self.assertEqual(result, True) self.assert_called_with('SoftLayer_Product_Order', 'placeOrder') call = self.calls('SoftLayer_Product_Order', 'placeOrder')[0] order_container = call.args[0] self.assertIn({'id': 1144}, order_container['prices']) self.assertIn({'id': 1133}, order_container['prices']) self.assertIn({'id': 1122}, order_container['prices']) self.assertEqual(order_container['virtualGuests'], [{'id': 1}]) def test_upgrade_dedicated_host_instance(self): mock = self.set_mock('SoftLayer_Virtual_Guest', 'getUpgradeItemPrices') mock.return_value = fixtures.SoftLayer_Virtual_Guest.DEDICATED_GET_UPGRADE_ITEM_PRICES # test single upgrade result = self.vs.upgrade(1, cpus=4, public=False) self.assertEqual(result, True) self.assert_called_with('SoftLayer_Product_Order', 'placeOrder') call = self.calls('SoftLayer_Product_Order', 'placeOrder')[0] order_container = call.args[0] self.assertEqual(order_container['prices'], [{'id': 115566}]) self.assertEqual(order_container['virtualGuests'], [{'id': 1}]) def test_get_item_id_for_upgrade(self): item_id = 0 package_items = self.client['Product_Package'].getItems(id=46) for item in package_items: if ((item['prices'][0]['categories'][0]['id'] == 3) and (item.get('capacity') == '2')): item_id = item['prices'][0]['id'] break self.assertEqual(1133, item_id) def test_get_package_items(self): self.vs._get_package_items() self.assert_called_with('SoftLayer_Product_Package', 'getItems') def test_get_price_id_for_upgrade(self): package_items = self.vs._get_package_items() price_id = self.vs._get_price_id_for_upgrade(package_items=package_items, option='cpus', value='4') self.assertEqual(1144, price_id) def test_get_price_id_for_upgrade_skips_location_price(self): package_items = self.vs._get_package_items() price_id = self.vs._get_price_id_for_upgrade(package_items=package_items, option='cpus', value='55') self.assertEqual(None, price_id) def test_get_price_id_for_upgrade_finds_nic_price(self): package_items = self.vs._get_package_items() price_id = self.vs._get_price_id_for_upgrade(package_items=package_items, option='memory', value='2') self.assertEqual(1133, price_id) def test_get_price_id_for_upgrade_finds_memory_price(self): package_items = self.vs._get_package_items() price_id = self.vs._get_price_id_for_upgrade(package_items=package_items, option='nic_speed', value='1000') self.assertEqual(1122, price_id) class VSWaitReadyGoTests(testing.TestCase): def set_up(self): self.client = mock.MagicMock() self.vs = SoftLayer.VSManager(self.client) self.guestObject = self.client['Virtual_Guest'].getObject @mock.patch('SoftLayer.managers.vs.VSManager.wait_for_ready') def test_wait_interface(self, ready): # verify interface to wait_for_ready is intact self.vs.wait_for_transaction(1, 1) ready.assert_called_once_with(1, 1, delay=10, pending=True) def test_active_not_provisioned(self): # active transaction and no provision date should be false self.guestObject.return_value = {'activeTransaction': {'id': 1}} value = self.vs.wait_for_ready(1, 0) self.assertFalse(value) def test_active_and_provisiondate(self): # active transaction and provision date should be True self.guestObject.side_effect = [ {'activeTransaction': {'id': 1}, 'provisionDate': 'aaa'}, ] value = self.vs.wait_for_ready(1, 1) self.assertTrue(value) @mock.patch('time.sleep') @mock.patch('time.time') def test_active_provision_pending(self, _now, _sleep): _now.side_effect = [0, 0, 1, 1, 2, 2] # active transaction and provision date # and pending should be false self.guestObject.return_value = {'activeTransaction': {'id': 2}, 'provisionDate': 'aaa'} value = self.vs.wait_for_ready(instance_id=1, limit=1, delay=1, pending=True) _sleep.assert_has_calls([mock.call(0)]) self.assertFalse(value) def test_active_reload(self): # actively running reload self.guestObject.side_effect = [ {'activeTransaction': {'id': 1}}, { 'activeTransaction': {'id': 1}, 'provisionDate': 'aaa', 'lastOperatingSystemReload': {'id': 1}, }, ] value = self.vs.wait_for_ready(1, 0) self.assertFalse(value) def test_reload_no_pending(self): # reload complete, maintance transactions self.guestObject.return_value = { 'activeTransaction': {'id': 2}, 'provisionDate': 'aaa', 'lastOperatingSystemReload': {'id': 1}, } value = self.vs.wait_for_ready(1, 1) self.assertTrue(value) @mock.patch('time.sleep') @mock.patch('time.time') def test_reload_pending(self, _now, _sleep): _now.side_effect = [0, 0, 1, 1, 2, 2] # reload complete, pending maintance transactions self.guestObject.return_value = {'activeTransaction': {'id': 2}, 'provisionDate': 'aaa', 'lastOperatingSystemReload': {'id': 1}} value = self.vs.wait_for_ready(instance_id=1, limit=1, delay=1, pending=True) _sleep.assert_has_calls([mock.call(0)]) self.assertFalse(value) @mock.patch('time.sleep') def test_ready_iter_once_incomplete(self, _sleep): # no iteration, false self.guestObject.return_value = {'activeTransaction': {'id': 1}} value = self.vs.wait_for_ready(1, 0, delay=1) self.assertFalse(value) _sleep.assert_has_calls([mock.call(0)]) @mock.patch('time.sleep') def test_iter_once_complete(self, _sleep): # no iteration, true self.guestObject.return_value = {'provisionDate': 'aaa'} value = self.vs.wait_for_ready(1, 1, delay=1) self.assertTrue(value) self.assertFalse(_sleep.called) @mock.patch('time.sleep') def test_iter_four_complete(self, _sleep): # test 4 iterations with positive match self.guestObject.side_effect = [ {'activeTransaction': {'id': 1}}, {'activeTransaction': {'id': 1}}, {'activeTransaction': {'id': 1}}, {'provisionDate': 'aaa'}, ] value = self.vs.wait_for_ready(1, 4, delay=1) self.assertTrue(value) _sleep.assert_has_calls([mock.call(1), mock.call(1), mock.call(1)]) self.guestObject.assert_has_calls([ mock.call(id=1, mask=mock.ANY), mock.call(id=1, mask=mock.ANY), mock.call(id=1, mask=mock.ANY), mock.call(id=1, mask=mock.ANY), ]) @mock.patch('time.time') @mock.patch('time.sleep') def test_iter_two_incomplete(self, _sleep, _time): # test 2 iterations, with no matches self.guestObject.side_effect = [ {'activeTransaction': {'id': 1}}, {'activeTransaction': {'id': 1}}, {'activeTransaction': {'id': 1}}, {'provisionDate': 'aaa'} ] # logging calls time.time as of pytest3.3, not sure if there is a better way of getting around that. _time.side_effect = [0, 1, 2, 3, 4, 5, 6] value = self.vs.wait_for_ready(1, 2, delay=1) self.assertFalse(value) _sleep.assert_has_calls([mock.call(1), mock.call(0)]) self.guestObject.assert_has_calls([ mock.call(id=1, mask=mock.ANY), mock.call(id=1, mask=mock.ANY), ]) @mock.patch('time.time') @mock.patch('time.sleep') def test_iter_20_incomplete(self, _sleep, _time): """Wait for up to 20 seconds (sleeping for 10 seconds) for a server.""" self.guestObject.return_value = {'activeTransaction': {'id': 1}} # logging calls time.time as of pytest3.3, not sure if there is a better way of getting around that. _time.side_effect = [0, 0, 10, 10, 20, 20, 50, 60] value = self.vs.wait_for_ready(1, 20, delay=10) self.assertFalse(value) self.guestObject.assert_has_calls([mock.call(id=1, mask=mock.ANY)]) _sleep.assert_has_calls([mock.call(10)]) @mock.patch('SoftLayer.decoration.sleep') @mock.patch('SoftLayer.transports.FixtureTransport.__call__') @mock.patch('time.time') @mock.patch('time.sleep') def test_exception_from_api(self, _sleep, _time, _vs, _dsleep): """Tests escalating scale back when an excaption is thrown""" _dsleep.return_value = False self.guestObject.side_effect = [ exceptions.TransportError(104, "Its broken"), {'activeTransaction': {'id': 1}}, {'provisionDate': 'aaa'} ] # logging calls time.time as of pytest3.3, not sure if there is a better way of getting around that. _time.side_effect = [0, 1, 2, 3, 4] value = self.vs.wait_for_ready(1, 20, delay=1) _sleep.assert_called_once() _dsleep.assert_called_once() self.assertTrue(value) softlayer-python-5.4.2/tests/resources/000077500000000000000000000000001324365065500202365ustar00rootroot00000000000000softlayer-python-5.4.2/tests/resources/attachment_upload000066400000000000000000000000241324365065500236510ustar00rootroot00000000000000ticket attached datasoftlayer-python-5.4.2/tests/transport_tests.py000066400000000000000000000435761324365065500220730ustar00rootroot00000000000000""" SoftLayer.tests.transport_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import io import warnings import mock import pytest import requests import six import SoftLayer from SoftLayer import consts from SoftLayer import testing from SoftLayer import transports def get_xmlrpc_response(): response = requests.Response() list_body = six.b(''' ''') response.raw = io.BytesIO(list_body) response.headers['SoftLayer-Total-Items'] = 10 response.status_code = 200 return response class TestXmlRpcAPICall(testing.TestCase): def set_up(self): self.transport = transports.XmlRpcTransport( endpoint_url='http://something.com', ) self.response = get_xmlrpc_response() @mock.patch('SoftLayer.transports.requests.Session.request') def test_call(self, request): request.return_value = self.response data = ''' getObject headers ''' req = transports.Request() req.service = 'SoftLayer_Service' req.method = 'getObject' resp = self.transport(req) request.assert_called_with('POST', 'http://something.com/SoftLayer_Service', headers={'Content-Type': 'application/xml', 'User-Agent': consts.USER_AGENT}, proxies=None, data=data, timeout=None, cert=None, verify=True) self.assertEqual(resp, []) self.assertIsInstance(resp, transports.SoftLayerListResult) self.assertEqual(resp.total_count, 10) def test_proxy_without_protocol(self): req = transports.Request() req.service = 'SoftLayer_Service' req.method = 'Resource' req.proxy = 'localhost:3128' try: self.assertRaises(SoftLayer.TransportError, self.transport, req) except AssertionError: warnings.warn("Incorrect Exception raised. Expected a " "SoftLayer.TransportError error") @mock.patch('SoftLayer.transports.requests.Session.request') def test_valid_proxy(self, request): request.return_value = self.response self.transport.proxy = 'http://localhost:3128' req = transports.Request() req.service = 'SoftLayer_Service' req.method = 'Resource' self.transport(req) request.assert_called_with( 'POST', mock.ANY, proxies={'https': 'http://localhost:3128', 'http': 'http://localhost:3128'}, data=mock.ANY, headers=mock.ANY, timeout=None, cert=None, verify=True) @mock.patch('SoftLayer.transports.requests.Session.request') def test_identifier(self, request): request.return_value = self.response req = transports.Request() req.endpoint = "http://something.com" req.service = "SoftLayer_Service" req.method = "getObject" req.identifier = 1234 self.transport(req) _, kwargs = request.call_args self.assertIn( """ id 1234 """, kwargs['data']) @mock.patch('SoftLayer.transports.requests.Session.request') def test_filter(self, request): request.return_value = self.response req = transports.Request() req.endpoint = "http://something.com" req.service = "SoftLayer_Service" req.method = "getObject" req.filter = {'TYPE': {'attribute': {'operation': '^= prefix'}}} self.transport(req) args, kwargs = request.call_args self.assertIn( """ operation ^= prefix """, kwargs['data']) @mock.patch('SoftLayer.transports.requests.Session.request') def test_limit_offset(self, request): request.return_value = self.response req = transports.Request() req.endpoint = "http://something.com" req.service = "SoftLayer_Service" req.method = "getObject" req.limit = 10 self.transport(req) args, kwargs = request.call_args self.assertIn(""" resultLimit """, kwargs['data']) self.assertIn("""limit 10 """, kwargs['data']) @mock.patch('SoftLayer.transports.requests.Session.request') def test_old_mask(self, request): request.return_value = self.response req = transports.Request() req.endpoint = "http://something.com" req.service = "SoftLayer_Service" req.method = "getObject" req.mask = {"something": "nested"} self.transport(req) args, kwargs = request.call_args self.assertIn(""" mask something nested """, kwargs['data']) @mock.patch('SoftLayer.transports.requests.Session.request') def test_mask_call_no_mask_prefix(self, request): request.return_value = self.response req = transports.Request() req.endpoint = "http://something.com" req.service = "SoftLayer_Service" req.method = "getObject" req.mask = "something.nested" self.transport(req) args, kwargs = request.call_args self.assertIn( "mask[something.nested]", kwargs['data']) @mock.patch('SoftLayer.transports.requests.Session.request') def test_mask_call_v2(self, request): request.return_value = self.response req = transports.Request() req.endpoint = "http://something.com" req.service = "SoftLayer_Service" req.method = "getObject" req.mask = "mask[something[nested]]" self.transport(req) args, kwargs = request.call_args self.assertIn( "mask[something[nested]]", kwargs['data']) @mock.patch('SoftLayer.transports.requests.Session.request') def test_mask_call_v2_dot(self, request): request.return_value = self.response req = transports.Request() req.endpoint = "http://something.com" req.service = "SoftLayer_Service" req.method = "getObject" req.mask = "mask.something.nested" self.transport(req) args, kwargs = request.call_args self.assertIn("mask.something.nested", kwargs['data']) @mock.patch('SoftLayer.transports.requests.Session.request') def test_request_exception(self, request): # Test Text Error e = requests.HTTPError('error') e.response = mock.MagicMock() e.response.status_code = 404 e.response.content = 'Error Code' request().raise_for_status.side_effect = e req = transports.Request() req.service = 'SoftLayer_Service' req.method = 'getObject' self.assertRaises(SoftLayer.TransportError, self.transport, req) @mock.patch('SoftLayer.transports.requests.Session.request') @pytest.mark.parametrize( "transport_verify,request_verify,expected", [ (True, True, True), (True, False, False), (True, None, True), (False, True, True), (False, False, False), (False, None, False), (None, True, True), (None, False, False), (None, None, True), ] ) def test_verify(request, transport_verify, request_verify, expected): request.return_value = get_xmlrpc_response() transport = transports.XmlRpcTransport( endpoint_url='http://something.com', ) req = transports.Request() req.service = 'SoftLayer_Service' req.method = 'getObject' if request_verify is not None: req.verify = request_verify if transport_verify is not None: transport.verify = transport_verify transport(req) request.assert_called_with('POST', 'http://something.com/SoftLayer_Service', data=mock.ANY, headers=mock.ANY, cert=mock.ANY, proxies=mock.ANY, timeout=mock.ANY, verify=expected) class TestRestAPICall(testing.TestCase): def set_up(self): self.transport = transports.RestTransport( endpoint_url='http://something.com', ) @mock.patch('SoftLayer.transports.requests.Session.request') def test_basic(self, request): request().content = '[]' request().text = '[]' request().headers = requests.structures.CaseInsensitiveDict({ 'SoftLayer-Total-Items': '10', }) req = transports.Request() req.service = 'SoftLayer_Service' req.method = 'Resource' resp = self.transport(req) self.assertEqual(resp, []) self.assertIsInstance(resp, transports.SoftLayerListResult) self.assertEqual(resp.total_count, 10) request.assert_called_with( 'GET', 'http://something.com/SoftLayer_Service/Resource.json', headers=mock.ANY, auth=None, data=None, params={}, verify=True, cert=None, proxies=None, timeout=None) @mock.patch('SoftLayer.transports.requests.Session.request') def test_error(self, request): # Test JSON Error e = requests.HTTPError('error') e.response = mock.MagicMock() e.response.status_code = 404 e.response.text = '''{ "error": "description", "code": "Error Code" }''' request().raise_for_status.side_effect = e req = transports.Request() req.service = 'SoftLayer_Service' req.method = 'Resource' self.assertRaises(SoftLayer.SoftLayerAPIError, self.transport, req) def test_proxy_without_protocol(self): req = transports.Request() req.service = 'SoftLayer_Service' req.method = 'Resource' req.proxy = 'localhost:3128' try: self.assertRaises(SoftLayer.TransportError, self.transport, req) except AssertionError: warnings.warn("AssertionError raised instead of a SoftLayer.TransportError error") @mock.patch('SoftLayer.transports.requests.Session.request') def test_valid_proxy(self, request): request().text = '{}' self.transport.proxy = 'http://localhost:3128' req = transports.Request() req.service = 'SoftLayer_Service' req.method = 'Resource' self.transport(req) request.assert_called_with( 'GET', 'http://something.com/SoftLayer_Service/Resource.json', proxies={'https': 'http://localhost:3128', 'http': 'http://localhost:3128'}, auth=None, data=None, params={}, verify=True, cert=None, timeout=mock.ANY, headers=mock.ANY) @mock.patch('SoftLayer.transports.requests.Session.request') def test_with_id(self, request): request().text = '{}' req = transports.Request() req.service = 'SoftLayer_Service' req.method = 'getObject' req.identifier = 2 resp = self.transport(req) self.assertEqual(resp, {}) request.assert_called_with( 'GET', 'http://something.com/SoftLayer_Service/2/getObject.json', headers=mock.ANY, auth=None, data=None, params={}, verify=True, cert=None, proxies=None, timeout=None) @mock.patch('SoftLayer.transports.requests.Session.request') def test_with_args(self, request): request().text = '{}' req = transports.Request() req.service = 'SoftLayer_Service' req.method = 'getObject' req.args = ('test', 1) resp = self.transport(req) self.assertEqual(resp, {}) request.assert_called_with( 'POST', 'http://something.com/SoftLayer_Service/getObject.json', headers=mock.ANY, auth=None, data='{"parameters": ["test", 1]}', params={}, verify=True, cert=None, proxies=None, timeout=None) @mock.patch('SoftLayer.transports.requests.Session.request') def test_with_filter(self, request): request().text = '{}' req = transports.Request() req.service = 'SoftLayer_Service' req.method = 'getObject' req.filter = {'TYPE': {'attribute': {'operation': '^= prefix'}}} resp = self.transport(req) self.assertEqual(resp, {}) request.assert_called_with( 'GET', 'http://something.com/SoftLayer_Service/getObject.json', params={'objectFilter': '{"TYPE": {"attribute": {"operation": "^= prefix"}}}'}, headers=mock.ANY, auth=None, data=None, verify=True, cert=None, proxies=None, timeout=None) @mock.patch('SoftLayer.transports.requests.Session.request') def test_with_mask(self, request): request().text = '{}' req = transports.Request() req.service = 'SoftLayer_Service' req.method = 'getObject' req.mask = 'id,property' resp = self.transport(req) self.assertEqual(resp, {}) request.assert_called_with( 'GET', 'http://something.com/SoftLayer_Service/getObject.json', params={'objectMask': 'mask[id,property]'}, headers=mock.ANY, auth=None, data=None, verify=True, cert=None, proxies=None, timeout=None) # Now test with mask[] prefix req = transports.Request() req.service = 'SoftLayer_Service' req.method = 'getObject' req.mask = 'mask[id,property]' resp = self.transport(req) self.assertEqual(resp, {}) request.assert_called_with( 'GET', 'http://something.com/SoftLayer_Service/getObject.json', params={'objectMask': 'mask[id,property]'}, headers=mock.ANY, auth=None, data=None, verify=True, cert=None, proxies=None, timeout=None) @mock.patch('SoftLayer.transports.requests.Session.request') def test_with_limit_offset(self, request): request().text = '{}' req = transports.Request() req.service = 'SoftLayer_Service' req.method = 'getObject' req.identifier = 2 req.limit = 10 req.offset = 5 resp = self.transport(req) self.assertEqual(resp, {}) request.assert_called_with( 'GET', 'http://something.com/SoftLayer_Service/2/getObject.json', headers=mock.ANY, auth=None, data=None, params={'limit': 10, 'offset': 5}, verify=True, cert=None, proxies=None, timeout=None) @mock.patch('SoftLayer.transports.requests.Session.request') def test_unknown_error(self, request): e = requests.RequestException('error') e.response = mock.MagicMock() e.response.status_code = 404 e.response.content = 'Error Code' request().raise_for_status.side_effect = e req = transports.Request() req.service = 'SoftLayer_Service' req.method = 'getObject' self.assertRaises(SoftLayer.TransportError, self.transport, req) @mock.patch('SoftLayer.transports.requests.Session.request') @mock.patch('requests.auth.HTTPBasicAuth') def test_with_special_auth(self, auth, request): request().text = '{}' user = 'asdf' password = 'zxcv' req = transports.Request() req.service = 'SoftLayer_Service' req.method = 'getObject' req.identifier = 2 req.transport_user = user req.transport_password = password resp = self.transport(req) self.assertEqual(resp, {}) auth.assert_called_with(user, password) request.assert_called_with( 'GET', 'http://something.com/SoftLayer_Service/2/getObject.json', headers=mock.ANY, auth=mock.ANY, data=None, params={}, verify=True, cert=None, proxies=None, timeout=None) class TestFixtureTransport(testing.TestCase): def set_up(self): self.transport = transports.FixtureTransport() def test_basic(self): req = transports.Request() req.service = 'SoftLayer_Account' req.method = 'getObject' resp = self.transport(req) self.assertEqual(resp['accountId'], 1234) def test_no_module(self): req = transports.Request() req.service = 'Doesnt_Exist' req.method = 'getObject' self.assertRaises(NotImplementedError, self.transport, req) def test_no_method(self): req = transports.Request() req.service = 'SoftLayer_Account' req.method = 'getObjectzzzz' self.assertRaises(NotImplementedError, self.transport, req) softlayer-python-5.4.2/tools/000077500000000000000000000000001324365065500162225ustar00rootroot00000000000000softlayer-python-5.4.2/tools/requirements.txt000066400000000000000000000001271324365065500215060ustar00rootroot00000000000000requests >= 2.18.4 click >= 5 prettytable >= 0.7.0 six >= 1.7.0 prompt_toolkit urllib3 softlayer-python-5.4.2/tools/test-requirements.txt000066400000000000000000000001071324365065500224610ustar00rootroot00000000000000tox pytest pytest-cov mock sphinx testtools urllib3 requests >= 2.18.4 softlayer-python-5.4.2/tox.ini000066400000000000000000000026101324365065500163740ustar00rootroot00000000000000[tox] envlist = py27,py35,py36,pypy,analysis,coverage [flake8] max-line-length=120 [testenv] deps = -r{toxinidir}/tools/test-requirements.txt commands = py.test {posargs:tests} [testenv:coverage] commands = py.test {posargs:tests} \ --cov=SoftLayer \ --cov-fail-under=77 \ --cov-report=html \ --cov-report=term-missing [testenv:analysis] deps = -r{toxinidir}/tools/test-requirements.txt hacking pylint commands = flake8 SoftLayer tests # redefined-variable-type - This prevents polymorphism pylint SoftLayer \ -r n \ --ignore=tests,fixtures \ -d too-many-locals \ -d star-args \ -d redefined-variable-type \ -d locally-disabled \ -d no-else-return \ -d len-as-condition \ --max-args=25 \ --max-branches=20 \ --max-statements=65 \ --min-public-methods=0 \ --max-public-methods=35 \ --min-similarity-lines=30 \ --max-line-length=120 # invalid-name - Fixtures don't follow proper naming conventions # missing-docstring - Fixtures don't have docstrings pylint SoftLayer/fixtures \ -d invalid-name \ -d missing-docstring \ --max-module-lines=2000 \ --min-similarity-lines=50 \ --max-line-length=120 \ -r n