pax_global_header00006660000000000000000000000064141537645770014535gustar00rootroot0000000000000052 comment=cdef7d63c66413197a9a97b0414de9f95887a82a softlayer-python-5.9.8/000077500000000000000000000000001415376457700151075ustar00rootroot00000000000000softlayer-python-5.9.8/.github/000077500000000000000000000000001415376457700164475ustar00rootroot00000000000000softlayer-python-5.9.8/.github/ISSUE_TEMPLATE/000077500000000000000000000000001415376457700206325ustar00rootroot00000000000000softlayer-python-5.9.8/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000011451415376457700233250ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve title: '' labels: Bug assignees: '' --- > Reminder: No username or APIkeys should be added to these issues, as they are public. **Describe the bug** A clear and concise description of what the bug is. Include the command you used, make sure to include the `-v` flag, as that information is very helpful. Ex: `slcli -v vs list` **Expected behavior** A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. **Version** Include the output of `slcli --version` softlayer-python-5.9.8/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000012511415376457700243560ustar00rootroot00000000000000--- name: Feature request about: Suggest an idea for this project title: '' labels: New Feature assignees: '' --- > REMINDER: Never add usernames or apikeys in these issues, as they are public. **What are you trying to do?** A brief explanation of what you are trying to do. Could be something simple like `slcli vs list` doesn't support a filter you need. Or more complex like recreating some functionality that exists in the cloud.ibm.com portal **Screen shots** If the functionality you want exists in the portal, please add a screenshot so we have a better idea of what you need. **Additional context** Add any other context or screenshots about the feature request here. softlayer-python-5.9.8/.github/workflows/000077500000000000000000000000001415376457700205045ustar00rootroot00000000000000softlayer-python-5.9.8/.github/workflows/codeql-analysis.yml000066400000000000000000000046171415376457700243270ustar00rootroot00000000000000# For most projects, this workflow file will not need changing; you simply need # to commit it to your repository. # # You may wish to alter this file to override the set of languages analyzed, # or to provide custom queries or build logic. # # ******** NOTE ******** # We have attempted to detect the languages in your repository. Please check # the `language` matrix defined below to confirm you have the correct set of # supported CodeQL languages. # name: "CodeQL" on: push: branches: [ master ] pull_request: # The branches below must be a subset of the branches above branches: [ master ] schedule: - cron: '41 6 * * 5' jobs: analyze: name: Analyze runs-on: ubuntu-latest permissions: actions: read contents: read security-events: write strategy: fail-fast: false matrix: language: [ 'python' ] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] # Learn more: # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed steps: - name: Checkout repository uses: actions/checkout@v2 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v1 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. # queries: ./path/to/local/query, your-org/your-repo/queries@main # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild uses: github/codeql-action/autobuild@v1 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines # and modify them (or add more) to build your code if your project # uses a compiled language #- run: | # make bootstrap # make release - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v1 softlayer-python-5.9.8/.github/workflows/documentation.yml000066400000000000000000000012001415376457700240710ustar00rootroot00000000000000name: documentation on: push: branches: [ master ] pull_request: branches: [ master ] jobs: build: runs-on: ubuntu-latest strategy: matrix: python-version: [3.8] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r tools/test-requirements.txt - name: Documentation Checks run: | python docCheck.py softlayer-python-5.9.8/.github/workflows/release.yml000066400000000000000000000026021415376457700226470ustar00rootroot00000000000000name: Release Snapcraft and PyPi (Testing) on: release: types: [published] jobs: snap-release: runs-on: ubuntu-18.04 strategy: matrix: arch: ['armhf','amd64','arm64','ppc64el','s390x','i386'] steps: - name: Install Snapcraft uses: samuelmeuli/action-snapcraft@v1.2.0 with: snapcraft_token: ${{ secrets.snapcraft_token }} - name: Push to stable run: | VERSION=`snapcraft list-revisions slcli --arch ${{ matrix.arch }} | grep "edge\*" | awk '{print $1}'` echo Publishing $VERSION on ${{ matrix.arch }} snapcraft release slcli $VERSION stable build-n-publish: name: Build and publish Python 🐍 distributions 📦 to PyPI and TestPyPI runs-on: ubuntu-latest steps: - uses: actions/checkout@master - name: Set up Python 3.8 uses: actions/setup-python@v2 with: python-version: 3.8 - name: Install pypa/build run: >- python -m pip install build --user - name: Build a binary wheel and a source tarball run: >- python -m build --sdist --wheel --outdir dist/ . - name: Publish 📦 to Test PyPI uses: pypa/gh-action-pypi-publish@master with: password: ${{ secrets.CGALLO_TEST_PYPI }} repository_url: https://test.pypi.org/legacy/ softlayer-python-5.9.8/.github/workflows/test_pypi_release.yml000066400000000000000000000017341415376457700247540ustar00rootroot00000000000000# https://packaging.python.org/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/ name: Publish 📦 to TestPyPI on: push: branches: [test-pypi ] jobs: build-n-publish: name: Build and publish Python 🐍 distributions 📦 to PyPI and TestPyPI runs-on: ubuntu-latest steps: - uses: actions/checkout@master - name: Set up Python 3.8 uses: actions/setup-python@v2 with: python-version: 3.8 - name: Install pypa/build run: >- python -m pip install build --user - name: Build a binary wheel and a source tarball run: >- python -m build --sdist --wheel --outdir dist/ . - name: Publish 📦 to Test PyPI uses: pypa/gh-action-pypi-publish@master with: password: ${{ secrets.CGALLO_TEST_PYPI }} repository_url: https://test.pypi.org/legacy/softlayer-python-5.9.8/.github/workflows/tests.yml000066400000000000000000000024731415376457700223770ustar00rootroot00000000000000name: Tests on: push: branches: [ master ] pull_request: branches: [ master ] jobs: test: runs-on: ubuntu-latest strategy: matrix: python-version: [3.6,3.7,3.8,3.9] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r tools/test-requirements.txt - name: Tox Test run: tox -e py coverage: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set up Python uses: actions/setup-python@v1 with: python-version: 3.9 - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r tools/test-requirements.txt - name: Tox Coverage run: tox -e coverage analysis: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set up Python uses: actions/setup-python@v1 with: python-version: 3.9 - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r tools/test-requirements.txt - name: Tox Analysis run: tox -e analysis softlayer-python-5.9.8/.gitignore000066400000000000000000000002231415376457700170740ustar00rootroot00000000000000.DS_Store *.swp Thumbs.db .svn ._* *.pyc .coverage htmlcov cover/* .tox docs/_build/* build/* dist/* *.egg-info .cache .idea .pytest_cache/* slcli softlayer-python-5.9.8/.readthedocs.yml000066400000000000000000000011141415376457700201720ustar00rootroot00000000000000# .readthedocs.yml # Read the Docs configuration file # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 # Build documentation in the docs/ directory with Sphinx sphinx: builder: htmldir configuration: docs/conf.py # Build documentation with MkDocs #mkdocs: # configuration: mkdocs.yml # Optionally build your docs in additional formats such as PDF and ePub formats: all # Optionally set the version of Python and requirements required to build your docs python: version: 3.7 install: - requirements: docs/requirements.txt softlayer-python-5.9.8/CHANGELOG.md000066400000000000000000001246121415376457700167260ustar00rootroot00000000000000# Change Log ## [5.9.8] - 2021-12-07 https://github.com/softlayer/softlayer-python/compare/v5.9.7...v5.9.8 #### Improvements - Fix code blocks formatting of The Solution section docs #1534 - Add retry decorator to documentation #1535 - Updated utility docs #1536 - Add Exceptions to Documentation #1537 - Forces specific encoding on XMLRPC requests #1543 - Add sensor data to hardware #1544 - Ignoring f-string related messages for tox for now #1548 - Fix account events #1546 - Improved loadbal details #1549 - Fix initialized accountmanger #1552 - Fix hw billing reports 0 items #1556 - Update API docs link and remove travisCI mention #1557 - Fix errors with vs bandwidth #1563 - Add Item names to vs billing report #1564 - Mapping is now in collections.abc #1565 - fix vs placementgroup list #1567 - fixed up snapshot-notification cli commands #1569 #### New Commands - loadbal l7policies #1553 + ` slcli loadbal l7policies --protocol-id` + `slcli loadbal l7policies` - Snapshot notify #1554 + `slcli file|block snapshot-set-notification` + `slcli file|block snapshot-get-notification-status` ## [5.9.7] - 2021-08-04 https://github.com/softlayer/softlayer-python/compare/v5.9.6...v5.9.7 #### Improvements - Fixed some doc block issues when generating HTML #1513 - Updates to the Release workflow for publishing to test pypi #1514 - Adding in CodeQL Analysis #1517 - Create SECURITY.md #1518 - Fix the network space is empty on subnet detail #1523 - Prevents SLCLI_VERSION environment variable from breaking things #1527 - Refactor loadbal order-options #1521 - slcli server create-options dal13 Error #1526 #### New Commands - add new feature on vlan cli #1499 + `slcli vlan create` ## [5.9.6] - 2021-07-05 https://github.com/softlayer/softlayer-python/compare/v5.9.5...v5.9.6 #### Improvements - Updated snap to core20 and edited README #1494 - Add a table result for `slcli hw upgrade` output. #1488 - Remove block/file interval option for replica volume. #1497 - `slcli vlan cancel` should report if a vlan is automatic. #1495 - New method to manage how long text is in output tables. #1506 - Fix Tox-analysis issues. #1510 #### New Commands - add new email feature #1483 + `slcli email list` + `slcli email detail` + `slcli email edit` - `slcli vlan cancel` - Add slcli account licenses #1501 + `slcli account licenses` - Create a new commands on slcli that create/cancel a VMware licenses #1504 + `slcli licenses create` + `slcli licenses cancel` ## [5.9.5] - 2021-05-25 https://github.com/softlayer/softlayer-python/compare/v5.9.4...v5.9.5 #### Improvements - Changed a testing domain to one that really doesnt exist #1492 - Fix Incomplete notes field for file and block #1484 - Show component versions on hw detail #1470 - Add the firewall information on slcli firewall detail #1475 - Add an --orderBy parameters to call-api #1459 - Add image detail transaction data #1479 ## [5.9.4] - 2021-04-27 https://github.com/softlayer/softlayer-python/compare/v5.9.3...v5.9.4 #### New Commands - `slcli hw authorize-storage` #1439 - `slcli order quote-save` #1451 #### Improvements - Refactored managers.ordering_manager.verify_quote() to work better with the REST endpoing #1430 - Add routers for each DC in slcli hw create-options #1432 - Add preset datatype in slcli virtual detail #1435 - Add upgrade option to slcli hw. #1437 - Ibmcloud authentication support #1315 / #1447 + `slcli config setup --ibmid` + `slcli config setup --sso` + `slcli config setup --cloud_key` + `slcli config setup --classic_key` - Refactor slcli hw detail prices. #1443 - Updated contributing guide #1458 - Add the Hardware components on "slcli hardware detail" #1452 - Add billing and lastTransaction on hardware detail #1446 - Forced reserved capacity guests to be monthly #1454 - Removing the rwhois commands #1456 - Added automation to publish to test-pypi #1467 - Updating author_email to SLDN distro list #1469 - Add the option to add and upgrade the hw disk. #1455 - Added a utility to merge objectFilters, #1468 - Fixes shift+ins when pasteing into a password field for windows users. #1460 - Add Billing and lastTransaction on slcli virtual detail #1466 - Fixing 'import mock' pylint issues #1476 ## [5.9.3] - 2021-03-03 https://github.com/softlayer/softlayer-python/compare/v5.9.2...v5.9.3 #### New Commands - `slcli file|block disaster-recovery-failover` #1407 #### Improvements - Unit testing for large integers #1403 - Add Multi factor authentication to users list #1408 - Add pagination to object storage list accounts. #1411 - Add username lookup to slcli object-storage credential #1415 - Add IOPs data to slcli block volume-list. #1418 - Add 2FA and classic APIKeys fields to slcli user list as default values #1421 - Add a flags in the report bandwidth #1420 - Add the option network component by router to slcli hw create. #1422 - Add slcli vs create by router data. #1414 - Add testing and support for python 3.9. #1429 - Checking for TermLength on prices #1428 ## [5.9.2] - 2020-12-03 https://github.com/softlayer/softlayer-python/compare/v5.9.1...v5.9.2 #### New Commands - `slcli account orders` #1349 - `slcli order lookup` #1354 #### Improvements - Ordering price information improvements. #1319 - refactor vsi create-option #1337 - Add Invoice Item id as parameter in `slcli account item-detail` command - Added order lookup command to block and file orders. #1350 - Add prices to vs create-options. #1351 - Allow orders without a location if needed #1356 - Refactor file and block commands to use the username resolver #1357 - Fix create subnet static for ipv4 price. #1358 - moved snapcraft readme #1363 - Update snapcraft.yaml #1365 - Updated documentation on how to deal with KeyError #1366 - Fix order item-list --prices location #1360 - Removed Nessus scanner from docs and examples #1368 - Fix subnet list. #1379 - Fixed analysis/flake8 tests #1381 - Remove the `-a` option from `slcli user create`. Only the user themselves can create an API key now. #1377 ## [5.9.1] - 2020-09-15 https://github.com/softlayer/softlayer-python/compare/v5.9.0...v5.9.1 - Fix the ha option for firewalls, add and implement unit test #1327 - BluePages_Search and IntegratedOfferingTeam_Region don't need SoftLayer_ prefix #972 - Fix new TOX issues #1330 - Add more unit test coverage #1331 - Set notes for network storage #1322 - Some improvements to the dns commands #999 + dns zone-list: added resourceRecordCount, added automatic pagination for large zones + dns record-list: fixed an issue where a record (like SRV types) that don't have a host would cause the command to fail - Renamed managers.storage.refresh_dep_dupe to SoftLayer.managers.storage.refresh_dupe #1342 to support the new API method. CLI commands now use this method. - #1295 added disk upgrade options for virtual guests ## [5.9.0] - 2020-08-03 https://github.com/softlayer/softlayer-python/compare/v5.8.9...v5.9.0 - #1280 Notification Management + slcli user notifications + slcli user edit-notifications - #828 Added networking options to slcli hw create-options + Refactored slcli hw create to use the ordering manager + Added --network option to slcli hw create for more granular network choices. + Deprecated --port-speed and --no-public . They still work for now, but will be removed in a future release. - #1298 Fix Unhandled exception in CLI - vs detail - #1309 Fix the empty lines in slcli vs create-options - #1301 Ability to list VirtualHost capable guests + slcli hardware guests + slcli vs list will show guests on VirtualHost servers - #875 added option to reload bare metal servers with LVM enabled - #874 Added Migrate command - #1313 Added support for filteredMask - #1305 Update docs links - #1302 Fix lots of whitespace slcli vs create-options - #900 Support for STDIN on creating and updating tickets. - #1318 add Drive number in guest drives details using the device number - #1323 add vs list hardware and all option ## [5.8.9] - 2020-07-06 https://github.com/softlayer/softlayer-python/compare/v5.8.8...v5.8.9 - #1252 Automated Snap publisher - #1230 Tag Management + slcli tags cleanup + slcli tags delete + slcli tags details + slcli tags list + slcli tags set + slcli tags taggable - #1285 Vlan editing functionality - #1287 Edit IP note and add ipAddress table in detail view - #1283 Subnet Tagging - #1291 Storage documentation updates - #1293 add system operation referenceCode in create-option ## [5.8.8] - 2020-05-18 https://github.com/softlayer/softlayer-python/compare/v5.8.7...v5.8.8 - #1266 Fixed ticket upload with REST endpoint - #1263 add the redundant/degraded option to hardware - #1262 Added `iter` option for ordering manager functions - #1264 Add Account planned, unplanned and announcement events - #1265 fixed pylint 2.5.0 errors - #1261 Fix AttributeError: 'NoneType' object has no attribute 'keys - #1256 Adding more github action tests, removing travis CI tests - #887 fix Response shows additional new lines (\n) in ticket details - #1241 Storage feature for virtual and hardware servers - #1242 Hardware and Virtual billing info - #1239 VPN subnet access to a use - #1254 added account billing-items/item-details/cancel-item commands ## [5.8.7] - 2020-03-26 https://github.com/softlayer/softlayer-python/compare/v5.8.5...v5.8.7 - #1222 Get load balancer (LBaaS) by name - #1221 Added version checker - #1227 Updated unit test suite for TravisCI to run properly - #1225 Add note about using multiple colon symbols not working when setting tags. - #1228 Support ordering [Dependent Duplicate Volumes](https://cloud.ibm.com/docs/BlockStorage?topic=BlockStorage-dependentduplicate) - #1233 Refactored File/Block managers to reduce duplicated code. - #1231 Added Refresh functions for Dependent Duplicate Volumes - #801 Added support for JSON styled parameters and object filters - #1234 Added ability to change which datacenters an image template was stored in ## [5.8.6] - Skipped ## [5.8.5] - 2020-01-29 https://github.com/softlayer/softlayer-python/compare/v5.8.4...v5.8.5 - #1195 Fixed an issue with `slcli vs dns-sync --ptr`. Added `slcli hw dns-sync` - #1199 Fix File Storage failback and failover. - #1198 Fix issue where the summary command fails due to None being provided as the datacenter name. - #1208 Added The following commands: - `slcli block volume-limits` - `slcli file volume-limits` - #1209 Add testing/CI for python 3.8. - #1212 Fix vs detail erroring on servers pending cancellation. - #1210 support subnet ACL management through cli + `slcli block subnets-list` + `slcli block subnets-assign` + `slcli block subnets-remove` - #1215 Added documentation for all SLCLI commands. ## [5.8.4] - 2019-12-20 https://github.com/softlayer/softlayer-python/compare/v5.8.3...v5.8.4 - #1199 Fix block storage failback and failover. - #1202 Order a virtual server private. ## [5.8.3] - 2019-12-11 https://github.com/softlayer/softlayer-python/compare/v5.8.2...v5.8.3 - #771 Fixed unicode errors in image list (for windows) - #1191 Fixed ordering virtual server dedicated from the CLI - #1155 Fixed capacity restriction when ordering storage quotes - #1192 Fixed hardware detail bandwidth allocation errors. ## [5.8.2] - 2019-11-15 - https://github.com/softlayer/softlayer-python/compare/v5.8.1...v5.8.2 + #1186 Fixed a unit test that could fail if the test took too long to run. + #1183 Added a check to ensure subnet and vlan options are properly added to the order for virtual servers. + #1184 Fixed a readme misspelling. + #1182 Fixed vs reboot unable to resolve vs names. + #1095 Handle missing Fixtures better for unit tests. ## [5.8.1] - 2019-10-11 - https://github.com/softlayer/softlayer-python/compare/v5.8.0...v5.8.1 + #1169 Drop python 2.7 support + #1170 Added CS# to ticket listing + #1162 Fixed issue looking up OS keyName instead of referenceCode + #627 Autoscale support * slcli autoscale detail * slcli autoscale edit * slcli autoscale list * slcli autoscale logs * slcli autoscale scale * slcli autoscale tag ## [5.8.0] - 2019-09-04 - https://github.com/softlayer/softlayer-python/compare/v5.7.2...v5.8.0 + #1143 Upgrade to prompt_toolkit >= 2 + #1003 Bandwidth Feature * slcli summary * slcli report bandwidth * slcli vs bandwidth * slcli hw bandwidth * Added bandwidth to VS and HW details page + #1146 DOCS: replace 'developer' with 'sldn' links + #1147 property 'contents' is not valid for 'SoftLayer_Ticket' when creating a ticket + #1139 cannot create static subnet with slcli + #1145 Refactor cdn network. + #1152 IBMID auth support + #1153, #1052 Transient VSI support + #1167 Removed legacy LoadBalancer command, added Citrix and IBM LBaaS commands. * slcli lb cancel * slcli lb detail * slcli lb health * slcli lb l7pool-add * slcli lb l7pool-del * slcli lb list * slcli lb member-add * slcli lb member-del * slcli lb ns-detail * slcli lb ns-list * slcli lb order * slcli lb order-options * slcli lb pool-add * slcli lb pool-del * slcli lb pool-edit + #1157 Remove VpnAllowedFlag. + #1160 Improve hardware cancellation to deal with additional cases ## [5.7.2] - 2019-05-03 - https://github.com/softlayer/softlayer-python/compare/v5.7.1...v5.7.2 + #1107 Added exception to handle json parsing error when ordering + #1068 Support for -1 when changing port speed + #1109 Fixed docs about placement groups + #1112 File storage endurance iops upgrade + #1101 Handle the new user creation exceptions + #1116 Fix order place quantity option + #1002 Invoice commands * account invoices * account invoice-detail * account summary + #1004 Event Notification Management commands * account events * account event-detail + #1117 Two PCIe items can be added at order time + #1121 Fix object storage apiType for S3 and Swift. + #1100 Event Log performance improvements. + #872 column 'name' was renamed to 'hostname' + #1127 Fix object storage credentials. + #1129 Fixed unexpected errors in slcli subnet create + #1134 Change encrypt parameters for importing of images. Adds root-key-crn + #208 Quote ordering commands * order quote * order quote-detail * order quote-list + #1113 VS usage information command * virtual usage + #1131 made sure config_tests dont actually make api calls. ## [5.7.1] - 2019-02-26 - https://github.com/softlayer/softlayer-python/compare/v5.7.0...v5.7.1 + #1089 removed legacy SL message queue commands + Support for Hardware reflash firmware CLI/Manager method ## [5.7.0] - 2019-02-15 - Changes: https://github.com/softlayer/softlayer-python/compare/v5.6.4...v5.7.0 + #1099 Support for security group Ids + event-log cli command + #1069 Virtual Placement Group Support ``` slcli vs placementgroup --help Commands: create Create a placement group. create-options List options for creating a placement group. delete Delete a placement group. detail View details of a placement group. list List placement groups. ``` + #962 Rest Transport improvements. Properly handle HTTP exceptions instead of crashing. + #1090 removed power_state column option from "slcli server list" + #676 - ipv6 support for creating virtual guests * Refactored virtual guest creation to use Product_Order::placeOrder instead of Virtual_Guest::createObject, because createObject doesn't allow adding IPv6 + #882 Added table which shows the status of each url in object storage + #1085 Update provisionedIops reading to handle float-y values + #1074 fixed issue with config setup + #1081 Fix file volume-cancel + #1059 Support for SoftLayer_Hardware_Server::toggleManagementInterface * `slcli hw toggle-ipmi` ## [5.6.4] - 2018-11-16 - Changes: https://github.com/softlayer/softlayer-python/compare/v5.6.3...v5.6.4 + #1041 Dedicated host cancel, cancel-guests, list-guests + #1071 added createDate and modifyDate parameters to sg rule-list + #1060 Fixed slcli subnet list + #1056 Fixed documentation link in image manager + #1062 Added description to slcli order ## [5.6.3] - 2018-11-07 - Changes: https://github.com/softlayer/softlayer-python/compare/v5.6.0...v5.6.3 + #1065 Updated urllib3 and requests libraries due to CVE-2018-18074 + #1070 Fixed an ordering bug + Updated release process and fab-file ## [5.6.0] - 2018-10-16 - Changes: https://github.com/softlayer/softlayer-python/compare/v5.5.3...v5.6.0 + #1026 Support for [Reserved Capacity](https://cloud.ibm.com/docs/virtual-servers?topic=virtual-servers-about-reserved-virtual-servers) * `slcli vs capacity create` * `slcli vs capacity create-guest` * `slcli vs capacity create-options` * `slcli vs capacity detail` * `slcli vs capacity list` + #1050 Fix `post_uri` parameter name on docstring + #1039 Fixed suspend cloud server order. + #1055 Update to use click 7 + #1053 Add export/import capabilities to/from IBM Cloud Object Storage to the image manager as well as the slcli. ## [5.5.3] - 2018-08-31 - Changes: https://github.com/softlayer/softlayer-python/compare/v5.5.2...v5.5.3 + Added `slcli user delete` + #1023 Added `slcli order quote` to let users create a quote from the slcli. + #1032 Fixed vs upgrades when using flavors. + #1034 Added pagination to ticket list commands + #1037 Fixed DNS manager to be more flexible and support more zone types. + #1044 Pinned Click library version at >=5 < 7 ## [5.5.2] - 2018-08-31 - Changes: https://github.com/softlayer/softlayer-python/compare/v5.5.1...v5.5.2 + #1018 Fixed hardware credentials. + #1019 support for ticket priorities + #1025 create dedicated host with gpu fixed. ## [5.5.1] - 2018-08-06 - Changes: https://github.com/softlayer/softlayer-python/compare/v5.5.0...v5.5.1 - #1006, added paginations to several slcli methods, making them work better with large result sets. - #995, Fixed an issue displaying VLANs. - #1011, Fixed an issue displaying some NAS passwords - #1014, Ability to delete users ## [5.5.0] - 2018-07-09 - Changes: https://github.com/softlayer/softlayer-python/compare/v5.4.4...v5.5.0 - Added a warning when ordering legacy storage volumes - Added documentation link to volume-order - Increased slcli output width limit to 999 characters - More unit tests - Fixed an issue canceling some block storage volumes - Fixed `slcli order` to work with network gateways - Fixed an issue showing hardware credentials when they do not exist - Fixed an issue showing addressSpace when listing virtual servers - Updated ordering class to support baremetal servers with multiple GPU - Updated prompt-toolkit as a fix for `slcli shell` - Fixed `slcli vlan detail` to not fail when objects don't have a hostname - Added user management ## [5.4.4] - 2018-04-18 - Changes: https://github.com/softlayer/softlayer-python/compare/v5.4.3...v5.4.4 - fixed hw list not showing transactions - Re-factored RestTransport and XMLRPCTransport, logging is now only done in the DebugTransport - Added print_reproduceable to XMLRPCTransport and RestTransport, which should be very useful in printing out pure API calls. - Fixed an issue with RestTransport and locationGroupId ## [5.4.3] - 2018-03-30 - Changes: https://github.com/softlayer/softlayer-python/compare/v5.4.2...v5.4.3 - Corrected to current create-options output - Allow ordering of account restricted presets - Added lookup function for datacenter names and ability to use `slcli order` with short DC names - Changed locatoinGroupId to check for None instead of empty string - Added a way to try to cancel montly bare metal immediately. THis is done by automatically updating the cancellation request. A human still needs to read the ticket and process it for the reclaim to complete. ## [5.4.2] - 2018-02-22 - Changes: https://github.com/softlayer/softlayer-python/compare/v5.4.1...v5.4.2 - 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.9.8/CONTRIBUTING.md000066400000000000000000000146401415376457700173450ustar00rootroot00000000000000# Contributing to softlayer-python We are happy to accept contributions to softlayer-python. Please follow the guidelines below. ## Procedural 1. All code changes require a corresponding issue. [Open an issue here](https://github.com/softlayer/softlayer-python/issues). 2. Fork the [softlayer-python](https://github.com/softlayer/softlayer-python) repository. 3. Make any changes required, commit messages should reference the issue number (include #1234 if the message if your issue is number 1234 for example). 4. Make a pull request from your fork/branch to origin/master 5. Requires 1 approval for merging * Additional infomration can be found in our [contribution guide](http://softlayer-python.readthedocs.org/en/latest/dev/index.html) ## Legal * See our [Contributor License Agreement](./docs/dev/cla-individual.md). Opening a pull request is acceptance of this agreement. * 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). ## Code style Code is tested and style checked with tox, you can run the tox tests individually by doing `tox -e ` * `autopep8 -r -v -i --max-line-length 119 SoftLayer/` * `autopep8 -r -v -i --max-line-length 119 tests/` * `tox -e analysis` * `tox -e py36` * `git commit --message="# ` * `git push origin ` * create pull request ## Documentation CLI command should have a more human readable style of documentation. Manager methods should have a decent docblock describing any parameters and what the method does. Docs are generated with [Sphinx](https://docs.readthedocs.io/en/latest/intro/getting-started-with-sphinx.html) and once Sphinx is setup, you can simply do `make html` in the softlayer-python/docs directory, which should generate the HTML in softlayer-python/docs/_build/html for testing. ## Unit Tests All new features should be 100% code covered, and your pull request should at the very least increase total code overage. ### Mocks To tests results from the API, we keep mock results in SoftLayer/fixtures// with the method name matching the variable name. Any call to a service that doesn't have a fixture will result in a TransportError ### Overriding Fixtures Adding your expected output in the fixtures file with a unique name is a good way to define a fixture that gets used frequently in a test. ```python from SoftLayer.fixtures import SoftLayer_Product_Package def test_test(self): amock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') amock.return_value = fixtures.SoftLayer_Product_Package.RESERVED_CAPACITY ``` Otherwise defining it on the spot works too. ```python def test_test(self): mock = self.set_mock('SoftLayer_Network_Storage', 'getObject') mock.return_value = { 'billingItem': {'hourlyFlag': True, 'id': 449}, } ``` ### Call testing Testing your code to make sure it makes the correct API call is also very important. The testing.TestCase class has a method call `assert_called_with` which is pretty handy here. ```python self.assert_called_with( 'SoftLayer_Billing_Item', # Service 'cancelItem', # Method args=(True, True, ''), # Args identifier=449, # Id mask=mock.ANY, # object Mask, filter=mock.ANY, # object Filter limit=0, # result Limit offset=0 # result Offset ) ``` Making sure a API was NOT called ```python self.assertEqual([], self.calls('SoftLayer_Account', 'getObject')) ``` Making sure an API call has a specific arg, but you don't want to list out the entire API call (like with a place order test) ```python # Get the API Call signature order_call = self.calls('SoftLayer_Product_Order', 'placeOrder') # Get the args property of that API call, which is a tuple, with the first entry being our data. order_args = getattr(order_call[0], 'args')[0] # Test our specific argument value self.assertEqual(123, order_args['hostId']) ``` ## Project Management ### Issues * _Title_: Should contain quick highlight of the issue is about * _Body_: All the technical information goes here * _Assignee_: Should be the person who is actively working on an issue. * _Label_: All issues should have at least 1 Label. * _Projects_: Should be added to the quarerly Softlayer project when being worked on * _Milestones_: Not really used, can be left blank * _Linked Pull Request_: Should be linked to the relavent pull request when it is opened. ### Pull Requests * _Title_: Should be similar to the title of the issue it is fixing, or otherwise descibe what is chaning in the pull request * _Body_: Should have "Fixes #1234" at least, with some notes about the specific pull request if needed. Most technical information should still be in the github issue. * _Reviewers_: 1 Reviewer is required * _Assignee_: Should be the person who opened the pull request * _Labels_: Should match the issue * _Projects_: Should match the issue * _Milestones_: Not really used, can be left blank * _Linked issues_: If you put "Fixes #" in the body, this should be automatically filled in, otherwise link manually. ### Code Reviews All issues should be reviewed by at least 1 member of the SLDN team that is not the person opening the pull request. Time permitting, all members of the SLDN team should review the request. #### Things to look for while doing a review As a reviewer, these are some guidelines when doing a review, but not hard rules. * Code Style: Generally `tox -e analysis` will pick up most style violations, but anything that is wildly different from the normal code patters in this project should be changed to match, unless there is a strong reason to not do so. * API Calls: Close attention should be made to any new API calls, to make sure they will work as expected, and errors are handled if needed. * DocBlock comments: CLI and manager methods need to be documented well enough for users to easily understand what they do. * Easy to read code: Code should generally be easy enough to understand at a glance. Confusing code is a sign that it should either be better documented, or refactored a bit to be clearer in design. ### Testing When doing testing of a code change, indicate this with a comment on the pull request like :heavy_check: `slcli vs list --new-feature` :x: `slcli vs list --broken-feature` softlayer-python-5.9.8/CONTRIBUTORS000066400000000000000000000024071415376457700167720ustar00rootroot00000000000000Amol Jadhav Aparna Patil Boden Russell Brian Cline chechuironman Christopher Gallo David Ibarra Hans Kristian Moen Ian Sutton 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.9.8/ISSUE_TEMPLATE000066400000000000000000000004451415376457700172200ustar00rootroot00000000000000**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.9.8/LICENSE000066400000000000000000000021051415376457700161120ustar00rootroot00000000000000Copyright (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.9.8/MANIFEST.in000066400000000000000000000000431415376457700166420ustar00rootroot00000000000000include LICENSE include README.rst softlayer-python-5.9.8/Makefile000066400000000000000000000164421415376457700165560ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage 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 " applehelp to make an Apple Help Book" @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 " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @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 " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" @echo " coverage to run coverage check of 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/softlayer-python.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/softlayer-python.qhc" applehelp: $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp @echo @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." @echo "N.B. You won't be able to view it unless you put it in" \ "~/Library/Documentation/Help or install it in your application" \ "bundle." devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/softlayer-python" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/softlayer-python" @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." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @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." coverage: $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage @echo "Testing of coverage in the sources finished, look at the " \ "results in $(BUILDDIR)/coverage/python.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." softlayer-python-5.9.8/README-snapcraft.md000066400000000000000000000010411415376457700203410ustar00rootroot00000000000000# To Install: `sudo snap install slcli` ------------------------------------------------------------------------ # What are SNAPS? Snaps are available for any Linux OS running snapd, the service that runs and manage snaps. For more info, see: https://snapcraft.io/ or to learn to build and publish your own snaps, please see: https://docs.snapcraft.io/build-snaps/languages?_ga=2.49470950.193172077.1519771181-1009549731.1511399964 # Releasing Builds should be automagic here. https://build.snapcraft.io/user/softlayer/softlayer-python softlayer-python-5.9.8/README.rst000066400000000000000000000136121415376457700166010ustar00rootroot00000000000000SoftLayer API Python Client =========================== .. image:: https://github.com/softlayer/softlayer-python/workflows/Tests/badge.svg :target: https://github.com/softlayer/softlayer-python/actions?query=workflow%3ATests .. image:: https://github.com/softlayer/softlayer-python/workflows/documentation/badge.svg :target: https://github.com/softlayer/softlayer-python/actions?query=workflow%3Adocumentation .. 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 .. image:: https://snapcraft.io//slcli/badge.svg :target: https://snapcraft.io/slcli 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 `Read the Docs `_ . 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 Another (safer) method of installation is to use the published snap. Snaps are available for any Linux OS running snapd, the service that runs and manage snaps. Snaps are "auto-updating" packages and will not disrupt the current versions of libraries and software packages on your Linux-based system. To learn more, please visit: https://snapcraft.io/ To install the slcli snap: .. code-block:: bash $ sudo snap install slcli (or to get the latest release) $ sudo snap install slcli --edge 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. Basic Usage ----------- - `The Complete Command Directory `_ Advanced Usage -------------- You can automatically set some parameters via environment variables with by using the SLCLI prefix. For example .. code-block:: bash $ export SLCLI_VERBOSE=3 $ export SLCLI_FORMAT=json $ slcli vs list is equivalent to .. code-block:: bash $ slcli -vvv --format=json vs list 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. Examples -------- A curated list of examples on how to use this library can be found at `SLDN `_ Debugging --------- To get the exact API call that this library makes, you can do the following. For the CLI, just use the -vvv option. If you are using the REST endpoint, this will print out a curl command that you can use, if using XML, this will print the minimal python code to make the request without the softlayer library. .. code-block:: bash $ slcli -vvv vs list If you are using the library directly in python, you can do something like this. .. code-block:: python import SoftLayer import logging class invoices(): def __init__(self): self.client = SoftLayer.Client() debugger = SoftLayer.DebugTransport(self.client.transport) self.client.transport = debugger def main(self): mask = "mask[id]" account = self.client.call('Account', 'getObject', mask=mask); print("AccountID: %s" % account['id']) def debug(self): for call in self.client.transport.get_last_calls(): print(self.client.transport.print_reproduceable(call)) if __name__ == "__main__": main = example() main.main() main.debug() System Requirements ------------------- * Python 3.5, 3.6, 3.7, 3.8, or 3.9. * A valid SoftLayer API username and key. * A connection to SoftLayer's private network is required to use our private network API endpoints. Python 2.7 Support ------------------ As of version 5.8.0 SoftLayer-Python will no longer support python2.7, which is `End Of Life as of 2020 `_ . If you cannot install python 3.6+ for some reason, you will need to use a version of softlayer-python <= 5.7.2 Python Packages --------------- * ptable >= 0.9.2 * click >= 7 * requests >= 2.20.0 * prompt_toolkit >= 2 * pygments >= 2.0.0 * urllib3 >= 1.24 Copyright --------- This software is Copyright (c) 2016-2021 SoftLayer Technologies, Inc. See the bundled LICENSE file for more information. softlayer-python-5.9.8/RELEASE.md000066400000000000000000000054261415376457700165200ustar00rootroot00000000000000 # Versions This project follows the Major.Minor.Revision versioning system. Fixes, and minor additions would increment Revision. Large changes and additions would increment Minor, and anything that would be a "Breaking" change, or redesign would be an increment of Major. # Changelog When doing a release, the Changelog format should be as follows: ```markdown ## [Version] - YYYY-MM-DD https://github.com/softlayer/softlayer-python/compare/v5.9.0...v5.9.1 #### New Command - `slcli new command` #issueNumber #### Improvements - List out improvements #issueNumber - Something else that changed #issueNumber #### Deprecated - List something that got removed #issueNumber ``` # Normal Release steps A "release" of the softlayer-python project is the current state of the `master` branch. Any changes in the master branch should be considered releaseable. 1. Create the changelog entry, us this to update `CHANGELOG.md` and as the text for the release on github. 2. Update the version numbers in these files on the master branch. - `SoftLayer/consts.py` - `setup.py` 3. Make sure the tests for the build all pass 4. [Draft a new release](https://github.com/softlayer/softlayer-python/releases/new) - Version should start with `v` followed by Major.Minor.Revision: `vM.m.r` - Title should be `M.m.r` - Description should be the release notes - Target should be the `master` branch 5. The github automation should take care of publishing the release to [PyPi](https://pypi.org/project/SoftLayer/). This may take a few minutes to update. # Manual Release steps 1. Create the changelog entry, us this to update `CHANGELOG.md` and as the text for the release on github. 2. Update the version numbers in these files on the master branch. - `SoftLayer/consts.py` - `setup.py` 3. Commit your changes to `master`, and make sure `softlayer/softlayer-python` repo is updated to reflect that 4. Make sure your `upstream` repo is set ``` git remote -v upstream git@github.com:softlayer/softlayer-python.git (fetch) upstream git@github.com:softlayer/softlayer-python.git (push) ``` 5. Create and publish the package - Make sure you have `twine` installed, this is what uploads the pacakge to PyPi. - Before you do this, make sure you have the organization repository set up as upstream remote, 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 ``` - Run `python fabfile.py 5.7.2`. Where `5.7.2` is the `M.m.r` version number. Don't use the `v` here in the version number. *NOTE* PyPi doesn't let you reupload a version, if you upload a bad package for some reason, you have to create a new version. softlayer-python-5.9.8/SECURITY.md000066400000000000000000000010721415376457700167000ustar00rootroot00000000000000# Security Policy ## Supported Versions Generally only the latest release will be actively worked on and supported. Version 5.7.2 is the last version that supports python2.7. | Version | Supported | | ------- | ------------------ | | 5.9.x | :white_check_mark: | | 5.7.2 | :white_check_mark: | | < 5.7.2 | :x: | ## Reporting a Vulnerability Create a new [Bug Report](https://github.com/softlayer/softlayer-python/issues/new?assignees=&labels=Bug&template=bug_report.md&title=) to let us know about any vulnerabilities in the code base. softlayer-python-5.9.8/SoftLayer/000077500000000000000000000000001415376457700170175ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/API.py000066400000000000000000000546161415376457700200160ustar00rootroot00000000000000""" SoftLayer.API ~~~~~~~~~~~~~ SoftLayer API bindings :license: MIT, see LICENSE for more details. """ # pylint: disable=invalid-name import time import warnings import json import logging import requests from SoftLayer import auth as slauth from SoftLayer import config from SoftLayer import consts from SoftLayer import exceptions from SoftLayer import transports LOGGER = logging.getLogger(__name__) API_PUBLIC_ENDPOINT = consts.API_PUBLIC_ENDPOINT API_PRIVATE_ENDPOINT = consts.API_PRIVATE_ENDPOINT CONFIG_FILE = consts.CONFIG_FILE __all__ = [ 'create_client_from_env', 'Client', 'BaseClient', 'API_PUBLIC_ENDPOINT', 'API_PRIVATE_ENDPOINT', 'IAMClient', ] 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' """ if config_file is None: config_file = CONFIG_FILE 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, config_file=config_file) 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, config_file=None): if config_file is None: config_file = CONFIG_FILE self.auth = auth self.config_file = config_file self.settings = config.get_config(self.config_file) if transport is None: url = self.settings['softlayer'].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=url, proxy=self.settings['softlayer'].get('proxy'), # prevents an exception incase timeout is a float number. timeout=int(self.settings['softlayer'].getfloat('timeout')), user_agent=consts.USER_AGENT, verify=self.settings['softlayer'].getboolean('verify'), ) else: # Default the transport to use XMLRPC transport = transports.XmlRpcTransport( endpoint_url=url, proxy=self.settings['softlayer'].get('proxy'), timeout=int(self.settings['softlayer'].getfloat('timeout')), user_agent=consts.USER_AGENT, verify=self.settings['softlayer'].getboolean('verify'), ) 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): # Most of the codebase assumes a non-generator will be returned, so casting to list # keeps those sections working return list(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)) prefixes = (self._prefix, 'BluePages_Search', 'IntegratedOfferingTeam_Region') if self._prefix and not service.startswith(prefixes): 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 limit: 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 """ limit = kwargs.pop('limit', 100) offset = kwargs.pop('offset', 0) if limit <= 0: raise AttributeError("Limit size should be greater than zero.") # Set to make unit tests, which call this function directly, play nice. kwargs['iter'] = False result_count = 0 keep_looping = True while keep_looping: # Get the next results results = self.call(service, method, offset=offset, limit=limit, *args, **kwargs) # Apparently this method doesn't return a list. # Why are you even iterating over this? if not isinstance(results, transports.SoftLayerListResult): if isinstance(results, list): # Close enough, this makes testing a lot easier results = transports.SoftLayerListResult(results, len(results)) else: yield results return for item in results: yield item result_count += 1 # Got less results than requested, we are at the end if len(results) < limit: keep_looping = False # Got all the needed items if result_count >= results.total_count: keep_looping = False offset += limit def __repr__(self): return "Client(transport=%r, auth=%r)" % (self.transport, self.auth) __str__ = __repr__ def __len__(self): return 0 class IAMClient(BaseClient): """IBM ID Client for using IAM authentication :param auth: auth driver that looks like SoftLayer.auth.AuthenticationBase :param transport: An object that's callable with this signature: transport(SoftLayer.transports.Request) """ def authenticate_with_password(self, username, password, security_question_id=None, security_question_answer=None): """Performs IBM IAM Username/Password Authentication :param string username: your IBMid username :param string password: your IBMid password """ iam_client = requests.Session() headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'User-Agent': consts.USER_AGENT, 'Accept': 'application/json' } data = { 'grant_type': 'password', 'password': password, 'response_type': 'cloud_iam', 'username': username } try: response = iam_client.request( 'POST', 'https://iam.cloud.ibm.com/identity/token', data=data, headers=headers, auth=requests.auth.HTTPBasicAuth('bx', 'bx') ) if response.status_code != 200: LOGGER.error("Unable to login: %s", response.text) response.raise_for_status() tokens = json.loads(response.text) except requests.HTTPError as ex: error = json.loads(response.text) raise exceptions.IAMError(response.status_code, error.get('errorMessage'), 'https://iam.cloud.ibm.com/identity/token') from ex self.settings['softlayer']['access_token'] = tokens['access_token'] self.settings['softlayer']['refresh_token'] = tokens['refresh_token'] config.write_config(self.settings, self.config_file) self.auth = slauth.BearerAuthentication('', tokens['access_token'], tokens['refresh_token']) return tokens def authenticate_with_passcode(self, passcode): """Performs IBM IAM SSO Authentication :param string passcode: your IBMid password """ iam_client = requests.Session() headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'User-Agent': consts.USER_AGENT, 'Accept': 'application/json' } data = { 'grant_type': 'urn:ibm:params:oauth:grant-type:passcode', 'passcode': passcode, 'response_type': 'cloud_iam' } try: response = iam_client.request( 'POST', 'https://iam.cloud.ibm.com/identity/token', data=data, headers=headers, auth=requests.auth.HTTPBasicAuth('bx', 'bx') ) if response.status_code != 200: LOGGER.error("Unable to login: %s", response.text) response.raise_for_status() tokens = json.loads(response.text) except requests.HTTPError as ex: error = json.loads(response.text) raise exceptions.IAMError(response.status_code, error.get('errorMessage'), 'https://iam.cloud.ibm.com/identity/token') from ex self.settings['softlayer']['access_token'] = tokens['access_token'] self.settings['softlayer']['refresh_token'] = tokens['refresh_token'] a_expire = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(tokens['expiration'])) r_expire = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(tokens['refresh_token_expiration'])) LOGGER.warning("Tokens retrieved, expires at %s, Refresh expires at %s", a_expire, r_expire) config.write_config(self.settings, self.config_file) self.auth = slauth.BearerAuthentication('', tokens['access_token'], tokens['refresh_token']) return tokens def authenticate_with_iam_token(self, a_token, r_token=None): """Authenticates to the SL API with an IAM Token :param string a_token: Access token :param string r_token: Refresh Token, to be used if Access token is expired. """ self.auth = slauth.BearerAuthentication('', a_token, r_token) def refresh_iam_token(self, r_token, account_id=None, ims_account=None): """Refreshes the IAM Token, will default to values in the config file""" iam_client = requests.Session() headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'User-Agent': consts.USER_AGENT, 'Accept': 'application/json' } data = { 'grant_type': 'refresh_token', 'refresh_token': r_token, 'response_type': 'cloud_iam' } sl_config = self.settings['softlayer'] if account_id is None and sl_config.get('account_id', False): account_id = sl_config.get('account_id') if ims_account is None and sl_config.get('ims_account', False): ims_account = sl_config.get('ims_account') data['account'] = account_id data['ims_account'] = ims_account try: response = iam_client.request( 'POST', 'https://iam.cloud.ibm.com/identity/token', data=data, headers=headers, auth=requests.auth.HTTPBasicAuth('bx', 'bx') ) if response.status_code != 200: LOGGER.warning("Unable to refresh IAM Token. %s", response.text) response.raise_for_status() tokens = json.loads(response.text) except requests.HTTPError as ex: error = json.loads(response.text) raise exceptions.IAMError(response.status_code, error.get('errorMessage'), 'https://iam.cloud.ibm.com/identity/token') from ex a_expire = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(tokens['expiration'])) r_expire = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(tokens['refresh_token_expiration'])) LOGGER.warning("Tokens retrieved, expires at %s, Refresh expires at %s", a_expire, r_expire) self.settings['softlayer']['access_token'] = tokens['access_token'] self.settings['softlayer']['refresh_token'] = tokens['refresh_token'] config.write_config(self.settings, self.config_file) self.auth = slauth.BearerAuthentication('', tokens['access_token']) return tokens def call(self, service, method, *args, **kwargs): """Handles refreshing IAM tokens in case of a HTTP 401 error""" try: return super().call(service, method, *args, **kwargs) except exceptions.SoftLayerAPIError as ex: if ex.faultCode == 401: LOGGER.warning("Token has expired, trying to refresh. %s", ex.faultString) return ex else: raise ex def __repr__(self): return "IAMClient(transport=%r, auth=%r)" % (self.transport, self.auth) 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.9.8/SoftLayer/CLI/000077500000000000000000000000001415376457700174265ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/__init__.py000066400000000000000000000003451415376457700215410ustar00rootroot00000000000000""" SoftLayer.CLI ~~~~~~~~~~~~~~ Contains all code related to the CLI interface :license: MIT, see LICENSE for more details. """ # pylint: disable=w0401, invalid-name from SoftLayer.CLI.helpers import * # NOQA softlayer-python-5.9.8/SoftLayer/CLI/account/000077500000000000000000000000001415376457700210625ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/account/__init__.py000066400000000000000000000000271415376457700231720ustar00rootroot00000000000000"""Account commands""" softlayer-python-5.9.8/SoftLayer/CLI/account/billing_items.py000066400000000000000000000037041415376457700242610ustar00rootroot00000000000000"""Lists all active billing items on this account. See https://cloud.ibm.com/billing/billing-items""" # :license: MIT, see LICENSE for more details. import click from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.managers.account import AccountManager as AccountManager from SoftLayer import utils @click.command() @environment.pass_env def cli(env): """Lists billing items with some other useful information. Similiar to https://cloud.ibm.com/billing/billing-items """ manager = AccountManager(env.client) items = manager.get_account_billing_items() table = item_table(items) env.fout(table) def item_table(items): """Formats a table for billing items""" table = formatting.Table([ "Id", "Create Date", "Cost", "Category Code", "Ordered By", "Description", "Notes" ], title="Billing Items") table.align['Description'] = 'l' table.align['Category Code'] = 'l' for item in items: description = item.get('description') fqdn = "{}.{}".format(item.get('hostName', ''), item.get('domainName', '')) if fqdn != ".": description = fqdn user = utils.lookup(item, 'orderItem', 'order', 'userRecord') ordered_by = "IBM" create_date = utils.clean_time(item.get('createDate'), in_format='%Y-%m-%d', out_format='%Y-%m-%d') if user: # ordered_by = "{} ({})".format(user.get('displayName'), utils.lookup(user, 'userStatus', 'name')) ordered_by = user.get('displayName') table.add_row([ item.get('id'), create_date, item.get('nextInvoiceTotalRecurringAmount'), item.get('categoryCode'), ordered_by, utils.trim_to(description, 50), utils.trim_to(item.get('notes', 'None'), 40), ]) return table softlayer-python-5.9.8/SoftLayer/CLI/account/cancel_item.py000066400000000000000000000007071415376457700237030ustar00rootroot00000000000000"""Cancels a billing item.""" # :license: MIT, see LICENSE for more details. import click from SoftLayer.CLI import environment from SoftLayer.managers.account import AccountManager as AccountManager @click.command() @click.argument('identifier') @environment.pass_env def cli(env, identifier): """Cancels a billing item.""" manager = AccountManager(env.client) item = manager.cancel_item(identifier) env.fout(item) softlayer-python-5.9.8/SoftLayer/CLI/account/event_detail.py000066400000000000000000000044761415376457700241120ustar00rootroot00000000000000"""Details of a specific event, and ability to acknowledge event.""" # :license: MIT, see LICENSE for more details. import click from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.managers.account import AccountManager as AccountManager from SoftLayer import utils @click.command() @click.argument('identifier') @click.option('--ack', is_flag=True, default=False, help="Acknowledge Event. Doing so will turn off the popup in the control portal") @environment.pass_env def cli(env, identifier, ack): """Details of a specific event, and ability to acknowledge event.""" # Print a list of all on going maintenance manager = AccountManager(env.client) event = manager.get_event(identifier) if ack: manager.ack_event(identifier) env.fout(basic_event_table(event)) env.fout(impacted_table(event)) env.fout(update_table(event)) def basic_event_table(event): """Formats a basic event table""" table = formatting.Table(["Id", "Status", "Type", "Start", "End"], title=utils.clean_splitlines(event.get('subject'))) table.add_row([ event.get('id'), utils.lookup(event, 'statusCode', 'name'), utils.lookup(event, 'notificationOccurrenceEventType', 'keyName'), utils.clean_time(event.get('startDate')), utils.clean_time(event.get('endDate')) ]) return table def impacted_table(event): """Formats a basic impacted resources table""" table = formatting.Table([ "Type", "Id", "Hostname", "PrivateIp", "Label" ]) for item in event.get('impactedResources', []): table.add_row([ item.get('resourceType'), item.get('resourceTableId'), item.get('hostname'), item.get('privateIp'), item.get('filterLabel') ]) return table def update_table(event): """Formats a basic event update table""" update_number = 0 for update in event.get('updates', []): header = "======= Update #%s on %s =======" % (update_number, utils.clean_time(update.get('startDate'))) click.secho(header, fg='green') update_number = update_number + 1 text = update.get('contents') # deals with all the \r\n from the API click.secho(utils.clean_splitlines(text)) softlayer-python-5.9.8/SoftLayer/CLI/account/events.py000066400000000000000000000101571415376457700227440ustar00rootroot00000000000000"""Summary and acknowledgement of upcoming and ongoing maintenance events""" # :license: MIT, see LICENSE for more details. import click from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.managers.account import AccountManager as AccountManager from SoftLayer import utils @click.command() @click.option('--ack-all', is_flag=True, default=False, help="Acknowledge every upcoming event. Doing so will turn off the popup in the control portal") @environment.pass_env def cli(env, ack_all): """Summary and acknowledgement of upcoming and ongoing maintenance events""" manager = AccountManager(env.client) planned_events = manager.get_upcoming_events("PLANNED") unplanned_events = manager.get_upcoming_events("UNPLANNED_INCIDENT") announcement_events = manager.get_upcoming_events("ANNOUNCEMENT") add_ack_flag(planned_events, manager, ack_all) env.fout(planned_event_table(planned_events)) add_ack_flag(unplanned_events, manager, ack_all) env.fout(unplanned_event_table(unplanned_events)) add_ack_flag(announcement_events, manager, ack_all) env.fout(announcement_event_table(announcement_events)) def add_ack_flag(events, manager, ack_all): """Add acknowledgedFlag to the event""" if ack_all: for event in events: result = manager.ack_event(event['id']) event['acknowledgedFlag'] = result def planned_event_table(events): """Formats a table for events""" planned_table = formatting.Table(['Event Data', 'Id', 'Event ID', 'Subject', 'Status', 'Items', 'Start Date', 'End Date', 'Acknowledged', 'Updates'], title="Planned Events") planned_table.align['Subject'] = 'l' planned_table.align['Impacted Resources'] = 'l' for event in events: planned_table.add_row([ utils.clean_time(event.get('startDate')), event.get('id'), event.get('systemTicketId'), # Some subjects can have \r\n for some reason. utils.clean_splitlines(event.get('subject')), utils.lookup(event, 'statusCode', 'name'), event.get('impactedResourceCount'), utils.clean_time(event.get('startDate')), utils.clean_time(event.get('endDate')), event.get('acknowledgedFlag'), event.get('updateCount'), ]) return planned_table def unplanned_event_table(events): """Formats a table for events""" unplanned_table = formatting.Table(['Id', 'Event ID', 'Subject', 'Status', 'Items', 'Start Date', 'Last Updated', 'Acknowledged', 'Updates'], title="Unplanned Events") unplanned_table.align['Subject'] = 'l' unplanned_table.align['Impacted Resources'] = 'l' for event in events: unplanned_table.add_row([ event.get('id'), event.get('systemTicketId'), # Some subjects can have \r\n for some reason. utils.clean_splitlines(event.get('subject')), utils.lookup(event, 'statusCode', 'name'), event.get('impactedResourceCount'), utils.clean_time(event.get('startDate')), utils.clean_time(event.get('modifyDate')), event.get('acknowledgedFlag'), event.get('updateCount'), ]) return unplanned_table def announcement_event_table(events): """Formats a table for events""" announcement_table = formatting.Table( ['Id', 'Event ID', 'Subject', 'Status', 'Items', 'Acknowledged', 'Updates'], title="Announcement Events") announcement_table.align['Subject'] = 'l' announcement_table.align['Impacted Resources'] = 'l' for event in events: announcement_table.add_row([ event.get('id'), event.get('systemTicketId'), # Some subjects can have \r\n for some reason. utils.clean_splitlines(event.get('subject')), utils.lookup(event, 'statusCode', 'name'), event.get('impactedResourceCount'), event.get('acknowledgedFlag'), event.get('updateCount') ]) return announcement_table softlayer-python-5.9.8/SoftLayer/CLI/account/invoice_detail.py000066400000000000000000000052061415376457700244150ustar00rootroot00000000000000"""Invoice details""" # :license: MIT, see LICENSE for more details. import click from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.managers.account import AccountManager as AccountManager from SoftLayer import utils @click.command() @click.argument('identifier') @click.option('--details', is_flag=True, default=False, show_default=True, help="Shows a very detailed list of charges") @environment.pass_env def cli(env, identifier, details): """Invoice details""" manager = AccountManager(env.client) top_items = manager.get_billing_items(identifier) table = get_invoice_table(identifier, top_items, details) env.fout(table) def nice_string(ugly_string, limit=100): """Format and trims strings""" return (ugly_string[:limit] + '..') if len(ugly_string) > limit else ugly_string def get_invoice_table(identifier, top_items, details): """Formats a table for invoice top level items. :param int identifier: Invoice identifier. :param list top_items: invoiceTopLevelItems. :param bool details: To add very detailed list of charges. """ title = "Invoice %s" % identifier table = formatting.Table(["Item Id", "Category", "Description", "Single", "Monthly", "Create Date", "Location"], title=title) table.align['category'] = 'l' table.align['description'] = 'l' for item in top_items: fqdn = "%s.%s" % (item.get('hostName', ''), item.get('domainName', '')) # category id=2046, ram_usage doesn't have a name... category = utils.lookup(item, 'category', 'name') or item.get('categoryCode') description = nice_string(item.get('description')) if fqdn != '.': description = "%s (%s)" % (item.get('description'), fqdn) table.add_row([ item.get('id'), category, nice_string(description), "$%.2f" % float(item.get('oneTimeAfterTaxAmount')), "$%.2f" % float(item.get('recurringAfterTaxAmount')), utils.clean_time(item.get('createDate'), out_format="%Y-%m-%d"), utils.lookup(item, 'location', 'name') ]) if details: for child in item.get('children', []): table.add_row([ '>>>', utils.lookup(child, 'category', 'name'), nice_string(child.get('description')), "$%.2f" % float(child.get('oneTimeAfterTaxAmount')), "$%.2f" % float(child.get('recurringAfterTaxAmount')), '---', '---' ]) return table softlayer-python-5.9.8/SoftLayer/CLI/account/invoices.py000066400000000000000000000032171415376457700232560ustar00rootroot00000000000000"""Invoice listing""" # :license: MIT, see LICENSE for more details. import click from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.managers.account import AccountManager as AccountManager from SoftLayer import utils @click.command() @click.option('--limit', default=50, show_default=True, help="How many invoices to get back.") @click.option('--closed', is_flag=True, default=False, show_default=True, help="Include invoices with a CLOSED status.") @click.option('--all', 'get_all', is_flag=True, default=False, show_default=True, help="Return ALL invoices. There may be a lot of these.") @environment.pass_env def cli(env, limit, closed=False, get_all=False): """List invoices""" manager = AccountManager(env.client) invoices = manager.get_invoices(limit, closed, get_all) table = formatting.Table([ "Id", "Created", "Type", "Status", "Starting Balance", "Ending Balance", "Invoice Amount", "Items" ]) table.align['Starting Balance'] = 'l' table.align['Ending Balance'] = 'l' table.align['Invoice Amount'] = 'l' table.align['Items'] = 'l' if isinstance(invoices, dict): invoices = [invoices] for invoice in invoices: table.add_row([ invoice.get('id'), utils.clean_time(invoice.get('createDate'), out_format="%Y-%m-%d"), invoice.get('typeCode'), invoice.get('statusCode'), invoice.get('startingBalance'), invoice.get('endingBalance'), invoice.get('invoiceTotalAmount'), invoice.get('itemCount') ]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/account/item_detail.py000066400000000000000000000042061415376457700237160ustar00rootroot00000000000000"""Gets some details about a specific billing item.""" # :license: MIT, see LICENSE for more details. import click from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.managers.account import AccountManager as AccountManager from SoftLayer import utils @click.command() @click.argument('identifier') @environment.pass_env def cli(env, identifier): """Gets detailed information about a billing item.""" manager = AccountManager(env.client) item = manager.get_item_detail(identifier) env.fout(item_table(item)) def item_table(item): """Formats a table for billing items""" date_format = '%Y-%m-%d' table = formatting.Table(["Key", "Value"], title="{}".format(item.get('description', 'Billing Item'))) table.add_row(['createDate', utils.clean_time(item.get('createDate'), date_format, date_format)]) table.add_row(['cycleStartDate', utils.clean_time(item.get('cycleStartDate'), date_format, date_format)]) table.add_row(['cancellationDate', utils.clean_time(item.get('cancellationDate'), date_format, date_format)]) table.add_row(['description', item.get('description')]) table.align = 'l' fqdn = "{}.{}".format(item.get('hostName'), item.get('domain')) if fqdn != ".": table.add_row(['FQDN', fqdn]) if item.get('hourlyFlag', False): table.add_row(['hourlyRecurringFee', item.get('hourlyRecurringFee')]) table.add_row(['hoursUsed', item.get('hoursUsed')]) table.add_row(['currentHourlyCharge', item.get('currentHourlyCharge')]) else: table.add_row(['recurringFee', item.get('recurringFee')]) ordered_by = "IBM" user = utils.lookup(item, 'orderItem', 'order', 'userRecord') if user: ordered_by = "{} ({})".format(user.get('displayName'), utils.lookup(user, 'userStatus', 'name')) table.add_row(['Ordered By', ordered_by]) table.add_row(['Notes', item.get('notes')]) table.add_row(['Location', utils.lookup(item, 'location', 'name')]) if item.get('children'): for child in item.get('children'): table.add_row([child.get('categoryCode'), child.get('description')]) return table softlayer-python-5.9.8/SoftLayer/CLI/account/licenses.py000066400000000000000000000034641415376457700232500ustar00rootroot00000000000000"""Show all licenses.""" # :license: MIT, see LICENSE for more details. import click from SoftLayer import utils from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.managers import account @click.command() @environment.pass_env def cli(env): """Show all licenses.""" manager = account.AccountManager(env.client) control_panel = manager.get_active_virtual_licenses() vmwares = manager.get_active_account_licenses() table_panel = formatting.KeyValueTable(['id', 'ip_address', 'manufacturer', 'software', 'key', 'subnet', 'subnet notes'], title="Control Panel Licenses") table_vmware = formatting.KeyValueTable(['name', 'license_key', 'cpus', 'description', 'manufacturer', 'requiredUser'], title="VMware Licenses") for panel in control_panel: table_panel.add_row([panel.get('id'), panel.get('ipAddress'), utils.lookup(panel, 'softwareDescription', 'manufacturer'), utils.trim_to(utils.lookup(panel, 'softwareDescription', 'longDescription'), 40), panel.get('key'), utils.lookup(panel, 'subnet', 'broadcastAddress'), utils.lookup(panel, 'subnet', 'note')]) env.fout(table_panel) for vmware in vmwares: table_vmware.add_row([utils.lookup(vmware, 'softwareDescription', 'name'), vmware.get('key'), vmware.get('capacity'), utils.lookup(vmware, 'billingItem', 'description'), utils.lookup(vmware, 'softwareDescription', 'manufacturer'), utils.lookup(vmware, 'softwareDescription', 'requiredUser')]) env.fout(table_vmware) softlayer-python-5.9.8/SoftLayer/CLI/account/orders.py000066400000000000000000000024501415376457700227330ustar00rootroot00000000000000"""Lists account orders.""" # :license: MIT, see LICENSE for more details. import click from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.managers.account import AccountManager as AccountManager from SoftLayer import utils @click.command() @click.option('--limit', '-l', help='How many results to get in one api call', default=100, show_default=True) @environment.pass_env def cli(env, limit): """Lists account orders. Use `slcli order lookup ` to find more details about a specific order.""" manager = AccountManager(env.client) orders = manager.get_account_all_billing_orders(limit) order_table = formatting.Table(['Id', 'State', 'User', 'Date', 'Amount', 'Item'], title="orders") order_table.align = 'l' for order in orders: items = [] for item in order['items']: items.append(item['description']) create_date = utils.clean_time(order['createDate'], in_format='%Y-%m-%d', out_format='%Y-%m-%d') order_table.add_row([order['id'], order['status'], order['userRecord']['username'], create_date, order['orderTotalAmount'], utils.trim_to(' '.join(map(str, items)), 50)]) env.fout(order_table) softlayer-python-5.9.8/SoftLayer/CLI/account/summary.py000066400000000000000000000033421415376457700231330ustar00rootroot00000000000000"""Account Summary page""" # :license: MIT, see LICENSE for more details. import click from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.managers.account import AccountManager as AccountManager from SoftLayer import utils @click.command() @environment.pass_env def cli(env): """Prints some various bits of information about an account""" manager = AccountManager(env.client) summary = manager.get_summary() env.fout(get_snapshot_table(summary)) def get_snapshot_table(account): """Generates a table for printing account summary data""" table = formatting.KeyValueTable(["Name", "Value"], title="Account Snapshot") table.align['Name'] = 'r' table.align['Value'] = 'l' table.add_row(['Company Name', account.get('companyName', '-')]) table.add_row(['Balance', utils.lookup(account, 'pendingInvoice', 'startingBalance')]) table.add_row(['Upcoming Invoice', utils.lookup(account, 'pendingInvoice', 'invoiceTotalAmount')]) table.add_row(['Image Templates', account.get('blockDeviceTemplateGroupCount', '-')]) table.add_row(['Dedicated Hosts', account.get('dedicatedHostCount', '-')]) table.add_row(['Hardware', account.get('hardwareCount', '-')]) table.add_row(['Virtual Guests', account.get('virtualGuestCount', '-')]) table.add_row(['Domains', account.get('domainCount', '-')]) table.add_row(['Network Storage Volumes', account.get('networkStorageCount', '-')]) table.add_row(['Open Tickets', account.get('openTicketCount', '-')]) table.add_row(['Network Vlans', account.get('networkVlanCount', '-')]) table.add_row(['Subnets', account.get('subnetCount', '-')]) table.add_row(['Users', account.get('userCount', '-')]) return table softlayer-python-5.9.8/SoftLayer/CLI/autoscale/000077500000000000000000000000001415376457700214065ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/autoscale/__init__.py000066400000000000000000000000211415376457700235100ustar00rootroot00000000000000"""Autoscale""" softlayer-python-5.9.8/SoftLayer/CLI/autoscale/detail.py000066400000000000000000000075031415376457700232270ustar00rootroot00000000000000"""Get details of an Autoscale groups.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.managers.autoscale import AutoScaleManager from SoftLayer import utils @click.command() @click.argument('identifier') @environment.pass_env def cli(env, identifier): """Get details of an Autoscale groups.""" autoscale = AutoScaleManager(env.client) group = autoscale.details(identifier) # Group Config Table table = formatting.KeyValueTable(["Group", "Value"]) table.align['Group'] = 'l' table.align['Value'] = 'l' table.add_row(['Id', group.get('id')]) # Ideally we would use regionalGroup->preferredDatacenter, but that generates an error. table.add_row(['Datacenter', group['regionalGroup']['locations'][0]['longName']]) table.add_row(['Termination', utils.lookup(group, 'terminationPolicy', 'name')]) table.add_row(['Minimum Members', group.get('minimumMemberCount')]) table.add_row(['Maximum Members', group.get('maximumMemberCount')]) table.add_row(['Current Members', group.get('virtualGuestMemberCount')]) table.add_row(['Cooldown', "{} seconds".format(group.get('cooldown'))]) table.add_row(['Last Action', utils.clean_time(group.get('lastActionDate'))]) for network in group.get('networkVlans', []): network_type = utils.lookup(network, 'networkVlan', 'networkSpace') router = utils.lookup(network, 'networkVlan', 'primaryRouter', 'hostname') vlan_number = utils.lookup(network, 'networkVlan', 'vlanNumber') vlan_name = "{}.{}".format(router, vlan_number) table.add_row([network_type, vlan_name]) env.fout(table) # Template Config Table config_table = formatting.KeyValueTable(["Template", "Value"]) config_table.align['Template'] = 'l' config_table.align['Value'] = 'l' template = group.get('virtualGuestMemberTemplate') config_table.add_row(['Hostname', template.get('hostname')]) config_table.add_row(['Domain', template.get('domain')]) config_table.add_row(['Core', template.get('startCpus')]) config_table.add_row(['Ram', template.get('maxMemory')]) network = template.get('networkComponents') config_table.add_row(['Network', network[0]['maxSpeed'] if network else 'Default']) ssh_keys = template.get('sshKeys', []) ssh_manager = SoftLayer.SshKeyManager(env.client) for key in ssh_keys: # Label isn't included when retrieved from the AutoScale group... ssh_key = ssh_manager.get_key(key.get('id')) config_table.add_row(['SSH Key {}'.format(ssh_key.get('id')), ssh_key.get('label')]) disks = template.get('blockDevices', []) disk_type = "Local" if template.get('localDiskFlag') else "SAN" for disk in disks: disk_image = disk.get('diskImage') config_table.add_row(['{} Disk {}'.format(disk_type, disk.get('device')), disk_image.get('capacity')]) config_table.add_row(['OS', template.get('operatingSystemReferenceCode')]) config_table.add_row(['Post Install', template.get('postInstallScriptUri') or 'None']) env.fout(config_table) # Policy Config Table policy_table = formatting.KeyValueTable(["Policy", "Cooldown"]) policies = group.get('policies', []) for policy in policies: policy_table.add_row([policy.get('name'), policy.get('cooldown') or group.get('cooldown')]) env.fout(policy_table) # Active Guests member_table = formatting.Table(['Id', 'Hostname', 'Created'], title="Active Guests") guests = group.get('virtualGuestMembers', []) for guest in guests: real_guest = guest.get('virtualGuest') member_table.add_row([ real_guest.get('id'), real_guest.get('hostname'), utils.clean_time(real_guest.get('provisionDate')) ]) env.fout(member_table) softlayer-python-5.9.8/SoftLayer/CLI/autoscale/edit.py000066400000000000000000000045761415376457700227210ustar00rootroot00000000000000"""Edits an Autoscale group.""" # :license: MIT, see LICENSE for more details. import click from SoftLayer.CLI import environment from SoftLayer.managers.autoscale import AutoScaleManager @click.command() @click.argument('identifier') @click.option('--name', help="Scale group's name.") @click.option('--min', 'minimum', type=click.INT, help="Set the minimum number of guests") @click.option('--max', 'maximum', type=click.INT, help="Set the maximum number of guests") @click.option('--userdata', help="User defined metadata string") @click.option('--userfile', '-F', help="Read userdata from a file", type=click.Path(exists=True, readable=True, resolve_path=True)) @click.option('--cpu', type=click.INT, help="Number of CPUs for new guests (existing not effected") @click.option('--memory', type=click.INT, help="RAM in MB or GB for new guests (existing not effected") @environment.pass_env def cli(env, identifier, name, minimum, maximum, userdata, userfile, cpu, memory): """Edits an Autoscale group.""" template = {} autoscale = AutoScaleManager(env.client) group = autoscale.details(identifier) template['name'] = name template['minimumMemberCount'] = minimum template['maximumMemberCount'] = maximum virt_template = {} if userdata: virt_template['userData'] = [{"value": userdata}] elif userfile: with open(userfile, 'r', encoding="utf-8") as userfile_obj: virt_template['userData'] = [{"value": userfile_obj.read()}] virt_template['startCpus'] = cpu virt_template['maxMemory'] = memory # Remove any entries that are `None` as the API will complain about them. template['virtualGuestMemberTemplate'] = clean_dict(virt_template) clean_template = clean_dict(template) # If there are any values edited in the template, we need to get the OLD template values and replace them. if template['virtualGuestMemberTemplate']: # Update old template with new values for key, value in clean_template['virtualGuestMemberTemplate'].items(): group['virtualGuestMemberTemplate'][key] = value clean_template['virtualGuestMemberTemplate'] = group['virtualGuestMemberTemplate'] autoscale.edit(identifier, clean_template) click.echo("Done") def clean_dict(dictionary): """Removes any `None` entires from the dictionary""" return {k: v for k, v in dictionary.items() if v} softlayer-python-5.9.8/SoftLayer/CLI/autoscale/list.py000066400000000000000000000015351415376457700227370ustar00rootroot00000000000000"""List Autoscale groups.""" # :license: MIT, see LICENSE for more details. import click from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.managers.autoscale import AutoScaleManager from SoftLayer import utils @click.command() @environment.pass_env def cli(env): """List AutoScale Groups.""" autoscale = AutoScaleManager(env.client) groups = autoscale.list() table = formatting.Table(["Id", "Name", "Status", "Min/Max", "Running"]) table.align['Name'] = 'l' for group in groups: status = utils.lookup(group, 'status', 'name') min_max = "{}/{}".format(group.get('minimumMemberCount'), group.get('maximumMemberCount')) table.add_row([ group.get('id'), group.get('name'), status, min_max, group.get('virtualGuestMemberCount') ]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/autoscale/logs.py000066400000000000000000000023271415376457700227300ustar00rootroot00000000000000"""Retreive logs for an autoscale group""" # :license: MIT, see LICENSE for more details. import click from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.managers.autoscale import AutoScaleManager from SoftLayer import utils @click.command() @click.argument('identifier') @click.option('--date-min', '-d', 'date_min', type=click.DateTime(formats=["%Y-%m-%d", "%m/%d/%Y"]), help='Earliest date to retreive logs for.') @environment.pass_env def cli(env, identifier, date_min): """Retreive logs for an autoscale group""" autoscale = AutoScaleManager(env.client) mask = "mask[id,createDate,description]" object_filter = {} if date_min: object_filter['logs'] = { 'createDate': { 'operation': 'greaterThanDate', 'options': [{'name': 'date', 'value': [date_min.strftime("%m/%d/%Y")]}] } } logs = autoscale.get_logs(identifier, mask=mask, object_filter=object_filter) table = formatting.Table(['Date', 'Entry'], title="Logs") table.align['Entry'] = 'l' for log in logs: table.add_row([utils.clean_time(log.get('createDate')), log.get('description')]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/autoscale/scale.py000066400000000000000000000042211415376457700230460ustar00rootroot00000000000000"""Scales an Autoscale group""" # :license: MIT, see LICENSE for more details. import click from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.managers.autoscale import AutoScaleManager from SoftLayer import utils @click.command() @click.argument('identifier') @click.option('--up/--down', 'scale_up', is_flag=True, default=True, help="'--up' adds guests, '--down' removes guests.") @click.option('--by/--to', 'scale_by', is_flag=True, required=True, help="'--by' will add/remove the specified number of guests." " '--to' will add/remove a number of guests to get the group's guest count to the specified number.") @click.option('--amount', required=True, type=click.INT, help="Number of guests for the scale action.") @environment.pass_env def cli(env, identifier, scale_up, scale_by, amount): """Scales an Autoscale group. Bypasses a scale group's cooldown period.""" autoscale = AutoScaleManager(env.client) # Scale By, and go down, need to use negative amount if not scale_up and scale_by: amount = amount * -1 result = [] if scale_by: click.secho("Scaling group {} by {}".format(identifier, amount), fg='green') result = autoscale.scale(identifier, amount) else: click.secho("Scaling group {} to {}".format(identifier, amount), fg='green') result = autoscale.scale_to(identifier, amount) try: # Check if the first guest has a cancellation date, assume we are removing guests if it is. cancel_date = result[0]['virtualGuest']['billingItem']['cancellationDate'] or False except (IndexError, KeyError, TypeError): cancel_date = False if cancel_date: member_table = formatting.Table(['Id', 'Hostname', 'Created'], title="Cancelled Guests") else: member_table = formatting.Table(['Id', 'Hostname', 'Created'], title="Added Guests") for guest in result: real_guest = guest.get('virtualGuest') member_table.add_row([ guest.get('id'), real_guest.get('hostname'), utils.clean_time(real_guest.get('createDate')) ]) env.fout(member_table) softlayer-python-5.9.8/SoftLayer/CLI/autoscale/tag.py000066400000000000000000000022251415376457700225340ustar00rootroot00000000000000"""Tags all guests in an autoscale group.""" # :license: MIT, see LICENSE for more details. import click from SoftLayer.CLI import environment from SoftLayer.managers.autoscale import AutoScaleManager from SoftLayer.managers.vs import VSManager @click.command() @click.argument('identifier') @click.option('--tags', '-g', help="Tags to set for each guest in this group. Existing tags are overwritten. " "An empty string will remove all tags") @environment.pass_env def cli(env, identifier, tags): """Tags all guests in an autoscale group. --tags "Use, quotes, if you, want whitespace" --tags Otherwise,Just,commas """ autoscale = AutoScaleManager(env.client) vsmanager = VSManager(env.client) mask = "mask[id,virtualGuestId,virtualGuest[tagReferences,id,hostname]]" guests = autoscale.get_virtual_guests(identifier, mask=mask) click.echo("New Tags: {}".format(tags)) for guest in guests: real_guest = guest.get('virtualGuest') click.echo("Setting tags for {}".format(real_guest.get('hostname'))) vsmanager.set_tags(tags, real_guest.get('id'),) click.echo("Done") softlayer-python-5.9.8/SoftLayer/CLI/block/000077500000000000000000000000001415376457700205205ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/block/__init__.py000066400000000000000000000000251415376457700226260ustar00rootroot00000000000000"""Block Storage.""" softlayer-python-5.9.8/SoftLayer/CLI/block/access/000077500000000000000000000000001415376457700217615ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/block/access/__init__.py000066400000000000000000000000441415376457700240700ustar00rootroot00000000000000"""Block Storage Access Control.""" softlayer-python-5.9.8/SoftLayer/CLI/block/access/authorize.py000066400000000000000000000033511415376457700243470ustar00rootroot00000000000000"""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') 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.9.8/SoftLayer/CLI/block/access/list.py000066400000000000000000000031021415376457700233020ustar00rootroot00000000000000"""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 helpers 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) resolved_id = helpers.resolve_id(block_manager.resolve_ids, volume_id, 'Volume Id') access_list = block_manager.get_block_volume_access_list( volume_id=resolved_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.9.8/SoftLayer/CLI/block/access/password.py000066400000000000000000000015211415376457700241740ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/block/access/revoke.py000066400000000000000000000033541415376457700236330ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/block/cancel.py000066400000000000000000000025361415376457700223250ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/block/convert.py000066400000000000000000000007521415376457700225560ustar00rootroot00000000000000"""Convert a dependent duplicate volume to an independent volume.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment @click.command() @click.argument('volume_id') @environment.pass_env def cli(env, volume_id): """Convert a dependent duplicate volume to an independent volume.""" block_manager = SoftLayer.BlockStorageManager(env.client) resp = block_manager.convert_dep_dupe(volume_id) click.echo(resp) softlayer-python-5.9.8/SoftLayer/CLI/block/count.py000066400000000000000000000030341415376457700222220ustar00rootroot00000000000000"""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 = {} 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(): # pylint: disable=consider-iterating-dictionary datacenters[datacenter_name] = 1 else: datacenters[datacenter_name] += 1 table = formatting.KeyValueTable(DEFAULT_COLUMNS) table.sortby = sortby for key, value in datacenters.items(): table.add_row([key, value]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/block/detail.py000066400000000000000000000113201415376457700223310ustar00rootroot00000000000000"""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.CLI import helpers 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_id = helpers.resolve_id(block_manager.resolve_ids, volume_id, 'Block Volume') block_volume = block_manager.get_block_volume_details(block_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', float(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]) notes = '{}'.format(block_volume.get('notes', '')) table.add_row(['Notes', notes]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/block/duplicate.py000066400000000000000000000106631415376457700230520ustar00rootroot00000000000000"""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)") @click.option('--dependent-duplicate', type=click.BOOL, default=False, show_default=True, help='Whether or not this duplicate will be a dependent duplicate ' 'of the origin volume.') @environment.pass_env def cli(env, origin_volume_id, origin_snapshot_id, duplicate_size, duplicate_iops, duplicate_tier, duplicate_snapshot_size, billing, dependent_duplicate): """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, dependent_duplicate=dependent_duplicate ) 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.9.8/SoftLayer/CLI/block/limit.py000066400000000000000000000020061415376457700222060ustar00rootroot00000000000000"""List number of block storage volumes limit 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', 'MaximumAvailableCount', 'ProvisionedCount' ] @click.command() @click.option('--sortby', help='Column to sort by', default='Datacenter') @environment.pass_env def cli(env, sortby): """List number of block storage volumes limit per datacenter.""" block_manager = SoftLayer.BlockStorageManager(env.client) block_volumes = block_manager.list_block_volume_limit() table = formatting.KeyValueTable(DEFAULT_COLUMNS) table.sortby = sortby for volume in block_volumes: datacenter_name = volume['datacenterName'] maximum_available_count = volume['maximumAvailableCount'] provisioned_count = volume['provisionedCount'] table.add_row([datacenter_name, maximum_available_count, provisioned_count]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/block/list.py000066400000000000000000000064141415376457700220520ustar00rootroot00000000000000"""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 storage_utils 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('IOPs', ('provisionedIops',), mask="provisionedIops"), 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')), column_helper.Column('notes', ('notes',), mask="notes"), ] DEFAULT_COLUMNS = [ 'id', 'username', 'datacenter', 'storage_type', 'capacity_gb', 'bytes_used', 'IOPs', 'ip_addr', 'lunId', 'active_transactions', 'rep_partner_count', 'notes' ] @click.command() @click.option('--username', '-u', help='Volume username') @click.option('--datacenter', '-d', help='Datacenter shortname') @click.option('--order', '-o', type=int, help='Filter by ID of the order that purchased the block storage') @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, order): """List block storage.""" block_manager = SoftLayer.BlockStorageManager(env.client) block_volumes = block_manager.list_block_volumes(datacenter=datacenter, username=username, storage_type=storage_type, order=order, mask=columns.mask()) table = storage_utils.build_output_table(env, block_volumes, columns, sortby) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/block/lun.py000066400000000000000000000022011415376457700216630ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/block/modify.py000066400000000000000000000052421415376457700223640ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/block/order.py000066400000000000000000000122111415376457700222020ustar00rootroot00000000000000"""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.', required=True) @click.option('--iops', type=int, help="""Performance Storage IOPs. Options vary based on storage size. [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\']. enterprise and performance are depreciated""", 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. Valid size and iops options can be found here: https://cloud.ibm.com/docs/BlockStorage/index.html#provisioning-considerations """ block_manager = SoftLayer.BlockStorageManager(env.client) storage_type = storage_type.lower() hourly_billing_flag = False if billing.lower() == "hourly": hourly_billing_flag = True if service_offering != 'storage_as_a_service': click.secho('{} is a legacy storage offering'.format(service_offering), fg='red') if hourly_billing_flag: 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 service_offering == 'performance' and snapshot_size is not None: raise exceptions.CLIAbort( '--snapshot-size is not available for performance service offerings. ' 'Use --service-offering storage_as_a_service' ) 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']) click.echo( '\nYou may run "slcli block volume-list --order {0}" to find this block volume after it ' 'is ready.'.format(order['placedOrder']['id'])) else: click.echo("Order could not be placed! Please verify your options and try again.") softlayer-python-5.9.8/SoftLayer/CLI/block/refresh.py000066400000000000000000000010311415376457700225230ustar00rootroot00000000000000"""Refresh a duplicate volume with a snapshot from its parent.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment @click.command() @click.argument('volume_id') @click.argument('snapshot_id') @environment.pass_env def cli(env, volume_id, snapshot_id): """Refresh a duplicate volume with a snapshot from its parent.""" block_manager = SoftLayer.BlockStorageManager(env.client) resp = block_manager.refresh_dupe(volume_id, snapshot_id) click.echo(resp) softlayer-python-5.9.8/SoftLayer/CLI/block/replication/000077500000000000000000000000001415376457700230315ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/block/replication/__init__.py000066400000000000000000000000511415376457700251360ustar00rootroot00000000000000"""Block Storage Replication Control.""" softlayer-python-5.9.8/SoftLayer/CLI/block/replication/disaster_recovery_failover.py000066400000000000000000000036421415376457700310330ustar00rootroot00000000000000"""Failover an inaccessible block volume to its available replicant 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(epilog="""Failover an inaccessible block volume to its available replicant volume. If a volume (with replication) becomes inaccessible due to a disaster event, this method can be used to immediately failover to an available replica in another location. This method does not allow for failback via API. After using this method, to failback to the original volume, please open a support ticket. If you wish to test failover, please use replica-failover.""") @click.argument('volume-id') @click.option('--replicant-id', help="ID of the replicant volume") @environment.pass_env def cli(env, volume_id, replicant_id): """Failover an inaccessible block volume to its available replicant volume.""" block_storage_manager = SoftLayer.BlockStorageManager(env.client) click.secho("""WARNING : Failover an inaccessible block volume to its available replicant volume.""" """If a volume (with replication) becomes inaccessible due to a disaster event,""" """this method can be used to immediately failover to an available replica in another location.""" """This method does not allow for failback via the API.""" """To failback to the original volume after using this method, open a support ticket.""" """If you wish to test failover, use replica-failover instead.""", fg='red') if not formatting.confirm('Are you sure you want to continue?'): raise exceptions.CLIAbort('Aborted.') block_storage_manager.disaster_recovery_failover_to_replicant( volume_id, replicant_id ) click.echo("Disaster Recovery Failover to replicant is now in progress.") softlayer-python-5.9.8/SoftLayer/CLI/block/replication/failback.py000066400000000000000000000011521415376457700251360ustar00rootroot00000000000000"""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') @environment.pass_env def cli(env, volume_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) if success: click.echo("Failback from replicant is now in progress.") else: click.echo("Failback operation could not be initiated.") softlayer-python-5.9.8/SoftLayer/CLI/block/replication/failover.py000066400000000000000000000013271415376457700252150ustar00rootroot00000000000000"""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") @environment.pass_env def cli(env, volume_id, replicant_id): """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 ) if success: click.echo("Failover to replicant is now in progress.") else: click.echo("Failover operation could not be initiated.") softlayer-python-5.9.8/SoftLayer/CLI/block/replication/locations.py000066400000000000000000000031051415376457700253750ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/block/replication/order.py000066400000000000000000000047161415376457700245260ustar00rootroot00000000000000"""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 from SoftLayer.CLI import helpers from SoftLayer import utils 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, ' '(HOURLY | DAILY | WEEKLY)', required=True, type=click.Choice(['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) block_volume_id = helpers.resolve_id(block_manager.resolve_ids, volume_id, 'Block Volume') if tier is not None: tier = float(tier) try: order = block_manager.order_replicant_volume( block_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( utils.lookup(order, 'placedOrder', 'id'))) for item in utils.lookup(order, 'placedOrder', 'items'): click.echo(" > %s" % item.get('description')) else: click.echo("Order could not be placed! Please verify your options " + "and try again.") softlayer-python-5.9.8/SoftLayer/CLI/block/replication/partners.py000066400000000000000000000027021415376457700252420ustar00rootroot00000000000000"""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 from SoftLayer.CLI import storage_utils COLUMNS = storage_utils.REPLICATION_PARTNER_COLUMNS DEFAULT_COLUMNS = storage_utils.REPLICATION_PARTNER_DEFAULT @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.9.8/SoftLayer/CLI/block/set_note.py000066400000000000000000000015721415376457700227170ustar00rootroot00000000000000"""Set note for 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 helpers @click.command() @click.argument('volume-id') @click.option('--note', '-n', type=str, required=True, help='Public notes related to a Storage volume') @environment.pass_env def cli(env, volume_id, note): """Set note for an existing block storage volume.""" block_manager = SoftLayer.BlockStorageManager(env.client) block_volume_id = helpers.resolve_id(block_manager.resolve_ids, volume_id, 'Block Volume') result = block_manager.volume_set_note(block_volume_id, note) if result: click.echo("Set note successfully!") else: click.echo("Note could not be set! Please verify your options and try again.") softlayer-python-5.9.8/SoftLayer/CLI/block/snapshot/000077500000000000000000000000001415376457700223575ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/block/snapshot/__init__.py000066400000000000000000000000461415376457700244700ustar00rootroot00000000000000"""Block Storage Snapshot Control.""" softlayer-python-5.9.8/SoftLayer/CLI/block/snapshot/cancel.py000066400000000000000000000025571415376457700241670ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/block/snapshot/create.py000066400000000000000000000014661415376457700242030ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/block/snapshot/delete.py000066400000000000000000000007551415376457700242020ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/block/snapshot/disable.py000066400000000000000000000017221415376457700243360ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/block/snapshot/enable.py000066400000000000000000000044651415376457700241700ustar00rootroot00000000000000# 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.9.8/SoftLayer/CLI/block/snapshot/get_notify_status.py000066400000000000000000000014031415376457700265010ustar00rootroot00000000000000"""Get the snapshots space usage threshold warning flag setting for specific volume""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment @click.command() @click.argument('volume_id') @environment.pass_env def cli(env, volume_id): """Get snapshots space usage threshold warning flag setting for a given volume""" block_manager = SoftLayer.BlockStorageManager(env.client) enabled = block_manager.get_volume_snapshot_notification_status(volume_id) if enabled == 0: click.echo("Disabled: Snapshots space usage threshold is disabled for volume {}".format(volume_id)) else: click.echo("Enabled: Snapshots space usage threshold is enabled for volume {}".format(volume_id)) softlayer-python-5.9.8/SoftLayer/CLI/block/snapshot/list.py000066400000000000000000000032771415376457700237150ustar00rootroot00000000000000"""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 from SoftLayer.CLI import helpers 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) resolved_id = helpers.resolve_id(block_manager.resolve_ids, volume_id, 'Volume Id') snapshots = block_manager.get_block_volume_snapshot_list( resolved_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.9.8/SoftLayer/CLI/block/snapshot/order.py000066400000000000000000000035011415376457700240430ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/block/snapshot/restore.py000066400000000000000000000013471415376457700244210ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/block/snapshot/schedule_list.py000066400000000000000000000045041415376457700255630ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/block/snapshot/set_notify_status.py000066400000000000000000000016211415376457700265170ustar00rootroot00000000000000"""Disable/Enable snapshots space usage threshold warning for a specific 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( '--enable/--disable', default=True, help=""" Enable/Disable snapshot notification. Use `slcli block snapshot-set-notification volumeId --enable` to enable """, required=True) @environment.pass_env def cli(env, volume_id, enable): """Enables/Disables snapshot space usage threshold warning for a given volume""" block_manager = SoftLayer.BlockStorageManager(env.client) status = block_manager.set_volume_snapshot_notification(volume_id, enable) if status: click.echo( 'Snapshots space usage threshold warning notification has bee set to %s for volume %s' % (enable, volume_id)) softlayer-python-5.9.8/SoftLayer/CLI/block/subnets/000077500000000000000000000000001415376457700222035ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/block/subnets/__init__.py000066400000000000000000000000451415376457700243130ustar00rootroot00000000000000"""Block Storage Subnets Control.""" softlayer-python-5.9.8/SoftLayer/CLI/block/subnets/assign.py000066400000000000000000000026451415376457700240500ustar00rootroot00000000000000"""Assign block storage subnets to the given host id.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment @click.command() @click.argument('access_id', type=int) @click.option('--subnet-id', multiple=True, type=int, help="ID of the subnets to assign; e.g.: --subnet-id 1234") @environment.pass_env def cli(env, access_id, subnet_id): """Assign block storage subnets to the given host id. access_id is the host_id obtained by: slcli block access-list SoftLayer_Account::iscsiisolationdisabled must be False to use this command """ try: subnet_ids = list(subnet_id) block_manager = SoftLayer.BlockStorageManager(env.client) assigned_subnets = block_manager.assign_subnets_to_acl(access_id, subnet_ids) for subnet in assigned_subnets: message = "Successfully assigned subnet id: {} to allowed host id: {}".format(subnet, access_id) click.echo(message) failed_to_assign_subnets = list(set(subnet_ids) - set(assigned_subnets)) for subnet in failed_to_assign_subnets: message = "Failed to assign subnet id: {} to allowed host id: {}".format(subnet, access_id) click.echo(message) except SoftLayer.SoftLayerAPIError as ex: message = "Unable to assign subnets.\nReason: {}".format(ex.faultString) click.echo(message) softlayer-python-5.9.8/SoftLayer/CLI/block/subnets/list.py000066400000000000000000000025151415376457700235330ustar00rootroot00000000000000"""List block storage assigned subnets for the given host id.""" # :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 COLUMNS = [ 'id', 'createDate', 'networkIdentifier', 'cidr' ] @click.command() @click.argument('access_id', type=int) @environment.pass_env def cli(env, access_id): """List block storage assigned subnets for the given host id. access_id is the host_id obtained by: slcli block access-list """ try: block_manager = SoftLayer.BlockStorageManager(env.client) resolved_id = helpers.resolve_id(block_manager.resolve_ids, access_id, 'Volume Id') subnets = block_manager.get_subnets_in_acl(resolved_id) table = formatting.Table(COLUMNS) for subnet in subnets: row = ["{0}".format(subnet['id']), "{0}".format(subnet['createDate']), "{0}".format(subnet['networkIdentifier']), "{0}".format(subnet['cidr'])] table.add_row(row) env.fout(table) except SoftLayer.SoftLayerAPIError as ex: message = "Unable to list assigned subnets for access-id: {}.\nReason: {}".format(access_id, ex.faultString) click.echo(message) softlayer-python-5.9.8/SoftLayer/CLI/block/subnets/remove.py000066400000000000000000000026471415376457700240630ustar00rootroot00000000000000"""Remove block storage subnets for the given host id.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment @click.command() @click.argument('access_id', type=int) @click.option('--subnet-id', multiple=True, type=int, help="ID of the subnets to remove; e.g.: --subnet-id 1234") @environment.pass_env def cli(env, access_id, subnet_id): """Remove block storage subnets for the given host id. access_id is the host_id obtained by: slcli block access-list SoftLayer_Account::iscsiisolationdisabled must be False to use this command """ try: subnet_ids = list(subnet_id) block_manager = SoftLayer.BlockStorageManager(env.client) removed_subnets = block_manager.remove_subnets_from_acl(access_id, subnet_ids) for subnet in removed_subnets: message = "Successfully removed subnet id: {} for allowed host id: {}".format(subnet, access_id) click.echo(message) failed_to_remove_subnets = list(set(subnet_ids) - set(removed_subnets)) for subnet in failed_to_remove_subnets: message = "Failed to remove subnet id: {} for allowed host id: {}".format(subnet, access_id) click.echo(message) except SoftLayer.SoftLayerAPIError as ex: message = "Unable to remove subnets.\nReason: {}".format(ex.faultString) click.echo(message) softlayer-python-5.9.8/SoftLayer/CLI/call_api.py000066400000000000000000000155441415376457700215550ustar00rootroot00000000000000"""Call arbitrary API endpoints.""" import json 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 def _validate_filter(ctx, param, value): # pylint: disable=unused-argument """Validates a JSON style object filter""" _filter = None if value: try: _filter = json.loads(value) if not isinstance(_filter, dict): raise exceptions.CLIAbort("\"{}\" should be a JSON object, but is a {} instead.". format(_filter, type(_filter))) except json.JSONDecodeError as error: raise exceptions.CLIAbort("\"{}\" is not valid JSON. {}".format(value, error)) return _filter def _validate_parameters(ctx, param, value): # pylint: disable=unused-argument """Checks if value is a JSON string, and converts it to a datastructure if that is true""" validated_values = [] for parameter in value: if isinstance(parameter, str): # looks like a JSON string... if '{' in parameter or '[' in parameter: try: parameter = json.loads(parameter) except json.JSONDecodeError as error: click.secho("{} looked like json, but was invalid, passing to API as is. {}". format(parameter, error), fg='red') validated_values.append(parameter) return validated_values @click.command('call', short_help="Call arbitrary API endpoints.") @click.argument('service') @click.argument('method') @click.argument('parameters', nargs=-1, callback=_validate_parameters) @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 should use --json-filter.") @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('--orderBy', type=click.STRING, help="To set the sort direction, ASC or DESC can be provided." "This should be of the form: '--orderBy nested.property' default DESC or " "'--orderBy nested.property=ASC', e.g. " " --orderBy subnets.id default DESC" " --orderBy subnets.id=ASC") @click.option('--output-python / --no-output-python', help="Show python example code instead of executing the call") @click.option('--json-filter', callback=_validate_filter, help="A JSON string to be passed in as the object filter to the API call. " "Remember to use double quotes (\") for variable names. Can NOT be used with --filter. " "Dont use whitespace outside of strings, or the slcli might have trouble parsing it.") @environment.pass_env def cli(env, service, method, parameters, _id, _filters, mask, limit, offset, orderby=None, output_python=False, json_filter=None): """Call arbitrary API endpoints with the given SERVICE and METHOD. For parameters that require a datatype, use a JSON string for that parameter. Example:: 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' slcli call-api Account getVirtualGuests \\ --json-filter '{"virtualGuests":{"hostname":{"operation":"^= test"}}}' --limit=10 slcli -v call-api SoftLayer_User_Customer addBulkPortalPermission --id=1234567 \\ '[{"keyName": "NETWORK_MESSAGE_DELIVERY_MANAGE"}]' slcli call-api Account getVirtualGuests \\ --orderBy virttualguests.id=ASC """ if _filters and json_filter: raise exceptions.CLIAbort("--filter and --json-filter cannot be used together.") object_filter = _build_filters(_filters) if orderby: orderby = utils.build_filter_orderby(orderby) object_filter = utils.dict_merge(object_filter, orderby) if json_filter: object_filter = utils.dict_merge(json_filter, object_filter) args = [service, method] + list(parameters) kwargs = { 'id': _id, 'filter': object_filter, '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.9.8/SoftLayer/CLI/cdn/000077500000000000000000000000001415376457700201725ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/cdn/__init__.py000066400000000000000000000001171415376457700223020ustar00rootroot00000000000000"""Content Delivery Network.""" # :license: MIT, see LICENSE for more details. softlayer-python-5.9.8/SoftLayer/CLI/cdn/detail.py000066400000000000000000000030521415376457700220060ustar00rootroot00000000000000"""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('unique_id') @click.option('--history', default=30, type=click.IntRange(1, 89), help='Bandwidth, Hits, Ratio counted over history number of days ago. 89 is the maximum. ') @environment.pass_env def cli(env, unique_id, history): """Detail a CDN Account.""" manager = SoftLayer.CDNManager(env.client) cdn_mapping = manager.get_cdn(unique_id) cdn_metrics = manager.get_usage_metrics(unique_id, history=history) # usage metrics total_bandwidth = "%s GB" % cdn_metrics['totals'][0] total_hits = cdn_metrics['totals'][1] hit_ratio = "%s %%" % cdn_metrics['totals'][2] table = formatting.KeyValueTable(['name', 'value']) table.align['name'] = 'r' table.align['value'] = 'l' table.add_row(['unique_id', cdn_mapping['uniqueId']]) table.add_row(['hostname', cdn_mapping['domain']]) table.add_row(['protocol', cdn_mapping['protocol']]) table.add_row(['origin', cdn_mapping['originHost']]) table.add_row(['origin_type', cdn_mapping['originType']]) table.add_row(['path', cdn_mapping['path']]) table.add_row(['provider', cdn_mapping['vendorName']]) table.add_row(['status', cdn_mapping['status']]) table.add_row(['total_bandwidth', total_bandwidth]) table.add_row(['total_hits', total_hits]) table.add_row(['hit_radio', hit_ratio]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/cdn/edit.py000066400000000000000000000065641415376457700215040ustar00rootroot00000000000000"""Edit a CDN Account.""" # :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('--header', '-H', type=click.STRING, help="Host header." ) @click.option('--http-port', '-t', type=click.INT, help="HTTP port." ) @click.option('--origin', '-o', type=click.STRING, help="Origin server address." ) @click.option('--respect-headers', '-r', type=click.Choice(['1', '0']), help="Respect headers. The value 1 is On and 0 is Off." ) @click.option('--cache', '-c', multiple=True, type=str, help="Cache key optimization. These are the valid options to choose: 'include-all', 'ignore-all', " "'include-specified', 'ignore-specified'. If you select 'include-specified' or 'ignore-specified' " "please add a description too using again --cache, " "e.g --cache=include-specified --cache=description." ) @click.option('--performance-configuration', '-p', type=click.Choice(['General web delivery', 'Large file optimization', 'Video on demand optimization']), help="Optimize for, General web delivery', 'Large file optimization', 'Video on demand optimization', " "the Dynamic content acceleration option is not added because this has a special configuration." ) @environment.pass_env def cli(env, identifier, header, http_port, origin, respect_headers, cache, performance_configuration): """Edit a CDN Account. Note: You can use the hostname or uniqueId as IDENTIFIER. """ manager = SoftLayer.CDNManager(env.client) cdn_id = helpers.resolve_id(manager.resolve_ids, identifier, 'CDN') cache_result = {} if cache: if len(cache) > 1: cache_result['cacheKeyQueryRule'] = cache[0] cache_result['description'] = cache[1] else: cache_result['cacheKeyQueryRule'] = cache[0] cdn_result = manager.edit(cdn_id, header=header, http_port=http_port, origin=origin, respect_headers=respect_headers, cache=cache_result, performance_configuration=performance_configuration) table = formatting.KeyValueTable(['name', 'value']) table.align['name'] = 'r' table.align['value'] = 'l' for cdn in cdn_result: table.add_row(['Create Date', cdn.get('createDate')]) table.add_row(['Header', cdn.get('header')]) table.add_row(['Http Port', cdn.get('httpPort')]) table.add_row(['Origin Type', cdn.get('originType')]) table.add_row(['Performance Configuration', cdn.get('performanceConfiguration')]) table.add_row(['Protocol', cdn.get('protocol')]) table.add_row(['Respect Headers', cdn.get('respectHeaders')]) table.add_row(['Unique Id', cdn.get('uniqueId')]) table.add_row(['Vendor Name', cdn.get('vendorName')]) table.add_row(['Cache key optimization', cdn.get('cacheKeyQueryRule')]) table.add_row(['cname', cdn.get('cname')]) table.add_row(['Origin server address', cdn.get('originHost')]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/cdn/list.py000066400000000000000000000023221415376457700215160ustar00rootroot00000000000000"""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(['unique_id', 'domain', 'origin', 'vendor', 'cname', 'status'])) @environment.pass_env def cli(env, sortby): """List all CDN accounts.""" manager = SoftLayer.CDNManager(env.client) accounts = manager.list_cdn() table = formatting.Table(['unique_id', 'domain', 'origin', 'vendor', 'cname', 'status']) for account in accounts: table.add_row([ account['uniqueId'], account['domain'], account['originHost'], account['vendorName'], account['cname'], account['status'] ]) table.sortby = sortby env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/cdn/origin_add.py000066400000000000000000000064621415376457700226530ustar00rootroot00000000000000"""Create an origin pull mapping.""" # :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('unique_id') @click.argument('origin') @click.argument('path') @click.option('--origin-type', '-t', type=click.Choice(['server', 'storage']), help='The origin type.', default='server', show_default=True) @click.option('--header', '-H', type=click.STRING, help='The host header to communicate with the origin.') @click.option('--bucket-name', '-b', type=click.STRING, help="The name of the available resource [required if --origin-type=storage]") @click.option('--port', '-p', type=click.INT, help="The http port number.", default=80, show_default=True) @click.option('--protocol', '-P', type=click.STRING, help="The protocol used by the origin.", default='http', show_default=True) @click.option('--optimize-for', '-o', type=click.Choice(['web', 'video', 'file']), help="Performance configuration", default='web', show_default=True) @click.option('--extensions', '-e', type=click.STRING, help="File extensions that can be stored in the CDN, example: 'jpg, png, pdf'") @click.option('--cache-query', '-c', type=click.STRING, help="Cache query rules with the following formats:\n" "'ignore-all', 'include: ', 'ignore: '", default="include-all", show_default=True) @environment.pass_env def cli(env, unique_id, origin, path, origin_type, header, bucket_name, port, protocol, optimize_for, extensions, cache_query): """Create an origin path for an existing CDN mapping. For more information see the following documentation: \n https://cloud.ibm.com/docs/infrastructure/CDN?topic=CDN-manage-your-cdn#adding-origin-path-details """ manager = SoftLayer.CDNManager(env.client) if origin_type == 'storage' and not bucket_name: raise exceptions.ArgumentError('[-b | --bucket-name] is required when [-t | --origin-type] is "storage"') result = manager.add_origin(unique_id, origin, path, origin_type=origin_type, header=header, port=port, protocol=protocol, bucket_name=bucket_name, file_extensions=extensions, optimize_for=optimize_for, cache_query=cache_query) table = formatting.Table(['Item', 'Value']) table.align['Item'] = 'r' table.align['Value'] = 'r' table.add_row(['CDN Unique ID', result['mappingUniqueId']]) if origin_type == 'storage': table.add_row(['Bucket Name', result['bucketName']]) table.add_row(['Origin', result['origin']]) table.add_row(['Origin Type', result['originType']]) table.add_row(['Path', result['path']]) table.add_row(['Port', result['httpPort']]) table.add_row(['Configuration', result['performanceConfiguration']]) table.add_row(['Status', result['status']]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/cdn/origin_list.py000066400000000000000000000013161415376457700230670ustar00rootroot00000000000000"""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('unique_id') @environment.pass_env def cli(env, unique_id): """List origin path for an existing CDN mapping.""" manager = SoftLayer.CDNManager(env.client) origins = manager.get_origins(unique_id) table = formatting.Table(['Path', 'Origin', 'HTTP Port', 'Status']) for origin in origins: table.add_row([origin['path'], origin['origin'], origin['httpPort'], origin['status']]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/cdn/origin_remove.py000066400000000000000000000010301415376457700234020ustar00rootroot00000000000000"""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('unique_id') @click.argument('origin_path') @environment.pass_env def cli(env, unique_id, origin_path): """Removes an origin path for an existing CDN mapping.""" manager = SoftLayer.CDNManager(env.client) manager.remove_origin(unique_id, origin_path) click.secho("Origin with path %s has been deleted" % origin_path, fg='green') softlayer-python-5.9.8/SoftLayer/CLI/cdn/purge.py000066400000000000000000000017421415376457700216720ustar00rootroot00000000000000"""Purge cached files from all edge nodes.""" # :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('unique_id') @click.argument('path') @environment.pass_env def cli(env, unique_id, path): """Creates a purge record and also initiates the purge call. Example: slcli cdn purge 9779455 /article/file.txt For more information see the following documentation: \n https://cloud.ibm.com/docs/infrastructure/CDN?topic=CDN-manage-your-cdn#purging-cached-content """ manager = SoftLayer.CDNManager(env.client) result = manager.purge_content(unique_id, path) table = formatting.Table(['Date', 'Path', 'Saved', 'Status']) for data in result: table.add_row([ data['date'], data['path'], data['saved'], data['status'] ]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/columns.py000066400000000000000000000044011415376457700214570ustar00rootroot00000000000000""" 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 returned 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.9.8/SoftLayer/CLI/config/000077500000000000000000000000001415376457700206735ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/config/__init__.py000066400000000000000000000027761415376457700230200ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/config/setup.py000066400000000000000000000241671415376457700224170ustar00rootroot00000000000000"""Setup CLI configuration.""" # :license: MIT, see LICENSE for more details. import webbrowser import configparser import json import os.path import requests import click import SoftLayer from SoftLayer.CLI import config from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting from SoftLayer.consts import USER_AGENT from SoftLayer import utils def get_api_key(client, username, secret): # pylint: disable=inconsistent-return-statements """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 or username == 'apikey': try: client['Account'].getCurrentUser() return secret except SoftLayer.SoftLayerAPIError as ex: if 'invalid api token' not in ex.faultString.lower(): raise else: if isinstance(client, SoftLayer.API.IAMClient): client.authenticate_with_iam_token(secret) else: # Try to use a client with username/password client.authenticate_with_password(username, secret) user_record = client.call('Account', 'getCurrentUser', mask='id, apiAuthenticationKeys') api_keys = user_record['apiAuthenticationKeys'] if len(api_keys) == 0: return client.call('User_Customer', 'addApiAuthenticationKey', id=user_record['id']) return api_keys[0]['authenticationKey'] @click.command() @click.option('-a', '--auth', type=click.Choice(['ibmid', 'cloud_key', 'classic_key', 'sso']), help="Select a method of authentication.", default='classic_key', show_default=True) @environment.pass_env def cli(env, auth): """Setup the ~/.softlayer file with username and apikey. [Auth Types] ibmid: Requires your cloud.ibm.com username and password, and generates a classic infrastructure API key. cloud_key: A 32 character API key. Username will be 'apikey' classic_key: A 64 character API key used in the Softlayer/Classic Infrastructure systems. sso: For users with @ibm.com email addresses. """ username = None api_key = None timeout = 0 defaults = config.get_settings_from_client(env.client) endpoint_url = get_endpoint_url(env, defaults.get('endpoint_url', 'public')) # Get ths username and API key if auth == 'ibmid': username, api_key = ibmid_login(env) elif auth == 'cloud_key': username = 'apikey' secret = env.getpass('Classic Infrastructue API Key', default=defaults['api_key']) new_client = SoftLayer.Client(username=username, api_key=secret, endpoint_url=endpoint_url, timeout=timeout) api_key = get_api_key(new_client, username, secret) elif auth == 'sso': username, api_key = sso_login(env) else: username = env.input('Classic Infrastructue Username', default=defaults['username']) secret = env.getpass('Classic Infrastructue API Key', default=defaults['api_key']) new_client = SoftLayer.Client(username=username, api_key=secret, endpoint_url=endpoint_url, timeout=timeout) api_key = get_api_key(new_client, username, secret) # Ask for timeout, convert to float, then to int timeout = int(float(env.input('Timeout', default=defaults['timeout'] or 0))) 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 = configparser.RawConfigParser() parsed_config.read(config_path) try: parsed_config.add_section('softlayer') except 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_endpoint_url(env, endpoint='public'): """Gets the Endpoint to use.""" endpoint_type = env.input('Endpoint (public|private|custom)', default=endpoint) endpoint_type = endpoint_type.lower() if endpoint_type == 'public': endpoint_url = SoftLayer.API_PUBLIC_ENDPOINT elif endpoint_type == 'private': endpoint_url = SoftLayer.API_PRIVATE_ENDPOINT else: if endpoint_type == 'custom': endpoint_url = env.input('Endpoint URL', default=endpoint) else: endpoint_url = endpoint_type return endpoint_url def ibmid_login(env): """Uses an IBMid and Password to get an access token, and that access token to get an API key""" email = env.input("Email").strip() password = env.getpass("Password").strip() client = SoftLayer.API.IAMClient(config_file=env.config_file) # STEP 1: Get the base IAM Token with a username/password tokens = client.authenticate_with_password(email, password) # STEP 2: Figure out which account we want to use account = get_accounts(env, tokens['access_token']) # STEP 3: Refresh Token, using a specific account this time. tokens = client.refresh_iam_token(tokens['refresh_token'], account['account_id'], account['ims_id']) # STEP 4: Get or create the Classic Infrastructure API key user = client.call('SoftLayer_Account', 'getCurrentUser', mask="mask[id,username,apiAuthenticationKeys]") if len(user.get('apiAuthenticationKeys', [])) == 0: env.fout("Creating a Classic Infrastrucutre API key for {}".format(user['username'])) api_key = client.call('User_Customer', 'addApiAuthenticationKey', id=user['id']) else: api_key = user['apiAuthenticationKeys'][0]['authenticationKey'] return user.get('username'), api_key def get_accounts(env, a_token): """Gets account list from accounts.cloud.ibm.com/v1/accounts""" iam_client = requests.Session() headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'User-Agent': USER_AGENT, 'Accept': 'application/json' } headers['Authorization'] = 'Bearer {}'.format(a_token) response = iam_client.request( 'GET', 'https://accounts.cloud.ibm.com/v1/accounts', headers=headers ) response.raise_for_status() accounts = json.loads(response.text) selected = None ims_id = None if accounts.get('total_results', 0) == 1: selected = accounts['resources'][0] else: env.fout("Select an Account...") counter = 1 for selected in accounts.get('resources', []): links = utils.lookup(selected, 'metadata', 'linked_accounts') or [] for link in links: if link.get('origin') == "IMS": ims_id = link.get('id') if ims_id is None: ims_id = "Unlinked" env.fout("{}: {} ({})".format(counter, utils.lookup(selected, 'entity', 'name'), ims_id)) counter = counter + 1 ims_id = None # Reset ims_id to avoid any mix-match or something. choice = click.prompt('Enter a number', type=int) # Test to make sure choice is not out of bounds... selected = accounts['resources'][choice - 1] account_id = utils.lookup(selected, 'metadata', 'guid') links = utils.lookup(selected, 'metadata', 'linked_accounts') or [] for link in links: if link.get('origin') == "IMS": ims_id = link.get('id') print("Using account {}".format(utils.lookup(selected, 'entity', 'name'))) return {"account_id": account_id, "ims_id": ims_id} def get_sso_url(): """Gets the URL for using SSO Tokens""" iam_client = requests.Session() headers = { 'Content-Type': 'application/json', 'User-Agent': USER_AGENT, 'Accept': 'application/json' } response = iam_client.request( 'GET', 'https://iam.cloud.ibm.com/identity/.well-known/openid-configuration', headers=headers ) response.raise_for_status() data = json.loads(response.text) return data.get('passcode_endpoint') def sso_login(env): """Uses a SSO token to get a SL apikey""" passcode_url = get_sso_url() env.fout("Get a one-time code from {} to proceed.".format(passcode_url)) open_browser = env.input("Open the URL in the default browser? [Y/n]", default='Y') if open_browser.lower() == 'y': webbrowser.open(passcode_url) passcode = env.input("One-time code") client = SoftLayer.API.IAMClient(config_file=env.config_file) # STEP 1: Get the base IAM Token with a username/password tokens = client.authenticate_with_passcode(passcode) # STEP 2: Figure out which account we want to use account = get_accounts(env, tokens['access_token']) # STEP 3: Refresh Token, using a specific account this time. tokens = client.refresh_iam_token(tokens['refresh_token'], account['account_id'], account['ims_id']) # STEP 4: Get or create the Classic Infrastructure API key # client.authenticate_with_iam_token(tokens['access_token']) user = client.call('SoftLayer_Account', 'getCurrentUser', mask="mask[id,username,apiAuthenticationKeys]") if len(user.get('apiAuthenticationKeys', [])) == 0: env.fout("Creating a Classic Infrastrucutre API key for {}".format(user['username'])) api_key = client.call('User_Customer', 'addApiAuthenticationKey', id=user['id']) else: api_key = user['apiAuthenticationKeys'][0]['authenticationKey'] return user.get('username'), api_key softlayer-python-5.9.8/SoftLayer/CLI/config/show.py000066400000000000000000000005601415376457700222260ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/core.py000066400000000000000000000175161415376457700207420ustar00rootroot00000000000000""" SoftLayer.CLI.core ~~~~~~~~~~~~~~~~~~ Core for the SoftLayer CLI :license: MIT, see LICENSE for more details. """ import logging import os import sys import time import traceback import types import click import requests 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 } PROG_NAME = "slcli (SoftLayer Command-line)" 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, cmd_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: cmd_name = env.resolve_alias(cmd_name) new_path = list(self.path) new_path.append(cmd_name) module = env.get_command(*new_path) if isinstance(module, types.ModuleType): return CommandLoader(*new_path, help=module.__doc__ or '') else: return module def get_latest_version(): """Gets the latest version of the Softlayer library.""" try: result = requests.get('https://pypi.org/pypi/SoftLayer/json') json_result = result.json() latest = 'v{}'.format(json_result['info']['version']) except Exception: latest = "Unable to get version from pypi." return latest def get_version_message(ctx, param, value): """Gets current and latest release versions message.""" if not value or ctx.resilient_parsing: return current = SoftLayer.consts.VERSION latest = get_latest_version() click.secho("Current: {prog} {current}\nLatest: {prog} {latest}".format( prog=PROG_NAME, current=current, latest=latest)) ctx.exit() @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', 'max_content_width': 999}) @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.option('--version', is_flag=True, expose_value=False, is_eager=True, callback=get_version_message, help="Show version information.", allow_from_autoenv=False,) @environment.pass_env def cli(env, format='table', config=None, verbose=0, proxy=None, really=False, demo=False, **kwargs): """Main click CLI entry-point.""" # Populate environment 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() logger = logging.getLogger() if demo is False: logger.addHandler(logging.StreamHandler()) else: # This section is for running CLI tests. logging.getLogger("urllib3").setLevel(logging.WARNING) logger.addHandler(logging.NullHandler()) logger.setLevel(DEBUG_LOGGING_MAP.get(verbose, logging.DEBUG)) env.vars['_timings'] = SoftLayer.DebugTransport(env.client.transport) env.vars['verbose'] = verbose env.client.transport = env.vars['_timings'] @cli.resultcallback() @environment.pass_env def output_diagnostics(env, result, 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 in env.client.transport.get_last_calls(): api_call_value.append("%s::%s (%fs)" % (call.service, call.method, call.end_time - call.start_time)) 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)) if verbose > 1: for call in env.client.transport.get_last_calls(): call_table = formatting.Table(['', '{}::{}'.format(call.service, call.method)]) nice_mask = '' if call.mask is not None: nice_mask = call.mask call_table.add_row(['id', call.identifier]) call_table.add_row(['mask', nice_mask]) call_table.add_row(['filter', call.filter]) call_table.add_row(['limit', call.limit]) call_table.add_row(['offset', call.offset]) env.err(env.fmt(call_table)) if verbose > 2: for call in env.client.transport.get_last_calls(): env.err(env.client.transport.print_reproduceable(call)) 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 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") print("The following snippet should be able to reproduce the error") exit_status = 1 sys.exit(exit_status) if __name__ == '__main__': main() softlayer-python-5.9.8/SoftLayer/CLI/custom_types.py000066400000000000000000000020571415376457700225420ustar00rootroot00000000000000""" SoftLayer.CLI.custom_types ~~~~~~~~~~~~~~~~~~~~~~~~ Custom type declarations extending click.ParamType :license: MIT, see LICENSE for more details. """ import click # pylint: disable=inconsistent-return-statements 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.9.8/SoftLayer/CLI/dedicatedhost/000077500000000000000000000000001415376457700222325ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/dedicatedhost/__init__.py000066400000000000000000000001051415376457700243370ustar00rootroot00000000000000"""Dedicated Host.""" # :license: MIT, see LICENSE for more details. softlayer-python-5.9.8/SoftLayer/CLI/dedicatedhost/cancel.py000066400000000000000000000013751415376457700240370ustar00rootroot00000000000000"""Cancel 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 helpers @click.command() @click.argument('identifier') @environment.pass_env def cli(env, identifier): """Cancel a dedicated host server immediately""" mgr = SoftLayer.DedicatedHostManager(env.client) host_id = helpers.resolve_id(mgr.resolve_ids, identifier, 'dedicated host') if not (env.skip_confirmations or formatting.no_going_back(host_id)): raise exceptions.CLIAbort('Aborted') mgr.cancel_host(host_id) click.secho('Dedicated Host %s was cancelled' % host_id, fg='green') softlayer-python-5.9.8/SoftLayer/CLI/dedicatedhost/cancel_guests.py000066400000000000000000000022331415376457700254230ustar00rootroot00000000000000"""Cancel 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 helpers @click.command() @click.argument('identifier') @environment.pass_env def cli(env, identifier): """Cancel all virtual guests of the dedicated host immediately. Use the 'slcli vs cancel' command to cancel an specific guest """ dh_mgr = SoftLayer.DedicatedHostManager(env.client) host_id = helpers.resolve_id(dh_mgr.resolve_ids, identifier, 'dedicated host') if not (env.skip_confirmations or formatting.no_going_back(host_id)): raise exceptions.CLIAbort('Aborted') table = formatting.Table(['id', 'server name', 'status']) result = dh_mgr.cancel_guests(host_id) if result: for status in result: table.add_row([ status['id'], status['fqdn'], status['status'] ]) env.fout(table) else: click.secho('There is not any guest into the dedicated host %s' % host_id, fg='red') softlayer-python-5.9.8/SoftLayer/CLI/dedicatedhost/create.py000066400000000000000000000076011415376457700240530ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/dedicatedhost/create_options.py000066400000000000000000000044261415376457700256300ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/dedicatedhost/detail.py000066400000000000000000000046511415376457700240540ustar00rootroot00000000000000"""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 dedicated host.""" 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.9.8/SoftLayer/CLI/dedicatedhost/list.py000066400000000000000000000044111415376457700235570ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/dedicatedhost/list_guests.py000066400000000000000000000051671415376457700251620ustar00rootroot00000000000000"""List guests which are in a dedicated host server.""" # :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('guid', ('globalIdentifier',)), column_helper.Column('cpu', ('maxCpu',)), column_helper.Column('memory', ('maxMemory',)), column_helper.Column('datacenter', ('datacenter', 'name')), column_helper.Column('primary_ip', ('primaryIpAddress',)), column_helper.Column('backend_ip', ('primaryBackendIpAddress',)), column_helper.Column( 'created_by', ('billingItem', 'orderItem', 'order', 'userRecord', 'username')), column_helper.Column('power_state', ('powerState', 'name')), column_helper.Column( 'tags', lambda server: formatting.tags(server.get('tagReferences')), mask="tagReferences.tag.name"), ] DEFAULT_COLUMNS = [ 'id', 'hostname', 'domain', 'primary_ip', 'backend_ip', 'power_state' ] @click.command() @click.argument('identifier') @click.option('--cpu', '-c', help='Number of CPU cores', type=click.INT) @click.option('--domain', '-D', help='Domain portion of the FQDN') @click.option('--hostname', '-H', help='Host portion of the FQDN') @click.option('--memory', '-m', help='Memory in mebibytes', type=click.INT) @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, identifier, sortby, cpu, domain, hostname, memory, tag, columns): """List guests which are in a dedicated host server.""" mgr = SoftLayer.DedicatedHostManager(env.client) guests = mgr.list_guests(host_id=identifier, cpus=cpu, hostname=hostname, domain=domain, memory=memory, 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.9.8/SoftLayer/CLI/dns/000077500000000000000000000000001415376457700202125ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/dns/__init__.py000066400000000000000000000001111415376457700223140ustar00rootroot00000000000000"""Domain Name System.""" # :license: MIT, see LICENSE for more details. softlayer-python-5.9.8/SoftLayer/CLI/dns/record_add.py000066400000000000000000000070221415376457700226530ustar00rootroot00000000000000"""Add 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 helpers # pylint: disable=redefined-builtin @click.command() @click.argument('record') @click.argument('record_type') @click.argument('data') @click.option('--zone', help="Zone name or identifier that the resource record will be associated with.\n" "Required for all record types except PTR") @click.option('--ttl', default=900, show_default=True, help='TTL value in seconds, such as 86400') @click.option('--priority', default=10, show_default=True, help='The priority of the target host. (MX or SRV type only)') @click.option('--protocol', type=click.Choice(['tcp', 'udp', 'tls']), default='tcp', show_default=True, help='The protocol of the service, usually either TCP or UDP. (SRV type only)') @click.option('--port', type=click.INT, help='The TCP/UDP/TLS port on which the service is to be found. (SRV type only)') @click.option('--service', help='The symbolic name of the desired service. (SRV type only)') @click.option('--weight', default=5, show_default=True, help='Relative weight for records with same priority. (SRV type only)') @environment.pass_env def cli(env, record, record_type, data, zone, ttl, priority, protocol, port, service, weight): """Add resource record. Each resource record contains a RECORD and DATA property, defining a resource's name and it's target data. Domains contain multiple types of resource records so it can take one of the following values: A, AAAA, CNAME, MX, SPF, SRV, and PTR. About reverse records (PTR), the RECORD value must to be the public Ip Address of device you would like to manage reverse DNS. slcli dns record-add 10.10.8.21 PTR myhost.com --ttl=900 Examples: slcli dns record-add myhost.com A 192.168.1.10 --zone=foobar.com --ttl=900 slcli dns record-add myhost.com AAAA 2001:DB8::1 --zone=foobar.com slcli dns record-add 192.168.1.2 MX 192.168.1.10 --zone=foobar.com --priority=11 --ttl=1800 slcli dns record-add myhost.com TXT "txt-verification=rXOxyZounZs87oacJSKvbUSIQ" --zone=2223334 slcli dns record-add myhost.com SPF "v=spf1 include:_spf.google.com ~all" --zone=2223334 slcli dns record-add myhost.com SRV 192.168.1.10 --zone=2223334 --service=foobar --port=80 --protocol=TCP """ manager = SoftLayer.DNSManager(env.client) record_type = record_type.upper() if zone and record_type != 'PTR': zone_id = helpers.resolve_id(manager.resolve_ids, zone, name='zone') if record_type == 'MX': manager.create_record_mx(zone_id, record, data, ttl=ttl, priority=priority) elif record_type == 'SRV': manager.create_record_srv(zone_id, record, data, protocol, port, service, ttl=ttl, priority=priority, weight=weight) else: manager.create_record(zone_id, record, record_type, data, ttl=ttl) elif record_type == 'PTR': manager.create_record_ptr(record, data, ttl=ttl) else: raise exceptions.CLIAbort("%s isn't a valid record type or zone is missing" % record_type) click.secho("%s record added successfully" % record_type, fg='green') softlayer-python-5.9.8/SoftLayer/CLI/dns/record_edit.py000066400000000000000000000020731415376457700230510ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/dns/record_list.py000066400000000000000000000024721415376457700231020ustar00rootroot00000000000000"""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', 'record_type', help='Record type, such as A or CNAME') @environment.pass_env def cli(env, zone, data, record, ttl, record_type): """List all records in a zone.""" manager = SoftLayer.DNSManager(env.client) table = formatting.Table(['id', 'record', 'type', 'ttl', 'data']) table.align = 'l' zone_id = helpers.resolve_id(manager.resolve_ids, zone, name='zone') records = manager.get_records(zone_id, record_type=record_type, host=record, ttl=ttl, data=data) for the_record in records: table.add_row([ the_record.get('id'), the_record.get('host', ''), the_record.get('type').upper(), the_record.get('ttl'), the_record.get('data') ]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/dns/record_remove.py000066400000000000000000000010531415376457700234160ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/dns/zone_create.py000066400000000000000000000005061415376457700230630ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/dns/zone_delete.py000066400000000000000000000011611415376457700230600ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/dns/zone_import.py000066400000000000000000000075501415376457700231400ustar00rootroot00000000000000"""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, encoding="utf-8") 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.9.8/SoftLayer/CLI/dns/zone_list.py000066400000000000000000000021731415376457700225750ustar00rootroot00000000000000"""List all zones.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.utils import clean_time @click.command() @environment.pass_env def cli(env): """List all zones.""" manager = SoftLayer.DNSManager(env.client) object_mask = "mask[id,name,serial,updateDate,resourceRecordCount]" zones = manager.list_zones(mask=object_mask) table = formatting.Table(['id', 'zone', 'serial', 'updated', 'records']) table.align = 'l' for zone in zones: zone_serial = str(zone.get('serial')) zone_date = zone.get('updateDate', None) if zone_date is None: # The serial is just YYYYMMDD##, and since there is no createDate, just format it like it was one. zone_date = "{}-{}-{}T00:00:00-06:00".format(zone_serial[0:4], zone_serial[4:6], zone_serial[6:8]) table.add_row([ zone['id'], zone['name'], zone_serial, clean_time(zone_date), zone.get('resourceRecordCount', 0) ]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/dns/zone_print.py000066400000000000000000000007241415376457700227560ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/email/000077500000000000000000000000001415376457700205155ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/email/__init__.py000066400000000000000000000000371415376457700226260ustar00rootroot00000000000000"""Network Delivery account""" softlayer-python-5.9.8/SoftLayer/CLI/email/detail.py000066400000000000000000000027431415376457700223370ustar00rootroot00000000000000"""Display details for a specified email account.""" # :license: MIT, see LICENSE for more details. import click from SoftLayer.CLI.email.list import build_statistics_table from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.managers.email import EmailManager from SoftLayer import utils @click.command() @click.argument('identifier') @environment.pass_env def cli(env, identifier): """Display details for a specified email.""" email_manager = EmailManager(env.client) result = email_manager.get_instance(identifier) table = formatting.KeyValueTable(['name', 'value']) table.align['name'] = 'r' table.align['value'] = 'l' table.add_row(['id', result.get('id')]) table.add_row(['username', result.get('username')]) table.add_row(['email_address', result.get('emailAddress')]) table.add_row(['create_date', result.get('createDate')]) table.add_row(['category_code', utils.lookup(result, 'billingItem', 'categoryCode')]) table.add_row(['description', utils.lookup(result, 'billingItem', 'description')]) table.add_row(['type_description', utils.lookup(result, 'type', 'description')]) table.add_row(['type', utils.lookup(result, 'type', 'keyName')]) table.add_row(['vendor', utils.lookup(result, 'vendor', 'keyName')]) statistics = email_manager.get_statistics(identifier) for statistic in statistics: table.add_row(['statistics', build_statistics_table(statistic)]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/email/edit.py000066400000000000000000000024671415376457700220250ustar00rootroot00000000000000"""Edit details of an Delivery email account.""" # :license: MIT, see LICENSE for more details. import click from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.managers.email import EmailManager @click.command() @click.argument('identifier') @click.option('--username', help="Sets username for this account") @click.option('--email', help="Sets the contact email for this account") @click.option('--password', help="Password must be between 8 and 20 characters " "and must contain one letter and one number.") @environment.pass_env def cli(env, identifier, username, email, password): """Edit details of an email delivery account.""" email_manager = EmailManager(env.client) data = {} update = False if email: if email_manager.update_email(identifier, email): update = True else: raise exceptions.CLIAbort("Failed to Edit emailAddress account") if username: data['username'] = username if password: data['password'] = password if len(data) != 0: if email_manager.editObject(identifier, **data): update = True else: raise exceptions.CLIAbort("Failed to Edit email account") if update: env.fout('Updated Successfully') softlayer-python-5.9.8/SoftLayer/CLI/email/list.py000066400000000000000000000051631415376457700220470ustar00rootroot00000000000000"""Lists Email Delivery Service """ # :license: MIT, see LICENSE for more details. import click from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.managers.account import AccountManager from SoftLayer.managers.email import EmailManager from SoftLayer import utils @click.command() @environment.pass_env def cli(env): """Lists Email Delivery Service """ manager = AccountManager(env.client) email_manager = EmailManager(env.client) result = manager.get_network_message_delivery_accounts() table = formatting.KeyValueTable(['name', 'value']) table.align['name'] = 'r' table.align['value'] = 'l' table_information = formatting.KeyValueTable(['id', 'username', 'hostname', 'description', 'vendor']) table_information.align['id'] = 'r' table_information.align['username'] = 'l' for email in result: table_information.add_row([email.get('id'), email.get('username'), email.get('emailAddress'), utils.lookup(email, 'type', 'description'), utils.lookup(email, 'vendor', 'keyName')]) overview_table = _build_overview_table(email_manager.get_account_overview(email.get('id'))) statistics = email_manager.get_statistics(email.get('id')) table.add_row(['email_information', table_information]) table.add_row(['email_overview', overview_table]) for statistic in statistics: table.add_row(['statistics', build_statistics_table(statistic)]) env.fout(table) def _build_overview_table(email_overview): table = formatting.Table( ['credit_allowed', 'credits_remain', 'credits_overage', 'credits_used', 'package', 'reputation', 'requests']) table.align['name'] = 'r' table.align['value'] = 'l' table.add_row([email_overview.get('creditsAllowed'), email_overview.get('creditsRemain'), email_overview.get('creditsOverage'), email_overview.get('creditsUsed'), email_overview.get('package'), email_overview.get('reputation'), email_overview.get('requests')]) return table def build_statistics_table(statistics): """statistics records of Email Delivery account""" table = formatting.Table(['delivered', 'requests', 'bounces', 'opens', 'clicks', 'spam_reports']) table.align['name'] = 'r' table.align['value'] = 'l' table.add_row([statistics.get('delivered'), statistics.get('requests'), statistics.get('bounces'), statistics.get('opens'), statistics.get('clicks'), statistics.get('spamReports')]) return table softlayer-python-5.9.8/SoftLayer/CLI/environment.py000066400000000000000000000147311415376457700223520ustar00rootroot00000000000000""" 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, fmt=None): """Format output based on current the environment format.""" if fmt is None: fmt = self.format return formatting.format_output(output, fmt) def format_output_is_json(self): """Return True if format output is json or jsonraw""" return 'json' in self.format def fout(self, output, newline=True): """Format the input and output to the console (stdout).""" if output is not None: try: self.out(self.fmt(output), newline=newline) except UnicodeEncodeError: # If we hit an undecodeable entry, just try outputting as json. self.out(self.fmt(output, 'json'), 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.""" password = click.prompt(prompt, hide_input=True, default=default) # https://github.com/softlayer/softlayer-python/issues/1436 # click.prompt uses python's getpass() in the background # https://github.com/python/cpython/blob/3.9/Lib/getpass.py#L97 # In windows, shift+insert actually inputs the below 2 characters # If we detect those 2 characters, need to manually read from the clipbaord instead # https://stackoverflow.com/questions/101128/how-do-i-read-text-from-the-clipboard if password == 'àR': # tkinter is a built in python gui, but it has clipboard reading functions. # pylint: disable=import-outside-toplevel from tkinter import Tk tk_manager = Tk() password = tk_manager.clipboard_get() # keep the window from showing tk_manager.withdraw() return password # 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.9.8/SoftLayer/CLI/event_log/000077500000000000000000000000001415376457700214105ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/event_log/__init__.py000066400000000000000000000000221415376457700235130ustar00rootroot00000000000000"""Event Logs.""" softlayer-python-5.9.8/SoftLayer/CLI/event_log/get.py000066400000000000000000000061511415376457700225440ustar00rootroot00000000000000"""Get Event Logs.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer import utils @click.command() @click.option('--date-min', '-d', help='The earliest date we want to search for event logs in mm/dd/yyyy format.') @click.option('--date-max', '-D', help='The latest date we want to search for event logs in mm/dd/yyyy format.') @click.option('--obj-event', '-e', help="The event we want to get event logs for") @click.option('--obj-id', '-i', help="The id of the object we want to get event logs for") @click.option('--obj-type', '-t', help="The type of the object we want to get event logs for") @click.option('--utc-offset', '-z', default='-0000', show_default=True, help="UTC Offset for searching with dates. +/-HHMM format") @click.option('--metadata/--no-metadata', default=False, show_default=True, help="Display metadata if present") @click.option('--limit', '-l', type=click.INT, default=50, show_default=True, help="Total number of result to return. -1 to return ALL, there may be a LOT of these.") @environment.pass_env def cli(env, date_min, date_max, obj_event, obj_id, obj_type, utc_offset, metadata, limit): """Get Event Logs Example: slcli event-log get -d 01/01/2019 -D 02/01/2019 -t User -l 10 """ columns = ['Event', 'Object', 'Type', 'Date', 'Username'] event_mgr = SoftLayer.EventLogManager(env.client) user_mgr = SoftLayer.UserManager(env.client) request_filter = event_mgr.build_filter(date_min, date_max, obj_event, obj_id, obj_type, utc_offset) logs = event_mgr.get_event_logs(request_filter) log_time = "%Y-%m-%dT%H:%M:%S.%f%z" user_data = {} if metadata: columns.append('Metadata') row_count = 0 click.secho(", ".join(columns)) for log in logs: if log is None: click.secho('No logs available for filter %s.' % request_filter, fg='red') return user = log['userType'] label = log.get('label', '') if user == "CUSTOMER": username = user_data.get(log['userId']) if username is None: username = user_mgr.get_user(log['userId'], "mask[username]")['username'] user_data[log['userId']] = username user = username if metadata: metadata_data = log['metaData'].strip("\n\t") click.secho("'{0}','{1}','{2}','{3}','{4}','{5}'".format( log['eventName'], label, log['objectName'], utils.clean_time(log['eventCreateDate'], in_format=log_time), user, metadata_data)) else: click.secho("'{0}','{1}','{2}','{3}','{4}'".format( log['eventName'], label, log['objectName'], utils.clean_time(log['eventCreateDate'], in_format=log_time), user)) row_count = row_count + 1 if row_count >= limit and limit != -1: return softlayer-python-5.9.8/SoftLayer/CLI/event_log/types.py000066400000000000000000000010201415376457700231170ustar00rootroot00000000000000"""Get Event Log Types.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting COLUMNS = ['types'] @click.command() @environment.pass_env def cli(env): """Get Event Log Types""" mgr = SoftLayer.EventLogManager(env.client) event_log_types = mgr.get_event_log_types() table = formatting.Table(COLUMNS) for event_log_type in event_log_types: table.add_row([event_log_type]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/exceptions.py000066400000000000000000000017531415376457700221670ustar00rootroot00000000000000""" 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().__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().__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().__init__(msg, *args) self.message = "Argument Error: %s" % msg softlayer-python-5.9.8/SoftLayer/CLI/file/000077500000000000000000000000001415376457700203455ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/file/__init__.py000066400000000000000000000000241415376457700224520ustar00rootroot00000000000000"""File Storage.""" softlayer-python-5.9.8/SoftLayer/CLI/file/access/000077500000000000000000000000001415376457700216065ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/file/access/__init__.py000066400000000000000000000000431415376457700237140ustar00rootroot00000000000000"""File Storage Access Control.""" softlayer-python-5.9.8/SoftLayer/CLI/file/access/authorize.py000066400000000000000000000040701415376457700241730ustar00rootroot00000000000000"""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') 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.9.8/SoftLayer/CLI/file/access/list.py000066400000000000000000000030741415376457700231370ustar00rootroot00000000000000"""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 helpers 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) resolved_id = helpers.resolve_id(file_manager.resolve_ids, volume_id, 'Volume Id') access_list = file_manager.get_file_volume_access_list( volume_id=resolved_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.9.8/SoftLayer/CLI/file/access/revoke.py000066400000000000000000000037071415376457700234620ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/file/cancel.py000066400000000000000000000025311415376457700221450ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/file/convert.py000066400000000000000000000007471415376457700224070ustar00rootroot00000000000000"""Convert a dependent duplicate volume to an independent volume.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment @click.command() @click.argument('volume_id') @environment.pass_env def cli(env, volume_id): """Convert a dependent duplicate volume to an independent volume.""" file_manager = SoftLayer.FileStorageManager(env.client) resp = file_manager.convert_dep_dupe(volume_id) click.echo(resp) softlayer-python-5.9.8/SoftLayer/CLI/file/count.py000066400000000000000000000027121415376457700220510ustar00rootroot00000000000000"""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 = {} 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(): # pylint: disable=consider-iterating-dictionary datacenters[datacenter_name] = 1 else: datacenters[datacenter_name] += 1 table = formatting.KeyValueTable(DEFAULT_COLUMNS) table.sortby = sortby for key, value in datacenters.items(): table.add_row([key, value]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/file/detail.py000066400000000000000000000123551415376457700221670ustar00rootroot00000000000000"""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.CLI import helpers 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_id = helpers.resolve_id(file_manager.resolve_ids, volume_id, 'File Storage') file_volume = file_manager.get_file_volume_details(file_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', float(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]) notes = '{}'.format(file_volume.get('notes', '')) table.add_row(['Notes', notes]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/file/duplicate.py000066400000000000000000000104631415376457700226750ustar00rootroot00000000000000"""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)") @click.option('--dependent-duplicate', type=click.BOOL, default=False, show_default=True, help='Whether or not this duplicate will be a dependent duplicate' 'of the origin volume.') @environment.pass_env def cli(env, origin_volume_id, origin_snapshot_id, duplicate_size, duplicate_iops, duplicate_tier, duplicate_snapshot_size, billing, dependent_duplicate): """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, dependent_duplicate=dependent_duplicate ) 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.9.8/SoftLayer/CLI/file/limit.py000066400000000000000000000017771415376457700220510ustar00rootroot00000000000000"""List number of file storage volumes limit 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', 'MaximumAvailableCount', 'ProvisionedCount' ] @click.command() @click.option('--sortby', help='Column to sort by', default='Datacenter') @environment.pass_env def cli(env, sortby): """List number of block storage volumes limit per datacenter.""" file_manager = SoftLayer.FileStorageManager(env.client) file_volumes = file_manager.list_file_volume_limit() table = formatting.KeyValueTable(DEFAULT_COLUMNS) table.sortby = sortby for volume in file_volumes: datacenter_name = volume['datacenterName'] maximum_available_count = volume['maximumAvailableCount'] provisioned_count = volume['provisionedCount'] table.add_row([datacenter_name, maximum_available_count, provisioned_count]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/file/list.py000066400000000000000000000063771415376457700217070ustar00rootroot00000000000000"""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 storage_utils 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')), column_helper.Column('notes', ('notes',), mask="notes"), ] DEFAULT_COLUMNS = [ 'id', 'username', 'datacenter', 'storage_type', 'capacity_gb', 'bytes_used', 'ip_addr', 'active_transactions', 'mount_addr', 'rep_partner_count', 'notes', ] DEFAULT_NOTES_SIZE = 20 @click.command() @click.option('--username', '-u', help='Volume username') @click.option('--datacenter', '-d', help='Datacenter shortname') @click.option('--order', '-o', type=int, help='Filter by ID of the order that purchased the block storage') @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, order): """List file storage.""" file_manager = SoftLayer.FileStorageManager(env.client) file_volumes = file_manager.list_file_volumes(datacenter=datacenter, username=username, storage_type=storage_type, order=order, mask=columns.mask()) table = storage_utils.build_output_table(env, file_volumes, columns, sortby) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/file/modify.py000066400000000000000000000052341415376457700222120ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/file/order.py000066400000000000000000000113261415376457700220350ustar00rootroot00000000000000"""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. Options vary based on storage size. [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\']. enterprise and performance are depreciated""", 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. Valid size and iops options can be found here: https://cloud.ibm.com/docs/FileStorage/index.html#provisioning-considerations """ file_manager = SoftLayer.FileStorageManager(env.client) storage_type = storage_type.lower() hourly_billing_flag = False if billing.lower() == "hourly": hourly_billing_flag = True if service_offering != 'storage_as_a_service': click.secho('{} is a legacy storage offering'.format(service_offering), fg='red') if hourly_billing_flag: 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 service_offering == 'performance' and snapshot_size is not None: raise exceptions.CLIAbort( '--snapshot-size is not available for performance service offerings. ' 'Use --service-offering storage_as_a_service' ) 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']) click.echo( '\nYou may run "slcli file volume-list --order {0}" to find this file volume after it ' 'is ready.'.format(order['placedOrder']['id'])) else: click.echo("Order could not be placed! Please verify your options and try again.") softlayer-python-5.9.8/SoftLayer/CLI/file/refresh.py000066400000000000000000000010401415376457700223500ustar00rootroot00000000000000"""Refresh a dependent duplicate volume with a snapshot from its parent.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment @click.command() @click.argument('volume_id') @click.argument('snapshot_id') @environment.pass_env def cli(env, volume_id, snapshot_id): """Refresh a duplicate volume with a snapshot from its parent.""" file_manager = SoftLayer.FileStorageManager(env.client) resp = file_manager.refresh_dupe(volume_id, snapshot_id) click.echo(resp) softlayer-python-5.9.8/SoftLayer/CLI/file/replication/000077500000000000000000000000001415376457700226565ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/file/replication/__init__.py000066400000000000000000000000501415376457700247620ustar00rootroot00000000000000"""File Storage Replication Control.""" softlayer-python-5.9.8/SoftLayer/CLI/file/replication/disaster_recovery_failover.py000066400000000000000000000036341415376457700306610ustar00rootroot00000000000000"""Failover an inaccessible file volume to its available replicant 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(epilog="""Failover an inaccessible file volume to its available replicant volume. If a volume (with replication) becomes inaccessible due to a disaster event, this method can be used to immediately failover to an available replica in another location. This method does not allow for failback via API. After using this method, to failback to the original volume, please open a support ticket. If you wish to test failover, please use replica-failover. """) @click.argument('volume-id') @click.option('--replicant-id', help="ID of the replicant volume") @environment.pass_env def cli(env, volume_id, replicant_id): """Failover an inaccessible file volume to its available replicant volume.""" file_storage_manager = SoftLayer.FileStorageManager(env.client) click.secho("""WARNING : Failover an inaccessible file volume to its available replicant volume.""" """If a volume (with replication) becomes inaccessible due to a disaster event,""" """this method can be used to immediately failover to an available replica in another location.""" """This method does not allow for failback via the API.""" """To failback to the original volume after using this method, open a support ticket.""" """If you wish to test failover, use replica-failover instead.""", fg='red') if not formatting.confirm('Are you sure you want to continue?'): raise exceptions.CLIAbort('Aborted.') file_storage_manager.disaster_recovery_failover_to_replicant( volume_id, replicant_id ) click.echo("Disaster Recovery Failover to replicant is now in progress.") softlayer-python-5.9.8/SoftLayer/CLI/file/replication/failback.py000066400000000000000000000011471415376457700247670ustar00rootroot00000000000000"""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') @environment.pass_env def cli(env, volume_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) if success: click.echo("Failback from replicant is now in progress.") else: click.echo("Failback operation could not be initiated.") softlayer-python-5.9.8/SoftLayer/CLI/file/replication/failover.py000066400000000000000000000013231415376457700250360ustar00rootroot00000000000000"""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") @environment.pass_env def cli(env, volume_id, replicant_id): """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 ) if success: click.echo("Failover to replicant is now in progress.") else: click.echo("Failover operation could not be initiated.") softlayer-python-5.9.8/SoftLayer/CLI/file/replication/locations.py000066400000000000000000000031021415376457700252170ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/file/replication/order.py000066400000000000000000000040111415376457700243370ustar00rootroot00000000000000"""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 from SoftLayer.CLI import helpers from SoftLayer import utils 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, ' '(HOURLY | DAILY | WEEKLY)', required=True, type=click.Choice(['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) file_volume_id = helpers.resolve_id(file_manager.resolve_ids, volume_id, 'File Storage') if tier is not None: tier = float(tier) try: order = file_manager.order_replicant_volume( file_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( utils.lookup(order, 'placedOrder', 'id'))) for item in utils.lookup(order, 'placedOrder', 'items'): click.echo(" > %s" % item.get('description')) else: click.echo("Order could not be placed! Please verify your options " + "and try again.") softlayer-python-5.9.8/SoftLayer/CLI/file/replication/partners.py000066400000000000000000000026761415376457700251010ustar00rootroot00000000000000"""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 from SoftLayer.CLI import storage_utils COLUMNS = storage_utils.REPLICATION_PARTNER_COLUMNS DEFAULT_COLUMNS = storage_utils.REPLICATION_PARTNER_DEFAULT @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.9.8/SoftLayer/CLI/file/set_note.py000066400000000000000000000015621415376457700225430ustar00rootroot00000000000000"""Set note for 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 helpers @click.command() @click.argument('volume-id') @click.option('--note', '-n', type=str, required=True, help='Public notes related to a Storage volume') @environment.pass_env def cli(env, volume_id, note): """Set note for an existing file storage volume.""" file_manager = SoftLayer.FileStorageManager(env.client) file_volume_id = helpers.resolve_id(file_manager.resolve_ids, volume_id, 'File Storage') result = file_manager.volume_set_note(file_volume_id, note) if result: click.echo("Set note successfully!") else: click.echo("Note could not be set! Please verify your options and try again.") softlayer-python-5.9.8/SoftLayer/CLI/file/snapshot/000077500000000000000000000000001415376457700222045ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/file/snapshot/__init__.py000066400000000000000000000000451415376457700243140ustar00rootroot00000000000000"""File Storage Snapshot Control.""" softlayer-python-5.9.8/SoftLayer/CLI/file/snapshot/cancel.py000066400000000000000000000025511415376457700240060ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/file/snapshot/create.py000066400000000000000000000015021415376457700240170ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/file/snapshot/delete.py000066400000000000000000000007711415376457700240250ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/file/snapshot/disable.py000066400000000000000000000017171415376457700241670ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/file/snapshot/enable.py000066400000000000000000000044601415376457700240100ustar00rootroot00000000000000# 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.9.8/SoftLayer/CLI/file/snapshot/get_notify_status.py000066400000000000000000000014011415376457700263240ustar00rootroot00000000000000"""Get the snapshots space usage threshold warning flag setting for specific volume""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment @click.command() @click.argument('volume_id') @environment.pass_env def cli(env, volume_id): """Get snapshots space usage threshold warning flag setting for a given volume""" file_manager = SoftLayer.FileStorageManager(env.client) enabled = file_manager.get_volume_snapshot_notification_status(volume_id) if enabled == 0: click.echo("Disabled: Snapshots space usage threshold is disabled for volume {}".format(volume_id)) else: click.echo("Enabled: Snapshots space usage threshold is enabled for volume {}".format(volume_id)) softlayer-python-5.9.8/SoftLayer/CLI/file/snapshot/list.py000066400000000000000000000032671415376457700235410ustar00rootroot00000000000000"""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 from SoftLayer.CLI import helpers 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) resolved_id = helpers.resolve_id(file_manager.resolve_ids, volume_id, 'Volume Id') snapshots = file_manager.get_file_volume_snapshot_list( resolved_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.9.8/SoftLayer/CLI/file/snapshot/order.py000066400000000000000000000034731415376457700237000ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/file/snapshot/restore.py000066400000000000000000000013411415376457700242400ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/file/snapshot/schedule_list.py000066400000000000000000000045051415376457700254110ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/file/snapshot/set_notify_status.py000066400000000000000000000015761415376457700263550ustar00rootroot00000000000000"""Disable/Enable snapshots space usage threshold warning for a specific 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( '--enable/--disable', default=True, help='Enable/Disable snapshot notification. Use `slcli file snapshot-set-notification volumeId --enable` to enable', required=True) @environment.pass_env def cli(env, volume_id, enable): """Enables/Disables snapshot space usage threshold warning for a given volume""" file_manager = SoftLayer.FileStorageManager(env.client) status = file_manager.set_volume_snapshot_notification(volume_id, enable) if status: click.echo( 'Snapshots space usage threshold warning notification has bee set to %s for volume %s' % (enable, volume_id)) softlayer-python-5.9.8/SoftLayer/CLI/firewall/000077500000000000000000000000001415376457700212335ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/firewall/__init__.py000066400000000000000000000007471415376457700233540ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/firewall/add.py000066400000000000000000000036501415376457700223410ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/firewall/cancel.py000066400000000000000000000020451415376457700230330ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/firewall/detail.py000066400000000000000000000040501415376457700230460ustar00rootroot00000000000000"""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) _firewall = mgr.get_instance(firewall_id) table = formatting.KeyValueTable(['name', 'value']) table.align['name'] = 'r' table.align['value'] = 'l' table.add_row(['id', _firewall.get('id')]) table.add_row(['primaryIpAddress', _firewall.get('primaryIpAddress')]) table.add_row(['datacenter', utils.lookup(_firewall, 'datacenter', 'longName')]) table.add_row(['networkVlan', utils.lookup(_firewall, 'networkVlan', 'name')]) table.add_row(['networkVlaniD', utils.lookup(_firewall, 'networkVlan', 'id')]) if firewall_type == 'vlan': rules = mgr.get_dedicated_fwl_rules(firewall_id) else: rules = mgr.get_standard_fwl_rules(firewall_id) table.add_row(['rules', get_rules_table(rules)]) env.fout(table) 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.9.8/SoftLayer/CLI/firewall/edit.py000066400000000000000000000144601415376457700225370ustar00rootroot00000000000000"""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 = [] 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': mgr.edit_dedicated_fwl_rules(firewall_id, rules) else: 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 raise exceptions.CLIAbort('Aborted.') raise exceptions.CLIAbort('Aborted.') env.fout('Firewall updated!') else: raise exceptions.CLIAbort('Aborted.') softlayer-python-5.9.8/SoftLayer/CLI/firewall/list.py000066400000000000000000000050061415376457700225610ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/formatting.py000066400000000000000000000312541415376457700221570ustar00rootroot00000000000000""" SoftLayer.formatting ~~~~~~~~~~~~~~~~~~~~ Provider classes and helper functions to display output onto a command-line. """ # pylint: disable=E0202, consider-merging-isinstance, arguments-differ, keyword-arg-before-vararg import collections import json import os import click # If both PTable and prettytable are installed, its impossible to use the new version try: from prettytable import prettytable except ImportError: 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, str): 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().__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, o): """Encode object if it implements to_python().""" if hasattr(o, 'to_python'): return o.to_python() return super().default(o) class Table(object): """A Table structure used for output. :param list columns: a list of column names """ def __init__(self, columns, title=None): 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 self.title = title 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) if isinstance(self.align, str): table.align = self.align else: # Required because PrettyTable has a strict setter function for alignment for a_col, alignment in self.align.items(): table.align[a_col] = alignment if self.title: table.title = self.title # 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 new_result = [item for item in result if item] if isinstance(new_result[0], dict): return _format_list_objects(new_result) table = Table(['value']) for item in new_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: if isinstance(item, dict): all_keys = all_keys.union(item.keys()) all_keys = sorted(all_keys) table = Table(all_keys) for item in result: if not item: continue 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.9.8/SoftLayer/CLI/globalip/000077500000000000000000000000001415376457700212175ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/globalip/__init__.py000066400000000000000000000001121415376457700233220ustar00rootroot00000000000000"""Global IP addresses.""" # :license: MIT, see LICENSE for more details. softlayer-python-5.9.8/SoftLayer/CLI/globalip/assign.py000066400000000000000000000011241415376457700230530ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/globalip/cancel.py000066400000000000000000000013201415376457700230120ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/globalip/create.py000066400000000000000000000023461415376457700230410ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/globalip/list.py000066400000000000000000000027101415376457700225440ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/globalip/unassign.py000066400000000000000000000010601415376457700234150ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/hardware/000077500000000000000000000000001415376457700212235ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/hardware/__init__.py000066400000000000000000000000301415376457700233250ustar00rootroot00000000000000"""Hardware servers.""" softlayer-python-5.9.8/SoftLayer/CLI/hardware/authorize_storage.py000066400000000000000000000015561415376457700253420ustar00rootroot00000000000000"""Authorize File or Block Storage to a Hardware 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 helpers @click.command() @click.argument('identifier') @click.option('--username-storage', '-u', type=click.STRING, help="The storage username to be added to the hardware server") @environment.pass_env def cli(env, identifier, username_storage): """Authorize File or Block Storage to a Hardware Server.""" hardware = SoftLayer.HardwareManager(env.client) hardware_id = helpers.resolve_id(hardware.resolve_ids, identifier, 'hardware') if not hardware.authorize_storage(hardware_id, username_storage): raise exceptions.CLIAbort('Authorize Storage Failed') env.fout('Successfully Storage Added.') softlayer-python-5.9.8/SoftLayer/CLI/hardware/bandwidth.py000066400000000000000000000036211415376457700235430ustar00rootroot00000000000000"""GBandwidth data over date range. Bandwidth is listed in GB""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import helpers from SoftLayer.CLI.virt.bandwidth import create_bandwidth_table @click.command() @click.argument('identifier') @click.option('--start_date', '-s', type=click.STRING, required=True, help="Start Date YYYY-MM-DD, YYYY-MM-DDTHH:mm:ss,") @click.option('--end_date', '-e', type=click.STRING, required=True, help="End Date YYYY-MM-DD, YYYY-MM-DDTHH:mm:ss") @click.option('--summary_period', '-p', type=click.INT, default=3600, show_default=True, help="300, 600, 1800, 3600, 43200 or 86400 seconds") @click.option('--quite_summary', '-q', is_flag=True, default=False, show_default=True, help="Only show the summary table") @environment.pass_env def cli(env, identifier, start_date, end_date, summary_period, quite_summary): """Bandwidth data over date range. Bandwidth is listed in GB Using just a date might get you times off by 1 hour, use T00:01 to get just the specific days data Timezones can also be included with the YYYY-MM-DDTHH:mm:ss.00000-HH:mm format. Due to some rounding and date alignment details, results here might be slightly different than results in the control portal. Example:: slcli hw bandwidth 1234 -s 2019-05-01T00:01 -e 2019-05-02T00:00:01.00000-12:00 """ hardware = SoftLayer.HardwareManager(env.client) hardware_id = helpers.resolve_id(hardware.resolve_ids, identifier, 'hardware') data = hardware.get_bandwidth_data(hardware_id, start_date, end_date, None, summary_period) title = "Bandwidth Report: %s - %s" % (start_date, end_date) table, sum_table = create_bandwidth_table(data, summary_period, title) env.fout(sum_table) if not quite_summary: env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/hardware/billing.py000066400000000000000000000026011415376457700232140ustar00rootroot00000000000000"""Get billing 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') @environment.pass_env def cli(env, identifier): """Get billing for a hardware device.""" hardware = SoftLayer.HardwareManager(env.client) hardware_id = helpers.resolve_id(hardware.resolve_ids, identifier, 'hardware') result = hardware.get_hardware(hardware_id) table = formatting.KeyValueTable(['name', 'value']) table.align['name'] = 'r' table.align['value'] = 'l' table.add_row(['Id', identifier]) table.add_row(['Billing Item Id', utils.lookup(result, 'billingItem', 'id')]) table.add_row(['Recurring Fee', utils.lookup(result, 'billingItem', 'recurringFee')]) table.add_row(['Total', utils.lookup(result, 'billingItem', 'nextInvoiceTotalRecurringAmount')]) table.add_row(['Provision Date', utils.lookup(result, 'billingItem', 'provisionDate')]) price_table = formatting.Table(['Item', 'Recurring Price']) for item in utils.lookup(result, 'billingItem', 'nextInvoiceChildren') or []: price_table.add_row([item['description'], item['nextInvoiceTotalRecurringAmount']]) table.add_row(['prices', price_table]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/hardware/cancel.py000066400000000000000000000021551415376457700230250ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/hardware/cancel_reasons.py000066400000000000000000000011161415376457700245530ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/hardware/create.py000066400000000000000000000116421415376457700230440ustar00rootroot00000000000000"""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', required=True, prompt=True, help="Host portion of the FQDN") @click.option('--domain', '-D', required=True, prompt=True, help="Domain portion of the FQDN") @click.option('--size', '-s', required=True, prompt=True, help="Hardware size") @click.option('--os', '-o', required=True, prompt=True, help="OS Key value") @click.option('--datacenter', '-d', required=True, prompt=True, help="Datacenter shortname") @click.option('--port-speed', type=click.INT, help="Port speeds. DEPRECATED, use --network") @click.option('--no-public', is_flag=True, help="Private network only. DEPRECATED, use --network.") @click.option('--network', help="Network Option Key. Use instead of port-speed option") @click.option('--billing', default='hourly', show_default=True, type=click.Choice(['hourly', 'monthly']), help="Billing rate") @click.option('--postinstall', '-i', help="Post-install script. Should be a HTTPS URL.") @click.option('--test', is_flag=True, help="Do not actually create the server") @click.option('--template', '-t', is_eager=True, type=click.Path(exists=True, readable=True, resolve_path=True), callback=template.TemplateCallback(list_args=['key']), help="A template file that defaults the command-line options") @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") @click.option('--router-public', type=click.INT, help="The ID of the public ROUTER on which you want the virtual server placed") @click.option('--router-private', type=click.INT, help="The ID of the private ROUTER on which you want the virtual server placed") @helpers.multi_option('--key', '-k', help="SSH keys to add to the root user") @helpers.multi_option('--extra', '-e', help="Extra option Key Names") @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'), 'network': args.get('network'), 'public_router': args.get('router_public', None), 'private_router': args.get('router_private', None) } # 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.9.8/SoftLayer/CLI/hardware/create_options.py000066400000000000000000000142031415376457700246130ustar00rootroot00000000000000"""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 account from SoftLayer.managers import hardware @click.command() @click.argument('location', required=False) @click.option('--prices', '-p', is_flag=True, help='Use --prices to list the server item prices, and to list the Item Prices by location,' 'add it to the --prices option using location short name, e.g. --prices dal13') @environment.pass_env def cli(env, prices, location=None): """Server order options for a given chassis.""" hardware_manager = hardware.HardwareManager(env.client) account_manager = account.AccountManager(env.client) options = hardware_manager.get_create_options(location) routers = account_manager.get_routers(location=location) tables = [] # Datacenters dc_table = formatting.Table(['Datacenter', 'Value'], title="Datacenters") dc_table.sortby = 'Value' dc_table.align = 'l' for location_info in options['locations']: dc_table.add_row([location_info['name'], location_info['key']]) tables.append(dc_table) tables.append(_preset_prices_table(options['sizes'], prices)) tables.append(_os_prices_table(options['operating_systems'], prices)) tables.append(_port_speed_prices_table(options['port_speeds'], prices)) tables.append(_extras_prices_table(options['extras'], prices)) tables.append(_get_routers(routers)) # since this is multiple tables, this is required for a valid JSON object to be rendered. env.fout(formatting.listing(tables, separator='\n')) def _preset_prices_table(sizes, prices=False): """Shows Server Preset options prices. :param [] sizes: List of Hardware Server sizes. :param prices: Create a price table or not """ if prices: table = formatting.Table(['Size', 'Value', 'Hourly', 'Monthly'], title="Sizes") for size in sizes: if size.get('hourlyRecurringFee', 0) + size.get('recurringFee', 0) + 1 > 0: table.add_row([size['name'], size['key'], "%.4f" % size['hourlyRecurringFee'], "%.4f" % size['recurringFee']]) else: table = formatting.Table(['Size', 'Value'], title="Sizes") for size in sizes: table.add_row([size['name'], size['key']]) table.sortby = 'Value' table.align = 'l' return table def _os_prices_table(operating_systems, prices=False): """Shows Server Operating Systems prices cost and capacity restriction. :param [] operating_systems: List of Hardware Server operating systems. :param prices: Create a price table or not """ if prices: table = formatting.Table(['Key', 'Hourly', 'Monthly', 'Restriction'], title="Operating Systems") for operating_system in operating_systems: for price in operating_system['prices']: cr_max = _get_price_data(price, 'capacityRestrictionMaximum') cr_min = _get_price_data(price, 'capacityRestrictionMinimum') cr_type = _get_price_data(price, 'capacityRestrictionType') table.add_row( [operating_system['key'], _get_price_data(price, 'hourlyRecurringFee'), _get_price_data(price, 'recurringFee'), "%s - %s %s" % (cr_min, cr_max, cr_type)]) else: table = formatting.Table(['OS', 'Key', 'Reference Code'], title="Operating Systems") for operating_system in operating_systems: table.add_row([operating_system['name'], operating_system['key'], operating_system['referenceCode']]) table.sortby = 'Key' table.align = 'l' return table def _port_speed_prices_table(port_speeds, prices=False): """Shows Server Port Speeds prices cost and capacity restriction. :param [] port_speeds: List of Hardware Server Port Speeds. :param prices: Create a price table or not """ if prices: table = formatting.Table(['Key', 'Speed', 'Hourly', 'Monthly'], title="Network Options") for speed in port_speeds: for price in speed['prices']: table.add_row( [speed['key'], speed['speed'], _get_price_data(price, 'hourlyRecurringFee'), _get_price_data(price, 'recurringFee')]) else: table = formatting.Table(['Network', 'Speed', 'Key'], title="Network Options") for speed in port_speeds: table.add_row([speed['name'], speed['speed'], speed['key']]) table.sortby = 'Speed' table.align = 'l' return table def _extras_prices_table(extras, prices=False): """Shows Server extras prices cost and capacity restriction. :param [] extras: List of Hardware Server Extras. :param prices: Create a price table or not """ if prices: table = formatting.Table(['Key', 'Hourly', 'Monthly'], title="Extras") for extra in extras: for price in extra['prices']: table.add_row( [extra['key'], _get_price_data(price, 'hourlyRecurringFee'), _get_price_data(price, 'recurringFee')]) else: table = formatting.Table(['Extra Option', 'Key'], title="Extras") for extra in extras: table.add_row([extra['name'], extra['key']]) table.sortby = 'Key' table.align = 'l' return table def _get_price_data(price, item): """Get a specific data from HS price. :param price: Hardware Server price. :param string item: Hardware Server price data. """ result = '-' if item in price: result = price[item] return result def _get_routers(routers): """Get all routers information :param routers: Routers data """ table = formatting.Table(["id", "hostname", "name"], title='Routers') for router in routers: table.add_row([router['id'], router['hostname'], router['topLevelLocation']['longName'], ]) table.align = 'l' return table softlayer-python-5.9.8/SoftLayer/CLI/hardware/credentials.py000066400000000000000000000020161415376457700240710ustar00rootroot00000000000000"""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 from SoftLayer import exceptions @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['softwareComponents']: if 'passwords' not in item: raise exceptions.SoftLayerError("No passwords found in softwareComponents") for credentials in item['passwords']: table.add_row([credentials.get('username', 'None'), credentials.get('password', 'None')]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/hardware/detail.py000066400000000000000000000162771415376457700230540ustar00rootroot00000000000000"""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 # pylint: disable=R0915 @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') @click.option('--components', is_flag=True, default=False, help='Show associated hardware components') @environment.pass_env def cli(env, identifier, passwords, price, components): """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) hard_drives = hardware.get_hard_drives(hardware_id) 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_hard_drives = formatting.Table(['Name', 'Capacity', 'Serial #']) for drives in hard_drives: name = drives['hardwareComponentModel']['manufacturer'] + " " + drives['hardwareComponentModel']['name'] capacity = str(drives['hardwareComponentModel']['hardwareGenericComponentModel']['capacity']) + " " + str( drives['hardwareComponentModel']['hardwareGenericComponentModel']['units']) serial = drives['serialNumber'] table_hard_drives.add_row([name, capacity, serial]) 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(['drives', table_hard_drives]) 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()]) last_transaction = "{} ({})".format(utils.lookup(result, 'lastTransaction', 'transactionGroup', 'name'), utils.clean_time(utils.lookup(result, 'lastTransaction', 'modifyDate'))) table.add_row(['last_transaction', last_transaction]) table.add_row(['billing', 'Hourly' if result['hourlyBillingFlag'] else 'Monthly']) 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]) bandwidth = hardware.get_bandwidth_allocation(hardware_id) bw_table = _bw_table(bandwidth) table.add_row(['Bandwidth', bw_table]) system_table = _system_table(result['activeComponents']) table.add_row(['System_data', system_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', 'CategoryCode', 'Recurring Price']) price_table.align['Item'] = 'l' price_table.add_row(['Total', '-', total_price]) for item in utils.lookup(result, 'billingItem', 'nextInvoiceChildren') or []: price_table.add_row([item['description'], item['categoryCode'], 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]) if components: components = hardware.get_components(identifier) components_table = formatting.Table(['name', 'Firmware version', 'Firmware build date', 'Type']) components_table.align['date'] = 'l' component_ids = [] for hw_component in components: if hw_component['id'] not in component_ids: firmware = hw_component['hardwareComponentModel']['firmwares'][0] components_table.add_row([utils.lookup(hw_component, 'hardwareComponentModel', 'longDescription'), utils.lookup(firmware, 'version'), utils.clean_time(utils.lookup(firmware, 'createDate')), utils.lookup(hw_component, 'hardwareComponentModel', 'hardwareGenericComponentModel', 'hardwareComponentType', 'keyName')]) component_ids.append(hw_component['id']) table.add_row(['components', components_table]) table.add_row(['tags', formatting.tags(result['tagReferences'])]) env.fout(table) def _bw_table(bw_data): """Generates a bandwidth usage table""" table = formatting.Table(['Type', 'In GB', 'Out GB', 'Allotment']) for bw_point in bw_data.get('usage'): bw_type = 'Private' allotment = 'N/A' if bw_point['type']['alias'] == 'PUBLIC_SERVER_BW': bw_type = 'Public' if not bw_data.get('allotment'): allotment = '-' else: allotment = utils.lookup(bw_data, 'allotment', 'amount') table.add_row([bw_type, bw_point['amountIn'], bw_point['amountOut'], allotment]) return table def _system_table(system_data): table = formatting.Table(['Type', 'name']) for system in system_data: table.add_row([utils.lookup(system, 'hardwareComponentModel', 'hardwareGenericComponentModel', 'hardwareComponentType', 'keyName'), utils.lookup(system, 'hardwareComponentModel', 'longDescription')]) return table softlayer-python-5.9.8/SoftLayer/CLI/hardware/dns.py000066400000000000000000000056711415376457700223720ustar00rootroot00000000000000"""Sync DNS records.""" # :license: MIT, see LICENSE for more details. # pylint: disable=duplicate-code 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.""" mask = """mask[id, globalIdentifier, fullyQualifiedDomainName, hostname, domain, primaryBackendIpAddress,primaryIpAddress, primaryNetworkComponent[id,primaryIpAddress,primaryVersion6IpAddressRecord[ipAddress]]]""" dns = SoftLayer.DNSManager(env.client) server = SoftLayer.HardwareManager(env.client) server_id = helpers.resolve_id(server.resolve_ids, identifier, 'VS') instance = server.get_hardware(server_id, mask=mask) zone_id = helpers.resolve_id(dns.resolve_ids, instance['domain'], name='zone') if not instance['primaryIpAddress']: raise exceptions.CLIAbort('No primary IP address associated with this hardware') 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 will be true only if no options are passed in, basically. both = (not ptr) and (not a_record) and (not aaaa_record) if both or a_record: dns.sync_host_record(zone_id, instance['hostname'], instance['primaryIpAddress'], 'a', ttl) if both or ptr: # getReverseDomainRecords returns a list of 1 element, so just get the top. ptr_domains = env.client['Hardware_Server'].getReverseDomainRecords(id=instance['id']).pop() dns.sync_ptr_record(ptr_domains, instance['primaryIpAddress'], instance['fullyQualifiedDomainName'], ttl) if aaaa_record: try: # done this way to stay within 80 character lines ipv6 = instance['primaryNetworkComponent']['primaryVersion6IpAddressRecord']['ipAddress'] dns.sync_host_record(zone_id, instance['hostname'], ipv6, 'aaaa', ttl) except KeyError as ex: message = "{} does not have an ipv6 address".format(instance['fullyQualifiedDomainName']) raise exceptions.CLIAbort(message) from ex softlayer-python-5.9.8/SoftLayer/CLI/hardware/edit.py000066400000000000000000000054501415376457700225260ustar00rootroot00000000000000"""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', type=click.Path(exists=True, readable=True, resolve_path=True), help="Read userdata from file") @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', default=None, type=click.Choice(['0', '10', '100', '1000', '10000', '-1']), help="Public port speed. -1 is best speed available") @click.option('--private-speed', default=None, type=click.Choice(['0', '10', '100', '1000', '10000', '-1']), help="Private port speed. -1 is best speed available") @click.option('--redundant', is_flag=True, default=False, help="The desired state of redundancy for the interface(s)") @click.option('--degraded', is_flag=True, default=False, help="The desired state of degraded for the interface(s)") @environment.pass_env def cli(env, identifier, domain, userfile, tag, hostname, userdata, public_speed, private_speed, redundant, degraded): """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', encoding="utf-8") 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: if redundant: mgr.change_port_speed(hw_id, True, int(public_speed), 'redundant') if degraded: mgr.change_port_speed(hw_id, True, int(public_speed), 'degraded') if not redundant and not degraded: raise exceptions.CLIAbort("Failed to update hardwar") if private_speed is not None: if redundant: mgr.change_port_speed(hw_id, False, int(private_speed), 'redundant') if degraded: mgr.change_port_speed(hw_id, False, int(private_speed), 'degraded') if not redundant and not degraded: raise exceptions.CLIAbort("Failed to update hardware") softlayer-python-5.9.8/SoftLayer/CLI/hardware/guests.py000066400000000000000000000023051415376457700231070ustar00rootroot00000000000000"""Lists the Virtual Guests running on this 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 import utils @click.command() @click.argument('identifier') @environment.pass_env def cli(env, identifier): """Lists the Virtual Guests running on this server.""" mgr = SoftLayer.HardwareManager(env.client) hw_id = helpers.resolve_id(mgr.resolve_ids, identifier, 'hardware') hw_guests = mgr.get_hardware_guests(hw_id) if not hw_guests: raise exceptions.CLIAbort("No Virtual Guests found.") table = formatting.Table(['id', 'hostname', 'CPU', 'Memory', 'Start Date', 'Status', 'powerState']) table.sortby = 'hostname' for guest in hw_guests: table.add_row([ guest['id'], guest['hostname'], '%i %s' % (guest['maxCpu'], guest['maxCpuUnits']), guest['maxMemory'], utils.clean_time(guest['createDate']), guest['status']['keyName'], guest['powerState']['keyName'] ]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/hardware/list.py000066400000000000000000000060211415376457700225470ustar00rootroot00000000000000"""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='activeTransaction[id, transactionStatus[name, friendlyName]]'), 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) @click.option('--limit', '-l', help='How many results to get in one api call, default is 100', default=100, show_default=True) @environment.pass_env def cli(env, sortby, cpu, domain, datacenter, hostname, memory, network, tag, columns, limit): """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="mask(SoftLayer_Hardware_Server)[%s]" % columns.mask(), limit=limit) 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.9.8/SoftLayer/CLI/hardware/power.py000066400000000000000000000055761415376457700227460ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/hardware/ready.py000066400000000000000000000014561415376457700227070ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/hardware/reflash_firmware.py000066400000000000000000000014121415376457700251130ustar00rootroot00000000000000"""Reflash 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): """Reflash 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 ' 'reflash device firmware. Continue?' % hw_id)): raise exceptions.CLIAbort('Aborted.') mgr.reflash_firmware(hw_id) softlayer-python-5.9.8/SoftLayer/CLI/hardware/reload.py000066400000000000000000000025571415376457700230540ustar00rootroot00000000000000"""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") @click.option('--lvm', '-l', is_flag=True, default=False, show_default=True, help="A flag indicating that the provision should use LVM for all logical drives.") @environment.pass_env def cli(env, identifier, postinstall, key, lvm): """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, lvm) softlayer-python-5.9.8/SoftLayer/CLI/hardware/sensor.py000066400000000000000000000055301415376457700231110ustar00rootroot00000000000000"""Hardware internal Sensor .""" # :license: MIT, see LICENSE for more details. import SoftLayer from SoftLayer.CLI import click from SoftLayer.CLI import environment from SoftLayer.CLI import formatting @click.command() @click.argument('identifier') @click.option('--discrete', is_flag=True, default=False, help='Show discrete units associated hardware sensor') @environment.pass_env def cli(env, identifier, discrete): """Retrieve a server’s hardware state via its internal sensors.""" mgr = SoftLayer.HardwareManager(env.client) sensors = mgr.get_sensors(identifier) temperature_table = formatting.Table(["Sensor", "Status", "Reading", "Min", "Max"], title='Temperature (c)') volts_table = formatting.Table(["Sensor", "Status", "Reading", "Min", "Max"], title='Volts') watts_table = formatting.Table(["Sensor", "Status", "Reading"], title='Watts') rpm_table = formatting.Table(["Sensor", "Status", "Reading", "Min"], title='RPM') discrete_table = formatting.Table(["Sensor", "Status", "Reading"], title='Discrete') for sensor in sensors: if sensor.get('sensorUnits') == 'degrees C': temperature_table.add_row([sensor.get('sensorId'), sensor.get('status'), sensor.get('sensorReading'), sensor.get('upperNonCritical'), sensor.get('upperCritical')]) if sensor.get('sensorUnits') == 'volts': volts_table.add_row([sensor.get('sensorId'), sensor.get('status'), sensor.get('sensorReading'), sensor.get('lowerNonCritical'), sensor.get('lowerCritical')]) if sensor.get('sensorUnits') == 'Watts': watts_table.add_row([sensor.get('sensorId'), sensor.get('status'), sensor.get('sensorReading')]) if sensor.get('sensorUnits') == 'RPM': rpm_table.add_row([sensor.get('sensorId'), sensor.get('status'), sensor.get('sensorReading'), sensor.get('lowerCritical')]) if sensor.get('sensorUnits') == 'discrete': discrete_table.add_row([sensor.get('sensorId'), sensor.get('status'), sensor.get('sensorReading')]) env.fout(temperature_table) env.fout(rpm_table) env.fout(volts_table) env.fout(watts_table) if discrete: env.fout(discrete_table) softlayer-python-5.9.8/SoftLayer/CLI/hardware/storage.py000066400000000000000000000054471415376457700232530ustar00rootroot00000000000000"""Get storage details for a hardware server.""" # :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): """Get storage details for a hardware server.""" hardware = SoftLayer.HardwareManager(env.client) hardware_id = helpers.resolve_id(hardware.resolve_ids, identifier, 'hardware') iscsi_storage_data = hardware.get_storage_details(hardware_id, "ISCSI") nas_storage_data = hardware.get_storage_details(hardware_id, "NAS") storage_credentials = hardware.get_storage_credentials(hardware_id) hard_drives = hardware.get_hard_drives(hardware_id) table_credentials = formatting.Table(['Username', 'Password', 'IQN'], title="Block Storage Details \n iSCSI") if storage_credentials: table_credentials.add_row([storage_credentials['credential']['username'], storage_credentials['credential']['password'], storage_credentials['name']]) table_iscsi = formatting.Table(['LUN name', 'capacity', 'Target address', 'Location', 'Notes']) for iscsi in iscsi_storage_data: table_iscsi.add_row([iscsi['username'], iscsi['capacityGb'], iscsi['serviceResourceBackendIpAddress'], iscsi['allowedHardware'][0]['datacenter']['longName'], iscsi.get('notes', None)]) table_nas = formatting.Table(['Volume name', 'capacity', 'Host Name', 'Location', 'Notes'], title="File Storage Details") for nas in nas_storage_data: table_nas.add_row([nas['username'], nas['capacityGb'], nas['serviceResourceBackendIpAddress'], nas['allowedHardware'][0]['datacenter']['longName'], nas.get('notes', None)]) table_hard_drives = formatting.Table(['Type', 'Name', 'Capacity', 'Serial #'], title="Other storage details") for drives in hard_drives: type_drive = drives['hardwareComponentModel']['hardwareGenericComponentModel']['hardwareComponentType']['type'] name = drives['hardwareComponentModel']['manufacturer'] + " " + drives['hardwareComponentModel']['name'] capacity = str(drives['hardwareComponentModel']['hardwareGenericComponentModel']['capacity']) + " " + str( drives['hardwareComponentModel']['hardwareGenericComponentModel']['units']) serial = drives['serialNumber'] table_hard_drives.add_row([type_drive, name, capacity, serial]) env.fout(table_credentials) env.fout(table_iscsi) env.fout(table_nas) env.fout(table_hard_drives) softlayer-python-5.9.8/SoftLayer/CLI/hardware/toggle_ipmi.py000066400000000000000000000012731415376457700240770ustar00rootroot00000000000000"""Toggle the IPMI interface on and off.""" # :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.option('--enable/--disable', default=True, help="Whether enable (DEFAULT) or disable the interface.") @environment.pass_env def cli(env, identifier, enable): """Toggle the IPMI interface on and off""" mgr = SoftLayer.HardwareManager(env.client) hw_id = helpers.resolve_id(mgr.resolve_ids, identifier, 'hardware') result = env.client['Hardware_Server'].toggleManagementInterface(enable, id=hw_id) env.fout(result) softlayer-python-5.9.8/SoftLayer/CLI/hardware/update_firmware.py000066400000000000000000000014061415376457700247540ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/hardware/upgrade.py000066400000000000000000000165401415376457700232320ustar00rootroot00000000000000"""Upgrade a hardware server.""" # :license: MIT, see LICENSE for more details. import click from SoftLayer import utils 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('--memory', type=click.INT, help="Memory Size in GB") @click.option('--network', help="Network port speed in Mbps", default=None, type=click.Choice(['100', '100 Redundant', '100 Dual', '1000', '1000 Redundant', '1000 Dual', '10000', '10000 Redundant', '10000 Dual']) ) @click.option('--drive-controller', help="Drive Controller", default=None, type=click.Choice(['Non-RAID', 'RAID'])) @click.option('--public-bandwidth', type=click.INT, help="Public Bandwidth in GB") @click.option('--add-disk', nargs=2, multiple=True, type=(int, int), help="Add a Hard disk in GB to a specific channel, e.g 1000 GB in disk2, it will be " "--add-disk 1000 2") @click.option('--resize-disk', nargs=2, multiple=True, type=(int, int), help="Upgrade a specific disk size in GB, e.g --resize-disk 2000 2") @click.option('--test', is_flag=True, default=False, help="Do not actually upgrade the hardware server") @environment.pass_env def cli(env, identifier, memory, network, drive_controller, public_bandwidth, add_disk, resize_disk, test): """Upgrade a Hardware Server.""" mgr = SoftLayer.HardwareManager(env.client) table = formatting.KeyValueTable(['name', 'value']) table.align['name'] = 'r' table.align['value'] = 'l' if not any([memory, network, drive_controller, public_bandwidth, add_disk, resize_disk]): raise exceptions.ArgumentError("Must provide " " [--memory], [--network], [--drive-controller], [--public-bandwidth]," "[--add-disk] or [--resize-disk]") hw_id = helpers.resolve_id(mgr.resolve_ids, identifier, 'Hardware') if not test: if not (env.skip_confirmations or formatting.confirm( "This action will incur charges on your account. Continue?")): raise exceptions.CLIAbort('Aborted') disk_list = [] if add_disk: for guest_disk in add_disk: disks = {'description': 'add_disk', 'capacity': guest_disk[0], 'number': guest_disk[1]} disk_list.append(disks) if resize_disk: for guest_disk in resize_disk: disks = {'description': 'resize_disk', 'capacity': guest_disk[0], 'number': guest_disk[1]} disk_list.append(disks) response = mgr.upgrade(hw_id, memory=memory, nic_speed=network, drive_controller=drive_controller, public_bandwidth=public_bandwidth, disk=disk_list, test=test) if response: if test: add_data_to_table(response, table) else: table.add_row(['Order Date', response.get('orderDate')]) table.add_row(['Order Id', response.get('orderId')]) add_data_to_table(response['orderDetails'], table) place_order_table = get_place_order_information(response) table.add_row(['Place Order Information', place_order_table]) order_detail_table = get_order_detail(response) table.add_row(['Order Detail (Billing Information)', order_detail_table]) env.fout(table) def add_data_to_table(response, table): """Add the hardware server upgrade result to the table""" table.add_row(['location', utils.lookup(response, 'locationObject', 'longName')]) table.add_row(['quantity', response.get('quantity')]) table.add_row(['Package Id', response.get('packageId')]) table.add_row(['Currency Short Name', response.get('currencyShortName')]) table.add_row(['Prorated Initial Charge', response.get('proratedInitialCharge')]) table.add_row(['Prorated Order Total', response.get('proratedOrderTotal')]) table.add_row(['Hourly Pricing', response.get('useHourlyPricing')]) table_hardware = get_hardware_detail(response) table.add_row(['Hardware', table_hardware]) table_prices = get_hardware_prices(response) table.add_row(['Prices', table_prices]) def get_place_order_information(response): """Get the hardware server place order information.""" table_place_order = formatting.Table(['Id', 'Account Id', 'Status', 'Account CompanyName', 'UserRecord FirstName', 'UserRecord LastName', 'UserRecord Username']) table_place_order.add_row([response.get('id'), response.get('accountId'), response.get('status'), utils.lookup(response, 'account', 'companyName'), utils.lookup(response, 'userRecord', 'firstName'), utils.lookup(response, 'account', 'lastName'), utils.lookup(response, 'account', 'username')]) return table_place_order def get_hardware_detail(response): """Get the hardware server detail.""" table_hardware = formatting.Table(['Account Id', 'Hostname', 'Domain']) for hardware in response['hardware']: table_hardware.add_row([hardware.get('accountId'), hardware.get('hostname'), hardware.get('domain')]) return table_hardware def get_hardware_prices(response): """Get the hardware server prices.""" table_prices = formatting.Table(['Id', 'HourlyRecurringFee', 'RecurringFee', 'Categories', 'Item Description', 'Item Units']) for price in response['prices']: categories = price.get('categories')[0] table_prices.add_row([price.get('id'), price.get('hourlyRecurringFee'), price.get('recurringFee'), categories.get('name'), utils.lookup(price, 'item', 'description'), utils.lookup(price, 'item', 'units')]) return table_prices def get_order_detail(response): """Get the hardware server order detail.""" table_order_detail = formatting.Table(['Billing City', 'Billing Country Code', 'Billing Email', 'Billing Name First', 'Billing Name Last', 'Billing Postal Code', 'Billing State']) table_order_detail.add_row([utils.lookup(response, 'orderDetails', 'billingInformation', 'billingCity'), utils.lookup(response, 'orderDetails', 'billingInformation', 'billingCountryCode'), utils.lookup(response, 'orderDetails', 'billingInformation', 'billingEmail'), utils.lookup(response, 'orderDetails', 'billingInformation', 'billingNameFirst'), utils.lookup(response, 'orderDetails', 'billingInformation', 'billingNameLast'), utils.lookup(response, 'orderDetails', 'billingInformation', 'billingPostalCode'), utils.lookup(response, 'orderDetails', 'billingInformation', 'billingState')]) return table_order_detail softlayer-python-5.9.8/SoftLayer/CLI/helpers.py000066400000000000000000000026331415376457700214460ustar00rootroot00000000000000""" 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 """ try: return int(identifier) except ValueError: pass # It was worth a shot 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.9.8/SoftLayer/CLI/image/000077500000000000000000000000001415376457700205105ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/image/__init__.py000066400000000000000000000010471415376457700226230ustar00rootroot00000000000000"""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 + (',firstChild,children[id,blockDevicesDiskSpaceTotal,datacenter,' 'transaction[transactionGroup,transactionStatus]],' 'note,createDate,status,transaction') PUBLIC_TYPE = formatting.FormattedItem('PUBLIC', 'Public') PRIVATE_TYPE = formatting.FormattedItem('PRIVATE', 'Private') softlayer-python-5.9.8/SoftLayer/CLI/image/datacenter.py000066400000000000000000000014311415376457700231730ustar00rootroot00000000000000"""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 helpers @click.command() @click.argument('identifier') @click.option('--add/--remove', default=True, help="To add or remove Datacenter") @click.argument('locations', nargs=-1, required=True) @environment.pass_env def cli(env, identifier, add, locations): """Add/Remove datacenter of an image.""" image_mgr = SoftLayer.ImageManager(env.client) image_id = helpers.resolve_id(image_mgr.resolve_ids, identifier, 'image') if add: result = image_mgr.add_locations(image_id, locations) else: result = image_mgr.remove_locations(image_id, locations) env.fout(result) softlayer-python-5.9.8/SoftLayer/CLI/image/delete.py000066400000000000000000000007221415376457700223250ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/image/detail.py000066400000000000000000000057531415376457700223360ustar00rootroot00000000000000"""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) children_images = image.get('children') total_size = utils.lookup(image, 'firstChild', 'blockDevicesDiskSpaceTotal') or 0 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.listing(_get_transaction_groups(children_images), separator=','), ]) 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(['total_size', formatting.b_to_gb(total_size)]) table.add_row(['datacenters', _get_datacenter_table(children_images)]) env.fout(table) def _get_datacenter_table(children_images): """Returns image details as datacenter, size, and transaction within a formatting table. :param children_images: A list of images. """ table_datacenter = formatting.Table(['DC', 'size', 'transaction']) for child in children_images: table_datacenter.add_row([ utils.lookup(child, 'datacenter', 'name'), formatting.b_to_gb(child.get('blockDevicesDiskSpaceTotal', 0)), formatting.transaction_status(child.get('transaction')) ]) return table_datacenter def _get_transaction_groups(children_images): """Returns a Set of transaction groups. :param children_images: A list of images. """ transactions = set() for child in children_images: transactions.add(utils.lookup(child, 'transaction', 'transactionGroup', 'name')) return transactions softlayer-python-5.9.8/SoftLayer/CLI/image/edit.py000066400000000000000000000016031415376457700220070ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/image/export.py000066400000000000000000000024521415376457700224060ustar00rootroot00000000000000"""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') @click.option('--ibm-api-key', default=None, help="The IBM Cloud API Key with access to IBM Cloud Object " "Storage instance. For help creating this key see " "https://cloud.ibm.com/docs/cloud-object-storage?" "topic=cloud-object-storage-iam-overview#iam-overview" "-service-id-api-key " ) @environment.pass_env def cli(env, identifier, uri, ibm_api_key): """Export an image to object storage. The URI for an object storage object (.vhd/.iso file) of the format: swift://@// or cos://// if using IBM Cloud Object Storage """ 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, ibm_api_key) if not result: raise exceptions.CLIAbort("Failed to export Image") softlayer-python-5.9.8/SoftLayer/CLI/image/import.py000066400000000000000000000055451415376457700224050ustar00rootroot00000000000000"""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', help="The referenceCode of the operating system software" " description for the imported VHD, ISO, or RAW image") @click.option('--ibm-api-key', default=None, help="The IBM Cloud API Key with access to IBM Cloud Object " "Storage instance and IBM KeyProtect instance. For help " "creating this key see https://cloud.ibm.com/docs/" "cloud-object-storage?topic=cloud-object-storage" "-iam-overview#iam-overview-service-id-api-key") @click.option('--root-key-crn', default=None, help="CRN of the root key in your KMS instance") @click.option('--wrapped-dek', default=None, help="Wrapped Data Encryption Key provided by IBM KeyProtect. " "For more info see " "https://cloud.ibm.com/docs/key-protect?topic=key-protect-wrap-keys") @click.option('--cloud-init', is_flag=True, help="Specifies if image is cloud-init") @click.option('--byol', is_flag=True, help="Specifies if image is bring your own license") @click.option('--is-encrypted', is_flag=True, help="Specifies if image is encrypted") @environment.pass_env def cli(env, name, note, os_code, uri, ibm_api_key, root_key_crn, wrapped_dek, cloud_init, byol, is_encrypted): """Import an image. The URI for an object storage object (.vhd/.iso file) of the format: swift://@// or cos://// if using IBM Cloud Object Storage """ image_mgr = SoftLayer.ImageManager(env.client) result = image_mgr.import_image_from_uri( name=name, note=note, os_code=os_code, uri=uri, ibm_api_key=ibm_api_key, root_key_crn=root_key_crn, wrapped_dek=wrapped_dek, cloud_init=cloud_init, byol=byol, is_encrypted=is_encrypted ) 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.9.8/SoftLayer/CLI/image/list.py000066400000000000000000000031011415376457700220300ustar00rootroot00000000000000"""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 not 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.9.8/SoftLayer/CLI/licenses/000077500000000000000000000000001415376457700212335ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/licenses/__init__.py000066400000000000000000000000001415376457700233320ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/licenses/cancel.py000066400000000000000000000006741415376457700230410ustar00rootroot00000000000000"""Cancel a license.""" # :licenses: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment @click.command() @click.argument('key') @click.option('--immediate', is_flag=True, help='Immediate cancellation') @environment.pass_env def cli(env, key, immediate): """Cancel a license.""" licenses = SoftLayer.LicensesManager(env.client) env.fout(licenses.cancel_item(key, immediate)) softlayer-python-5.9.8/SoftLayer/CLI/licenses/create.py000066400000000000000000000020321415376457700230450ustar00rootroot00000000000000"""Order/create a vwmare licenses.""" # :licenses: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting @click.command() @click.option('--key', '-k', required=True, prompt=True, help="The VMware License Key. " "To get could use the product_package::getItems id=301 with name Software License Package" "E.g VMWARE_VSAN_ENTERPRISE_TIER_III_65_124_TB_6_X_2") @click.option('--datacenter', '-d', required=True, prompt=True, help="Datacenter shortname") @environment.pass_env def cli(env, key, datacenter): """Order/Create License.""" item_package = [key] licenses = SoftLayer.LicensesManager(env.client) result = licenses.create(datacenter, item_package) 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']]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/licenses/create_options.py000066400000000000000000000015671415376457700246340ustar00rootroot00000000000000"""Licenses order options for a given VMware licenses.""" # :license: MIT, see LICENSE for more details. import click from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.managers.licenses import LicensesManager from SoftLayer import utils @click.command() @environment.pass_env def cli(env): """Server order options for a given chassis.""" licenses_manager = LicensesManager(env.client) options = licenses_manager.get_create_options() table = formatting.Table(['Id', 'description', 'keyName', 'capacity', 'recurringFee']) for item in options: table.add_row([item.get('id'), utils.trim_to(item.get('description'), 40), item.get('keyName'), item.get('capacity'), item.get('prices')[0]['recurringFee']]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/loadbal/000077500000000000000000000000001415376457700210245ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/loadbal/__init__.py000066400000000000000000000000261415376457700231330ustar00rootroot00000000000000"""Load balancers.""" softlayer-python-5.9.8/SoftLayer/CLI/loadbal/detail.py000066400000000000000000000135541415376457700226500ustar00rootroot00000000000000"""Get Load Balancer as a Service details.""" import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer import utils @click.command() @click.argument('identifier') @environment.pass_env def cli(env, identifier): """Get Load Balancer as a Service details.""" mgr = SoftLayer.LoadBalancerManager(env.client) _, lbid = mgr.get_lbaas_uuid_id(identifier) this_lb = mgr.get_lb(lbid) if this_lb.get('previousErrorText'): print(this_lb.get('previousErrorText')) table = lbaas_table(this_lb) env.fout(table) def lbaas_table(this_lb): """Generates a table from a list of LBaaS devices""" table = formatting.KeyValueTable(['name', 'value']) table.align['name'] = 'r' table.align['value'] = 'l' table.add_row(['Id', this_lb.get('id')]) table.add_row(['UUI', this_lb.get('uuid')]) table.add_row(['Name', this_lb.get('name')]) table.add_row(['Address', this_lb.get('address')]) table.add_row(['Type', SoftLayer.LoadBalancerManager.TYPE.get(this_lb.get('type'))]) table.add_row(['Location', utils.lookup(this_lb, 'datacenter', 'longName')]) table.add_row(['Description', this_lb.get('description')]) table.add_row(['Status', "{} / {}".format(this_lb.get('provisioningStatus'), this_lb.get('operatingStatus'))]) listener_table, pools = get_listener_table(this_lb) table.add_row(['Protocols', listener_table]) member_table = get_member_table(this_lb, pools) table.add_row(['Members', member_table]) hp_table = get_hp_table(this_lb) table.add_row(['Health Checks', hp_table]) l7pool_table = get_l7pool_table(this_lb) table.add_row(['L7 Pools', l7pool_table]) ssl_table = get_ssl_table(this_lb) table.add_row(['Ciphers', ssl_table]) return table def get_hp_table(this_lb): """Generates a table from a list of LBaaS devices""" # https://sldn.softlayer.com/reference/datatypes/SoftLayer_Network_LBaaS_HealthMonitor/ hp_table = formatting.Table(['UUID', 'Interval', 'Retries', 'Type', 'Timeout', 'Modify', 'Active']) for health in this_lb.get('healthMonitors', []): hp_table.add_row([ health.get('uuid'), health.get('interval'), health.get('maxRetries'), health.get('monitorType'), health.get('timeout'), utils.clean_time(health.get('modifyDate')), health.get('provisioningStatus') ]) return hp_table def get_member_table(this_lb, pools): """Generates a members table from a list of LBaaS devices""" # https://sldn.softlayer.com/reference/datatypes/SoftLayer_Network_LBaaS_Member/ member_col = ['UUID', 'Address', 'Weight', 'Modify', 'Active'] counter = 0 for uuid in pools.values(): member_col.append(f'P{counter}-> {uuid}') counter += 1 member_table = formatting.Table(member_col) for member in this_lb.get('members', []): row = [ member.get('uuid'), member.get('address'), member.get('weight'), utils.clean_time(member.get('modifyDate')), member.get('provisioningStatus') ] for uuid in pools: row.append(get_member_hp(this_lb.get('health'), member.get('uuid'), uuid)) member_table.add_row(row) return member_table def get_ssl_table(this_lb): """Generates a ssl table from a list of LBaaS devices""" # https://sldn.softlayer.com/reference/datatypes/SoftLayer_Network_LBaaS_SSLCipher/ ssl_table = formatting.Table(['Id', 'Name']) for ssl in this_lb.get('sslCiphers', []): ssl_table.add_row([ssl.get('id'), ssl.get('name')]) return ssl_table def get_listener_table(this_lb): """Generates a protocols table from a list of LBaaS devices""" pools = {} # https://sldn.softlayer.com/reference/datatypes/SoftLayer_Network_LBaaS_Listener/ listener_table = formatting.Table(['UUID', 'Max Connection', 'Mapping', 'Balancer', 'Modify', 'Active']) for listener in this_lb.get('listeners', []): pool = listener.get('defaultPool') priv_map = "{}:{}".format(pool['protocol'], pool['protocolPort']) pools[pool['uuid']] = priv_map mapping = "{}:{} -> {}".format(listener.get('protocol'), listener.get('protocolPort'), priv_map) listener_table.add_row([ listener.get('uuid'), listener.get('connectionLimit', 'None'), mapping, pool.get('loadBalancingAlgorithm', 'None'), utils.clean_time(listener.get('modifyDate')), listener.get('provisioningStatus') ]) return listener_table, pools def get_l7pool_table(this_lb): """Generates a l7Pools table from a list of LBaaS devices""" # https://sldn.softlayer.com/reference/datatypes/SoftLayer_Network_LBaaS_L7Pool/ l7_table = formatting.Table(['Id', 'UUID', 'Balancer', 'Name', 'Protocol', 'Modify', 'Active']) for layer7 in this_lb.get('l7Pools', []): l7_table.add_row([ layer7.get('id'), layer7.get('uuid'), layer7.get('loadBalancingAlgorithm'), layer7.get('name'), layer7.get('protocol'), utils.clean_time(layer7.get('modifyDate')), layer7.get('provisioningStatus') ]) return l7_table def get_member_hp(checks, member_uuid, pool_uuid): """Helper function to find a members health in a given pool :param checks list: https://sldn.softlayer.com/reference/datatypes/SoftLayer_Network_LBaaS_Pool/#healthMonitor :param member_uuid: server UUID we are looking for :param pool_uuid: Connection pool uuid to search for """ status = "---" for check in checks: if check.get('poolUuid') == pool_uuid: for hp_member in check.get('membersHealth'): if hp_member.get('uuid') == member_uuid: status = hp_member.get('status') return status softlayer-python-5.9.8/SoftLayer/CLI/loadbal/health.py000066400000000000000000000061111415376457700226420ustar00rootroot00000000000000"""Manage LBaaS health checks.""" import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.exceptions import SoftLayerAPIError from SoftLayer import utils @click.command() @click.argument('identifier') @click.option('--uuid', required=True, help="Health check UUID to modify.") @click.option('--interval', '-i', type=click.IntRange(2, 60), help="Seconds between checks. [2-60]") @click.option('--retry', '-r', type=click.IntRange(1, 10), help="Number of times before marking as DOWN. [1-10]") @click.option('--timeout', '-t', type=click.IntRange(1, 59), help="Seconds to wait for a connection. [1-59]") @click.option('--url', '-u', help="Url path for HTTP/HTTPS checks.") @environment.pass_env def cli(env, identifier, uuid, interval, retry, timeout, url): """Manage LBaaS health checks.""" if not any([interval, retry, timeout, url]): raise exceptions.ArgumentError("Specify either interval, retry, timeout, url") # map parameters to expected API names template = {'healthMonitorUuid': uuid, 'interval': interval, 'maxRetries': retry, 'timeout': timeout, 'urlPath': url} # Removes those empty values clean_template = {k: v for k, v in template.items() if v is not None} mgr = SoftLayer.LoadBalancerManager(env.client) # Need to get the LBaaS uuid if it wasn't supplied lb_uuid, lb_id = mgr.get_lbaas_uuid_id(identifier) print("UUID: {}, ID: {}".format(lb_uuid, lb_id)) # Get the current health checks, and find the one we are updating. mask = "mask[healthMonitors, listeners[uuid,defaultPool[healthMonitor]]]" lbaas = mgr.get_lb(lb_id, mask=mask) check = {} # Set the default values, because these all need to be set if we are not updating them. for listener in lbaas.get('listeners', []): if utils.lookup(listener, 'defaultPool', 'healthMonitor', 'uuid') == uuid: check['backendProtocol'] = utils.lookup(listener, 'defaultPool', 'protocol') check['backendPort'] = utils.lookup(listener, 'defaultPool', 'protocolPort') check['healthMonitorUuid'] = uuid check['interval'] = utils.lookup(listener, 'defaultPool', 'healthMonitor', 'interval') check['maxRetries'] = utils.lookup(listener, 'defaultPool', 'healthMonitor', 'maxRetries') check['timeout'] = utils.lookup(listener, 'defaultPool', 'healthMonitor', 'timeout') check['urlPath'] = utils.lookup(listener, 'defaultPool', 'healthMonitor', 'urlPath') if url and check['backendProtocol'] == 'TCP': raise exceptions.ArgumentError('--url cannot be used with TCP checks') # Update existing check with supplied values for key in clean_template.keys(): check[key] = clean_template[key] try: mgr.update_lb_health_monitors(lb_uuid, [check]) click.secho('Health Check {} updated successfully'.format(uuid), fg='green') except SoftLayerAPIError as exception: click.secho('Failed to update {}'.format(uuid), fg='red') click.secho("ERROR: {}".format(exception.faultString), fg='red') softlayer-python-5.9.8/SoftLayer/CLI/loadbal/layer7_policy_list.py000066400000000000000000000034471415376457700252230ustar00rootroot00000000000000"""List Layer7 policies""" import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting @click.command() @click.option('--protocol-id', '-p', required=False, type=int, help="Front-end Protocol identifier") @environment.pass_env def policies(env, protocol_id): """List policies of the front-end protocol (listener).""" mgr = SoftLayer.LoadBalancerManager(env.client) if protocol_id: l7policies = mgr.get_l7policies(protocol_id) table = generate_l7policies_table(l7policies, protocol_id) else: l7policies = mgr.get_all_l7policies() table = l7policies_table(l7policies) env.fout(table) def generate_l7policies_table(l7policies, identifier): """Takes a list of Layer7 policies and makes a table""" table = formatting.Table([ 'Id', 'UUID', 'Name', 'Action', 'Redirect', 'Priority', 'Create Date' ], title=f"Layer7 policies - protocol ID {identifier}") table.align['Name'] = 'l' table.align['Action'] = 'l' table.align['Redirect'] = 'l' for l7policy in sorted(l7policies, key=lambda data: data.get('priority')): table.add_row([ l7policy.get('id'), l7policy.get('uuid'), l7policy.get('name'), l7policy.get('action'), l7policy.get('redirectL7PoolId') or l7policy.get('redirectUrl') or formatting.blank(), l7policy.get('priority'), l7policy.get('createDate'), ]) return table def l7policies_table(listeners): """Takes a dict of (protocols: policies list) and makes a list of tables""" tables = [] for listener_id, list_policy in listeners.items(): tables.append(generate_l7policies_table(list_policy, listener_id)) return tables softlayer-python-5.9.8/SoftLayer/CLI/loadbal/list.py000066400000000000000000000030351415376457700223520ustar00rootroot00000000000000"""List active Load Balancer as a Service devices.""" 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 active Load Balancer as a Service devices.""" mgr = SoftLayer.LoadBalancerManager(env.client) lbaas = mgr.get_lbaas() if lbaas: lbaas_table = generate_lbaas_table(lbaas) env.fout(lbaas_table) else: env.fout("No LBaaS devices found") def location_sort(location): """Quick function that just returns the datacenter longName for sorting""" return utils.lookup(location, 'datacenter', 'longName') def generate_lbaas_table(lbaas): """Takes a list of SoftLayer_Network_LBaaS_LoadBalancer and makes a table""" table = formatting.Table([ 'Id', 'Location', 'Name', 'Description', 'Public', 'Create Date', 'Members', 'Listeners' ], title="IBM Cloud LoadBalancer") table.align['Name'] = 'l' table.align['Description'] = 'l' table.align['Location'] = 'l' for this_lb in sorted(lbaas, key=location_sort): table.add_row([ this_lb.get('id'), utils.lookup(this_lb, 'datacenter', 'longName'), this_lb.get('name'), this_lb.get('description'), 'Yes' if this_lb.get('isPublic', 1) == 1 else 'No', utils.clean_time(this_lb.get('createDate')), this_lb.get('memberCount', 0), this_lb.get('listenerCount', 0) ]) return table softlayer-python-5.9.8/SoftLayer/CLI/loadbal/members.py000066400000000000000000000040561415376457700230350ustar00rootroot00000000000000"""Manage LBaaS members.""" import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.exceptions import SoftLayerAPIError @click.command() @click.argument('identifier') @click.option('--member', '-m', required=True, help="Member UUID") @environment.pass_env def remove(env, identifier, member): """Remove a LBaaS member. Member UUID can be found from `slcli lb detail`. """ mgr = SoftLayer.LoadBalancerManager(env.client) uuid, _ = mgr.get_lbaas_uuid_id(identifier) # Get a member ID to remove try: mgr.delete_lb_member(uuid, member) click.secho("Member {} removed".format(member), fg='green') except SoftLayerAPIError as exception: click.secho("ERROR: {}".format(exception.faultString), fg='red') @click.command() @click.argument('identifier') @click.option('--private/--public', default=True, required=True, help="Private or public IP of the new member.") @click.option('--member', '-m', required=True, help="Member IP address.") @click.option('--weight', '-w', default=50, type=int, help="Weight of this member.") @environment.pass_env def add(env, identifier, private, member, weight): """Add a new LBaaS members.""" mgr = SoftLayer.LoadBalancerManager(env.client) uuid, _ = mgr.get_lbaas_uuid_id(identifier) # Get a server ID to add to_add = {"weight": weight} if private: to_add['privateIpAddress'] = member else: to_add['publicIpAddress'] = member try: mgr.add_lb_member(uuid, to_add) click.secho("Member {} added".format(member), fg='green') except SoftLayerAPIError as exception: if 'publicIpAddress must be a string' in exception.faultString: click.secho("This LB requires a Public IP address for its members and none was supplied", fg='red') elif 'privateIpAddress must be a string' in exception.faultString: click.secho("This LB requires a Private IP address for its members and none was supplied", fg='red') click.secho("ERROR: {}".format(exception.faultString), fg='red') softlayer-python-5.9.8/SoftLayer/CLI/loadbal/ns_detail.py000066400000000000000000000034171415376457700233450ustar00rootroot00000000000000"""Get Netscaler details.""" import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer import utils @click.command() @click.argument('identifier') @environment.pass_env def cli(env, identifier): """Get Netscaler details.""" mgr = SoftLayer.LoadBalancerManager(env.client) this_lb = mgr.get_adc(identifier) table = netscaler_table(this_lb) env.fout(table) def netscaler_table(this_lb): """Formats the netscaler info table""" table = formatting.KeyValueTable(['name', 'value']) table.align['name'] = 'r' table.align['value'] = 'l' table.add_row(['Id', this_lb.get('id')]) table.add_row(['Type', this_lb.get('description')]) table.add_row(['Name', this_lb.get('name')]) table.add_row(['Location', utils.lookup(this_lb, 'datacenter', 'longName')]) table.add_row(['Managment Ip', this_lb.get('managementIpAddress')]) table.add_row(['Root Password', utils.lookup(this_lb, 'password', 'password')]) table.add_row(['Primary Ip', this_lb.get('primaryIpAddress')]) table.add_row(['License Expiration', utils.clean_time(this_lb.get('licenseExpirationDate'))]) subnet_table = formatting.Table(['Id', 'Subnet', 'Type', 'Space']) for subnet in this_lb.get('subnets', []): subnet_table.add_row([ subnet.get('id'), "{}/{}".format(subnet.get('networkIdentifier'), subnet.get('cidr')), subnet.get('subnetType'), subnet.get('addressSpace') ]) table.add_row(['Subnets', subnet_table]) vlan_table = formatting.Table(['Id', 'Number']) for vlan in this_lb.get('networkVlans', []): vlan_table.add_row([vlan.get('id'), vlan.get('vlanNumber')]) table.add_row(['Vlans', vlan_table]) return table softlayer-python-5.9.8/SoftLayer/CLI/loadbal/ns_list.py000066400000000000000000000026151415376457700230550ustar00rootroot00000000000000"""List active Netscaler devices.""" 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 active Netscaler devices.""" mgr = SoftLayer.LoadBalancerManager(env.client) netscalers = mgr.get_adcs() if netscalers: adc_table = generate_netscaler_table(netscalers) env.fout(adc_table) else: env.fout("No Netscalers") def location_sort(location): """Quick function that just returns the datacenter longName for sorting""" return utils.lookup(location, 'datacenter', 'longName') def generate_netscaler_table(netscalers): """Tales a list of SoftLayer_Network_Application_Delivery_Controller and makes a table""" table = formatting.Table([ 'Id', 'Location', 'Name', 'Description', 'IP Address', 'Management Ip', 'Bandwidth', 'Create Date' ], title="Netscalers") for adc in sorted(netscalers, key=location_sort): table.add_row([ adc.get('id'), utils.lookup(adc, 'datacenter', 'longName'), adc.get('name'), adc.get('description'), adc.get('primaryIpAddress'), adc.get('managementIpAddress'), adc.get('outboundPublicBandwidthUsage', 0), utils.clean_time(adc.get('createDate')) ]) return table softlayer-python-5.9.8/SoftLayer/CLI/loadbal/order.py000066400000000000000000000155471415376457700225250ustar00rootroot00000000000000"""Order and Cancel LBaaS instances.""" import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting from SoftLayer.exceptions import SoftLayerAPIError from SoftLayer import utils # pylint: disable=unused-argument def parse_proto(ctx, param, value): """Parses the frontend and backend cli options""" proto = {'protocol': 'HTTP', 'port': 80} splitout = value.split(':') if len(splitout) != 2: raise exceptions.ArgumentError("{}={} is not properly formatted.".format(param, value)) proto['protocol'] = splitout[0] proto['port'] = int(splitout[1]) return proto @click.command() @click.option('--name', '-n', help='Label for this loadbalancer.', required=True) @click.option('--datacenter', '-d', help='Datacenter shortname (dal13).', required=True) @click.option('--label', '-l', help='A descriptive label for this loadbalancer.') @click.option('--frontend', '-f', required=True, default='HTTP:80', show_default=True, callback=parse_proto, help='PROTOCOL:PORT string for incoming internet connections.') @click.option('--backend', '-b', required=True, default='HTTP:80', show_default=True, callback=parse_proto, help='PROTOCOL:PORT string for connecting to backend servers.') @click.option('--method', '-m', help="Balancing Method.", default='ROUNDROBIN', show_default=True, type=click.Choice(['ROUNDROBIN', 'LEASTCONNECTION', 'WEIGHTED_RR'])) @click.option('--subnet', '-s', required=True, help="Private subnet Id to order the LB on. See `slcli lb order-options`") @click.option('--public', is_flag=True, default=False, show_default=True, help="Use a Public to Public loadbalancer.") @click.option('--verify', is_flag=True, default=False, show_default=True, help="Only verify an order, dont actually create one.") @environment.pass_env def order(env, **args): """Creates a LB. Protocols supported are TCP, HTTP, and HTTPS.""" mgr = SoftLayer.LoadBalancerManager(env.client) location = args.get('datacenter') name = args.get('name') description = args.get('label', None) backend = args.get('backend') frontend = args.get('frontend') protocols = [ { "backendPort": backend.get('port'), "backendProtocol": backend.get('protocol'), "frontendPort": frontend.get('port'), "frontendProtocol": frontend.get('protocol'), "loadBalancingMethod": args.get('method'), "maxConn": 1000 } ] # remove verify=True to place the order receipt = mgr.order_lbaas(location, name, description, protocols, args.get('subnet'), public=args.get('public'), verify=args.get('verify')) table = parse_receipt(receipt) env.fout(table) def parse_receipt(receipt): """Takes an order receipt and nicely formats it for cli output""" table = formatting.KeyValueTable(['Item', 'Cost'], title="Order: {}".format(receipt.get('orderId', 'Quote'))) if receipt.get('prices'): for price in receipt.get('prices'): table.add_row([price['item']['description'], price['hourlyRecurringFee']]) elif receipt.get('orderDetails'): for price in receipt['orderDetails']['prices']: table.add_row([price['item']['description'], price['hourlyRecurringFee']]) return table @click.command() @click.option('--datacenter', '-d', help="Show only selected datacenter, use shortname (dal13) format.") @environment.pass_env def order_options(env, datacenter): """Prints options for order a LBaaS""" print("Prints options for ordering") mgr = SoftLayer.LoadBalancerManager(env.client) net_mgr = SoftLayer.NetworkManager(env.client) package = mgr.lbaas_order_options() if not datacenter: data_table = formatting.KeyValueTable(['Datacenters', 'City']) for region in package['regions']: data_table.add_row([region['description'].split('-')[0], region['description'].split('-')[1]]) # print(region) env.fout(data_table) click.secho("Use `slcli lb order-options --datacenter ` " "to find pricing information and private subnets for that specific site.") else: for region in package['regions']: dc_name = utils.lookup(region, 'location', 'location', 'name') # Skip locations if they are not the one requested. if datacenter and dc_name != datacenter.lower(): continue l_groups = [] for group in region['location']['location']['groups']: l_groups.append(group.get('id')) # Price lookups prices = [] price_table = formatting.KeyValueTable(['KeyName', 'Cost'], title='Prices') for item in package['items']: i_price = {'keyName': item['keyName']} for price in item.get('prices', []): if not price.get('locationGroupId'): i_price['default_price'] = price.get('hourlyRecurringFee') elif price.get('locationGroupId') in l_groups: i_price['region_price'] = price.get('hourlyRecurringFee') prices.append(i_price) for price in prices: if price.get('region_price'): price_table.add_row([price.get('keyName'), price.get('region_price')]) else: price_table.add_row([price.get('keyName'), price.get('default_price')]) # Vlan/Subnet Lookups mask = "mask[networkVlan,podName,addressSpace]" subnets = net_mgr.list_subnets(datacenter=dc_name, network_space='PRIVATE', mask=mask) subnet_table = formatting.Table(['Id', 'Subnet', 'Vlan'], title='Private subnet') for subnet in subnets: # Only show these types, easier to filter here than in an API call. if subnet.get('subnetType') != 'PRIMARY' and \ subnet.get('subnetType') != 'ADDITIONAL_PRIMARY': continue space = "{}/{}".format(subnet.get('networkIdentifier'), subnet.get('cidr')) vlan = "{}.{}".format(subnet['podName'], subnet['networkVlan']['vlanNumber']) subnet_table.add_row([subnet.get('id'), space, vlan]) env.fout(price_table) env.fout(subnet_table) @click.command() @click.argument('identifier') @environment.pass_env def cancel(env, identifier): """Cancels a LBaaS instance""" mgr = SoftLayer.LoadBalancerManager(env.client) uuid, _ = mgr.get_lbaas_uuid_id(identifier) try: mgr.cancel_lbaas(uuid) click.secho("LB {} canceled succesfully.".format(identifier), fg='green') except SoftLayerAPIError as exception: click.secho("ERROR: {}".format(exception.faultString), fg='red') softlayer-python-5.9.8/SoftLayer/CLI/loadbal/pools.py000066400000000000000000000212231415376457700225320ustar00rootroot00000000000000"""Manage LBaaS Pools/Listeners.""" import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import helpers from SoftLayer.exceptions import SoftLayerAPIError # pylint: disable=unused-argument def sticky_option(ctx, param, value): """parses sticky cli option""" if value: return 'SOURCE_IP' return None def parse_server(ctx, param, values): """Splits out the IP, Port, Weight from the --server argument for l7pools""" servers = [] for server in values: splitout = server.split(':') if len(splitout) != 3: raise exceptions.ArgumentError( "--server needs a port and a weight. {} improperly formatted".format(server)) server = { 'address': splitout[0], 'port': splitout[1], 'weight': splitout[2] } servers.append(server) return servers @click.command() @click.argument('identifier') @click.option('--frontProtocol', '-P', default='HTTP', type=click.Choice(['HTTP', 'HTTPS', 'TCP']), show_default=True, help="Protocol type to use for incoming connections") @click.option('--backProtocol', '-p', type=click.Choice(['HTTP', 'HTTPS', 'TCP']), help="Protocol type to use when connecting to backend servers. Defaults to whatever --frontProtocol is.") @click.option('--frontPort', '-f', required=True, type=int, help="Internet side port") @click.option('--backPort', '-b', required=True, type=int, help="Private side port") @click.option('--method', '-m', default='ROUNDROBIN', show_default=True, help="Balancing Method", type=click.Choice(['ROUNDROBIN', 'LEASTCONNECTION', 'WEIGHTED_RR'])) @click.option('--connections', '-c', type=int, help="Maximum number of connections to allow.") @click.option('--sticky', '-s', is_flag=True, callback=sticky_option, help="Make sessions sticky based on source_ip.") @click.option('--sslCert', '-x', help="SSL certificate ID. See `slcli ssl list`") @environment.pass_env def add(env, identifier, **args): """Adds a listener to the identifier LB""" mgr = SoftLayer.LoadBalancerManager(env.client) uuid, _ = mgr.get_lbaas_uuid_id(identifier) new_listener = { 'backendPort': args.get('backport'), 'backendProtocol': args.get('backprotocol') if args.get('backprotocol') else args.get('frontprotocol'), 'frontendPort': args.get('frontport'), 'frontendProtocol': args.get('frontprotocol'), 'loadBalancingMethod': args.get('method'), 'maxConn': args.get('connections', None), 'sessionType': args.get('sticky'), 'tlsCertificateId': args.get('sslcert') } try: mgr.add_lb_listener(uuid, new_listener) click.secho("Success", fg='green') except SoftLayerAPIError as exception: click.secho("ERROR: {}".format(exception.faultString), fg='red') @click.command() @click.argument('identifier') @click.argument('listener') @click.option('--frontProtocol', '-P', type=click.Choice(['HTTP', 'HTTPS', 'TCP']), help="Protocol type to use for incoming connections") @click.option('--backProtocol', '-p', type=click.Choice(['HTTP', 'HTTPS', 'TCP']), help="Protocol type to use when connecting to backend servers. Defaults to whatever --frontProtocol is.") @click.option('--frontPort', '-f', type=int, help="Internet side port") @click.option('--backPort', '-b', type=int, help="Private side port") @click.option('--method', '-m', help="Balancing Method", type=click.Choice(['ROUNDROBIN', 'LEASTCONNECTION', 'WEIGHTED_RR'])) @click.option('--connections', '-c', type=int, help="Maximum number of connections to allow.") @click.option('--clientTimeout', '-t', type=int, help="maximum idle time in seconds(Range: 1 to 7200).") @click.option('--sticky', '-s', is_flag=True, callback=sticky_option, help="Make sessions sticky based on source_ip.") @click.option('--sslCert', '-x', help="SSL certificate ID. See `slcli ssl list`") @environment.pass_env def edit(env, identifier, listener, **args): """Updates a listener's configuration. LISTENER should be a UUID, and can be found from `slcli lb detail ` """ mgr = SoftLayer.LoadBalancerManager(env.client) uuid, _ = mgr.get_lbaas_uuid_id(identifier) new_listener = { 'listenerUuid': listener } arg_to_option = { 'frontprotocol': 'frontendProtocol', 'backprotocol': 'backendProtocol', 'frontport': 'frontendPort', 'backport': 'backendPort', 'method': 'loadBalancingMethod', 'connections': 'maxConn', 'sticky': 'sessionType', 'clienttimeout': 'clientTimeout', 'sslcert': 'tlsCertificateId' } for key, value in args.items(): if value: new_listener[arg_to_option[key]] = value try: mgr.add_lb_listener(uuid, new_listener) click.secho("Success", fg='green') except SoftLayerAPIError as exception: click.secho("ERROR: {}".format(exception.faultString), fg='red') @click.command() @click.argument('identifier') @click.argument('listener') @environment.pass_env def delete(env, identifier, listener): """Removes the listener from identified LBaaS instance LISTENER should be a UUID, and can be found from `slcli lb detail ` """ mgr = SoftLayer.LoadBalancerManager(env.client) uuid, _ = mgr.get_lbaas_uuid_id(identifier) try: mgr.remove_lb_listener(uuid, listener) click.secho("Success", fg='green') except SoftLayerAPIError as exception: click.secho("ERROR: {}".format(exception.faultString), fg='red') @click.command() @click.argument('identifier') # https://sldn.softlayer.com/reference/datatypes/SoftLayer_Network_LBaaS_L7Pool/ @click.option('--name', '-n', required=True, help="Name for this L7 pool.") @click.option('--method', '-m', help="Balancing Method.", default='ROUNDROBIN', show_default=True, type=click.Choice(['ROUNDROBIN', 'LEASTCONNECTION', 'WEIGHTED_RR'])) @click.option('--protocol', '-P', type=click.Choice(['HTTP', 'HTTPS']), default='HTTP', show_default=True, help="Protocol type to use for incoming connections") # https://sldn.softlayer.com/reference/datatypes/SoftLayer_Network_LBaaS_L7Member/ @helpers.multi_option('--server', '-S', callback=parse_server, required=True, help="Backend servers that are part of this pool. Format is colon deliminated. " "BACKEND_IP:PORT:WEIGHT. eg. 10.0.0.1:80:50") # https://sldn.softlayer.com/reference/datatypes/SoftLayer_Network_LBaaS_L7HealthMonitor/ @click.option('--healthPath', default='/', show_default=True, help="Health check path.") @click.option('--healthInterval', default=5, type=int, show_default=True, help="Health check interval between checks.") @click.option('--healthRetry', default=2, type=int, show_default=True, help="Health check number of times before marking as DOWN.") @click.option('--healthTimeout', default=2, type=int, show_default=True, help="Health check timeout.") # https://sldn.softlayer.com/reference/datatypes/SoftLayer_Network_LBaaS_L7SessionAffinity/ @click.option('--sticky', '-s', is_flag=True, callback=sticky_option, help="Make sessions sticky based on source_ip.") @environment.pass_env def l7pool_add(env, identifier, **args): """Adds a new l7 pool -S is in colon deliminated format to make grouping IP:port:weight a bit easier. """ mgr = SoftLayer.LoadBalancerManager(env.client) uuid, _ = mgr.get_lbaas_uuid_id(identifier) pool_main = { 'name': args.get('name'), 'loadBalancingAlgorithm': args.get('method'), 'protocol': args.get('protocol') } pool_members = list(args.get('server')) pool_health = { 'interval': args.get('healthinterval'), 'timeout': args.get('healthtimeout'), 'maxRetries': args.get('healthretry'), 'urlPath': args.get('healthpath') } pool_sticky = { 'type': args.get('sticky') } try: mgr.add_lb_l7_pool(uuid, pool_main, pool_members, pool_health, pool_sticky) click.secho("Success", fg='green') except SoftLayerAPIError as exception: click.secho("ERROR: {}".format(exception.faultString), fg='red') @click.command() @click.argument('identifier') @environment.pass_env def l7pool_del(env, identifier): """Deletes the identified pool Identifier is L7Pool Id. NOT the UUID """ mgr = SoftLayer.LoadBalancerManager(env.client) try: mgr.del_lb_l7_pool(identifier) click.secho("Success", fg='green') except SoftLayerAPIError as exception: click.secho("ERROR: {}".format(exception.faultString), fg='red') softlayer-python-5.9.8/SoftLayer/CLI/metadata.py000066400000000000000000000046711415376457700215700ustar00rootroot00000000000000"""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 the machine making these API calls. .. csv-table:: Choices {choices} """.format(choices="\n ".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 as ex: message = 'Cannot connect to the backend service address. Make sure '\ 'this command is being ran from a device on the backend network.' raise exceptions.CLIAbort(message) from ex 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.9.8/SoftLayer/CLI/nas/000077500000000000000000000000001415376457700202075ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/nas/__init__.py000066400000000000000000000000401415376457700223120ustar00rootroot00000000000000"""Network Attached Storage.""" softlayer-python-5.9.8/SoftLayer/CLI/nas/credentials.py000066400000000000000000000011401415376457700230520ustar00rootroot00000000000000"""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.get('username', 'None'), result.get('password', 'None')]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/nas/list.py000066400000000000000000000020441415376457700215340ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/object_storage/000077500000000000000000000000001415376457700224205ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/object_storage/__init__.py000066400000000000000000000000261415376457700245270ustar00rootroot00000000000000"""Object Storage.""" softlayer-python-5.9.8/SoftLayer/CLI/object_storage/credential/000077500000000000000000000000001415376457700245325ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/object_storage/credential/__init__.py000066400000000000000000000024101415376457700266400ustar00rootroot00000000000000"""Manages Object Storage S3 Credentials.""" # :license: MIT, see LICENSE for more details. import importlib import os import click CONTEXT = {'help_option_names': ['-h', '--help'], 'max_content_width': 999} class CapacityCommands(click.MultiCommand): """Loads module for object storage S3 credentials related commands.""" def __init__(self, **attrs): click.MultiCommand.__init__(self, **attrs) self.path = os.path.dirname(__file__) def list_commands(self, ctx): """List all sub-commands.""" commands = [] for filename in os.listdir(self.path): if filename == '__init__.py': continue if filename.endswith('.py'): commands.append(filename[:-3].replace("_", "-")) commands.sort() return commands def get_command(self, ctx, cmd_name): """Get command for click.""" path = "%s.%s" % (__name__, cmd_name) path = path.replace("-", "_") module = importlib.import_module(path) return getattr(module, 'cli') # Required to get the sub-sub-sub command to work. @click.group(cls=CapacityCommands, context_settings=CONTEXT) def cli(): """Base command for all object storage credentials S3 related concerns""" softlayer-python-5.9.8/SoftLayer/CLI/object_storage/credential/create.py000066400000000000000000000016071415376457700263530ustar00rootroot00000000000000"""Create credentials for an IBM Cloud Object Storage Account.""" # :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): """Create credentials for an IBM Cloud Object Storage Account""" mgr = SoftLayer.ObjectStorageManager(env.client) storage_id = helpers.resolve_id(mgr.resolve_ids, identifier, 'Object Storage') credential = mgr.create_credential(storage_id) table = formatting.Table(['id', 'password', 'username', 'type_name']) table.sortby = 'id' table.add_row([ credential.get('id'), credential.get('password'), credential.get('username'), credential.get('type', {}).get('name') ]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/object_storage/credential/delete.py000066400000000000000000000013641415376457700263520ustar00rootroot00000000000000"""Delete the credential of an Object Storage Account.""" # :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.option('--credential_id', '-c', type=click.INT, help="This is the credential id associated with the volume") @environment.pass_env def cli(env, identifier, credential_id): """Delete the credential of an Object Storage Account.""" mgr = SoftLayer.ObjectStorageManager(env.client) storage_id = helpers.resolve_id(mgr.resolve_ids, identifier, 'Object Storage') credential = mgr.delete_credential(storage_id, credential_id=credential_id) env.fout(credential) softlayer-python-5.9.8/SoftLayer/CLI/object_storage/credential/limit.py000066400000000000000000000013331415376457700262220ustar00rootroot00000000000000""" Credential limits for this IBM Cloud Object Storage account.""" # :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): """Credential limits for this IBM Cloud Object Storage account.""" mgr = SoftLayer.ObjectStorageManager(env.client) storage_id = helpers.resolve_id(mgr.resolve_ids, identifier, 'Object Storage') credential_limit = mgr.limit_credential(storage_id) table = formatting.Table(['limit']) table.add_row([ credential_limit, ]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/object_storage/credential/list.py000066400000000000000000000017061415376457700260630ustar00rootroot00000000000000"""Retrieve credentials used for generating an AWS signature. Max of 2.""" # :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): """Retrieve credentials used for generating an AWS signature. Max of 2.""" mgr = SoftLayer.ObjectStorageManager(env.client) storage_id = helpers.resolve_id(mgr.resolve_ids, identifier, 'Object Storage') credential_list = mgr.list_credential(storage_id) table = formatting.Table(['id', 'password', 'username', 'type_name']) for credential in credential_list: table.add_row([ credential.get('id'), credential.get('password'), credential.get('username'), credential.get('type', {}).get('name') ]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/object_storage/list_accounts.py000066400000000000000000000017071415376457700256510ustar00rootroot00000000000000"""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() @click.option('--limit', type=int, default=10, help="Result limit") @environment.pass_env def cli(env, limit): """List object storage accounts.""" mgr = SoftLayer.ObjectStorageManager(env.client) accounts = mgr.list_accounts(limit=limit) table = formatting.Table(['id', 'name', 'apiType']) table.sortby = 'id' api_type = None for account in accounts: if 'vendorName' in account and account['vendorName'] == 'Swift': api_type = 'Swift' elif 'Cleversafe' in account['serviceResource']['name']: api_type = 'S3' table.add_row([ account['id'], account['username'], api_type, ]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/object_storage/list_endpoints.py000066400000000000000000000012011415376457700260220ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/order/000077500000000000000000000000001415376457700205415ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/order/__init__.py000066400000000000000000000001261415376457700226510ustar00rootroot00000000000000"""View and order from the catalog.""" # :license: MIT, see LICENSE for more details. softlayer-python-5.9.8/SoftLayer/CLI/order/category_list.py000066400000000000000000000023641415376457700237700ustar00rootroot00000000000000"""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. :: # List the categories of Bare Metal servers slcli order category-list BARE_METAL_SERVER # 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.9.8/SoftLayer/CLI/order/item_list.py000066400000000000000000000131031415376457700231020ustar00rootroot00000000000000"""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', 'priceId'] COLUMNS_ITEM_PRICES = ['keyName', 'priceId', 'Hourly', 'Monthly', 'Restriction'] COLUMNS_ITEM_PRICES_LOCATION = ['keyName', 'priceId', 'Hourly', 'Monthly', 'Restriction'] @click.command() @click.argument('package_keyname') @click.option('--keyword', '-k', help="A word (or string) used to filter item names.") @click.option('--category', '-c', help="Category code to filter items by") @click.option('--prices', '-p', is_flag=True, help='Use --prices to list the server item prices, and to list the ' 'Item Prices by location, add it to the --prices option using ' 'location KeyName, e.g. --prices AMSTERDAM02') @click.argument('location', required=False) @environment.pass_env def cli(env, package_keyname, keyword, category, prices, location=None): """List package items used for ordering. The item keyNames listed can be used with `slcli order place` to specify the items that are being ordered in the package. .. 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. :: # List all items in the VSI package slcli order item-list CLOUD_SERVER # List Ubuntu OSes from the os category of the Bare Metal package slcli order item-list BARE_METAL_SERVER --category os --keyword ubuntu """ manager = ordering.OrderingManager(env.client) tables = [] _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() if prices: _item_list_prices(categories, sorted_items, tables) if location: location_prices = manager.get_item_prices_by_location(location, package_keyname) _location_item_prices(location_prices, location, tables) else: table_items_detail = formatting.Table(COLUMNS) for category_name in sorted(categories): for item in sorted_items[category_name]: table_items_detail.add_row([category_name, item['keyName'], item['description'], get_price(item)]) tables.append(table_items_detail) env.fout(formatting.listing(tables, separator='\n')) 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 def get_price(item): """Given an SoftLayer_Product_Item, returns its default price id""" for price in item.get('prices', []): if not price.get('locationGroupId'): return price.get('id') return 0 def _item_list_prices(categories, sorted_items, tables): """Add the item prices cost and capacity restriction to the table""" table_prices = formatting.Table(COLUMNS_ITEM_PRICES) for category in sorted(categories): for item in sorted_items[category]: for price in item['prices']: if not price.get('locationGroupId'): cr_max = get_item_price_data(price, 'capacityRestrictionMaximum') cr_min = get_item_price_data(price, 'capacityRestrictionMinimum') cr_type = get_item_price_data(price, 'capacityRestrictionType') table_prices.add_row([item['keyName'], price['id'], get_item_price_data(price, 'hourlyRecurringFee'), get_item_price_data(price, 'recurringFee'), "%s - %s %s" % (cr_min, cr_max, cr_type)]) tables.append(table_prices) def get_item_price_data(price, item_attribute): """Given an SoftLayer_Product_Item_Price, returns its default price data""" result = '-' if item_attribute in price: result = price[item_attribute] return result def _location_item_prices(location_prices, location, tables): """Add a location prices table to tables. :param list location_prices : Location prices. :param string location : Location. :param list tables: Table list to add location prices table. """ location_prices_table = formatting.Table(COLUMNS_ITEM_PRICES_LOCATION, title="Item Prices for %s" % location) location_prices_table.sortby = 'keyName' location_prices_table.align = 'l' for price in location_prices: cr_max = get_item_price_data(price, 'capacityRestrictionMaximum') cr_min = get_item_price_data(price, 'capacityRestrictionMinimum') cr_type = get_item_price_data(price, 'capacityRestrictionType') location_prices_table.add_row( [price['item']['keyName'], price['id'], get_item_price_data(price, 'hourlyRecurringFee'), get_item_price_data(price, 'recurringFee'), "%s - %s %s" % (cr_min, cr_max, cr_type)]) tables.append(location_prices_table) softlayer-python-5.9.8/SoftLayer/CLI/order/lookup.py000066400000000000000000000047021415376457700224270ustar00rootroot00000000000000"""Provides some details related to the order.""" # :license: MIT, see LICENSE for more details. import click from SoftLayer.CLI.account.invoice_detail import get_invoice_table from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.managers import ordering from SoftLayer import utils @click.command() @click.argument('identifier') @click.option('--details', is_flag=True, default=False, show_default=True, help="Shows a very detailed list of charges") @environment.pass_env def cli(env, identifier, details): """Provides some details related to order owner, date order, cost information, initial invoice.""" manager = ordering.OrderingManager(env.client) order = manager.get_order_detail(identifier) order_table = get_order_table(order) invoice = order.get('initialInvoice', {}) top_items = invoice.get('invoiceTopLevelItems', []) invoice_id = invoice.get('id') invoice_table = get_invoice_table(invoice_id, top_items, details) order_table.add_row(['Initial Invoice', invoice_table]) env.fout(order_table) def get_order_table(order): """Formats a table for billing order""" title = "Order {id}".format(id=order.get('id')) date_format = '%Y-%m-%d' table = formatting.Table(["Key", "Value"], title=title) table.align = 'l' ordered_by = "IBM" user = order.get('userRecord', None) if user: ordered_by = "{} ({})".format(user.get('displayName'), utils.lookup(user, 'userStatus', 'name')) table.add_row(['Ordered By', ordered_by]) table.add_row(['Create Date', utils.clean_time(order.get('createDate'), date_format, date_format)]) table.add_row(['Modify Date', utils.clean_time(order.get('modifyDate'), date_format, date_format)]) table.add_row(['Order Approval Date', utils.clean_time(order.get('orderApprovalDate'), date_format, date_format)]) table.add_row(['Status', order.get('status')]) table.add_row(['Order Total Amount', "{price:.2f}".format(price=float(order.get('orderTotalAmount', '0')))]) table.add_row(['Invoice Total Amount', "{price:.2f}". format(price=float(order.get('initialInvoice', {}).get('invoiceTotalAmount', '0')))]) items = order.get('items', []) item_table = formatting.Table(["Item Description"]) item_table.align['description'] = 'l' for item in items: item_table.add_row([item.get('description')]) table.add_row(['Items', item_table]) return table softlayer-python-5.9.8/SoftLayer/CLI/order/package_list.py000066400000000000000000000027521415376457700235470ustar00rootroot00000000000000"""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', '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. :: # List out all packages for ordering slcli order package-list # List out all packages with "server" in the name slcli order package-list --keyword server # Select only specifict package types 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['id'], package['name'], package['keyName'], package['type']['keyName'] ]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/order/package_locations.py000066400000000000000000000016251415376457700245650ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/order/place.py000066400000000000000000000100071415376457700221750ustar00rootroot00000000000000"""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('--quantity', type=int, default=1, help="The quantity of the item being ordered") @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. Starts 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, quantity, extras, order_items): """Place or verify an order. \b 1. Find the package keyName from `slcli order package-list` 2. Find the location from `slcli order package-locations PUBLIC_CLOUD_SERVER` If the package does not require a location, use 'NONE' instead. 3. Find the needed items `slcli order item-list PUBLIC_CLOUD_SERVER` Some packages, like PUBLIC_CLOUD_SERVER need presets, `slcli order preset-list PUBLIC_CLOUD_SERVER` 4. Find the complex type from https://sldn.softlayer.com/reference 5. Use that complex type to fill out any --extras Example:: slcli order place --verify --preset B1_2X8X100 --billing hourly --complex-type SoftLayer_Container_Product_Order_Virtual_Guest --extras '{"virtualGuests": [{"hostname": "test", "domain": "ibm.com"}]}' PUBLIC_CLOUD_SERVER DALLAS13 BANDWIDTH_0_GB_2 MONITORING_HOST_PING NOTIFICATION_EMAIL_AND_TICKET OS_DEBIAN_9_X_STRETCH_LAMP_64_BIT 1_IP_ADDRESS 1_IPV6_ADDRESS 1_GBPS_PUBLIC_PRIVATE_NETWORK_UPLINKS REBOOT_REMOTE_CONSOLE AUTOMATED_NOTIFICATION UNLIMITED_SSL_VPN_USERS_1_PPTP_VPN_USER_PER_ACCOUNT """ manager = ordering.OrderingManager(env.client) if extras: try: extras = json.loads(extras) except ValueError as err: raise exceptions.CLIAbort("There was an error when parsing the --extras value: {}".format(err)) args = (package_keyname, location, order_items) kwargs = {'preset_keyname': preset, 'extras': extras, 'quantity': quantity, 'complex_type': complex_type, 'hourly': bool(billing == 'hourly')} if verify: table = formatting.Table(COLUMNS) order_to_place = manager.verify_order(*args, **kwargs) for price in order_to_place['orderContainers'][0]['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.9.8/SoftLayer/CLI/order/place_quote.py000066400000000000000000000072431415376457700234220ustar00rootroot00000000000000"""Place quote""" # :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 @click.command() @click.argument('package_keyname') @click.argument('location') @click.option('--preset', help="The order preset (if required by the package)") @click.option('--name', help="A custom name to be assigned to the quote (optional)") @click.option('--send-email', is_flag=True, help="The quote will be sent to the email address associated with your user.") @click.option('--complex-type', help="The complex type of the order. Starts 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, name, send_email, complex_type, extras, order_items): """Place a quote. This CLI command is used for creating a quote 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_quote() with the same keynames. Packages for ordering can be retrieved 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`. Example:: # Place quote a 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-quote --name "foobar" --send-email 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 \\ --extras '{"virtualGuests": [{"hostname": "test", "domain": "softlayer.com"}]}' \\ --complex-type SoftLayer_Container_Product_Order_Virtual_Guest """ manager = ordering.OrderingManager(env.client) if extras: try: extras = json.loads(extras) except ValueError as err: raise exceptions.CLIAbort("There was an error when parsing the --extras value: {}".format(err)) args = (package_keyname, location, order_items) kwargs = {'preset_keyname': preset, 'extras': extras, 'quantity': 1, 'quote_name': name, 'send_email': send_email, 'complex_type': complex_type} order = manager.place_quote(*args, **kwargs) table = formatting.KeyValueTable(['name', 'value']) table.align['name'] = 'r' table.align['value'] = 'l' table.add_row(['id', order['quote']['id']]) table.add_row(['name', order['quote']['name']]) table.add_row(['created', order['orderDate']]) table.add_row(['expires', order['quote']['expirationDate']]) table.add_row(['status', order['quote']['status']]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/order/preset_list.py000066400000000000000000000025731415376457700234570ustar00rootroot00000000000000"""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. .. Note:: Presets are set CPU / RAM / Disk allotments. You still need to specify required items. Some packages do not have presets. :: # List the presets for Bare Metal servers slcli order preset-list BARE_METAL_SERVER # 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([ str(preset['name']).strip(), str(preset['keyName']).strip(), str(preset['description']).strip() ]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/order/quote.py000066400000000000000000000121341415376457700222510ustar00rootroot00000000000000"""View and Order a quote""" # :license: MIT, see LICENSE for more details. import click from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.CLI import helpers from SoftLayer.managers import ImageManager as ImageManager from SoftLayer.managers import ordering from SoftLayer.managers import SshKeyManager as SshKeyManager def _parse_create_args(client, args): """Converts CLI arguments to args for VSManager.create_instance. :param dict args: CLI arguments """ data = {} if args.get('quantity'): data['quantity'] = int(args.get('quantity')) if args.get('postinstall'): data['provisionScripts'] = [args.get('postinstall')] if args.get('complex_type'): data['complexType'] = args.get('complex_type') if args.get('fqdn'): servers = [] for name in args.get('fqdn'): fqdn = name.split(".", 1) servers.append({'hostname': fqdn[0], 'domain': fqdn[1]}) data['hardware'] = servers if args.get('image'): if args.get('image').isdigit(): image_mgr = ImageManager(client) image_details = image_mgr.get_image(args.get('image'), mask="id,globalIdentifier") data['imageTemplateGlobalIdentifier'] = image_details['globalIdentifier'] else: data['imageTemplateGlobalIdentifier'] = args['image'] userdata = None if args.get('userdata'): userdata = args['userdata'] elif args.get('userfile'): with open(args['userfile'], 'r', encoding="utf-8") as userfile: userdata = userfile.read() if userdata: for hardware in data['hardware']: hardware['userData'] = [{'value': userdata}] # Get the SSH keys if args.get('key'): keys = [] for key in args.get('key'): resolver = SshKeyManager(client).resolve_ids key_id = helpers.resolve_id(resolver, key, 'SshKey') keys.append(key_id) data['sshKeys'] = keys return data @click.command() @click.argument('quote') @click.option('--verify', is_flag=True, default=False, show_default=True, help="If specified, will only show what the quote will order, will NOT place an order") @click.option('--quantity', type=int, default=None, help="The quantity of the item being ordered if different from quoted value") @click.option('--complex-type', default='SoftLayer_Container_Product_Order_Hardware_Server', show_default=True, help=("The complex type of the order. Starts with 'SoftLayer_Container_Product_Order'.")) @click.option('--userdata', '-u', help="User defined metadata string") @click.option('--userfile', '-F', type=click.Path(exists=True, readable=True, resolve_path=True), help="Read userdata from 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('--fqdn', required=True, help=". formatted name to use. Specify one fqdn per server") @click.option('--image', help="Image ID. See: 'slcli image list' for reference") @environment.pass_env def cli(env, quote, **args): """View and Order a quote \f :note: The hostname and domain are split out from the fully qualified domain name. If you want to order multiple servers, you need to specify each FQDN. Postinstall, userdata, and sshkeys are applied to all servers in an order. :: slcli order quote 12345 --fqdn testing.tester.com \\ --complex-type SoftLayer_Container_Product_Order_Virtual_Guest -k sshKeyNameLabel\\ -i https://domain.com/runthis.sh --userdata DataGoesHere """ table = formatting.Table([ 'Id', 'Name', 'Created', 'Expiration', 'Status' ]) create_args = _parse_create_args(env.client, args) manager = ordering.OrderingManager(env.client) quote_details = manager.get_quote_details(quote) package = quote_details['order']['items'][0]['package'] create_args['packageId'] = package['id'] if args.get('verify'): result = manager.verify_quote(quote, create_args) verify_table = formatting.Table(['keyName', 'description', 'cost']) verify_table.align['keyName'] = 'l' verify_table.align['description'] = 'l' for price in result['prices']: cost_key = 'hourlyRecurringFee' if result['useHourlyPricing'] is True else 'recurringFee' verify_table.add_row([ price['item']['keyName'], price['item']['description'], price[cost_key] if cost_key in price else formatting.blank() ]) env.fout(verify_table) else: result = manager.order_quote(quote, create_args) 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']]) table.add_row(['status', result['placedOrder']['status']]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/order/quote_detail.py000066400000000000000000000021041415376457700235670ustar00rootroot00000000000000"""View a quote""" # :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 @click.command() @click.argument('quote') @environment.pass_env def cli(env, quote): """View a quote""" manager = ordering.OrderingManager(env.client) result = manager.get_quote_details(quote) package = result['order']['items'][0]['package'] title = "{} - Package: {}, Id {}".format(result.get('name'), package['keyName'], package['id']) table = formatting.Table([ 'Category', 'Description', 'Quantity', 'Recurring', 'One Time' ], title=title) table.align['Category'] = 'l' table.align['Description'] = 'l' items = lookup(result, 'order', 'items') for item in items: table.add_row([ item.get('categoryCode'), item.get('description'), item.get('quantity'), item.get('recurringFee'), item.get('oneTimeFee') ]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/order/quote_list.py000066400000000000000000000020401415376457700232770ustar00rootroot00000000000000"""List active quotes on an account.""" # :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 clean_time @click.command() @environment.pass_env def cli(env): """List all active quotes on an account""" table = formatting.Table([ 'Id', 'Name', 'Created', 'Expiration', 'Status', 'Package Name', 'Package Id' ]) table.align['Name'] = 'l' table.align['Package Name'] = 'r' table.align['Package Id'] = 'l' manager = ordering.OrderingManager(env.client) items = manager.get_quotes() for item in items: package = item['order']['items'][0]['package'] table.add_row([ item.get('id'), item.get('name'), clean_time(item.get('createDate')), clean_time(item.get('modifyDate')), item.get('status'), package.get('keyName'), package.get('id') ]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/order/quote_save.py000066400000000000000000000014171415376457700232710ustar00rootroot00000000000000"""Save a quote""" # :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 clean_time @click.command() @click.argument('quote') @environment.pass_env def cli(env, quote): """Save a quote""" manager = ordering.OrderingManager(env.client) result = manager.save_quote(quote) table = formatting.Table([ 'Id', 'Name', 'Created', 'Modified', 'Status' ]) table.align['Name'] = 'l' table.add_row([ result.get('id'), result.get('name'), clean_time(result.get('createDate')), clean_time(result.get('modifyDate')), result.get('status'), ]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/report/000077500000000000000000000000001415376457700207415ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/report/__init__.py000066400000000000000000000000171415376457700230500ustar00rootroot00000000000000"""Reports.""" softlayer-python-5.9.8/SoftLayer/CLI/report/bandwidth.py000066400000000000000000000211331415376457700232570ustar00rootroot00000000000000"""Metric Utilities""" 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) as ex: message = "not in the format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'" raise click.BadParameter(message) from ex 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) @click.option('--virtual', is_flag=True, help='Show the all bandwidth summary for each virtual server', default=False) @click.option('--server', is_flag=True, help='show the all bandwidth summary for each hardware server', default=False) @environment.pass_env def cli(env, start, end, sortby, virtual, server): """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', 'hostname', '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) def _input_to_table(item): "Input metric data to table" 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(), ]) try: if virtual: for item in itertools.chain(_get_pooled_bandwidth(env, start, end), _get_virtual_bandwidth(env, start, end)): _input_to_table(item) elif server: for item in itertools.chain(_get_pooled_bandwidth(env, start, end), _get_hardware_bandwidth(env, start, end)): _input_to_table(item) else: for item in itertools.chain(_get_pooled_bandwidth(env, start, end), _get_hardware_bandwidth(env, start, end), _get_virtual_bandwidth(env, start, end)): _input_to_table(item) except KeyboardInterrupt: env.err("Printing collected results and then aborting.") env.out(env.fmt(table)) softlayer-python-5.9.8/SoftLayer/CLI/routes.py000066400000000000000000000506341415376457700213310ustar00rootroot00000000000000""" 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'), ('account', 'SoftLayer.CLI.account'), ('account:invoice-detail', 'SoftLayer.CLI.account.invoice_detail:cli'), ('account:invoices', 'SoftLayer.CLI.account.invoices:cli'), ('account:events', 'SoftLayer.CLI.account.events:cli'), ('account:event-detail', 'SoftLayer.CLI.account.event_detail:cli'), ('account:licenses', 'SoftLayer.CLI.account.licenses:cli'), ('account:summary', 'SoftLayer.CLI.account.summary:cli'), ('account:billing-items', 'SoftLayer.CLI.account.billing_items:cli'), ('account:item-detail', 'SoftLayer.CLI.account.item_detail:cli'), ('account:cancel-item', 'SoftLayer.CLI.account.cancel_item:cli'), ('account:orders', 'SoftLayer.CLI.account.orders:cli'), ('virtual', 'SoftLayer.CLI.virt'), ('virtual:bandwidth', 'SoftLayer.CLI.virt.bandwidth:cli'), ('virtual:billing', 'SoftLayer.CLI.virt.billing:cli'), ('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:storage', 'SoftLayer.CLI.virt.storage:cli'), ('virtual:upgrade', 'SoftLayer.CLI.virt.upgrade:cli'), ('virtual:usage', 'SoftLayer.CLI.virt.usage:cli'), ('virtual:credentials', 'SoftLayer.CLI.virt.credentials:cli'), ('virtual:authorize-storage', 'SoftLayer.CLI.virt.authorize_storage:cli'), ('virtual:capacity', 'SoftLayer.CLI.virt.capacity:cli'), ('virtual:placementgroup', 'SoftLayer.CLI.virt.placementgroup:cli'), ('virtual:migrate', 'SoftLayer.CLI.virt.migrate: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'), ('dedicatedhost:cancel', 'SoftLayer.CLI.dedicatedhost.cancel:cli'), ('dedicatedhost:cancel-guests', 'SoftLayer.CLI.dedicatedhost.cancel_guests:cli'), ('dedicatedhost:list-guests', 'SoftLayer.CLI.dedicatedhost.list_guests:cli'), ('cdn', 'SoftLayer.CLI.cdn'), ('cdn:detail', 'SoftLayer.CLI.cdn.detail:cli'), ('cdn:edit', 'SoftLayer.CLI.cdn.edit:cli'), ('cdn:list', 'SoftLayer.CLI.cdn.list: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:subnets-list', 'SoftLayer.CLI.block.subnets.list:cli'), ('block:subnets-assign', 'SoftLayer.CLI.block.subnets.assign:cli'), ('block:subnets-remove', 'SoftLayer.CLI.block.subnets.remove:cli'), ('block:replica-failback', 'SoftLayer.CLI.block.replication.failback:cli'), ('block:replica-failover', 'SoftLayer.CLI.block.replication.failover:cli'), ('block:disaster-recovery-failover', 'SoftLayer.CLI.block.replication.disaster_recovery_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-set-notification', 'SoftLayer.CLI.block.snapshot.set_notify_status:cli'), ('block:snapshot-get-notification-status', 'SoftLayer.CLI.block.snapshot.get_notify_status: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'), ('block:volume-limits', 'SoftLayer.CLI.block.limit:cli'), ('block:volume-refresh', 'SoftLayer.CLI.block.refresh:cli'), ('block:volume-convert', 'SoftLayer.CLI.block.convert:cli'), ('block:volume-set-note', 'SoftLayer.CLI.block.set_note:cli'), ('email', 'SoftLayer.CLI.email'), ('email:list', 'SoftLayer.CLI.email.list:cli'), ('email:detail', 'SoftLayer.CLI.email.detail:cli'), ('email:edit', 'SoftLayer.CLI.email.edit:cli'), ('licenses', 'SoftLayer.CLI.licenses'), ('licenses:create-options', 'SoftLayer.CLI.licenses.create_options:cli'), ('event-log', 'SoftLayer.CLI.event_log'), ('event-log:get', 'SoftLayer.CLI.event_log.get:cli'), ('event-log:types', 'SoftLayer.CLI.event_log.types: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:disaster-recovery-failover', 'SoftLayer.CLI.file.replication.disaster_recovery_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-set-notification', 'SoftLayer.CLI.file.snapshot.set_notify_status:cli'), ('file:snapshot-get-notification-status', 'SoftLayer.CLI.file.snapshot.get_notify_status: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'), ('file:volume-limits', 'SoftLayer.CLI.file.limit:cli'), ('file:volume-refresh', 'SoftLayer.CLI.file.refresh:cli'), ('file:volume-convert', 'SoftLayer.CLI.file.convert:cli'), ('file:volume-set-note', 'SoftLayer.CLI.file.set_note: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'), ('image:datacenter', 'SoftLayer.CLI.image.datacenter: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:detail', 'SoftLayer.CLI.loadbal.detail:cli'), ('loadbal:list', 'SoftLayer.CLI.loadbal.list:cli'), ('loadbal:health', 'SoftLayer.CLI.loadbal.health:cli'), ('loadbal:member-add', 'SoftLayer.CLI.loadbal.members:add'), ('loadbal:member-del', 'SoftLayer.CLI.loadbal.members:remove'), ('loadbal:l7policies', 'SoftLayer.CLI.loadbal.layer7_policy_list:policies'), ('loadbal:pool-add', 'SoftLayer.CLI.loadbal.pools:add'), ('loadbal:pool-edit', 'SoftLayer.CLI.loadbal.pools:edit'), ('loadbal:pool-del', 'SoftLayer.CLI.loadbal.pools:delete'), ('loadbal:l7pool-add', 'SoftLayer.CLI.loadbal.pools:l7pool_add'), ('loadbal:l7pool-del', 'SoftLayer.CLI.loadbal.pools:l7pool_del'), ('loadbal:order', 'SoftLayer.CLI.loadbal.order:order'), ('loadbal:order-options', 'SoftLayer.CLI.loadbal.order:order_options'), ('loadbal:cancel', 'SoftLayer.CLI.loadbal.order:cancel'), ('loadbal:ns-detail', 'SoftLayer.CLI.loadbal.ns_detail:cli'), ('loadbal:ns-list', 'SoftLayer.CLI.loadbal.ns_list:cli'), ('metadata', 'SoftLayer.CLI.metadata:cli'), ('nas', 'SoftLayer.CLI.nas'), ('nas:list', 'SoftLayer.CLI.nas.list:cli'), ('nas:credentials', 'SoftLayer.CLI.nas.credentials:cli'), ('licenses', 'SoftLayer.CLI.licenses'), ('licenses:create', 'SoftLayer.CLI.licenses.create:cli'), ('licenses:cancel', 'SoftLayer.CLI.licenses.cancel: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'), ('object-storage:credential', 'SoftLayer.CLI.object_storage.credential: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'), ('order:place-quote', 'SoftLayer.CLI.order.place_quote:cli'), ('order:quote-list', 'SoftLayer.CLI.order.quote_list:cli'), ('order:quote-detail', 'SoftLayer.CLI.order.quote_detail:cli'), ('order:quote-save', 'SoftLayer.CLI.order.quote_save:cli'), ('order:quote', 'SoftLayer.CLI.order.quote:cli'), ('order:lookup', 'SoftLayer.CLI.order.lookup:cli'), ('hardware', 'SoftLayer.CLI.hardware'), ('hardware:bandwidth', 'SoftLayer.CLI.hardware.bandwidth:cli'), ('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:billing', 'SoftLayer.CLI.hardware.billing:cli'), ('hardware:edit', 'SoftLayer.CLI.hardware.edit:cli'), ('hardware:guests', 'SoftLayer.CLI.hardware.guests: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:reflash-firmware', 'SoftLayer.CLI.hardware.reflash_firmware:cli'), ('hardware:rescue', 'SoftLayer.CLI.hardware.power:rescue'), ('hardware:ready', 'SoftLayer.CLI.hardware.ready:cli'), ('hardware:toggle-ipmi', 'SoftLayer.CLI.hardware.toggle_ipmi:cli'), ('hardware:authorize-storage', 'SoftLayer.CLI.hardware.authorize_storage:cli'), ('hardware:dns-sync', 'SoftLayer.CLI.hardware.dns:cli'), ('hardware:storage', 'SoftLayer.CLI.hardware.storage:cli'), ('hardware:upgrade', 'SoftLayer.CLI.hardware.upgrade:cli'), ('hardware:sensor', 'SoftLayer.CLI.hardware.sensor: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'), ('securitygroup:event-log', 'SoftLayer.CLI.securitygroup.event_log:get_by_request_id'), ('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:edit', 'SoftLayer.CLI.subnet.edit:cli'), ('subnet:detail', 'SoftLayer.CLI.subnet.detail:cli'), ('subnet:list', 'SoftLayer.CLI.subnet.list:cli'), ('subnet:lookup', 'SoftLayer.CLI.subnet.lookup:cli'), ('subnet:edit-ip', 'SoftLayer.CLI.subnet.edit_ip:cli'), ('tags', 'SoftLayer.CLI.tags'), ('tags:cleanup', 'SoftLayer.CLI.tags.cleanup:cli'), ('tags:list', 'SoftLayer.CLI.tags.list:cli'), ('tags:set', 'SoftLayer.CLI.tags.set:cli'), ('tags:details', 'SoftLayer.CLI.tags.details:cli'), ('tags:delete', 'SoftLayer.CLI.tags.delete:cli'), ('tags:taggable', 'SoftLayer.CLI.tags.taggable: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'), ('user', 'SoftLayer.CLI.user'), ('user:list', 'SoftLayer.CLI.user.list:cli'), ('user:detail', 'SoftLayer.CLI.user.detail:cli'), ('user:permissions', 'SoftLayer.CLI.user.permissions:cli'), ('user:edit-permissions', 'SoftLayer.CLI.user.edit_permissions:cli'), ('user:notifications', 'SoftLayer.CLI.user.notifications:cli'), ('user:edit-notifications', 'SoftLayer.CLI.user.edit_notifications:cli'), ('user:edit-details', 'SoftLayer.CLI.user.edit_details:cli'), ('user:create', 'SoftLayer.CLI.user.create:cli'), ('user:delete', 'SoftLayer.CLI.user.delete:cli'), ('user:vpn-manual', 'SoftLayer.CLI.user.vpn_manual:cli'), ('user:vpn-subnet', 'SoftLayer.CLI.user.vpn_subnet:cli'), ('vlan', 'SoftLayer.CLI.vlan'), ('vlan:create', 'SoftLayer.CLI.vlan.create:cli'), ('vlan:detail', 'SoftLayer.CLI.vlan.detail:cli'), ('vlan:edit', 'SoftLayer.CLI.vlan.edit:cli'), ('vlan:list', 'SoftLayer.CLI.vlan.list:cli'), ('vlan:cancel', 'SoftLayer.CLI.vlan.cancel:cli'), ('summary', 'SoftLayer.CLI.summary:cli'), ('report', 'SoftLayer.CLI.report'), ('report:bandwidth', 'SoftLayer.CLI.report.bandwidth:cli'), ('autoscale', 'SoftLayer.CLI.autoscale'), ('autoscale:list', 'SoftLayer.CLI.autoscale.list:cli'), ('autoscale:detail', 'SoftLayer.CLI.autoscale.detail:cli'), ('autoscale:scale', 'SoftLayer.CLI.autoscale.scale:cli'), ('autoscale:logs', 'SoftLayer.CLI.autoscale.logs:cli'), ('autoscale:tag', 'SoftLayer.CLI.autoscale.tag:cli'), ('autoscale:edit', 'SoftLayer.CLI.autoscale.edit:cli') ] ALL_ALIASES = { 'hw': 'hardware', 'lb': 'loadbal', 'meta': 'metadata', 'my': 'metadata', 'sg': 'securitygroup', 'server': 'hardware', 'vm': 'virtual', 'vs': 'virtual', 'dh': 'dedicatedhost', 'pg': 'placementgroup', } softlayer-python-5.9.8/SoftLayer/CLI/securitygroup/000077500000000000000000000000001415376457700223525ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/securitygroup/__init__.py000066400000000000000000000000371415376457700244630ustar00rootroot00000000000000"""Network security groups.""" softlayer-python-5.9.8/SoftLayer/CLI/securitygroup/create.py000066400000000000000000000017511415376457700241730ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/securitygroup/delete.py000066400000000000000000000007731415376457700241750ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/securitygroup/detail.py000066400000000000000000000052241415376457700241710ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/securitygroup/edit.py000066400000000000000000000014571415376457700236600ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/securitygroup/event_log.py000066400000000000000000000014631415376457700247120ustar00rootroot00000000000000"""Get event logs relating to security groups""" # :license: MIT, see LICENSE for more details. import json import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting COLUMNS = ['event', 'label', 'date', 'metadata'] @click.command() @click.argument('request_id') @environment.pass_env def get_by_request_id(env, request_id): """Search for event logs by request id""" mgr = SoftLayer.NetworkManager(env.client) logs = mgr.get_event_logs_by_request_id(request_id) table = formatting.Table(COLUMNS) table.align['metadata'] = "l" for log in logs: metadata = json.dumps(json.loads(log['metaData']), indent=4, sort_keys=True) table.add_row([log['eventName'], log['label'], log['eventCreateDate'], metadata]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/securitygroup/interface.py000066400000000000000000000126421415376457700246710ustar00rootroot00000000000000"""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', ] REQUEST_COLUMNS = ['requestId'] @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) ret = mgr.attach_securitygroup_component(securitygroup_id, component_id) if not ret: raise exceptions.CLIAbort("Could not attach network component") table = formatting.Table(REQUEST_COLUMNS) table.add_row([ret['requestId']]) env.fout(table) @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) ret = mgr.detach_securitygroup_component(securitygroup_id, component_id) if not ret: raise exceptions.CLIAbort("Could not detach network component") table = formatting.Table(REQUEST_COLUMNS) table.add_row([ret['requestId']]) env.fout(table) 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.9.8/SoftLayer/CLI/securitygroup/list.py000066400000000000000000000017661415376457700237110ustar00rootroot00000000000000"""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)) @click.option('--limit', '-l', help='How many results to get in one api call, default is 100', default=100, show_default=True) @environment.pass_env def cli(env, sortby, limit): """List security groups.""" mgr = SoftLayer.NetworkManager(env.client) table = formatting.Table(COLUMNS) table.sortby = sortby sgs = mgr.list_securitygroups(limit=limit) 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.9.8/SoftLayer/CLI/securitygroup/rule.py000066400000000000000000000137261415376457700237040ustar00rootroot00000000000000"""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', 'createDate', 'modifyDate'] REQUEST_BOOL_COLUMNS = ['requestId', 'response'] REQUEST_RULES_COLUMNS = ['requestId', 'rules'] @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(), rule.get('createDate') or formatting.blank(), rule.get('modifyDate') 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") table = formatting.Table(REQUEST_RULES_COLUMNS) table.add_row([ret['requestId'], str(ret['rules'])]) env.fout(table) @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 ret = mgr.edit_securitygroup_rule(securitygroup_id, rule_id, **data) if not ret: raise exceptions.CLIAbort("Failed to edit security group rule") table = formatting.Table(REQUEST_BOOL_COLUMNS) table.add_row([ret['requestId']]) env.fout(table) @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) ret = mgr.remove_securitygroup_rule(securitygroup_id, rule_id) if not ret: raise exceptions.CLIAbort("Failed to remove security group rule") table = formatting.Table(REQUEST_BOOL_COLUMNS) table.add_row([ret['requestId']]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/sshkey/000077500000000000000000000000001415376457700207345ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/sshkey/__init__.py000066400000000000000000000000201415376457700230350ustar00rootroot00000000000000"""SSH Keys.""" softlayer-python-5.9.8/SoftLayer/CLI/sshkey/add.py000066400000000000000000000024121415376457700220350ustar00rootroot00000000000000"""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: with open(path.expanduser(in_file), 'rU', encoding="utf-8") as key_file: 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.9.8/SoftLayer/CLI/sshkey/edit.py000066400000000000000000000013031415376457700222300ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/sshkey/list.py000066400000000000000000000015651415376457700222700ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/sshkey/print.py000066400000000000000000000020551415376457700224440ustar00rootroot00000000000000"""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', encoding="utf-8") 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.9.8/SoftLayer/CLI/sshkey/remove.py000066400000000000000000000012321415376457700226010ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/ssl/000077500000000000000000000000001415376457700202275ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/ssl/__init__.py000066400000000000000000000000301415376457700223310ustar00rootroot00000000000000"""SSL Certificates.""" softlayer-python-5.9.8/SoftLayer/CLI/ssl/add.py000066400000000000000000000027321415376457700213350ustar00rootroot00000000000000"""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, } with open(crt, encoding="utf-8") as file_crt: template['certificate'] = file_crt.read() with open(key, encoding="utf-8") as file_key: template['privateKey'] = file_key.read() with open(csr, encoding="utf-8") as file_csr: if csr: body = file_csr.read() template['certificateSigningRequest'] = body with open(icc, encoding="utf-8") as file_icc: if icc: body = file_icc.read() template['intermediateCertificate'] = body manager = SoftLayer.SSLManager(env.client) manager.add_certificate(template) softlayer-python-5.9.8/SoftLayer/CLI/ssl/download.py000066400000000000000000000021011415376457700224020ustar00rootroot00000000000000"""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', encoding="utf-8") as cert_file: cert_file.write(content) softlayer-python-5.9.8/SoftLayer/CLI/ssl/edit.py000066400000000000000000000026521415376457700215330ustar00rootroot00000000000000"""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} with open(crt, encoding="utf-8") as file_crt: if crt: template['certificate'] = file_crt.read() with open(key, encoding="utf-8") as file_key: if key: template['privateKey'] = file_key.read() with open(csr, encoding="utf-8") as file_csr: if csr: template['certificateSigningRequest'] = file_csr.read() with open(icc, encoding="utf-8") as file_icc: if icc: template['intermediateCertificate'] = file_icc.read() if notes: template['notes'] = notes manager = SoftLayer.SSLManager(env.client) manager.edit_certificate(template) softlayer-python-5.9.8/SoftLayer/CLI/ssl/list.py000066400000000000000000000024221415376457700215540ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/ssl/remove.py000066400000000000000000000010621415376457700220750ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/storage_utils.py000066400000000000000000000114131415376457700226640ustar00rootroot00000000000000"""Utility functions for use with File and Block commands.""" # :license: MIT, see LICENSE for more details. from SoftLayer.CLI import columns as column_helper from SoftLayer.CLI import formatting DEFAULT_NOTES_SIZE = 20 def reduce_notes(volumes, env): """Reduces all long notes found in the volumes list just if the format output is different from a JSON format. :param list volumes: An list of storage volumes :param env :A environment console. """ if env.format_output_is_json(): return for volume in volumes: if len(volume.get('notes', '')) > DEFAULT_NOTES_SIZE: shortened_notes = volume['notes'][:DEFAULT_NOTES_SIZE] volume['notes'] = shortened_notes def build_output_table(env, volumes, columns, sortby): """Builds a formatting table for a list of volumes. :param env :A Environment console. :param list volumes: An list of storage volumes :param columns :A ColumnFormatter for column names :param str sortby :A string to sort by. """ table = formatting.Table(columns.columns) if sortby in table.columns: table.sortby = sortby reduce_notes(volumes, env) for volume in volumes: table.add_row([value or formatting.blank() for value in columns.row(volume)]) return table 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', ] REPLICATION_PARTNER_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"), ] REPLICATION_PARTNER_DEFAULT = [ 'ID', 'Username', 'Account ID', 'Capacity (GB)', 'Hardware ID', 'Guest ID', 'Host ID' ] softlayer-python-5.9.8/SoftLayer/CLI/subnet/000077500000000000000000000000001415376457700207265ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/subnet/__init__.py000066400000000000000000000000271415376457700230360ustar00rootroot00000000000000"""Network subnets.""" softlayer-python-5.9.8/SoftLayer/CLI/subnet/cancel.py000066400000000000000000000012711415376457700225260ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/subnet/create.py000066400000000000000000000047121415376457700225470ustar00rootroot00000000000000"""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(['static', 'public', 'private'])) @click.argument('quantity', type=click.INT) @click.argument('endpoint-id', type=click.INT) @click.option('--ipv6', '--v6', 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, endpoint_id, ipv6, test): """Add a new subnet to your account. Valid quantities vary by type. \b IPv4 static - 1, 2, 4, 8, 16, 32, 64, 128, 256 public - 4, 8, 16, 32, 64, 128, 256 private - 4, 8, 16, 32, 64, 128, 256 \b IPv6 static - 64 public - 64 \b endpoint-id static - Network_Subnet_IpAddress identifier. public - Network_Vlan identifier private - Network_Vlan identifier """ 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 try: result = mgr.add_subnet(network, quantity=quantity, endpoint_id=endpoint_id, version=version, test_order=test) except SoftLayer.SoftLayerAPIError as error: raise exceptions.CLIAbort('Unable to order {} {} ipv{} , error: {}'.format(quantity, network, version, error.faultString)) 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.9.8/SoftLayer/CLI/subnet/detail.py000066400000000000000000000064171415376457700225520ustar00rootroot00000000000000"""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') mask = 'mask[ipAddresses[id, ipAddress,note], datacenter, virtualGuests, hardware, networkVlan[networkSpace]]' subnet = mgr.get_subnet(subnet_id, mask=mask) 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())]) table.add_row(['note', subnet.get('note', formatting.blank())]) table.add_row(['tags', formatting.tags(subnet.get('tagReferences'))]) ip_address = subnet.get('ipAddresses') ip_table = formatting.KeyValueTable(['id', 'ip', 'note']) for address in ip_address: ip_table.add_row([address.get('id'), address.get('ipAddress'), address.get('note')]) table.add_row(['ipAddresses', ip_table]) 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', formatting.blank()]) 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', formatting.blank()]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/subnet/edit.py000066400000000000000000000023001415376457700222200ustar00rootroot00000000000000"""Edit a subnet.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import helpers @click.command(short_help="Edit note and tags of a subnet") @click.argument('identifier') @click.option('--tags', '-t', type=click.STRING, help='Comma separated list of tags, enclosed in quotes. "tag1, tag2"') @click.option('--note', '-n', type=click.STRING, help="The note") @environment.pass_env def cli(env, identifier, tags, note): """Edit note and tags of a subnet.""" mgr = SoftLayer.NetworkManager(env.client) subnet_id = helpers.resolve_id(mgr.resolve_subnet_ids, identifier, name='subnet') if tags: result = mgr.set_tags_subnet(subnet_id, tags) print_result(result, "Set tags") if note: result = mgr.edit_note_subnet(subnet_id, note) print_result(result, "Edit note") def print_result(result, detail): """Prints a successfully or Failed message.""" if result: click.secho("{} successfully".format(detail), fg='green') else: click.secho("Failed to {}".format(detail.lower()), fg='red') softlayer-python-5.9.8/SoftLayer/CLI/subnet/edit_ip.py000066400000000000000000000012131415376457700227120ustar00rootroot00000000000000"""Edit ip note""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment @click.command() @click.argument('identifier') @click.option('--note', help="set ip address note of subnet") @environment.pass_env def cli(env, identifier, note): """Set the note of the ipAddress""" data = { 'note': note } mgr = SoftLayer.NetworkManager(env.client) ip_id = None if str.isdigit(identifier): ip_id = identifier else: ip_object = mgr.get_ip_by_address(identifier) ip_id = ip_object.get('id') mgr.set_subnet_ipddress_note(ip_id, data) softlayer-python-5.9.8/SoftLayer/CLI/subnet/list.py000066400000000000000000000045361415376457700222630ustar00rootroot00000000000000"""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('--ipv4', '--v4', is_flag=True, help="Display only IPv4 subnets") @click.option('--ipv6', '--v6', 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.9.8/SoftLayer/CLI/subnet/lookup.py000066400000000000000000000041701415376457700226130ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/summary.py000066400000000000000000000022511415376457700214750ustar00rootroot00000000000000"""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 or formatting.blank(), datacenter.get('hardware_count', formatting.blank()), datacenter.get('virtual_guest_count', formatting.blank()), datacenter.get('vlan_count', formatting.blank()), datacenter.get('subnet_count', formatting.blank()), datacenter.get('public_ip_count', formatting.blank()), ]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/tags/000077500000000000000000000000001415376457700203645ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/tags/__init__.py000066400000000000000000000000231415376457700224700ustar00rootroot00000000000000"""Manage Tags""" softlayer-python-5.9.8/SoftLayer/CLI/tags/cleanup.py000066400000000000000000000015231415376457700223660ustar00rootroot00000000000000"""Removes unused Tags""" # :license: MIT, see LICENSE for more details. import click from SoftLayer.CLI import environment from SoftLayer.managers.tags import TagManager @click.command() @click.option('--dry-run', '-d', is_flag=True, default=False, help="Don't delete, just show what will be deleted.") @environment.pass_env def cli(env, dry_run): """Removes all empty tags.""" tag_manager = TagManager(env.client) empty_tags = tag_manager.get_unattached_tags() for tag in empty_tags: if dry_run: click.secho("(Dry Run) Removing {}".format(tag.get('name')), fg='yellow') else: result = tag_manager.delete_tag(tag.get('name')) color = 'green' if result else 'red' click.secho("Removing {}".format(tag.get('name')), fg=color) softlayer-python-5.9.8/SoftLayer/CLI/tags/delete.py000066400000000000000000000020041415376457700221740ustar00rootroot00000000000000"""Delete Tags.""" # :license: MIT, see LICENSE for more details. import click from SoftLayer.CLI import environment from SoftLayer.managers.tags import TagManager @click.command() @click.argument('identifier') @click.option('--name', required=False, default=False, is_flag=True, show_default=False, help='Assume identifier is a tag name. Useful if your tag name is a number.') @environment.pass_env def cli(env, identifier, name): """Delete a Tag. Tag names that contain spaces need to be encased in quotes""" tag_manager = TagManager(env.client) tag_name = identifier # If the identifier is a int, and user didn't tell us it was a name. if str.isdigit(identifier) and not name: tag = tag_manager.get_tag(identifier) tag_name = tag.get('name', None) result = tag_manager.delete_tag(tag_name) if result: click.secho("Tag {} has been removed".format(tag_name), fg='green') else: click.secho("Failed to remove tag {}".format(tag_name), fg='red') softlayer-python-5.9.8/SoftLayer/CLI/tags/details.py000066400000000000000000000016651415376457700223730ustar00rootroot00000000000000"""Details of a Tag.""" # :license: MIT, see LICENSE for more details. import click from SoftLayer.CLI import environment from SoftLayer.CLI.tags.list import detailed_table from SoftLayer.managers.tags import TagManager @click.command() @click.argument('identifier') @click.option('--name', required=False, default=False, is_flag=True, show_default=False, help='Assume identifier is a tag name. Useful if your tag name is a number.') @environment.pass_env def cli(env, identifier, name): """Get details for a Tag. Identifier can be either a name or tag-id""" tag_manager = TagManager(env.client) # If the identifier is a int, and user didn't tell us it was a name. if str.isdigit(identifier) and not name: tags = [tag_manager.get_tag(identifier)] else: tags = tag_manager.get_tag_by_name(identifier) table = detailed_table(tag_manager, tags) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/tags/list.py000066400000000000000000000046521415376457700217200ustar00rootroot00000000000000"""List Tags.""" # :license: MIT, see LICENSE for more details. import click from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.exceptions import SoftLayerAPIError from SoftLayer.managers.tags import TagManager from SoftLayer import utils # pylint: disable=unnecessary-lambda @click.command() @click.option('--detail', '-d', is_flag=True, default=False, help="Show information about the resources using this tag.") @environment.pass_env def cli(env, detail): """List Tags.""" tag_manager = TagManager(env.client) if detail: tables = detailed_table(tag_manager, tag_manager.get_attached_tags()) for table in tables: env.fout(table) else: table = simple_table(tag_manager) env.fout(table) # pp(tags.list_tags()) def tag_row(tag): """Format a tag table row""" return [tag.get('id'), tag.get('name'), tag.get('referenceCount', 0)] def detailed_table(tag_manager, tags): """Creates a table for each tag, with details about resources using it""" tables = [] for tag in tags: references = tag_manager.get_tag_references(tag.get('id')) # pp(references) new_table = formatting.Table(['Id', 'Type', 'Resource'], title=tag.get('name')) for reference in references: tag_type = utils.lookup(reference, 'tagType', 'keyName') resource_id = reference.get('resourceTableId') resource_row = get_resource_name(tag_manager, resource_id, tag_type) new_table.add_row([resource_id, tag_type, resource_row]) tables.append(new_table) return tables def simple_table(tag_manager): """Just tags and how many resources on each""" tags = tag_manager.list_tags() table = formatting.Table(['Id', 'Tag', 'Count'], title='Tags') for tag in tags.get('attached', []): table.add_row(tag_row(tag)) for tag in tags.get('unattached', []): table.add_row(tag_row(tag)) return table def get_resource_name(tag_manager, resource_id, tag_type): """Returns a string to identify a resource""" name = None try: resource = tag_manager.reference_lookup(resource_id, tag_type) name = tag_manager.get_resource_name(resource, tag_type) except SoftLayerAPIError as exception: name = "{}".format(exception.reason) return name softlayer-python-5.9.8/SoftLayer/CLI/tags/set.py000066400000000000000000000016351415376457700215360ustar00rootroot00000000000000"""Set Tags.""" # :license: MIT, see LICENSE for more details. import click from SoftLayer.CLI import environment from SoftLayer.managers.tags import TagManager @click.command() @click.option('--tags', '-t', type=click.STRING, required=True, help='Comma seperated list of tags, enclosed in quotes. "tag1, tag2"') @click.option('--key-name', '-k', type=click.STRING, required=True, help="Key name of a tag type e.g. GUEST, HARDWARE. See slcli tags taggable output.") @click.option('--resource-id', '-r', type=click.INT, required=True, help="ID of the object being tagged") @environment.pass_env def cli(env, tags, key_name, resource_id): """Set Tags.""" tag_manager = TagManager(env.client) tags = tag_manager.set_tags(tags, key_name, resource_id) if tags: click.secho("Set tags successfully", fg='green') else: click.secho("Failed to set tags", fg='red') softlayer-python-5.9.8/SoftLayer/CLI/tags/taggable.py000066400000000000000000000016351415376457700225110ustar00rootroot00000000000000"""List everything that could be tagged.""" # :license: MIT, see LICENSE for more details. import click from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.managers.tags import TagManager @click.command() @environment.pass_env def cli(env): """List everything that could be tagged.""" tag_manager = TagManager(env.client) tag_types = tag_manager.get_all_tag_types() for tag_type in tag_types: title = "{} ({})".format(tag_type['description'], tag_type['keyName']) table = formatting.Table(['Id', 'Name'], title=title) resources = tag_manager.taggable_by_type(tag_type['keyName']) for resource in resources: table.add_row([ resource['resource']['id'], tag_manager.get_resource_name(resource['resource'], tag_type['keyName']) ]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/template.py000066400000000000000000000042211415376457700216120ustar00rootroot00000000000000""" 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 configparser import io import os.path 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 with open(os.path.expanduser(value), 'r', encoding="utf-8") as file_handle: config = configparser.ConfigParser() ini_str = '[settings]\n' + file_handle.read() ini_fp = io.StringIO(ini_str) config.read_file(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", encoding="utf-8") 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.9.8/SoftLayer/CLI/ticket/000077500000000000000000000000001415376457700207115ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/ticket/__init__.py000066400000000000000000000050441415376457700230250ustar00rootroot00000000000000"""Support tickets.""" import re import click from SoftLayer.CLI import formatting TEMPLATE_MSG = "***** SoftLayer Ticket Content ******" # https://softlayer.github.io/reference/services/SoftLayer_Ticket_Priority/getPriorities/ PRIORITY_MAP = [ 'No Priority', 'Severity 1 - Critical Impact / Service Down', 'Severity 2 - Significant Business Impact', 'Severity 3 - Minor Business Impact', 'Severity 4 - Minimal Business Impact' ] def get_ticket_results(mgr, ticket_id, is_json=False, 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(['Case_Number', ticket['serviceProviderResourceId']]) table.add_row(['title', ticket['title']]) table.add_row(['priority', PRIORITY_MAP[ticket.get('priority', 0)]]) 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', '')) if is_json and '\n' in wrapped_entry: wrapped_entry = re.sub(r"(? (3, 6): import secrets # pylint: disable=import-error,import-outside-toplevel alphabet = string.ascii_letters + string.digits password = ''.join(secrets.choice(alphabet) for i in range(20)) special = ''.join(secrets.choice(string.punctuation) for i in range(3)) return password + special else: raise ImportError("Generating passwords require python 3.6 or higher") softlayer-python-5.9.8/SoftLayer/CLI/user/delete.py000066400000000000000000000015561415376457700222270ustar00rootroot00000000000000"""Delete user.""" # :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): """Sets a user's status to CANCEL_PENDING, which will immediately disable the account, and will eventually be fully removed from the account by an automated internal process. Example: slcli user delete userId """ mgr = SoftLayer.UserManager(env.client) user_id = helpers.resolve_id(mgr.resolve_ids, identifier, 'username') user_template = {'userStatusId': 1021} result = mgr.edit_user(user_id, user_template) if result: click.secho("%s deleted successfully" % identifier, fg='green') else: click.secho("Failed to delete %s" % identifier, fg='red') softlayer-python-5.9.8/SoftLayer/CLI/user/detail.py000066400000000000000000000140621415376457700222230ustar00rootroot00000000000000"""User 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('--keys', '-k', is_flag=True, default=False, help="Show the users API key.") @click.option('--permissions', '-p', is_flag=True, default=False, help="Display permissions assigned to this user. Master users will show no permissions") @click.option('--hardware', '-h', is_flag=True, default=False, help="Display hardware this user has access to.") @click.option('--virtual', '-v', is_flag=True, default=False, help="Display virtual guests this user has access to.") @click.option('--logins', '-l', is_flag=True, default=False, help="Show login history of this user for the last 30 days") @click.option('--events', '-e', is_flag=True, default=False, help="Show event log for this user.") @environment.pass_env def cli(env, identifier, keys, permissions, hardware, virtual, logins, events): """User details.""" mgr = SoftLayer.UserManager(env.client) user_id = helpers.resolve_id(mgr.resolve_ids, identifier, 'username') object_mask = "userStatus[name], parent[id, username], apiAuthenticationKeys[authenticationKey], "\ "unsuccessfulLogins, successfulLogins" user = mgr.get_user(user_id, object_mask) env.fout(basic_info(user, keys)) if permissions: perms = mgr.get_user_permissions(user_id) env.fout(print_permissions(perms)) if hardware: mask = "id, hardware, dedicatedHosts" access = mgr.get_user(user_id, mask) env.fout(print_dedicated_access(access.get('dedicatedHosts', []))) env.fout(print_access(access.get('hardware', []), 'Hardware')) if virtual: mask = "id, virtualGuests" access = mgr.get_user(user_id, mask) env.fout(print_access(access.get('virtualGuests', []), 'Virtual Guests')) if logins: login_log = mgr.get_logins(user_id) env.fout(print_logins(login_log)) if events: event_log = mgr.get_events(user_id) env.fout(print_events(event_log)) def basic_info(user, keys): """Prints a table of basic user information""" table = formatting.KeyValueTable(['Title', 'Basic Information']) table.align['Title'] = 'r' table.align['Basic Information'] = 'l' table.add_row(['Id', user.get('id', '-')]) table.add_row(['Username', user.get('username', '-')]) if keys: for key in user.get('apiAuthenticationKeys'): table.add_row(['APIKEY', key.get('authenticationKey')]) table.add_row(['Name', "%s %s" % (user.get('firstName', '-'), user.get('lastName', '-'))]) table.add_row(['Email', user.get('email')]) table.add_row(['OpenID', user.get('openIdConnectUserName')]) address = "%s %s %s %s %s %s" % ( user.get('address1'), user.get('address2'), user.get('city'), user.get('state'), user.get('country'), user.get('postalCode')) table.add_row(['Address', address]) table.add_row(['Company', user.get('companyName')]) table.add_row(['Created', user.get('createDate')]) table.add_row(['Phone Number', user.get('officePhone')]) if user.get('parentId', False): table.add_row(['Parent User', utils.lookup(user, 'parent', 'username')]) table.add_row( ['Status', utils.lookup(user, 'userStatus', 'name')]) table.add_row(['SSL VPN', user.get('sslVpnAllowedFlag', 'No')]) for login in user.get('unsuccessfulLogins', {}): login_string = "%s From: %s" % (login.get('createDate'), login.get('ipAddress')) table.add_row(['Last Failed Login', login_string]) break for login in user.get('successfulLogins', {}): login_string = "%s From: %s" % (login.get('createDate'), login.get('ipAddress')) table.add_row(['Last Login', login_string]) break return table def print_permissions(permissions): """Prints out a users permissions""" table = formatting.Table(['keyName', 'Description']) for perm in permissions: table.add_row([perm['keyName'], perm['name']]) return table def print_access(access, title): """Prints out the hardware or virtual guests a user can access""" columns = ['id', 'hostname', 'Primary Public IP', 'Primary Private IP', 'Created'] table = formatting.Table(columns, title) for host in access: host_id = host.get('id') host_fqdn = host.get('fullyQualifiedDomainName', '-') host_primary = host.get('primaryIpAddress') host_private = host.get('primaryBackendIpAddress') host_created = host.get('provisionDate') table.add_row([host_id, host_fqdn, host_primary, host_private, host_created]) return table def print_dedicated_access(access): """Prints out the dedicated hosts a user can access""" table = formatting.Table(['id', 'Name', 'Cpus', 'Memory', 'Disk', 'Created'], 'Dedicated Access') for host in access: host_id = host.get('id') host_fqdn = host.get('name') host_cpu = host.get('cpuCount') host_mem = host.get('memoryCapacity') host_disk = host.get('diskCapacity') host_created = host.get('createDate') table.add_row([host_id, host_fqdn, host_cpu, host_mem, host_disk, host_created]) return table def print_logins(logins): """Prints out the login history for a user""" table = formatting.Table(['Date', 'IP Address', 'Successufl Login?']) for login in logins: table.add_row([login.get('createDate'), login.get('ipAddress'), login.get('successFlag')]) return table def print_events(events): """Prints out the event log for a user""" columns = ['Date', 'Type', 'IP Address', 'label', 'username'] table = formatting.Table(columns) for event in events: table.add_row([event.get('eventCreateDate'), event.get('eventName'), event.get('ipAddress'), event.get('label'), event.get('username')]) return table softlayer-python-5.9.8/SoftLayer/CLI/user/edit_details.py000066400000000000000000000025211415376457700234100ustar00rootroot00000000000000"""List Users.""" # :license: MIT, see LICENSE for more details. import json import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import helpers @click.command() @click.argument('user') @click.option('--template', '-t', required=True, help="A json string describing https://softlayer.github.io/reference/datatypes/SoftLayer_User_Customer/") @environment.pass_env def cli(env, user, template): """Edit a Users details JSON strings should be enclosed in '' and each item should be enclosed in "" Example:: slcli user edit-details testUser -t '{"firstName": "Test", "lastName": "Testerson"}' """ mgr = SoftLayer.UserManager(env.client) user_id = helpers.resolve_id(mgr.resolve_ids, user, 'username') user_template = {} if template is not None: try: template_object = json.loads(template) for key in template_object: user_template[key] = template_object[key] except ValueError as ex: raise exceptions.ArgumentError("Unable to parse --template. %s" % ex) result = mgr.edit_user(user_id, user_template) if result: click.secho("%s updated successfully" % (user), fg='green') else: click.secho("Failed to update %s" % (user), fg='red') softlayer-python-5.9.8/SoftLayer/CLI/user/edit_notifications.py000066400000000000000000000020721415376457700246350ustar00rootroot00000000000000"""Enable or Disable specific noticication for the current user""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment @click.command() @click.option('--enable/--disable', default=True, help="Enable (DEFAULT) or Disable selected notification") @click.argument('notification', nargs=-1, required=True) @environment.pass_env def cli(env, enable, notification): """Enable or Disable specific notifications for the active user. Notification names should be enclosed in quotation marks. Example:: slcli user edit-notifications --enable 'Order Approved' 'Reload Complete' """ mgr = SoftLayer.UserManager(env.client) if enable: result = mgr.enable_notifications(notification) else: result = mgr.disable_notifications(notification) if result: click.secho("Notifications updated successfully: %s" % ", ".join(notification), fg='green') else: click.secho("Failed to update notifications: %s" % ", ".join(notification), fg='red') softlayer-python-5.9.8/SoftLayer/CLI/user/edit_permissions.py000066400000000000000000000030211415376457700243320ustar00rootroot00000000000000"""Enable or Disable specific permissions for a user""" # :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.option('--enable/--disable', default=True, help="Enable (DEFAULT) or Disable selected permissions") @click.option('--permission', '-p', multiple=True, help="Permission keyName to set, multiple instances allowed. " "Use keyword ALL to select ALL permisssions") @click.option('--from-user', '-u', default=None, help="Set permissions to match this user's permissions. " "Will add then remove the appropriate permissions") @environment.pass_env def cli(env, identifier, enable, permission, from_user): """Enable or Disable specific permissions.""" mgr = SoftLayer.UserManager(env.client) user_id = helpers.resolve_id(mgr.resolve_ids, identifier, 'username') result = False if from_user: from_user_id = helpers.resolve_id(mgr.resolve_ids, from_user, 'username') result = mgr.permissions_from_user(user_id, from_user_id) elif enable: result = mgr.add_permissions(user_id, permission) else: result = mgr.remove_permissions(user_id, permission) if result: click.secho("Permissions updated successfully: %s" % ", ".join(permission), fg='green') else: click.secho("Failed to update permissions: %s" % ", ".join(permission), fg='red') softlayer-python-5.9.8/SoftLayer/CLI/user/list.py000066400000000000000000000034701415376457700217350ustar00rootroot00000000000000"""List Users.""" # :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 TWO_FACTO_AUTH = 'externalBindingCount' CLASSIC_API_KEYS = 'apiAuthenticationKeyCount' COLUMNS = [ column_helper.Column('id', ('id',)), column_helper.Column('username', ('username',)), column_helper.Column('email', ('email',)), column_helper.Column('displayName', ('displayName',)), column_helper.Column('status', ('userStatus', 'name')), column_helper.Column('hardwareCount', ('hardwareCount',)), column_helper.Column('virtualGuestCount', ('virtualGuestCount',)), column_helper.Column('2FA', (TWO_FACTO_AUTH,)), column_helper.Column('classicAPIKey', (CLASSIC_API_KEYS,)) ] DEFAULT_COLUMNS = [ 'id', 'username', 'email', 'displayName', '2FA', 'classicAPIKey', ] @click.command() @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, columns): """List Users.""" mgr = SoftLayer.UserManager(env.client) users = mgr.list_users() table = formatting.Table(columns.columns) for user in users: user = _yes_format(user, [TWO_FACTO_AUTH, CLASSIC_API_KEYS]) table.add_row([value or formatting.blank() for value in columns.row(user)]) env.fout(table) def _yes_format(user, keys): """Changes all dictionary values to yes whose keys are in the list. """ for key in keys: if user.get(key): user[key] = 'yes' return user softlayer-python-5.9.8/SoftLayer/CLI/user/notifications.py000066400000000000000000000016251415376457700236330ustar00rootroot00000000000000"""List user notifications""" import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting @click.command() @environment.pass_env def cli(env): """My Notifications.""" mgr = SoftLayer.UserManager(env.client) all_notifications = mgr.get_all_notifications() env.fout(notification_table(all_notifications)) def notification_table(all_notifications): """Creates a table of available notifications""" table = formatting.Table(['Id', 'Name', 'Description', 'Enabled']) table.align['Id'] = 'l' table.align['Name'] = 'l' table.align['Description'] = 'l' table.align['Enabled'] = 'l' for notification in all_notifications: table.add_row([notification['id'], notification['name'], notification['description'], notification['enabled']]) return table softlayer-python-5.9.8/SoftLayer/CLI/user/permissions.py000066400000000000000000000034461415376457700233400ustar00rootroot00000000000000"""List A users permissions.""" 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): """User Permissions.""" mgr = SoftLayer.UserManager(env.client) user_id = helpers.resolve_id(mgr.resolve_ids, identifier, 'username') object_mask = "mask[id, permissions, isMasterUserFlag, roles]" user = mgr.get_user(user_id, object_mask) all_permissions = mgr.get_all_permissions() user_permissions = perms_to_dict(user['permissions']) if user['isMasterUserFlag']: click.secho('This account is the Master User and has all permissions enabled', fg='green') env.fout(roles_table(user)) env.fout(permission_table(user_permissions, all_permissions)) def perms_to_dict(perms): """Takes a list of permissions and transforms it into a dictionary for better searching""" permission_dict = {} for perm in perms: permission_dict[perm['keyName']] = True return permission_dict def permission_table(user_permissions, all_permissions): """Creates a table of available permissions""" table = formatting.Table(['Description', 'KeyName', 'Assigned']) table.align['KeyName'] = 'l' table.align['Description'] = 'l' table.align['Assigned'] = 'l' for perm in all_permissions: assigned = user_permissions.get(perm['keyName'], False) table.add_row([perm['name'], perm['keyName'], assigned]) return table def roles_table(user): """Creates a table for a users roles""" table = formatting.Table(['id', 'Role Name', 'Description']) for role in user['roles']: table.add_row([role['id'], role['name'], role['description']]) return table softlayer-python-5.9.8/SoftLayer/CLI/user/vpn_manual.py000066400000000000000000000015361415376457700231230ustar00rootroot00000000000000"""Enable or Disable vpn subnets manual config for a user.""" # :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('user') @click.option('--enable/--disable', default=True, help="Enable or disable vpn subnets manual config.") @environment.pass_env def cli(env, user, enable): """Enable or disable user vpn subnets manual config""" mgr = SoftLayer.UserManager(env.client) user_id = helpers.resolve_id(mgr.resolve_ids, user, 'username') result = mgr.vpn_manual(user_id, enable) message = "{} vpn manual config {}".format(user, 'enable' if enable else 'disable') if result: click.secho(message, fg='green') else: click.secho("Failed to update {}".format(user), fg='red') softlayer-python-5.9.8/SoftLayer/CLI/user/vpn_subnet.py000066400000000000000000000016441415376457700231460ustar00rootroot00000000000000"""Add or remove specific subnets access for a user.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import helpers @click.command() @click.option('--add/--remove', default=True, help="Add or remove access to subnets.") @click.argument('user', nargs=1, required=True) @click.argument('subnet', nargs=-1, required=True) @environment.pass_env def cli(env, user, add, subnet): """Add or remove subnets access for a user.""" mgr = SoftLayer.UserManager(env.client) user_id = helpers.resolve_id(mgr.resolve_ids, user, 'username') if add: result = mgr.vpn_subnet_add(user_id, subnet) else: result = mgr.vpn_subnet_remove(user_id, subnet) if result: click.secho("%s updated successfully" % (user), fg='green') else: click.secho("Failed to update %s" % (user), fg='red') softlayer-python-5.9.8/SoftLayer/CLI/virt/000077500000000000000000000000001415376457700204125ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/virt/__init__.py000066400000000000000000000021431415376457700225230ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/virt/authorize_storage.py000066400000000000000000000032321415376457700245220ustar00rootroot00000000000000"""Authorize File, Block and Portable Storage to 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('--username-storage', '-u', type=click.STRING, help="The storage username to be added to the virtual server") @click.option('--portable-id', type=click.INT, help="The portable storage id to be added to the virtual server") @environment.pass_env def cli(env, identifier, username_storage, portable_id): """Authorize File, Block and Portable Storage to a Virtual Server.""" virtual = SoftLayer.VSManager(env.client) virtual_id = helpers.resolve_id(virtual.resolve_ids, identifier, 'virtual') table = formatting.KeyValueTable(['name', 'value'], title="Portable Storage Detail") table.align['name'] = 'r' table.align['value'] = 'l' if username_storage: if not virtual.authorize_storage(virtual_id, username_storage): raise exceptions.CLIAbort('Authorize Volume Failed') env.fout('Successfully Volume: %s was Added.' % username_storage) if portable_id: portable_id = helpers.resolve_id(virtual.resolve_ids, portable_id, 'storage') portable_result = virtual.attach_portable_storage(virtual_id, portable_id) env.fout('Successfully Portable Storage: %i was Added.' % portable_id) table.add_row(['Id', portable_result['id']]) table.add_row(['createDate', portable_result['createDate']]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/virt/bandwidth.py000066400000000000000000000105771415376457700227420ustar00rootroot00000000000000"""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('--start_date', '-s', type=click.STRING, required=True, help="Start Date YYYY-MM-DD, YYYY-MM-DDTHH:mm:ss,") @click.option('--end_date', '-e', type=click.STRING, required=True, help="End Date YYYY-MM-DD, YYYY-MM-DDTHH:mm:ss") @click.option('--summary_period', '-p', type=click.INT, default=3600, show_default=True, help="300, 600, 1800, 3600, 43200 or 86400 seconds") @click.option('--quite_summary', '-q', is_flag=True, default=False, show_default=True, help="Only show the summary table") @environment.pass_env def cli(env, identifier, start_date, end_date, summary_period, quite_summary): """Bandwidth data over date range. Bandwidth is listed in GB Using just a date might get you times off by 1 hour, use T00:01 to get just the specific days data Timezones can also be included with the YYYY-MM-DDTHH:mm:ss.00000-HH:mm format. Due to some rounding and date alignment details, results here might be slightly different than results in the control portal. Example:: slcli hw bandwidth 1234 -s 2019-05-01T00:01 -e 2019-05-02T00:00:01.00000-12:00 """ vsi = SoftLayer.VSManager(env.client) vsi_id = helpers.resolve_id(vsi.resolve_ids, identifier, 'VS') # Summary period is broken for virtual guests, check VIRT-11733 for a resolution. # For now, we are going to ignore summary_period and set it to the default the API imposes if summary_period != 300: click.secho("""The Summary Period option is currently set to the 300s as the backend API will throw an exception any other value. This should be resolved in the next version of the slcli.""", fg='yellow') summary_period = 300 data = vsi.get_bandwidth_data(vsi_id, start_date, end_date, None, None) title = "Bandwidth Report: %s - %s" % (start_date, end_date) table, sum_table = create_bandwidth_table(data, summary_period, title) env.fout(sum_table) if not quite_summary: env.fout(table) def create_bandwidth_table(data, summary_period, title="Bandwidth Report"): """Create 2 tables, bandwidth and sumamry. Used here and in hw bandwidth command""" formatted_data = {} for point in data: key = utils.clean_time(point['dateTime']) data_type = point['type'] # conversion from byte to megabyte value = round(float(point['counter']) / 2 ** 20, 4) if formatted_data.get(key) is None: formatted_data[key] = {} formatted_data[key][data_type] = float(value) table = formatting.Table(['Date', 'Pub In', 'Pub Out', 'Pri In', 'Pri Out'], title=title) sum_table = formatting.Table(['Type', 'Sum GB', 'Average MBps', 'Max GB', 'Max Date'], title="Summary") # Required to specify keyName because getBandwidthTotals returns other counter types for some reason. bw_totals = [ {'keyName': 'publicIn_net_octet', 'sum': 0.0, 'max': 0, 'name': 'Pub In'}, {'keyName': 'publicOut_net_octet', 'sum': 0.0, 'max': 0, 'name': 'Pub Out'}, {'keyName': 'privateIn_net_octet', 'sum': 0.0, 'max': 0, 'name': 'Pri In'}, {'keyName': 'privateOut_net_octet', 'sum': 0.0, 'max': 0, 'name': 'Pri Out'}, ] for key, value in formatted_data.items(): new_row = [key] for bw_type in bw_totals: counter = value.get(bw_type['keyName'], 0) new_row.append(mb_to_gb(counter)) bw_type['sum'] = bw_type['sum'] + counter if counter > bw_type['max']: bw_type['max'] = counter bw_type['maxDate'] = key table.add_row(new_row) for bw_type in bw_totals: total = bw_type.get('sum', 0.0) average = 0 if total > 0: average = round(total / len(formatted_data) / summary_period, 4) sum_table.add_row([ bw_type.get('name'), mb_to_gb(total), average, mb_to_gb(bw_type.get('max')), bw_type.get('maxDate') ]) return table, sum_table def mb_to_gb(mbytes): """Converts a MegaByte int to GigaByte. mbytes/2^10""" return round(mbytes / 2 ** 10, 4) softlayer-python-5.9.8/SoftLayer/CLI/virt/billing.py000066400000000000000000000025401415376457700224050ustar00rootroot00000000000000"""Get billing for a virtual 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') @environment.pass_env def cli(env, identifier): """Get billing for a virtual device.""" virtual = SoftLayer.VSManager(env.client) virtual_id = helpers.resolve_id(virtual.resolve_ids, identifier, 'virtual') result = virtual.get_instance(virtual_id) table = formatting.KeyValueTable(['name', 'value']) table.align['name'] = 'r' table.align['value'] = 'l' table.add_row(['Id', identifier]) table.add_row(['Billing Item Id', utils.lookup(result, 'billingItem', 'id')]) table.add_row(['Recurring Fee', utils.lookup(result, 'billingItem', 'recurringFee')]) table.add_row(['Total', utils.lookup(result, 'billingItem', 'nextInvoiceTotalRecurringAmount')]) table.add_row(['Provision Date', utils.lookup(result, 'provisionDate')]) price_table = formatting.Table(['Description', 'Recurring Price']) for item in utils.lookup(result, 'billingItem', 'children') or []: price_table.add_row([item['description'], item['nextInvoiceTotalRecurringAmount']]) table.add_row(['prices', price_table]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/virt/cancel.py000066400000000000000000000012041415376457700222060ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/virt/capacity/000077500000000000000000000000001415376457700222075ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/virt/capacity/__init__.py000066400000000000000000000027071415376457700243260ustar00rootroot00000000000000"""Manages Reserved Capacity.""" # :license: MIT, see LICENSE for more details. import importlib import os import click CONTEXT = {'help_option_names': ['-h', '--help'], 'max_content_width': 999} class CapacityCommands(click.MultiCommand): """Loads module for capacity related commands. Will automatically replace _ with - where appropriate. I'm not sure if this is better or worse than using a long list of manual routes, so I'm trying it here. CLI/virt/capacity/create_guest.py -> slcli vs capacity create-guest """ def __init__(self, **attrs): click.MultiCommand.__init__(self, **attrs) self.path = os.path.dirname(__file__) def list_commands(self, ctx): """List all sub-commands.""" commands = [] for filename in os.listdir(self.path): if filename == '__init__.py': continue if filename.endswith('.py'): commands.append(filename[:-3].replace("_", "-")) commands.sort() return commands def get_command(self, ctx, cmd_name): """Get command for click.""" path = "%s.%s" % (__name__, cmd_name) path = path.replace("-", "_") module = importlib.import_module(path) return getattr(module, 'cli') # Required to get the sub-sub-sub command to work. @click.group(cls=CapacityCommands, context_settings=CONTEXT) def cli(): """Base command for all capacity related concerns""" softlayer-python-5.9.8/SoftLayer/CLI/virt/capacity/create.py000066400000000000000000000044411415376457700240270ustar00rootroot00000000000000"""Create a Reserved Capacity instance.""" import click from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.managers.vs_capacity import CapacityManager as CapacityManager @click.command(epilog=click.style("""WARNING: Reserved Capacity is on a yearly contract""" """ and not cancelable until the contract is expired.""", fg='red')) @click.option('--name', '-n', required=True, prompt=True, help="Name for your new reserved capacity") @click.option('--backend_router_id', '-b', required=True, prompt=True, type=int, help="backendRouterId, create-options has a list of valid ids to use.") @click.option('--flavor', '-f', required=True, prompt=True, help="Capacity keyname (C1_2X2_1_YEAR_TERM for example).") @click.option('--instances', '-i', required=True, prompt=True, type=int, help="Number of VSI instances this capacity reservation can support.") @click.option('--test', is_flag=True, help="Do not actually create the virtual server") @environment.pass_env def cli(env, name, backend_router_id, flavor, instances, test=False): """Create a Reserved Capacity instance. *WARNING*: Reserved Capacity is on a yearly contract and not cancelable until the contract is expired. """ manager = CapacityManager(env.client) result = manager.create( name=name, backend_router_id=backend_router_id, flavor=flavor, instances=instances, test=test) if test: table = formatting.Table(['Name', 'Value'], "Test Order") container = result['orderContainers'][0] table.add_row(['Name', container['name']]) table.add_row(['Location', container['locationObject']['longName']]) for price in container['prices']: table.add_row(['Contract', price['item']['description']]) table.add_row(['Hourly Total', result['postTaxRecurring']]) else: table = formatting.Table(['Name', 'Value'], "Reciept") table.add_row(['Order Date', result['orderDate']]) table.add_row(['Order ID', result['orderId']]) table.add_row(['status', result['placedOrder']['status']]) table.add_row(['Hourly Total', result['orderDetails']['postTaxRecurring']]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/virt/capacity/create_guest.py000066400000000000000000000060771415376457700252450ustar00rootroot00000000000000"""List Reserved Capacity""" # :license: MIT, see LICENSE for more details. import click from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.CLI import helpers from SoftLayer.CLI.virt.create import _parse_create_args as _parse_create_args from SoftLayer.CLI.virt.create import _update_with_like_args as _update_with_like_args from SoftLayer.managers.vs_capacity import CapacityManager as CapacityManager @click.command() @click.option('--capacity-id', type=click.INT, help="Reserve capacity Id to provision this guest into.") @click.option('--primary-disk', type=click.Choice(['25', '100']), default='25', help="Size of the main drive.") @click.option('--hostname', '-H', required=True, prompt=True, help="Host portion of the FQDN.") @click.option('--domain', '-D', required=True, prompt=True, help="Domain portion of the FQDN.") @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', type=click.STRING, help="Specify the mode to boot the OS in. Supported modes are HVM and PV.") @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="Additional 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('--userdata', '-u', help="User defined metadata string.") @click.option('--ipv6', is_flag=True, help="Adds an IPv6 address to this guest") @click.option('--test', is_flag=True, help="Test order, will return the order container, but not actually order a server.") @environment.pass_env def cli(env, **args): """Allows for creating a virtual guest in a reserved capacity. Only MONTHLY guests are supported at this time. If you would like support for hourly reserved capacity guests, please open an issue on the softlayer-python github. """ create_args = _parse_create_args(env.client, args) create_args['primary_disk'] = args.get('primary_disk') manager = CapacityManager(env.client) capacity_id = args.get('capacity_id') test = args.get('test') result = manager.create_guest(capacity_id, test, create_args) env.fout(_build_receipt(result, test)) def _build_receipt(result, test=False): title = "OrderId: %s" % (result.get('orderId', 'No order placed')) table = formatting.Table(['Item Id', 'Description'], title=title) table.align['Description'] = 'l' if test: prices = result['prices'] else: prices = result['orderDetails']['prices'] for item in prices: table.add_row([item['id'], item['item']['description']]) return table softlayer-python-5.9.8/SoftLayer/CLI/virt/capacity/create_options.py000066400000000000000000000031701415376457700256000ustar00rootroot00000000000000"""List options for creating Reserved Capacity""" # :license: MIT, see LICENSE for more details. import click from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.managers.vs_capacity import CapacityManager as CapacityManager @click.command() @environment.pass_env def cli(env): """List options for creating Reserved Capacity""" manager = CapacityManager(env.client) items = manager.get_create_options() items.sort(key=lambda term: int(term['capacity'])) table = formatting.Table(["KeyName", "Description", "Term", "Default Hourly Price Per Instance"], title="Reserved Capacity Options") table.align["Hourly Price"] = "l" table.align["Description"] = "l" table.align["KeyName"] = "l" for item in items: table.add_row([ item['keyName'], item['description'], item['capacity'], get_price(item) ]) env.fout(table) regions = manager.get_available_routers() location_table = formatting.Table(['Location', 'POD', 'BackendRouterId'], 'Orderable Locations') for region in regions: for location in region['locations']: for pod in location['location']['pods']: location_table.add_row([region['keyname'], pod['backendRouterName'], pod['backendRouterId']]) env.fout(location_table) def get_price(item): """Finds the price with the default locationGroupId""" the_price = "No Default Pricing" for price in item.get('prices', []): if not price.get('locationGroupId'): the_price = "%0.4f" % float(price['hourlyRecurringFee']) return the_price softlayer-python-5.9.8/SoftLayer/CLI/virt/capacity/detail.py000066400000000000000000000040661415376457700240310ustar00rootroot00000000000000"""Shows the details of a reserved capacity group""" import click from SoftLayer.CLI import columns as column_helper from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.managers.vs_capacity import CapacityManager as CapacityManager COLUMNS = [ column_helper.Column('Id', ('id',)), column_helper.Column('hostname', ('hostname',)), column_helper.Column('domain', ('domain',)), column_helper.Column('primary_ip', ('primaryIpAddress',)), column_helper.Column('backend_ip', ('primaryBackendIpAddress',)), ] DEFAULT_COLUMNS = [ 'id', 'hostname', 'domain', 'primary_ip', 'backend_ip' ] @click.command(epilog="Once provisioned, virtual guests can be managed with the slcli vs commands") @click.argument('identifier') @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, identifier, columns): """Reserved Capacity Group details. Will show which guests are assigned to a reservation.""" manager = CapacityManager(env.client) mask = """mask[instances[id,createDate,guestId,billingItem[id, description, recurringFee, category[name]], guest[modifyDate,id, primaryBackendIpAddress, primaryIpAddress,domain, hostname]]]""" result = manager.get_object(identifier, mask) try: flavor = result['instances'][0]['billingItem']['description'] except KeyError: flavor = "Pending Approval..." table = formatting.Table(columns.columns, title="%s - %s" % (result.get('name'), flavor)) # RCI = Reserved Capacity Instance for rci in result['instances']: guest = rci.get('guest', None) if guest is not None: table.add_row([value or formatting.blank() for value in columns.row(guest)]) else: table.add_row(['-' for value in columns.columns]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/virt/capacity/list.py000066400000000000000000000021671415376457700235420ustar00rootroot00000000000000"""List Reserved Capacity""" import click from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.managers.vs_capacity import CapacityManager as CapacityManager @click.command() @environment.pass_env def cli(env): """List Reserved Capacity groups.""" manager = CapacityManager(env.client) result = manager.list() table = formatting.Table( ["ID", "Name", "Capacity", "Flavor", "Location", "Created"], title="Reserved Capacity" ) for r_c in result: occupied_string = "#" * int(r_c.get('occupiedInstanceCount', 0)) available_string = "-" * int(r_c.get('availableInstanceCount', 0)) try: flavor = r_c['instances'][0]['billingItem']['description'] # cost = float(r_c['instances'][0]['billingItem']['hourlyRecurringFee']) except KeyError: flavor = "Unknown Billing Item" location = r_c['backendRouter']['hostname'] capacity = "%s%s" % (occupied_string, available_string) table.add_row([r_c['id'], r_c['name'], capacity, flavor, location, r_c['createDate']]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/virt/capture.py000066400000000000000000000024661415376457700224370ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/virt/create.py000066400000000000000000000341571415376457700222410ustar00rootroot00000000000000"""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'], 'placement_id': like_details.get('placementGroupId', None), 'transient': like_details.get('transientGuestFlag', None), } 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.get('billing', 'hourly') == 'hourly', "cpus": args.get('cpu', None), "ipv6": args.get('ipv6', None), "disks": args.get('disk', None), "os_code": args.get('os', None), "memory": args.get('memory', None), "flavor": args.get('flavor', None), "domain": args.get('domain', None), "host_id": args.get('host_id', None), "private": args.get('private', None), "transient": args.get('transient', None), "hostname": args.get('hostname', None), "nic_speed": args.get('network', None), "boot_mode": args.get('boot_mode', None), "dedicated": args.get('dedicated', None), "post_uri": args.get('postinstall', None), "datacenter": args.get('datacenter', None), "public_vlan": args.get('vlan_public', None), "private_vlan": args.get('vlan_private', None), "public_subnet": args.get('subnet_public', None), "private_subnet": args.get('subnet_private', None), "public_router": args.get('router_public', None), "private_router": args.get('router_private', 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.get('san') 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('userdata'): data['userdata'] = args['userdata'] elif args.get('userfile'): with open(args['userfile'], 'r', encoding="utf-8") as userfile: data['userdata'] = userfile.read() # 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('public_security_group'): pub_groups = args.get('public_security_group') data['public_security_groups'] = list(pub_groups) if args.get('private_security_group'): priv_groups = args.get('private_security_group') data['private_security_groups'] = list(priv_groups) if args.get('tag', False): data['tags'] = ','.join(args['tag']) if args.get('host_id'): data['host_id'] = args['host_id'] if args.get('placementgroup'): resolver = SoftLayer.managers.PlacementManager(client).resolve_ids data['placement_id'] = helpers.resolve_id(resolver, args.get('placementgroup'), 'PlacementGroup') return data @click.command(epilog="See 'slcli vs create-options' for valid options") @click.option('--hostname', '-H', required=True, prompt=True, help="Host portion of the FQDN") @click.option('--domain', '-D', required=True, prompt=True, help="Domain portion of the FQDN") @click.option('--cpu', '-c', type=click.INT, help="Number of CPU cores (not available with flavors)") @click.option('--memory', '-m', type=virt.MEM_TYPE, help="Memory in mebibytes (not available with flavors)") @click.option('--flavor', '-f', type=click.STRING, help="Public Virtual Server flavor key name") @click.option('--datacenter', '-d', required=True, prompt=True, help="Datacenter shortname") @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', type=click.STRING, help="Specify the mode to boot the OS in. Supported modes are HVM and PV.") @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", type=click.INT) @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', type=click.Path(exists=True, readable=True, resolve_path=True), help="Read userdata from file") @click.option('--vlan-public', type=click.INT, help="The ID of the public VLAN on which you want the virtual server placed") @click.option('--vlan-private', type=click.INT, help="The ID of the private VLAN on which you want the virtual server placed") @click.option('--subnet-public', type=click.INT, help="The ID of the public SUBNET on which you want the virtual server placed") @click.option('--subnet-private', type=click.INT, help="The ID of the private SUBNET on which you want the virtual server placed") @click.option('--router-public', type=click.INT, help="The ID of the public ROUTER on which you want the virtual server placed") @click.option('--router-private', type=click.INT, help="The ID of the private ROUTER on which you want the virtual server placed") @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") @click.option('--placementgroup', help="Placement Group name or Id to order this guest on. See: slcli vs placementgroup list") @click.option('--ipv6', is_flag=True, help="Adds an IPv6 address to this guest") @click.option('--transient', is_flag=True, help="Create a transient virtual server") @environment.pass_env def cli(env, **args): """Order/create virtual servers.""" vsi = SoftLayer.VSManager(env.client) _validate_args(env, args) create_args = _parse_create_args(env.client, args) test = args.get('test', False) do_create = not (args.get('export') or test) 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.') if args.get('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.') else: result = vsi.order_guest(create_args, test) output = _build_receipt_table(result, args.get('billing'), test) if do_create: env.fout(_build_guest_table(result)) env.fout(output) if args.get('wait'): virtual_guests = utils.lookup(result, 'orderDetails', 'virtualGuests') guest_id = virtual_guests[0]['id'] click.secho("Waiting for %s to finish provisioning..." % guest_id, fg='green') ready = vsi.wait_for_ready(guest_id, args.get('wait') or 1) if ready is False: env.out(env.fmt(output)) raise exceptions.CLIHalt(code=1) def _build_receipt_table(result, billing="hourly", test=False): """Retrieve the total recurring fee of the items prices""" title = "OrderId: %s" % (result.get('orderId', 'No order placed')) table = formatting.Table(['Cost', 'Description'], title=title) table.align['Cost'] = 'r' table.align['Description'] = 'l' total = 0.000 if test: prices = result['prices'] else: prices = result['orderDetails']['prices'] for item in prices: rate = 0.000 if billing == "hourly": rate += float(item.get('hourlyRecurringFee', 0.000)) else: rate += float(item.get('recurringFee', 0.000)) total += rate table.add_row([rate, item['item']['description']]) table.add_row(["%.3f" % total, "Total %s cost" % billing]) return table def _build_guest_table(result): table = formatting.Table(['ID', 'FQDN', 'guid', 'Order Date']) table.align['name'] = 'r' table.align['value'] = 'l' virtual_guests = utils.lookup(result, 'orderDetails', 'virtualGuests') for guest in virtual_guests: table.add_row([guest['id'], guest['fullyQualifiedDomainName'], guest['globalIdentifier'], result['orderDate']]) return table 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['transient']]): raise exceptions.ArgumentError( '[--dedicated] not allowed with [--transient]') 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 args['transient'] and args['billing'] == 'monthly': raise exceptions.ArgumentError( '[--transient] not allowed with [--billing monthly]') 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.9.8/SoftLayer/CLI/virt/create_options.py000066400000000000000000000253121415376457700240050ustar00rootroot00000000000000"""Virtual server order options.""" # :license: MIT, see LICENSE for more details. # pylint: disable=too-many-statements import click import SoftLayer from SoftLayer.CLI import environment from SoftLayer.CLI import formatting @click.command(short_help="Get options to use for creating virtual servers.") @click.argument('location', required=False) @click.option('--vsi-type', required=False, show_default=True, default='PUBLIC_CLOUD_SERVER', type=click.Choice(['PUBLIC_CLOUD_SERVER', 'TRANSIENT_CLOUD_SERVER', 'SUSPEND_CLOUD_SERVER', 'CLOUD_SERVER']), help="VS keyName type.") @click.option('--prices', '-p', is_flag=True, help='Use --prices to list the server item prices, and to list the Item Prices by location,' 'add it to the --prices option using location short name, e.g. --prices dal13') @environment.pass_env def cli(env, vsi_type, prices, location=None): """Virtual server order options.""" vsi = SoftLayer.VSManager(env.client) options = vsi.get_create_options(vsi_type, location) tables = [] # Datacenters dc_table = formatting.Table(['datacenter', 'Value'], title="Datacenters") dc_table.sortby = 'Value' dc_table.align = 'l' for location_info in options['locations']: dc_table.add_row([location_info['name'], location_info['key']]) tables.append(dc_table) if vsi_type == 'CLOUD_SERVER': tables.append(guest_core_prices_table(options['guest_core'], prices)) tables.append(ram_prices_table(options['ram'], prices)) else: tables.append(preset_prices_table(options['sizes'], prices)) tables.append(os_prices_table(options['operating_systems'], prices)) tables.append(port_speed_prices_table(options['port_speed'], prices)) tables.append(database_prices_table(options['database'], prices)) tables.append(guest_disk_prices_table(options['guest_disk'], prices)) tables.append(extras_prices_table(options['extras'], prices)) env.fout(tables) def preset_prices_table(sizes, prices=False): """Shows Server Preset options prices. :param [] sizes: List of Hardware Server sizes. :param prices: Include pricing information or not. """ preset_price_table = formatting.Table(['Size', 'Value', 'Hourly', 'Monthly'], title="Sizes Prices") preset_price_table.sortby = 'Value' preset_price_table.align = 'l' preset_table = formatting.Table(['Size', 'Value'], title="Sizes") preset_table.sortby = 'Value' preset_table.align = 'l' for size in sizes: if (size['hourlyRecurringFee'] > 0) or (size['recurringFee'] > 0): preset_price_table.add_row([size['name'], size['key'], "%.4f" % size['hourlyRecurringFee'], "%.4f" % size['recurringFee']]) preset_table.add_row([size['name'], size['key']]) if prices: return preset_price_table return preset_table def os_prices_table(operating_systems, prices=False): """Shows Server Operating Systems prices cost and capacity restriction. :param [] operating_systems: List of Hardware Server operating systems. :param prices: Include pricing information or not. """ os_price_table = formatting.Table(['OS Key', 'Hourly', 'Monthly', 'Restriction'], title="Operating Systems Prices") os_price_table.sortby = 'OS Key' os_price_table.align = 'l' os_table = formatting.Table(['OS', 'Key', 'Reference Code'], title="Operating Systems") os_table.sortby = 'Key' os_table.align = 'l' for operating_system in operating_systems: for price in operating_system['prices']: cr_max = _get_price_data(price, 'capacityRestrictionMaximum') cr_min = _get_price_data(price, 'capacityRestrictionMinimum') cr_type = _get_price_data(price, 'capacityRestrictionType') os_price_table.add_row( [operating_system['key'], _get_price_data(price, 'hourlyRecurringFee'), _get_price_data(price, 'recurringFee'), "%s - %s %s" % (cr_min, cr_max, cr_type)]) os_table.add_row([operating_system['name'], operating_system['key'], operating_system['referenceCode']]) if prices: return os_price_table return os_table def port_speed_prices_table(port_speeds, prices=False): """Shows Server Port Speeds prices cost and capacity restriction. :param [] port_speeds: List of Hardware Server Port Speeds. :param prices: Include pricing information or not. """ port_speed_price_table = formatting.Table(['Key', 'Speed', 'Hourly', 'Monthly'], title="Network Options Prices") port_speed_price_table.sortby = 'Speed' port_speed_price_table.align = 'l' port_speed_table = formatting.Table(['network', 'Key'], title="Network Options") port_speed_table.sortby = 'Key' port_speed_table.align = 'l' for speed in port_speeds: for price in speed['prices']: port_speed_price_table.add_row( [speed['key'], speed['speed'], _get_price_data(price, 'hourlyRecurringFee'), _get_price_data(price, 'recurringFee')]) port_speed_table.add_row([speed['name'], speed['key']]) if prices: return port_speed_price_table return port_speed_table def extras_prices_table(extras, prices=False): """Shows Server extras prices cost and capacity restriction. :param [] extras: List of Hardware Server Extras. :param prices: Include pricing information or not. """ extras_price_table = formatting.Table(['Extra Option Key', 'Hourly', 'Monthly'], title="Extras Prices") extras_price_table.align = 'l' extras_table = formatting.Table(['Extra Option', 'Value'], title="Extras") extras_table.sortby = 'Value' extras_table.align = 'l' for extra in extras: for price in extra['prices']: extras_price_table.add_row( [extra['key'], _get_price_data(price, 'hourlyRecurringFee'), _get_price_data(price, 'recurringFee')]) extras_table.add_row([extra['name'], extra['key']]) if prices: return extras_price_table return extras_table def ram_prices_table(ram_list, prices=False): """Shows Server Port Speeds prices cost and capacity restriction. :param [] ram_list: List of Virtual Server Ram. :param prices: Include pricing information or not. """ ram_price_table = formatting.Table(['Key', 'Hourly', 'Monthly'], title="Ram Prices") ram_price_table.sortby = 'Key' ram_price_table.align = 'l' ram_table = formatting.Table(['memory', 'Value'], title="RAM") ram_table.sortby = 'Value' ram_table.align = 'l' for ram in ram_list: for price in ram['prices']: ram_price_table.add_row( [ram['key'], _get_price_data(price, 'hourlyRecurringFee'), _get_price_data(price, 'recurringFee')]) ram_table.add_row([ram['name'], ram['key']]) if prices: return ram_price_table return ram_table def database_prices_table(database_list, prices=False): """Shows Server Port Speeds prices cost and capacity restriction. :param [] database_list: List of Virtual Server database. :param prices: Include pricing information or not. """ database_price_table = formatting.Table(['Key', 'Hourly', 'Monthly', 'Restriction'], title="Data Base Prices") database_price_table.sortby = 'Key' database_price_table.align = 'l' database_table = formatting.Table(['database', 'Value'], title="Databases") database_table.sortby = 'Value' database_table.align = 'l' for database in database_list: for price in database['prices']: cr_max = _get_price_data(price, 'capacityRestrictionMaximum') cr_min = _get_price_data(price, 'capacityRestrictionMinimum') cr_type = _get_price_data(price, 'capacityRestrictionType') database_price_table.add_row( [database['key'], _get_price_data(price, 'hourlyRecurringFee'), _get_price_data(price, 'recurringFee'), "%s - %s %s" % (cr_min, cr_max, cr_type)]) database_table.add_row([database['name'], database['key']]) if prices: return database_price_table return database_table def guest_core_prices_table(guest_core_list, prices=False): """Shows Server Port Speeds prices cost and capacity restriction. :param [] guest_core_list: List of Virtual Server guest_core. :param prices: Include pricing information or not. """ guest_core_price_table = formatting.Table(['Key', 'Hourly', 'Monthly'], title="Guest Core Prices") guest_core_price_table.sortby = 'Key' guest_core_price_table.align = 'l' guest_core_table = formatting.Table(['cpu', 'Value', 'Capacity'], title="Guest_core") guest_core_table.sortby = 'Value' guest_core_table.align = 'l' for guest_core in guest_core_list: for price in guest_core['prices']: guest_core_price_table.add_row( [guest_core['key'], _get_price_data(price, 'hourlyRecurringFee'), _get_price_data(price, 'recurringFee')]) guest_core_table.add_row([guest_core['name'], guest_core['key'], guest_core['capacity']]) if prices: return guest_core_price_table return guest_core_table def guest_disk_prices_table(guest_disk_list, prices=False): """Shows Server Port Speeds prices cost and capacity restriction. :param [] guest_disk_list: List of Virtual Server guest_disk. :param prices: Include pricing information or not. """ guest_disk_price_table = formatting.Table(['Key', 'Hourly', 'Monthly'], title="Guest Disk Prices") guest_disk_price_table.sortby = 'Key' guest_disk_price_table.align = 'l' guest_disk_table = formatting.Table(['guest_disk', 'Value', 'Capacity', 'Disk'], title="Guest_disks") guest_disk_table.sortby = 'Value' guest_disk_table.align = 'l' for guest_disk in guest_disk_list: for price in guest_disk['prices']: guest_disk_price_table.add_row( [guest_disk['key'], _get_price_data(price, 'hourlyRecurringFee'), _get_price_data(price, 'recurringFee')]) guest_disk_table.add_row( [guest_disk['name'], guest_disk['key'], guest_disk['capacity'], guest_disk['disk']]) if prices: return guest_disk_price_table return guest_disk_table def _get_price_data(price, item): """Get a specific data from HS price. :param price: Hardware Server price. :param string item: Hardware Server price data. """ result = '-' if item in price: result = price[item] return result softlayer-python-5.9.8/SoftLayer/CLI/virt/credentials.py000066400000000000000000000013051415376457700232600ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/virt/detail.py000066400000000000000000000177071415376457700222420ustar00rootroot00000000000000"""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.CLI.virt.storage import get_local_storage_table 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) local_disks = vsi.get_local_disks(vs_id) table_local_disks = get_local_storage_table(local_disks) 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'], result['status']['name'] )]) 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', '-')]) table.add_row(['os_version', operating_system.get('version', '-')]) table.add_row(['cores', result['maxCpu']]) table.add_row(['memory', formatting.mb_to_gb(result['maxMemory'])]) table.add_row(['drives', table_local_disks]) table.add_row(['public_ip', result.get('primaryIpAddress', '-')]) table.add_row(['private_ip', result.get('primaryBackendIpAddress', '-')]) table.add_row(['private_only', result['privateNetworkOnlyFlag']]) table.add_row(['private_cpu', result['dedicatedAccountHostOnlyFlag']]) table.add_row(['transient', result.get('transientGuestFlag', False)]) table.add_row(['created', result['createDate']]) table.add_row(['modified', result['modifyDate']]) last_transaction = "{} ({})".format(utils.lookup(result, 'lastTransaction', 'transactionGroup', 'name'), utils.clean_time(utils.lookup(result, 'lastTransaction', 'modifyDate'))) table.add_row(['last_transaction', last_transaction]) table.add_row(['billing', 'Hourly' if result['hourlyBillingFlag'] else'Monthly']) table.add_row(['preset', utils.lookup(result, 'billingItem', 'orderItem', 'preset', 'keyName') or '-']) table.add_row(_get_owner_row(result)) table.add_row(_get_vlan_table(result)) bandwidth = vsi.get_bandwidth_allocation(vs_id) table.add_row(['Bandwidth', _bw_table(bandwidth)]) security_table = _get_security_table(result) if security_table is not None: table.add_row(['security_groups', security_table]) table.add_row(['notes', result.get('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 _bw_table(bw_data): """Generates a bandwidth useage table""" table = formatting.Table(['Type', 'In GB', 'Out GB', 'Allotment']) for bw_point in bw_data.get('usage'): bw_type = 'Private' allotment = 'N/A' if bw_point['type']['alias'] == 'PUBLIC_SERVER_BW': bw_type = 'Public' if not bw_data.get('allotment'): allotment = '-' else: allotment = utils.lookup(bw_data, 'allotment', 'amount') table.add_row([bw_type, bw_point['amountIn'], bw_point['amountOut'], allotment]) return 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()]) def _get_owner_row(result): """Formats and resturns the Owner row""" if utils.lookup(result, 'billingItem') != []: owner = utils.lookup(result, 'billingItem', 'orderItem', 'order', 'userRecord', 'username') else: owner = formatting.blank() return (['owner', owner]) def _get_vlan_table(result): """Formats and resturns a vlan table""" vlan_table = formatting.Table(['type', 'number', 'id']) for vlan in result['networkVlans']: vlan_table.add_row([ vlan['networkSpace'], vlan['vlanNumber'], vlan['id']]) return ['vlans', vlan_table] def _get_security_table(result): secgroup_table = formatting.Table(['interface', 'id', 'name']) has_secgroups = False if result.get('networkComponents'): 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', '-')]) if has_secgroups: return secgroup_table else: return None softlayer-python-5.9.8/SoftLayer/CLI/virt/dns.py000066400000000000000000000056531415376457700215610ustar00rootroot00000000000000"""Sync DNS records.""" # :license: MIT, see LICENSE for more details. # pylint: disable=duplicate-code 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.""" mask = """mask[id, globalIdentifier, fullyQualifiedDomainName, hostname, domain, primaryBackendIpAddress,primaryIpAddress, primaryNetworkComponent[id,primaryIpAddress,primaryVersion6IpAddressRecord[ipAddress]]]""" dns = SoftLayer.DNSManager(env.client) server = SoftLayer.VSManager(env.client) server_id = helpers.resolve_id(server.resolve_ids, identifier, 'VS') instance = server.get_instance(server_id, mask=mask) zone_id = helpers.resolve_id(dns.resolve_ids, instance['domain'], name='zone') if not instance['primaryIpAddress']: raise exceptions.CLIAbort('No primary IP address associated with this VS') 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 will be true only if no options are passed in, basically. both = (not ptr) and (not a_record) and (not aaaa_record) if both or a_record: dns.sync_host_record(zone_id, instance['hostname'], instance['primaryIpAddress'], 'a', ttl) if both or ptr: # getReverseDomainRecords returns a list of 1 element, so just get the top. ptr_domains = env.client['Virtual_Guest'].getReverseDomainRecords(id=instance['id']).pop() dns.sync_ptr_record(ptr_domains, instance['primaryIpAddress'], instance['fullyQualifiedDomainName'], ttl) if aaaa_record: try: # done this way to stay within 80 character lines ipv6 = instance['primaryNetworkComponent']['primaryVersion6IpAddressRecord']['ipAddress'] dns.sync_host_record(zone_id, instance['hostname'], ipv6, 'aaaa', ttl) except KeyError as ex: message = "{} does not have an ipv6 address".format(instance['fullyQualifiedDomainName']) raise exceptions.CLIAbort(message) from ex softlayer-python-5.9.8/SoftLayer/CLI/virt/edit.py000066400000000000000000000041751415376457700217200ustar00rootroot00000000000000"""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', encoding="utf-8") 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.9.8/SoftLayer/CLI/virt/list.py000066400000000000000000000117151415376457700217440ustar00rootroot00000000000000"""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 from SoftLayer import utils # 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(short_help="List virtual servers.") @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') @click.option('--transient', help='Filter by transient instances', type=click.BOOL) @click.option('--hardware', is_flag=True, default=False, help='Show the all VSI related to hardware') @click.option('--all-guests', is_flag=True, default=False, help='Show the all VSI and hardware VSIs') @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) @click.option('--limit', '-l', help='How many results to get in one api call, default is 100', default=100, show_default=True) @environment.pass_env def cli(env, sortby, cpu, domain, datacenter, hostname, memory, network, hourly, monthly, tag, columns, limit, transient, hardware, all_guests): """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, transient=transient, tags=tag, mask=columns.mask(), limit=limit) table = formatting.Table(columns.columns) table.sortby = sortby if not hardware or all_guests: for guest in guests: table.add_row([value or formatting.blank() for value in columns.row(guest)]) env.fout(table) if hardware or all_guests: hardware_guests = vsi.get_hardware_guests() for hd_guest in hardware_guests: if hd_guest['virtualHost']['guests']: title = "Hardware(id = {hardwareId}) guests associated".format(hardwareId=hd_guest['id']) table_hardware_guest = formatting.Table(['id', 'hostname', 'CPU', 'Memory', 'Start Date', 'Status', 'powerState'], title=title) table_hardware_guest.sortby = 'hostname' for guest in hd_guest['virtualHost']['guests']: table_hardware_guest.add_row([ guest['id'], guest['hostname'], '%i %s' % (guest['maxCpu'], guest['maxCpuUnits']), guest['maxMemory'], utils.clean_time(guest['createDate']), guest['status']['keyName'], guest['powerState']['keyName'] ]) env.fout(table_hardware_guest) softlayer-python-5.9.8/SoftLayer/CLI/virt/migrate.py000066400000000000000000000071371415376457700224240ustar00rootroot00000000000000"""Manage Migrations of Virtual Guests""" # :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('--guest', '-g', type=click.INT, help="Guest ID to immediately migrate.") @click.option('--all', '-a', 'migrate_all', is_flag=True, default=False, help="Migrate ALL guests that require migration immediately.") @click.option('--host', '-h', type=click.INT, help="Dedicated Host ID to migrate to. Only works on guests that are already on a dedicated host.") @environment.pass_env def cli(env, guest, migrate_all, host): """Manage VSIs that require migration. Can migrate Dedicated Host VSIs as well.""" vsi = SoftLayer.VSManager(env.client) pending_filter = {'virtualGuests': {'pendingMigrationFlag': {'operation': 1}}} dedicated_filter = {'virtualGuests': {'dedicatedHost': {'id': {'operation': 'not null'}}}} mask = """mask[ id, hostname, domain, datacenter, pendingMigrationFlag, powerState, primaryIpAddress,primaryBackendIpAddress, dedicatedHost ]""" # No options, just print out a list of guests that can be migrated if not (guest or migrate_all): require_migration = vsi.list_instances(filter=pending_filter, mask=mask) require_table = formatting.Table(['id', 'hostname', 'domain', 'datacenter'], title="Require Migration") for vsi_object in require_migration: require_table.add_row([ vsi_object.get('id'), vsi_object.get('hostname'), vsi_object.get('domain'), utils.lookup(vsi_object, 'datacenter', 'name') ]) if require_migration: env.fout(require_table) else: click.secho("No guests require migration at this time", fg='green') migrateable = vsi.list_instances(filter=dedicated_filter, mask=mask) migrateable_table = formatting.Table(['id', 'hostname', 'domain', 'datacenter', 'Host Name', 'Host Id'], title="Dedicated Guests") for vsi_object in migrateable: migrateable_table.add_row([ vsi_object.get('id'), vsi_object.get('hostname'), vsi_object.get('domain'), utils.lookup(vsi_object, 'datacenter', 'name'), utils.lookup(vsi_object, 'dedicatedHost', 'name'), utils.lookup(vsi_object, 'dedicatedHost', 'id') ]) env.fout(migrateable_table) # Migrate all guests with pendingMigrationFlag=True elif migrate_all: require_migration = vsi.list_instances(filter=pending_filter, mask="mask[id]") if not require_migration: click.secho("No guests require migration at this time", fg='green') for vsi_object in require_migration: migrate(vsi, vsi_object['id']) # Just migrate based on the options else: migrate(vsi, guest, host) def migrate(vsi_manager, vsi_id, host_id=None): """Handles actually migrating virtual guests and handling the exception""" try: if host_id: vsi_manager.migrate_dedicated(vsi_id, host_id) else: vsi_manager.migrate(vsi_id) click.secho("Started a migration on {}".format(vsi_id), fg='green') except SoftLayer.exceptions.SoftLayerAPIError as ex: click.secho("Failed to migrate {}. {}".format(vsi_id, str(ex)), fg='red') softlayer-python-5.9.8/SoftLayer/CLI/virt/placementgroup/000077500000000000000000000000001415376457700234375ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/virt/placementgroup/__init__.py000066400000000000000000000025711415376457700255550ustar00rootroot00000000000000"""Manages Reserved Capacity.""" # :license: MIT, see LICENSE for more details. import importlib import os import click CONTEXT = {'help_option_names': ['-h', '--help'], 'max_content_width': 999} class PlacementGroupCommands(click.MultiCommand): """Loads module for placement group related commands. Currently the base command loader only supports going two commands deep. So this small loader is required for going that third level. """ def __init__(self, **attrs): click.MultiCommand.__init__(self, **attrs) self.path = os.path.dirname(__file__) def list_commands(self, ctx): """List all sub-commands.""" commands = [] for filename in os.listdir(self.path): if filename == '__init__.py': continue if filename.endswith('.py'): commands.append(filename[:-3].replace("_", "-")) commands.sort() return commands def get_command(self, ctx, cmd_name): """Get command for click.""" path = "%s.%s" % (__name__, cmd_name) path = path.replace("-", "_") module = importlib.import_module(path) return getattr(module, 'cli') # Required to get the sub-sub-sub command to work. @click.group(cls=PlacementGroupCommands, context_settings=CONTEXT) def cli(): """Base command for all capacity related concerns""" softlayer-python-5.9.8/SoftLayer/CLI/virt/placementgroup/create.py000066400000000000000000000025041415376457700252550ustar00rootroot00000000000000"""Create a placement group""" import click from SoftLayer.CLI import environment from SoftLayer.CLI import helpers from SoftLayer.managers.vs_placement import PlacementManager as PlacementManager @click.command() @click.option('--name', type=click.STRING, required=True, prompt=True, help="Name for this new placement group.") @click.option('--backend_router', '-b', required=True, prompt=True, help="backendRouter, can be either the hostname or id.") @click.option('--rule', '-r', required=True, prompt=True, help="The keyName or Id of the rule to govern this placement group.") @environment.pass_env def cli(env, **args): """Create a placement group.""" manager = PlacementManager(env.client) backend_router_id = helpers.resolve_id(manager.get_backend_router_id_from_hostname, args.get('backend_router'), 'backendRouter') rule_id = helpers.resolve_id(manager.get_rule_id_from_name, args.get('rule'), 'Rule') placement_object = { 'name': args.get('name'), 'backendRouterId': backend_router_id, 'ruleId': rule_id } result = manager.create(placement_object) click.secho("Successfully created placement group: ID: %s, Name: %s" % (result['id'], result['name']), fg='green') softlayer-python-5.9.8/SoftLayer/CLI/virt/placementgroup/create_options.py000066400000000000000000000022421415376457700270270ustar00rootroot00000000000000"""List options for creating Placement Groups""" # :license: MIT, see LICENSE for more details. import click from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.managers.vs_placement import PlacementManager as PlacementManager @click.command() @environment.pass_env def cli(env): """List options for creating a placement group.""" manager = PlacementManager(env.client) routers = manager.get_routers() env.fout(get_router_table(routers)) rules = manager.get_all_rules() env.fout(get_rule_table(rules)) def get_router_table(routers): """Formats output from _get_routers and returns a table. """ table = formatting.Table(['Datacenter', 'Hostname', 'Backend Router Id'], "Available Routers") for router in routers: datacenter = router['topLevelLocation']['longName'] table.add_row([datacenter, router['hostname'], router['id']]) return table def get_rule_table(rules): """Formats output from get_all_rules and returns a table. """ table = formatting.Table(['Id', 'KeyName'], "Rules") for rule in rules: table.add_row([rule['id'], rule['keyName']]) return table softlayer-python-5.9.8/SoftLayer/CLI/virt/placementgroup/delete.py000066400000000000000000000044061415376457700252570ustar00rootroot00000000000000"""Delete a placement group.""" import click from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting from SoftLayer.CLI import helpers from SoftLayer.managers.vs import VSManager as VSManager from SoftLayer.managers.vs_placement import PlacementManager as PlacementManager @click.command(epilog="Once provisioned, virtual guests can be managed with the slcli vs commands") @click.argument('identifier') @click.option('--purge', is_flag=True, help="Delete all guests in this placement group. " "The group itself can be deleted once all VMs are fully reclaimed") @environment.pass_env def cli(env, identifier, purge): """Delete a placement group. Placement Group MUST be empty before you can delete it. IDENTIFIER can be either the Name or Id of the placement group you want to view """ manager = PlacementManager(env.client) group_id = helpers.resolve_id(manager.resolve_ids, identifier, 'placement_group') if purge: placement_group = manager.get_object(group_id) guest_list = ', '.join([guest['fullyQualifiedDomainName'] for guest in placement_group['guests']]) if len(placement_group['guests']) < 1: raise exceptions.CLIAbort('No virtual servers were found in placement group %s' % identifier) click.secho("You are about to delete the following guests!\n%s" % guest_list, fg='red') if not (env.skip_confirmations or formatting.confirm("This action will cancel all guests! Continue?")): raise exceptions.CLIAbort('Aborting virtual server order.') vm_manager = VSManager(env.client) for guest in placement_group['guests']: click.secho("Deleting %s..." % guest['fullyQualifiedDomainName']) vm_manager.cancel_instance(guest['id']) return click.secho("You are about to delete the following placement group! %s" % identifier, fg='red') if not (env.skip_confirmations or formatting.confirm("This action will cancel the placement group! Continue?")): raise exceptions.CLIAbort('Aborting virtual server order.') cancel_result = manager.delete(group_id) if cancel_result: click.secho("Placement Group %s has been canceld." % identifier, fg='green') softlayer-python-5.9.8/SoftLayer/CLI/virt/placementgroup/detail.py000066400000000000000000000031431415376457700252540ustar00rootroot00000000000000"""View details of a placement group""" import click from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.CLI import helpers from SoftLayer.managers.vs_placement import PlacementManager as PlacementManager @click.command(epilog="Once provisioned, virtual guests can be managed with the slcli vs commands") @click.argument('identifier') @environment.pass_env def cli(env, identifier): """View details of a placement group. IDENTIFIER can be either the Name or Id of the placement group you want to view """ manager = PlacementManager(env.client) group_id = helpers.resolve_id(manager.resolve_ids, identifier, 'placement_group') result = manager.get_object(group_id) table = formatting.Table(["Id", "Name", "Backend Router", "Rule", "Created"]) table.add_row([ result['id'], result['name'], result['backendRouter']['hostname'], result['rule']['name'], result['createDate'] ]) guest_table = formatting.Table([ "Id", "FQDN", "Primary IP", "Backend IP", "CPU", "Memory", "Provisioned", "Transaction" ]) for guest in result['guests']: guest_table.add_row([ guest.get('id'), guest.get('fullyQualifiedDomainName'), guest.get('primaryIpAddress'), guest.get('primaryBackendIpAddress'), guest.get('maxCpu'), guest.get('maxMemory'), guest.get('provisionDate'), formatting.active_txn(guest) ]) env.fout(table) env.fout(guest_table) softlayer-python-5.9.8/SoftLayer/CLI/virt/placementgroup/list.py000066400000000000000000000015731415376457700247720ustar00rootroot00000000000000"""List Placement Groups""" import click from SoftLayer.CLI import environment from SoftLayer.CLI import formatting from SoftLayer.managers.vs_placement import PlacementManager as PlacementManager from SoftLayer import utils @click.command() @environment.pass_env def cli(env): """List placement groups.""" manager = PlacementManager(env.client) result = manager.list() table = formatting.Table( ["Id", "Name", "Backend Router", "Rule", "Guests", "Created"], title="Placement Groups" ) for group in result: table.add_row([ utils.lookup(group, 'id'), utils.lookup(group, 'name'), utils.lookup(group, 'backendRouter', 'hostname'), utils.lookup(group, 'rule', 'name'), utils.lookup(group, 'guestCount'), utils.lookup(group, 'createDate') ]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/virt/power.py000066400000000000000000000063661415376457700221330ustar00rootroot00000000000000"""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.VSManager(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.9.8/SoftLayer/CLI/virt/ready.py000066400000000000000000000013471415376457700220750ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/virt/reload.py000066400000000000000000000025451415376457700222400ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/virt/storage.py000066400000000000000000000065761415376457700224460ustar00rootroot00000000000000"""Get storage details for a virtual server.""" # :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): """Get storage details for a virtual server.""" vsi = SoftLayer.VSManager(env.client) vsi_id = helpers.resolve_id(vsi.resolve_ids, identifier, 'VS') iscsi_storage_data = vsi.get_storage_details(vsi_id, "ISCSI") nas_storage_data = vsi.get_storage_details(vsi_id, "NAS") storage_credentials = vsi.get_storage_credentials(vsi_id) portable_storage = vsi.get_portable_storage(vsi_id) local_disks = vsi.get_local_disks(vsi_id) table_credentials = formatting.Table(['Username', 'Password', 'IQN'], title="Block Storage Details \n iSCSI") if storage_credentials: table_credentials.add_row([storage_credentials['credential']['username'], storage_credentials['credential']['password'], storage_credentials['name']]) table_iscsi = formatting.Table(['LUN name', 'capacity', 'Target address', 'Location', 'Notes']) for iscsi in iscsi_storage_data: table_iscsi.add_row([iscsi['username'], iscsi['capacityGb'], iscsi['serviceResourceBackendIpAddress'], iscsi['allowedVirtualGuests'][0]['datacenter']['longName'], iscsi.get('notes', None)]) table_portable = formatting.Table(['Description', 'Capacity'], title="Portable Storage") for portable in portable_storage: table_portable.add_row([portable.get('description', None), portable.get('capacity', None)]) table_nas = formatting.Table(['Volume name', 'capacity', 'Host Name', 'Location', 'Notes'], title="File Storage Details") for nas in nas_storage_data: table_nas.add_row([nas['username'], nas['capacityGb'], nas['serviceResourceBackendIpAddress'], nas['allowedVirtualGuests'][0]['datacenter']['longName'], nas.get('notes', None)]) table_local_disks = get_local_storage_table(local_disks) table_local_disks.title = "Other storage details" env.fout(table_credentials) env.fout(table_iscsi) env.fout(table_portable) env.fout(table_nas) env.fout(table_local_disks) def get_local_type(disks): """Returns the virtual server local disk type. :param disks: virtual server local disks. """ disk_type = 'System' if 'SWAP' in disks.get('diskImage', {}).get('description', []): disk_type = 'Swap' return disk_type def get_local_storage_table(local_disks): """Returns a formatting local disk table :param local_disks: virtual server local disks. """ table_local_disks = formatting.Table(['Type', 'Name', 'Drive', 'Capacity']) for disk in local_disks: if 'diskImage' in disk: table_local_disks.add_row([ get_local_type(disk), disk['mountType'], disk['device'], "{capacity} {unit}".format(capacity=disk['diskImage']['capacity'], unit=disk['diskImage']['units']) ]) return table_local_disks softlayer-python-5.9.8/SoftLayer/CLI/virt/upgrade.py000066400000000000000000000051241415376457700224150ustar00rootroot00000000000000"""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") @click.option('--add-disk', type=click.INT, multiple=True, required=False, help="add Hard disk in GB") @click.option('--resize-disk', nargs=2, multiple=True, type=(int, int), help="Update disk number to size in GB. --resize-disk 250 2 ") @click.option('--flavor', type=click.STRING, help="Flavor keyName\nDo not use --memory, --cpu or --private, if you are using flavors") @environment.pass_env def cli(env, identifier, cpu, private, memory, network, flavor, add_disk, resize_disk): """Upgrade a virtual server.""" vsi = SoftLayer.VSManager(env.client) if not any([cpu, memory, network, flavor, resize_disk, add_disk]): raise exceptions.ArgumentError("Must provide [--cpu]," " [--memory], [--network], [--flavor], [--resize-disk], or [--add] 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') disk_json = [] if memory: memory = int(memory / 1024) if resize_disk: for guest_disk in resize_disk: disks = {'capacity': guest_disk[0], 'number': guest_disk[1]} disk_json.append(disks) elif add_disk: for guest_disk in add_disk: disks = {'capacity': guest_disk, 'number': -1} disk_json.append(disks) if not vsi.upgrade(vs_id, cpus=cpu, memory=memory, nic_speed=network, public=not private, preset=flavor, disk=disk_json): raise exceptions.CLIAbort('VS Upgrade Failed') softlayer-python-5.9.8/SoftLayer/CLI/virt/usage.py000066400000000000000000000041021415376457700220650ustar00rootroot00000000000000"""Usage information of 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.utils import clean_time @click.command() @click.argument('identifier') @click.option('--start_date', '-s', type=click.STRING, required=True, help="Start Date e.g. 2019-3-4 (yyyy-MM-dd)") @click.option('--end_date', '-e', type=click.STRING, required=True, help="End Date e.g. 2019-4-2 (yyyy-MM-dd)") @click.option('--valid_type', '-t', type=click.STRING, required=True, help="Metric_Data_Type keyName e.g. CPU0, CPU1, MEMORY_USAGE, etc.") @click.option('--summary_period', '-p', type=click.INT, default=3600, help="300, 600, 1800, 3600, 43200 or 86400 seconds") @environment.pass_env def cli(env, identifier, start_date, end_date, valid_type, summary_period): """Usage information of a virtual server.""" vsi = SoftLayer.VSManager(env.client) table = formatting.Table(['counter', 'dateTime', 'type']) table_average = formatting.Table(['Average']) vs_id = helpers.resolve_id(vsi.resolve_ids, identifier, 'VS') result = vsi.get_summary_data_usage(vs_id, start_date=start_date, end_date=end_date, valid_type=valid_type.upper(), summary_period=summary_period) if len(result) == 0: raise exceptions.CLIAbort('No metric data for this range of dates provided') count = 0 counter = 0.00 for data in result: if valid_type.upper() == "MEMORY_USAGE": usage_counter = data['counter'] / 2 ** 30 else: usage_counter = data['counter'] table.add_row([ round(usage_counter, 2), clean_time(data['dateTime']), data['type'], ]) counter = counter + usage_counter count = count + 1 average = counter / count env.fout(table_average.add_row([round(average, 2)])) env.fout(table_average) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/vlan/000077500000000000000000000000001415376457700203665ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/vlan/__init__.py000066400000000000000000000000251415376457700224740ustar00rootroot00000000000000"""Network VLANs.""" softlayer-python-5.9.8/SoftLayer/CLI/vlan/cancel.py000066400000000000000000000020711415376457700221650ustar00rootroot00000000000000"""Cancel Network Vlan.""" # :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): """Cancel network VLAN.""" mgr = SoftLayer.NetworkManager(env.client) if not (env.skip_confirmations or formatting.no_going_back(identifier)): raise exceptions.CLIAbort('Aborted') reasons = mgr.get_cancel_failure_reasons(identifier) if len(reasons) > 0: raise exceptions.CLIAbort(reasons) item = mgr.get_vlan(identifier).get('billingItem') if item: mgr.cancel_item(item.get('id'), True, 'Cancel by cli command', 'Cancel by cli command') else: raise exceptions.CLIAbort( "VLAN is an automatically assigned and free of charge VLAN," " it will automatically be removed from your account when it is empty") softlayer-python-5.9.8/SoftLayer/CLI/vlan/create.py000066400000000000000000000046751415376457700222170ustar00rootroot00000000000000"""Order/create a VLAN instance.""" # :license: MIT, see LICENSE for more details. import click import SoftLayer from SoftLayer.managers import ordering from SoftLayer.CLI import environment from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting @click.command() @click.option('--name', required=False, prompt=True, help="Vlan name") @click.option('--datacenter', '-d', required=False, help="Datacenter shortname") @click.option('--pod', '-p', required=False, help="Pod name. E.g dal05.pod01") @click.option('--network', default='public', show_default=True, type=click.Choice(['public', 'private']), help='Network vlan type') @click.option('--billing', default='hourly', show_default=True, type=click.Choice(['hourly', 'monthly']), help="Billing rate") @environment.pass_env def cli(env, name, datacenter, pod, network, billing): """Order/create a VLAN instance.""" item_package = ['PUBLIC_NETWORK_VLAN'] complex_type = 'SoftLayer_Container_Product_Order_Network_Vlan' extras = {'name': name} if pod: datacenter = pod.split('.')[0] mgr = SoftLayer.NetworkManager(env.client) pods = mgr.get_pods() for router in pods: if router.get('name') == pod: if network == 'public': extras['routerId'] = router.get('frontendRouterId') elif network == 'private': extras['routerId'] = router.get('backendRouterId') break if not extras.get('routerId'): raise exceptions.CLIAbort( "Unable to find pod name: {}".format(pod)) if network == 'private': item_package = ['PRIVATE_NETWORK_VLAN'] ordering_manager = ordering.OrderingManager(env.client) result = ordering_manager.place_order(package_keyname='NETWORK_VLAN', location=datacenter, item_keynames=item_package, complex_type=complex_type, hourly=billing, extras=extras) 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']]) table.add_row(['name', result['orderDetails']['orderContainers'][0]['name']]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/vlan/detail.py000066400000000000000000000066471415376457700222170ustar00rootroot00000000000000"""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 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 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.get('id')]) table.add_row(['number', vlan.get('vlanNumber')]) table.add_row(['datacenter', utils.lookup(vlan, 'primaryRouter', 'datacenter', 'longName')]) table.add_row(['primary_router', utils.lookup(vlan, 'primaryRouter', 'fullyQualifiedDomainName')]) table.add_row(['Gateway/Firewall', get_gateway_firewall(vlan)]) 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.get('id')]) subnet_table.add_row(['identifier', subnet.get('networkIdentifier')]) subnet_table.add_row(['netmask', subnet.get('netmask')]) subnet_table.add_row(['gateway', subnet.get('gateway', formatting.blank())]) subnet_table.add_row(['type', subnet.get('subnetType')]) subnet_table.add_row(['usable ips', subnet.get('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.get('hostname'), vsi.get('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.get('hostname'), hardware.get('domain'), hardware.get('primaryIpAddress'), hardware.get('primaryBackendIpAddress')]) table.add_row(['hardware', hw_table]) else: table.add_row(['hardware', 'none']) env.fout(table) def get_gateway_firewall(vlan): """Gets the name of a gateway/firewall from a VLAN. """ firewall = utils.lookup(vlan, 'networkVlanFirewall', 'fullyQualifiedDomainName') if firewall: return firewall gateway = utils.lookup(vlan, 'attachedNetworkGateway', 'name') if gateway: return gateway return formatting.blank() softlayer-python-5.9.8/SoftLayer/CLI/vlan/edit.py000066400000000000000000000020231415376457700216620ustar00rootroot00000000000000"""Edit a vlan's details.""" # :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.option('--name', '-n', help="The optional name for this VLAN") @click.option('--note', '-e', help="The note for this vlan.") @click.option('--tags', '-g', multiple=True, help='Tags to set e.g. "tag1,tag2", or empty string to remove all' ) @environment.pass_env def cli(env, identifier, name, note, tags): """Edit a vlan's details.""" new_tags = None if tags: new_tags = ','.join(tags) mgr = SoftLayer.NetworkManager(env.client) vlan_id = helpers.resolve_id(mgr.resolve_vlan_ids, identifier, 'VLAN') vlan = mgr.edit(vlan_id, name=name, note=note, tags=new_tags) if vlan: click.secho("Vlan edited successfully", fg='green') else: click.secho("Failed to edit the vlan", fg='red') softlayer-python-5.9.8/SoftLayer/CLI/vlan/list.py000066400000000000000000000034061415376457700217160ustar00rootroot00000000000000"""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.CLI.vlan.detail import get_gateway_firewall from SoftLayer import utils COLUMNS = ['id', 'number', 'name', 'Gateway/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') @click.option('--limit', '-l', help='How many results to get in one api call, default is 100', default=100, show_default=True) @environment.pass_env def cli(env, sortby, datacenter, number, name, limit): """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, limit=limit) for vlan in vlans: table.add_row([ vlan.get('id'), vlan.get('vlanNumber'), vlan.get('name') or formatting.blank(), get_gateway_firewall(vlan), utils.lookup(vlan, 'primaryRouter', 'datacenter', 'name'), vlan.get('hardwareCount'), vlan.get('virtualGuestCount'), vlan.get('totalPrimaryIpAddressCount'), ]) env.fout(table) softlayer-python-5.9.8/SoftLayer/CLI/vpn/000077500000000000000000000000001415376457700202315ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/vpn/__init__.py000066400000000000000000000000371415376457700223420ustar00rootroot00000000000000"""Virtual Private Networks""" softlayer-python-5.9.8/SoftLayer/CLI/vpn/ipsec/000077500000000000000000000000001415376457700213345ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/vpn/ipsec/__init__.py000066400000000000000000000000201415376457700234350ustar00rootroot00000000000000"""IPSEC VPN""" softlayer-python-5.9.8/SoftLayer/CLI/vpn/ipsec/configure.py000066400000000000000000000020351415376457700236670ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/vpn/ipsec/detail.py000066400000000000000000000175171415376457700231630ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/vpn/ipsec/list.py000066400000000000000000000020241415376457700226570ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/vpn/ipsec/subnet/000077500000000000000000000000001415376457700226345ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/vpn/ipsec/subnet/__init__.py000066400000000000000000000000301415376457700247360ustar00rootroot00000000000000"""IPSEC VPN Subnets""" softlayer-python-5.9.8/SoftLayer/CLI/vpn/ipsec/subnet/add.py000066400000000000000000000057721415376457700237510ustar00rootroot00000000000000"""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', '--subnet-type', '--type', required=True, type=click.Choice(['internal', 'remote', 'service']), help='Subnet type to add') @click.option('-n', '--network-identifier', '--network', 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.9.8/SoftLayer/CLI/vpn/ipsec/subnet/remove.py000066400000000000000000000032211415376457700245010ustar00rootroot00000000000000"""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', '--subnet-type', '--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.9.8/SoftLayer/CLI/vpn/ipsec/translation/000077500000000000000000000000001415376457700236725ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/CLI/vpn/ipsec/translation/__init__.py000066400000000000000000000000451415376457700260020ustar00rootroot00000000000000"""IPSEC VPN Address Translations""" softlayer-python-5.9.8/SoftLayer/CLI/vpn/ipsec/translation/add.py000066400000000000000000000026331415376457700250000ustar00rootroot00000000000000"""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) @click.option('-s', '--static-ip', required=True, help='Static IP address value') @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.9.8/SoftLayer/CLI/vpn/ipsec/translation/remove.py000066400000000000000000000020741415376457700255440ustar00rootroot00000000000000"""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.9.8/SoftLayer/CLI/vpn/ipsec/translation/update.py000066400000000000000000000030561415376457700255320ustar00rootroot00000000000000"""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') @click.option('-s', '--static-ip', default=None, help='Static IP address value') @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.9.8/SoftLayer/CLI/vpn/ipsec/update.py000066400000000000000000000071221415376457700231720ustar00rootroot00000000000000"""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') @click.option('--remote-peer', default=None, help='Remote peer IP address value') @click.option('--preshared-key', default=None, help='Preshared key value') @click.option('--phase1-auth', '--p1-auth', default=None, type=click.Choice(['MD5', 'SHA1', 'SHA256']), help='Phase 1 authentication value') @click.option('--phase1-crypto', '--p1-crypto', default=None, type=click.Choice(['DES', '3DES', 'AES128', 'AES192', 'AES256']), help='Phase 1 encryption value') @click.option('--phase1-dh', '--p1-dh', default=None, type=click.Choice(['0', '1', '2', '5']), help='Phase 1 diffie hellman group value') @click.option('--phase1-key-ttl', '--p1-key-ttl', default=None, type=click.IntRange(120, 172800), help='Phase 1 key life value') @click.option('--phase2-auth', '--p2-auth', default=None, type=click.Choice(['MD5', 'SHA1', 'SHA256']), help='Phase 2 authentication value') @click.option('--phase2-crypto', '--p2-crypto', default=None, type=click.Choice(['DES', '3DES', 'AES128', 'AES192', 'AES256']), help='Phase 2 encryption value') @click.option('--phase2-dh', '--p2-dh', default=None, type=click.Choice(['0', '1', '2', '5']), help='Phase 2 diffie hellman group value') @click.option('--phase2-forward-secrecy', '--p2-forward-secrecy', default=None, type=click.IntRange(0, 1), help='Phase 2 perfect forward secrecy value') @click.option('--phase2-key-ttl', '--p2-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.9.8/SoftLayer/__init__.py000066400000000000000000000023431415376457700211320ustar00rootroot00000000000000""" 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=r0401,invalid-name,wildcard-import # NOQA appears to no longer be working. The code might have been upgraded. 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__ = [ # noqa: F405 'BaseClient', 'IAMClient', 'create_client_from_env', 'Client', 'BasicAuthentication', 'SoftLayerError', 'SoftLayerAPIError', 'SoftLayerListResult', 'API_PUBLIC_ENDPOINT', 'API_PRIVATE_ENDPOINT', ] softlayer-python-5.9.8/SoftLayer/auth.py000066400000000000000000000100041415376457700203250ustar00rootroot00000000000000""" 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.""" # See https://cloud.ibm.com/docs/iam?topic=iam-iamapikeysforservices for why this is the way it is if self.username == 'apikey': request.transport_user = self.username request.transport_password = self.api_key else: 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 class BearerAuthentication(AuthenticationBase): """Bearer Token authentication class. :param username str: a user's username, not really needed but all the others use it. :param api_key str: a user's IAM Token """ def __init__(self, username, token, r_token=None): """For using IBM IAM authentication :param username str: Not really needed, will be set to their current username though for logging :param token str: the IAM Token :param r_token str: The refresh Token, optional """ self.username = username self.api_key = token self.r_token = r_token def get_request(self, request): """Sets token-based auth headers.""" request.transport_headers['Authorization'] = 'Bearer {}'.format(self.api_key) request.transport_user = self.username return request def __repr__(self): return "BearerAuthentication(username={}, token={})".format(self.username, self.api_key) softlayer-python-5.9.8/SoftLayer/config.py000066400000000000000000000073601415376457700206440ustar00rootroot00000000000000""" SoftLayer.config ~~~~~~~~~~~~~~~~ Handles different methods for loading configuration for the API bindings :license: MIT, see LICENSE for more details. """ import configparser import logging import os import os.path LOGGER = logging.getLogger(__name__) 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 = 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 def get_config(config_file=None): """Returns a parsed config object""" if config_file is None: config_file = '~/.softlayer' config = configparser.ConfigParser() config.read(os.path.expanduser(config_file)) # No configuration file found. if not config.has_section('softlayer'): config.add_section('softlayer') config['softlayer']['username'] = '' config['softlayer']['endpoint_url'] = '' config['softlayer']['api_key'] = '' config['softlayer']['timeout'] = '0' return config def write_config(configuration, config_file=None): """Writes a configuration to config_file""" if config_file is None: config_file = '~/.softlayer' config_file = os.path.expanduser(config_file) with open(config_file, 'w', encoding="utf-8") as file: configuration.write(file) softlayer-python-5.9.8/SoftLayer/consts.py000066400000000000000000000010101415376457700206720ustar00rootroot00000000000000""" SoftLayer.consts ~~~~~~~~~~~~~~~~ Contains constants used throughout the library :license: MIT, see LICENSE for more details. """ VERSION = 'v5.9.8' 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 CONFIG_FILE = "~/.softlayer" softlayer-python-5.9.8/SoftLayer/decoration.py000066400000000000000000000035221415376457700215220ustar00rootroot00000000000000""" 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, ) 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.9.8/SoftLayer/exceptions.py000066400000000000000000000044051415376457700215550ustar00rootroot00000000000000""" 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 IAMError(SoftLayerError): """Errors from iam.cloud.ibm.com""" def __init__(self, fault_code, fault_string, url=None): SoftLayerError.__init__(self, fault_string) self.faultCode = fault_code self.faultString = fault_string self.url = url def __repr__(self): return "{} ({}): {}".format(self.url, self.faultCode, self.faultString) def __str__(self): return "{} ({}): {}".format(self.url, self.faultCode, self.faultString) 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.""" class UnsupportedEncoding(ParseError): """Encoding not supported.""" class InvalidCharacter(ParseError): """There was an invalid character.""" class SpecViolation(ServerError): """There was a spec violation.""" class MethodNotFound(SoftLayerAPIError): """Method name not found.""" class InvalidMethodParameters(SoftLayerAPIError): """Invalid method paramters.""" class InternalError(ServerError): """Internal Server Error.""" softlayer-python-5.9.8/SoftLayer/fixtures/000077500000000000000000000000001415376457700206705ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/fixtures/BluePages_Search.py000066400000000000000000000000351415376457700243740ustar00rootroot00000000000000findBluePagesProfile = True softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Account.py000066400000000000000000001026331415376457700246330ustar00rootroot00000000000000getPrivateBlockDeviceTemplateGroups = [{ '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': '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 } }, "virtualHost": { "accountId": 11111, "id": 22222, "name": "vmware.chechu.com", "guests": [ { "accountId": 11111, "createDate": "2019-09-05T17:03:42-06:00", "hostname": "NSX-T Manager", "id": 33333, "maxCpu": 16, "maxCpuUnits": "CORE", "maxMemory": 49152, "powerState": { "keyName": "RUNNING", "name": "Running" }, "status": { "keyName": "ACTIVE", "name": "Active" } }]} }, { '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 }, ], "virtualHost": { "accountId": 11111, "id": 22222, "name": "host14.vmware.chechu.com", "guests": [] } }, { '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 }, ], "virtualHost": { "accountId": 11111, "id": 22222, "name": "host14.vmware.chechu.com", "guests": [] } }, { 'id': 1003, "virtualHost": { "accountId": 11111, "id": 22222, "name": "host14.vmware.chechu.com", "guests": [] } }] 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', 'cidr': '/24', 'networkVlanId': 123, 'datacenter': {'name': 'dal00'}, 'version': 4, 'subnetType': 'PRIMARY', 'ipAddressCount': 10, 'virtualGuests': [], 'hardware': [], "podName": "dal05.pod04", "networkVlan": { "accountId": 123, "id": 2581232, "modifyDate": "2019-07-17T01:09:51+08:00", "vlanNumber": 795 } }, { "gateway": "5.111.11.111", "id": '111', "modifyDate": "2018-07-24T17:14:57+08:00", 'networkIdentifier': '10.0.0.1', 'ipAddressCount': 10, 'cidr': '/24', 'virtualGuests': [], 'hardware': [], "networkVlanId": 22222, "sortOrder": "2", "subnetType": "SECONDARY_ON_VLAN", "totalIpAddresses": "8", "usableIpAddressCount": "5", "version": 4 } ] 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, 'validityDays': 0, }] getExpiredSecurityCertificates = getSecurityCertificates getValidSecurityCertificates = getSecurityCertificates getTickets = [ { "accountId": 1234, "serviceProviderResourceId": "CS123456", "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, "serviceProviderResourceId": "CS123456", "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, "serviceProviderResourceId": "CS123456", "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, 'username': 'testAccount', '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': [], 'vlanNumber': 12, 'networkComponents': [], 'primaryRouter': { 'datacenter': {'name': 'dal00'} }, 'virtualGuestCount': 0, 'virtualGuests': [], 'dedicatedFirewallFlag': True, 'highAvailabilityFirewallFlag': True, 'networkVlanFirewall': {'id': 1234}, 'totalPrimaryIpAddressCount': 1, 'subnetCount': 0, 'subnets': [], 'firewallInterfaces': [ { 'id': 1, 'name': 'outside' }, { 'id': 12, 'name': 'inside' } ] }, { 'id': 2, 'networkSpace': 'PRIVATE', 'totalPrimaryIpAddressCount': 2, 'dedicatedFirewallFlag': False, 'highAvailabilityFirewallFlag': True, 'networkVlanFirewall': {'id': 7896}, 'hardwareCount': 0, 'hardware': [], 'vlanNumber': 13, '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'}], 'firewallInterfaces': [], 'subnetCount': 0, 'subnets': [], }, { 'id': 3, 'networkSpace': 'PRIVATE', 'name': 'dal00', 'hardwareCount': 1, 'dedicatedFirewallFlag': True, 'highAvailabilityFirewallFlag': True, 'networkVlanFirewall': {'id': 23456}, 'vlanNumber': 14, 'hardware': [{'id': 1}], 'networkComponents': [{'id': 2}], 'primaryRouter': { 'datacenter': {'name': 'dal00'} }, 'firewallInterfaces': [ { 'id': 31, 'name': 'outside' }], '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 = [{ 'accountId': 1234, 'id': 1234, 'name': 'TestQuote1234', 'quoteKey': '1234test4321', 'createDate': '2019-04-10T14:26:03-06:00', 'modifyDate': '2019-04-10T14:26:03-06:00', 'order': { 'id': 37623333, 'items': [ { 'categoryCode': 'guest_core', 'description': '4 x 2.0 GHz or higher Cores', 'id': 468394713, 'itemId': 859, 'itemPriceId': '1642', 'oneTimeAfterTaxAmount': '0', 'oneTimeFee': '0', 'oneTimeFeeTaxRate': '0', 'oneTimeTaxAmount': '0', 'quantity': 1, 'recurringAfterTaxAmount': '0', 'recurringFee': '0', 'recurringTaxAmount': '0', 'setupAfterTaxAmount': '0', 'setupFee': '0', 'setupFeeDeferralMonths': None, 'setupFeeTaxRate': '0', 'setupTaxAmount': '0', 'package': {'id': 46, 'keyName': 'CLOUD_SERVER'} }, ] } }] 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', 'serviceResource': {'name': 'Cleversafe - US Region'}}, {'id': 12346, 'username': 'SLOS12345-2', 'vendorName': 'Swift'}] 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': 'test-dedicated', 'diskCapacity': 1200, 'guestCount': 1, 'cpuCount': 56, 'id': 12345 }] getUsers = [ {'displayName': 'ChristopherG', 'hardwareCount': 138, 'id': 11100, 'userStatus': {'name': 'Active'}, 'username': 'SL1234', 'virtualGuestCount': 99, 'externalBindingCount': 1, 'apiAuthenticationKeyCount': 1, }, {'displayName': 'PulseL', 'hardwareCount': 100, 'id': 11111, 'userStatus': {'name': 'Active'}, 'username': 'sl1234-abob', 'virtualGuestCount': 99, } ] getReservedCapacityGroups = [ { 'accountId': 1234, 'backendRouterId': 1411193, 'createDate': '2018-09-24T16:33:09-06:00', 'id': 3103, 'modifyDate': '', 'name': 'test-capacity', 'availableInstanceCount': 1, 'instanceCount': 3, 'occupiedInstanceCount': 1, 'backendRouter': { 'accountId': 1, 'bareMetalInstanceFlag': 0, 'domain': 'softlayer.com', 'fullyQualifiedDomainName': 'bcr02a.dal13.softlayer.com', 'hardwareStatusId': 5, 'hostname': 'bcr02a.dal13', 'id': 1411193, 'notes': '', 'provisionDate': '', 'serviceProviderId': 1, 'serviceProviderResourceId': '', 'primaryIpAddress': '10.0.144.28', 'datacenter': { 'id': 1854895, 'longName': 'Dallas 13', 'name': 'dal13', 'statusId': 2 }, 'hardwareFunction': { 'code': 'ROUTER', 'description': 'Router', 'id': 1 }, 'topLevelLocation': { 'id': 1854895, 'longName': 'Dallas 13', 'name': 'dal13', 'statusId': 2 } }, 'instances': [ { 'id': 3501, 'billingItem': { 'description': 'B1.1x2 (1 Year Term)', 'hourlyRecurringFee': '.032' } }, { 'id': 3519, 'billingItem': { 'description': 'B1.1x2 (1 Year Term)', 'hourlyRecurringFee': '.032' } }, { 'id': 3519 } ] } ] getPlacementGroups = [{ "createDate": "2019-01-18T16:08:44-06:00", "id": 12345, "name": "test01", "guestCount": 0, "backendRouter": { "hostname": "bcr01a.mex01", "id": 329266 }, "rule": { "id": 1, "keyName": "SPREAD", "name": "SPREAD" } }] getInvoices = [ { 'id': 33816665, 'modifyDate': '2019-03-04T00:17:42-06:00', 'createDate': '2019-03-04T00:17:42-06:00', 'startingBalance': '129251.73', 'statusCode': 'OPEN', 'typeCode': 'RECURRING', 'itemCount': 3317, 'invoiceTotalAmount': '6230.66' }, { 'id': 12345667, 'modifyDate': '2019-03-05T00:17:42-06:00', 'createDate': '2019-03-04T00:17:42-06:00', 'startingBalance': '129251.73', 'statusCode': 'OPEN', 'typeCode': 'RECURRING', 'itemCount': 12, 'invoiceTotalAmount': '6230.66', 'endingBalance': '12345.55' } ] getApplicationDeliveryControllers = [ { 'accountId': 307608, 'createDate': '2015-05-05T16:23:52-06:00', 'id': 11449, 'modifyDate': '2015-05-05T16:24:09-06:00', 'name': 'SLADC307608-1', 'typeId': 2, 'description': 'Citrix NetScaler VPX 10.5 10Mbps Standard', 'managementIpAddress': '10.11.11.112', 'outboundPublicBandwidthUsage': '.00365', 'primaryIpAddress': '19.4.24.16', 'datacenter': { 'longName': 'Dallas 9', 'name': 'dal09', }, 'password': { 'password': 'aaaaa', 'username': 'root' }, 'type': { 'keyName': 'NETSCALER_VPX', 'name': 'NetScaler VPX' } } ] getScaleGroups = [ { "accountId": 31111, "cooldown": 1800, "createDate": "2016-10-25T01:48:34+08:00", "id": 12222222, "lastActionDate": "2016-10-25T01:48:34+08:00", "maximumMemberCount": 5, "minimumMemberCount": 0, "name": "tests", "regionalGroupId": 663, "virtualGuestMemberTemplate": { "accountId": 31111, "domain": "sodg.com", "hostname": "testing", "id": None, "maxCpu": None, "maxMemory": 32768, "startCpus": 32, "blockDevices": [ { "device": "0", "diskImage": { "capacity": 25, } } ], "datacenter": { "name": "sao01", }, "hourlyBillingFlag": True, "operatingSystemReferenceCode": "CENTOS_LATEST", }, "virtualGuestMemberCount": 0, "status": { "id": 1, "keyName": "ACTIVE", "name": "Active" }, }, { "accountId": 31111, "cooldown": 1800, "createDate": "2018-04-24T04:22:00+08:00", "id": 224533333, "lastActionDate": "2019-01-19T04:53:18+08:00", "maximumMemberCount": 10, "minimumMemberCount": 0, "modifyDate": "2019-01-19T04:53:21+08:00", "name": "test-ajcb", "regionalGroupId": 1025, "virtualGuestMemberTemplate": { "accountId": 31111, "domain": "test.local", "hostname": "autoscale-ajcb01", "id": None, "maxCpu": None, "maxMemory": 1024, "postInstallScriptUri": "http://test.com", "startCpus": 1, "blockDevices": [ { "device": "0", "diskImage": { "capacity": 25, } } ], "datacenter": { "name": "seo01", }, "hourlyBillingFlag": True, "operatingSystemReferenceCode": "CENTOS_7_64", }, "virtualGuestMemberCount": 0, "status": { "id": 1, "keyName": "ACTIVE", "name": "Active" } } ] getPortableStorageVolumes = [ { "capacity": 200, "createDate": "2018-10-06T04:27:59-06:00", "description": "Disk 2", "id": 11111, "modifyDate": "", "name": "Disk 2", "parentId": "", "storageRepositoryId": 22222, "typeId": 241, "units": "GB", "uuid": "fd477feb-bf32-408e-882f-02540gghgh111" } ] getAllTopLevelBillingItems = [ { "allowCancellationFlag": 1, "cancellationDate": "None", "categoryCode": "server", "createDate": "2015-05-28T09:53:41-06:00", "cycleStartDate": "2020-04-03T23:12:04-06:00", "description": "Dual E5-2690 v3 (12 Cores, 2.60 GHz)", "domainName": "sl-netbase.com", "hostName": "testsangeles101", "id": 53891943, "lastBillDate": "2020-04-03T23:12:04-06:00", "modifyDate": "2020-04-03T23:12:07-06:00", "nextBillDate": "2020-05-03T23:00:00-06:00", "orderItemId": 68626055, "parentId": "None", "recurringFee": "1000", "recurringFeeTaxRate": "0", "recurringMonths": 1, "hourlyFlag": False, "location": { "id": 265592, "longName": "Amsterdam 1", "name": "ams01", "statusId": 2 }, "nextInvoiceTotalRecurringAmount": 0, "orderItem": { "id": 68626055, "order": { "id": 4544893, "userRecord": { "displayName": "TEst", "email": "test@us.ibm.com", "id": 167758, "userStatus": { "id": 1001, "keyName": "CANCEL_PENDING", "name": "Cancel Pending" } } } }, "resourceTableId": 544444 }, { "allowCancellationFlag": 1, "cancellationDate": "None", "categoryCode": "server", "createDate": "2015-05-28T09:56:44-06:00", "cycleStartDate": "2020-04-03T23:12:05-06:00", "description": "Dual E5-2690 v3 (12 Cores, 2.60 GHz)", "domainName": "sl-netbase.com", "hostName": "testsangeles101", "id": 53892197, "lastBillDate": "2020-04-03T23:12:05-06:00", "modifyDate": "2020-04-03T23:12:07-06:00", "nextBillDate": "2020-05-03T23:00:00-06:00", "orderItemId": 68626801, "recurringFee": "22220", "hourlyFlag": False, "location": { "id": 265592, "longName": "Amsterdam 1", "name": "ams01", "statusId": 2 }, "nextInvoiceTotalRecurringAmount": 0, "orderItem": { "id": 68626801, "order": { "id": 4545911, "userRecord": { "displayName": "Test", "email": "test@us.ibm.com", "id": 167758, "userStatus": { "id": 1001, "keyName": "ACTIVE", "name": "Active" } } } }, "resourceTableId": 777777 } ] getNetworkStorage = [ { "accountId": 1111111, "capacityGb": 20, "createDate": "2016-01-21T12:11:07-06:00", "id": 1234567, "nasType": "ISCSI", "username": "SL01SEL301234-11", }, { "accountId": 1111111, "capacityGb": 20, "createDate": "2015-04-29T07:55:55-06:00", "id": 4917123, "nasType": "NAS", "username": "SL01SEV1234567_111" } ] getRouters = [ { "accountId": 1, "bareMetalInstanceFlag": 0, "domain": "softlayer.com", "fullyQualifiedDomainName": "fcr01a.ams01.softlayer.com", "hardwareStatusId": 5, "hostname": "fcr01a.ams01", "id": 123456, "serviceProviderId": 1, "topLevelLocation": { "id": 265592, "longName": "Amsterdam 1", "name": "ams01", "statusId": 2 } }] getNetworkStorage = [ { "accountId": 1111111, "capacityGb": 20, "createDate": "2016-01-21T12:11:07-06:00", "id": 1234567, "nasType": "ISCSI", "username": "SL01SEL301234-11", }, { "accountId": 1111111, "capacityGb": 20, "createDate": "2015-04-29T07:55:55-06:00", "id": 4917123, "nasType": "NAS", "username": "SL01SEV1234567_111" } ] getNetworkMessageDeliveryAccounts = [ { "accountId": 147258, "createDate": "2020-07-06T10:29:11-06:00", "id": 1232123, "typeId": 21, "username": "test_CLI@ie.ibm.com", "vendorId": 1, "type": { "description": "Delivery of messages through e-mail", "id": 21, "keyName": "EMAIL", "name": "Email" }, "vendor": { "id": 1, "keyName": "SENDGRID", "name": "SendGrid" }, "emailAddress": "test_CLI@ie.ibm.com", "smtpAccess": "1" } ] getActiveAccountLicenses = [{ "accountId": 123456, "capacity": "4", "key": "Y8GNS-7QRNG-OUIJO-MATEI-5GJRM", "units": "CPU", "billingItem": { "allowCancellationFlag": 1, "categoryCode": "software_license", "cycleStartDate": "2021-06-03T23:11:22-06:00", "description": "vCenter Server Appliance 6.0", "id": 741258963, "laborFee": "0", "laborFeeTaxRate": "0", "oneTimeFee": "0", "oneTimeFeeTaxRate": "0", "orderItemId": 963258741, "recurringFee": "0", "recurringFeeTaxRate": "0", "recurringMonths": 1, "serviceProviderId": 1, "setupFee": "0", "setupFeeTaxRate": "0" }, "softwareDescription": { "controlPanel": 0, "id": 15963, "licenseTermValue": 0, "longDescription": "VMware vCenter 6.0", "manufacturer": "VMware", "name": "vCenter", "operatingSystem": 0, "version": "6.0", "virtualLicense": 0, "virtualizationPlatform": 0, "requiredUser": "administrator@vsphere.local" } }, { "accountId": 123456, "capacity": "4", "key": "TSZES-SJF85-04GLD-AXA64-8O1EO", "units": "CPU", "billingItem": { "allowCancellationFlag": 1, "categoryCode": "software_license", "description": "vCenter Server Appliance 6.x", "id": 36987456, "laborFee": "0", "laborFeeTaxRate": "0", "oneTimeFee": "0", "oneTimeFeeTaxRate": "0", "orderItemId": 25839, "recurringFee": "0", "recurringFeeTaxRate": "0", "recurringMonths": 1, "serviceProviderId": 1, "setupFee": "0", "setupFeeTaxRate": "0" }, "softwareDescription": { "controlPanel": 0, "id": 1472, "licenseTermValue": 0, "longDescription": "VMware vCenter 6.0", "manufacturer": "VMware", "name": "vCenter", "operatingSystem": 0, "version": "6.0", "virtualLicense": 0, "virtualizationPlatform": 0, "requiredUser": "administrator@vsphere.local" } } ] getActiveVirtualLicenses = [{ "id": 12345, "ipAddress": "192.168.23.78", "key": "TEST.60220734.0000", "billingItem": { "categoryCode": "control_panel", "description": "Plesk Onyx (Linux) - (Unlimited) - VPS " }, "softwareDescription": { "longDescription": "Plesk - Unlimited Domain w/ Power Pack for VPS 17.8.11 Linux", "manufacturer": "Plesk", "name": "Plesk - Unlimited Domain w/ Power Pack for VPS" }, "subnet": { "broadcastAddress": "192.168.23.79", "cidr": 28, "gateway": "192.168.23.65", "id": 1973163, "isCustomerOwned": False, "isCustomerRoutable": False, "netmask": "255.255.255.240", "networkIdentifier": "128.116.23.64", "networkVlanId": 123456, "note": "test note", "sortOrder": "1", "subnetType": "ADDITIONAL_PRIMARY", "totalIpAddresses": "16", "usableIpAddressCount": "13", "version": 4 } }] softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Billing_Invoice.py000066400000000000000000000014011415376457700262620ustar00rootroot00000000000000getInvoiceTopLevelItems = [ { 'categoryCode': 'sov_sec_ip_addresses_priv', 'createDate': '2018-04-04T23:15:20-06:00', 'description': '64 Portable Private IP Addresses', 'id': 724951323, 'oneTimeAfterTaxAmount': '0', 'recurringAfterTaxAmount': '0', 'hostName': 'bleg', 'domainName': 'beh.com', 'category': {'name': 'Private (only) Secondary VLAN IP Addresses'}, 'children': [ { 'id': 12345, 'category': {'name': 'Fake Child Category'}, 'description': 'Blah', 'oneTimeAfterTaxAmount': 55.50, 'recurringAfterTaxAmount': 0.10 } ], 'location': {'name': 'fra02'} } ] softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Billing_Invoice_Item.py000066400000000000000000000001551415376457700272450ustar00rootroot00000000000000from SoftLayer.fixtures.SoftLayer_Billing_Item import getObject as billingItem getBillingItem = billingItem softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Billing_Item.py000066400000000000000000000040771415376457700256000ustar00rootroot00000000000000cancelService = True cancelServiceOnAnniversaryDate = True cancelItem = True getObject = { "allowCancellationFlag": 1, "cancellationDate": "None", "categoryCode": "server", "createDate": "2015-05-28T10:36:38-06:00", "cycleStartDate": "2020-04-03T23:12:05-06:00", "description": "Dual E5-2690 v3 (12 Cores, 2.60 GHz)", "domainName": "sl-test.com", "hostName": "testsangeles101", "id": 53897671, "lastBillDate": "2020-04-03T23:12:05-06:00", "modifyDate": "2020-04-03T23:12:07-06:00", "nextBillDate": "2020-05-03T23:00:00-06:00", "orderItemId": 68634907, "parentId": "None", "recurringFee": "1000", "recurringMonths": 1, "children": [ { "allowCancellationFlag": 1, "associatedBillingItemId": "53897671", "cancellationDate": "None", "categoryCode": "second_processor", "createDate": "2015-05-28T10:36:38-06:00", "cycleStartDate": "2020-04-03T23:12:05-06:00", "description": "E5-2690 v3 (12 Cores, 2.60 GHz)", "id": 53897673, "lastBillDate": "2020-04-03T23:12:05-06:00", "modifyDate": "2020-04-03T23:12:07-06:00", "nextBillDate": "2020-05-03T23:00:00-06:00", "oneTimeFee": "0", "oneTimeFeeTaxRate": "0", "orderItemId": 68634909, "parentId": 53897671, "recurringFee": "1000", "setupFeeTaxRate": "0" }, ], "hourlyFlag": False, "location": { "id": 265592, "longName": "Amsterdam 1", "name": "ams01", "statusId": 2 }, "nextInvoiceTotalRecurringAmount": 0, "orderItem": { "id": 68634907, "order": { "id": 4546175, "userRecord": { "displayName": "Tester", "email": "test@us.ibm.com", "id": 167758, "userStatus": { "keyName": "ACTIVE", "name": "Active" } } } }, "resourceTableId": "None" } softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Billing_Order.py000066400000000000000000000036301415376457700257470ustar00rootroot00000000000000getAllObjects = [{ 'accountId': 123456, 'createDate': '2020-09-15T13:12:08-06:00', 'id': 112356450, 'modifyDate': '2020-09-15T13:13:13-06:00', 'status': 'COMPLETED', 'userRecordId': 987456321, 'userRecord': { 'username': 'test@test.com' }, 'items': [ { 'categoryCode': 'port_speed', 'description': '100 Mbps Private Network Uplink' }, { 'categoryCode': 'service_port', 'description': '100 Mbps Private Uplink' }, { 'categoryCode': 'public_port', 'description': '0 Mbps Public Uplink' } ], 'orderApprovalDate': '2020-09-15T13:13:13-06:00', 'orderTotalAmount': '0' }, { 'accountId': 123456, 'createDate': '2019-09-15T13:12:08-06:00', 'id': 645698550, 'modifyDate': '2019-09-15T13:13:13-06:00', 'status': 'COMPLETED', 'userRecordId': 987456321, 'userRecord': { 'username': 'test@test.com' }, 'items': [ { 'categoryCode': 'port_speed', 'description': '100 Mbps Private Network Uplink' }, ], 'orderApprovalDate': '2019-09-15T13:13:13-06:00', 'orderTotalAmount': '0' }] getObject = { 'accountId': 1234, 'createDate': '2020-09-23T16:22:30-06:00', 'id': 6543210, 'impersonatingUserRecordId': None, 'initialInvoice': { 'amount': '0', 'id': 60012345, 'invoiceTotalAmount': '0' }, 'items': [{ 'description': 'Dual Intel Xeon Silver 4210 (20 Cores, 2.20 GHz)' }], 'modifyDate': '2020-09-23T16:22:32-06:00', 'orderQuoteId': None, 'orderTypeId': 11, 'presaleEventId': None, 'privateCloudOrderFlag': False, 'status': 'APPROVED', 'userRecord': { 'displayName': 'testUser' }, 'userRecordId': 7654321, } softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Billing_Order_Quote.py000066400000000000000000000055551415376457700271340ustar00rootroot00000000000000getObject = { 'accountId': 1234, 'id': 1234, 'name': 'TestQuote1234', 'quoteKey': '1234test4321', 'order': { 'id': 37623333, 'items': [ { 'categoryCode': 'guest_core', 'description': '4 x 2.0 GHz or higher Cores', 'id': 468394713, 'itemId': 859, 'itemPriceId': '1642', 'oneTimeAfterTaxAmount': '0', 'oneTimeFee': '0', 'oneTimeFeeTaxRate': '0', 'oneTimeTaxAmount': '0', 'quantity': 1, 'recurringAfterTaxAmount': '0', 'recurringFee': '0', 'recurringTaxAmount': '0', 'setupAfterTaxAmount': '0', 'setupFee': '0', 'setupFeeDeferralMonths': None, 'setupFeeTaxRate': '0', 'setupTaxAmount': '0', 'package': {'id': 46, 'keyName': 'CLOUD_SERVER'} }, ] } } getRecalculatedOrderContainer = { 'presetId': '', 'prices': [{ 'id': 1921 }], 'quantity': 1, 'packageId': 50, 'useHourlyPricing': '', 'reservedCapacityId': '', } verifyOrder = { 'orderId': 1234, 'orderDate': '2013-08-01 15:23:45', 'useHourlyPricing': False, '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', 'keyName': 'TheThing'}, }]} placeOrder = { 'orderId': 1234, 'orderDate': '2013-08-01 15:23:45', 'orderDetails': { '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'}, }], 'virtualGuests': [{ 'id': 1234567, 'globalIdentifier': '1a2b3c-1701', 'fullyQualifiedDomainName': 'test.guest.com' }], }, 'placedOrder': { 'id': 37985543, 'orderQuoteId': 2639077, 'orderTypeId': 4, 'status': 'PENDING_AUTO_APPROVAL', 'items': [ { 'categoryCode': 'guest_core', 'description': '4 x 2.0 GHz or higher Cores', 'id': 472527133, 'itemId': 859, 'itemPriceId': '1642', 'laborFee': '0', 'oneTimeFee': '0', 'recurringFee': '0', 'setupFee': '0', } ] } } saveQuote = getObject softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Dns_Domain.py000066400000000000000000000011361415376457700252460ustar00rootroot00000000000000createObject = {'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.9.8/SoftLayer/fixtures/SoftLayer_Dns_Domain_ResourceRecord.py000066400000000000000000000002371415376457700302550ustar00rootroot00000000000000createObject = {'name': 'example.com'} deleteObject = True editObject = True getObject = {'id': 12345, 'ttl': 7200, 'data': 'd', 'host': 'a', 'type': 'cname'} softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Email_Subscription.py000066400000000000000000000007571415376457700270360ustar00rootroot00000000000000getAllObjects = [ {'description': 'Email about your order.', 'enabled': True, 'id': 1, 'name': 'Order Being Reviewed' }, {'description': 'Maintenances that will or are likely to cause service ' 'outages and disruptions', 'enabled': True, 'id': 8, 'name': 'High Impact' }, {'description': 'Testing description.', 'enabled': True, 'id': 111, 'name': 'Test notification' } ] enable = True disable = True softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Event_Log.py000066400000000000000000000122211415376457700251120ustar00rootroot00000000000000getAllObjects = [ { 'accountId': 100, 'eventCreateDate': '2017-10-23T14:22:36.221541-05:00', 'eventName': 'Disable Port', 'ipAddress': '192.168.0.1', 'label': 'test.softlayer.com', 'metaData': '', 'objectId': 300, 'objectName': 'CCI', 'traceId': '100', 'userId': '', 'userType': 'SYSTEM' }, { 'accountId': 100, 'eventCreateDate': '2017-10-18T09:40:41.830338-05:00', 'eventName': 'Security Group Rule Added', 'ipAddress': '192.168.0.1', 'label': 'test.softlayer.com', 'metaData': '{"securityGroupId":"200",' '"securityGroupName":"test_SG",' '"networkComponentId":"100",' '"networkInterfaceType":"public",' '"requestId":"53d0b91d392864e062f4958",' '"rules":[{"ruleId":"100",' '"remoteIp":null,"remoteGroupId":null,"direction":"ingress",' '"ethertype":"IPv4",' '"portRangeMin":2000,"portRangeMax":2001,"protocol":"tcp"}]}', 'objectId': 300, 'objectName': 'CCI', 'traceId': '59e767e9c2184', 'userId': 400, 'userType': 'CUSTOMER', 'username': 'user' }, { 'accountId': 100, 'eventCreateDate': '2017-10-18T09:40:32.238869-05:00', 'eventName': 'Security Group Added', 'ipAddress': '192.168.0.1', 'label': 'test.softlayer.com', 'metaData': '{"securityGroupId":"200",' '"securityGroupName":"test_SG",' '"networkComponentId":"100",' '"networkInterfaceType":"public",' '"requestId":"96c9b47b9e102d2e1d81fba"}', 'objectId': 300, 'objectName': 'CCI', 'traceId': '59e767e03a57e', 'userId': 400, 'userType': 'CUSTOMER', 'username': 'user' }, { 'accountId': 100, 'eventCreateDate': '2017-10-18T10:42:13.089536-05:00', 'eventName': 'Security Group Rule(s) Removed', 'ipAddress': '192.168.0.1', 'label': 'test_SG', 'metaData': '{"requestId":"2abda7ca97e5a1444cae0b9",' '"rules":[{"ruleId":"800",' '"remoteIp":null,"remoteGroupId":null,"direction":"ingress",' '"ethertype":"IPv4",' '"portRangeMin":2000,"portRangeMax":2001,"protocol":"tcp"}]}', 'objectId': 700, 'objectName': 'Security Group', 'traceId': '59e7765515e28', 'userId': 400, 'userType': 'CUSTOMER', 'username': 'user' }, { 'accountId': 100, 'eventCreateDate': '2017-10-18T10:42:11.679736-05:00', 'eventName': 'Network Component Removed from Security Group', 'ipAddress': '192.168.0.1', 'label': 'test_SG', 'metaData': '{"requestId":"6b9a87a9ab8ac9a22e87a00",' '"fullyQualifiedDomainName":"test.softlayer.com",' '"networkComponentId":"100",' '"networkInterfaceType":"public"}', 'objectId': 700, 'objectName': 'Security Group', 'traceId': '59e77653a1e5f', 'userId': 400, 'userType': 'CUSTOMER', 'username': 'user' }, { 'accountId': 100, 'eventCreateDate': '2017-10-18T10:41:49.802498-05:00', 'eventName': 'Security Group Rule(s) Added', 'ipAddress': '192.168.0.1', 'label': 'test_SG', 'metaData': '{"requestId":"0a293c1c3e59e4471da6495",' '"rules":[{"ruleId":"800",' '"remoteIp":null,"remoteGroupId":null,"direction":"ingress",' '"ethertype":"IPv4",' '"portRangeMin":2000,"portRangeMax":2001,"protocol":"tcp"}]}', 'objectId': 700, 'objectName': 'Security Group', 'traceId': '59e7763dc3f1c', 'userId': 400, 'userType': 'CUSTOMER', 'username': 'user' }, { 'accountId': 100, 'eventCreateDate': '2017-10-18T10:41:42.176328-05:00', 'eventName': 'Network Component Added to Security Group', 'ipAddress': '192.168.0.1', 'label': 'test_SG', 'metaData': '{"requestId":"4709e02ad42c83f80345904",' '"fullyQualifiedDomainName":"test.softlayer.com",' '"networkComponentId":"100",' '"networkInterfaceType":"public"}', 'objectId': 700, 'objectName': 'Security Group', 'traceId': '59e77636261e7', 'userId': 400, 'userType': 'CUSTOMER', 'username': 'user' } ] getAllEventObjectNames = [ { 'value': "Account" }, { 'value': "CDN" }, { 'value': "User" }, { 'value': "Bare Metal Instance" }, { 'value': "API Authentication" }, { 'value': "Server" }, { 'value': "CCI" }, { 'value': "Image" }, { 'value': "Bluemix LB" }, { 'value': "Facility" }, { 'value': "Cloud Object Storage" }, { 'value': "Security Group" } ] softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Hardware.py000066400000000000000000000050621415376457700247720ustar00rootroot00000000000000getObject = { 'id': 1234, 'globalIdentifier': 'xxxxc-asd', 'datacenter': {'id': 12, 'name': 'DALLAS21', 'description': 'Dallas 21'}, 'billingItem': { 'id': 6327, 'recurringFee': 1.54, 'nextInvoiceTotalRecurringAmount': 16.08, 'children': [ {'description': 'test', 'nextInvoiceTotalRecurringAmount': 1}, ], 'orderItem': { 'order': { 'userRecord': { 'username': 'bob', } } } }, 'primaryIpAddress': '4.4.4.4', 'hostname': 'testtest1', 'domain': 'test.sftlyr.ws', 'bareMetalInstanceFlag': True, 'fullyQualifiedDomainName': 'testtest1.test.sftlyr.ws', 'processorPhysicalCoreAmount': 4, 'memoryCapacity': 4, 'primaryBackendIpAddress': '10.4.4.4', 'networkManagementIpAddress': '10.4.4.4', 'hardwareStatus': {'status': 'ACTIVE'}, 'primaryNetworkComponent': {'maxSpeed': 1000, 'speed': 1000}, 'provisionDate': '2020-08-01 15:23:45', 'notes': 'NOTES NOTES NOTES', 'operatingSystem': { 'softwareLicense': { 'softwareDescription': { 'referenceCode': 'UBUNTU_20_64', 'name': 'Ubuntu', 'version': 'Ubuntu 20.04 LTS', } }, 'passwords': [ {'username': 'root', 'password': 'xxxxxxxxxxxx'} ], }, 'remoteManagementAccounts': [ {'username': 'root', 'password': 'zzzzzzzzzzzzzz'} ], 'networkVlans': [ { 'networkSpace': 'PRIVATE', 'vlanNumber': 1234, 'id': 11111 }, ], 'tagReferences': [ {'tag': {'name': 'a tag'}} ], } allowAccessToNetworkStorageList = True getSensorData = [ { "sensorId": "Ambient 1 Temperature", "sensorReading": "25.000", "sensorUnits": "degrees C", "status": "ok", "upperCritical": "43.000", "upperNonCritical": "41.000", "upperNonRecoverable": "46.000" }, { "lowerCritical": "3500.000", "sensorId": "Fan 1 Tach", "sensorReading": "6580.000", "sensorUnits": "RPM", "status": "ok" }, { "sensorId": "IPMI Watchdog", "sensorReading": "0x0", "sensorUnits": "discrete", "status": "0x0080" }, { "sensorId": "Avg Power", "sensorReading": "70.000", "sensorUnits": "Watts", "status": "ok" }] softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Hardware_Server.py000066400000000000000000000247171415376457700263300ustar00rootroot00000000000000getObject = { 'id': 1000, 'globalIdentifier': '1a2b3c-1701', 'datacenter': {'id': 50, 'name': 'TEST00', 'description': 'Test Data Center'}, 'billingItem': { 'id': 6327, 'recurringFee': 1.54, 'package': { 'id': 911 }, 'nextInvoiceTotalRecurringAmount': 16.08, 'children': [ {'description': 'test', 'nextInvoiceTotalRecurringAmount': 1}, ], 'nextInvoiceChildren': [ {'description': 'test', 'nextInvoiceTotalRecurringAmount': 1, 'categoryCode': 'disk1'}, {'description': 'test2', 'nextInvoiceTotalRecurringAmount': 2, 'categoryCode': 'disk3'} ], '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 toggleManagementInterface = True powerOff = True powerOn = True powerCycle = True rebootSoft = True rebootDefault = True rebootHard = True createFirmwareUpdateTransaction = True createFirmwareReflashTransaction = 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}] } } ] getBandwidthAllotmentDetail = { 'allocationId': 25465663, 'bandwidthAllotmentId': 138442, 'effectiveDate': '2019-04-03T23:00:00-06:00', 'endEffectiveDate': None, 'id': 25888247, 'serviceProviderId': 1, 'allocation': { 'amount': '250' } } getBillingCycleBandwidthUsage = [ { 'amountIn': '.448', 'amountOut': '.52157', 'type': { 'alias': 'PUBLIC_SERVER_BW' } }, { 'amountIn': '.03842', 'amountOut': '.01822', 'type': { 'alias': 'PRIVATE_SERVER_BW' } } ] getMetricTrackingObjectId = 1000 getAttachedNetworkStorages = [ { "accountId": 11111, "capacityGb": 20, "createDate": "2018-04-05T05:15:49-06:00", "id": 22222, "nasType": "NAS", "serviceProviderId": 1, "storageTypeId": "13", "username": "SL02SEV311111_11", "allowedHardware": [ { "id": 12345, "datacenter": { "id": 449506, "longName": "Frankfurt 2", "name": "fra02", "statusId": 2 } } ], "serviceResourceBackendIpAddress": "fsn-fra0201a-fz.service.softlayer.com", "serviceResourceName": "Storage Type 02 File Aggregate stfm-fra0201a" }, { "accountId": 11111, "capacityGb": 12000, "createDate": "2018-01-28T04:57:30-06:00", "id": 3777111, "nasType": "ISCSI", "notes": "BlockStorage12T", "password": "", "serviceProviderId": 1, "storageTypeId": "7", "username": "SL02SEL32222-9", "allowedHardware": [ { "id": 629222, "datacenter": { "id": 449506, "longName": "Frankfurt 2", "name": "fra02", "statusId": 2 } } ], "serviceResourceBackendIpAddress": "10.31.95.152", "serviceResourceName": "Storage Type 02 Block Aggregate stbm-fra0201a" } ] getAllowedHost = { "accountId": 11111, "credentialId": 22222, "id": 33333, "name": "iqn.2020-03.com.ibm:sl02su11111-v62941551", "resourceTableId": 6291111, "resourceTableName": "VIRTUAL_GUEST", "credential": { "accountId": "11111", "createDate": "2020-03-20T13:35:47-06:00", "id": 44444, "nasCredentialTypeId": 2, "password": "SjFDCpHrjskfj", "username": "SL02SU11111-V62941551" } } getHardDrives = [ { "id": 11111, "serialNumber": "z1w4sdf", "serviceProviderId": 1, "hardwareComponentModel": { "capacity": "1000", "description": "SATAIII:2000:8300:Constellation", "id": 111, "manufacturer": "Seagate", "name": "Constellation ES", "hardwareGenericComponentModel": { "capacity": "1000", "units": "GB", "hardwareComponentType": { "id": 1, "keyName": "HARD_DRIVE", "type": "Hard Drive", "typeParentId": 5 } } } } ] getVirtualHost = { "accountId": 11111, "createDate": "2018-10-08T10:54:48-06:00", "description": "host16.vmware.chechu.com", "hardwareId": 22222, "id": 33333, "name": "host16.vmware.chechu.com", "uuid": "00000000-0000-0000-0000-0cc11111", "hardware": { "accountId": 11111, "domain": "chechu.com", "hostname": "host16.vmware", "id": 22222, "hardwareStatus": { "id": 5, "status": "ACTIVE" } } } getUpgradeItemPrices = [ { "id": 21525, "recurringFee": "0", "categories": [ { "categoryCode": "port_speed", "id": 26, "name": "Uplink Port Speeds", } ], "item": { "capacity": "10000", "description": "10 Gbps Redundant Public & Private Network Uplinks", "id": 4342, "keyName": "10_GBPS_REDUNDANT_PUBLIC_PRIVATE_NETWORK_UPLINKS" } }, { "hourlyRecurringFee": ".247", "id": 209391, "recurringFee": "164", "categories": [ { "categoryCode": "ram", "id": 3, "name": "RAM" } ], "item": { "capacity": "32", "description": "32 GB RAM", "id": 11291, "keyName": "RAM_32_GB_DDR4_2133_ECC_NON_REG" } }, { "hourlyRecurringFee": ".068", "id": 22482, "recurringFee": "50", "categories": [ { "categoryCode": "disk_controller", "id": 11, "name": "Disk Controller", } ], "item": { "capacity": "0", "description": "RAID", "id": 4478, "keyName": "DISK_CONTROLLER_RAID", } }, { "id": 50357, "recurringFee": "0", "categories": [ { "categoryCode": "bandwidth", "id": 10, "name": "Public Bandwidth", } ], "item": { "capacity": "500", "description": "500 GB Bandwidth Allotment", "id": 6177, "keyName": "BANDWIDTH_500_GB" } }, { "hourlyRecurringFee": ".023", "id": 49759, "recurringFee": "15", "categories": [ { "categoryCode": "disk2", "id": 6, "name": "Third Hard Drive" } ], "item": { "capacity": "1000", "description": "1.00 TB SATA", "id": 6159, "keyName": "HARD_DRIVE_1_00_TB_SATA_2", } }, { "id": 49759, "recurringFee": "0", "categories": [ { "categoryCode": "disk1", "id": 5, "name": "Second Hard Drive" } ], "item": { "capacity": "1000", "description": "1.00 TB SATA", "id": 6159, "keyName": "HARD_DRIVE_1_00_TB_SATA_2" } } ] getComponents = [{ "hardwareComponentModelId": 147, "hardwareId": 1234, "id": 369, "modifyDate": "2017-11-10T16:59:38-06:00", "serviceProviderId": 1, "hardwareComponentModel": { "name": "IMM2 - Onboard", "firmwares": [ { "createDate": "2020-09-24T13:46:29-06:00", "version": "5.60" }, { "createDate": "2019-10-14T16:51:12-06:00", "version": "5.10" }]}}] softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Location.py000066400000000000000000000004451415376457700250050ustar00rootroot00000000000000getDataCenters = [ { "id": 358694, "longName": "London 2", "name": "lon02" }, { "id": 168642, "longName": "San Jose 1", "name": "sjc01" }] getDatacenters = [{'id': 1854895, 'name': 'dal13', 'regions': [{'keyname': 'DALLAS13'}]}] softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Location_Datacenter.py000066400000000000000000000003141415376457700271320ustar00rootroot00000000000000getDatacenters = [ { "id": 1441195, "longName": "Dallas 10", "name": "dal10" }, { "id": 449494, "longName": "Dallas 9", "name": "dal09" } ] softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Metric_Tracking_Object.py000066400000000000000000000024231415376457700275660ustar00rootroot00000000000000getSummaryData = [ { "counter": 1.44, "dateTime": "2019-03-04T00:00:00-06:00", "type": "cpu0" }, { "counter": 1.53, "dateTime": "2019-03-04T00:05:00-06:00", "type": "cpu0" }, ] # Using counter > 32bit int causes unit tests to fail. getBandwidthData = [ { 'counter': 37.21, 'dateTime': '2019-05-20T23:00:00-06:00', 'type': 'cpu0' }, { 'counter': 76.12, 'dateTime': '2019-05-20T23:00:00-06:00', 'type': 'cpu1' }, { 'counter': 257623973, 'dateTime': '2019-05-20T23:00:00-06:00', 'type': 'memory' }, { 'counter': 137118503, 'dateTime': '2019-05-20T23:00:00-06:00', 'type': 'memory_usage' }, { 'counter': 125888818, 'dateTime': '2019-05-20T23:00:00-06:00', 'type': 'privateIn_net_octet' }, { 'counter': 961037, 'dateTime': '2019-05-20T23:00:00-06:00', 'type': 'privateOut_net_octet' }, { 'counter': 1449885176, 'dateTime': '2019-05-20T23:00:00-06:00', 'type': 'publicIn_net_octet' }, { 'counter': 91803794, 'dateTime': '2019-05-20T23:00:00-06:00', 'type': 'publicOut_net_octet' } ] softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Network_Application_Delivery_Controller.py000066400000000000000000000032121415376457700332520ustar00rootroot00000000000000getObject = { 'accountId': 307608, 'createDate': '2015-05-05T16:23:52-06:00', 'id': 11449, 'modifyDate': '2015-05-05T16:24:09-06:00', 'name': 'SLADC307608-1', 'typeId': 2, 'description': 'Citrix NetScaler VPX 10.5 10Mbps Standard', 'managementIpAddress': '10.11.11.112', 'outboundPublicBandwidthUsage': '.00365', 'primaryIpAddress': '19.4.24.16', 'datacenter': { 'longName': 'Dallas 9', 'name': 'dal09', }, "networkVlans": [ { "accountId": 11111, "id": 33333, "modifyDate": "2019-07-17T01:09:38+08:00", "name": "FirewallTesting", "primarySubnetId": 91111, "vlanNumber": 1711 }, { "accountId": 11111, "id": 862222, "modifyDate": "2019-07-17T01:09:42+08:00", "primarySubnetId": 502211, "vlanNumber": 722 } ], 'password': { 'password': 'aaaaa', 'username': 'root' }, "subnets": [ { "broadcastAddress": "", "cidr": 32, "gateway": "", "id": 74222, "modifyDate": "2016-10-26T23:39:12+08:00", "netmask": "255.255.255.255", "networkIdentifier": "159.253.111.111", "networkVlanId": 3611111, "sortOrder": "4", "subnetType": "STATIC_IP_ROUTED", "totalIpAddresses": "2", "usableIpAddressCount": "2", "version": 4, "addressSpace": "PUBLIC" } ], 'type': { 'keyName': 'NETSCALER_VPX', 'name': 'NetScaler VPX' } } SoftLayer_Network_Application_Delivery_Controller_LoadBalancer_Health_Check_Type.py000066400000000000000000000005201415376457700411440ustar00rootroot00000000000000softlayer-python-5.9.8/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.py000066400000000000000000000031711415376457700405750ustar00rootroot00000000000000softlayer-python-5.9.8/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.py000066400000000000000000000005651415376457700403020ustar00rootroot00000000000000softlayer-python-5.9.8/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.py000066400000000000000000000000501415376457700372370ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/fixturesdeleteObject = True toggleStatus = True SoftLayer_Network_Application_Delivery_Controller_LoadBalancer_Service_Group.py000066400000000000000000000000321415376457700404130ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/fixtureskickAllConnections = True SoftLayer_Network_Application_Delivery_Controller_LoadBalancer_VirtualIpAddress.py000066400000000000000000000060531415376457700410750ustar00rootroot00000000000000softlayer-python-5.9.8/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.py000066400000000000000000000000241415376457700404550ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/fixturesdeleteObject = True SoftLayer_Network_CdnMarketplace_Configuration_Cache_Purge.py000066400000000000000000000000211415376457700346460ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/fixturescreatePurge = [] softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Network_CdnMarketplace_Configuration_Mapping.py000066400000000000000000000027661415376457700341750ustar00rootroot00000000000000listDomainMappings = [ { "cacheKeyQueryRule": "include-all", "cname": "cdnakauuiet7s6u6.cdnedge.bluemix.net", "createDate": "2020-09-29T15:19:01-06:00", "domain": "test.example.com", "header": "test.example.com", "httpPort": 80, "originHost": "1.1.1.1", "originType": "HOST_SERVER", "path": "/", "protocol": "HTTP", "status": "CNAME_CONFIGURATION", "uniqueId": "9934111111111", "vendorName": "akamai" } ] listDomainMappingByUniqueId = [ { "cname": "cdnakauuiet7s6u6.cdnedge.bluemix.net", "performanceConfiguration": "Large file optimization", "domain": "test.example.com", "header": "test.example.com", "httpPort": 80, "originHost": "1.1.1.1", "originType": "HOST_SERVER", "path": "/", "protocol": "HTTP", "status": "CNAME_CONFIGURATION", "uniqueId": "9934111111111", "vendorName": "akamai" } ] updateDomainMapping = [ { "createDate": "2021-02-09T19:32:29-06:00", "originType": "HOST_SERVER", "path": "/*", "performanceConfiguration": "Large file optimization", "protocol": "HTTP", "respectHeaders": True, "uniqueId": "424406419091111", "vendorName": "akamai", "header": "www.test.com", "httpPort": 83, "cname": "cdn.test.cloud", "originHost": "1.1.1.1", "cacheKeyQueryRule": "include: test" } ] SoftLayer_Network_CdnMarketplace_Configuration_Mapping_Path.py000066400000000000000000000017311415376457700350610ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/fixtureslistOriginPath = [ { "header": "test.example.com", "httpPort": 80, "mappingUniqueId": "993419389425697", "origin": "10.10.10.1", "originType": "HOST_SERVER", "path": "/example", "status": "RUNNING" }, { "header": "test.example.com", "httpPort": 80, "mappingUniqueId": "993419389425697", "origin": "10.10.10.1", "originType": "HOST_SERVER", "path": "/example1", "status": "RUNNING" } ] createOriginPath = [ { "header": "test.example.com", "httpPort": 80, "mappingUniqueId": "993419389425697", "origin": "10.10.10.1", "originType": "HOST_SERVER", "path": "/example", "status": "RUNNING", "bucketName": "test-bucket", 'fileExtension': 'jpg', "performanceConfiguration": "General web delivery" } ] deleteOriginPath = "Origin with path /example/videos/* has been deleted" softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Network_CdnMarketplace_Metrics.py000066400000000000000000000004031415376457700313030ustar00rootroot00000000000000getMappingUsageMetrics = [ { "names": [ "TotalBandwidth", "TotalHits", "HitRatio" ], "totals": [ "0.0", "0", "0.0" ], "type": "TOTALS" } ] softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Network_Component_Firewall.py000066400000000000000000000063601415376457700305370ustar00rootroot00000000000000createCancelServerTicket = {'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.9.8/SoftLayer/fixtures/SoftLayer_Network_Firewall_Update_Request.py000066400000000000000000000001501415376457700315160ustar00rootroot00000000000000getObject = { "guestNetworkComponentId": 1705294, "id": 1234, } createObject = {} edit = True softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Network_LBaaS_HealthMonitor.py000066400000000000000000000003571415376457700305270ustar00rootroot00000000000000updateLoadBalancerHealthMonitors = { 'backendPort': 80, 'backendProtocol': 'HTTP', 'healthMonitorUuid': '1a1aa111-4474-4e16-9f02-4de959244444', 'interval': 50, 'maxRetries': 10, 'timeout': 10, 'urlPath': None } softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Network_LBaaS_L7Pool.py000066400000000000000000000032641415376457700270660ustar00rootroot00000000000000createL7Pool = {'accountId': 1234, 'address': '01-307608-ams01.clb.appdomain.cloud', 'createDate': '2019-08-12T07:49:43-06:00', 'id': 1111111, 'isPublic': 0, 'locationId': 265592, 'modifyDate': '2019-08-13T16:26:06-06:00', 'name': 'dcabero-01', 'operatingStatus': 'ONLINE', 'provisioningStatus': 'ACTIVE', 'type': 0, 'useSystemPublicIpPool': 1, 'uuid': '1a1aa111-4474-4e16-9f02-4de959229b85', 'listenerCount': 4, 'memberCount': 1, 'datacenter': { 'id': 265592, 'longName': 'Amsterdam 1', 'name': 'ams01', 'statusId': 2 }} deleteObject = {'accountId': 1234, 'address': '01-307608-ams01.clb.appdomain.cloud', 'createDate': '2019-08-12T07:49:43-06:00', 'id': 1111111, 'isPublic': 0, 'locationId': 265592, 'modifyDate': '2019-08-13T16:26:06-06:00', 'name': 'dcabero-01', 'operatingStatus': 'ONLINE', 'provisioningStatus': 'ACTIVE', 'type': 0, 'useSystemPublicIpPool': 1, 'uuid': '1a1aa111-4474-4e16-9f02-4de959229b85', 'listenerCount': 4, 'memberCount': 1, 'datacenter': { 'id': 265592, 'longName': 'Amsterdam 1', 'name': 'ams01', 'statusId': 2 }} softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Network_LBaaS_Listener.py000066400000000000000000000057041415376457700275400ustar00rootroot00000000000000# https://sldn.softlayer.com/reference/datatypes/SoftLayer_Network_LBaaS_Listener/ updateLoadBalancerProtocols = {'accountId': 1234, 'address': '01-307608-ams01.clb.appdomain.cloud', 'createDate': '2019-08-12T07:49:43-06:00', 'id': 1111111, 'isPublic': 0, 'locationId': 265592, 'modifyDate': '2019-08-13T16:26:06-06:00', 'name': 'dcabero-01', 'operatingStatus': 'ONLINE', 'provisioningStatus': 'ACTIVE', 'type': 0, 'useSystemPublicIpPool': 1, 'uuid': '1a1aa111-4474-4e16-9f02-4de959229b85', 'listenerCount': 4, 'memberCount': 1, 'datacenter': { 'id': 265592, 'longName': 'Amsterdam 1', 'name': 'ams01', 'statusId': 2 }} deleteLoadBalancerProtocols = {'accountId': 1234, 'address': '01-307608-ams01.clb.appdomain.cloud', 'createDate': '2019-08-12T07:49:43-06:00', 'id': 1111111, 'isPublic': 0, 'locationId': 265592, 'modifyDate': '2019-08-13T16:26:06-06:00', 'name': 'dcabero-01', 'operatingStatus': 'ONLINE', 'provisioningStatus': 'ACTIVE', 'type': 0, 'useSystemPublicIpPool': 1, 'uuid': '1a1aa111-4474-4e16-9f02-4de959229b85', 'listenerCount': 4, 'memberCount': 1, 'datacenter': { 'id': 265592, 'longName': 'Amsterdam 1', 'name': 'ams01', 'statusId': 2 }} getL7Policies = [ {'action': 'REJECT', 'createDate': '2021-09-08T15:08:35-06:00', 'id': 123456, 'modifyDate': None, 'name': 'test-reject', 'priority': 2, 'redirectL7PoolId': None, 'uuid': '123mock-1234-43c9-b659-12345678mock' }, {'action': 'REDIRECT_HTTPS', 'createDate': '2021-09-08T15:03:53-06:00', 'id': 432922, 'modifyDate': None, 'name': 'test-policy-https-1', 'priority': 0, 'redirectL7PoolId': None, 'redirectUrl': 'url-test-uuid-mock-1234565', 'uuid': 'test-uuid-mock-1234565' } ] softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Network_LBaaS_LoadBalancer.py000066400000000000000000000132661415376457700302640ustar00rootroot00000000000000getObject = { 'accountId': 1234, 'address': 'test-01-307608-ams01.clb.appdomain.cloud', 'createDate': '2019-08-12T07:49:43-06:00', 'id': 1111111, 'isPublic': 0, 'locationId': 265592, 'modifyDate': '2019-08-13T16:26:06-06:00', 'name': 'test-01', 'operatingStatus': 'ONLINE', 'provisioningStatus': 'ACTIVE', 'type': 0, 'useSystemPublicIpPool': 1, 'uuid': '1a1aa111-4474-4e16-9f02-4de959229b85', 'listenerCount': 4, 'memberCount': 1, 'previousErrorText': 'test', 'datacenter': { 'id': 265592, 'longName': 'Amsterdam 1', 'name': 'ams01', 'statusId': 2 }, 'healthMonitors': [ { 'createDate': '2019-08-20T18:05:09-04:00', 'interval': 5, 'maxRetries': 2, 'modifyDate': '2019-08-20T18:05:18-04:00', 'monitorType': 'HTTP', 'provisioningStatus': 'ACTIVE', 'timeout': 2, 'urlPath': '/', 'uuid': 'c11111c1-f5ab-4c15-ba96-d7b95dc7c824' } ], 'l7Pools': [ { 'createDate': '2019-08-19T16:33:37-04:00', 'id': 222222, 'loadBalancingAlgorithm': 'ROUNDROBIN', 'modifyDate': None, 'name': 'test', 'protocol': 'HTTP', 'provisioningStatus': 'ACTIVE', 'uuid': 'a1111111-c5e7-413f-9f78-84f6c5e1ca04' } ], 'listeners': [ { 'clientTimeout': 15, 'defaultPool': { 'healthMonitor': { 'uuid': '222222ab-bbcc-4f32-9b31-1b6d3a1959c8' }, 'protocol': 'HTTP', 'protocolPort': 256, 'uuid': 'ab1a1abc-0e83-4690-b5d4-1359625dba8f', } }, { 'clientTimeout': 15, 'defaultPool': { 'healthMonitor': { 'uuid': '222222ab-bbcc-4f32-9b31-1b6d3a1959c0' }, 'protocol': 'HTTP', 'protocolPort': 256, 'uuid': 'ab1a1abc-0e83-4690-b5d4-1359625dba8x', } }, {'connectionLimit': None, 'createDate': '2019-08-21T17:19:25-04:00', 'defaultPool': {'createDate': '2019-08-21T17:19:25-04:00', 'healthMonitor': {'createDate': '2019-08-21T17:17:04-04:00', 'id': 859330, 'interval': 5, 'maxRetries': 2, 'modifyDate': '2019-08-21T17:17:15-04:00', 'monitorType': 'HTTP', 'provisioningStatus': 'ACTIVE', 'timeout': 2, 'urlPath': '/', 'uuid': '55e00152-75fd-4f94-9263-cb4c6e005f12'}, 'loadBalancingAlgorithm': 'ROUNDROBIN', 'members': [{'address': '10.136.4.220', 'createDate': '2019-08-12T09:49:43-04:00', 'id': 1023118, 'modifyDate': '2019-08-12T09:52:54-04:00', 'provisioningStatus': 'ACTIVE', 'uuid': 'ba23a166-faa4-4eb2-96e7-ef049d65ce60', 'weight': 50}], 'modifyDate': '2019-08-21T17:19:33-04:00', 'protocol': 'HTTP', 'protocolPort': 230, 'provisioningStatus': 'ACTIVE', 'uuid': '1c5f3ba6-ec7d-4cf8-8815-9bb174224a76'}, 'id': 889072, 'l7Policies': [{'action': 'REJECT', 'createDate': '2019-08-21T18:17:41-04:00', 'id': 215204, 'modifyDate': None, 'name': 'trestst', 'priority': 1, 'redirectL7PoolId': None, 'uuid': 'b8c30aae-3979-49a7-be8c-fb70e43a6b4b'}], 'modifyDate': '2019-08-22T10:58:02-04:00', 'protocol': 'HTTP', 'protocolPort': 110, 'provisioningStatus': 'ACTIVE', 'tlsCertificateId': None, 'clientTimeout': 30, 'uuid': 'a509723d-a3cb-4ae4-bc5b-5ecf04f890ff'} ], 'members': [ { 'address': '10.0.0.1', 'createDate': '2019-08-12T09:49:43-04:00', 'modifyDate': '2019-08-12T09:52:54-04:00', 'provisioningStatus': 'ACTIVE', 'uuid': 'ba23a166-faa4-4eb2-96e7-ef049d65ce60', 'weight': 50 } ], 'sslCiphers': [ { 'id': 2, 'name': 'ECDHE-RSA-AES256-GCM-SHA384' } ], } getAllObjects = [getObject] getLoadBalancer = { "accountId": 3071234, "createDate": "2019-08-12T21:49:43+08:00", "id": 81234, "isPublic": 0, "locationId": 265592, "modifyDate": "2019-08-14T06:26:06+08:00", "name": "dcabero-01", "uuid": "0a2da082-4474-4e16-9f02-4de11111", "datacenter": { "id": 265592, "longName": "Amsterdam 1", "name": "ams01", "statusId": 2 } } cancelLoadBalancer = True getLoadBalancerMemberHealth = [ { 'poolUuid': '1c5f3ba6-ec7d-4cf8-8815-9bb174224a76', 'membersHealth': [ { 'status': 'DOWN', 'uuid': 'ba23a166-faa4-4eb2-96e7-ef049d65ce60' } ] } ] getHealthMonitors = {} getLoadBalancer = getObject cancelLoadBalancer = getObject softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Network_LBaaS_Member.py000066400000000000000000000002521415376457700271530ustar00rootroot00000000000000# Should be sldn.softlayer.com/reference/datatypes/SoftLayer_Network_LBaaS_LoadBalancer deleteLoadBalancerMembers = {} addLoadBalancerMembers = deleteLoadBalancerMembers softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Network_Message_Delivery_Email_Sendgrid.py000066400000000000000000000024551415376457700331260ustar00rootroot00000000000000getAccountOverview = { "creditsAllowed": 25000, "creditsOverage": 0, "creditsRemain": 25000, "creditsUsed": 0, "package": "Free Package", "reputation": 100, "requests": 56 } getStatistics = [{ "blocks": 0, "bounces": 0, "clicks": 0, "date": "2021-04-28", "delivered": 0, "invalidEmail": 0, "opens": 0, "repeatBounces": 0, "repeatSpamReports": 0, "repeatUnsubscribes": 0, "requests": 0, "spamReports": 0, "uniqueClicks": 0, "uniqueOpens": 0, "unsubscribes": 0 }] getObject = { "accountId": 123456, "createDate": "2020-07-06T10:29:11-06:00", "id": 1232123, "password": "Test123456789", "typeId": 21, "username": "techsupport3@ie.ibm.com", "vendorId": 1, "billingItem": { "categoryCode": "network_message_delivery", "description": "Free Package", "id": 695735054, "notes": "techsupport3@ie.ibm.com", }, "type": { "description": "Delivery of messages through e-mail", "id": 21, "keyName": "EMAIL", "name": "Email" }, "vendor": { "id": 1, "keyName": "SENDGRID", "name": "SendGrid" }, "emailAddress": "techsupport3@ie.ibm.com", "smtpAccess": "1" } editObject = True updateEmailAddress = True softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Network_Pod.py000066400000000000000000000017241415376457700254710ustar00rootroot00000000000000getAllObjects = [ { 'backendRouterId': 117917, 'backendRouterName': 'bcr01a.ams01', 'datacenterId': 265592, 'datacenterLongName': 'Amsterdam 1', 'datacenterName': 'ams01', 'frontendRouterId': 117960, 'frontendRouterName': 'fcr01a.ams01', 'name': 'ams01.pod01' }, { 'backendRouterId': 1115295, 'backendRouterName': 'bcr01a.wdc07', 'datacenterId': 2017603, 'datacenterLongName': 'Washington 7', 'datacenterName': 'wdc07', 'frontendRouterId': 1114993, 'frontendRouterName': 'fcr01a.wdc07', 'name': 'wdc07.pod01' }, { 'backendRouterId': 1234567, 'backendRouterName': 'bcr01a.wdc07', 'datacenterId': 2017603, 'datacenterLongName': 'Washington 7', 'datacenterName': 'wdc07', 'frontendRouterId': 258741369, 'frontendRouterName': 'fcr01a.wdc07', 'name': 'TEST00.pod2' } ] softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Network_SecurityGroup.py000066400000000000000000000034471415376457700275770ustar00rootroot00000000000000getAllObjects = [ {'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 = {"requestId": "addRules", "rules": "[{'direction': 'ingress', " "'portRangeMax': '', " "'portRangeMin': '', " "'ethertype': 'IPv4', " "'securityGroupId': 100, " "'remoteGroupId': '', " "'id': 100}]"} editRules = {'requestId': 'editRules'} removeRules = {'requestId': 'removeRules'} attachNetworkComponents = {'requestId': 'interfaceAdd'} detachNetworkComponents = {'requestId': 'interfaceRemove'} softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Network_Service_Vpn_Overrides.py000066400000000000000000000000521415376457700312050ustar00rootroot00000000000000createObjects = True deleteObjects = True softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Network_Storage.py000066400000000000000000000160551415376457700263560ustar00rootroot00000000000000STAAS_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', 'dependentDuplicate': 1, } 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 editObject = True allowAccessFromHostList = True removeAccessFromHostList = True failoverToReplicant = True failbackFromReplicant = True restoreFromSnapshot = True disasterRecoveryFailoverToReplicant = True createSnapshot = { 'id': 449 } enableSnapshots = True disableSnapshots = True getVolumeCountLimits = { 'datacenterName': 'global', 'maximumAvailableCount': 300, 'provisionedCount': 100 } refreshDuplicate = { 'dependentDuplicate': 1 } convertCloneDependentToIndependent = { 'dependentDuplicate': 1 } softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Network_Storage_Allowed_Host.py000066400000000000000000000025701415376457700310170ustar00rootroot00000000000000TEST_ALLOWED_HOST = { 'id': 12345, 'name': 'Test Allowed Host', 'accountId': 1234, 'credentialId': None, 'createDate': '2020-01-01 00:00:01', 'iscsiAclCredentials': { 'id': 129, 'allowedHostId': 12345, 'subnetId': 12345678 }, 'subnetsInAcl': [{ 'id': 12345678, 'accountId': 1234, 'networkIdentifier': '10.11.12.13', 'cidr': '14', 'billingRecordId': None, 'parentId': None, 'networkVlanId': None, 'createDate': '2020-01-02 00:00:01', 'modifyDate': None, 'subnetType': 'SECONDARY_ON_VLAN', 'restrictAllocationFlag': 0, 'leafFlag': 1, 'ownerId': 1, 'ipAddressBegin': 129123, 'ipAddressEnd': 129145, 'purgeFlag': 0 }] } getObject = TEST_ALLOWED_HOST getSubnetsInAcl = [{ 'id': 12345678, 'accountId': 1234, 'networkIdentifier': '10.11.12.13', 'cidr': '14', 'billingRecordId': None, 'parentId': None, 'networkVlanId': None, 'createDate': '2020-01-02 00:00:01', 'modifyDate': None, 'subnetType': 'SECONDARY_ON_VLAN', 'restrictAllocationFlag': 0, 'leafFlag': 1, 'ownerId': 1, 'ipAddressBegin': 129123, 'ipAddressEnd': 129145, 'purgeFlag': 0 }] assignSubnetsToAcl = [ 12345678 ] removeSubnetsFromAcl = [ 12345678 ] setCredentialPassword = True softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Network_Storage_Hub_Cleversafe_Account.py000066400000000000000000000027261415376457700327670ustar00rootroot00000000000000credentialCreate = { "accountId": "12345", "createDate": "2019-04-05T13:25:25-06:00", "id": 11111, "password": "nwUEUsx6PiEoN0B1Xe9z9hUCyXMkAFhDOjHqYJva", "username": "XfHhBNBPlPdlWyaPPJAI", "type": { "description": "A credential for generating S3 Compatible Signatures.", "keyName": "S3_COMPATIBLE_SIGNATURE", "name": "S3 Compatible Signature" } } getCredentials = [ { "accountId": "12345", "createDate": "2019-04-05T13:25:25-06:00", "id": 11111, "password": "nwUEUsx6PiEoN0B1Xe9z9hUCyXMkAFhDOjHqYJva", "username": "XfHhBNBPlPdlWyaPPJAI", "type": { "description": "A credential for generating S3 Compatible Signatures.", "keyName": "S3_COMPATIBLE_SIGNATURE", "name": "S3 Compatible Signature" } }, { "accountId": "12345", "createDate": "2019-04-05T13:25:25-06:00", "id": 11111, "password": "nwUEUsx6PiEoN0B1Xe9z9hUCyXMkAFhDOjHqYJva", "username": "XfHhBNBPlPdlWyaPPJAI", "type": { "description": "A credential for generating S3 Compatible Signatures.", "keyName": "S3_COMPATIBLE_SIGNATURE", "name": "S3 Compatible Signature" } } ] getBuckets = [ { "bytesUsed": 40540117, "name": "normal-bucket", "objectCount": 4, "storageLocation": "us-standard" } ] getCredentialLimit = 2 credentialDelete = True softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Network_Storage_Iscsi.py000066400000000000000000000013371415376457700275050ustar00rootroot00000000000000getObject = { "id": 11111, "allowedVirtualGuests": [ { "id": 22222, "allowedHost": { "accountId": 12345, "id": 18311111, "name": "iqn.2020-03.com.ibm:sl02su11111-v62941551", "resourceTableId": 6222222, "resourceTableName": "VIRTUAL_GUEST", "credential": { "accountId": "12345", "createDate": "2020-03-20T13:35:47-06:00", "id": 1522222, "nasCredentialTypeId": 2, "password": "SjFDCpHrmKewos", "username": "SL02SU322222-V62922222" } } } ] } softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Network_Subnet.py000066400000000000000000000020221415376457700261770ustar00rootroot00000000000000getObject = { '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, 'note': 'test note', 'tagReferences': [ {'id': 1000123, 'resourceTableId': 1234, 'tag': {'id': 100123, 'name': 'subnet: test tag'}, } ], 'ipAddresses': [ {'id': 123456, 'ipAddress': '16.26.26.25'}, {'id': 123457, 'ipAddress': '16.26.26.26'}] } editNote = True setTags = True cancel = True softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Network_Subnet_IpAddress.py000066400000000000000000000015021415376457700301370ustar00rootroot00000000000000getByIpAddress = { 'id': 12345, 'ipAddress': '10.0.1.37', 'isBroadcast': False, 'isGateway': False, 'isNetwork': False, 'isReserved': False, 'subnetId': 5678, "hardware": { "id": 12856, "fullyQualifiedDomainName": "unit.test.com" }, "subnet": { "broadcastAddress": "10.0.1.91", "cidr": 26, "gateway": "10.47.16.129", "id": 258369, "isCustomerOwned": False, "isCustomerRoutable": False, "modifyDate": "2019-04-02T13:45:52-06:00", "netmask": "255.255.255.192", "networkIdentifier": "10.0.1.38", "networkVlanId": 1236987, "sortOrder": "0", "subnetType": "PRIMARY", "totalIpAddresses": "64", "usableIpAddressCount": "61", "version": 4 } } editObject = True softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Network_Subnet_IpAddress_Global.py000066400000000000000000000001221415376457700314140ustar00rootroot00000000000000route = True unroute = True getObject = {'id': 1234, 'billingItem': {'id': 1234}} softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Network_Subnet_Rwhois_Data.py000066400000000000000000000000221415376457700304610ustar00rootroot00000000000000editObject = True softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Network_Vlan.py000066400000000000000000000012521415376457700256430ustar00rootroot00000000000000getObject = { 'primaryRouter': { 'datacenter': {'id': 1234, 'longName': 'TestDC'}, 'fullyQualifiedDomainName': 'fcr01.TestDC' }, 'id': 1234, 'vlanNumber': 4444, 'firewallInterfaces': None, 'billingItem': { 'allowCancellationFlag': 1, 'categoryCode': 'network_vlan', 'description': 'Private Network Vlan', 'id': 235689, 'notes': 'test cli', 'orderItemId': 147258, } } editObject = True setTags = True getList = [getObject] cancel = True getCancelFailureReasons = [ "1 bare metal server(s) still on the VLAN ", "1 virtual guest(s) still on the VLAN " ] softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Network_Vlan_Firewall.py000066400000000000000000000075411415376457700274770ustar00rootroot00000000000000createCancelServerTicket = {'id': 1234, 'title': 'Server Cancellation Request'} getObject = { "administrativeBypassFlag": "", "customerManagedFlag": False, "billingItem": { "id": 21370815 }, "id": 3130, "primaryIpAddress": "192.155.239.146", "datacenter": { "id": 265592, "longName": "Amsterdam 1", "name": "ams01", "statusId": 2 }, "networkVlan": { "accountId": 307608, "id": 371028, "primarySubnetId": 536252, "name": 'testvlan', "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 } ] } ] }, "rules": [ {'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' } ] } 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': 'tmp', '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' } ] edit = True softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Notification_Occurrence_Event.py000066400000000000000000000017021415376457700311710ustar00rootroot00000000000000getObject = { 'endDate': '2019-03-18T17:00:00-06:00', 'id': 1234, 'lastImpactedUserCount': 417756, 'modifyDate': '2019-03-12T15:32:48-06:00', 'recoveryTime': None, 'startDate': '2019-03-18T16:00:00-06:00', 'subject': 'Public Website Maintenance', 'summary': 'Blah Blah Blah', 'systemTicketId': 76057381, 'acknowledgedFlag': False, 'attachments': [], 'impactedResources': [{ 'resourceType': 'Server', 'resourceTableId': 12345, 'hostname': 'test', 'privateIp': '10.0.0.1', 'filterLable': 'Server' }], 'notificationOccurrenceEventType': {'keyName': 'PLANNED'}, 'statusCode': {'keyName': 'PUBLISHED', 'name': 'Published'}, 'updates': [{ 'contents': 'More Blah Blah', 'createDate': '2019-03-12T13:07:22-06:00', 'endDate': None, 'startDate': '2019-03-12T13:07:22-06:00' }] } getAllObjects = [getObject] acknowledgeNotification = True softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Product_Order.py000066400000000000000000000211131415376457700260030ustar00rootroot00000000000000verifyOrder = { '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 = { 'orderId': 1234, 'orderDate': '2013-08-01 15:23:45', 'orderDetails': { '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'}, }], 'virtualGuests': [{ 'id': 1234567, 'globalIdentifier': '1a2b3c-1701', 'fullyQualifiedDomainName': 'test.guest.com' }] } } # Reserved Capacity Stuff rsc_verifyOrder = { 'orderContainers': [ { 'locationObject': { 'id': 1854895, 'longName': 'Dallas 13', 'name': 'dal13' }, 'name': 'test-capacity', 'postTaxRecurring': '0.32', 'prices': [ { 'item': { 'id': 1, 'description': 'B1.1x2 (1 Year ''Term)', 'keyName': 'B1_1X2_1_YEAR_TERM', } } ] } ], 'postTaxRecurring': '0.32', } hardware_verifyOrder = { "currencyShortName": "USD", "hardware": [ { "accountId": 1111, "domain": "testedit.com", "hostname": "test", "globalIdentifier": "81434794-af69-44d5-bb97-12312asdasdasd" } ], "location": "1441195", "locationObject": { "id": 1441195, "longName": "Dallas 10", "name": "dal10" }, "packageId": 911, "postTaxRecurring": "0", "postTaxRecurringHourly": "0", "postTaxRecurringMonthly": "0", "preTaxRecurring": "0", "preTaxRecurringHourly": "0", "preTaxRecurringMonthly": "0", "prices": [ { "hourlyRecurringFee": "0", "id": 209391, "recurringFee": "0", "categories": [ { "categoryCode": "ram", "id": 3, "name": "RAM" } ], "item": { "capacity": "32", "description": "32 GB RAM", "id": 11291, "units": "GB" } } ], "proratedInitialCharge": "0", "proratedOrderTotal": "0", "quantity": 1, "sendQuoteEmailFlag": None, "totalRecurringTax": "0", "useHourlyPricing": False } hardware_placeOrder = { "orderDate": "2021-05-07T07:41:41-06:00", "orderDetails": { "billingInformation": { "billingAddressLine1": "4849 Alpha Rd", "billingCity": "Dallas", "billingCountryCode": "US", "billingEmail": "test.ibm.com", "billingNameCompany": "SoftLayer Internal - Development Community", "billingNameFirst": "Test", "billingNameLast": "Test", "billingPhoneVoice": "1111111", "billingPostalCode": "75244-1111", "billingState": "TX", }, "currencyShortName": "USD", "hardware": [ { "accountId": 1111111, "bareMetalInstanceFlag": 0, "domain": "testedit.com", "fullyQualifiedDomainName": "test.testedit.com", "hostname": "test", "globalIdentifier": "81434794-af69-44d5-bb97-1111111" } ], "location": "1441195", "locationObject": { "id": 1441195, "longName": "Dallas 10", "name": "dal10" }, "packageId": 911, "paymentType": "ADD_TO_BALANCE", "postTaxRecurring": "0", "postTaxRecurringHourly": "0", "postTaxRecurringMonthly": "0", "postTaxSetup": "0", "preTaxRecurring": "0", "preTaxRecurringHourly": "0", "preTaxRecurringMonthly": "0", "preTaxSetup": "0", "prices": [ { "hourlyRecurringFee": "0", "id": 209391, "recurringFee": "0", "categories": [ { "categoryCode": "ram", "id": 3, "name": "RAM" } ], "item": { "capacity": "32", "description": "32 GB RAM", "id": 11291, "keyName": "RAM_32_GB_DDR4_2133_ECC_NON_REG", "units": "GB", } } ], "proratedInitialCharge": "0", "proratedOrderTotal": "0", "quantity": 1, "totalRecurringTax": "0", "useHourlyPricing": False }, "orderId": 78332111, "placedOrder": { "accountId": 1111111, "id": 1234, "status": "PENDING_UPGRADE", "account": { "brandId": 2, "companyName": "SoftLayer Internal - Development Community", "id": 1234 }, "items": [ { "categoryCode": "ram", "description": "32 GB RAM", "id": 824199364, "recurringFee": "0" } ], "userRecord": { "accountId": 1234, "firstName": "test", "id": 3333, "lastName": "test", "username": "sl1234-test" } } } rsc_placeOrder = { 'orderDate': '2013-08-01 15:23:45', 'orderId': 1234, 'orderDetails': { 'postTaxRecurring': '0.32', }, 'placedOrder': { 'status': 'Great, thanks for asking', 'locationObject': { 'id': 1854895, 'longName': 'Dallas 13', 'name': 'dal13' }, 'name': 'test-capacity', 'items': [ { 'description': 'B1.1x2 (1 Year ''Term)', 'keyName': 'B1_1X2_1_YEAR_TERM', 'categoryCode': 'guest_core', } ] } } rsi_placeOrder = { 'orderId': 1234, 'orderDetails': { 'prices': [ { 'id': 4, 'item': { 'id': 1, 'description': 'B1.1x2 (1 Year ''Term)', 'keyName': 'B1_1X2_1_YEAR_TERM', }, 'hourlyRecurringFee': 1.0, 'recurringFee': 2.0 } ] } } wmware_placeOrder = { "orderDate": "2021-06-02 15:23:47", "orderId": 123456, "prices": [ { "id": 176535, "itemId": 8109, "categories": [ { "categoryCode": "software_license", "id": 438, "name": "Software License" } ], "item": { "capacity": "1", "description": "VMware vSAN Advanced Tier III 64 - 124 TB 6.x", "id": 8109, "keyName": "VMWARE_VSAN_ADVANCE_TIER_III_64_124_6_X", "softwareDescription": { "id": 1795, }, "thirdPartyPolicyAssignments": [ { "id": 29263, "policyName": "3rd Party Software Terms VMWare v4" } ] } } ]} vlan_placeOrder = {"orderDate": "2021-06-02 15:23:47", "orderId": 123456, "orderDetails": { "orderContainers": [{ "name": "test"}]}, "prices": [{ "id": 2018, "itemId": 1071, "categories": [{ "categoryCode": "network_vlan", "id": 113, "name": "Network Vlan"}], "item": { "capacity": "0", "description": "Public Network Vlan", "id": 1071, "keyName": "PUBLIC_NETWORK_VLAN"}} ]} softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Product_Package.py000066400000000000000000002125541415376457700262760ustar00rootroot00000000000000# pylint: skip-file HARDWARE_ITEMS = [ {'attributes': [], 'capacity': '999', 'description': 'Unknown', 'itemCategory': {'categoryCode': 'unknown', 'id': 325}, 'keyName': 'UNKNOWN', 'prices': [{'accountRestrictions': [], 'currentPriceFlag': '', 'hourlyRecurringFee': '0', 'id': 1245172, "locationGroupId": '', '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, "locationGroupId": '', '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, "locationGroupId": '', '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, "locationGroupId": '', '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, "locationGroupId": '', '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, "locationGroupId": '', '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, "locationGroupId": '', '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, "locationGroupId": '', '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, "locationGroupId": '', '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': '' } ] } ] } SAAS_REST_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': None } ] }, { 'capacity': '20', 'keyName': '', 'prices': [ { 'capacityRestrictionMaximum': '200', 'capacityRestrictionMinimum': '200', 'capacityRestrictionType': 'STORAGE_TIER_LEVEL', 'categories': [ {'categoryCode': 'storage_snapshot_space'} ], 'id': 193853, 'locationGroupId': None } ] }, { 'capacity': '0', 'capacityMaximum': '1999', 'capacityMinimum': '1000', 'itemCategory': {'categoryCode': 'performance_storage_space'}, 'keyName': '1000_1999_GBS', 'prices': [ { 'id': 190113, 'categories': [ {'categoryCode': 'performance_storage_space'} ], 'locationGroupId': None } ] }, { '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': None } ] }, { 'capacity': '0', 'keyName': '', 'prices': [ { 'categories': [ {'categoryCode': 'storage_file'} ], 'id': 189453, 'locationGroupId': None } ] } ] } activePreset1 = { '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, 'prices': [ { "hourlyRecurringFee": "1.18", "id": 165711, "locationGroupId": '', "recurringFee": "780", } ] } activePreset2 = { 'description': 'Dual Xeon Gold, 384GB Ram, 4x960GB SSD, RAID 10', 'id': 65, 'isActive': '1', 'keyName': 'DGOLD_6140_384GB_4X960GB_SSD_SED_RAID_10', 'name': 'DGOLD 6140 384GB 4X960GB SSD SED RAID 10', 'packageId': 200, 'prices': [ { "hourlyRecurringFee": "1.18", "id": 165711, "locationGroupId": '', "recurringFee": "780", } ] } getAllObjects = [{ 'activePresets': [activePreset1], 'accountRestrictedActivePresets': [activePreset2], '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, "itemPrices": [ { "hourlyRecurringFee": ".027", "id": 205911, "laborFee": "0", "locationGroupId": 505, "capacityRestrictionMaximum": "40", "capacityRestrictionMinimum": "40", "capacityRestrictionType": "CORE", "item": { "capacity": "0", "description": "Load Balancer Uptime", "id": 10785, "keyName": "LOAD_BALANCER_UPTIME", } }, { "hourlyRecurringFee": "0", "id": 199467, "laborFee": "0", "locationGroupId": '', "recurringFee": "0", "item": { "capacity": "0", "description": "Load Balancer Bandwidth", "id": 10051, "keyName": "LOAD_BALANCER_BANDWIDTH", } }, { "hourlyRecurringFee": ".028", "id": 205913, "laborFee": "0", "locationGroupId": 507, "item": { "capacity": "0", "description": "Load Balancer Uptime", "id": 10785, "keyName": "LOAD_BALANCER_UPTIME", } }] }] getItems = [ { 'id': 1234, 'keyName': 'KeyName01', 'capacity': '1000', 'description': 'Public & Private Networks', 'itemCategory': {'categoryCode': 'Uplink Port Speeds'}, 'softwareDescription': { 'id': 1228, 'longDescription': 'Redhat EL 5.10-64', 'referenceCode': 'REDHAT_5_64' }, 'prices': [{'id': 1122, 'hourlyRecurringFee': 0.10, 'recurringFee': 0.10, 'categories': [{'id': 26, 'name': 'Uplink Port Speeds', 'categoryCode': 'port_speed'}]}], }, { 'id': 2233, 'keyName': 'KeyName02', 'capacity': '1000', 'description': 'Public & Private Networks', 'itemCategory': {'categoryCode': 'Uplink Port Speeds'}, 'prices': [{'id': 4477, 'hourlyRecurringFee': 0.10, 'recurringFee': 0.10, 'categories': [{'id': 26, 'name': 'Uplink Port Speeds', 'categoryCode': 'port_speed'}]}], }, { 'id': 1239, 'keyName': 'KeyName03', 'capacity': '2', 'description': 'RAM', 'itemCategory': {'categoryCode': 'RAM'}, 'prices': [{'id': 1133, 'hourlyRecurringFee': 0.0, 'recurringFee': 0.0, 'categories': [{'id': 3, 'name': 'RAM', 'categoryCode': 'ram'}]}], }, { 'id': 1240, 'keyName': 'KeyName014', 'capacity': '4', 'units': 'PRIVATE_CORE', 'description': 'Computing Instance (Dedicated)', 'itemCategory': {'categoryCode': 'Computing Instance'}, 'prices': [{'id': 1007, 'hourlyRecurringFee': 0.0, 'recurringFee': 0.0, 'categories': [{'id': 80, 'name': 'Computing Instance', 'categoryCode': 'guest_core'}]}], }, { 'id': 1250, 'keyName': 'KeyName015', 'capacity': '4', 'units': 'CORE', 'description': 'Computing Instance', 'itemCategory': {'categoryCode': 'Computing Instance'}, 'prices': [{'id': 1144, 'locationGroupId': None, 'hourlyRecurringFee': 0.10, 'recurringFee': 0.10, 'categories': [{'id': 80, 'name': 'Computing Instance', 'categoryCode': 'guest_core'}]}], }, { 'id': 112233, 'keyName': 'KeyName016', 'capacity': '55', 'units': 'CORE', 'description': 'Computing Instance', 'itemCategory': {'categoryCode': 'Computing Instance'}, 'prices': [{'id': 332211, 'locationGroupId': 1, 'hourlyRecurringFee': 0.0, 'recurringFee': 0.0, 'categories': [{'id': 80, 'name': 'Computing Instance', 'categoryCode': 'guest_core'}]}], }, { 'id': 4439, 'keyName': 'KeyName017', 'capacity': '1', 'description': '1 GB iSCSI Storage', 'itemCategory': {'categoryCode': 'iscsi'}, 'prices': [{'id': 2222, 'hourlyRecurringFee': 0.10, 'recurringFee': 0.10}], }, { 'id': 1121, 'keyName': 'KeyName081', 'capacity': '20', 'description': '20 GB iSCSI snapshot', 'itemCategory': {'categoryCode': 'iscsi_snapshot_space'}, 'prices': [{'id': 2014, 'hourlyRecurringFee': 0.10}], }, { 'id': 4440, 'keyName': 'KeyName019', 'capacity': '4', 'description': '4 Portable Public IP Addresses', 'itemCategory': {'categoryCode': 'sov_sec_ip_addresses_pub'}, 'prices': [{'id': 4444, 'hourlyRecurringFee': 0.10, 'recurringFee': 0.10}], }, { 'id': 8880, 'keyName': 'KeyName0199', 'capacity': '8', 'description': '8 Portable Public IP Addresses', 'itemCategory': {'categoryCode': 'sov_sec_ip_addresses_pub'}, 'prices': [{'id': 8888, 'hourlyRecurringFee': 0.10, 'recurringFee': 0.10}], }, { 'id': 44400, 'keyName': 'KeyName0155', 'capacity': '4', 'description': '4 Portable Private IP Addresses', 'itemCategory': {'categoryCode': 'sov_sec_ip_addresses_priv'}, 'prices': [{'id': 44441, 'hourlyRecurringFee': 0.10, 'recurringFee': 0.10}], }, { 'id': 88800, 'keyName': 'KeyName0144', 'capacity': '8', 'description': '8 Portable Private IP Addresses', 'itemCategory': {'categoryCode': 'sov_sec_ip_addresses_priv'}, 'prices': [{'id': 88881, 'hourlyRecurringFee': 0.0, 'recurringFee': 0.0}], }, { 'id': 10, 'keyName': 'KeyName0341', 'capacity': '0', 'description': 'Global IPv4', 'itemCategory': {'categoryCode': 'global_ipv4'}, 'prices': [{'id': 11, 'hourlyRecurringFee': 0.0, 'recurringFee': 0.0}], }, { 'id': 66464, 'keyName': '1_IPV6_ADDRESS', 'capacity': '64', 'description': '/64 Block Portable Public IPv6 Addresses', 'itemCategory': {'categoryCode': 'static_ipv6_addresses'}, 'prices': [{'id': 664641, 'hourlyRecurringFee': '0', 'locationGroupId': '', 'recurringFee': '0'}], }, { 'id': 610, 'keyName': 'KeyName031', 'capacity': '0', 'description': 'Global IPv6', 'itemCategory': {'categoryCode': 'global_ipv6'}, 'prices': [{'id': 611, 'hourlyRecurringFee': 0.10, 'recurringFee': 0.10}], }, {'attributes': [], 'capacity': '0', 'description': '0 GB Bandwidth', 'itemCategory': {'categoryCode': 'bandwidth', 'id': 10}, 'keyName': 'BANDWIDTH_0_GB_2', 'prices': [{'accountRestrictions': [], 'currentPriceFlag': '', 'hourlyRecurringFee': '0', 'id': 1800, "locationGroupId": '', 'itemId': 439, 'laborFee': '0', 'onSaleFlag': '', 'oneTimeFee': '0', 'quantity': '', 'setupFee': '0', 'sort': 99}]}, {'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, "locationGroupId": '', '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.10', 'id': 37650, "locationGroupId": '', 'itemId': 4702, 'laborFee': '0', 'onSaleFlag': '', 'oneTimeFee': '0', 'quantity': '', 'recurringFee': '0.1', 'setupFee': '0.1', 'sort': 9}], 'softwareDescription': {'id': 1362, 'longDescription': 'Ubuntu / 14.04-64', 'referenceCode': 'UBUNTU_14_64'}} ] getItemPricesISCSI = [ { '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 }] getItemsVS = [ { 'id': 1234, 'keyName': 'KeyName01', 'capacity': '1000', 'description': 'Public & Private Networks', 'itemCategory': {'categoryCode': 'Uplink Port Speeds'}, 'softwareDescription': { 'id': 1228, 'longDescription': 'Redhat EL 5.10-64', 'referenceCode': 'REDHAT_5_64' }, 'prices': [{'id': 1122, 'hourlyRecurringFee': 0.0, 'recurringFee': 0.0, 'categories': [{'id': 26, 'name': 'Uplink Port Speeds', 'categoryCode': 'port_speed'}]}], }, { 'id': 2233, 'keyName': 'KeyName02', 'capacity': '1000', 'description': 'Public & Private Networks', 'itemCategory': {'categoryCode': 'Uplink Port Speeds'}, 'prices': [{'id': 4477, 'hourlyRecurringFee': 0.0, 'recurringFee': 0.0, 'categories': [{'id': 26, 'name': 'Uplink Port Speeds', 'categoryCode': 'port_speed'}]}], }, { 'id': 1239, 'keyName': 'KeyName03', 'capacity': '2', 'description': 'RAM', 'itemCategory': {'categoryCode': 'RAM'}, 'prices': [{'id': 1133, 'hourlyRecurringFee': 0.0, 'recurringFee': 0.0, 'categories': [{'id': 3, 'name': 'RAM', 'categoryCode': 'ram'}]}], } ] 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 } itemsLoadbal = [ { "capacity": "0", "description": "Load Balancer as a Service", "id": 10043, "keyName": "LOAD_BALANCER_AS_A_SERVICE", "itemCategory": { "categoryCode": "load_balancer_as_a_service", "id": 1116, "name": "Load Balancer As A Service", }, "prices": [ { "hourlyRecurringFee": "0", "id": 199447, "locationGroupId": '', "recurringFee": "0", } ] }, { "capacity": "0", "description": "Load Balancer Uptime", "id": 10785, "keyName": "LOAD_BALANCER_UPTIME", "itemCategory": { "categoryCode": "load_balancer_uptime", "id": 1119, "name": "Load Balancer Uptime", }, "prices": [ { "hourlyRecurringFee": ".028", "id": 205913, "locationGroupId": 507, }]} ] regionsLoadbal = [{'description': 'WDC01 - Washington, DC - East Coast U.S.', 'keyname': 'WASHINGTON_DC', 'location': {'location': {'id': 37473, 'longName': 'Washington 1', 'name': 'wdc01', "groups": [ { "description": "Location Group 4", "id": 507, "locationGroupTypeId": 82, "name": "Location Group 4", "locationGroupType": { "name": "PRICING" } }, { "description": "COS Cross Region - EU", "id": 1303, "locationGroupTypeId": 82, "name": "eu", "locationGroupType": { "name": "PRICING" } }, { "description": "COS Regional Frankfurt", "id": 1783, "locationGroupTypeId": 82, "name": "eu-de", "locationGroupType": { "name": "PRICING" } } ] }}, 'sortOrder': 10}] getAllObjectsLoadbal = [ { "id": 805, "keyName": "LBAAS", "name": "Load Balancer As A Service (LBaaS)", "items": itemsLoadbal, "regions": regionsLoadbal } ] getAllObjectsDH = [{ "subDescription": "Dedicated Host", "name": "Dedicated Host", "items": [{ "capacity": "56", "description": "56 Cores X 242 RAM X 1.2 TB", "bundleItems": [ { "capacity": "1200", "keyName": "1_4_TB_LOCAL_STORAGE_DEDICATED_HOST_CAPACITY", "categories": [{ "categoryCode": "dedicated_host_disk" }] }, { "capacity": "242", "keyName": "242_GB_RAM", "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" }] getAllObjectsDHGpu = [{ "subDescription": "Dedicated Host", "name": "Dedicated Host", "items": [{ "capacity": "56", "description": "56 Cores x 360 RAM x 1.2 TB x 2 GPU P100 [encryption enabled]", "bundleItems": [ { "capacity": "1200", "keyName": "1.2 TB Local Storage (Dedicated Host Capacity)", "categories": [{ "categoryCode": "dedicated_host_disk" }] }, { "capacity": "242", "keyName": "2_GPU_P100_DEDICATED", "hardwareGenericComponentModel": { "capacity": "16", "id": 849, "hardwareComponentType": { "id": 20, "keyName": "GPU" } }, "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_484_RAM_X_1_5_TB_X_2_GPU_P100", "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", "location": { "locationId": 2017603, "location": { "id": 2017603, "longName": "Washington 7", "name": "wdc07", "priceGroups": [ { "description": "COS Regional - US East", "id": 1305, "locationGroupTypeId": 82, "name": "us-east", "locationGroupType": { "name": "PRICING" } } ] } }, "locations": [{ "location": { "euCompliantFlag": False, "id": 2017603, "longName": "Washington 7", "name": "wdc07", "statusId": 2}, "locationPackageDetails": [{ "isAvailable": 1, "locationId": 2017603, "packageId": 46 }] }] }] getItemPrices = [ { "hourlyRecurringFee": ".093", "id": 204015, "recurringFee": "62", "categories": [ { "categoryCode": "guest_core" } ], "item": { "description": "4 x 2.0 GHz or higher Cores", "id": 859, "keyName": "GUEST_CORES_4", }, "pricingLocationGroup": { "id": 503, "locations": [ { "id": 449610, "longName": "Montreal 1", "name": "mon01", "statusId": 2, "regions": [ { "description": "MON01 - Montreal", "keyname": "MONTREAL", "sortOrder": 94 } ] }, { "id": 449618, "longName": "Montreal 2", "name": "mon02", "statusId": 2 }, { "id": 448994, "longName": "Toronto 1", "name": "tor01", "statusId": 2 }, { "id": 350993, "longName": "Toronto 2", "name": "tor02", "statusId": 2 }, { "id": 221894, "longName": "Amsterdam 2", "name": "ams02", "statusId": 2, "regions": [ { "description": "AMS02 POP - Amsterdam", "keyname": "AMSTERDAM02", "sortOrder": 12 } ] }, { "id": 265592, "longName": "Amsterdam 1", "name": "ams01", "statusId": 2 }, { "id": 814994, "longName": "Amsterdam 3", "name": "ams03", "statusId": 2 } ] } }, { "hourlyRecurringFee": ".006", "id": 204663, "recurringFee": "4.1", "item": { "description": "100 GB (LOCAL)", "id": 3899, "keyName": "GUEST_DISK_100_GB_LOCAL_3", }, "pricingLocationGroup": { "id": 503, "locations": [ { "id": 449610, "longName": "Montreal 1", "name": "mon01", "statusId": 2 }, { "id": 449618, "longName": "Montreal 2", "name": "mon02", "statusId": 2 }, { "id": 448994, "longName": "Toronto 1", "name": "tor01", "statusId": 2 }, { "id": 350993, "longName": "Toronto 2", "name": "tor02", "statusId": 2 }, { "id": 221894, "longName": "Amsterdam 2", "name": "ams02", "statusId": 2 }, { "id": 265592, "longName": "Amsterdam 1", "name": "ams01", "statusId": 2 }, { "id": 814994, "longName": "Amsterdam 3", "name": "ams03", "statusId": 2 } ] } }, { "hourlyRecurringFee": ".217", "id": 204255, "recurringFee": "144", "item": { "description": "16 GB ", "id": 1017, "keyName": "RAM_16_GB", }, "pricingLocationGroup": { "id": 503, "locations": [ { "id": 449610, "longName": "Montreal 1", "name": "mon01", "statusId": 2 }, { "id": 449618, "longName": "Montreal 2", "name": "mon02", "statusId": 2 }, { "id": 448994, "longName": "Toronto 1", "name": "tor01", "statusId": 2 }, { "id": 350993, "longName": "Toronto 2", "name": "tor02", "statusId": 2 }, { "id": 221894, "longName": "Amsterdam 2", "name": "ams02", "statusId": 2 }, { "id": 265592, "longName": "Amsterdam 1", "name": "ams01", "statusId": 2 }, { "id": 814994, "longName": "Amsterdam 3", "name": "ams03", "statusId": 2 } ] } } ] getActivePresets = [ { "description": "M1.64x512x25", "id": 799, "isActive": "1", "keyName": "M1_64X512X25", "name": "M1.64x512x25", "packageId": 835 }, { "description": "M1.56x448x100", "id": 797, "isActive": "1", "keyName": "M1_56X448X100", "name": "M1.56x448x100", "packageId": 835 }, { "description": "M1.64x512x100", "id": 801, "isActive": "1", "keyName": "M1_64X512X100", "name": "M1.64x512x100", "packageId": 835 } ] getAccountRestrictedActivePresets = [] RESERVED_CAPACITY = [{"id": 1059}] getItems_RESERVED_CAPACITY = [ { 'id': 12273, 'keyName': 'B1_1X2_1_YEAR_TERM', 'description': 'B1 1x2 1 year term', 'capacity': 12, 'itemCategory': { 'categoryCode': 'reserved_capacity', 'id': 2060, 'name': 'Reserved Capacity', 'quantityLimit': 20, 'sortOrder': '' }, 'prices': [ { 'currentPriceFlag': '', 'hourlyRecurringFee': '.032', 'id': 217561, 'itemId': 12273, 'laborFee': '0', 'locationGroupId': '', 'onSaleFlag': '', 'oneTimeFee': '0', 'quantity': '', 'setupFee': '0', 'sort': 0, 'tierMinimumThreshold': '', 'categories': [ { 'categoryCode': 'reserved_capacity', 'id': 2060, 'name': 'Reserved Capacity', 'quantityLimit': 20, 'sortOrder': '' } ] } ] } ] getItems_1_IPV6_ADDRESS = [ { 'id': 4097, 'keyName': '1_IPV6_ADDRESS', 'itemCategory': { 'categoryCode': 'pri_ipv6_addresses', 'id': 325, 'name': 'Primary IPv6 Addresses', 'quantityLimit': 0, 'sortOrder': 34 }, 'prices': [ { 'currentPriceFlag': '', 'hourlyRecurringFee': '0', 'id': 17129, 'itemId': 4097, 'laborFee': '0', 'locationGroupId': '', 'onSaleFlag': '', 'oneTimeFee': '0', 'quantity': '', 'recurringFee': '0', 'setupFee': '0', 'sort': 0, 'tierMinimumThreshold': '', 'categories': [ { 'categoryCode': 'pri_ipv6_addresses', 'id': 325, 'name': 'Primary IPv6 Addresses', 'quantityLimit': 0, 'sortOrder': 34 } ] } ] } ] getObject = { 'id': 200, 'regions': [{'description': 'WDC01 - Washington, DC - East Coast U.S.', 'keyname': 'WASHINGTON_DC', 'location': {'location': {'id': 37473, 'longName': 'Washington 1', 'name': 'wdc01'}}, 'sortOrder': 10}], 'accountRestrictedActivePresets': [], 'activePresets': [ { 'description': 'AC2.8x60x25', 'id': 861, 'isActive': '1', 'keyName': 'AC2_8X60X25', 'name': 'AC2.8x60x25', 'packageId': 835 }, { 'description': 'AC2.8x60x100', 'id': 863, 'isActive': '1', 'keyName': 'AC2_8X60X100', 'name': 'AC2.8x60x100', 'packageId': 835 }], "items": [{ "capacity": "56", "description": "56 Cores x 360 RAM x 1.2 TB x 2 GPU P100 [encryption enabled]", "bundleItems": [ { "capacity": "1200", "keyName": "1.2 TB Local Storage (Dedicated Host Capacity)", "categories": [{ "categoryCode": "dedicated_host_disk" }] }, { "capacity": "242", "keyName": "2_GPU_P100_DEDICATED", "hardwareGenericComponentModel": { "capacity": "16", "id": 849, "hardwareComponentType": { "id": 20, "keyName": "GPU" } }, "categories": [{ "categoryCode": "dedicated_host_ram" }, { "capacity": "2", "description": "2 x 2.0 GHz or higher Cores", "keyName": "GUEST_CORES_2", "attributes": [ { "id": 8261, "attributeTypeKeyName": "ORDER_SAVES_USAGE_FEES" } ], "itemCategory": { "categoryCode": "guest_core", "id": 80 }}] } ], "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_484_RAM_X_1_5_TB_X_2_GPU_P100", "id": 10195, "itemCategory": { "categoryCode": "dedicated_virtual_hosts" } }]} getItems_vmware = [{ "capacity": "2", "description": "VMware vSAN Enterprise Tier III 65 - 124 TB 6.x", "id": 9567, "itemTaxCategoryId": 166, "keyName": "VMWARE_VSAN_ENTERPRISE_TIER_III_65_124_TB_6_X_2", "softwareDescriptionId": 1979, "units": "CPU", "itemCategory": { "categoryCode": "software_license", "id": 438, "name": "Software License", "quantityLimit": 1, }, "prices": [ { "id": 245164, "itemId": 9567, "laborFee": "0", "locationGroupId": None, "recurringFee": "0", "setupFee": "0", "sort": 0, } ]}] getItemsVLAN = [{ "description": "Private Network Vlan", "id": 1072, "itemTaxCategoryId": 166, "keyName": "PRIVATE_NETWORK_VLAN", "itemCategory": { "categoryCode": "network_vlan", "id": 113, "name": "Network Vlan"}, "prices": [{ "id": 203707, "itemId": 1072, "laborFee": "0", "locationGroupId": 505, "oneTimeFee": "0", "recurringFee": "0", "setupFee": "0", "sort": 10, }, { "id": 203727, "itemId": 1072, "laborFee": "0", "locationGroupId": 545, "oneTimeFee": "0", "recurringFee": "0", "setupFee": "0", "sort": 10, }] }, { "description": "Public Network Vlan", "id": 1071, "itemTaxCategoryId": 166, "keyName": "PUBLIC_NETWORK_VLAN", "units": "N/A", "itemCategory": { "categoryCode": "network_vlan", "id": 113, "name": "Network Vlan", }, "prices": [{ "id": 203637, "itemId": 1071, "laborFee": "0", "locationGroupId": 509, "oneTimeFee": "0", "recurringFee": "0", "setupFee": "0", "sort": 10, }, { "id": 203667, "itemId": 1071, "laborFee": "0", "locationGroupId": 545, "oneTimeFee": "0", "recurringFee": "0", "setupFee": "0", "sort": 10, }] } ] softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Product_Package_Preset.py000066400000000000000000000033721415376457700276140ustar00rootroot00000000000000getObject = { "id": 405, "keyName": "AC1_8X60X25", "prices": [ { "hourlyRecurringFee": "1.425", "id": 207345, "recurringFee": "936.23", "item": { "description": "1 x P100 GPU", "id": 10933, "keyName": "1_X_P100_GPU", "itemCategory": { "categoryCode": "guest_pcie_device0", "id": 1259 } } }, { "hourlyRecurringFee": "0", "id": 2202, "recurringFee": "0", "item": { "description": "25 GB (SAN)", "id": 1178, "keyName": "GUEST_DISK_25_GB_SAN", "itemCategory": { "categoryCode": "guest_disk0", "id": 81 } } }, { "hourlyRecurringFee": ".342", "id": 207361, "recurringFee": "224.69", "item": { "description": "60 GB", "id": 10939, "keyName": "RAM_0_UNIT_PLACEHOLDER_10", "itemCategory": { "categoryCode": "ram", "id": 3 } } }, { "hourlyRecurringFee": ".181", "id": 209595, "recurringFee": "118.26", "item": { "capacity": 8, "description": "8 x 2.0 GHz or higher Cores", "id": 11307, "keyName": "GUEST_CORE_8", "itemCategory": { "categoryCode": "guest_core", "id": 80 } } } ] } softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Provisioning_Maintenance_Window.py000066400000000000000000000012341415376457700315510ustar00rootroot00000000000000getMaintenanceWindows = [ { "beginDate": "2021-05-13T16:00:00-06:00", "dayOfWeek": 4, "endDate": "2021-05-13T19:00:00-06:00", "id": 198351, "locationId": 1441195, "portalTzId": 114 }, { "beginDat": "2021-05-14T00:00:00-06:00", "dayOfWeek": 5, "endDate": "2021-05-14T03:00:00-06:00", "id": 189747, "locationId": 1441195, "portalTzId": 114 }, { "beginDate": "2021-05-14T08:00:00-06:00", "dayOfWeek": 5, "endDate": "2021-05-14T11:00:00-06:00", "id": 198355, "locationId": 1441195, "portalTzId": 114 } ] softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Resource_Metadata.py000066400000000000000000000004221415376457700266170ustar00rootroot00000000000000getFrontendMacAddresses = ['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.9.8/SoftLayer/fixtures/SoftLayer_Scale_Group.py000066400000000000000000000332571415376457700254470ustar00rootroot00000000000000getObject = { 'accountId': 31111, 'balancedTerminationFlag': False, 'cooldown': 1800, 'createDate': '2018-04-30T15:07:40-04:00', 'desiredMemberCount': None, 'id': 12222222, 'lastActionDate': '2019-10-02T16:26:17-04:00', 'loadBalancers': [], 'maximumMemberCount': 6, 'minimumMemberCount': 2, 'modifyDate': '2019-10-03T17:16:47-04:00', 'name': 'tests', 'networkVlans': [ { 'networkVlan': { 'accountId': 31111, 'id': 2222222, 'modifyDate': '2019-07-16T13:09:47-04:00', 'networkSpace': 'PRIVATE', 'primaryRouter': { 'hostname': 'bcr01a.sao01' }, 'primarySubnetId': 1172222, 'vlanNumber': 1111 }, 'networkVlanId': 2281111 } ], 'policies': [ { 'actions': [ { 'amount': 1, 'createDate': '2019-09-26T18:30:22-04:00', 'deleteFlag': None, 'id': 611111, 'modifyDate': None, 'scalePolicy': None, 'scalePolicyId': 681111, 'scaleType': 'RELATIVE', 'typeId': 1 } ], 'cooldown': None, 'createDate': '2019-09-26T18:30:14-04:00', 'id': 680000, 'name': 'prime-poly', 'scaleActions': [ { 'amount': 1, 'createDate': '2019-09-26T18:30:22-04:00', 'deleteFlag': None, 'id': 633333, 'modifyDate': None, 'scalePolicy': None, 'scalePolicyId': 680123, 'scaleType': 'RELATIVE', 'typeId': 1 } ], 'triggers': [ { 'createDate': '2019-09-26T18:30:14-04:00', 'deleteFlag': None, 'id': 557111, 'modifyDate': None, 'scalePolicy': None, 'scalePolicyId': 680000, 'typeId': 3 } ] } ], 'regionalGroup': { 'description': 'sa-bra-south-1', 'id': 663, 'locationGroupTypeId': 42, 'locations': [ { 'id': 983497, 'longName': 'Sao Paulo 1', 'name': 'sao01', 'statusId': 2 } ], 'name': 'sa-bra-south-1', 'securityLevelId': None }, 'regionalGroupId': 663, 'status': { 'id': 1, 'keyName': 'ACTIVE', 'name': 'Active' }, 'suspendedFlag': False, 'terminationPolicy': { 'id': 2, 'keyName': 'NEWEST', 'name': 'Newest' }, 'terminationPolicyId': 2, 'virtualGuestAssets': [], 'virtualGuestMemberCount': 6, 'virtualGuestMemberTemplate': { 'accountId': 31111, 'blockDevices': [ { 'bootableFlag': None, 'createDate': None, 'device': '0', 'diskImage': { 'capacity': 25, 'createDate': None, 'id': None, 'modifyDate': None, 'parentId': None, 'storageRepositoryId': None, 'typeId': None}, 'diskImageId': None, 'guestId': None, 'hotPlugFlag': None, 'id': None, 'modifyDate': None, 'statusId': None }, { 'bootableFlag': None, 'createDate': None, 'device': '2', 'diskImage': { 'capacity': 10, 'createDate': None, 'id': None, 'modifyDate': None, 'parentId': None, 'storageRepositoryId': None, 'typeId': None}, 'diskImageId': None, 'guestId': None, 'hotPlugFlag': None, 'id': None, 'modifyDate': None, 'statusId': None } ], 'createDate': None, 'datacenter': { 'id': None, 'name': 'sao01', 'statusId': None }, 'dedicatedAccountHostOnlyFlag': None, 'domain': 'tech-support.com', 'hostname': 'testing', 'hourlyBillingFlag': True, 'id': None, 'lastPowerStateId': None, 'lastVerifiedDate': None, 'localDiskFlag': False, 'maxCpu': None, 'maxMemory': 1024, 'metricPollDate': None, 'modifyDate': None, 'networkComponents': [ { 'createDate': None, 'guestId': None, 'id': None, 'maxSpeed': 100, 'modifyDate': None, 'networkId': None, 'port': None, 'speed': None } ], 'operatingSystemReferenceCode': 'CENTOS_LATEST', 'placementGroupId': None, 'postInstallScriptUri': 'https://test.com/', 'privateNetworkOnlyFlag': False, 'provisionDate': None, 'sshKeys': [ { 'createDate': None, 'id': 490279, 'modifyDate': None } ], 'startCpus': 1, 'statusId': None, 'typeId': None}, 'virtualGuestMembers': [ { 'id': 3111111, 'virtualGuest': { 'domain': 'tech-support.com', 'hostname': 'test', 'provisionDate': '2019-09-27T14:29:53-04:00' } } ] } getVirtualGuestMembers = getObject['virtualGuestMembers'] scale = [ { "accountId": 31111, "cooldown": 1800, "createDate": "2016-10-25T01:48:34+08:00", "id": 12222222, "maximumMemberCount": 5, "minimumMemberCount": 0, "name": "tests", "virtualGuest": { "accountId": 31111, "createDate": "2019-10-02T15:24:54-06:00", "billingItem": { "cancellationDate": "2019-10-02T08:34:21-06:00"} }, "virtualGuestMemberTemplate": { "accountId": 31111, "domain": "sodg.com", "hostname": "testing", "maxMemory": 32768, "startCpus": 32, "blockDevices": [ { "device": "0", "diskImage": { "capacity": 25, } } ], "datacenter": { "name": "sao01", }, "hourlyBillingFlag": True, "operatingSystemReferenceCode": "CENTOS_LATEST", "privateNetworkOnlyFlag": True }, "virtualGuestMemberCount": 0, "status": { "id": 1, "keyName": "ACTIVE", "name": "Active" }, "virtualGuestAssets": [], "virtualGuestMembers": [] }, { "accountId": 31111, "cooldown": 1800, "createDate": "2018-04-24T04:22:00+08:00", "id": 224533333, "maximumMemberCount": 10, "minimumMemberCount": 0, "modifyDate": "2019-01-19T04:53:21+08:00", "name": "test-ajcb", "virtualGuest": { "accountId": 31111, "createDate": "2019-10-02T15:24:54-06:00", "billingItem": { "cancellationDate": "2019-10-02T08:34:21-06:00"} }, "virtualGuestMemberTemplate": { "accountId": 31111, "domain": "test.local", "hostname": "autoscale-ajcb01", "id": None, "maxCpu": None, "maxMemory": 1024, "postInstallScriptUri": "http://test.com", "startCpus": 1, "blockDevices": [ { "device": "0", "diskImage": { "capacity": 25, } } ], "datacenter": { "name": "seo01", }, "hourlyBillingFlag": True, "operatingSystemReferenceCode": "CENTOS_7_64", }, "virtualGuestMemberCount": 0, "status": { "id": 1, "keyName": "ACTIVE", "name": "Active" }, "virtualGuestAssets": [], "virtualGuestMembers": [] } ] scaleTo = [ { "accountId": 31111, "cooldown": 1800, "createDate": "2016-10-25T01:48:34+08:00", "id": 12222222, "lastActionDate": "2016-10-25T01:48:34+08:00", "maximumMemberCount": 5, "minimumMemberCount": 0, "name": "tests", "regionalGroupId": 663, "virtualGuest": { }, "virtualGuestMemberTemplate": { "accountId": 31111, "domain": "sodg.com", "hostname": "testing", "id": None, "maxCpu": None, "maxMemory": 32768, "startCpus": 32, "datacenter": { "name": "sao01", }, "hourlyBillingFlag": True, "operatingSystemReferenceCode": "CENTOS_LATEST", "privateNetworkOnlyFlag": True }, "virtualGuestMemberCount": 0, "status": { "id": 1, "keyName": "ACTIVE", "name": "Active" }, "virtualGuestAssets": [], "virtualGuestMembers": [] }, { "accountId": 31111, "cooldown": 1800, "createDate": "2018-04-24T04:22:00+08:00", "id": 224533333, "lastActionDate": "2019-01-19T04:53:18+08:00", "maximumMemberCount": 10, "minimumMemberCount": 0, "modifyDate": "2019-01-19T04:53:21+08:00", "name": "test-ajcb", "regionalGroupId": 1025, "virtualGuest": { "accountId": 31111, "createDate": "2019-10-02T15:24:54-06:00", "billingItem": { "cancellationDate": "2019-10-02T08:34:21-06:00"} }, "virtualGuestMemberTemplate": { "accountId": 31111, "domain": "test.local", "hostname": "autoscale-ajcb01", "id": None, "maxCpu": None, "maxMemory": 1024, "postInstallScriptUri": "http://test.com", "startCpus": 1, "blockDevices": [ { "device": "0", "diskImage": { "capacity": 25, } } ], "datacenter": { "name": "seo01", }, "hourlyBillingFlag": True, "operatingSystemReferenceCode": "CENTOS_7_64", }, "virtualGuestMemberCount": 0, "status": { "id": 1, "keyName": "ACTIVE", "name": "Active" }, "virtualGuestAssets": [], "virtualGuestMembers": [] }, ] getLogs = [ { "createDate": "2019-10-03T04:26:11+08:00", "description": "Scaling group to 6 member(s) by adding 3 member(s) as manually requested", "id": 3821111, "scaleGroupId": 2252222, "scaleGroup": { "accountId": 31111, "cooldown": 1800, "createDate": "2018-05-01T03:07:40+08:00", "id": 2251111, "lastActionDate": "2019-10-03T04:26:17+08:00", "maximumMemberCount": 6, "minimumMemberCount": 2, "modifyDate": "2019-10-03T04:26:21+08:00", "name": "ajcb-autoscale11", "regionalGroupId": 663, "terminationPolicyId": 2, "virtualGuestMemberTemplate": { "accountId": 31111, "domain": "techsupport.com", "hostname": "ajcb-autoscale22", "maxMemory": 1024, "postInstallScriptUri": "https://pastebin.com/raw/62wrEKuW", "startCpus": 1, "blockDevices": [ { "device": "0", "diskImage": { "capacity": 25, } }, { "device": "2", "diskImage": { "capacity": 10, } } ], "datacenter": { "name": "sao01", }, "networkComponents": [ { "maxSpeed": 100, } ], "operatingSystemReferenceCode": "CENTOS_LATEST", "sshKeys": [ { "id": 49111, } ] }, "logs": [ { "createDate": "2019-09-28T02:31:35+08:00", "description": "Scaling group to 3 member(s) by removing -1 member(s) as manually requested", "id": 3821111, "scaleGroupId": 2251111, }, { "createDate": "2019-09-28T02:26:11+08:00", "description": "Scaling group to 4 member(s) by adding 2 member(s) as manually requested", "id": 38211111, "scaleGroupId": 2251111, }, ] } }, ] editObject = True softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Scale_Policy.py000066400000000000000000000032271415376457700256040ustar00rootroot00000000000000getObject = { "cooldown": None, "createDate": "2019-09-27T06:30:14+08:00", "id": 11111, "name": "prime-poly", "scaleGroupId": 2255111, "scaleGroup": { "accountId": 31111, "cooldown": 1800, "createDate": "2018-05-01T03:07:40+08:00", "id": 2252222, "lastActionDate": "2019-09-28T02:31:47+08:00", "maximumMemberCount": 6, "minimumMemberCount": 2, "modifyDate": "2019-09-28T02:31:50+08:00", "name": "ajcb-autoscale11", "regionalGroupId": 663, "terminationPolicyId": 2, "virtualGuestMemberTemplate": { "accountId": 31111, "domain": "techsupport.com", "hostname": "ajcb-autoscale22", "id": None, "maxMemory": 1024, "postInstallScriptUri": "https://pastebin.com/raw/62wrEKuW", "startCpus": 1, "blockDevices": [ { "device": "0", "diskImage": { "capacity": 25, } }, { "device": "2", "diskImage": { "capacity": 10, } } ], "datacenter": { "id": None, "name": "sao01", }, "networkComponents": [ { "maxSpeed": 100, } ], "operatingSystemReferenceCode": "CENTOS_LATEST", "sshKeys": [ { "id": 490279, } ] } } } softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Search.py000066400000000000000000000013341415376457700244400ustar00rootroot00000000000000advancedSearch = [ { "relevanceScore": "4", "resourceType": "SoftLayer_Hardware", "resource": { "accountId": 307608, "domain": "vmware.test.com", "fullyQualifiedDomainName": "host14.vmware.test.com", "hardwareStatusId": 5, "hostname": "host14", "id": 123456, "manufacturerSerialNumber": "AAAAAAAAA", "notes": "A test notes", "provisionDate": "2018-08-24T12:32:10-06:00", "serialNumber": "SL12345678", "serviceProviderId": 1, "hardwareStatus": { "id": 5, "status": "ACTIVE" } } } ] softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Security_Certificate.py000066400000000000000000000017751415376457700273550ustar00rootroot00000000000000getObject = { "certificate": "-----BEGIN CERTIFICATE----- \nMIIEJTCCAw2gAwIBAgIDCbQ0MA0GCSqGSIb3DQEBCwUAMEcxCzAJBgNVBAYTAlVT" " -----END CERTIFICATE-----", "certificateSigningRequest": "-----BEGIN CERTIFICATE REQUEST-----\n" "MIIC1jCCAb4CAQAwgZAxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVUZXhh123456QMA4G\n" "-----END CERTIFICATE REQUEST-----", "commonName": "techbabble.xyz", "createDate": "2016-01-20T10:56:44-06:00", "id": 123456, "intermediateCertificate": "", "keySize": 258369, "modifyDate": "2019-06-05T14:10:40-06:00", "organizationName": "Unspecified", "privateKey": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA3SwTZ7sh7we5zIbmtSbxGJxff07eutrK12345678WXtwQSdE\n" "-----END RSA PRIVATE KEY-----", "validityBegin": "2016-01-19T17:59:37-06:00", "validityDays": 0, "validityEnd": "2017-01-20T13:48:41-06:00" } createObject = {} editObject = True deleteObject = True softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Security_Ssh_Key.py000066400000000000000000000004341415376457700264670ustar00rootroot00000000000000deleteObject = 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 getAllObjects = [getObject] softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Software_AccountLicense.py000066400000000000000000000035341415376457700300100ustar00rootroot00000000000000getAllObjects = [{ "capacity": "4", "key": "ABCDE-6CJ8L-J8R9H-000R0-CDR70", "units": "CPU", "billingItem": { "allowCancellationFlag": 1, "categoryCode": "software_license", "createDate": "2018-10-22T11:16:48-06:00", "cycleStartDate": "2021-06-03T23:11:22-06:00", "description": "vCenter Server Appliance 6.0", "id": 123654789, "lastBillDate": "2021-06-03T23:11:22-06:00", "modifyDate": "2021-06-03T23:11:22-06:00", "nextBillDate": "2021-07-03T23:00:00-06:00", "orderItemId": 385054741, "recurringMonths": 1, "serviceProviderId": 1, }, "softwareDescription": { "id": 1529, "longDescription": "VMware vCenter 6.0", "manufacturer": "VMware", "name": "vCenter", "version": "6.0", "requiredUser": "administrator@vsphere.local" } }, { "capacity": "1", "key": "CBERT-4RL92-K8999-031K4-AJF5J", "units": "CPU", "billingItem": { "allowCancellationFlag": 1, "categoryCode": "software_license", "createDate": "2021-06-09T14:51:38-06:00", "cycleStartDate": "2021-06-09T14:51:38-06:00", "description": "VMware vSAN Advanced Tier III 64 - 124 TB 6.x", "id": 369852174, "nextBillDate": "2021-07-03T23:00:00-06:00", "orderItemId": 836502628, "recurringMonths": 1, "serviceProviderId": 1, }, "softwareDescription": { "id": 1795, "longDescription": "VMware Virtual SAN Advanced Tier III 6.2", "manufacturer": "VMware", "name": "Virtual SAN Advanced Tier III", "version": "6.2", } }] softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Tag.py000066400000000000000000000014141415376457700237450ustar00rootroot00000000000000getUnattachedTagsForCurrentUser = [{'id': 287895, 'name': 'coreos', 'referenceCount': 0}] getAttachedTagsForCurrentUser = [{'id': 1286571, 'name': 'bs_test_instance', 'referenceCount': 5}] getReferences = [ { 'id': 73009305, 'resourceTableId': 33488921, 'tag': { 'id': 1286571, 'name': 'bs_test_instance', }, 'tagId': 1286571, 'tagType': {'description': 'CCI', 'keyName': 'GUEST'}, 'tagTypeId': 2, 'usrRecordId': 6625205 } ] deleteTag = True setTags = True getObject = getAttachedTagsForCurrentUser[0] getTagByTagName = getAttachedTagsForCurrentUser getAllTagTypes = [ { "description": "Hardware", "keyName": "HARDWARE" } ] softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Ticket.py000066400000000000000000000032271415376457700244610ustar00rootroot00000000000000createCancelServerTicket = {'id': 1234, 'title': 'Server Cancellation Request'} getObject = { "accountId": 1234, "serviceProviderResourceId": "CS123456", "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, "serviceProviderResourceId": "CS123456", "id": 100, "contents": "body", "subjectId": 1004, "title": "Cloud Instance Cancellation - 08/01/13" } edit = True addUpdate = {} gatList = getObject 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.9.8/SoftLayer/fixtures/SoftLayer_Ticket_Subject.py000066400000000000000000000005511415376457700261350ustar00rootroot00000000000000getAllObjects = [ { "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.9.8/SoftLayer/fixtures/SoftLayer_User_Customer.py000066400000000000000000000044241415376457700260350ustar00rootroot00000000000000getObject = { 'accountId': 12345, 'address1': '315 Test Street', 'apiAuthenticationKeys': [{'authenticationKey': 'aaaaaaaaaaaaaaaaaaaaaaaaa'}], 'city': 'Houston', 'companyName': 'SoftLayer Development Community', 'country': 'US', 'createDate': '2014-08-18T12:58:02-06:00', 'displayName': 'Test', 'email': 'test@us.ibm.com', 'firstName': 'Test', 'id': 244956, 'isMasterUserFlag': False, 'lastName': 'Testerson', 'openIdConnectUserName': 'test@us.ibm.com', 'parent': { 'id': 167758, 'username': 'SL12345'}, 'parentId': 167758, 'postalCode': '77002', 'sslVpnAllowedFlag': True, 'state': 'TX', 'statusDate': None, 'successfulLogins': [ {'createDate': '2018-05-08T15:28:32-06:00', 'ipAddress': '175.125.126.118', 'successFlag': True, 'userId': 244956}, ], 'timezone': { 'id': 113, 'longName': '(GMT-06:00) America/Chicago - CST', 'name': 'America/Chicago', 'offset': '-0600', 'shortName': 'CST'}, 'timezoneId': 113, 'unsuccessfulLogins': [ {'createDate': '2018-02-09T14:13:15-06:00', 'ipAddress': '73.136.219.36', 'successFlag': False, 'userId': 244956}, ], 'userStatus': {'name': 'Active'}, 'userStatusId': 1001, 'username': 'SL12345-test', 'vpnManualConfig': False, 'permissions': [ {'key': 'ALL_1', 'keyName': 'ACCESS_ALL_HARDWARE', 'name': 'All Hardware Access'} ], 'roles': [] } getPermissions = [ {'key': 'ALL_1', 'keyName': 'ACCESS_ALL_HARDWARE', 'name': 'All Hardware Access'}, {'key': 'A_1', 'keyName': 'ACCOUNT_SUMMARY_VIEW', 'name': 'View Account Summary'}, {'key': 'A_10', 'keyName': 'ADD_SERVICE_STORAGE', 'name': 'Add/Upgrade Storage (StorageLayer)'} ] getLoginAttempts = [ { "createDate": "2017-10-03T09:28:33-06:00", "ipAddress": "1.2.3.4", "successFlag": False, "userId": 1111, "username": "sl1234" } ] getOverrides = [ { 'id': 3661234, 'subnetId': 1234 } ] addBulkPortalPermission = True removeBulkPortalPermission = True createObject = getObject editObject = True addApiAuthenticationKey = True updateVpnUser = True softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_User_Customer_CustomerPermission_Permission.py000066400000000000000000000016211415376457700341730ustar00rootroot00000000000000getAllObjects = [ { "key": "T_1", "keyName": "TICKET_VIEW", "name": "View Tickets" }, { "key": "T_2", "keyName": "TEST", "name": "A Testing Permission" }, { "key": "T_3", "keyName": "TEST_3", "name": "A Testing Permission 3" }, { "key": "T_4", "keyName": "TEST_4", "name": "A Testing Permission 4" }, { "key": "T_5", "keyName": "ACCESS_ALL_HARDWARE", "name": "A Testing Permission 5" }, { 'key': 'ALL_1', 'keyName': 'ACCESS_ALL_HARDWARE', 'name': 'All Hardware Access' }, { 'key': 'A_1', 'keyName': 'ACCOUNT_SUMMARY_VIEW', 'name': 'View Account Summary' }, { 'key': 'A_10', 'keyName': 'ADD_SERVICE_STORAGE', 'name': 'Add/Upgrade Storage (StorageLayer)' } ] softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Virtual_DedicatedHost.py000066400000000000000000000073051415376457700274510ustar00rootroot00000000000000getObject = { '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': 12345}, {'hostname': 'bcr02a.dal05', 'id': 12346}, {'hostname': 'bcr03a.dal05', 'id': 12347}, {'hostname': 'bcr04a.dal05', 'id': 12348} ] getObjectById = { 'datacenter': { 'id': 12345, 'name': 'dal05', 'longName': 'Dallas 5' }, 'memoryCapacity': 242, 'modifyDate': '2017-11-06T11:38:20-06:00', 'name': 'test-dedicated', 'diskCapacity': 1200, 'backendRouter': { 'domain': 'test.com', 'hostname': 'bcr01a.dal05', 'id': 12345 }, 'guestCount': 1, 'cpuCount': 56, 'guests': [{ 'domain': 'test.com', 'hostname': 'test-dedicated', 'id': 12345, 'uuid': 'F9329795-4220-4B0A-B970-C86B950667FA' }], 'billingItem': { 'nextInvoiceTotalRecurringAmount': 1515.556, 'orderItem': { 'id': 12345, 'order': { 'status': 'APPROVED', 'privateCloudOrderFlag': False, 'modifyDate': '2017-11-02T11:42:50-07:00', 'orderQuoteId': '', 'userRecordId': 12345, 'createDate': '2017-11-02T11:40:56-07:00', 'impersonatingUserRecordId': '', 'orderTypeId': 7, 'presaleEventId': '', 'userRecord': { 'username': 'test-dedicated' }, 'id': 12345, 'accountId': 12345 } }, 'id': 12345, 'children': [ { 'nextInvoiceTotalRecurringAmount': 0.0, 'categoryCode': 'dedicated_host_ram' }, { 'nextInvoiceTotalRecurringAmount': 0.0, 'categoryCode': 'dedicated_host_disk' } ] }, 'id': 12345, 'createDate': '2017-11-02T11:40:56-07:00' } deleteObject = True getGuests = [{ 'id': 200, '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': 202, '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', } } } } }] softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Virtual_Guest.py000066400000000000000000000622461415376457700260410ustar00rootroot00000000000000getObject = { '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': [ { 'categoryCode': 'ram', 'description': '1 GB', 'nextInvoiceTotalRecurringAmount': 1 }, { 'categoryCode': 'remote_management', 'description': 'Reboot / Remote Console', 'nextInvoiceTotalRecurringAmount': 1 }, { 'categoryCode': 'port_speed', 'description': '1 Gbps Public & Private Network Uplinks', 'nextInvoiceTotalRecurringAmount': 1 }, { 'categoryCode': 'public_port', 'description': '1 Gbps Public Uplink', 'nextInvoiceTotalRecurringAmount': 1 }, { 'categoryCode': 'service_port', 'description': '1 Gbps Private Uplink', 'nextInvoiceTotalRecurringAmount': 1 } ], 'package': { "id": 835, "keyName": "PUBLIC_CLOUD_SERVER" }, 'orderItem': { 'order': { 'userRecord': { 'username': 'chechu', } }, 'preset': {'keyName': 'B1_8X16X100'} } }, '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}, 'transientGuestFlag': False, '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', 'name': 'B1-1X2X25' }, 'template': { 'supplementalCreateObjectOptions': { 'flavorKeyName': 'B1_1X2X25' } } }, { 'flavor': { 'keyName': 'B1_1X2X25_TRANSIENT', 'name': 'B1-1X2X25_TRANSIENT' }, 'template': { 'supplementalCreateObjectOptions': { 'flavorKeyName': 'B1_1X2X25_TRANSIENT' }, 'transientGuestFlag': True } }, { 'flavor': { 'keyName': 'B1_1X2X100', 'name': 'B1-1X2X100' }, 'template': { 'supplementalCreateObjectOptions': { 'flavorKeyName': 'B1_1X2X100' } } }, { 'flavor': { 'keyName': 'BL1_1X2X100', 'name': 'BL1_1X2X100' }, 'template': { 'supplementalCreateObjectOptions': { 'flavorKeyName': 'BL1_1X2X100' } } }, { 'flavor': { 'keyName': 'BL2_1X2X100', 'name': 'BL2-1X2X100' }, 'template': { 'supplementalCreateObjectOptions': { 'flavorKeyName': 'BL2_1X2X100' } } }, { 'flavor': { 'keyName': 'C1_1X2X25', 'name': 'C1-1X2X25' }, 'template': { 'supplementalCreateObjectOptions': { 'flavorKeyName': 'C1_1X2X25' } } }, { 'flavor': { 'keyName': 'M1_1X2X100', 'name': 'M1_1X2X100' }, 'template': { 'supplementalCreateObjectOptions': { 'flavorKeyName': 'M1_1X2X100' } } }, { 'flavor': { 'keyName': 'AC1_1X2X100', 'name': 'AC1_1X2X100' }, 'template': { 'supplementalCreateObjectOptions': { 'flavorKeyName': 'AC1_1X2X100' } } }, { 'flavor': { 'keyName': 'ACL1_1X2X100', 'name': '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': [ { "description": "1 GB ", "attributes": [], "itemCategory": { "categoryCode": "ram", "id": 4 }, 'itemPrice': { 'item': {'description': '1 GB'}, 'hourlyRecurringFee': '.03', 'recurringFee': '21' }, 'template': {'maxMemory': 1024} }, { "description": "2 GB ", "attributes": [], "itemCategory": { "categoryCode": "ram", "id": 5 }, 'itemPrice': { 'item': {'description': '2 GB'}, 'hourlyRecurringFee': '.06', 'recurringFee': '42' }, 'template': {'maxMemory': 2048} }, { "description": "3 GB", "attributes": [], "itemCategory": { "categoryCode": "ram", "id": 65 }, 'itemPrice': { 'item': {'description': '3 GB'}, 'hourlyRecurringFee': '.085', 'recurringFee': '59.5'}, 'template': {'maxMemory': 3072} }, { "description": "4 GB", "attributes": [], "itemCategory": { "categoryCode": "ram", "id": 35 }, 'itemPrice': { 'item': {'description': '4 GB'}, 'hourlyRecurringFee': '.11', 'recurringFee': '77' }, 'template': {'maxMemory': 4096} }, { "description": "64 GB (Dedicated Host)", "attributes": [], "itemCategory": { "categoryCode": "ram", "id": 3 }, 'itemPrice': { 'hourlyRecurringFee': '0', 'recurringFee': '0', 'dedicatedHostInstanceFlag': True, 'item': { 'description': '64 GB (Dedicated Host)' } }, 'template': { 'maxMemory': 65536 } }, { "description": "8 GB (Dedicated Host)", "attributes": [], "itemCategory": { "categoryCode": "ram", "id": 36 }, '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'}}}, ], 'guest_disk': [{ "description": "25 GB (SAN)", "attributes": [ { "id": 197, "attributeTypeKeyName": "SAN_DISK" } ], "itemCategory": { "categoryCode": "guest_disk0", "id": 81 }}, { "description": "250 GB (SAN)", "attributes": [ { "id": 198, "attributeTypeKeyName": "SAN_DISK" }], "itemCategory": { "categoryCode": "guest_disk0", "id": 89 }}], 'guest_core': [{ "description": "4 x 2.0 GHz or higher Cores (Dedicated)", "attributes": [], "itemCategory": { "categoryCode": "guest_core", "id": 80 }}, { "description": "8 x 2.0 GHz or higher Cores", "attributes": [], "itemCategory": { "categoryCode": "guest_core", "id": 90 }}] } 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 = { "imageTemplateId": None, "location": "1854895", "packageId": 835, "presetId": 405, "prices": [ { "hourlyRecurringFee": "0", "id": 45466, "recurringFee": "0", "item": { "description": "CentOS 7.x - Minimal Install (64 bit)" } }, { "hourlyRecurringFee": "0", "id": 2202, "recurringFee": "0", "item": { "description": "25 GB (SAN)" } }, { "hourlyRecurringFee": "0", "id": 905, "recurringFee": "0", "item": { "description": "Reboot / Remote Console" } }, { "hourlyRecurringFee": ".02", "id": 899, "recurringFee": "10", "item": { "description": "1 Gbps Private Network Uplink" } }, { "hourlyRecurringFee": "0", "id": 1800, "item": { "description": "0 GB Bandwidth Allotment" } }, { "hourlyRecurringFee": "0", "id": 21, "recurringFee": "0", "item": { "description": "1 IP Address" } }, { "hourlyRecurringFee": "0", "id": 55, "recurringFee": "0", "item": { "description": "Host Ping" } }, { "hourlyRecurringFee": "0", "id": 57, "recurringFee": "0", "item": { "description": "Email and Ticket" } }, { "hourlyRecurringFee": "0", "id": 58, "recurringFee": "0", "item": { "description": "Automated Notification" } }, { "hourlyRecurringFee": "0", "id": 420, "recurringFee": "0", "item": { "description": "Unlimited SSL VPN Users & 1 PPTP VPN User per account" } }, ], "quantity": 1, "sourceVirtualGuestId": None, "sshKeys": [], "useHourlyPricing": True, "virtualGuests": [ { "domain": "test.local", "hostname": "test" } ], "complexType": "SoftLayer_Container_Product_Order_Virtual_Guest" } setUserMetadata = ['meta'] reloadOperatingSystem = 'OK' setTags = True createArchiveTransaction = { 'createDate': '2018-12-10T17:29:18-06:00', 'elapsedSeconds': 0, 'guestId': 12345678, 'hardwareId': None, 'id': 12345, 'modifyDate': '2018-12-10T17:29:18-06:00', 'statusChangeDate': '2018-12-10T17:29:18-06:00' } 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', } }, { "id": 2255, "categories": [ { "categoryCode": "guest_disk1", "id": 82, "name": "Second Disk" }, { "categoryCode": "guest_disk2", "id": 92, "name": "Third Disk" }, { "categoryCode": "guest_disk3", "id": 93, "name": "Fourth Disk" }, { "categoryCode": "guest_disk4", "id": 116, "name": "Fifth Disk" } ], "item": { "capacity": "10", "description": "10 GB (SAN)" } } ] 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)', } }, ] getMetricTrackingObjectId = 1000 getBandwidthAllotmentDetail = { 'allocationId': 25465663, 'bandwidthAllotmentId': 138442, 'effectiveDate': '2019-04-03T23:00:00-06:00', 'endEffectiveDate': None, 'id': 25888247, 'serviceProviderId': 1, 'allocation': { 'amount': '250' } } getBillingCycleBandwidthUsage = [ { 'amountIn': '.448', 'amountOut': '.52157', 'type': { 'alias': 'PUBLIC_SERVER_BW' } }, { 'amountIn': '.03842', 'amountOut': '.01822', 'type': { 'alias': 'PRIVATE_SERVER_BW' } } ] getAttachedNetworkStorages = [ { "accountId": 11111, "capacityGb": 20, "createDate": "2018-04-05T05:15:49-06:00", "id": 22222, "nasType": "NAS", "serviceProviderId": 1, "storageTypeId": "13", "username": "SL02SEV311111_11", "allowedVirtualGuests": [ { "id": 12345, "datacenter": { "id": 449506, "longName": "Frankfurt 2", "name": "fra02", "statusId": 2 } } ], "serviceResourceBackendIpAddress": "fsn-fra0201a-fz.service.softlayer.com", "serviceResourceName": "Storage Type 02 File Aggregate stfm-fra0201a" }, { "accountId": 11111, "capacityGb": 12000, "createDate": "2018-01-28T04:57:30-06:00", "id": 3777111, "nasType": "ISCSI", "notes": "BlockStorage12T", "password": "", "serviceProviderId": 1, "storageTypeId": "7", "username": "SL02SEL32222-9", "allowedVirtualGuests": [ { "id": 629222, "datacenter": { "id": 449506, "longName": "Frankfurt 2", "name": "fra02", "statusId": 2 } } ], "serviceResourceBackendIpAddress": "10.31.95.152", "serviceResourceName": "Storage Type 02 Block Aggregate stbm-fra0201a" } ] getAllowedHost = { "accountId": 11111, "credentialId": 22222, "id": 33333, "name": "iqn.2020-03.com.ibm:sl02su11111-v62941551", "resourceTableId": 6291111, "resourceTableName": "VIRTUAL_GUEST", "credential": { "accountId": "11111", "createDate": "2020-03-20T13:35:47-06:00", "id": 44444, "nasCredentialTypeId": 2, "password": "SjFDCpHrjskfj", "username": "SL02SU11111-V62941551" } } getBlockDevices = [ { "createDate": "2018-10-06T04:27:35-06:00", "device": "0", "id": 11111, "mountType": "Disk", "diskImage": { "capacity": 100, "description": "adns.vmware.com", "id": 72222, "name": "adns.vmware.com", "units": "GB", } }, { "device": "1", "id": 22222, "mountType": "Disk", "statusId": 1, "diskImage": { "capacity": 2, "description": "6211111-SWAP", "id": 33333, "name": "6211111-SWAP", "units": "GB", } } ] migrate = True migrateDedicatedHost = True allowAccessToNetworkStorageList = True attachDiskImage = { "createDate": "2021-03-22T13:15:31-06:00", "id": 1234567 } softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Virtual_Guest_Block_Device_Template_Group.py000066400000000000000000000026051415376457700334320ustar00rootroot00000000000000IMAGES = [{ '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, 'children': [{ 'datacenter': { 'name': 'ams01' } }], }, { '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, 'children': [{ 'datacenter': { 'name': 'ams01' } }], }] 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', } createFromIcos = { 'createDate': '2013-12-05T21:53:03-06:00', 'globalIdentifier': '0B5DEAF4-643D-46CA-A695-CECBE8832C9D', 'id': 100, 'name': 'test_image', } copyToExternalSource = True copyToIcos = True addLocations = True removeLocations = True getStorageLocations = [ {'id': 265592, 'longName': 'Amsterdam 1', 'name': 'ams01', 'statusId': 2}, {'id': 814994, 'longName': 'Amsterdam 3', 'name': 'ams03', 'statusId': 2}, ] softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Virtual_Host.py000066400000000000000000000017351415376457700256630ustar00rootroot00000000000000getGuests = [ { "accountId": 11111, "createDate": "2019-09-05T17:03:42-06:00", "fullyQualifiedDomainName": "NSX-T Manager", "hostname": "NSX-T Manager", "id": 22222, "maxCpu": 16, "maxCpuUnits": "CORE", "maxMemory": 49152, "startCpus": 16, "powerState": { "keyName": "RUNNING", "name": "Running" }, "status": { "keyName": "ACTIVE", "name": "Active" } }, { "accountId": 11111, "createDate": "2019-09-23T06:00:53-06:00", "hostname": "NSX-T Manager2", "id": 33333, "maxCpu": 12, "maxCpuUnits": "CORE", "maxMemory": 49152, "startCpus": 12, "statusId": 1001, "powerState": { "keyName": "RUNNING", "name": "Running" }, "status": { "keyName": "ACTIVE", "name": "Active" } } ] softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Virtual_PlacementGroup.py000066400000000000000000000031531415376457700276670ustar00rootroot00000000000000getAvailableRouters = [{ "accountId": 1, "fullyQualifiedDomainName": "bcr01.dal01.softlayer.com", "hostname": "bcr01.dal01", "id": 1, "topLevelLocation": { "id": 3, "longName": "Dallas 1", "name": "dal01", } }] createObject = { "accountId": 123, "backendRouterId": 444, "createDate": "2019-01-18T16:08:44-06:00", "id": 5555, "modifyDate": None, "name": "test01", "ruleId": 1 } getObject = { "createDate": "2019-01-17T14:36:42-06:00", "id": 1234, "name": "test-group", "backendRouter": { "hostname": "bcr01a.mex01", "id": 329266 }, "guests": [{ "accountId": 123456789, "createDate": "2019-01-17T16:44:46-06:00", "domain": "test.com", "fullyQualifiedDomainName": "issues10691547765077.test.com", "hostname": "issues10691547765077", "id": 69131875, "maxCpu": 1, "maxMemory": 1024, "placementGroupId": 1234, "provisionDate": "2019-01-17T16:47:17-06:00", "activeTransaction": { "id": 107585077, "transactionStatus": { "friendlyName": "TESTING TXN", "name": "RECLAIM_WAIT" } }, "globalIdentifier": "c786ac04-b612-4649-9d19-9662434eeaea", "primaryBackendIpAddress": "10.131.11.14", "primaryIpAddress": "169.57.70.180", "status": { "keyName": "DISCONNECTED", "name": "Disconnected" } }], "rule": { "id": 1, "keyName": "SPREAD", "name": "SPREAD" } } deleteObject = True softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Virtual_PlacementGroup_Rule.py000066400000000000000000000001471415376457700306560ustar00rootroot00000000000000getAllObjects = [ { "id": 1, "keyName": "SPREAD", "name": "SPREAD" } ] softlayer-python-5.9.8/SoftLayer/fixtures/SoftLayer_Virtual_ReservedCapacityGroup.py000066400000000000000000000044051415376457700312150ustar00rootroot00000000000000getObject = { 'accountId': 1234, 'backendRouterId': 1411193, 'backendRouter': { 'fullyQualifiedDomainName': 'bcr02a.dal13.softlayer.com', 'hostname': 'bcr02a.dal13', 'id': 1411193, 'datacenter': { 'id': 1854895, 'longName': 'Dallas 13', 'name': 'dal13', } }, 'createDate': '2018-09-24T16:33:09-06:00', 'id': 3103, 'modifyDate': '', 'name': 'test-capacity', 'instances': [ { 'createDate': '2018-09-24T16:33:09-06:00', 'guestId': 62159257, 'id': 3501, 'billingItem': { 'id': 348319479, 'recurringFee': '3.04', 'category': {'name': 'Reserved Capacity'}, 'item': { 'keyName': 'B1_1X2_1_YEAR_TERM' } }, 'guest': { 'domain': 'cgallo.com', 'hostname': 'test-reserved-instance', 'id': 62159257, 'modifyDate': '2018-09-27T16:49:26-06:00', 'primaryBackendIpAddress': '10.73.150.179', 'primaryIpAddress': '169.62.147.165' } }, { 'createDate': '2018-09-24T16:33:10-06:00', 'guestId': 62159275, 'id': 3519, 'billingItem': { 'id': 348319443, 'recurringFee': '3.04', 'category': { 'name': 'Reserved Capacity' }, 'item': { 'keyName': 'B1_1X2_1_YEAR_TERM' } } } ] } getObject_pending = { 'accountId': 1234, 'backendRouterId': 1411193, 'backendRouter': { 'fullyQualifiedDomainName': 'bcr02a.dal13.softlayer.com', 'hostname': 'bcr02a.dal13', 'id': 1411193, 'datacenter': { 'id': 1854895, 'longName': 'Dallas 13', 'name': 'dal13', } }, 'createDate': '2018-09-24T16:33:09-06:00', 'id': 3103, 'modifyDate': '', 'name': 'test-capacity', 'instances': [ { 'createDate': '2018-09-24T16:33:09-06:00', 'guestId': 62159257, 'id': 3501, } ] } softlayer-python-5.9.8/SoftLayer/fixtures/__init__.py000066400000000000000000000000001415376457700227670ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/fixtures/empty.conf000066400000000000000000000000001415376457700226630ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/fixtures/full.conf000066400000000000000000000001151415376457700224760ustar00rootroot00000000000000[softlayer] username=myusername api_key=myapi_key endpoint_url=myendpoint_urlsoftlayer-python-5.9.8/SoftLayer/fixtures/id_rsa.pub000066400000000000000000000000471415376457700226420ustar00rootroot00000000000000ssh-rsa AAAAB3N...pa67 user@example.comsoftlayer-python-5.9.8/SoftLayer/fixtures/no_options.conf000066400000000000000000000000131415376457700237200ustar00rootroot00000000000000[softlayer]softlayer-python-5.9.8/SoftLayer/fixtures/realtest.com000066400000000000000000000015261415376457700232170ustar00rootroot00000000000000$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.9.8/SoftLayer/fixtures/sample_vs_template.conf000066400000000000000000000002271415376457700254240ustar00rootroot00000000000000hostname = 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.9.8/SoftLayer/managers/000077500000000000000000000000001415376457700206145ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/managers/__init__.py000066400000000000000000000042621415376457700227310ustar00rootroot00000000000000""" 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.account import AccountManager 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.event_log import EventLogManager 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.license import LicensesManager from SoftLayer.managers.load_balancer import LoadBalancerManager 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.tags import TagManager from SoftLayer.managers.ticket import TicketManager from SoftLayer.managers.user import UserManager from SoftLayer.managers.vs import VSManager from SoftLayer.managers.vs_capacity import CapacityManager from SoftLayer.managers.vs_placement import PlacementManager __all__ = [ 'AccountManager', 'BlockStorageManager', 'CapacityManager', 'CDNManager', 'DedicatedHostManager', 'DNSManager', 'EventLogManager', 'FileStorageManager', 'FirewallManager', 'HardwareManager', 'ImageManager', 'IPSECManager', 'LicensesManager', 'LoadBalancerManager', 'MetadataManager', 'NetworkManager', 'ObjectStorageManager', 'OrderingManager', 'PlacementManager', 'SshKeyManager', 'SSLManager', 'TagManager', 'TicketManager', 'UserManager', 'VSManager', ] softlayer-python-5.9.8/SoftLayer/managers/account.py000066400000000000000000000255051415376457700226310ustar00rootroot00000000000000""" SoftLayer.account ~~~~~~~~~~~~~~~~~~~~~~~ Account manager :license: MIT, see License for more details. """ import logging from SoftLayer.exceptions import SoftLayerAPIError 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 AccountManager(utils.IdentifierMixin, object): """Common functions for getting information from the Account service :param SoftLayer.API.BaseClient client: the client instance """ _DEFAULT_BILLING_ITEM_MASK = """mask[ orderItem[id,order[id,userRecord[id,email,displayName,userStatus]]], nextInvoiceTotalRecurringAmount, location, hourlyFlag, children ]""" def __init__(self, client): self.client = client def get_summary(self): """Gets some basic account information :return: Account object """ mask = """mask[ nextInvoiceTotalAmount, pendingInvoice[invoiceTotalAmount], blockDeviceTemplateGroupCount, dedicatedHostCount, domainCount, hardwareCount, networkStorageCount, openTicketCount, networkVlanCount, subnetCount, userCount, virtualGuestCount ] """ return self.client.call('Account', 'getObject', mask=mask) def get_upcoming_events(self, event_type): """Retrieves a list of Notification_Occurrence_Events that have not ended yet :param: String event_type: notification event type. :return: SoftLayer_Notification_Occurrence_Event """ mask = "mask[id, subject, startDate, endDate, modifyDate, statusCode, acknowledgedFlag, " \ "impactedResourceCount, updateCount, systemTicketId, notificationOccurrenceEventType[keyName]]" _filter = { 'notificationOccurrenceEventType': { 'keyName': { 'operation': event_type } } } self.add_event_filter(_filter, event_type) return self.client.call('Notification_Occurrence_Event', 'getAllObjects', filter=_filter, mask=mask, iter=True) @staticmethod def add_event_filter(_filter, event_type): """Add data to the object filter. :param: _filter: event filter. :param: string event_type: event type. """ if event_type == 'PLANNED': _filter['endDate'] = { 'operation': '> sysdate - 2' } _filter['startDate'] = { 'operation': 'orderBy', 'options': [{ 'name': 'sort', 'value': ['DESC'] }] } if event_type == 'UNPLANNED_INCIDENT': _filter['modifyDate'] = { 'operation': '> sysdate - 2' } if event_type == 'ANNOUNCEMENT': _filter['statusCode'] = { 'keyName': { 'operation': 'in', 'options': [{ 'name': 'data', 'value': ['PUBLISHED'] }] } } def ack_event(self, event_id): """Acknowledge an event. This mostly prevents it from appearing as a notification in the control portal. :param int event_id: Notification_Occurrence_Event ID you want to ack :return: True on success, Exception otherwise. """ return self.client.call('Notification_Occurrence_Event', 'acknowledgeNotification', id=event_id) def get_event(self, event_id): """Gets details about a maintenance event :param int event_id: Notification_Occurrence_Event ID :return: Notification_Occurrence_Event """ mask = """mask[ acknowledgedFlag, attachments, impactedResources, statusCode, updates, notificationOccurrenceEventType] """ return self.client.call('Notification_Occurrence_Event', 'getObject', id=event_id, mask=mask) def get_invoices(self, limit=50, closed=False, get_all=False): """Gets an accounts invoices. :param int limit: Number of invoices to get back in a single call. :param bool closed: If True, will also get CLOSED invoices :param bool get_all: If True, will paginate through invoices until all have been retrieved. :return: Billing_Invoice """ mask = "mask[invoiceTotalAmount, itemCount]" _filter = { 'invoices': { 'createDate': { 'operation': 'orderBy', 'options': [{ 'name': 'sort', 'value': ['DESC'] }] }, 'statusCode': {'operation': 'OPEN'}, } } if closed: del _filter['invoices']['statusCode'] return self.client.call('Account', 'getInvoices', mask=mask, filter=_filter, iter=get_all, limit=limit) def get_billing_items(self, identifier): """Gets all topLevelBillingItems from a specific invoice :param int identifier: Invoice Id :return: Billing_Invoice_Item """ mask = """mask[ id, description, hostName, domainName, oneTimeAfterTaxAmount, recurringAfterTaxAmount, createDate, categoryCode, category[name], location[name], children[id, category[name], description, oneTimeAfterTaxAmount, recurringAfterTaxAmount] ]""" return self.client.call( 'Billing_Invoice', 'getInvoiceTopLevelItems', id=identifier, mask=mask, iter=True, limit=100 ) def get_account_billing_items(self, mask=None): """Gets all the topLevelBillingItems currently active on the account :param string mask: Object Mask :return: Billing_Item """ if mask is None: mask = """mask[ orderItem[id,order[id,userRecord[id,email,displayName,userStatus]]], nextInvoiceTotalRecurringAmount, location, hourlyFlag ]""" object_filter = { "allTopLevelBillingItems": { "cancellationDate": { "operation": "is null" }, "createDate": utils.query_filter_orderby() } } return self.client.call('Account', 'getAllTopLevelBillingItems', mask=mask, filter=object_filter, iter=True, limit=100) def get_billing_item(self, identifier, mask=None): """Gets details about a billing item :param int identifier: Billing_Item id :param string mask: Object mask to use. :return: Billing_Item """ if mask is None: mask = self._DEFAULT_BILLING_ITEM_MASK return self.client.call('Billing_Item', 'getObject', id=identifier, mask=mask) def get_billing_item_from_invoice(self, identifier, mask=None): """Gets details about a billing item of a billing invoice item :param int identifier: Billing_Invoice_Item id :param mask: Object mask to use. :return: Billing_Item """ if mask is None: mask = self._DEFAULT_BILLING_ITEM_MASK return self.client.call('Billing_Invoice_Item', 'getBillingItem', id=identifier, mask=mask) def get_item_detail(self, identifier): """Gets details about a billing item :param int identifier: Billing_Item id or Billing_Invoice_Item :return: Billing_Item """ try: return self.get_billing_item(identifier) except SoftLayerAPIError as exception: if exception.faultCode == 404: return self.get_billing_item_from_invoice(identifier) raise def cancel_item(self, identifier, reason="No longer needed", note=None): """Cancels a specific billing item with a reason :param int identifier: Billing_Item id :param string reason: A cancellation reason :param string note: Custom note to set when cancelling. Defaults to information about who canceled the item. :return: bool """ if note is None: user = self.client.call('Account', 'getCurrentUser', mask="mask[id,displayName,email,username]") note = "Cancelled by {} with the SLCLI".format(user.get('username')) return self.client.call('Billing_Item', 'cancelItem', False, True, reason, note, id=identifier) def get_account_all_billing_orders(self, limit=100, mask=None): """Gets all the topLevelBillingItems currently active on the account :param string mask: Object Mask :return: Billing_Item """ if mask is None: mask = """ orderTotalAmount, userRecord, initialInvoice[id,amount,invoiceTotalAmount], items[description] """ return self.client.call('Billing_Order', 'getAllObjects', limit=limit, mask=mask) def get_routers(self, location=None, mask=None): """Gets all the routers currently active on the account :param string mask: Object Mask :param string location: location string :returns: Routers """ object_filter = '' if location: object_filter = { 'routers': { 'topLevelLocation': {'name': {'operation': location}} } } return self.client['SoftLayer_Account'].getRouters(filter=object_filter, mask=mask) def get_network_message_delivery_accounts(self): """Gets all Network Message delivery accounts. :returns: Network Message delivery accounts """ _mask = """vendor,type""" return self.client['SoftLayer_Account'].getNetworkMessageDeliveryAccounts(mask=_mask) def get_active_virtual_licenses(self): """Gets all active virtual licenses account. :returns: active virtual licenses account """ _mask = """billingItem[categoryCode,createDate,description], key,id,ipAddress, softwareDescription[longDescription,name,manufacturer], subnet""" return self.client['SoftLayer_Account'].getActiveVirtualLicenses(mask=_mask) def get_active_account_licenses(self): """Gets all active account licenses. :returns: Active account Licenses """ _mask = """billingItem,softwareDescription""" return self.client['SoftLayer_Account'].getActiveAccountLicenses(mask=_mask) softlayer-python-5.9.8/SoftLayer/managers/autoscale.py000066400000000000000000000114631415376457700231530ustar00rootroot00000000000000""" SoftLayer.autoscale ~~~~~~~~~~~~~~~~~~~ Autoscale manager :license: MIT, see LICENSE for more details. """ class AutoScaleManager(object): """Manager for interacting with Autoscale instances.""" def __init__(self, client): self.client = client def list(self, mask=None): """Calls `SoftLayer_Account::getScaleGroups()`_ :param mask: optional SoftLayer_Scale_Group objectMask .. _SoftLayer_Account::getScaleGroups(): https://sldn.softlayer.com/reference/services/SoftLayer_Account/getScaleGroups/ """ if not mask: mask = "mask[status,virtualGuestMemberCount]" return self.client.call('SoftLayer_Account', 'getScaleGroups', mask=mask, iter=True) def details(self, identifier, mask=None): """Calls `SoftLayer_Scale_Group::getObject()`_ :param identifier: SoftLayer_Scale_Group id :param mask: optional SoftLayer_Scale_Group objectMask .. _SoftLayer_Scale_Group::getObject(): https://sldn.softlayer.com/reference/services/SoftLayer_Scale_Group/getObject/ """ if not mask: mask = """mask[virtualGuestMembers[id,virtualGuest[id,hostname,domain,provisionDate]], terminationPolicy, virtualGuestMemberCount, virtualGuestMemberTemplate[sshKeys], policies[id,name,createDate,cooldown,actions,triggers,scaleActions], networkVlans[networkVlanId,networkVlan[networkSpace,primaryRouter[hostname]]], loadBalancers, regionalGroup[locations]]""" return self.client.call('SoftLayer_Scale_Group', 'getObject', id=identifier, mask=mask) def get_policy(self, identifier, mask=None): """Calls `SoftLayer_Scale_Policy::getObject()`_ :param identifier: SoftLayer_Scale_Policy id :param mask: optional SoftLayer_Scale_Policy objectMask .. _SoftLayer_Scale_Policy::getObject(): https://sldn.softlayer.com/reference/services/SoftLayer_Scale_Policy/getObject/ """ if not mask: mask = """mask[cooldown, createDate, id, name, actions, triggers[type] ]""" return self.client.call('SoftLayer_Scale_Policy', 'getObject', id=identifier, mask=mask) def scale(self, identifier, amount): """Calls `SoftLayer_Scale_Group::scale()`_ :param identifier: SoftLayer_Scale_Group Id :param amount: positive or negative number to scale the group by .. _SoftLayer_Scale_Group::scale(): https://sldn.softlayer.com/reference/services/SoftLayer_Scale_Group/scale/ """ return self.client.call('SoftLayer_Scale_Group', 'scale', amount, id=identifier) def scale_to(self, identifier, amount): """Calls `SoftLayer_Scale_Group::scaleTo()`_ :param identifier: SoftLayer_Scale_Group Id :param amount: number to scale the group to. .. _SoftLayer_Scale_Group::scaleTo(): https://sldn.softlayer.com/reference/services/SoftLayer_Scale_Group/scaleTo/ """ return self.client.call('SoftLayer_Scale_Group', 'scaleTo', amount, id=identifier) def get_logs(self, identifier, mask=None, object_filter=None): """Calls `SoftLayer_Scale_Group::getLogs()`_ :param identifier: SoftLayer_Scale_Group Id :param mask: optional SoftLayer_Scale_Group_Log objectMask :param object_filter: optional SoftLayer_Scale_Group_Log objectFilter .. _SoftLayer_Scale_Group::getLogs(): https://sldn.softlayer.com/reference/services/SoftLayer_Scale_Group/getLogs/ """ return self.client.call('SoftLayer_Scale_Group', 'getLogs', id=identifier, mask=mask, filter=object_filter, iter=True) def get_virtual_guests(self, identifier, mask=None): """Calls `SoftLayer_Scale_Group::getVirtualGuestMembers()`_ :param identifier: SoftLayer_Scale_Group Id :param mask: optional SoftLayer_Scale_Member objectMask .. _SoftLayer_Scale_Group::getVirtualGuestMembers(): https://sldn.softlayer.com/reference/services/SoftLayer_Scale_Group/getVirtualGuestMembers/ """ return self.client.call('SoftLayer_Scale_Group', 'getVirtualGuestMembers', id=identifier, mask=mask, iter=True) def edit(self, identifier, template): """Calls `SoftLayer_Scale_Group::editObject()`_ :param identifier: SoftLayer_Scale_Group id :param template: `SoftLayer_Scale_Group`_ .. _SoftLayer_Scale_Group::editObject(): https://sldn.softlayer.com/reference/services/SoftLayer_Scale_Group/editObject/ .. _SoftLayer_Scale_Group: https://sldn.softlayer.com/reference/datatypes/SoftLayer_Scale_Group/ """ return self.client.call('SoftLayer_Scale_Group', 'editObject', template, id=identifier) softlayer-python-5.9.8/SoftLayer/managers/block.py000066400000000000000000000174661415376457700222760ustar00rootroot00000000000000""" SoftLayer.block ~~~~~~~~~~~~~~~ Block Storage Manager :license: MIT, see LICENSE for more details. """ from SoftLayer.managers.storage import StorageManager from SoftLayer.managers import storage_utils from SoftLayer import utils # pylint: disable=too-many-public-methods class BlockStorageManager(StorageManager): """Manages SoftLayer Block Storage volumes. See product information here: https://www.ibm.com/cloud/block-storage """ def list_block_volume_limit(self): """Returns a list of block volume count limit. :return: Returns a list of block volume count limit. """ return self.get_volume_count_limits() def list_block_volumes(self, datacenter=None, username=None, storage_type=None, order=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 order: Volume order id. :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)) if order: _filter['iscsiNetworkStorage']['billingItem']['orderItem'][ 'order']['id'] = (utils.query_filter(order)) kwargs['filter'] = _filter.to_dict() return self.client.call('Account', 'getIscsiNetworkStorage', iter=True, **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. """ return self.get_volume_details(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. """ return self.get_volume_access_list(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. """ return self.get_volume_snapshot_list(volume_id, **kwargs) def assign_subnets_to_acl(self, access_id, subnet_ids): """Assigns subnet records to ACL for the access host. access_id is the host_id obtained by: slcli block access-list :param integer access_id: id of the access host :param list subnet_ids: The ids of the subnets to be assigned :return: Returns int array of assigned subnet ids """ return self.client.call('Network_Storage_Allowed_Host', 'assignSubnetsToAcl', subnet_ids, id=access_id) def remove_subnets_from_acl(self, access_id, subnet_ids): """Removes subnet records from ACL for the access host. access_id is the host_id obtained by: slcli block access-list :param integer access_id: id of the access host :param list subnet_ids: The ids of the subnets to be removed :return: Returns int array of removed subnet ids """ return self.client.call('Network_Storage_Allowed_Host', 'removeSubnetsFromAcl', subnet_ids, id=access_id) def get_subnets_in_acl(self, access_id): """Returns a list of subnet records for the access host. access_id is the host_id obtained by: slcli block access-list :param integer access_id: id of the access host :return: Returns an array of SoftLayer_Network_Subnet objects """ return self.client.call('Network_Storage_Allowed_Host', 'getSubnetsInAcl', id=access_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 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 """ return self.cancel_volume(volume_id, reason, immediate) 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) def _get_ids_from_username(self, username): object_mask = "mask[id]" results = self.list_block_volumes(username=username, mask=object_mask) if results: return [result['id'] for result in results] return [] softlayer-python-5.9.8/SoftLayer/managers/cdn.py000066400000000000000000000244661415376457700217460ustar00rootroot00000000000000""" SoftLayer.cdn ~~~~~~~~~~~~~ CDN Manager/helpers :license: MIT, see LICENSE for more details. """ import SoftLayer from SoftLayer import utils # pylint: disable=no-self-use,too-many-lines,too-many-instance-attributes class CDNManager(utils.IdentifierMixin, object): """Manage Content Delivery Networks in the account. See product information here: https://www.ibm.com/cloud/cdn https://cloud.ibm.com/docs/infrastructure/CDN?topic=CDN-about-content-delivery-networks-cdn- :param SoftLayer.API.BaseClient client: the client instance """ def __init__(self, client): self.client = client self._start_date = None self._end_date = None self.cdn_configuration = self.client['Network_CdnMarketplace_Configuration_Mapping'] self.cdn_path = self.client['SoftLayer_Network_CdnMarketplace_Configuration_Mapping_Path'] self.cdn_metrics = self.client['Network_CdnMarketplace_Metrics'] self.cdn_purge = self.client['SoftLayer_Network_CdnMarketplace_Configuration_Cache_Purge'] self.resolvers = [self._get_ids_from_hostname] def list_cdn(self, **kwargs): """Lists Content Delivery Networks for the active user. :param dict \\*\\*kwargs: header-level options (mask, limit, etc.) :returns: The list of CDN objects in the account """ return self.cdn_configuration.listDomainMappings(**kwargs) def get_cdn(self, unique_id, **kwargs): """Retrieves the information about the CDN account object. :param str unique_id: The unique ID associated with the CDN. :param dict \\*\\*kwargs: header-level option (mask) :returns: The CDN object """ cdn_list = self.cdn_configuration.listDomainMappingByUniqueId(unique_id, **kwargs) # The method listDomainMappingByUniqueId() returns an array but there is only 1 object return cdn_list[0] def get_origins(self, unique_id, **kwargs): """Retrieves list of origin pull mappings for a specified CDN account. :param str unique_id: The unique ID associated with the CDN. :param dict \\*\\*kwargs: header-level options (mask, limit, etc.) :returns: The list of origin paths in the CDN object. """ return self.cdn_path.listOriginPath(unique_id, **kwargs) def add_origin(self, unique_id, origin, path, origin_type="server", header=None, port=80, protocol='http', bucket_name=None, file_extensions=None, optimize_for="web", cache_query="include all"): """Creates an origin path for an existing CDN. :param str unique_id: The unique ID associated with the CDN. :param str path: relative path to the domain provided, e.g. "/articles/video" :param str origin: ip address or hostname if origin_type=server, API endpoint for your S3 object storage if origin_type=storage :param str origin_type: it can be 'server' or 'storage' types. :param str header: the edge server uses the host header to communicate with the origin. It defaults to hostname. (optional) :param int port: the http port number (default: 80) :param str protocol: the protocol of the origin (default: HTTP) :param str bucket_name: name of the available resource :param str file_extensions: file extensions that can be stored in the CDN, e.g. "jpg,png" :param str optimize_for: performance configuration, available options: web, video, and file where: - 'web' = 'General web delivery' - 'video' = 'Video on demand optimization' - 'file' = 'Large file optimization' :param str cache_query: rules with the following formats: 'include-all', 'ignore-all', 'include: space separated query-names', 'ignore: space separated query-names'.' :return: a CDN origin path object """ types = {'server': 'HOST_SERVER', 'storage': 'OBJECT_STORAGE'} performance_config = { 'web': 'General web delivery', 'video': 'Video on demand optimization', 'file': 'Large file optimization' } new_origin = { 'uniqueId': unique_id, 'path': path, 'origin': origin, 'originType': types.get(origin_type), 'httpPort': port, 'protocol': protocol.upper(), 'performanceConfiguration': performance_config.get(optimize_for, 'General web delivery'), 'cacheKeyQueryRule': cache_query } if header: new_origin['header'] = header if types.get(origin_type) == 'OBJECT_STORAGE': if bucket_name: new_origin['bucketName'] = bucket_name if file_extensions: new_origin['fileExtension'] = file_extensions origin = self.cdn_path.createOriginPath(new_origin) # The method createOriginPath() returns an array but there is only 1 object return origin[0] def remove_origin(self, unique_id, path): """Removes an origin pull mapping with the given origin pull ID. :param str unique_id: The unique ID associated with the CDN. :param str path: The origin path to delete. :returns: A string value """ return self.cdn_path.deleteOriginPath(unique_id, path) def purge_content(self, unique_id, path): """Purges a URL or path from the CDN. :param str unique_id: The unique ID associated with the CDN. :param str path: A string of url or path that should be purged. :returns: A Container_Network_CdnMarketplace_Configuration_Cache_Purge array object """ return self.cdn_purge.createPurge(unique_id, path) def get_usage_metrics(self, unique_id, history=30, frequency="aggregate"): """Retrieves the cdn usage metrics. It uses the 'days' argument if start_date and end_date are None. :param int unique_id: The CDN uniqueId from which the usage metrics will be obtained. :param int history: Last N days, default days is 30. :param str frequency: It can be day, week, month and aggregate. The default is "aggregate". :returns: A Container_Network_CdnMarketplace_Metrics object """ _start = utils.days_to_datetime(history) _end = utils.days_to_datetime(0) self._start_date = utils.timestamp(_start) self._end_date = utils.timestamp(_end) usage = self.cdn_metrics.getMappingUsageMetrics(unique_id, self._start_date, self._end_date, frequency) # The method getMappingUsageMetrics() returns an array but there is only 1 object return usage[0] @property def start_data(self): """Retrieve the cdn usage metric start date.""" return self._start_date @property def end_date(self): """Retrieve the cdn usage metric end date.""" return self._end_date def edit(self, identifier, header=None, http_port=None, origin=None, respect_headers=None, cache=None, performance_configuration=None): """Edit the cdn object. :param string identifier: The CDN identifier. :param header: The cdn Host header. :param http_port: The cdn HTTP port. :param origin: The cdn Origin server address. :param respect_headers: The cdn Respect headers. :param cache: The cdn Cache key optimization. :param performance_configuration: The cdn performance configuration. :returns: SoftLayer_Container_Network_CdnMarketplace_Configuration_Mapping[]. """ cdn_instance_detail = self.get_cdn(str(identifier)) config = { 'uniqueId': cdn_instance_detail.get('uniqueId'), 'originType': cdn_instance_detail.get('originType'), 'protocol': cdn_instance_detail.get('protocol'), 'path': cdn_instance_detail.get('path'), 'vendorName': cdn_instance_detail.get('vendorName'), 'cname': cdn_instance_detail.get('cname'), 'domain': cdn_instance_detail.get('domain'), 'httpPort': cdn_instance_detail.get('httpPort'), 'origin': cdn_instance_detail.get('originHost'), 'header': cdn_instance_detail.get('header') } if header: config['header'] = header if http_port: config['httpPort'] = http_port if origin: config['origin'] = origin if respect_headers: config['respectHeaders'] = respect_headers if cache: if 'include-specified' in cache['cacheKeyQueryRule']: cache_key_rule = self.get_cache_key_query_rule('include', cache) config['cacheKeyQueryRule'] = cache_key_rule elif 'ignore-specified' in cache['cacheKeyQueryRule']: cache_key_rule = self.get_cache_key_query_rule('ignore', cache) config['cacheKeyQueryRule'] = cache_key_rule else: config['cacheKeyQueryRule'] = cache['cacheKeyQueryRule'] if performance_configuration: config['performanceConfiguration'] = performance_configuration return self.cdn_configuration.updateDomainMapping(config) def _get_ids_from_hostname(self, hostname): """Get the cdn object detail. :param string hostname: The CDN identifier. :returns: SoftLayer_Container_Network_CdnMarketplace_Configuration_Mapping[]. """ result = [] cdn_list = self.cdn_configuration.listDomainMappings() for cdn in cdn_list: if cdn.get('domain', '').lower() == hostname.lower(): result.append(cdn.get('uniqueId')) break return result @staticmethod def get_cache_key_query_rule(cache_type, cache): """Get the cdn object detail. :param string cache_type: Cache type. :param cache: Cache description. :return: string value. """ if 'description' not in cache: raise SoftLayer.SoftLayerError('Please add a description to be able to update the' ' cache.') cache_result = '%s: %s' % (cache_type, cache['description']) return cache_result softlayer-python-5.9.8/SoftLayer/managers/dedicated_host.py000066400000000000000000000457221415376457700241430ustar00rootroot00000000000000""" SoftLayer.dedicatedhost ~~~~~~~~~~~~~~~~~~~~~~~ DH Manager/helpers :license: MIT, see License for more details. """ import logging from SoftLayer.exceptions import SoftLayerAPIError from SoftLayer.exceptions import SoftLayerError 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'] self.guest = client['Virtual_Guest'] if ordering_manager is None: self.ordering_manager = ordering.OrderingManager(client) def cancel_host(self, host_id): """Cancel a dedicated host immediately, it fails if there are still guests in the host. :param host_id: The ID of the dedicated host to be cancelled. :return: True on success or an exception Example:: # Cancels dedicated host id 12345 result = mgr.cancel_host(12345) """ return self.host.deleteObject(id=host_id) def cancel_guests(self, host_id): """Cancel all guests into the dedicated host immediately. To cancel an specified guest use the method VSManager.cancel_instance() :param host_id: The ID of the dedicated host. :return: The id, fqdn and status of all guests into a dictionary. The status could be 'Cancelled' or an exception message, The dictionary is empty if there isn't any guest in the dedicated host. Example:: # Cancel guests of dedicated host id 12345 result = mgr.cancel_guests(12345) """ result = [] guests = self.host.getGuests(id=host_id, mask='id,fullyQualifiedDomainName') if guests: for vs in guests: status_info = { 'id': vs['id'], 'fqdn': vs['fullyQualifiedDomainName'], 'status': self._delete_guest(vs['id']) } result.append(status_info) return result def list_guests(self, host_id, tags=None, cpus=None, memory=None, hostname=None, domain=None, local_disk=None, nic_speed=None, public_ip=None, private_ip=None, **kwargs): """Retrieve a list of all virtual servers on the dedicated host. Example:: # Print out a list of instances with 4 cpu cores in the host id 12345. for vsi in mgr.list_guests(host_id=12345, cpus=4): 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_guests(mask=object_mask,cpus=4): print vsi :param integer host_id: the identifier of dedicated host :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 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', 'hourlyBillingFlag', 'powerState', 'maxCpu', 'maxMemory', 'datacenter', 'activeTransaction.transactionStatus[friendlyName,name]', 'status', ] kwargs['mask'] = "mask[%s]" % ','.join(items) _filter = utils.NestedDict(kwargs.get('filter') or {}) if tags: _filter['guests']['tagReferences']['tag']['name'] = { 'operation': 'in', 'options': [{'name': 'data', 'value': tags}], } if cpus: _filter['guests']['maxCpu'] = utils.query_filter(cpus) if memory: _filter['guests']['maxMemory'] = utils.query_filter(memory) if hostname: _filter['guests']['hostname'] = utils.query_filter(hostname) if domain: _filter['guests']['domain'] = utils.query_filter(domain) if local_disk is not None: _filter['guests']['localDiskFlag'] = ( utils.query_filter(bool(local_disk))) if nic_speed: _filter['guests']['networkComponents']['maxSpeed'] = ( utils.query_filter(nic_speed)) if public_ip: _filter['guests']['primaryIpAddress'] = ( utils.query_filter(public_ip)) if private_ip: _filter['guests']['primaryBackendIpAddress'] = ( utils.query_filter(private_ip)) kwargs['filter'] = _filter.to_dict() kwargs['iter'] = True return self.host.getGuests(id=host_id, **kwargs) 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,keyName,categories[categoryCode],hardwareGenericComponentModel[id, hardwareComponentType[keyName]]] ], 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 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 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 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'] for hardwareComponent in item['bundleItems']: if hardwareComponent['keyName'].find("GPU") != -1: hardwareComponentType = hardwareComponent['hardwareGenericComponentModel']['hardwareComponentType'] gpuComponents = [ { 'hardwareComponentModel': { 'hardwareGenericComponentModel': { 'id': hardwareComponent['hardwareGenericComponentModel']['id'], 'hardwareComponentType': { 'keyName': hardwareComponentType['keyName'] } } } }, { 'hardwareComponentModel': { 'hardwareGenericComponentModel': { 'id': hardwareComponent['hardwareGenericComponentModel']['id'], 'hardwareComponentType': { 'keyName': hardwareComponentType['keyName'] } } } } ] 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 } } if item['keyName'].find("GPU") != -1: host['pciDevices'] = gpuComponents routers = self.host.getAvailableRouters(host, mask=mask) return routers raise 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 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) def _delete_guest(self, guest_id): """Deletes a guest and returns 'Cancelled' or and Exception message""" msg = 'Cancelled' try: self.guest.deleteObject(id=guest_id) except SoftLayerAPIError as e: msg = 'Exception: ' + e.faultString return msg softlayer-python-5.9.8/SoftLayer/managers/dns.py000066400000000000000000000241041415376457700217530ustar00rootroot00000000000000""" SoftLayer.dns ~~~~~~~~~~~~~ DNS Manager/helpers :license: MIT, see LICENSE for more details. """ import time from SoftLayer import exceptions from SoftLayer import utils class DNSManager(utils.IdentifierMixin, object): """Manage SoftLayer DNS. See product information here: https://www.ibm.com/cloud/dns :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. """ if kwargs.get('iter') is None: kwargs['iter'] = True return self.client.call('SoftLayer_Account', 'getDomains', **kwargs) # 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, TXT, etc.) :param data: the record's value :param integer ttl: the TTL or time-to-live value (default: 60) """ resource_record = self._generate_create_dict(record, record_type, data, ttl, domainId=zone_id) return self.record.createObject(resource_record) def create_record_mx(self, zone_id, record, data, ttl=60, priority=10): """Create a mx resource record on a domain. :param integer id: the zone's ID :param record: the name of the record to add :param data: the record's value :param integer ttl: the TTL or time-to-live value (default: 60) :param integer priority: the priority of the target host """ resource_record = self._generate_create_dict(record, 'MX', data, ttl, domainId=zone_id, mxPriority=priority) return self.record.createObject(resource_record) def create_record_srv(self, zone_id, record, data, protocol, port, service, ttl=60, priority=20, weight=10): """Create a resource record on a domain. :param integer id: the zone's ID :param record: the name of the record to add :param data: the record's value :param string protocol: the protocol of the service, usually either TCP or UDP. :param integer port: the TCP or UDP port on which the service is to be found. :param string service: the symbolic name of the desired service. :param integer ttl: the TTL or time-to-live value (default: 60) :param integer priority: the priority of the target host (default: 20) :param integer weight: relative weight for records with same priority (default: 10) """ resource_record = self._generate_create_dict(record, 'SRV', data, ttl, domainId=zone_id, priority=priority, protocol=protocol, port=port, service=service, weight=weight) # The createObject won't creates SRV records unless we send the following complexType. resource_record['complexType'] = 'SoftLayer_Dns_Domain_ResourceRecord_SrvType' return self.record.createObject(resource_record) def create_record_ptr(self, record, data, ttl=60): """Create a reverse record. :param record: the public ip address of device for which you would like to manage reverse DNS. :param data: the record's value :param integer ttl: the TTL or time-to-live value (default: 60) """ resource_record = self._generate_create_dict(record, 'PTR', data, ttl) return self.record.createObject(resource_record) @staticmethod def _generate_create_dict(record, record_type, data, ttl, **kwargs): """Returns a dict appropriate to pass into Dns_Domain_ResourceRecord::createObject""" # Basic dns record structure resource_record = { 'host': record, 'data': data, 'ttl': ttl, 'type': record_type } for (key, value) in kwargs.items(): resource_record.setdefault(key, value) return resource_record 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()) object_mask = 'id,expire,domainId,host,minimum,refresh,retry,mxPriority,ttl,type,data,responsiblePerson' results = self.client.call('SoftLayer_Dns_Domain', 'getResourceRecords', id=zone_id, mask=object_mask, filter=_filter.to_dict(), iter=True) 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 """ record.pop('isGatewayAddress', None) 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) def sync_host_record(self, zone_id, hostname, ip_address, record_type='a', ttl=7200): """For a given zone_id, will set hostname's A record to ip_address :param integer zone_id: The zone id for the domain :param string hostname: host part of the record :param string ip_address: data part of the record :param integer ttl: TTL for the record :param string record_type: 'a' or 'aaaa' """ records = self.get_records(zone_id, host=hostname, record_type=record_type) if not records: # don't have a record, lets add one to the base zone self.create_record(zone_id, hostname, record_type, ip_address, ttl=ttl) else: if len(records) != 1: raise exceptions.SoftLayerError("Aborting record sync, found %d records!" % len(records)) rec = records[0] rec['data'] = ip_address rec['ttl'] = ttl self.edit_record(rec) def sync_ptr_record(self, ptr_domains, ip_address, fqdn, ttl=7200): """Sync PTR record. :param dict ptr_domains: result from SoftLayer_Virtual_Guest.getReverseDomainRecords or SoftLayer_Hardware_Server.getReverseDomainRecords :param string ip_address: ip address to sync with :param string fqdn: Fully Qualified Domain Name :param integer ttl: TTL for the record """ host_rec = ip_address.split('.')[-1] edit_ptr = None for ptr in ptr_domains['resourceRecords']: if ptr.get('host', '') == host_rec: ptr['ttl'] = ttl edit_ptr = ptr break if edit_ptr: edit_ptr['data'] = fqdn self.edit_record(edit_ptr) else: self.create_record(ptr_domains['id'], host_rec, 'ptr', fqdn, ttl=ttl) softlayer-python-5.9.8/SoftLayer/managers/email.py000066400000000000000000000055551415376457700222670ustar00rootroot00000000000000""" SoftLayer.email ~~~~~~~~~~~~~~~~~~~~~~~ Email manager :license: MIT, see License for more details. """ from SoftLayer import utils # Invalid names are ignored due to long method names and short argument names # pylint: disable=invalid-name, no-self-use class EmailManager(utils.IdentifierMixin, object): """Common functions for getting information from the email service :param SoftLayer.API.BaseClient client: the client instance """ def __init__(self, client): self.client = client def get_account_overview(self, identifier): """Gets all the Network Message Delivery Account Overview :returns: Network Message Delivery Account overview """ return self.client.call('SoftLayer_Network_Message_Delivery_Email_Sendgrid', 'getAccountOverview', id=identifier) def get_statistics(self, identifier, days=30): """gets statistics from email accounts :days: range number :returns: statistics Network Message Delivery Account """ body = [["requests", "delivered", "opens", "clicks", "bounds"], True, True, True, days ] return self.client.call('SoftLayer_Network_Message_Delivery_Email_Sendgrid', 'getStatistics', id=identifier, *body) def get_instance(self, identifier): """Gets the Network_Message_Delivery_Email_Sendgrid instance :return: Network_Message_Delivery_Email_Sendgrid """ _mask = """emailAddress,type,billingItem,vendor""" return self.client.call('SoftLayer_Network_Message_Delivery_Email_Sendgrid', 'getObject', id=identifier, mask=_mask) def editObject(self, identifier, username=None, password=None): """Edit email delivery account related details. :param int identifier: The ID of the email account :param string username: username of the email account. :param string email: email of the email account. :param string password: password of the email account to be updated to. """ data = {} if username: data['username'] = username if password: data['password'] = password return self.client.call('SoftLayer_Network_Message_Delivery_Email_Sendgrid', 'editObject', data, id=identifier) def update_email(self, identifier, email): """Edit email address delivery account . :param int identifier: The ID of the email account :param string email: email of the email account. """ return self.client.call('SoftLayer_Network_Message_Delivery_Email_Sendgrid', 'updateEmailAddress', email, id=identifier) softlayer-python-5.9.8/SoftLayer/managers/event_log.py000066400000000000000000000067111415376457700231550ustar00rootroot00000000000000""" SoftLayer.event_log ~~~~~~~~~~~~~~~~~~~ Network Manager/helpers :license: MIT, see LICENSE for more details. """ from SoftLayer import utils class EventLogManager(object): """Provides an interface for the SoftLayer Event Log Service. See product information here: http://sldn.softlayer.com/reference/services/SoftLayer_Event_Log """ def __init__(self, client): self.client = client self.event_log = client['Event_Log'] def get_event_logs(self, request_filter=None, log_limit=20, iterator=True): """Returns a list of event logs Example:: event_mgr = SoftLayer.EventLogManager(env.client) request_filter = event_mgr.build_filter(date_min="01/01/2019", date_max="02/01/2019") logs = event_mgr.get_event_logs(request_filter) for log in logs: print("Event Name: {}".format(log['eventName'])) :param dict request_filter: filter dict :param int log_limit: number of results to get in one API call :param bool iterator: False will only make one API call for log_limit results. True will keep making API calls until all logs have been retreived. There may be a lot of these. :returns: List of event logs. If iterator=True, will return a python generator object instead. """ if iterator: # Call iter_call directly as this returns the actual generator return self.client.iter_call('Event_Log', 'getAllObjects', filter=request_filter, limit=log_limit) return self.client.call('Event_Log', 'getAllObjects', filter=request_filter, limit=log_limit) def get_event_log_types(self): """Returns a list of event log types :returns: List of event log types """ results = self.event_log.getAllEventObjectNames() return results @staticmethod def build_filter(date_min=None, date_max=None, obj_event=None, obj_id=None, obj_type=None, utc_offset=None): """Returns a query filter that can be passed into EventLogManager.get_event_logs :param string date_min: Lower bound date in MM/DD/YYYY format :param string date_max: Upper bound date in MM/DD/YYYY format :param string obj_event: The name of the events we want to filter by :param int obj_id: The id of the event we want to filter by :param string obj_type: The type of event we want to filter by :param string utc_offset: The UTC offset we want to use when converting date_min and date_max. (default '+0000') :returns: dict: The generated query filter """ if not any([date_min, date_max, obj_event, obj_id, obj_type]): return {} request_filter = {} if date_min and date_max: request_filter['eventCreateDate'] = utils.event_log_filter_between_date(date_min, date_max, utc_offset) else: if date_min: request_filter['eventCreateDate'] = utils.event_log_filter_greater_than_date(date_min, utc_offset) elif date_max: request_filter['eventCreateDate'] = utils.event_log_filter_less_than_date(date_max, utc_offset) if obj_event: request_filter['eventName'] = {'operation': obj_event} if obj_id: request_filter['objectId'] = {'operation': obj_id} if obj_type: request_filter['objectName'] = {'operation': obj_type} return request_filter softlayer-python-5.9.8/SoftLayer/managers/file.py000066400000000000000000000150371415376457700221130ustar00rootroot00000000000000""" SoftLayer.file ~~~~~~~~~~~~~~~ File Storage Manager :license: MIT, see LICENSE for more details. """ from SoftLayer.managers.storage import StorageManager from SoftLayer.managers import storage_utils from SoftLayer import utils # pylint: disable=too-many-public-methods class FileStorageManager(StorageManager): """Manages file Storage volumes.""" def list_file_volume_limit(self): """Returns a list of block volume count limit. :return: Returns a list of block volume count limit. """ return self.get_volume_count_limits() def list_file_volumes(self, datacenter=None, username=None, storage_type=None, order=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 order: Volume order id. :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)) if order: _filter['nasNetworkStorage']['billingItem']['orderItem'][ 'order']['id'] = (utils.query_filter(order)) kwargs['filter'] = _filter.to_dict() return self.client.call('Account', 'getNasNetworkStorage', iter=True, **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]]]', 'notes', ] kwargs['mask'] = ','.join(items) return self.get_volume_details(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. """ return self.get_volume_access_list(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. """ return self.get_volume_snapshot_list(volume_id, **kwargs) 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 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 """ return self.cancel_volume(volume_id, reason, immediate) def _get_ids_from_username(self, username): object_mask = "mask[id]" results = self.list_file_volumes(username=username, mask=object_mask) if results: return [result['id'] for result in results] return [] softlayer-python-5.9.8/SoftLayer/managers/firewall.py000066400000000000000000000271521415376457700230020ustar00rootroot00000000000000""" 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: https://www.ibm.com/cloud/network-security :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 list rules: the rules to be pushed on the firewall as defined by SoftLayer_Network_Firewall_Update_Request_Rule """ rule_svc = self.client['Network_Firewall_Update_Request'] template = {'networkComponentFirewallId': firewall_id, 'rules': rules} return rule_svc.createObject(template) def get_instance(self, firewall_id, mask=None): """Get the firewall information :param integer firewall_id: the instance ID of the standard firewall """ if not mask: mask = 'mask[datacenter,networkVlan]' svc = self.client['Network_Vlan_Firewall'] return svc.getObject(id=firewall_id, mask=mask) softlayer-python-5.9.8/SoftLayer/managers/hardware.py000066400000000000000000001462721415376457700227770ustar00rootroot00000000000000""" SoftLayer.hardware ~~~~~~~~~~~~~~~~~~ Hardware Manager/helpers :license: MIT, see LICENSE for more details. """ import datetime import logging import socket import time from SoftLayer.decoration import retry from SoftLayer import exceptions from SoftLayer.exceptions import SoftLayerError from SoftLayer.managers import ordering from SoftLayer.managers.ticket import TicketManager 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, too-many-lines EXTRA_CATEGORIES = ['pri_ipv6_addresses', 'static_ipv6_addresses', 'sec_ip_addresses', 'trusted_platform_module', 'software_guard_extensions'] 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: https://www.ibm.com/cloud/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] self.package_keyname = 'BARE_METAL_SERVER' 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. :param bool immediate: If set to True, will automatically update the cancelation ticket to request the resource be reclaimed asap. This request still has to be reviewed by a human :returns: True on success or an exception """ # Get cancel reason reasons = self.get_cancellation_reasons() cancel_reason = reasons.get(reason, reasons['unneeded']) ticket_mgr = TicketManager(self.client) mask = 'mask[id, hourlyBillingFlag, billingItem[id], openCancellationTicket[id], activeTransaction]' hw_billing = self.get_hardware(hardware_id, mask=mask) if 'activeTransaction' in hw_billing: raise SoftLayerError("Unable to cancel hardware with running transaction") if 'billingItem' not in hw_billing: if utils.lookup(hw_billing, 'openCancellationTicket', 'id'): raise SoftLayerError("Ticket #%s already exists for this server" % hw_billing['openCancellationTicket']['id']) raise SoftLayerError("Cannot locate billing for the server. The server may already be cancelled.") billing_id = hw_billing['billingItem']['id'] if immediate and not hw_billing['hourlyBillingFlag']: LOGGER.warning("Immediate cancellation of monthly servers is not guaranteed." "Please check the cancellation ticket for updates.") result = self.client.call('Billing_Item', 'cancelItem', False, False, cancel_reason, comment, id=billing_id) hw_billing = self.get_hardware(hardware_id, mask=mask) ticket_number = hw_billing['openCancellationTicket']['id'] cancel_message = "Please reclaim this server ASAP, it is no longer needed. Thankyou." ticket_mgr.update_ticket(ticket_number, cancel_message) LOGGER.info("Cancelation ticket #%s has been updated requesting immediate reclaim", ticket_number) else: result = self.client.call('Billing_Item', 'cancelItem', immediate, False, cancel_reason, comment, id=billing_id) hw_billing = self.get_hardware(hardware_id, mask=mask) ticket_number = hw_billing['openCancellationTicket']['id'] LOGGER.info("Cancelation ticket #%s has been created", ticket_number) return result @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() kwargs['iter'] = True return self.client.call('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,' 'activeComponents[id,hardwareComponentModel[' 'hardwareGenericComponentModel[id,hardwareComponentType[keyName]]]],' '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]],''' '''softwareComponents[ softwareLicense[softwareDescription[manufacturer, name, version, referenceCode]], passwords[username,password]],''' 'billingItem[' 'id,nextInvoiceTotalRecurringAmount,' 'nextInvoiceChildren[nextInvoiceTotalRecurringAmount],' 'orderItem.order.userRecord[username]' '],' 'lastTransaction[transactionGroup],' '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, lvm=False): """Perform an OS reload of a server with its current configuration. https://sldn.softlayer.com/reference/datatypes/SoftLayer_Container_Hardware_Server_Configuration/ :param integer hardware_id: the instance ID to reload :param string post_uri: 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 bool lvm: A flag indicating that the provision should use LVM for all logical drives. """ config = {} if post_uri: config['customProvisionScriptUri'] = post_uri if ssh_keys: config['sshKeyIds'] = list(ssh_keys) if lvm: config['lvmFlag'] = lvm 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, redundant=None): """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, redundant], id=hardware_id) else: return self.client.call('Hardware_Server', 'setPrivateNetworkInterfaceSpeed', [speed, redundant], 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.ordering_manager.place_order(**create_options) # 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.ordering_manager.verify_order(**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, datacenter=None): """Returns valid options for ordering hardware. :param string datacenter: short name, like dal09 """ package = self._get_package() location_group_id = None if datacenter: _filter = {"name": {"operation": datacenter}} _mask = "mask[priceGroups]" dc_details = self.client.call('SoftLayer_Location', 'getDatacenters', mask=_mask, filter=_filter, limit=1) if not dc_details: raise SoftLayerError("Unable to find a datacenter named {}".format(datacenter)) # A DC will have several price groups, no good way to deal with this other than checking each. # An item should only belong to one type of price group. for group in dc_details[0].get('priceGroups', []): # We only care about SOME of the priceGroups, which all SHOULD start with `Location Group X` # Hopefully this never changes.... if group.get('description').startswith('Location'): location_group_id = group.get('id') # Locations locations = [] for region in package['regions']: if datacenter is None or datacenter == region['location']['location']['name']: locations.append({ 'name': region['location']['location']['longName'], 'key': region['location']['location']['name'], }) # Sizes sizes = [] for preset in package['activePresets'] + package['accountRestrictedActivePresets']: sizes.append({ 'name': preset['description'], 'key': preset['keyName'], 'hourlyRecurringFee': _get_preset_cost(preset, package['items'], 'hourly', location_group_id), 'recurringFee': _get_preset_cost(preset, package['items'], 'monthly', location_group_id) }) operating_systems = [] port_speeds = [] extras = [] for item in package['items']: category = item['itemCategory']['categoryCode'] # Operating systems if category == 'os': operating_systems.append({ 'name': item['softwareDescription']['longDescription'], 'key': item['keyName'], 'referenceCode': item['softwareDescription']['referenceCode'], 'prices': get_item_price(item['prices'], location_group_id) }) # Port speeds elif category == 'port_speed': port_speeds.append({ 'name': item['description'], 'speed': item['capacity'], 'key': item['keyName'], 'prices': get_item_price(item['prices'], location_group_id) }) # Extras elif category in EXTRA_CATEGORIES: extras.append({ 'name': item['description'], 'key': item['keyName'], 'prices': get_item_price(item['prices'], location_group_id) }) 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.""" items_mask = 'mask[id,keyName,capacity,description,attributes[id,attributeTypeKeyName],' \ 'itemCategory[id,categoryCode],softwareDescription[id,referenceCode,longDescription],' \ 'prices[categories]]' # The preset prices list will only have default prices. The prices->item->prices will have location specific presets_mask = 'mask[prices]' region_mask = 'location[location[priceGroups]]' package = {'items': [], 'activePresets': [], 'accountRestrictedActivePresets': [], 'regions': []} package_info = self.ordering_manager.get_package_by_key(self.package_keyname, mask="mask[id]") package['items'] = self.client.call('SoftLayer_Product_Package', 'getItems', id=package_info.get('id'), mask=items_mask) package['activePresets'] = self.client.call('SoftLayer_Product_Package', 'getActivePresets', id=package_info.get('id'), mask=presets_mask) package['accountRestrictedActivePresets'] = self.client.call('SoftLayer_Product_Package', 'getAccountRestrictedActivePresets', id=package_info.get('id'), mask=presets_mask) package['regions'] = self.client.call('SoftLayer_Product_Package', 'getRegions', id=package_info.get('id'), mask=region_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, network=None, public_router=None, private_router=None): """Translates arguments into a dictionary for creating a server.""" extras = extras or [] package = self._get_package() items = package.get('items', {}) location_id = _get_location(package, location) key_names = [ '1_IP_ADDRESS', 'UNLIMITED_SSL_VPN_USERS_1_PPTP_VPN_USER_PER_ACCOUNT', 'REBOOT_KVM_OVER_IP' ] # Operating System key_names.append(os) # Bandwidth Options key_names.append( _get_bandwidth_key(items, hourly=hourly, no_public=no_public, location=location_id) ) # Port Speed Options # New option in v5.9.0 if network: key_names.append(network) # Legacy Option, doesn't support bonded/redundant else: key_names.append( _get_port_speed_key(items, port_speed, no_public, location=location_id) ) # Extras for extra in extras: key_names.append(extra) extras = { 'hardware': [{ 'hostname': hostname, 'domain': domain, }] } if private_router: extras['hardware'][0]['primaryBackendNetworkComponent'] = {"router": {"id": int(private_router)}} if public_router: extras['hardware'][0]['primaryNetworkComponent'] = {"router": {"id": int(public_router)}} if post_uri: extras['provisionScripts'] = [post_uri] if ssh_keys: extras['sshKeys'] = [{'sshKeyIds': ssh_keys}] order = { 'package_keyname': self.package_keyname, 'location': location, 'item_keynames': key_names, 'complex_type': 'SoftLayer_Container_Product_Order_Hardware_Server', 'hourly': hourly, 'preset_keyname': size, 'extras': extras, 'quantity': 1, } 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 reflash_firmware(self, hardware_id, ipmi=True, raid_controller=True, bios=True): """Reflash hardware firmware. This will cause the server to be unavailable for ~60 minutes. The firmware will not be upgraded but rather reflashed to the version installed. :param int hardware_id: The ID of the hardware to have its firmware reflashed. :param bool ipmi: Reflash the ipmi firmware. :param bool raid_controller: Reflash the raid controller firmware. :param bool bios: Reflash the bios firmware. Example:: # Check the servers active transactions to see progress result = mgr.reflash_firmware(hardware_id=1234) """ return self.hardware.createFirmwareReflashTransaction( bool(ipmi), bool(raid_controller), bool(bios), 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_tracking_id(self, instance_id): """Returns the Metric Tracking Object Id for a hardware server :param int instance_id: Id of the hardware server """ return self.hardware.getMetricTrackingObjectId(id=instance_id) def get_bandwidth_data(self, instance_id, start_date=None, end_date=None, direction=None, rollup=3600): """Gets bandwidth data for a server Will get averaged bandwidth data for a given time period. If you use a rollup over 3600 be aware that the API will bump your start/end date to align with how data is stored. For example if you have a rollup of 86400 your start_date will be bumped to 00:00. If you are not using a time in the start/end date fields, this won't really matter. :param int instance_id: Hardware Id to get data for :param date start_date: Date to start pulling data for. :param date end_date: Date to finish pulling data for :param string direction: Can be either 'public', 'private', or None for both. :param int rollup: 300, 600, 1800, 3600, 43200 or 86400 seconds to average data over. """ tracking_id = self.get_tracking_id(instance_id) data = self.client.call('Metric_Tracking_Object', 'getBandwidthData', start_date, end_date, direction, rollup, id=tracking_id, iter=True) return data def get_bandwidth_allocation(self, instance_id): """Combines getBandwidthAllotmentDetail() and getBillingCycleBandwidthUsage() """ a_mask = "mask[allocation[amount]]" allotment = self.client.call('Hardware_Server', 'getBandwidthAllotmentDetail', id=instance_id, mask=a_mask) u_mask = "mask[amountIn,amountOut,type]" usage = self.client.call('Hardware_Server', 'getBillingCycleBandwidthUsage', id=instance_id, mask=u_mask) if allotment: return {'allotment': allotment.get('allocation'), 'usage': usage} return {'allotment': allotment, 'usage': usage} def get_storage_details(self, instance_id, nas_type): """Returns the hardware server attached network storage. :param int instance_id: Id of the hardware server :param nas_type: storage type. """ mask = 'mask[id,username,capacityGb,notes,serviceResourceBackendIpAddress,' \ 'allowedHardware[id,datacenter]]' return self.hardware.getAttachedNetworkStorages(nas_type, mask=mask, id=instance_id) def get_storage_credentials(self, instance_id): """Returns the hardware server storage credentials. :param int instance_id: Id of the hardware server """ mask = 'mask[credential]' return self.hardware.getAllowedHost(mask=mask, id=instance_id) def get_hard_drives(self, instance_id): """Returns the hardware server hard drives. :param int instance_id: Id of the hardware server """ return self.hardware.getHardDrives(id=instance_id) def get_hardware_guests(self, instance_id): """Returns the hardware server guests. :param int instance_id: Id of the hardware server. """ mask = "mask[id]" virtual_host = self.hardware.getVirtualHost(mask=mask, id=instance_id) if virtual_host: return self.client.call('SoftLayer_Virtual_Host', 'getGuests', mask='mask[powerState]', id=virtual_host['id']) return virtual_host def get_hardware_item_prices(self, location): """Returns the hardware server item prices by location. :param string location: location to get the item prices. """ object_mask = "filteredMask[pricingLocationGroup[locations[regions]]]" object_filter = { "itemPrices": {"pricingLocationGroup": {"locations": {"regions": {"keyname": {"operation": location}}}}}} package = self.ordering_manager.get_package_by_key(self.package_keyname) return self.client.call('SoftLayer_Product_Package', 'getItemPrices', mask=object_mask, filter=object_filter, id=package['id']) def authorize_storage(self, hardware_id, username_storage): """Authorize File or Block Storage to a Hardware Server. :param int hardware_id: Hardware server id. :param string username_storage: Storage username. :return: bool. """ _filter = {"networkStorage": {"username": {"operation": username_storage}}} storage_result = self.client.call('Account', 'getNetworkStorage', filter=_filter) if len(storage_result) == 0: raise SoftLayerError("The Storage with username: %s was not found, please" " enter a valid storage username" % username_storage) storage_template = [ { "id": storage_result[0]['id'], "username": username_storage } ] result = self.client.call('Hardware', 'allowAccessToNetworkStorageList', storage_template, id=hardware_id) return result def upgrade(self, instance_id, memory=None, nic_speed=None, drive_controller=None, public_bandwidth=None, disk=None, test=False): """Upgrades a hardware server instance. :param int instance_id: Instance id of the hardware server to be upgraded. :param int memory: Memory size. :param string nic_speed: Network Port Speed data. :param string drive_controller: Drive Controller data. :param int public_bandwidth: Public keyName data. :param list disk: List of disks to add or upgrade Hardware Server. :param bool test: Test option to verify the request. :returns: bool """ result = None maintenance_window_id = None upgrade_prices = self._get_upgrade_prices(instance_id) prices = [] data = {} if memory: data['memory'] = memory if nic_speed: data['nic_speed'] = nic_speed if drive_controller: data['disk_controller'] = drive_controller if public_bandwidth: data['bandwidth'] = public_bandwidth server_response = self.get_instance(instance_id) package_id = server_response['billingItem']['package']['id'] location_id = server_response['datacenter']['id'] maintenance_window = datetime.datetime.now(utils.UTC()) maintenance_window_detail = self.get_maintenance_windows_detail(location_id) if maintenance_window_detail: maintenance_window_id = maintenance_window_detail.get('id') order = { 'complexType': 'SoftLayer_Container_Product_Order_Hardware_Server_Upgrade', 'properties': [ { 'name': 'MAINTENANCE_WINDOW', 'value': maintenance_window.strftime("%Y-%m-%d %H:%M:%S%z") }, { 'name': 'MAINTENANCE_WINDOW_ID', 'value': str(maintenance_window_id) } ], 'hardware': [{'id': int(instance_id)}], 'packageId': package_id } if disk: prices = self._get_disk_price_list(instance_id, disk) order['prices'] = prices for option, value in data.items(): price_id = self._get_prices_for_upgrade_option(upgrade_prices, option, value) 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}) order['prices'] = prices if prices: if test: result = self.client['Product_Order'].verifyOrder(order) else: result = self.client['Product_Order'].placeOrder(order) return result def get_maintenance_windows_detail(self, location_id): """Get the disks prices to be added or upgraded. :param int location_id: Hardware Server location id. :return int: """ result = None begin_date_object = datetime.datetime.now() begin_date = begin_date_object.strftime("%Y-%m-%dT00:00:00.0000-06:00") end_date_object = datetime.date.today() + datetime.timedelta(days=30) end_date = end_date_object.strftime("%Y-%m-%dT00:00:00.0000-06:00") result_windows = self.client['SoftLayer_Provisioning_Maintenance_Window'].getMaintenanceWindows(begin_date, end_date, location_id) if len(result_windows) > 0: result = result_windows[0] return result @retry(logger=LOGGER) def get_instance(self, instance_id): """Get details about a hardware server instance. :param int instance_id: the instance ID :returns: A dictionary containing a large amount of information about the specified instance. """ mask = [ 'datacenter,billingItem[id,package[id,keyName],nextInvoiceChildren]' ] mask = "mask[%s]" % ','.join(mask) return self.hardware.getObject(id=instance_id, mask=mask) def _get_upgrade_prices(self, instance_id): """Following Method gets all the price ids related to upgrading a Hardware Server. :param int instance_id: Instance id of the Hardware Server to be upgraded. :returns: list """ mask = [ 'id', 'locationGroupId', 'categories[name,id,categoryCode]', 'item[keyName,description,capacity,units]' ] mask = "mask[%s]" % ','.join(mask) return self.hardware.getUpgradeItemPrices(id=instance_id, mask=mask) @staticmethod def _get_prices_for_upgrade_option(upgrade_prices, option, value): """Find the price id for the option and value to upgrade. This :param list upgrade_prices: Contains all the prices related to a hardware server upgrade. :param string option: Describes type of parameter to be upgraded :return: int. """ price_id = None option_category = { 'memory': 'ram', 'nic_speed': 'port_speed', 'disk_controller': 'disk_controller', 'bandwidth': 'bandwidth' } if 'disk' in option: category_code = option else: 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') for category in price.get('categories'): if not category.get('categoryCode') == category_code: continue if option == 'disk_controller': if value == product.get('description'): price_id = price.get('id') elif option == 'nic_speed': if value.isdigit(): if str(product.get('capacity')) == str(value): price_id = price.get('id') else: split_nic_speed = value.split(" ") if str(product.get('capacity')) == str(split_nic_speed[0]) and \ split_nic_speed[1] in product.get("description"): price_id = price.get('id') elif option == 'bandwidth': if str(product.get('capacity')) == str(value): price_id = price.get('id') elif 'disk' in option: if str(product.get('capacity')) == str(value): price_id = price else: if str(product.get('capacity')) == str(value): price_id = price.get('id') return price_id def _get_disk_price_list(self, instance_id, disk): """Get the disks prices to be added or upgraded. :param int instance_id: Hardware Server instance id. :param list disk: List of disks to be added o upgraded to the HW. :return list. """ prices = [] disk_exist = False upgrade_prices = self._get_upgrade_prices(instance_id) server_response = self.get_instance(instance_id) for disk_data in disk: disk_channel = 'disk' + str(disk_data.get('number')) for item in utils.lookup(server_response, 'billingItem', 'nextInvoiceChildren'): if disk_channel == item['categoryCode']: disk_exist = True break if disk_exist: disk_price_detail = self._get_disk_price_detail(disk_data, upgrade_prices, disk_channel, 'add_disk') prices.append(disk_price_detail) else: disk_price_detail = self._get_disk_price_detail(disk_data, upgrade_prices, disk_channel, 'resize_disk') prices.append(disk_price_detail) return prices def _get_disk_price_detail(self, disk_data, upgrade_prices, disk_channel, disk_type): """Get the disk price detail. :param disk_data: List of disks to be added or upgraded. :param list upgrade_prices: List of item prices. :param String disk_channel: Disk position. :param String disk_type: Disk type. """ if disk_data.get('description') == disk_type: if "add" in disk_type: raise SoftLayerError("Unable to add the disk because this already exists.") if "resize" in disk_type: raise SoftLayerError("Unable to resize the disk because this does not exists.") else: price_id = self._get_prices_for_upgrade_option(upgrade_prices, disk_channel, disk_data.get('capacity')) if not price_id: raise SoftLayerError("The item price was not found for %s with 'capacity:' %i" % (disk_channel, disk_data.get('capacity'))) disk_price = { "id": price_id.get('id'), "categories": [ { "categoryCode": price_id['categories'][0]['categoryCode'], "id": price_id['categories'][0]['id'] } ] } return disk_price def get_components(self, hardware_id, mask=None, filter_component=None): """Get details about a hardware components. :param int hardware_id: the instance ID :returns: A dictionary containing a large amount of information about the specified components. """ if not mask: mask = 'id,hardwareComponentModel[longDescription,' \ 'hardwareGenericComponentModel[description,hardwareComponentType[keyName]],' \ 'firmwares[createDate,version]]' if not filter_component: filter_component = {"components": { "hardwareComponentModel": { "firmwares": { "createDate": { "operation": "orderBy", "options": [ { "name": "sort", "value": [ "DESC" ] }, { "name": "sortOrder", "value": [ 1 ]}]} }}}} return self.client.call('Hardware_Server', 'getComponents', mask=mask, filter=filter_component, id=hardware_id) def get_sensors(self, hardware_id): """Returns Hardware sensor data""" return self.client.call('Hardware', 'getSensorData', id=hardware_id) def _get_bandwidth_key(items, hourly=True, no_public=False, location=None): """Picks a valid Bandwidth Item, returns the KeyName""" keyName = None # 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 keyName = item['keyName'] for price in item['prices']: if not _matches_billing(price, hourly): continue if not _matches_location(price, location): continue return keyName raise SoftLayerError("Could not find valid price for bandwidth option") def _get_port_speed_key(items, port_speed, no_public, location): """Choose a valid price id for port speed.""" keyName = None 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 keyName = item['keyName'] for price in item['prices']: if not _matches_location(price, location): continue return keyName raise 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 SoftLayerError("Could not find valid location for: '%s'" % location) def _get_preset_cost(preset, items, type_cost, location_group_id=None): """Get the preset cost. :param preset list: SoftLayer_Product_Package_Preset[] :param items list: SoftLayer_Product_Item[] :param type_cost string: 'hourly' or 'monthly' :param location_group_id int: locationGroupId's to get price for. """ # Location based pricing on presets is a huge pain. Requires a few steps # 1. Get the presets prices, which are only ever the default prices # 2. Get that prices item ID, and use that to match the packages item # 3. find the package item, THEN find that items prices # 4. from those item prices, find the one that matches your locationGroupId item_cost = 0.00 if type_cost == 'hourly': cost_key = 'hourlyRecurringFee' else: cost_key = 'recurringFee' for price in preset.get('prices', []): # Need to find the location specific price if location_group_id: # Find the item in the packages item list item_cost = find_item_in_package(cost_key, items, location_group_id, price) else: item_cost += float(price.get(cost_key)) return item_cost def find_item_in_package(cost_key, items, location_group_id, price): """Find the item in the packages item list. Will return the item cost. :param string cost_key: item cost key hourlyRecurringFee or recurringFee. :param list items: items list. :param int location_group_id: locationGroupId's to get price for. :param price: price data. """ item_cost = 0.00 for item in items: # Same item as the price's item if item.get('id') == price.get('itemId'): # Find the items location specific price. for location_price in item.get('prices', []): if location_price.get('locationGroupId', 0) == location_group_id: item_cost += float(location_price.get(cost_key)) return item_cost def get_item_price(prices, location_group_id=None): """Get item prices, optionally for a specific location. Will return the default pricing information if there isn't any location specific pricing. :param prices list: SoftLayer_Product_Item_Price[] :param location_group_id int: locationGroupId's to get price for. """ prices_list = [] location_price = [] for price in prices: # Only look up location prices if we need to if location_group_id: if price['locationGroupId'] == location_group_id: location_price.append(price) # Always keep track of default prices if not price['locationGroupId']: prices_list.append(price) # If this item has location specific pricing, return that if location_price: return location_price # Otherwise reutrn the default price list. return prices_list softlayer-python-5.9.8/SoftLayer/managers/image.py000066400000000000000000000215631415376457700222570ustar00rootroot00000000000000""" SoftLayer.image ~~~~~~~~~~~~~~~ Image Manager/helpers :license: MIT, see LICENSE for more details. """ from SoftLayer import exceptions 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://cloud.ibm.com/docs/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, ibm_api_key=None, root_key_crn=None, wrapped_dek=None, cloud_init=False, byol=False, is_encrypted=False): """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://@// or (.vhd/.iso/.raw file) of the format: cos://// if using IBM Cloud Object Storage :param string os_code: The reference code of the operating system :param string note: Note to add to the image :param string ibm_api_key: Ibm Api Key needed to communicate with ICOS and your KMS :param string root_key_crn: CRN of the root key in your KMS. Go to your KMS (Key Protect or Hyper Protect) provider to get the CRN for your root key. An example CRN: crn:v1:bluemix:public:hs-crypto:us-south:acctID:serviceID:key:keyID' Used only when is_encrypted is True. :param string wrapped_dek: Wrapped Data Encryption Key provided by your KMS. Used only when is_encrypted is True. :param boolean cloud_init: Specifies if image is cloud-init :param boolean byol: Specifies if image is bring your own license :param boolean is_encrypted: Specifies if image is encrypted """ if 'cos://' in uri: return self.vgbdtg.createFromIcos({ 'name': name, 'note': note, 'operatingSystemReferenceCode': os_code, 'uri': uri, 'ibmApiKey': ibm_api_key, 'crkCrn': root_key_crn, 'wrappedDek': wrapped_dek, 'cloudInit': cloud_init, 'byol': byol, 'isEncrypted': is_encrypted }) else: return self.vgbdtg.createFromExternalSource({ 'name': name, 'note': note, 'operatingSystemReferenceCode': os_code, 'uri': uri, }) def export_image_to_uri(self, image_id, uri, ibm_api_key=None): """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://@// or cos://// if using IBM Cloud Object Storage :param string ibm_api_key: Ibm Api Key needed to communicate with IBM Cloud Object Storage """ if 'cos://' in uri: return self.vgbdtg.copyToIcos({ 'uri': uri, 'ibmApiKey': ibm_api_key }, id=image_id) else: return self.vgbdtg.copyToExternalSource({'uri': uri}, id=image_id) def add_locations(self, image_id, location_names): """Add available locations to an archive image template. :param int image_id: The ID of the image :param location_names: Locations for the Image. """ locations = self.get_locations_list(image_id, location_names) return self.vgbdtg.addLocations(locations, id=image_id) def remove_locations(self, image_id, location_names): """Remove available locations from an archive image template. :param int image_id: The ID of the image :param location_names: Locations for the Image. """ locations = self.get_locations_list(image_id, location_names) return self.vgbdtg.removeLocations(locations, id=image_id) def get_storage_locations(self, image_id): """Get available locations for public image storage. :param int image_id: The ID of the image """ return self.vgbdtg.getStorageLocations(id=image_id) def get_locations_list(self, image_id, location_names): """Converts a list of location names to a list of locations. :param int image_id: The ID of the image. :param list location_names: A list of location names strings. :returns: A list of locations associated with the given location names in the image. """ locations = self.get_storage_locations(image_id) locations_ids = [] matching_location = {} output_error = "Location {} does not exist for available locations for image {}" for location_name in location_names: for location in locations: if location_name == location.get('name'): matching_location = location break if matching_location.get('id') is None: raise exceptions.SoftLayerError(output_error.format(location_name, image_id)) locations_ids.append(matching_location) return locations_ids softlayer-python-5.9.8/SoftLayer/managers/ipsec.py000066400000000000000000000326311415376457700222760ustar00rootroot00000000000000""" 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 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.9.8/SoftLayer/managers/license.py000066400000000000000000000044551415376457700226200ustar00rootroot00000000000000""" SoftLayer.license ~~~~~~~~~~~~~~~ License Manager :license: MIT, see LICENSE for more details. """ # pylint: disable=too-many-public-methods from SoftLayer.CLI import exceptions from SoftLayer.managers import ordering from SoftLayer import utils class LicensesManager(object): """Manages account license.""" def __init__(self, client): self.client = client def get_all_objects(self): """Show the all VMware licenses of an account. """ _mask = '''softwareDescription,billingItem''' return self.client.call('SoftLayer_Software_AccountLicense', 'getAllObjects', mask=_mask) def cancel_item(self, key, cancel_immediately=False): """Cancel a billing item immediately, deleting all its data. :param integer identifier: the instance ID to cancel :param string reason_cancel: reason cancel """ vm_ware_licenses = self.get_all_objects() vm_ware_find = False for vm_ware in vm_ware_licenses: if vm_ware.get('key') == key: vm_ware_find = True self.client.call('SoftLayer_Billing_Item', 'cancelItem', cancel_immediately, True, 'Cancel by cli command', 'Cancel by cli command', id=utils.lookup(vm_ware, 'billingItem', 'id')) if not vm_ware_find: raise exceptions.CLIAbort( "Unable to find license key: {}".format(key)) return vm_ware_find def create(self, datacenter, item_package): """Create a license :param string datacenter: the datacenter shortname :param string[] item_package: items array """ complex_type = 'SoftLayer_Container_Product_Order_Software_License' ordering_manager = ordering.OrderingManager(self.client) return ordering_manager.place_order(package_keyname='SOFTWARE_LICENSE_PACKAGE', location=datacenter, item_keynames=item_package, complex_type=complex_type, hourly=False) softlayer-python-5.9.8/SoftLayer/managers/licenses.py000066400000000000000000000010471415376457700227750ustar00rootroot00000000000000""" SoftLayer.license ~~~~~~~~~~~~~~~ License Manager :license: MIT, see LICENSE for more details. """ # pylint: disable=too-many-public-methods LICENSE_PACKAGE_ID = 301 class LicensesManager(object): """Manages account lincese.""" def __init__(self, client): self.client = client def get_create_options(self): """Returns valid options for ordering Licenses. """ return self.client.call('SoftLayer_Product_Package', 'getItems', id=LICENSE_PACKAGE_ID) softlayer-python-5.9.8/SoftLayer/managers/load_balancer.py000066400000000000000000000261261415376457700237430ustar00rootroot00000000000000""" SoftLayer.load_balancer ~~~~~~~~~~~~~~~~~~~~~~~ Load Balancer Manager/helpers :license: MIT, see LICENSE for more details. """ from SoftLayer import exceptions from SoftLayer.managers import ordering from SoftLayer import utils class LoadBalancerManager(utils.IdentifierMixin, object): """Manages SoftLayer load balancers. See product information here: https://www.ibm.com/cloud/load-balancer :param SoftLayer.API.BaseClient client: the client instance """ TYPE = { 1: "Public to Private", 0: "Private to Private", 2: "Public to Public", } def __init__(self, client): self.client = client self.account = self.client['Account'] self.prod_pkg = self.client['Product_Package'] # Citrix Netscalers self.adc = self.client['Network_Application_Delivery_Controller'] # IBM CLoud LB self.lbaas = self.client['Network_LBaaS_LoadBalancer'] self.package_keyname = 'LBAAS' def get_adcs(self, mask=None): """Returns a list of all netscalers. :returns: SoftLayer_Network_Application_Delivery_Controller[]. """ if mask is None: mask = 'mask[managementIpAddress,outboundPublicBandwidthUsage,primaryIpAddress,datacenter]' return self.account.getApplicationDeliveryControllers(mask=mask) def get_adc(self, identifier, mask=None): """Returns a netscaler object. :returns: SoftLayer_Network_Application_Delivery_Controller. """ if mask is None: mask = "mask[networkVlans, password, managementIpAddress, primaryIpAddress, subnets, tagReferences, " \ "licenseExpirationDate, datacenter]" return self.adc.getObject(id=identifier, mask=mask) def get_lbaas(self, mask=None): """Returns a list of IBM Cloud Loadbalancers :returns: SoftLayer_Network_LBaaS_LoadBalancer[] """ if mask is None: mask = "mask[datacenter,listenerCount,memberCount]" this_lb = self.lbaas.getAllObjects(mask=mask) return this_lb def get_lb(self, identifier, mask=None): """Returns a IBM Cloud LoadBalancer :returns: SoftLayer_Network_LBaaS_LoadBalancer """ if mask is None: mask = "mask[healthMonitors, l7Pools, members, sslCiphers, " \ "listeners[defaultPool[healthMonitor, members, sessionAffinity],l7Policies]]" this_lb = self.lbaas.getObject(id=identifier, mask=mask) health = self.lbaas.getLoadBalancerMemberHealth(this_lb.get('uuid')) this_lb['health'] = health return this_lb def update_lb_health_monitors(self, uuid, checks): """calls SoftLayer_Network_LBaaS_HealthMonitor::updateLoadBalancerHealthMonitors() - `updateLoadBalancerHealthMonitors `_ - `SoftLayer_Network_LBaaS_LoadBalancerHealthMonitorConfiguration `_ :param uuid: loadBalancerUuid :param checks list: SoftLayer_Network_LBaaS_LoadBalancerHealthMonitorConfiguration[] """ # return self.lbaas.updateLoadBalancerHealthMonitors(uuid, checks) return self.client.call('SoftLayer_Network_LBaaS_HealthMonitor', 'updateLoadBalancerHealthMonitors', uuid, checks) def get_lbaas_uuid_id(self, identifier): """Gets a LBaaS uuid, id. Since sometimes you need one or the other. :param identifier: either the LB Id, UUID or Name, this function will return UUI and LB Id. :return (uuid, id): """ mask = "mask[id,uuid]" if isinstance(identifier, int) or identifier.isdigit(): this_lb = self.lbaas.getObject(id=identifier, mask=mask) elif len(identifier) == 36 and utils.UUID_RE.match(identifier): this_lb = self.lbaas.getLoadBalancer(identifier, mask=mask) else: this_lb = self.get_lbaas_by_name(identifier, mask=mask) return this_lb.get('uuid'), this_lb.get('id') def get_lbaas_by_name(self, name, mask=None): """Gets a LBaaS by name. :param name: Name of the LBaaS instance :param mask: :returns: SoftLayer_Network_LBaaS_LoadBalancer. """ object_filter = {'name': {'operation': name}} this_lbs = self.lbaas.getAllObjects(filter=object_filter, mask=mask) if not this_lbs: raise exceptions.SoftLayerError("Unable to find LBaaS with name: {}".format(name)) return this_lbs[0] def delete_lb_member(self, identifier, member_id): """Removes a member from a LBaaS instance https://sldn.softlayer.com/reference/services/SoftLayer_Network_LBaaS_Member/deleteLoadBalancerMembers/ :param identifier: UUID of the LBaaS instance :param member_id: Member UUID to remove. """ return self.client.call('SoftLayer_Network_LBaaS_Member', 'deleteLoadBalancerMembers', identifier, [member_id]) def add_lb_member(self, identifier, service_info): """Adds a member to a LBaaS instance https://sldn.softlayer.com/reference/services/SoftLayer_Network_LBaaS_Member/deleteLoadBalancerMembers/ :param identifier: UUID of the LBaaS instance :param service_info: datatypes/SoftLayer_Network_LBaaS_LoadBalancerServerInstanceInfo """ return self.client.call('SoftLayer_Network_LBaaS_Member', 'addLoadBalancerMembers', identifier, [service_info]) def add_lb_listener(self, identifier, listener): """Adds or update a listener to a LBaaS instance When using this to update a listener, just include the 'listenerUuid' in the listener object See the following for listener configuration options https://sldn.softlayer.com/reference/datatypes/SoftLayer_Network_LBaaS_LoadBalancerProtocolConfiguration/ :param identifier: UUID of the LBaaS instance :param listener: Object with all listener configurations """ return self.client.call('SoftLayer_Network_LBaaS_Listener', 'updateLoadBalancerProtocols', identifier, [listener]) def get_l7policies(self, identifier): """Gets Layer7 policies from a listener :param identifier: id """ return self.client.call('SoftLayer_Network_LBaaS_Listener', 'getL7Policies', id=identifier) def get_all_l7policies(self): """Gets all Layer7 policies :returns: Dictionary of (protocol_id: policies list). """ mask = 'mask[listeners[l7Policies]]' lbaas = self.get_lbaas(mask=mask) listeners = [] for load_bal in lbaas: listeners.extend(load_bal.get('listeners')) policies = {} for protocol in listeners: if protocol.get('l7Policies'): listener_id = protocol.get('id') l7policies = protocol.get('l7Policies') policies[listener_id] = l7policies return policies def add_lb_l7_pool(self, identifier, pool, members, health, session): """Creates a new l7 pool for a LBaaS instance - https://sldn.softlayer.com/reference/services/SoftLayer_Network_LBaaS_L7Pool/createL7Pool/ - https://cloud.ibm.com/docs/infrastructure/loadbalancer-service?topic=loadbalancer-service-api-reference :param identifier: UUID of the LBaaS instance :param pool SoftLayer_Network_LBaaS_L7Pool: Description of the pool :param members SoftLayer_Network_LBaaS_L7Member[]: Array of servers with their address, port, weight :param monitor SoftLayer_Network_LBaaS_L7HealthMonitor: A health monitor :param session SoftLayer_Network_LBaaS_L7SessionAffinity: Weather to use affinity """ return self.client.call('SoftLayer_Network_LBaaS_L7Pool', 'createL7Pool', identifier, pool, members, health, session) def del_lb_l7_pool(self, identifier): """Deletes a l7 pool :param identifier: Id of the L7Pool """ return self.client.call('SoftLayer_Network_LBaaS_L7Pool', 'deleteObject', id=identifier) def remove_lb_listener(self, identifier, listener): """Removes a listener to a LBaaS instance :param identifier: UUID of the LBaaS instance :param listener: UUID of the Listner to be removed. """ return self.client.call('SoftLayer_Network_LBaaS_Listener', 'deleteLoadBalancerProtocols', identifier, [listener]) def order_lbaas(self, datacenter, name, desc, protocols, subnet_id, public=False, verify=False): """Allows to order a Load Balancer :param datacenter: Shortname for the SoftLayer datacenter to order in. :param name: Identifier for the new LB. :param desc: Optional description for the lb. :param protocols: https://sldn.softlayer.com/reference/datatypes/SoftLayer_Network_LBaaS_Listener/ :param subnet_id: Id of the subnet for this new LB to live on. :param public: Use Public side for the backend. :param verify: Don't actually order if True. """ order_mgr = ordering.OrderingManager(self.client) package = order_mgr.get_package_by_key(self.package_keyname, mask='mask[id,keyName,itemPrices]') prices = [] for price in package.get('itemPrices'): if not price.get('locationGroupId', False): prices.append(price.get('id')) # Build the configuration of the order order_data = { 'complexType': 'SoftLayer_Container_Product_Order_Network_LoadBalancer_AsAService', 'name': name, 'description': desc, 'location': datacenter, 'packageId': package.get('id'), 'useHourlyPricing': True, # Required since LBaaS is an hourly service 'prices': [{'id': price_id} for price_id in prices], 'protocolConfigurations': protocols, 'subnets': [{'id': subnet_id}], 'isPublic': public } if verify: response = self.client['Product_Order'].verifyOrder(order_data) else: response = self.client['Product_Order'].placeOrder(order_data) return response def lbaas_order_options(self): """Gets the options to order a LBaaS instance.""" _filter = {'keyName': {'operation': self.package_keyname}} mask = "mask[id,keyName,name,items[prices],regions[location[location[groups]]]]" package = self.client.call('SoftLayer_Product_Package', 'getAllObjects', filter=_filter, mask=mask) return package.pop() def cancel_lbaas(self, uuid): """Cancels a LBaaS instance. https://sldn.softlayer.com/reference/services/SoftLayer_Network_LBaaS_LoadBalancer/cancelLoadBalancer/ :param uuid string: UUID of the LBaaS instance to cancel """ return self.lbaas.cancelLoadBalancer(uuid) softlayer-python-5.9.8/SoftLayer/managers/metadata.py000066400000000000000000000113251415376457700227500ustar00rootroot00000000000000""" SoftLayer.metadata ~~~~~~~~~~~~~~~~~~ Metadata Manager/helpers :license: MIT, see LICENSE for more details. """ from SoftLayer.API import BaseClient 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 = 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.9.8/SoftLayer/managers/network.py000066400000000000000000000746131415376457700226720ustar00rootroot00000000000000""" SoftLayer.network ~~~~~~~~~~~~~~~~~ Network Manager/helpers :license: MIT, see LICENSE for more details. """ import collections import json import logging from SoftLayer.decoration import retry from SoftLayer import exceptions from SoftLayer import utils from SoftLayer.managers import event_log LOGGER = logging.getLogger(__name__) # pylint: disable=too-many-public-methods DEFAULT_SUBNET_MASK = ','.join(['hardware', 'datacenter', 'networkVlanId', 'ipAddressCount', 'virtualGuests', 'id', 'networkIdentifier', 'cidr', 'subnetType', 'gateway', 'broadcastAddress', 'usableIpAddressCount', 'note', 'tagReferences[tag]', 'networkVlan[id,networkSpace]']) DEFAULT_VLAN_MASK = ','.join([ 'firewallInterfaces', 'hardwareCount', 'primaryRouter[id, fullyQualifiedDomainName, datacenter]', 'subnetCount', 'totalPrimaryIpAddressCount', 'virtualGuestCount', 'networkSpace', 'networkVlanFirewall[id,fullyQualifiedDomainName,primaryIpAddress]', 'attachedNetworkGateway[id,name,networkFirewall]', ]) DEFAULT_GET_VLAN_MASK = ','.join([ 'firewallInterfaces', 'primaryRouter[id, fullyQualifiedDomainName, datacenter]', 'totalPrimaryIpAddressCount', 'networkSpace', 'billingItem', 'hardware', 'subnets', 'virtualGuests', 'networkVlanFirewall[id,fullyQualifiedDomainName,primaryIpAddress]', 'attachedNetworkGateway[id,name,networkFirewall]', ]) class NetworkManager(object): """Manage SoftLayer network objects: VLANs, subnets and IPs See product information here: https://www.ibm.com/cloud/network :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, endpoint_id=None, version=4, test_order=False): """Orders a new subnet :param str subnet_type: Type of subnet to add: private, public, global,static :param int quantity: Number of IPs in the subnet :param int endpoint_id: 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" elif subnet_type == 'static': category = "static_sec_ip_addresses" else: category = 'static_ipv6_addresses' if subnet_type == 'global': quantity = 0 category = 'global_ipv6' desc = 'Global' elif subnet_type == 'public': desc = 'Portable' elif subnet_type == 'static': desc = 'Static' # 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) package_items = package.getItems(id=0, mask='mask[prices[packageReferences[package[keyName]]]]') for item in package_items: 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 = self.get_subnet_item_price(item, subnet_type, version) break 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 == 'static': order['endPointIpAddressId'] = endpoint_id elif subnet_type != 'global' and subnet_type != 'static': order['endPointVlanId'] = endpoint_id if test_order: return self.client['Product_Order'].verifyOrder(order) else: return self.client['Product_Order'].placeOrder(order) @staticmethod def get_subnet_item_price(item, subnet_type, version): """Get the subnet specific item price id. :param version: 4 for IPv4, 6 for IPv6. :param subnet_type: Type of subnet to add: private, public, global,static. :param item: Subnet item. """ price_id = None if version == 4 and subnet_type == 'static': for item_price in item['prices']: for package_reference in item_price['packageReferences']: if subnet_type.upper() in package_reference['package']['keyName']: price_id = item_price['id'] else: price_id = item['prices'][0]['id'] return price_id 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 set_tags_subnet(self, subnet_id, tags): """Tag a subnet by passing in one or more tags separated by a comma. :param int subnet_id: The ID of the subnet. :param string tags: Comma separated list of tags. """ return self.subnet.setTags(tags, id=subnet_id) def edit_note_subnet(self, subnet_id, note): """Edit the note for this subnet. :param int subnet_id: The ID of the subnet. :param string note: The note. """ return self.subnet.editNote(note, id=subnet_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_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_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, createDate, modifyDate],''' '''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() kwargs['iter'] = True return self.client.call('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 kwargs['iter'] = True return self.account.getNetworkVlans(**kwargs) def list_securitygroups(self, **kwargs): """List security groups.""" kwargs['iter'] = True 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, iter=True) 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 get_event_logs_by_request_id(self, request_id): """Gets all event logs by the given request id :param string request_id: The request id we want to filter on """ # Get all relevant event logs unfiltered_logs = self._get_cci_event_logs() + self._get_security_group_event_logs() # Grab only those that have the specific request id filtered_logs = [] for unfiltered_log in unfiltered_logs: try: metadata = json.loads(unfiltered_log['metaData']) if 'requestId' in metadata: if metadata['requestId'] == request_id: filtered_logs.append(unfiltered_log) except ValueError: continue return filtered_logs def _get_cci_event_logs(self): # Load the event log manager event_log_mgr = event_log.EventLogManager(self.client) # Get CCI Event Logs _filter = event_log_mgr.build_filter(obj_type='CCI') return event_log_mgr.get_event_logs(request_filter=_filter) def _get_security_group_event_logs(self): # Load the event log manager event_log_mgr = event_log.EventLogManager(self.client) # Get CCI Event Logs _filter = event_log_mgr.build_filter(obj_type='Security Group') return event_log_mgr.get_event_logs(request_filter=_filter) 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 def edit(self, instance_id, name=None, note=None, tags=None): """Edit a vlan. :param integer instance_id: the instance ID to edit. :param string name: valid name. :param string note: note about this particular vlan. :param string tags: tags to set on the vlan as a comma separated list. Use the empty string to remove all tags. :returns: bool -- True or an Exception """ obj = {} if tags is not None: self.set_tags(tags, vlan_id=instance_id) if name: obj['name'] = name if note: obj['note'] = note if not obj: return True return self.vlan.editObject(obj, id=instance_id) @retry(logger=LOGGER) def set_tags(self, tags, vlan_id): """Sets tags on a vlan with a retry decorator Just calls vlan.setTags, but if it fails from an APIError will retry. """ self.vlan.setTags(tags, id=vlan_id) def get_ip_by_address(self, ip_address): """get the ip address object :param string ip_address: the ip address to edit. """ return self.client.call('SoftLayer_Network_Subnet_IpAddress', 'getByIpAddress', ip_address) def set_subnet_ipddress_note(self, identifier, note): """Set the ip address note of the subnet :param integer identifier: the ip address ID to edit. :param json note: the note to edit. """ result = self.client.call('SoftLayer_Network_Subnet_IpAddress', 'editObject', note, id=identifier) return result def get_cancel_failure_reasons(self, identifier): """get the reasons why we cannot cancel the VLAN. :param integer identifier: the instance ID """ return self.vlan.getCancelFailureReasons(id=identifier) def cancel_item(self, identifier, cancel_immediately, reason_cancel, customer_note): """Cancel a billing item immediately, deleting all its data. :param integer identifier: the instance ID to cancel :param string reason_cancel: reason cancel """ return self.client.call('SoftLayer_Billing_Item', 'cancelItem', True, cancel_immediately, reason_cancel, customer_note, id=identifier) def get_pods(self, datacenter=None): """Calls SoftLayer_Network_Pod::getAllObjects() returns list of all network pods and their routers. """ _filter = None if datacenter: _filter = {"datacenterName": {"operation": datacenter}} return self.client.call('SoftLayer_Network_Pod', 'getAllObjects', filter=_filter) softlayer-python-5.9.8/SoftLayer/managers/object_storage.py000066400000000000000000000102741415376457700241640ustar00rootroot00000000000000""" SoftLayer.object_storage ~~~~~~~~~~~~~~~~~~~~~~~~ Object Storage Manager/helpers :license: MIT, see LICENSE for more details. """ from SoftLayer.exceptions import SoftLayerError from SoftLayer import utils LIST_ACCOUNTS_MASK = '''mask[ id,username,notes,vendorName,serviceResource ]''' ENDPOINT_MASK = '''mask(SoftLayer_Network_Storage_Hub_Swift)[ id,storageNodes[id,backendIpAddress,frontendIpAddress,datacenter] ]''' class ObjectStorageManager(utils.IdentifierMixin, object): """Manager for SoftLayer Object Storage accounts. See product information here: https://www.ibm.com/cloud/object-storage :param SoftLayer.API.BaseClient client: the client instance """ def __init__(self, client): self.client = client self.resolvers = [self._get_id_from_username] def list_accounts(self, object_mask=None, object_filter=None, limit=10): """Lists your object storage accounts.""" object_mask = object_mask if object_mask else LIST_ACCOUNTS_MASK return self.client.call('Account', 'getHubNetworkStorage', mask=object_mask, filter=object_filter, limit=limit) 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 def create_credential(self, identifier): """Create object storage credential. :param int identifier: The object storage account identifier. """ return self.client.call('SoftLayer_Network_Storage_Hub_Cleversafe_Account', 'credentialCreate', id=identifier) def delete_credential(self, identifier, credential_id=None): """Delete the object storage credential. :param int id: The object storage account identifier. :param int credential_id: The credential id to be deleted. """ credential = { 'id': credential_id } return self.client.call('SoftLayer_Network_Storage_Hub_Cleversafe_Account', 'credentialDelete', credential, id=identifier) def limit_credential(self, identifier): """Limit object storage credentials. :param int identifier: The object storage account identifier. """ return self.client.call('SoftLayer_Network_Storage_Hub_Cleversafe_Account', 'getCredentialLimit', id=identifier) def list_credential(self, identifier): """List the object storage credentials. :param int identifier: The object storage account identifier. """ return self.client.call('SoftLayer_Network_Storage_Hub_Cleversafe_Account', 'getCredentials', id=identifier) def _get_id_from_username(self, username): """Looks up a username's id :param string username: Username to lookup :returns: The id that matches username. """ _mask = "mask[id,username]" _filter = {'hubNetworkStorage': {'username': utils.query_filter(username)}} account = self.list_accounts(_mask, _filter) if len(account) == 1: return [account[0]['id']] elif len(account) > 1: raise SoftLayerError("Multiple object storage accounts found with the name: {}".format(username)) else: raise SoftLayerError("Unable to find object storage account id for: {}".format(username)) softlayer-python-5.9.8/SoftLayer/managers/ordering.py000066400000000000000000000750251415376457700230100ustar00rootroot00000000000000""" SoftLayer.ordering ~~~~~~~~~~~~~~~~~~ Ordering Manager :license: MIT, see LICENSE for more details. """ # pylint: disable=no-self-use from re import match from SoftLayer import exceptions CATEGORY_MASK = '''id, isRequired, itemCategory[id, name, categoryCode]''' ITEM_MASK = '''id, keyName, description, itemCategory, categories, prices''' 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'] self.package_preset = client['Product_Package_Preset'] 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 def get_order_detail(self, order_id, mask=None): """Get order details. :param int order_id: to specify the order that we want to retrieve. :param string mask: Mask to specify the properties we want to retrieve. """ _default_mask = ( 'mask[orderTotalAmount,orderApprovalDate,' 'initialInvoice[id,amount,invoiceTotalAmount,' 'invoiceTopLevelItems[id, description, hostName, domainName, oneTimeAfterTaxAmount,' 'recurringAfterTaxAmount, createDate,' 'categoryCode,' 'category[name],' 'location[name],' 'children[id, category[name], description, oneTimeAfterTaxAmount,recurringAfterTaxAmount]]],' 'items[description],userRecord[displayName,userStatus]]') mask = _default_mask if mask is None else mask order = self.billing_svc.getObject(mask=mask, id=order_id) return order @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 active quotes. :returns: a list of SoftLayer_Billing_Order_Quote """ mask = "mask[order[id,items[id,package[id,keyName]]]]" quotes = self.client['Account'].getActiveQuotes(mask=mask) return quotes def get_quote_details(self, quote_id): """Retrieve quote details. :param quote_id: ID number of target quote """ mask = "mask[order[id,items[package[id,keyName]]]]" quote = self.client['Billing_Order_Quote'].getObject(id=quote_id, mask=mask) return quote def save_quote(self, quote_id): """Save a quote. :param quote_id: ID number of target quote """ return self.client['Billing_Order_Quote'].saveQuote(id=quote_id) 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 def generate_order_template(self, quote_id, extra, quantity=1): """Generate a complete order template. :param int quote_id: ID of target quote :param dictionary extra: Overrides for the defaults of SoftLayer_Container_Product_Order :param int quantity: Number of items to order. """ if not isinstance(extra, dict): raise ValueError("extra is not formatted properly") container = self.get_order_container(quote_id) container['quantity'] = quantity for key in extra.keys(): container[key] = extra[key] return container def verify_quote(self, quote_id, extra): """Verifies that a quote order is valid. :: extras = { 'hardware': {'hostname': 'test', 'domain': 'testing.com'}, 'quantity': 2 } manager = ordering.OrderingManager(env.client) result = manager.verify_quote(12345, extras) :param int quote_id: ID for the target quote :param dictionary extra: Overrides for the defaults of SoftLayer_Container_Product_Order :param int quantity: Quantity to override default """ container = self.generate_order_template(quote_id, extra) clean_container = {} # There are a few fields that wil cause exceptions in the XML endpoint if you send in '', # or None in Rest endpoint (e.g. reservedCapacityId, hostId). But we clean all just to be safe. # This for some reason is only a problem on verify_quote. for key in container.keys(): if container.get(key): clean_container[key] = container[key] return self.client.call('SoftLayer_Billing_Order_Quote', 'verifyOrder', clean_container, id=quote_id) def order_quote(self, quote_id, extra): """Places an order using a quote :: extras = { 'hardware': {'hostname': 'test', 'domain': 'testing.com'}, 'quantity': 2 } manager = ordering.OrderingManager(env.client) result = manager.order_quote(12345, extras) :param int quote_id: ID for the target quote :param dictionary extra: Overrides for the defaults of SoftLayer_Container_Product_Order :param int quantity: Quantity to override default """ container = self.generate_order_template(quote_id, extra) return self.client.call('SoftLayer_Billing_Order_Quote', 'placeOrder', container, id=quote_id) 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 """ kwargs['mask'] = kwargs.get('mask', CATEGORY_MASK) if 'filter' in kwargs: kwargs['filter'] = kwargs['filter'] package = self.get_package_by_key(package_keyname, mask='id') categories = self.package_svc.getConfiguration(id=package['id'], **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 """ if 'mask' not in kwargs: kwargs['mask'] = ITEM_MASK package = self.get_package_by_key(package_keyname, mask='id') items = self.package_svc.getItems(id=package['id'], **kwargs) return items def list_packages(self, **kwargs): """List active packages. :returns: List of active packages. """ kwargs['mask'] = kwargs.get('mask', PACKAGE_MASK) if 'filter' in kwargs: kwargs['filter'] = kwargs['filter'] packages = self.package_svc.getAllObjects(**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 """ kwargs['mask'] = kwargs.get('mask', PRESET_MASK) if 'filter' in kwargs: kwargs['filter'] = kwargs['filter'] package = self.get_package_by_key(package_keyname, mask='id') acc_presets = self.package_svc.getAccountRestrictedActivePresets(id=package['id'], **kwargs) active_presets = self.package_svc.getActivePresets(id=package['id'], **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 } }, 'accountRestrictedActivePresets': { '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, core=None): """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 :param str core: preset guest core capacity. :returns: A list of price IDs associated with the given item keynames in the given package """ mask = 'id, description, capacity, itemCategory, keyName, prices[categories]' items = self.list_items(package_keyname, mask=mask) item_capacity = self.get_item_capacity(items, item_keynames) prices = [] category_dict = {"gpu0": -1, "pcie_slot0": -1} 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 as ex: message = "Item {} does not exist for package {}".format(item_keyname, package_keyname) raise exceptions.SoftLayerError(message) from ex # 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 item_category = matching_item['itemCategory']['categoryCode'] if item_category not in category_dict: if core is None: price_id = self.get_item_price_id(item_capacity, matching_item['prices']) else: price_id = self.get_item_price_id(core, matching_item['prices']) else: # GPU and PCIe items has two generic prices and they are added to the list # according to the number of items in the order. category_dict[item_category] += 1 category_code = item_category[:-1] + str(category_dict[item_category]) price_id = [p['id'] for p in matching_item['prices'] if not p['locationGroupId'] and p['categories'][0]['categoryCode'] == category_code][0] prices.append(price_id) return prices @staticmethod def get_item_price_id(core, prices, term=0): """get item price id core: None or a number to match against capacityRestrictionType prices: list of SoftLayer_Product_Item_Price term: int to match against SoftLayer_Product_Item_Price.termLength """ price_id = None for price in prices: if not price['locationGroupId'] and price.get('termLength', 0) in {term, '', None}: restriction = price.get('capacityRestrictionType', False) # There is a price restriction. Make sure the price is within the restriction if restriction and core is not None: capacity_min = int(price.get('capacityRestrictionMinimum', -1)) capacity_max = int(price.get('capacityRestrictionMaximum', -1)) if "STORAGE" in restriction: if capacity_min <= int(core) <= capacity_max: price_id = price['id'] if "CORE" in restriction: if capacity_min <= int(core) <= capacity_max: price_id = price['id'] if "PROCESSOR" in restriction: price_id = price['id'] # No price restrictions else: price_id = price['id'] return price_id def get_item_capacity(self, items, item_keynames): """Get item capacity.""" item_capacity = None for item_keyname in item_keynames: for item in items: if item['keyName'] == item_keyname: if "CORE" in item["keyName"]: item_capacity = item['capacity'] break if "TIER" in item["keyName"]: item_capacity = item['capacity'] break if "INTEL" in item["keyName"]: item_split = item['description'].split("(") item_core = item_split[1].split(" ") item_capacity = item_core[0] break return item_capacity def get_preset_prices(self, preset): """Get preset item prices. Retrieve a SoftLayer_Product_Package_Preset record. :param int preset: preset identifier. :returns: A list of price IDs associated with the given preset_id. """ mask = 'mask[prices[item]]' prices = self.package_preset.getObject(id=preset, mask=mask) return prices def get_item_prices(self, package_id): """Get item prices. Retrieve a SoftLayer_Product_Package item prices record. :param int package_id: package identifier. :returns: A list of price IDs associated with the given package. """ mask = 'mask[pricingLocationGroup[locations]]' prices = self.package_svc.getItemPrices(id=package_id, mask=mask) 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 place_quote(self, package_keyname, location, item_keynames, complex_type=None, preset_keyname=None, extras=None, quantity=1, quote_name=None, send_email=False): """Place a quote with the given package and prices. This function takes in parameters needed for an order and places the quote. :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 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 :param string quote_name: A custom name to be assigned to the quote (optional). :param bool send_email: This flag indicates that the quote should be sent to the email address associated with the account or order. """ order = self.generate_order(package_keyname, location, item_keynames, complex_type=complex_type, hourly=False, preset_keyname=preset_keyname, extras=extras, quantity=quantity) order['quoteName'] = quote_name order['sendQuoteEmailFlag'] = send_email return self.order_svc.placeQuote(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 """ container = {} 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['quantity'] = quantity order['location'] = self.get_location_id(location) order['useHourlyPricing'] = hourly preset_core = None if preset_keyname: preset_id = self.get_preset_by_key(package_keyname, preset_keyname)['id'] preset_items = self.get_preset_prices(preset_id) for item in preset_items['prices']: if item['item']['itemCategory']['categoryCode'] == "guest_core": preset_core = item['item']['capacity'] 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, preset_core) order['prices'] = [{'id': price_id} for price_id in price_ids] container['orderContainers'] = [order] return container 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 def get_location_id(self, location): """Finds the location ID of a given datacenter This is mostly used so either a dc name, or regions keyname can be used when ordering :param str location: Region Keyname (DALLAS13) or datacenter name (dal13) :returns: integer id of the datacenter """ if isinstance(location, int): return location # Some orders dont require a location, just use 0 if location.upper() == "NONE": return 0 mask = "mask[id,name,regions[keyname]]" if match(r'[a-zA-Z]{3}[0-9]{2}', location) is not None: search = {'name': {'operation': location}} else: search = {'regions': {'keyname': {'operation': location}}} datacenter = self.client.call('SoftLayer_Location', 'getDatacenters', mask=mask, filter=search) if len(datacenter) != 1: raise exceptions.SoftLayerError("Unable to find location: %s" % location) return datacenter[0]['id'] def get_item_prices_by_location(self, location, package_keyname): """Returns the item prices by location. :param string package_keyname: The package for which to get the items. :param string location: location name or keyname to get the item prices. """ object_mask = "filteredMask[pricingLocationGroup[locations]]" location_name = self.resolve_location_name(location) object_filter = { "itemPrices": {"pricingLocationGroup": {"locations": {"name": {"operation": location_name}}}}} package = self.get_package_by_key(package_keyname) return self.client.call('SoftLayer_Product_Package', 'getItemPrices', mask=object_mask, filter=object_filter, id=package['id']) def resolve_location_name(self, location_key): """Resolves a location name using a string location key. :param string location_key: A string location used to resolve the location name. :return: An location name. """ default_region_keyname = 'unknown' if not location_key or location_key == default_region_keyname: raise exceptions.SoftLayerError("Invalid location {}".format(location_key)) default_regions = [{'keyname': default_region_keyname}] index_first = 0 object_mask = "mask[regions]" locations = self.client.call('SoftLayer_Location', 'getDatacenters', mask=object_mask) for location in locations: location_name = location.get('name') if location_name == location_key: return location_key if location.get('regions', default_regions)[index_first].get('keyname') == location_key: return location_name raise exceptions.SoftLayerError("Location {} does not exist".format(location_key)) softlayer-python-5.9.8/SoftLayer/managers/sshkey.py000066400000000000000000000053561415376457700225050ustar00rootroot00000000000000""" 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.9.8/SoftLayer/managers/ssl.py000066400000000000000000000062551415376457700217770ustar00rootroot00000000000000""" SoftLayer.ssl ~~~~~~~~~~~~~ SSL Manager/helpers :license: MIT, see LICENSE for more details. """ class SSLManager(object): """Manages SSL certificates in SoftLayer. See product information here: https://www.ibm.com/cloud/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 sldn.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.9.8/SoftLayer/managers/storage.py000066400000000000000000000600061415376457700226340ustar00rootroot00000000000000""" SoftLayer.storage ~~~~~~~~~~~~~~~ Network 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 StorageManager(utils.IdentifierMixin, object): """"Base class for File and Block storage managers Any shared code between File and Block should ideally go here. :param SoftLayer.API.BaseClient client: the client instance """ def __init__(self, client): self.configuration = {} self.client = client self.resolvers = [self._get_ids_from_username] def _get_ids_from_username(self, username): # pylint: disable=unused-argument,no-self-use """Should only be actually called from the block/file manager""" return [] def get_volume_count_limits(self): """Returns a list of block volume count limit. :return: Returns a list of block volume count limit. """ return self.client.call('Network_Storage', 'getVolumeCountLimits') def get_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]]]', 'notes', ] kwargs['mask'] = ','.join(items) return self.client.call('Network_Storage', 'getObject', id=volume_id, **kwargs) def get_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_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 set_volume_snapshot_notification(self, volume_id, enable): """Enables/Disables snapshot space usage threshold warning for a given volume. :param volume_id: ID of volume. :param enable: Enable/Disable flag for snapshot warning notification. :return: Enables/Disables snapshot space usage threshold warning for a given volume. """ return self.client.call('Network_Storage', 'setSnapshotNotification', enable, id=volume_id) def get_volume_snapshot_notification_status(self, volume_id): """returns Enabled/Disabled status of snapshot space usage threshold warning for a given volume. :param volume_id: ID of volume. :return: Enables/Disables snapshot space usage threshold warning for a given volume. """ status = self.client.call('Network_Storage', 'getSnapshotNotificationStatus', id=volume_id) # A None status is enabled as well. if status is None: status = 1 # We need to force int on the return because otherwise the API will return the string '0' # instead of either a boolean or real int... return int(status) def authorize_host_to_volume(self, volume_id, hardware_ids=None, virtual_guest_ids=None, ip_address_ids=None, subnet_ids=None): """Authorizes hosts to 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. Only use with File volumes. :return: Returns an array of SoftLayer_Network_Storage_Allowed_Host objects which now have access to the given volume """ host_templates = storage_utils.populate_host_templates( hardware_ids, virtual_guest_ids, ip_address_ids, subnet_ids) return self.client.call('Network_Storage', 'allowAccessFromHostList', host_templates, id=volume_id) def deauthorize_host_to_volume(self, volume_id, hardware_ids=None, virtual_guest_ids=None, ip_address_ids=None, subnet_ids=None): """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. Only use with File volumes :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( hardware_ids, virtual_guest_ids, ip_address_ids, subnet_ids) return self.client.call('Network_Storage', 'removeAccessFromHostList', host_templates, id=volume_id) 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 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_volume_details(volume_id, mask=block_mask) storage_class = storage_utils.block_or_file( block_volume['storageType']['keyName']) order = storage_utils.prepare_replicant_order_object( self, snapshot_schedule, location, tier, block_volume, storage_class) if storage_class == 'block': 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['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, dependent_duplicate=False): """Places an order for a duplicate 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_volume_details(origin_volume_id, mask=block_mask) storage_class = storage_utils.block_or_file( origin_volume['storageType']['keyName']) order = storage_utils.prepare_duplicate_order_object( self, origin_volume, duplicate_iops, duplicate_tier_level, duplicate_size, duplicate_snapshot_size, storage_class, hourly_billing_flag) if storage_class == 'block': 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['osFormatType'] = {'keyName': os_type} if origin_snapshot_id is not None: order['duplicateOriginSnapshotId'] = origin_snapshot_id if dependent_duplicate: # if isDependentDuplicateFlag is set to ANYTHING, it is considered dependent. order['isDependentDuplicateFlag'] = 1 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_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 volume_set_note(self, volume_id, note): """Set the notes for an existing block volume. :param volume_id: The ID of the volume to be modified :param note: the note :return: Returns true if success """ template = {'notes': note} return self.client.call('SoftLayer_Network_Storage', 'editObject', template, id=volume_id) 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 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 """ object_mask = 'id,billingItem[location,hourlyFlag],' \ 'storageType[keyName],storageTierLevel,provisionedIops,' \ 'staasVersion,hasEncryptionAtRest' volume = self.get_volume_details(volume_id, mask=object_mask, **kwargs) order = storage_utils.prepare_snapshot_order_object( self, 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 """ object_mask = 'mask[id,billingItem[activeChildren,hourlyFlag]]' volume = self.get_volume_details(volume_id, mask=object_mask) if 'activeChildren' not in volume['billingItem']: raise exceptions.SoftLayerError( 'No snapshot space found to cancel') children_array = 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(volume, 'billingItem', 'hourlyFlag'): immediate = True return self.client.call('SoftLayer_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 """ object_mask = 'schedules[type,properties[type]]' volume_detail = self.client.call('Network_Storage', 'getObject', id=volume_id, mask=object_mask) 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 failover_to_replicant(self, volume_id, replicant_id): """Failover to a volume replicant. :param integer volume_id: The id of the volume :param integer replicant_id: ID of replicant to failover to :return: Returns whether failover was successful or not """ return self.client.call('Network_Storage', 'failoverToReplicant', replicant_id, id=volume_id) def disaster_recovery_failover_to_replicant(self, volume_id, replicant_id): """Disaster Recovery Failover to a volume replicant. :param integer volume_id: The id of the volume :param integer replicant: ID of replicant to failover to :return: Returns whether failover to successful or not """ return self.client.call('Network_Storage', 'disasterRecoveryFailoverToReplicant', replicant_id, id=volume_id) def failback_from_replicant(self, volume_id): """Failback from a volume replicant. :param integer volume_id: The id of the volume :return: Returns whether failback was successful or not """ return self.client.call('Network_Storage', 'failbackFromReplicant', id=volume_id) def cancel_volume(self, volume_id, reason='No longer needed', immediate=False): """Cancels the given 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 """ object_mask = 'mask[id,billingItem[id,hourlyFlag]]' volume = self.get_volume_details(volume_id, mask=object_mask) if 'billingItem' not in volume: raise exceptions.SoftLayerError( "Storage Volume was already cancelled") billing_item_id = volume['billingItem']['id'] if utils.lookup(volume, 'billingItem', 'hourlyFlag'): immediate = True return self.client.call('SoftLayer_Billing_Item', 'cancelItem', immediate, True, reason, id=billing_item_id) def refresh_dupe(self, volume_id, snapshot_id): """"Refresh a duplicate volume with a snapshot from its parent. :param integer volume_id: The id of the volume :param integer snapshot_id: The id of the snapshot """ return self.client.call('Network_Storage', 'refreshDuplicate', snapshot_id, id=volume_id) def convert_dep_dupe(self, volume_id): """Convert a dependent duplicate volume to an independent volume. :param integer volume_id: The id of the volume. """ return self.client.call('Network_Storage', 'convertCloneDependentToIndependent', id=volume_id) softlayer-python-5.9.8/SoftLayer/managers/storage_utils.py000066400000000000000000001226471415376457700240660ustar00rootroot00000000000000""" 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(hardware_ids=None, virtual_guest_ids=None, ip_address_ids=None, subnet_ids=None): """Returns a populated array with the IDs provided :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: array of objects formatted for allowAccessFromHostList """ host_templates = [] 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 }) return host_templates 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']: price_id = _find_price_id(item['prices'], price_category) if price_id: return 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' level = ENDURANCE_TIERS.get(tier_level) for item in package['items']: if int(item['capacity']) != size: continue price_id = _find_price_id(item['prices'], category_code, 'STORAGE_TIER_LEVEL', level) if price_id: return 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 price_id = _find_price_id(item['prices'], 'storage_tier_level') if price_id: return 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 price_id = _find_price_id(item['prices'], 'performance_storage_space') if price_id: return 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 price_id = _find_price_id(item['prices'], 'performance_storage_iops', 'STORAGE_SPACE', size) if price_id: return 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 key_name not in item['keyName']: 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 price_id = _find_price_id(item['prices'], 'performance_storage_space') if price_id: return 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 price_id = _find_price_id(item['prices'], 'storage_tier_level') if price_id: return 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 price_id = _find_price_id(item['prices'], 'performance_storage_space') if price_id: return 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 price_id = _find_price_id(item['prices'], 'performance_storage_iops', 'STORAGE_SPACE', size) if price_id: return 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 price_id = _find_price_id(item['prices'], 'storage_snapshot_space', target_restriction_type, target_value) if price_id: return 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 price_id = _find_price_id( item['prices'], 'performance_storage_replication', target_restriction_type, target_value ) if price_id: return 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 as ex: message = "Invalid datacenter name specified. Please provide the lower case short name (e.g.: dal09)" raise exceptions.SoftLayerError(message) from ex # 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'].get('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.get('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 as ex: message = "Invalid datacenter name specified. Please provide the lower case short name (e.g.: dal09)" raise exceptions.SoftLayerError(message) from ex # 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, dependent_duplicate=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) :param dependent_duplicate: Duplicate type, normal (False) or dependent duplicate (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 if dependent_duplicate: duplicate_order['isDependentDuplicateFlag'] = 1 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 block_or_file(storage_type_keyname): """returns either 'block' or 'file' :param storage_type_keyname: the Network_Storage['storageType']['keyName'] :returns: 'block' or 'file' """ return 'block' if 'BLOCK_STORAGE' in storage_type_keyname else 'file' 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'] def _find_price_id(prices, category, restriction_type=None, restriction_value=None): for price in prices: # Only collect prices from valid location groups. if price['locationGroupId']: continue if restriction_type is not None and restriction_value is not None: if restriction_type != price['capacityRestrictionType']\ or restriction_value < int(price['capacityRestrictionMinimum'])\ or restriction_value > int(price['capacityRestrictionMaximum']): continue if not _has_category(price['categories'], category): continue return {'id': price['id']} softlayer-python-5.9.8/SoftLayer/managers/tags.py000066400000000000000000000222321415376457700221250ustar00rootroot00000000000000""" SoftLayer.tags ~~~~~~~~~~~~ Tag Manager :license: MIT, see LICENSE for more details. """ import re from SoftLayer.exceptions import SoftLayerAPIError class TagManager(object): """Manager for Tag functions.""" def __init__(self, client): self.client = client def list_tags(self, mask=None): """Returns a list of all tags for the Current User :param str mask: Object mask to use if you do not want the default. """ if mask is None: mask = "mask[id,name,referenceCount]" unattached = self.get_unattached_tags(mask) attached = self.get_attached_tags(mask) return {'attached': attached, 'unattached': unattached} def get_unattached_tags(self, mask=None): """Calls SoftLayer_Tag::getUnattachedTagsForCurrentUser() :params string mask: Mask to use. """ return self.client.call('SoftLayer_Tag', 'getUnattachedTagsForCurrentUser', mask=mask, iter=True) def get_attached_tags(self, mask=None): """Calls SoftLayer_Tag::getAttachedTagsForCurrentUser() :params string mask: Mask to use. """ return self.client.call('SoftLayer_Tag', 'getAttachedTagsForCurrentUser', mask=mask, iter=True) def get_tag_references(self, tag_id, mask=None): """Calls SoftLayer_Tag::getReferences(id=tag_id) :params int tag_id: Tag id to get references from :params string mask: Mask to use. """ if mask is None: mask = "mask[tagType]" return self.client.call('SoftLayer_Tag', 'getReferences', id=tag_id, mask=mask, iter=True) def get_tag(self, tag_id, mask=None): """Calls SoftLayer_Tag::getObject(id=tag_id) :params int tag_id: Tag id to get object from :params string mask: Mask to use. """ if mask is None: mask = "mask[id,name]" result = self.client.call('SoftLayer_Tag', 'getObject', id=tag_id, mask=mask) return result def get_tag_by_name(self, tag_name, mask=None): """Calls SoftLayer_Tag::getTagByTagName(tag_name) :params string tag_name: Tag name to get object from :params string mask: Mask to use. """ if mask is None: mask = "mask[id,name]" result = self.client.call('SoftLayer_Tag', 'getTagByTagName', tag_name, mask=mask) return result def reference_lookup(self, resource_table_id, tag_type): """Returns the SoftLayer Service for the corresponding type :param int resource_table_id: Tag_Reference::resourceTableId :param string tag_type: Tag_Reference->tagType->keyName From SoftLayer_Tag::getAllTagTypes() |Type |Service | | ----------------------------- | ------ | |Hardware |HARDWARE| |CCI |GUEST| |Account Document |ACCOUNT_DOCUMENT| |Ticket |TICKET| |Vlan Firewall |NETWORK_VLAN_FIREWALL| |Contract |CONTRACT| |Image Template |IMAGE_TEMPLATE| |Application Delivery Controller |APPLICATION_DELIVERY_CONTROLLER| |Vlan |NETWORK_VLAN| |Dedicated Host |DEDICATED_HOST| """ service = self.type_to_service(tag_type) if service is None: raise SoftLayerAPIError(404, "Unable to lookup {} types".format(tag_type)) return self.client.call(service, 'getObject', id=resource_table_id) def delete_tag(self, name): """Calls SoftLayer_Tag::deleteTag :param string name: tag name to delete """ return self.client.call('SoftLayer_Tag', 'deleteTag', name) def set_tags(self, tags, key_name, resource_id): """Calls SoftLayer_Tag::setTags() :param string tags: List of tags. :param string key_name: Key name of a tag type. :param int resource_id: ID of the object being tagged. """ return self.client.call('SoftLayer_Tag', 'setTags', tags, key_name, resource_id) def get_all_tag_types(self): """Calls SoftLayer_Tag::getAllTagTypes()""" types = self.client.call('SoftLayer_Tag', 'getAllTagTypes') useable_types = [] for tag_type in types: service = self.type_to_service(tag_type['keyName']) # Mostly just to remove the types that are not user taggable. if service is not None: temp_type = tag_type temp_type['service'] = service useable_types.append(temp_type) return useable_types def taggable_by_type(self, tag_type): """Returns a list of resources that can be tagged, that are of the given type :param string tag_type: Key name of a tag type. See SoftLayer_Tag::getAllTagTypes """ service = self.type_to_service(tag_type) search_term = "_objectType:SoftLayer_{}".format(service) if tag_type == 'TICKET': search_term = "{} status.name: open".format(search_term) elif tag_type == 'IMAGE_TEMPLATE': mask = "mask[id,accountId,name,globalIdentifier,parentId,publicFlag,flexImageFlag,imageType]" resources = self.client.call('SoftLayer_Account', 'getPrivateBlockDeviceTemplateGroups', mask=mask, iter=True) to_return = [] # Fake search result output for resource in resources: to_return.append({'resourceType': service, 'resource': resource}) return to_return elif tag_type == 'NETWORK_SUBNET': resources = self.client.call('SoftLayer_Account', 'getSubnets', iter=True) to_return = [] # Fake search result output for resource in resources: to_return.append({'resourceType': service, 'resource': resource}) return to_return resources = self.client.call('SoftLayer_Search', 'advancedSearch', search_term, iter=True) return resources @staticmethod def type_to_service(tag_type): """Returns the SoftLayer service for the given tag_type""" service = None if tag_type in ['ACCOUNT_DOCUMENT', 'CONTRACT']: return None if tag_type == 'APPLICATION_DELIVERY_CONTROLLER': service = 'Network_Application_Delivery_Controller' elif tag_type == 'GUEST': service = 'Virtual_Guest' elif tag_type == 'DEDICATED_HOST': service = 'Virtual_DedicatedHost' elif tag_type == 'IMAGE_TEMPLATE': service = 'Virtual_Guest_Block_Device_Template_Group' else: tag_type = tag_type.lower() # Sets the First letter, and any letter preceeded by a '_' to uppercase # HARDWARE -> Hardware, NETWORK_VLAN -> Network_Vlan for example. service = re.sub(r'(^[a-z]|\_[a-z])', lambda x: x.group().upper(), tag_type) return service @staticmethod def get_resource_name(resource, tag_type): """Returns a string that names a resource :param dict resource: A SoftLayer datatype for the given tag_type :param string tag_type: Key name for the tag_type """ if tag_type == 'NETWORK_VLAN_FIREWALL': return resource.get('primaryIpAddress') elif tag_type == 'NETWORK_VLAN': return "{} ({})".format(resource.get('vlanNumber'), resource.get('name')) elif tag_type == 'IMAGE_TEMPLATE' or tag_type == 'APPLICATION_DELIVERY_CONTROLLER': return resource.get('name') elif tag_type == 'TICKET': return resource.get('subjet') elif tag_type == 'NETWORK_SUBNET': return resource.get('networkIdentifier') else: return resource.get('fullyQualifiedDomainName') # @staticmethod # def type_to_datatype(tag_type): # """Returns the SoftLayer datatye for the given tag_type""" # datatye = None # if tag_type in ['ACCOUNT_DOCUMENT', 'CONTRACT']: # return None # if tag_type == 'APPLICATION_DELIVERY_CONTROLLER': # datatye = 'adcLoadBalancers' # elif tag_type == 'GUEST': # datatye = 'virtualGuests' # elif tag_type == 'DEDICATED_HOST': # datatye = 'dedicatedHosts' # elif tag_type == 'HARDWARE': # datatye = 'hardware' # elif tag_type == 'TICKET': # datatye = 'openTickets' # elif tag_type == 'NETWORK_SUBNET': # datatye = 'subnets' # elif tag_type == 'NETWORK_VLAN': # datatye = 'networkVlans' # elif tag_type == 'NETWORK_VLAN_FIREWALL': # datatye = 'networkVlans' # elif tag_type == 'IMAGE_TEMPLATE': # datatye = 'blockDeviceTemplateGroups' # return datatye softlayer-python-5.9.8/SoftLayer/managers/ticket.py000066400000000000000000000123041415376457700224510ustar00rootroot00000000000000""" 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: https://www.ibm.com/cloud/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 = """mask[id, serviceProviderResourceId, title, assignedUser[firstName, lastName], priority, createDate, lastEditDate, accountId, status, updateCount]""" call = 'getTickets' if not all([open_status, closed_status]): if open_status: call = 'getOpenTickets' elif closed_status: call = 'getClosedTickets' else: raise ValueError("open_status and closed_status cannot both be False") return self.client.call('Account', call, mask=mask, iter=True) 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 = """mask[id, serviceProviderResourceId, title, assignedUser[firstName, lastName],status, createDate,lastEditDate,updates[entry,editor],updateCount, priority]""" return self.ticket.getObject(id=ticket_id, mask=mask) def create_ticket(self, title=None, body=None, subject=None, priority=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 :param integer priority: Value from 1 (highest) to 4 (lowest) """ current_user = self.account.getCurrentUser() new_ticket = { 'subjectId': subject, 'assignedUserId': current_user['id'], 'title': title, } if priority is not None: new_ticket['priority'] = int(priority) 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.9.8/SoftLayer/managers/user.py000066400000000000000000000376371415376457700221640ustar00rootroot00000000000000""" SoftLayer.user ~~~~~~~~~~~~~ User Manager/helpers :license: MIT, see LICENSE for more details. """ import datetime import logging from operator import itemgetter from SoftLayer import exceptions from SoftLayer import utils LOGGER = logging.getLogger(__name__) class UserManager(utils.IdentifierMixin, object): """Manages Users. See: https://softlayer.github.io/reference/datatypes/SoftLayer_User_Customer/ Example:: # Initialize the Manager. import SoftLayer client = SoftLayer.create_client_from_env() mgr = SoftLayer.UserManager(client) :param SoftLayer.API.BaseClient client: the client instance """ def __init__(self, client): self.client = client self.user_service = self.client['SoftLayer_User_Customer'] self.override_service = self.client['Network_Service_Vpn_Overrides'] self.account_service = self.client['SoftLayer_Account'] self.subscription_service = self.client['SoftLayer_Email_Subscription'] self.resolvers = [self._get_id_from_username] self.all_permissions = None def list_users(self, objectmask=None, objectfilter=None): """Lists all users on an account :param string objectmask: Used to overwrite the default objectmask. :param dictionary objectfilter: If you want to use an objectfilter. :returns: A list of dictionaries that describe each user Example:: result = mgr.list_users() """ if objectmask is None: objectmask = """mask[id, username, displayName, userStatus[name], hardwareCount, virtualGuestCount, email, roles, externalBindingCount,apiAuthenticationKeyCount]""" return self.account_service.getUsers(mask=objectmask, filter=objectfilter) def get_user(self, user_id, objectmask=None): """Calls SoftLayer_User_Customer::getObject :param int user_id: Id of the user :param string objectmask: default is 'mask[userStatus[name], parent[id, username]]' :returns: A user object. """ if objectmask is None: objectmask = "mask[userStatus[name], parent[id, username]]" return self.user_service.getObject(id=user_id, mask=objectmask) def get_current_user(self, objectmask=None): """Calls SoftLayer_Account::getCurrentUser""" if objectmask is None: objectmask = "mask[userStatus[name], parent[id, username]]" return self.account_service.getCurrentUser(mask=objectmask) def get_all_permissions(self): """Calls SoftLayer_User_CustomerPermissions_Permission::getAllObjects Stores the result in self.all_permissions :returns: A list of dictionaries that contains all valid permissions """ if self.all_permissions is None: permissions = self.client.call('User_Customer_CustomerPermission_Permission', 'getAllObjects') self.all_permissions = sorted(permissions, key=itemgetter('keyName')) return self.all_permissions def get_all_notifications(self): """Calls SoftLayer_Email_Subscription::getAllObjects Stores the result in self.all_permissions :returns: A list of dictionaries that contains all valid permissions """ return self.subscription_service.getAllObjects(mask='mask[enabled]') def enable_notifications(self, notifications_names): """Enables a list of notifications for the current a user profile. :param list notifications_names: List of notifications names to enable :returns: True on success Example:: enable_notifications(['Order Approved','Reload Complete']) """ result = False notifications = self.gather_notifications(notifications_names) for notification in notifications: notification_id = notification.get('id') result = self.subscription_service.enable(id=notification_id) if not result: return False return result def disable_notifications(self, notifications_names): """Disable a list of notifications for the current a user profile. :param list notifications_names: List of notifications names to disable :returns: True on success Example:: disable_notifications(['Order Approved','Reload Complete']) """ result = False notifications = self.gather_notifications(notifications_names) for notification in notifications: notification_id = notification.get('id') result = self.subscription_service.disable(id=notification_id) if not result: return False return result def add_permissions(self, user_id, permissions): """Enables a list of permissions for a user :param int id: user id to set :param list permissions: List of permissions keynames to enable :returns: True on success, Exception otherwise Example:: add_permissions(123, ['BANDWIDTH_MANAGE']) """ pretty_permissions = self.format_permission_object(permissions) LOGGER.warning("Adding the following permissions to %s: %s", user_id, pretty_permissions) return self.user_service.addBulkPortalPermission(pretty_permissions, id=user_id) def remove_permissions(self, user_id, permissions): """Disables a list of permissions for a user :param int id: user id to set :param list permissions: List of permissions keynames to disable :returns: True on success, Exception otherwise Example:: remove_permissions(123, ['BANDWIDTH_MANAGE']) """ pretty_permissions = self.format_permission_object(permissions) LOGGER.warning("Removing the following permissions to %s: %s", user_id, pretty_permissions) return self.user_service.removeBulkPortalPermission(pretty_permissions, id=user_id) def permissions_from_user(self, user_id, from_user_id): """Sets user_id's permission to be the same as from_user_id's Any permissions from_user_id has will be added to user_id. Any permissions from_user_id doesn't have will be removed from user_id. :param int user_id: The user to change permissions. :param int from_user_id: The use to base permissions from. :returns: True on success, Exception otherwise. """ from_permissions = self.get_user_permissions(from_user_id) self.add_permissions(user_id, from_permissions) all_permissions = self.get_all_permissions() remove_permissions = [] for permission in all_permissions: # If permission does not exist for from_user_id add it to the list to be removed if _keyname_search(from_permissions, permission['keyName']): continue remove_permissions.append({'keyName': permission['keyName']}) self.remove_permissions(user_id, remove_permissions) return True def get_user_permissions(self, user_id): """Returns a sorted list of a users permissions""" permissions = self.user_service.getPermissions(id=user_id) return sorted(permissions, key=itemgetter('keyName')) def get_logins(self, user_id, start_date=None): """Gets the login history for a user, default start_date is 30 days ago :param int id: User id to get :param string start_date: "%m/%d/%Y %H:%M:%s" formatted string. :returns: list https://softlayer.github.io/reference/datatypes/SoftLayer_User_Customer_Access_Authentication/ Example:: get_logins(123, '04/08/2018 0:0:0') """ if start_date is None: date_object = datetime.datetime.today() - datetime.timedelta(days=30) start_date = date_object.strftime("%m/%d/%Y 0:0:0") date_filter = { 'loginAttempts': { 'createDate': { 'operation': 'greaterThanDate', 'options': [{'name': 'date', 'value': [start_date]}] } } } login_log = self.user_service.getLoginAttempts(id=user_id, filter=date_filter) return login_log def get_events(self, user_id, start_date=None): """Gets the event log for a specific user, default start_date is 30 days ago :param int id: User id to view :param string start_date: "%Y-%m-%dT%H:%M:%s.0000-06:00" is the full formatted string. The Timezone part has to be HH:MM, notice the : there. :returns: https://softlayer.github.io/reference/datatypes/SoftLayer_Event_Log/ """ if start_date is None: date_object = datetime.datetime.today() - datetime.timedelta(days=30) start_date = date_object.strftime("%Y-%m-%dT00:00:00") object_filter = { 'userId': { 'operation': user_id }, 'eventCreateDate': { 'operation': 'greaterThanDate', 'options': [{'name': 'date', 'value': [start_date]}] } } events = self.client.call('Event_Log', 'getAllObjects', filter=object_filter) if events is None: events = [{'eventName': 'No Events Found'}] return events def _get_id_from_username(self, username): """Looks up a username's id :param string username: Username to lookup :returns: The id that matches username. """ _mask = "mask[id, username]" _filter = {'users': {'username': utils.query_filter(username)}} user = self.list_users(_mask, _filter) if len(user) == 1: return [user[0]['id']] elif len(user) > 1: raise exceptions.SoftLayerError("Multiple users found with the name: %s" % username) else: raise exceptions.SoftLayerError("Unable to find user id for %s" % username) def format_permission_object(self, permissions): """Formats a list of permission key names into something the SLAPI will respect. :param list permissions: A list of SLAPI permissions keyNames. keyName of ALL will return all permissions. :returns: list of dictionaries that can be sent to the api to add or remove permissions :throws SoftLayerError: If any permission is invalid this exception will be thrown. """ pretty_permissions = [] available_permissions = self.get_all_permissions() # pp(available_permissions) for permission in permissions: # Handle data retrieved directly from the API if isinstance(permission, dict): permission = permission['keyName'] permission = permission.upper() if permission == 'ALL': return available_permissions # Search through available_permissions to make sure what the user entered was valid if _keyname_search(available_permissions, permission): pretty_permissions.append({'keyName': permission}) else: raise exceptions.SoftLayerError("'%s' is not a valid permission" % permission) return pretty_permissions def gather_notifications(self, notifications_names): """Gets a list of notifications. :param list notifications_names: A list of notifications names. :returns: list of notifications. """ notifications = [] available_notifications = self.get_all_notifications() for notification in notifications_names: result = next((item for item in available_notifications if item.get('name') == notification), None) if result: notifications.append(result) else: raise exceptions.SoftLayerError("{} is not a valid notification name".format(notification)) return notifications def create_user(self, user_object, password): """Blindly sends user_object to SoftLayer_User_Customer::createObject :param dictionary user_object: https://softlayer.github.io/reference/datatypes/SoftLayer_User_Customer/ """ LOGGER.warning("Creating User %s", user_object['username']) try: return self.user_service.createObject(user_object, password, None) except exceptions.SoftLayerAPIError as ex: if ex.faultCode == "SoftLayer_Exception_User_Customer_DelegateIamIdInvitationToPaas": raise exceptions.SoftLayerError("Your request for a new user was received, but it needs to be " "processed by the Platform Services API first. Barring any errors on " "the Platform Services side, your new user should be created shortly.") raise def edit_user(self, user_id, user_object): """Blindly sends user_object to SoftLayer_User_Customer::editObject :param int user_id: User to edit :param dictionary user_object: https://softlayer.github.io/reference/datatypes/SoftLayer_User_Customer/ """ return self.user_service.editObject(user_object, id=user_id) def add_api_authentication_key(self, user_id): """Calls SoftLayer_User_Customer::addApiAuthenticationKey :param int user_id: User to add API key to """ return self.user_service.addApiAuthenticationKey(id=user_id) def vpn_manual(self, user_id, value): """Enable or disable the manual config of subnets. :param int user_id: User to edit. :param bool value: Value for vpnManualConfig flag. """ user_object = {'vpnManualConfig': value} return self.edit_user(user_id, user_object) def vpn_subnet_add(self, user_id, subnet_ids): """Add subnets for a user. :param int user_id: User to edit. :param list subnet_ids: list of subnet Ids. """ overrides = [{"userId": user_id, "subnetId": subnet_id} for subnet_id in subnet_ids] return_value = self.override_service.createObjects(overrides) update_success = self.user_service.updateVpnUser(id=user_id) if not update_success: raise exceptions.SoftLayerAPIError("Overrides created, but unable to update VPN user") return return_value def vpn_subnet_remove(self, user_id, subnet_ids): """Remove subnets for a user. :param int user_id: User to edit. :param list subnet_ids: list of subnet Ids. """ overrides = self.get_overrides_list(user_id, subnet_ids) return_value = self.override_service.deleteObjects(overrides) update_success = self.user_service.updateVpnUser(id=user_id) if not update_success: raise exceptions.SoftLayerAPIError("Overrides deleted, but unable to update VPN user") return return_value def get_overrides_list(self, user_id, subnet_ids): """Converts a list of subnets to a list of overrides. :param int user_id: The ID of the user. :param list subnet_ids: A list of subnets. :returns: A list of overrides associated with the given subnets. """ overrides_list = [] matching_overrides = {} output_error = "Subnet {} does not exist in the subnets assigned for user {}" _mask = 'mask[id,subnetId]' overrides = self.user_service.getOverrides(id=user_id, mask=_mask) for subnet in subnet_ids: for override in overrides: if int(subnet) == override.get('subnetId'): matching_overrides = override break if matching_overrides.get('subnetId') is None: raise exceptions.SoftLayerError(output_error.format(subnet, user_id)) overrides_list.append(matching_overrides) return overrides_list def _keyname_search(haystack, needle): for item in haystack: if item.get('keyName') == needle: return True return False softlayer-python-5.9.8/SoftLayer/managers/vs.py000066400000000000000000001664441415376457700216350ustar00rootroot00000000000000""" 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.exceptions import SoftLayerError from SoftLayer.managers.hardware import _get_preset_cost from SoftLayer.managers.hardware import get_item_price from SoftLayer.managers import ordering from SoftLayer import utils LOGGER = logging.getLogger(__name__) EXTRA_CATEGORIES = ['pri_ipv6_addresses', 'static_ipv6_addresses', 'sec_ip_addresses', 'trusted_platform_module', 'software_guard_extensions'] # pylint: disable=no-self-use,too-many-lines class VSManager(utils.IdentifierMixin, object): """Manages SoftLayer Virtual Servers. See product information here: https://www.ibm.com/cloud/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.package_svc = client['Product_Package'] self.storage_iscsi = client['SoftLayer_Network_Storage_Iscsi'] 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, transient=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 boolean transient: filter on transient or non-transient instances :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)) if transient is not None: _filter['virtualGuests']['transientGuestFlag'] = ( utils.query_filter(bool(transient)) ) kwargs['filter'] = _filter.to_dict() kwargs['iter'] = True return self.client.call('Account', call, **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,' 'transientGuestFlag,' 'privateNetworkOnlyFlag,' 'primaryBackendIpAddress,' 'primaryIpAddress,' '''networkComponents[id, status, speed, maxSpeed, name, macAddress, primaryIpAddress, port, primarySubnet[addressSpace], securityGroupBindings[ securityGroup[id, name]]],''' 'lastKnownPowerState.name,' 'powerState,' 'status,' 'maxCpu,' 'maxMemory,' 'datacenter,' 'activeTransaction[id, transactionStatus[friendlyName,name]],' 'lastTransaction[transactionStatus,modifyDate,transactionGroup[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, package[id,keyName], children[description,categoryCode,nextInvoiceTotalRecurringAmount], orderItem[id, order.userRecord[username], preset.keyName]],''' 'tagReferences[id,tag[name,id]],' 'networkVlans[id,vlanNumber,networkSpace],' 'dedicatedHost.id,' 'placementGroupId' ) return self.guest.getObject(id=instance_id, **kwargs) @retry(logger=LOGGER) def get_create_options(self, vsi_type="PUBLIC_CLOUD_SERVER", datacenter=None): """Retrieves the available options for creating a VS. :param string vsi_type: vs keyName. :param string datacenter: short name, like dal09 :returns: A dictionary of creation options. Example:: # Prints out the create option dictionary options = mgr.get_create_options() print(options) """ # TRANSIENT_CLOUD_SERVER # SUSPEND_CLOUD_SERVER package = self._get_package(vsi_type) location_group_id = None if datacenter: _filter = {"name": {"operation": datacenter}} _mask = "mask[priceGroups]" dc_details = self.client.call('SoftLayer_Location', 'getDatacenters', mask=_mask, filter=_filter, limit=1) if not dc_details: raise SoftLayerError("Unable to find a datacenter named {}".format(datacenter)) # A DC will have several price groups, no good way to deal with this other than checking each. # An item should only belong to one type of price group. for group in dc_details[0].get('priceGroups', []): # We only care about SOME of the priceGroups, which all SHOULD start with `Location Group X` # Hopefully this never changes.... if group.get('description').startswith('Location'): location_group_id = group.get('id') # Locations locations = [] for region in package['regions']: if datacenter is None or datacenter == region['location']['location']['name']: locations.append({ 'name': region['location']['location']['longName'], 'key': region['location']['location']['name'], }) operating_systems = [] database = [] port_speeds = [] guest_core = [] local_disk = [] extras = [] ram = [] sizes = [] for preset in package['activePresets'] + package['accountRestrictedActivePresets']: sizes.append({ 'name': preset['description'], 'key': preset['keyName'], 'hourlyRecurringFee': _get_preset_cost(preset, package['items'], 'hourly', location_group_id), 'recurringFee': _get_preset_cost(preset, package['items'], 'monthly', location_group_id) }) for item in package['items']: category = item['itemCategory']['categoryCode'] # Operating systems if category == 'os': operating_systems.append({ 'name': item['softwareDescription']['longDescription'], 'key': item['keyName'], 'referenceCode': item['softwareDescription']['referenceCode'], 'prices': get_item_price(item['prices'], location_group_id) }) # database elif category == 'database': database.append({ 'name': item['description'], 'key': item['keyName'], 'prices': get_item_price(item['prices'], location_group_id) }) elif category == 'port_speed': port_speeds.append({ 'name': item['description'], 'speed': item['capacity'], 'key': item['keyName'], 'prices': get_item_price(item['prices'], location_group_id) }) elif category == 'guest_core': guest_core.append({ 'name': item['description'], 'capacity': item['capacity'], 'key': item['keyName'], 'prices': get_item_price(item['prices'], location_group_id) }) elif category == 'ram': ram.append({ 'name': item['description'], 'capacity': item['capacity'], 'key': item['keyName'], 'prices': get_item_price(item['prices'], location_group_id) }) elif category.__contains__('guest_disk'): local_disk.append({ 'name': item['description'], 'capacity': item['capacity'], 'key': item['keyName'], 'disk': category, 'prices': get_item_price(item['prices'], location_group_id) }) # Extras elif category in EXTRA_CATEGORIES: extras.append({ 'name': item['description'], 'key': item['keyName'], 'prices': get_item_price(item['prices'], location_group_id) }) return { 'locations': locations, 'ram': ram, 'database': database, 'operating_systems': operating_systems, 'guest_core': guest_core, 'port_speed': port_speeds, 'guest_disk': local_disk, 'sizes': sizes, 'extras': extras, } @retry(logger=LOGGER) def _get_package(self, package_keyname): """Get the package related to simple vs ordering. :param string package_keyname: Virtual Server package keyName. """ items_mask = 'mask[id,keyName,capacity,description,attributes[id,attributeTypeKeyName],' \ 'itemCategory[id,categoryCode],softwareDescription[id,referenceCode,longDescription],' \ 'prices[categories]]' # The preset prices list will only have default prices. The prices->item->prices will have location specific presets_mask = 'mask[prices]' region_mask = 'location[location[priceGroups]]' package = {'items': [], 'activePresets': [], 'accountRestrictedActivePresets': [], 'regions': []} package_info = self.ordering_manager.get_package_by_key(package_keyname, mask="mask[id]") package['items'] = self.client.call('SoftLayer_Product_Package', 'getItems', id=package_info.get('id'), mask=items_mask) package['activePresets'] = self.client.call('SoftLayer_Product_Package', 'getActivePresets', id=package_info.get('id'), mask=presets_mask) package['accountRestrictedActivePresets'] = self.client.call('SoftLayer_Product_Package', 'getAccountRestrictedActivePresets', id=package_info.get('id'), mask=presets_mask) package['regions'] = self.client.call('SoftLayer_Product_Package', 'getRegions', id=package_info.get('id'), mask=region_mask) return package 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 GUID 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'] = list(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, private_subnet=None, public_subnet=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, transient=False, **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 transient: data['transientGuestFlag'] = transient if image_id: data["blockDeviceTemplateGroup"] = {"globalIdentifier": image_id} elif os_code: data["operatingSystemReferenceCode"] = os_code if datacenter: data["datacenter"] = {"name": datacenter} network_components = self._create_network_components(public_vlan, private_vlan, private_subnet, public_subnet, kwargs.get('private_router'), kwargs.get('public_router')) data.update(network_components) 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 def _create_network_components( self, public_vlan=None, private_vlan=None, private_subnet=None, public_subnet=None, private_router=None, public_router=None): parameters = {} if any([private_router, public_router]) and any([private_vlan, public_vlan, private_subnet, public_subnet]): raise exceptions.SoftLayerError("You have to select network vlan or network vlan with a subnet or " "only router, not all options") if private_router: parameters['primaryBackendNetworkComponent'] = {"router": {"id": int(private_router)}} if public_router: parameters['primaryNetworkComponent'] = {"router": {"id": int(public_router)}} if private_vlan: parameters['primaryBackendNetworkComponent'] = {"networkVlan": {"id": int(private_vlan)}} if public_vlan: parameters['primaryNetworkComponent'] = {"networkVlan": {"id": int(public_vlan)}} if public_subnet: if public_vlan is None: raise exceptions.SoftLayerError("You need to specify a public_vlan with public_subnet") parameters['primaryNetworkComponent']['networkVlan']['primarySubnet'] = {'id': int(public_subnet)} if private_subnet: if private_vlan is None: raise exceptions.SoftLayerError("You need to specify a private_vlan with private_subnet") parameters['primaryBackendNetworkComponent']['networkVlan']['primarySubnet'] = {'id': int(private_subnet)} return parameters @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', 'flavor': 'BL1_1X2X100' 'dedicated': False, 'private': False, 'transient': False, 'os_code' : u'UBUNTU_LATEST', 'hourly': True, 'ssh_keys': [1234], 'disks': ('100','25'), 'local_disk': True, 'tags': 'test, pleaseCancel', 'public_security_groups': [12, 15] } 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) template = self.guest.generateOrderTemplate(create_options) if kwargs.get('public_router') or kwargs.get('private_router'): if kwargs.get('private_vlan') or kwargs.get('public_vlan') or kwargs.get('private_subnet') \ or kwargs.get('public_subnet'): raise exceptions.SoftLayerError("You have to select network vlan or network vlan with a subnet or " "only router, not all options") vsi = template['virtualGuests'][0] network_components = self._create_network_components(kwargs.get('public_vlan', None), kwargs.get('private_vlan', None), kwargs.get('private_subnet', None), kwargs.get('public_subnet', None), kwargs.get('private_router', None), kwargs.get('public_router', None)) vsi.update(network_components) if kwargs.get('private_subnet') or kwargs.get('public_subnet'): vsi = template['virtualGuests'][0] network_components = self._create_network_components(kwargs.get('public_vlan', None), kwargs.get('private_vlan', None), kwargs.get('private_subnet', None), kwargs.get('public_subnet', None)) vsi.update(network_components) return template 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', 'flavor': 'BL1_1X2X100' 'dedicated': False, 'private': False, 'os_code' : u'UBUNTU_LATEST', 'hourly': True, 'ssh_keys': [1234], 'disks': ('100','25'), 'local_disk': True, '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 GUID 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'minion05', 'datacenter': u'hkg02', 'flavor': 'BL1_1X2X100' 'dedicated': False, 'private': False, 'os_code' : u'UBUNTU_LATEST', 'hourly': True, 'ssh_keys': [1234], 'disks': ('100','25'), 'local_disk': True, '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, preset=None, disk=None): """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 string preset: preset assigned to the vsi :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 = [] data = {'nic_speed': nic_speed} if cpus is not None and preset is not None: raise ValueError("Do not use cpu, private and memory if you are using flavors") data['cpus'] = cpus if memory is not None and preset is not None: raise ValueError("Do not use memory, private or cpu if you are using flavors") data['memory'] = memory maintenance_window = datetime.datetime.now(utils.UTC()) order = { 'complexType': 'SoftLayer_Container_Product_Order_Virtual_Guest_Upgrade', 'properties': [{ 'name': 'MAINTENANCE_WINDOW', 'value': maintenance_window.strftime("%Y-%m-%d %H:%M:%S%z") }], 'virtualGuests': [{'id': int(instance_id)}], } if disk: disk_number = 0 vsi_disk = self.get_instance(instance_id) for item in vsi_disk.get('billingItem').get('children'): if item.get('categoryCode').__contains__('guest_disk'): if disk_number < int("".join(filter(str.isdigit, item.get('categoryCode')))): disk_number = int("".join(filter(str.isdigit, item.get('categoryCode')))) for disk_guest in disk: if disk_guest.get('number') > 0: price_id = self._get_price_id_for_upgrade_option(upgrade_prices, 'disk', disk_guest.get('capacity'), public) disk_number = disk_guest.get('number') else: price_id = self._get_price_id_for_upgrade_option(upgrade_prices, 'disk', disk_guest.get('capacity'), public) disk_number = disk_number + 1 if price_id is None: raise exceptions.SoftLayerAPIError(500, 'Unable to find %s option with value %s' % ( ('disk', disk_guest.get('capacity')))) category = {'categories': [{ 'categoryCode': 'guest_disk' + str(disk_number), 'complexType': "SoftLayer_Product_Item_Category"}], 'complexType': 'SoftLayer_Product_Item_Price', 'id': price_id} prices.append(category) order['prices'] = prices for option, value in data.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}) order['prices'] = prices if preset is not None: vs_object = self.get_instance(instance_id)['billingItem']['package'] order['presetId'] = self.ordering_manager.get_preset_by_key(vs_object['keyName'], preset)['id'] if prices or preset: self.client['Product_Order'].placeOrder(order) return True return False def order_guest(self, guest_object, test=False): """Uses Product_Order::placeOrder to create a virtual guest. Useful when creating a virtual guest with options not supported by Virtual_Guest::createObject specifically ipv6 support. :param dictionary guest_object: See SoftLayer.CLI.virt.create._parse_create_args Example:: new_vsi = { 'domain': u'test01.labs.sftlyr.ws', 'hostname': u'minion05', 'datacenter': u'hkg02', 'flavor': 'BL1_1X2X100' 'dedicated': False, 'private': False, 'transient': False, 'os_code' : u'UBUNTU_LATEST', 'hourly': True, 'ssh_keys': [1234], 'disks': ('100','25'), 'local_disk': True, 'tags': 'test, pleaseCancel', 'public_security_groups': [12, 15], 'ipv6': True } vsi = mgr.order_guest(new_vsi) # vsi will have the newly created vsi receipt. # vsi['orderDetails']['virtualGuests'] will be an array of created Guests print vsi """ tags = guest_object.pop('tags', None) template = self.verify_create_instance(**guest_object) if guest_object.get('ipv6'): ipv6_price = self.ordering_manager.get_price_id_list('PUBLIC_CLOUD_SERVER', ['1_IPV6_ADDRESS']) template['prices'].append({'id': ipv6_price[0]}) # Notice this is `userdata` from the cli, but we send it in as `userData` if guest_object.get('userdata'): # SL_Virtual_Guest::generateOrderTemplate() doesn't respect userData, so we need to add it ourself template['virtualGuests'][0]['userData'] = [{"value": guest_object.get('userdata')}] if guest_object.get('host_id'): template['hostId'] = guest_object.get('host_id') if guest_object.get('placement_id'): template['virtualGuests'][0]['placementGroupId'] = guest_object.get('placement_id') if test: result = self.client.call('Product_Order', 'verifyOrder', template) else: result = self.client.call('Product_Order', 'placeOrder', template) if tags is not None: virtual_guests = utils.lookup(result, 'orderDetails', 'virtualGuests') for guest in virtual_guests: self.set_tags(tags, guest_id=guest['id']) return result 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', 'disk': 'guest_disk' } 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 option == 'disk': if (category_code == (''.join([i for i in category.get('categoryCode') if not i.isdigit()])) and str(product.get('capacity')) == str(value)): return price.get('id') 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') def get_summary_data_usage(self, instance_id, start_date=None, end_date=None, valid_type=None, summary_period=None): """Retrieve the usage information of a virtual server. :param string instance_id: a string identifier used to resolve ids :param string start_date: the start data to retrieve the vs usage information :param string end_date: the start data to retrieve the vs usage information :param string string valid_type: the Metric_Data_Type keyName. :param int summary_period: summary period. """ valid_types = [ { "keyName": valid_type, "summaryType": "max" } ] metric_tracking_id = self.get_tracking_id(instance_id) return self.client.call('Metric_Tracking_Object', 'getSummaryData', start_date, end_date, valid_types, summary_period, id=metric_tracking_id, iter=True) def get_tracking_id(self, instance_id): """Returns the Metric Tracking Object Id for a hardware server :param int instance_id: Id of the hardware server """ return self.guest.getMetricTrackingObjectId(id=instance_id) def get_bandwidth_data(self, instance_id, start_date=None, end_date=None, direction=None, rollup=3600): """Gets bandwidth data for a server Will get averaged bandwidth data for a given time period. If you use a rollup over 3600 be aware that the API will bump your start/end date to align with how data is stored. For example if you have a rollup of 86400 your start_date will be bumped to 00:00. If you are not using a time in the start/end date fields, this won't really matter. :param int instance_id: Hardware Id to get data for :param date start_date: Date to start pulling data for. :param date end_date: Date to finish pulling data for :param string direction: Can be either 'public', 'private', or None for both. :param int rollup: 300, 600, 1800, 3600, 43200 or 86400 seconds to average data over. """ tracking_id = self.get_tracking_id(instance_id) data = self.client.call('Metric_Tracking_Object', 'getBandwidthData', start_date, end_date, direction, rollup, id=tracking_id, iter=True) return data def get_bandwidth_allocation(self, instance_id): """Combines getBandwidthAllotmentDetail() and getBillingCycleBandwidthUsage() """ a_mask = "mask[allocation[amount]]" allotment = self.client.call('Virtual_Guest', 'getBandwidthAllotmentDetail', id=instance_id, mask=a_mask) u_mask = "mask[amountIn,amountOut,type]" usage = self.client.call('Virtual_Guest', 'getBillingCycleBandwidthUsage', id=instance_id, mask=u_mask) if allotment: return {'allotment': allotment.get('allocation'), 'usage': usage} return {'allotment': allotment, 'usage': usage} # 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'] def get_storage_details(self, instance_id, nas_type): """Returns the virtual server attached network storage. :param int instance_id: Id of the virtual server :param nas_type: storage type. """ mask = 'mask[id,username,capacityGb,notes,serviceResourceBackendIpAddress,' \ 'allowedVirtualGuests[id,datacenter]]' return self.guest.getAttachedNetworkStorages(nas_type, mask=mask, id=instance_id) def get_storage_credentials(self, instance_id): """Returns the virtual server storage credentials. :param int instance_id: Id of the virtual server """ mask = 'mask[credential]' return self.guest.getAllowedHost(mask=mask, id=instance_id) def get_portable_storage(self, instance_id): """Returns the virtual server portable storage. :param int instance_id: Id of the virtual server """ object_filter = {"portableStorageVolumes": {"blockDevices": {"guest": {"id": {"operation": instance_id}}}}} return self.account.getPortableStorageVolumes(filter=object_filter) def get_local_disks(self, instance_id): """Returns the virtual server local disks. :param int instance_id: Id of the virtual server """ mask = 'mask[diskImage]' return self.guest.getBlockDevices(mask=mask, id=instance_id) def migrate(self, instance_id): """Calls SoftLayer_Virtual_Guest::migrate Only actually does anything if the virtual server requires a migration. Will return an exception otherwise. :param int instance_id: Id of the virtual server """ return self.guest.migrate(id=instance_id) def migrate_dedicated(self, instance_id, host_id): """Calls SoftLayer_Virtual_Guest::migrate Only actually does anything if the virtual server requires a migration. Will return an exception otherwise. :param int instance_id: Id of the virtual server """ return self.guest.migrateDedicatedHost(host_id, id=instance_id) def get_hardware_guests(self): """Returns all virtualHost capable hardware objects and their guests. :return SoftLayer_Hardware[]. """ object_filter = {"hardware": {"virtualHost": {"id": {"operation": "not null"}}}} mask = "mask[virtualHost[guests[powerState]]]" return self.client.call('SoftLayer_Account', 'getHardware', mask=mask, filter=object_filter) def authorize_storage(self, vs_id, username_storage): """Authorize File or Block Storage to a Virtual Server. :param int vs_id: Virtual server id. :param string username_storage: Storage username. :return: bool. """ _filter = {"networkStorage": {"username": {"operation": username_storage}}} storage_result = self.client.call('Account', 'getNetworkStorage', filter=_filter) if len(storage_result) == 0: raise SoftLayerError("The Storage with username: %s was not found, please" " enter a valid storage username" % username_storage) storage_template = [ { "id": storage_result[0]['id'], "username": username_storage } ] result = self.client.call('SoftLayer_Virtual_Guest', 'allowAccessToNetworkStorageList', storage_template, id=vs_id) return result def attach_portable_storage(self, vs_id, portable_id): """Attach portal storage to a Virtual Server. :param int vs_id: Virtual server id. :param int portable_id: Portal storage id. :return: SoftLayer_Provisioning_Version1_Transaction. """ disk_id = portable_id result = self.client.call('SoftLayer_Virtual_Guest', 'attachDiskImage', disk_id, id=vs_id) return result softlayer-python-5.9.8/SoftLayer/managers/vs_capacity.py000066400000000000000000000166031415376457700235010ustar00rootroot00000000000000""" SoftLayer.vs_capacity ~~~~~~~~~~~~~~~~~~~~~~~ Reserved Capacity Manager and helpers :license: MIT, see License for more details. """ import logging from SoftLayer.exceptions import SoftLayerError from SoftLayer.managers import ordering from SoftLayer.managers.vs import VSManager 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 CapacityManager(utils.IdentifierMixin, object): """Manages SoftLayer Reserved Capacity Groups. Product Information - https://cloud.ibm.com/docs/virtual-servers?topic=virtual-servers-about-reserved-virtual-servers - https://softlayer.github.io/reference/services/SoftLayer_Virtual_ReservedCapacityGroup/ - https://softlayer.github.io/reference/services/SoftLayer_Virtual_ReservedCapacityGroup_Instance/ :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.capacity_package = 'RESERVED_CAPACITY' self.rcg_service = 'Virtual_ReservedCapacityGroup' if ordering_manager is None: self.ordering_manager = ordering.OrderingManager(client) def list(self): """List Reserved Capacities""" mask = """mask[availableInstanceCount, occupiedInstanceCount, instances[id, billingItem[description, hourlyRecurringFee]], instanceCount, backendRouter[datacenter]]""" results = self.client.call('Account', 'getReservedCapacityGroups', mask=mask) return results def get_object(self, identifier, mask=None): """Get a Reserved Capacity Group :param int identifier: Id of the SoftLayer_Virtual_ReservedCapacityGroup :param string mask: override default object Mask """ if mask is None: mask = "mask[instances[billingItem[item[keyName],category], guest], backendRouter[datacenter]]" result = self.client.call(self.rcg_service, 'getObject', id=identifier, mask=mask) return result def get_create_options(self): """List available reserved capacity plans""" mask = "mask[attributes,prices[pricingLocationGroup]]" results = self.ordering_manager.list_items(self.capacity_package, mask=mask) return results def get_available_routers(self, dc=None): """Pulls down all backendRouterIds that are available :param string dc: A specific location to get routers for, like 'dal13'. :returns list: A list of locations where RESERVED_CAPACITY can be ordered. """ mask = "mask[locations]" # Step 1, get the package id package = self.ordering_manager.get_package_by_key(self.capacity_package, mask="id") # Step 2, get the regions this package is orderable in regions = self.client.call('Product_Package', 'getRegions', id=package['id'], mask=mask, iter=True) _filter = None routers = {} if dc is not None: _filter = {'datacenterName': {'operation': dc}} # Step 3, for each location in each region, get the pod details, which contains the router id pods = self.client.call('Network_Pod', 'getAllObjects', filter=_filter, iter=True) for region in regions: routers[region['keyname']] = [] for location in region['locations']: location['location']['pods'] = [] for pod in pods: if pod['datacenterName'] == location['location']['name']: location['location']['pods'].append(pod) # Step 4, return the data. return regions def create(self, name, backend_router_id, flavor, instances, test=False): """Orders a Virtual_ReservedCapacityGroup :param string name: Name for the new reserved capacity :param int backend_router_id: This selects the pod. See create_options for a list :param string flavor: Capacity KeyName, see create_options for a list :param int instances: Number of guest this capacity can support :param bool test: If True, don't actually order, just test. """ # Since orderManger needs a DC id, just send in 0, the API will ignore it args = (self.capacity_package, 0, [flavor]) extras = {"backendRouterId": backend_router_id, "name": name} kwargs = { 'extras': extras, 'quantity': instances, 'complex_type': 'SoftLayer_Container_Product_Order_Virtual_ReservedCapacity', 'hourly': True } if test: receipt = self.ordering_manager.verify_order(*args, **kwargs) else: receipt = self.ordering_manager.place_order(*args, **kwargs) return receipt def create_guest(self, capacity_id, test, guest_object): """Turns an empty Reserve Capacity into a real Virtual Guest :param int capacity_id: ID of the RESERVED_CAPACITY_GROUP to create this guest into :param bool test: True will use verifyOrder, False will use placeOrder :param dictionary guest_object: Below is the minimum info you need to send in guest_object = { 'domain': 'test.com', 'hostname': 'A1538172419', 'os_code': 'UBUNTU_LATEST_64', 'primary_disk': '25', } """ vs_manager = VSManager(self.client) mask = "mask[instances[id, billingItem[id, item[id,keyName]]], backendRouter[id, datacenter[name]]]" capacity = self.get_object(capacity_id, mask=mask) try: capacity_flavor = capacity['instances'][0]['billingItem']['item']['keyName'] flavor = _flavor_string(capacity_flavor, guest_object['primary_disk']) except KeyError as ex: raise SoftLayerError("Unable to find capacity Flavor.") from ex guest_object['flavor'] = flavor guest_object['datacenter'] = capacity['backendRouter']['datacenter']['name'] # Reserved capacity only supports SAN as of 20181008 guest_object['local_disk'] = False # Reserved capacity only supports monthly ordering via Virtual_Guest::generateOrderTemplate # Hourly ordering would require building out the order manually. guest_object['hourly'] = False template = vs_manager.verify_create_instance(**guest_object) template['reservedCapacityId'] = capacity_id if guest_object.get('ipv6'): ipv6_price = self.ordering_manager.get_price_id_list('PUBLIC_CLOUD_SERVER', ['1_IPV6_ADDRESS']) template['prices'].append({'id': ipv6_price[0]}) if test: result = self.client.call('Product_Order', 'verifyOrder', template) else: result = self.client.call('Product_Order', 'placeOrder', template) return result def _flavor_string(capacity_key, primary_disk): """Removed the _X_YEAR_TERM from capacity_key and adds the primary disk size, creating the flavor keyName This will work fine unless 10 year terms are invented... or flavor format changes... """ flavor = "%sX%s" % (capacity_key[:-12], primary_disk) return flavor softlayer-python-5.9.8/SoftLayer/managers/vs_placement.py000066400000000000000000000107631415376457700236550ustar00rootroot00000000000000""" SoftLayer.vs_placement ~~~~~~~~~~~~~~~~~~~~~~~ Placement Group Manager :license: MIT, see License for more details. """ import logging 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 PlacementManager(utils.IdentifierMixin, object): """Manages SoftLayer Reserved Capacity Groups. Product Information - https://console.test.cloud.ibm.com/docs/vsi/vsi_placegroup.html#placement-groups - https://softlayer.github.io/reference/services/SoftLayer_Account/getPlacementGroups/ - https://softlayer.github.io/reference/services/SoftLayer_Virtual_PlacementGroup_Rule/ Existing instances cannot be added to a placement group. You can only add a virtual server instance to a placement group at provisioning. To remove an instance from a placement group, you must delete or reclaim the instance. :param SoftLayer.API.BaseClient client: the client instance """ def __init__(self, client): self.client = client self.account = client['Account'] self.resolvers = [self._get_id_from_name] def list(self, mask=None): """List existing placement groups Calls SoftLayer_Account::getPlacementGroups """ if mask is None: mask = "mask[id, name, createDate, rule, guestCount, backendRouter[id, hostname]]" groups = self.client.call('Account', 'getPlacementGroups', mask=mask, iter=True) return groups def create(self, placement_object): """Creates a placement group A placement_object is defined as:: placement_object = { 'backendRouterId': 12345, 'name': 'Test Name', 'ruleId': 12345 } - https://softlayer.github.io/reference/datatypes/SoftLayer_Virtual_PlacementGroup/ :param dictionary placement_object: """ return self.client.call('SoftLayer_Virtual_PlacementGroup', 'createObject', placement_object) def get_routers(self): """Calls SoftLayer_Virtual_PlacementGroup::getAvailableRouters()""" return self.client.call('SoftLayer_Virtual_PlacementGroup', 'getAvailableRouters') def get_object(self, group_id, mask=None): """Returns a PlacementGroup Object https://softlayer.github.io/reference/services/SoftLayer_Virtual_PlacementGroup/getObject """ if mask is None: mask = "mask[id, name, createDate, rule, backendRouter[id, hostname]," \ "guests[activeTransaction[id,transactionStatus[name,friendlyName]]]]" return self.client.call('SoftLayer_Virtual_PlacementGroup', 'getObject', id=group_id, mask=mask) def delete(self, group_id): """Deletes a PlacementGroup Placement group must be empty to be deleted. https://softlayer.github.io/reference/services/SoftLayer_Virtual_PlacementGroup/deleteObject """ return self.client.call('SoftLayer_Virtual_PlacementGroup', 'deleteObject', id=group_id) def get_all_rules(self): """Returns all available rules for creating a placement group""" return self.client.call('SoftLayer_Virtual_PlacementGroup_Rule', 'getAllObjects') def get_rule_id_from_name(self, name): """Finds the rule that matches name. SoftLayer_Virtual_PlacementGroup_Rule.getAllObjects doesn't support objectFilters. """ results = self.client.call('SoftLayer_Virtual_PlacementGroup_Rule', 'getAllObjects') return [result['id'] for result in results if result['keyName'] == name.upper()] def get_backend_router_id_from_hostname(self, hostname): """Finds the backend router Id that matches the hostname given No way to use an objectFilter to find a backendRouter, so we have to search the hard way. """ results = self.client.call('SoftLayer_Network_Pod', 'getAllObjects') return [result['backendRouterId'] for result in results if result['backendRouterName'] == hostname.lower()] def _get_id_from_name(self, name): """List placement group ids which match the given name.""" _filter = { 'placementGroups': { 'name': {'operation': name} } } mask = "mask[id, name]" results = self.client.call('Account', 'getPlacementGroups', filter=_filter, mask=mask) return [result['id'] for result in results] softlayer-python-5.9.8/SoftLayer/shell/000077500000000000000000000000001415376457700201265ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/shell/__init__.py000066400000000000000000000000001415376457700222250ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/shell/cmd_env.py000066400000000000000000000007151415376457700221160ustar00rootroot00000000000000"""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.9.8/SoftLayer/shell/cmd_exit.py000066400000000000000000000003101415376457700222660ustar00rootroot00000000000000"""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.9.8/SoftLayer/shell/cmd_help.py000066400000000000000000000021171415376457700222540ustar00rootroot00000000000000"""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) if command.short_help is None: command.short_help = command.help 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.9.8/SoftLayer/shell/completer.py000066400000000000000000000044021415376457700224720ustar00rootroot00000000000000""" 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) def _click_autocomplete(root, text): """Completer generator for click applications.""" try: parts = shlex.split(text) except ValueError: return location, incomplete = _click_resolve_command(root, parts) if not text.endswith(' ') and not incomplete and text: return 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.9.8/SoftLayer/shell/core.py000066400000000000000000000054361415376457700214400ustar00rootroot00000000000000""" SoftLayer.CLI.shell.core ~~~~~~~~~~~~~~~~~~~~~~~~ An interactive shell which exposes the CLI :license: MIT, see LICENSE for more details. """ import copy import os import shlex import sys import traceback import click from prompt_toolkit.auto_suggest import AutoSuggestFromHistory from prompt_toolkit import PromptSession 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.""" @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) complete = completer.ShellCompleter(core.cli) session = PromptSession() while True: try: line = session.prompt( completer=complete, complete_while_typing=True, auto_suggest=AutoSuggestFromHistory(), ) # 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: 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.9.8/SoftLayer/shell/routes.py000066400000000000000000000006121415376457700220200ustar00rootroot00000000000000""" 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.9.8/SoftLayer/testing/000077500000000000000000000000001415376457700204745ustar00rootroot00000000000000softlayer-python-5.9.8/SoftLayer/testing/__init__.py000066400000000000000000000147441415376457700226170ustar00rootroot00000000000000""" SoftLayer.testing ~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ # Disable pylint import error and too many methods error # pylint: disable=invalid-name import logging import os.path import unittest from unittest import mock as mock from click import testing 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(unittest.TestCase): """Testcase class with PEP-8 compatible 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.""" def tear_down(self): """Aliased from tearDown.""" def setUp(self): # NOQA unittest.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 super().tearDown() 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_not_called_with(self, service, method, **props): """Used to assert that API calls were NOT called with given properties. Props are properties of the given transport.Request object. """ if self.calls(service, method, **props): raise AssertionError('%s::%s was 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.exception) 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', stdin=None): """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, input=stdin, obj=env or self.env) def assertRaises(self, exception, function_callable, *args, **kwds): # pylint: disable=arguments-differ """Converts testtools.assertRaises to unittest.assertRaises calls. testtools==2.4.0 require unittest2, which breaks pytest>=5.4.1 on skipTest. But switching to just using unittest breaks assertRaises because the format is slightly different. This basically just reformats the call so I don't have to re-write a bunch of tests. """ with super().assertRaises(exception) as cm: function_callable(*args, **kwds) return cm.exception 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.critical( '%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.9.8/SoftLayer/testing/xmlrpc.py000066400000000000000000000100361415376457700223530ustar00rootroot00000000000000""" SoftLayer.testing.xmlrpc ~~~~~~~~~~~~~~~~~~~~~~~~ XMP-RPC server which can use a transport to proxy requests for testing. :license: MIT, see LICENSE for more details. """ import http.server import logging import threading import xmlrpc.client import SoftLayer from SoftLayer import transports from SoftLayer import utils # pylint: disable=invalid-name, broad-except, arguments-differ class TestServer(http.server.HTTPServer): """Test HTTP server which holds a given transport.""" def __init__(self, transport, *args, **kw): http.server.HTTPServer.__init__(self, *args, **kw) self.transport = transport class TestHandler(http.server.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 = 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 = 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 (NotImplementedError, NameError) as ex: self.send_response(200) self.end_headers() response = xmlrpc.client.Fault(404, str(ex)) response_body = xmlrpc.client.dumps(response, allow_none=True, methodresponse=True) self.wfile.write(response_body.encode('utf-8')) except SoftLayer.SoftLayerAPIError as ex: self.send_response(200) self.end_headers() response = xmlrpc.client.Fault(ex.faultCode, str(ex.reason)) response_body = xmlrpc.client.dumps(response, allow_none=True, methodresponse=True) self.wfile.write(response_body.encode('utf-8')) except Exception: self.send_response(500) logging.exception("Error while handling request") def log_message(self, fmt, *args): """Override log_message.""" 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.9.8/SoftLayer/transports.py000066400000000000000000000463171415376457700216230ustar00rootroot00000000000000""" SoftLayer.transports ~~~~~~~~~~~~~~~~~~~~ XML-RPC transport layer that uses the requests library. :license: MIT, see LICENSE for more details. """ import base64 import importlib import json import logging import re from string import Template import time import xmlrpc.client 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, no-self-use __all__ = [ 'Request', 'XmlRpcTransport', 'RestTransport', 'TimingTransport', 'DebugTransport', '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 #: Integer call start time self.start_time = None #: Integer call end time self.end_time = None #: String full url self.url = None #: String result of api call self.result = None #: String payload to send in self.payload = None #: Exception any exceptions that got caught self.exception = None def __repr__(self): """Prints out what this call is all about""" pretty_mask = utils.clean_string(self.mask) pretty_filter = self.filter param_string = "id={id}, mask='{mask}', filter='{filter}', args={args}, limit={limit}, offset={offset}".format( id=self.identifier, mask=pretty_mask, filter=pretty_filter, args=self.args, limit=self.limit, offset=self.offset) return "{service}::{method}({params})".format( service=self.service, method=self.method, params=param_string) class SoftLayerListResult(list): """A SoftLayer API list result.""" def __init__(self, items=None, total_count=0): #: 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().__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 auth = None if request.transport_user: auth = requests.auth.HTTPBasicAuth(request.transport_user, request.transport_password) if request.identifier is not None: header_name = request.service + 'InitParameters' headers[header_name] = {'id': request.identifier} if request.mask is not None: if isinstance(request.mask, dict): mheader = '%sObjectMask' % request.service else: mheader = 'SoftLayer_ObjectMask' request.mask = _format_object_mask(request.mask) headers.update({mheader: {'mask': request.mask}}) 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) request.url = '/'.join([self.endpoint_url, request.service]) request.payload = xmlrpc.client.dumps(tuple(largs), methodname=request.method, allow_none=True, encoding="iso-8859-1") # Prefer the request setting, if it's not None verify = request.verify if verify is None: request.verify = self.verify try: resp = self.client.request('POST', request.url, data=request.payload.encode(), auth=auth, headers=request.transport_headers, timeout=self.timeout, verify=request.verify, cert=request.cert, proxies=_proxies_dict(self.proxy)) resp.raise_for_status() result = 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 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) from ex 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)) def print_reproduceable(self, request): """Prints out the minimal python code to reproduce a specific request The will also automatically replace the API key so its not accidently exposed. :param request request: Request object """ output = Template('''============= testing.py ============= import requests from requests.auth import HTTPBasicAuth from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry from xml.etree import ElementTree client = requests.Session() client.headers.update({'Content-Type': 'application/json', 'User-Agent': 'softlayer-python/testing',}) retry = Retry(connect=3, backoff_factor=3) adapter = HTTPAdapter(max_retries=retry) client.mount('https://', adapter) # This is only needed if you are using an cloud.ibm.com api key #auth=HTTPBasicAuth('apikey', YOUR_CLOUD_API_KEY) auth=None url = '$url' payload = $payload transport_headers = $transport_headers timeout = $timeout verify = $verify cert = $cert proxy = $proxy response = client.request('POST', url, data=payload, headers=transport_headers, timeout=timeout, verify=verify, cert=cert, proxies=proxy, auth=auth) xml = ElementTree.fromstring(response.content) ElementTree.dump(xml) ==========================''') safe_payload = re.sub(r'[a-z0-9]{64}', r'API_KEY_GOES_HERE', request.payload) safe_payload = re.sub(r'(\s+)', r' ', safe_payload) safe_payload = safe_payload.encode() substitutions = dict(url=request.url, payload=safe_payload, transport_headers=request.transport_headers, timeout=self.timeout, verify=request.verify, cert=request.cert, proxy=_proxies_dict(self.proxy)) return output.substitute(substitutions) 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: request.mask = _format_object_mask(request.mask) params['objectMask'] = request.mask if request.limit or request.offset: limit = request.limit or 0 offset = request.offset or 0 params['resultLimit'] = "%d,%d" % (offset, limit) if request.filter: params['objectFilter'] = json.dumps(request.filter) request.params = params 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 if body: request.payload = json.dumps(body, cls=ComplexEncoder) 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) request.url = '%s.%s' % ('/'.join(url_parts), 'json') # Prefer the request setting, if it's not None if request.verify is None: request.verify = self.verify try: resp = self.client.request(method, request.url, auth=auth, headers=request.transport_headers, params=request.params, data=request.payload, timeout=self.timeout, verify=request.verify, cert=request.cert, proxies=_proxies_dict(self.proxy)) request.url = resp.url resp.raise_for_status() if resp.text != "": try: result = json.loads(resp.text) except ValueError as json_ex: LOGGER.warning(json_ex) raise exceptions.SoftLayerAPIError(resp.status_code, str(resp.text)) else: raise exceptions.SoftLayerAPIError(resp.status_code, "Empty response.") request.result = result if isinstance(result, list): return SoftLayerListResult( result, int(resp.headers.get('softlayer-total-items', 0))) else: return result except requests.HTTPError as ex: try: message = json.loads(ex.response.text)['error'] request.url = ex.response.url except ValueError as json_ex: if ex.response.text == "": raise exceptions.SoftLayerAPIError(resp.status_code, "Empty response.") LOGGER.warning(json_ex) raise exceptions.SoftLayerAPIError(resp.status_code, ex.response.text) raise exceptions.SoftLayerAPIError(ex.response.status_code, message) except requests.RequestException as ex: raise exceptions.TransportError(0, str(ex)) def print_reproduceable(self, request): """Prints out the minimal python code to reproduce a specific request The will also automatically replace the API key so its not accidently exposed. :param request request: Request object """ command = "curl -u $SL_USER:$SL_APIKEY -X {method} -H {headers} {data} '{uri}'" method = REST_SPECIAL_METHODS.get(request.method) if method is None: method = 'GET' if request.args: method = 'POST' data = '' if request.payload is not None: data = "-d '{}'".format(request.payload) headers = ['"{0}: {1}"'.format(k, v) for k, v in request.transport_headers.items()] headers = " -H ".join(headers) return command.format(method=method, headers=headers, data=data, uri=request.url) class DebugTransport(object): """Transport that records API call timings.""" def __init__(self, transport): self.transport = transport #: List All API calls made during a session self.requests = [] def __call__(self, call): call.start_time = time.time() self.pre_transport_log(call) try: call.result = self.transport(call) except (exceptions.SoftLayerAPIError, exceptions.TransportError) as ex: call.exception = ex self.post_transport_log(call) call.end_time = time.time() self.requests.append(call) if call.exception is not None: LOGGER.debug(self.print_reproduceable(call)) raise call.exception return call.result def pre_transport_log(self, call): """Prints a warning before calling the API """ output = "Calling: {})".format(call) LOGGER.warning(output) def post_transport_log(self, call): """Prints the result "Returned Data: \n%s" % (call.result)of an API call""" output = "Returned Data: \n{}".format(call.result) LOGGER.debug(output) def get_last_calls(self): """Returns all API calls for a session""" return self.requests def print_reproduceable(self, call): """Prints a reproduceable debugging output""" return self.transport.print_reproduceable(call) 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 def print_reproduceable(self, call): """Not Implemented""" return call.service 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 as ex: message = '{} fixture is not implemented'.format(call.service) raise NotImplementedError(message) from ex try: return getattr(module, call.method) except AttributeError as ex: message = '{}::{} fixture is not implemented'.format(call.service, call.method) raise NotImplementedError(message) from ex def print_reproduceable(self, call): """Not Implemented""" return call.service 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(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('[') and not objectmask.startswith('filteredMask')): objectmask = "mask[%s]" % objectmask return objectmask class ComplexEncoder(json.JSONEncoder): """ComplexEncoder helps jsonencoder deal with byte strings""" def default(self, o): """Encodes o as JSON""" # Base64 encode bytes type objects. if isinstance(o, bytes): base64_bytes = base64.b64encode(o) return base64_bytes.decode("utf-8") # Let the base class default method raise the TypeError return json.JSONEncoder.default(self, o) softlayer-python-5.9.8/SoftLayer/utils.py000066400000000000000000000275421415376457700205430ustar00rootroot00000000000000""" SoftLayer.utils ~~~~~~~~~~~~~~~ Utility function/classes. :license: MIT, see LICENSE for more details. """ import collections import datetime import re import time # pylint: disable=no-member, invalid-name UUID_RE = re.compile(r'^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$', re.I) KNOWN_OPERATIONS = ['<=', '>=', '<', '>', '~', '!~', '*=', '^=', '$=', '_='] 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 dict_merge(dct1, dct2): """Recursively merges dct2 and dct1, ideal for merging objectFilter together. :param dct1: A dictionary :param dct2: A dictionary :return: dct1 + dct2 """ dct = dct1.copy() for k, _ in dct2.items(): if (k in dct1 and isinstance(dct1[k], dict) and isinstance(dct2[k], collections.abc.Mapping)): dct[k] = dict_merge(dct1[k], dct2[k]) else: dct[k] = dct2[k] return dct 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, str): 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']} ] } def query_filter_orderby(sort="ASC"): """Returns an object filter operation for sorting :param string sort: either ASC or DESC """ _filter = { "operation": "orderBy", "options": [{ "name": "sort", "value": [sort] }] } return _filter def format_event_log_date(date_string, utc): """Gets a date in the format that the SoftLayer_EventLog object likes. :param string date_string: date in mm/dd/yyyy format :param string utc: utc offset. Defaults to '+0000' """ user_date_format = "%m/%d/%Y" user_date = datetime.datetime.strptime(date_string, user_date_format) dirty_time = user_date.isoformat() if utc is None: utc = "+0000" iso_time_zone = utc[:3] + ':' + utc[3:] cleaned_time = "{}.000000{}".format(dirty_time, iso_time_zone) return cleaned_time def event_log_filter_between_date(start, end, utc): """betweenDate Query filter that SoftLayer_EventLog likes :param string start: lower bound date in mm/dd/yyyy format :param string end: upper bound date in mm/dd/yyyy format :param string utc: utc offset. Defaults to '+0000' """ return { 'operation': 'betweenDate', 'options': [ {'name': 'startDate', 'value': [format_event_log_date(start, utc)]}, {'name': 'endDate', 'value': [format_event_log_date(end, utc)]} ] } def event_log_filter_greater_than_date(date, utc): """greaterThanDate Query filter that SoftLayer_EventLog likes :param string date: lower bound date in mm/dd/yyyy format :param string utc: utc offset. Defaults to '+0000' """ return { 'operation': 'greaterThanDate', 'options': [ {'name': 'date', 'value': [format_event_log_date(date, utc)]} ] } def event_log_filter_less_than_date(date, utc): """lessThanDate Query filter that SoftLayer_EventLog likes :param string date: upper bound date in mm/dd/yyyy format :param string utc: utc offset. Defaults to '+0000' """ return { 'operation': 'lessThanDate', 'options': [ {'name': 'date', 'value': [format_event_log_date(date, utc)]} ] } def build_filter_orderby(orderby): """Builds filters using the filter options passed into the CLI. It only supports the orderBy option, the default value is DESC. """ _filters = {} reverse_filter = list(reversed(orderby.split('.'))) for keyword in reverse_filter: _aux_filter = {} if '=' in keyword: _aux_filter[str(keyword).split('=', maxsplit=1)[0]] = query_filter_orderby(str(keyword).split('=')[1]) _filters = _aux_filter elif keyword == list(reverse_filter)[0]: _aux_filter[keyword] = query_filter_orderby('DESC') else: _aux_filter[keyword] = _filters _filters = _aux_filter return _filters 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 def clean_string(string): """Returns a string with all newline and other whitespace garbage removed. Mostly this method is used to print out objectMasks that have a lot of extra whitespace in them because making compact masks in python means they don't look nice in the IDE. :param string: The string to clean. :returns string: A string without extra whitespace. """ if string is None: return '' else: return " ".join(string.split()) def clean_splitlines(string): """Returns a string where \r\n is replaced with \n""" if string is None: return '' else: return "\n".join(string.splitlines()) def clean_time(sltime, in_format='%Y-%m-%dT%H:%M:%S%z', out_format='%Y-%m-%d %H:%M'): """Easy way to format time strings :param string sltime: A softlayer formatted time string :param string in_format: Datetime format for strptime :param string out_format: Datetime format for strftime """ if sltime is None: return None try: clean = datetime.datetime.strptime(sltime, in_format) return clean.strftime(out_format) # The %z option only exists with py3.6+ except ValueError as e: # Just ignore data that in_format didn't process. ulr = len(e.args[0].partition('unconverted data remains: ')[2]) if ulr: clean = datetime.datetime.strptime(sltime[:-ulr], in_format) return clean.strftime(out_format) return sltime def timestamp(date): """Converts a datetime to timestamp :param datetime date: :returns int: The timestamp of date. """ _timestamp = time.mktime(date.timetuple()) return int(_timestamp) def days_to_datetime(days): """Returns the datetime value of last N days. :param int days: From 0 to N days :returns int: The datetime of last N days or datetime.now() if days <= 0. """ date = datetime.datetime.now() if days > 0: date -= datetime.timedelta(days=days) return date def trim_to(string, length=80, tail="..."): """Returns a string that is length long. tail added if trimmed :param string string: String you want to trim :param int length: max length for the string :param string tail: appended to strings that were trimmed. """ if len(string) > length: return string[:length] + tail else: return string def format_comment(comment, max_line_length=60): """Return a string that is length long, added a next line and keep the table format. :param string comment: String you want to add next line :param int max_line_length: max length for the string """ comment_length = 0 words = comment.split(" ") formatted_comment = "" for word in words: if comment_length + (len(word) + 1) <= max_line_length: formatted_comment = formatted_comment + word + " " comment_length = comment_length + len(word) + 1 else: formatted_comment = formatted_comment + "\n" + word + " " comment_length = len(word) + 1 return formatted_comment softlayer-python-5.9.8/docCheck.py000066400000000000000000000062141415376457700171670ustar00rootroot00000000000000"""Makes sure all routes have documentation""" import SoftLayer from SoftLayer.CLI import routes from pprint import pprint as pp import glob import logging import os import sys import re class Checker(): def __init__(self): pass def getDocFiles(self, path=None): files = [] if path is None: path = ".{seper}docs{seper}cli".format(seper=os.path.sep) for file in glob.glob(path + '/*', recursive=True): if os.path.isdir(file): files = files + self.getDocFiles(file) else: files.append(file) return files def readDocs(self, path=None): files = self.getDocFiles(path) commands = {} click_regex = re.compile(r"\.\. click:: ([a-zA-Z0-9_\.:]*)") prog_regex = re.compile(r"\W*:prog: (.*)") for file in files: click_line = '' prog_line = '' with open(file, 'r') as f: for line in f: click_match = re.match(click_regex, line) prog_match = False if click_match: click_line = click_match.group(1) # Prog line should always be directly after click line. prog_match = re.match(prog_regex, f.readline()) if prog_match: prog_line = prog_match.group(1).replace(" ", ":") commands[prog_line] = click_line click_line = '' prog_line = '' # pp(commands) return commands def checkCommand(self, command, documented_commands): """Sees if a command is documented :param tuple command: like the entry in the routes file ('command:action', 'SoftLayer.CLI.module.function') :param documented_commands: dictionary of commands found to be auto-documented. """ # These commands use a slightly different loader. ignored = [ 'virtual:capacity', 'virtual:placementgroup', 'object-storage:credential' ] if command[0] in ignored: return True if documented_commands.get(command[0], False) == command[1]: return True return False def main(self, debug=0): existing_commands = routes.ALL_ROUTES documented_commands = self.readDocs() # pp(documented_commands) exitCode = 0 for command in existing_commands: if (command[1].find(":") == -1): # Header commands in the routes file, dont need documentaiton. continue else: if self.checkCommand(command, documented_commands): if debug: print("{} is documented".format(command[0])) else: print("===> {} {} IS UNDOCUMENTED <===".format(command[0], command[1])) exitCode = 1 sys.exit(exitCode) if __name__ == "__main__": main = Checker() main.main() softlayer-python-5.9.8/docs/000077500000000000000000000000001415376457700160375ustar00rootroot00000000000000softlayer-python-5.9.8/docs/Makefile000066400000000000000000000130041415376457700174750ustar00rootroot00000000000000# 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.9.8/docs/_static/000077500000000000000000000000001415376457700174655ustar00rootroot00000000000000softlayer-python-5.9.8/docs/_static/style.css000066400000000000000000000001731415376457700213400ustar00rootroot00000000000000@import url("nature.css"); p.deprecated { background-color: #ffe4e4; border: 1px solid #f66; padding: 0.2em; }softlayer-python-5.9.8/docs/api/000077500000000000000000000000001415376457700166105ustar00rootroot00000000000000softlayer-python-5.9.8/docs/api/client.rst000066400000000000000000000213511415376457700206220ustar00rootroot00000000000000.. _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. More information on `Object Filters `_. :NOTE: The `value` field for startDate and endDate is in `[]`, if you do not put the date in brackets the filter will not work. :: 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. :: from pprint import pprint page1 = client.call('Account', 'getVirtualGuests', limit=10, offset=0) # Page 1 page2 = client.call('Account', 'getVirtualGuests', limit=10, offset=10) # Page 2 #Automatic Pagination (v5.5.3+), default limit is 100 result = client.call('Account', 'getVirtualGuests', iter=True, limit=10) pprint(result) # Using a python generator, default limit is 100 results = client.iter_call('Account', 'getVirtualGuests', limit=10) for result in results: pprint(result) :NOTE: `client.call(iter=True)` will pull all results, then return. `client.iter_call()` will return a generator, and only make API calls as you iterate over the results. 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' }) Debugging ------------- If you ever need to figure out what exact API call the client is making, you can do the following: *NOTE* the `print_reproduceable` method produces different output for REST and XML-RPC endpoints. If you are using REST, this will produce a CURL call. IF you are using XML-RPC, it will produce some pure python code you can use outside of the SoftLayer library. :: # Setup the client as usual client = SoftLayer.Client() # Create an instance of the DebugTransport, which logs API calls debugger = SoftLayer.DebugTransport(client.transport) # Set that as the default client transport client.transport = debugger # Make your API call client.call('Account', 'getObject') # Print out the reproduceable call for call in client.transport.get_last_calls(): print(client.transport.print_reproduceable(call)) Dealing with KeyError Exceptions -------------------------------- One of the pain points in dealing with the SoftLayer API can be handling issues where you expected a property to be returned, but none was. The hostname property of a `SoftLayer_Billing_Item `_ is a good example of this. For example. :: # Uses default username and apikey from ~/.softlayer client = SoftLayer.create_client_from_env() # iter_call returns a python generator, and only makes another API call when the loop runs out of items. result = client.iter_call('Account', 'getAllBillingItems', iter=True, mask="mask[id,hostName]") print("Id, hostname") for item in result: # will throw a KeyError: 'hostName' exception on certain billing items that do not have a hostName print("{}, {}".format(item['id'], item['hostName'])) The Solution ^^^^^^^^^^^^ Using the python dictionary's `.get() `_ is great for non-nested items. :: print("{}, {}".format(item.get('id'), item.get('hostName'))) Otherwise, this SDK provides a util function to do something similar. Each additional argument passed into `utils.lookup` will go one level deeper into the nested dictionary to find the item requested, returning `None` if a KeyError shows up. :: itemId = SoftLayer.utils.lookup(item, 'id') itemHostname = SoftLayer.utils.lookup(item, 'hostName') print("{}, {}".format(itemId, itemHostname)) API Reference ------------- .. automodule:: SoftLayer :members: :ignore-module-all: .. automodule:: SoftLayer.API :members: :ignore-module-all: .. automodule:: SoftLayer.exceptions :members: .. automodule:: SoftLayer.decoration :members: .. automodule:: SoftLayer.utils :members: softlayer-python-5.9.8/docs/api/managers/000077500000000000000000000000001415376457700204055ustar00rootroot00000000000000softlayer-python-5.9.8/docs/api/managers/account.rst000066400000000000000000000001341415376457700225710ustar00rootroot00000000000000.. _account: .. automodule:: SoftLayer.managers.account :members: :inherited-members:softlayer-python-5.9.8/docs/api/managers/autoscale.rst000066400000000000000000000001401415376457700231120ustar00rootroot00000000000000.. _autoscale: .. automodule:: SoftLayer.managers.autoscale :members: :inherited-members:softlayer-python-5.9.8/docs/api/managers/block.rst000066400000000000000000000001301415376457700222230ustar00rootroot00000000000000.. _block: .. automodule:: SoftLayer.managers.block :members: :inherited-members:softlayer-python-5.9.8/docs/api/managers/cdn.rst000066400000000000000000000001251415376457700217010ustar00rootroot00000000000000.. _cdn: .. automodule:: SoftLayer.managers.cdn :members: :inherited-members: softlayer-python-5.9.8/docs/api/managers/dedicated_host.rst000066400000000000000000000001531415376457700241010ustar00rootroot00000000000000.. _dedicated_host: .. automodule:: SoftLayer.managers.dedicated_host :members: :inherited-members: softlayer-python-5.9.8/docs/api/managers/dns.rst000066400000000000000000000001251415376457700217210ustar00rootroot00000000000000.. _dns: .. automodule:: SoftLayer.managers.dns :members: :inherited-members: softlayer-python-5.9.8/docs/api/managers/email.rst000066400000000000000000000001301415376457700222200ustar00rootroot00000000000000.. _email: .. automodule:: SoftLayer.managers.email :members: :inherited-members:softlayer-python-5.9.8/docs/api/managers/event_log.rst000066400000000000000000000001411415376457700231150ustar00rootroot00000000000000.. _event_log: .. automodule:: SoftLayer.managers.event_log :members: :inherited-members: softlayer-python-5.9.8/docs/api/managers/file.rst000066400000000000000000000001261415376457700220550ustar00rootroot00000000000000.. _file: .. automodule:: SoftLayer.managers.file :members: :inherited-members:softlayer-python-5.9.8/docs/api/managers/firewall.rst000066400000000000000000000001371415376457700227450ustar00rootroot00000000000000.. _firewall: .. automodule:: SoftLayer.managers.firewall :members: :inherited-members: softlayer-python-5.9.8/docs/api/managers/hardware.rst000066400000000000000000000001361415376457700227340ustar00rootroot00000000000000.. _hardware: .. automodule:: SoftLayer.managers.hardware :members: :inherited-members:softlayer-python-5.9.8/docs/api/managers/image.rst000066400000000000000000000001311415376457700222140ustar00rootroot00000000000000.. _image: .. automodule:: SoftLayer.managers.image :members: :inherited-members: softlayer-python-5.9.8/docs/api/managers/ipsec.rst000066400000000000000000000001311415376457700222350ustar00rootroot00000000000000.. _ipsec: .. automodule:: SoftLayer.managers.ipsec :members: :inherited-members: softlayer-python-5.9.8/docs/api/managers/load_balancer.rst000066400000000000000000000001511415376457700237020ustar00rootroot00000000000000.. _load_balancer: .. automodule:: SoftLayer.managers.load_balancer :members: :inherited-members: softlayer-python-5.9.8/docs/api/managers/metadata.rst000066400000000000000000000002421415376457700227150ustar00rootroot00000000000000.. _metadata: .. automodule:: SoftLayer.managers.metadata :members: :inherited-members: .. autoattribute:: SoftLayer.managers.metadata.METADATA_ATTRIBUTESsoftlayer-python-5.9.8/docs/api/managers/network.rst000066400000000000000000000001351415376457700226270ustar00rootroot00000000000000.. _network: .. automodule:: SoftLayer.managers.network :members: :inherited-members: softlayer-python-5.9.8/docs/api/managers/ordering.rst000066400000000000000000000001371415376457700227510ustar00rootroot00000000000000.. _ordering: .. automodule:: SoftLayer.managers.ordering :members: :inherited-members: softlayer-python-5.9.8/docs/api/managers/sshkey.rst000066400000000000000000000001331415376457700224420ustar00rootroot00000000000000.. _sshkey: .. automodule:: SoftLayer.managers.sshkey :members: :inherited-members: softlayer-python-5.9.8/docs/api/managers/ssl.rst000066400000000000000000000001251415376457700217360ustar00rootroot00000000000000.. _ssl: .. automodule:: SoftLayer.managers.ssl :members: :inherited-members: softlayer-python-5.9.8/docs/api/managers/ticket.rst000066400000000000000000000001331415376457700224170ustar00rootroot00000000000000.. _ticket: .. automodule:: SoftLayer.managers.ticket :members: :inherited-members: softlayer-python-5.9.8/docs/api/managers/vs.rst000066400000000000000000000001231415376457700215630ustar00rootroot00000000000000.. _vs: .. automodule:: SoftLayer.managers.vs :members: :inherited-members: softlayer-python-5.9.8/docs/api/managers/vs_capacity.rst000066400000000000000000000001451415376457700234440ustar00rootroot00000000000000.. _vs_capacity: .. automodule:: SoftLayer.managers.vs_capacity :members: :inherited-members: softlayer-python-5.9.8/docs/api/managers/vs_placement.rst000066400000000000000000000001471415376457700236210ustar00rootroot00000000000000.. _vs_placement: .. automodule:: SoftLayer.managers.vs_placement :members: :inherited-members: softlayer-python-5.9.8/docs/cli.rst000066400000000000000000000314511415376457700173440ustar00rootroot00000000000000.. _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. .. _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.1/ : :..............:..................................................................: 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.1/ : :..............:..................................................................: If you are using an account created from the https://cloud.ibm.com portal, your username will be literally `apikey`, and use the key provided. `How to create an IBM apikey `_ 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: raw] -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. dedicatedhost Dedicated Host. dns Domain Name System. email Email Deliviry Network event-log Event Logs. file File Storage. firewall Firewalls. globalip Global IP addresses. hardware Hardware servers. image Compute images. ipsec IPSEC VPN loadbal Load balancers. metadata Find details about this machine. nas Network Attached Storage. object-storage Object Storage. order View and order from the catalog. report Reports. securitygroup Network security groups. setup Edit configuration. shell Enters a shell for slcli. sshkey SSH Keys. ssl SSL Certificates. subnet Network subnets. summary Account summary. ticket Support tickets. user Manage Users. 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`. Example:: $ 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. Debugging ========= To see exactly what API call is being made by the SLCLI, you can use the verbose option. A single `-v` will show a simple version of the API call, along with some statistics :: slcli -v vs detail 74397127 Calling: SoftLayer_Virtual_Guest::getObject(id=74397127, mask='id,globalIdentifier,fullyQualifiedDomainName,hostname,domain', filter='None', args=(), limit=None, offset=None)) Calling: SoftLayer_Virtual_Guest::getReverseDomainRecords(id=77460683, mask='', filter='None', args=(), limit=None, offset=None)) :..................:..............................................................: : name : value : :..................:..............................................................: : execution_time : 2.020334s : : api_calls : SoftLayer_Virtual_Guest::getObject (1.515583s) : : : SoftLayer_Virtual_Guest::getReverseDomainRecords (0.494480s) : : version : softlayer-python/v5.7.2 : : python_version : 3.7.3 (default, Mar 27 2019, 09:23:15) : : : [Clang 10.0.1 (clang-1001.0.46.3)] : : library_location : /Users/chris/Code/py3/lib/python3.7/site-packages/SoftLayer : :..................:..............................................................: Using `-vv` will print out some API call details in the summary as well. :: slcli -vv account summary Calling: SoftLayer_Account::getObject(id=None, mask='mask[ nextInvoiceTotalAmount, pendingInvoice[invoiceTotalAmount], blockDeviceTemplateGroupCount, dedicatedHostCount, domainCount, hardwareCount, networkStorageCount, openTicketCount, networkVlanCount, subnetCount, userCount, virtualGuestCount ]', filter='None', args=(), limit=None, offset=None)) :..................:.............................................................: : name : value : :..................:.............................................................: : execution_time : 0.921271s : : api_calls : SoftLayer_Account::getObject (0.911208s) : : version : softlayer-python/v5.7.2 : : python_version : 3.7.3 (default, Mar 27 2019, 09:23:15) : : : [Clang 10.0.1 (clang-1001.0.46.3)] : : library_location : /Users/chris/Code/py3/lib/python3.7/site-packages/SoftLayer : :..................:.............................................................: :........:.................................................: : : SoftLayer_Account::getObject : :........:.................................................: : id : None : : mask : mask[ : : : nextInvoiceTotalAmount, : : : pendingInvoice[invoiceTotalAmount], : : : blockDeviceTemplateGroupCount, : : : dedicatedHostCount, : : : domainCount, : : : hardwareCount, : : : networkStorageCount, : : : openTicketCount, : : : networkVlanCount, : : : subnetCount, : : : userCount, : : : virtualGuestCount : : : ] : : filter : None : : limit : None : : offset : None : :........:.................................................: Using `-vvv` will print out the exact API that can be used without the softlayer-python framework, A simple python code snippet for XML-RPC, a curl call for REST API calls. This is dependant on the endpoint you are using in the config file. :: slcli -vvv account summary curl -u $SL_USER:$SL_APIKEY -X GET -H "Accept: */*" -H "Accept-Encoding: gzip, deflate, compress" 'https://api.softlayer.com/rest/v3.1/SoftLayer_Account/getObject.json?objectMask=mask%5B%0A++++++++++++nextInvoiceTotalAmount%2C%0A++++++++++++pendingInvoice%5BinvoiceTotalAmount%5D%2C%0A++++++++++++blockDeviceTemplateGroupCount%2C%0A++++++++++++dedicatedHostCount%2C%0A++++++++++++domainCount%2C%0A++++++++++++hardwareCount%2C%0A++++++++++++networkStorageCount%2C%0A++++++++++++openTicketCount%2C%0A++++++++++++networkVlanCount%2C%0A++++++++++++subnetCount%2C%0A++++++++++++userCount%2C%0A++++++++++++virtualGuestCount%0A++++++++++++%5D' softlayer-python-5.9.8/docs/cli/000077500000000000000000000000001415376457700166065ustar00rootroot00000000000000softlayer-python-5.9.8/docs/cli/account.rst000066400000000000000000000017701415376457700210010ustar00rootroot00000000000000.. _cli_account: Account Commands ================= .. click:: SoftLayer.CLI.account.summary:cli :prog: account summary :show-nested: .. click:: SoftLayer.CLI.account.events:cli :prog: account events :show-nested: .. click:: SoftLayer.CLI.account.event_detail:cli :prog: account event-detail :show-nested: .. click:: SoftLayer.CLI.account.invoices:cli :prog: account invoices :show-nested: .. click:: SoftLayer.CLI.account.invoice_detail:cli :prog: account invoice-detail :show-nested: .. click:: SoftLayer.CLI.account.billing_items:cli :prog: account billing-items :show-nested: .. click:: SoftLayer.CLI.account.item_detail:cli :prog: account item-detail :show-nested: .. click:: SoftLayer.CLI.account.cancel_item:cli :prog: account cancel-item :show-nested: .. click:: SoftLayer.CLI.account.orders:cli :prog: account orders :show-nested: .. click:: SoftLayer.CLI.account.licenses:cli :prog: account licenses :show-nested: softlayer-python-5.9.8/docs/cli/autoscale.rst000066400000000000000000000020401415376457700213140ustar00rootroot00000000000000.. _cli_autoscale: Autoscale Commands ================== These commands were added in version `5.8.1 `_ For making changes to the triggers or the autoscale group itself, see the `Autoscale Portal`_ - `Autoscale Product `_ - `Autoscale Documentation `_ - `Autoscale Portal`_ .. click:: SoftLayer.CLI.autoscale.list:cli :prog: autoscale list :show-nested: .. click:: SoftLayer.CLI.autoscale.detail:cli :prog: autoscale detail :show-nested: .. click:: SoftLayer.CLI.autoscale.scale:cli :prog: autoscale scale :show-nested: .. click:: SoftLayer.CLI.autoscale.logs:cli :prog: autoscale logs :show-nested: .. click:: SoftLayer.CLI.autoscale.tag:cli :prog: autoscale tag :show-nested: .. click:: SoftLayer.CLI.autoscale.edit:cli :prog: autoscale edit :show-nested: .. _Autoscale Portal: https://cloud.ibm.com/classic/autoscalesoftlayer-python-5.9.8/docs/cli/block.rst000066400000000000000000000075121415376457700204370ustar00rootroot00000000000000.. _cli_block: Block Commands ============== .. click:: SoftLayer.CLI.block.access.authorize:cli :prog: block access-authorize :show-nested: .. click:: SoftLayer.CLI.block.access.list:cli :prog: block access-list :show-nested: .. click:: SoftLayer.CLI.block.access.revoke:cli :prog: block access-revoke :show-nested: .. click:: SoftLayer.CLI.block.access.password:cli :prog: block access-password :show-nested: .. click:: SoftLayer.CLI.block.replication.failback:cli :prog: block replica-failback :show-nested: .. click:: SoftLayer.CLI.block.replication.failover:cli :prog: block replica-failover :show-nested: .. click:: SoftLayer.CLI.block.replication.order:cli :prog: block replica-order :show-nested: .. click:: SoftLayer.CLI.block.replication.partners:cli :prog: block replica-partners :show-nested: .. click:: SoftLayer.CLI.block.replication.locations:cli :prog: block replica-locations :show-nested: .. click:: SoftLayer.CLI.block.snapshot.cancel:cli :prog: block snapshot-cancel :show-nested: .. click:: SoftLayer.CLI.block.snapshot.create:cli :prog: block snapshot-create :show-nested: .. click:: SoftLayer.CLI.block.snapshot.delete:cli :prog: block snapshot-delete :show-nested: .. click:: SoftLayer.CLI.block.snapshot.disable:cli :prog: block snapshot-disable :show-nested: .. click:: SoftLayer.CLI.block.snapshot.enable:cli :prog: block snapshot-enable :show-nested: .. click:: SoftLayer.CLI.block.snapshot.schedule_list:cli :prog: block snapshot-schedule-list :show-nested: .. click:: SoftLayer.CLI.block.snapshot.list:cli :prog: block snapshot-list :show-nested: .. click:: SoftLayer.CLI.block.snapshot.order:cli :prog: block snapshot-order :show-nested: .. click:: SoftLayer.CLI.block.snapshot.restore:cli :prog: block snapshot-restore :show-nested: .. click:: SoftLayer.CLI.block.cancel:cli :prog: block volume-cancel :show-nested: .. click:: SoftLayer.CLI.block.count:cli :prog: block volume-count :show-nested: .. click:: SoftLayer.CLI.block.detail:cli :prog: block volume-detail :show-nested: .. click:: SoftLayer.CLI.block.duplicate:cli :prog: block volume-duplicate :show-nested: .. click:: SoftLayer.CLI.block.list:cli :prog: block volume-list :show-nested: .. click:: SoftLayer.CLI.block.modify:cli :prog: block volume-modify :show-nested: .. click:: SoftLayer.CLI.block.order:cli :prog: block volume-order :show-nested: .. click:: SoftLayer.CLI.block.lun:cli :prog: block volume-set-lun-id :show-nested: .. click:: SoftLayer.CLI.block.limit:cli :prog: block volume-limits :show-nested: .. click:: SoftLayer.CLI.block.refresh:cli :prog: block volume-refresh :show-nested: .. click:: SoftLayer.CLI.block.convert:cli :prog: block volume-convert :show-nested: .. click:: SoftLayer.CLI.block.subnets.list:cli :prog: block subnets-list :show-nested: .. click:: SoftLayer.CLI.block.subnets.assign:cli :prog: block subnets-assign :show-nested: .. click:: SoftLayer.CLI.block.subnets.remove:cli :prog: block subnets-remove :show-nested: .. click:: SoftLayer.CLI.block.refresh:cli :prog: block volume-refresh :show-nested: .. click:: SoftLayer.CLI.block.convert:cli :prog: block volume-convert :show-nested: .. click:: SoftLayer.CLI.block.set_note:cli :prog: block volume-set-note :show-nested: .. click:: SoftLayer.CLI.block.replication.disaster_recovery_failover:cli :prog: block disaster-recovery-failover :show-nested: .. click:: SoftLayer.CLI.block.snapshot.set_notify_status:cli :prog: block snapshot-set-notification :show-nested: .. click:: SoftLayer.CLI.block.snapshot.get_notify_status:cli :prog: block snapshot-get-notification-status :show-nested: softlayer-python-5.9.8/docs/cli/cdn.rst000066400000000000000000000011711415376457700201040ustar00rootroot00000000000000.. _cli_cdn: Interacting with CDN ===================== .. click:: SoftLayer.CLI.cdn.detail:cli :prog: cdn detail :show-nested: .. click:: SoftLayer.CLI.cdn.list:cli :prog: cdn list :show-nested: .. click:: SoftLayer.CLI.cdn.origin_add:cli :prog: cdn origin-add :show-nested: .. click:: SoftLayer.CLI.cdn.origin_list:cli :prog: cdn origin-list :show-nested: .. click:: SoftLayer.CLI.cdn.origin_remove:cli :prog: cdn origin-remove :show-nested: .. click:: SoftLayer.CLI.cdn.purge:cli :prog: cdn purge :show-nested: .. click:: SoftLayer.CLI.cdn.edit:cli :prog: cdn edit :show-nested: softlayer-python-5.9.8/docs/cli/commands.rst000066400000000000000000000016171415376457700211460ustar00rootroot00000000000000.. _cli_commands: Call API ======== This function allows you to easily call any API. The format is `slcli call-api SoftLayer_Service method param1 param2 --id=1234 --mask="mask[id,name]"` Parameters should be in the order they are presented on sldn.softlayer.com. Any complex parameters (those that link to other datatypes) should be presented as JSON strings. They need to be enclosed in single quotes (`'`), and variables and strings enclosed in double quotes (`"`). For example: `{"hostname":"test",ssh_keys:[{"id":1234}]}` .. click:: SoftLayer.CLI.call_api:cli :prog: call-api :show-nested: Shell ===== .. click:: SoftLayer.shell.core:cli :prog: shell :show-nested: MetaData ======== Used to retrieve information about the server making the API call. Can be called with an un-authenticated API call. .. click:: SoftLayer.CLI.metadata:cli :prog: metadata :show-nested: softlayer-python-5.9.8/docs/cli/config.rst000066400000000000000000000011071415376457700206040ustar00rootroot00000000000000.. _cli_config: Config ====== `Creating an IBMID apikey `_ `IBMid for services `_ `Creating a SoftLayer apikey `_ .. click:: SoftLayer.CLI.config.setup:cli :prog: config setup :show-nested: .. click:: SoftLayer.CLI.config.show:cli :prog: config show :show-nested: .. click:: SoftLayer.CLI.config.setup:cli :prog: setup :show-nested: softlayer-python-5.9.8/docs/cli/dedicated.rst000066400000000000000000000014541415376457700212520ustar00rootroot00000000000000.. _cli_dedicated: Dedicated Host Commands ======================= .. click:: SoftLayer.CLI.dedicatedhost.list:cli :prog: dedicatedhost list :show-nested: .. click:: SoftLayer.CLI.dedicatedhost.create:cli :prog: dedicatedhost create :show-nested: .. click:: SoftLayer.CLI.dedicatedhost.create_options:cli :prog: dedicatedhost create-options :show-nested: .. click:: SoftLayer.CLI.dedicatedhost.detail:cli :prog: dedicatedhost detail :show-nested: .. click:: SoftLayer.CLI.dedicatedhost.cancel:cli :prog: dedicatedhost cancel :show-nested: .. click:: SoftLayer.CLI.dedicatedhost.cancel_guests:cli :prog: dedicatedhost cancel-guests :show-nested: .. click:: SoftLayer.CLI.dedicatedhost.list_guests:cli :prog: dedicatedhost list-guests :show-nested: softlayer-python-5.9.8/docs/cli/dns.rst000066400000000000000000000015271415376457700201310ustar00rootroot00000000000000.. _cli_dns: DNS Management ============== .. click:: SoftLayer.CLI.dns.zone_import:cli :prog: dns import :show-nested: .. click:: SoftLayer.CLI.dns.record_add:cli :prog: dns record-add :show-nested: .. click:: SoftLayer.CLI.dns.record_edit:cli :prog: dns record-edit :show-nested: .. click:: SoftLayer.CLI.dns.record_list:cli :prog: dns record-list :show-nested: .. click:: SoftLayer.CLI.dns.record_remove:cli :prog: dns record-remove :show-nested: .. click:: SoftLayer.CLI.dns.zone_create:cli :prog: dns zone-create :show-nested: .. click:: SoftLayer.CLI.dns.zone_delete:cli :prog: dns zone-delete :show-nested: .. click:: SoftLayer.CLI.dns.zone_list:cli :prog: dns zone-list :show-nested: .. click:: SoftLayer.CLI.dns.zone_print:cli :prog: dns zone-print :show-nested: softlayer-python-5.9.8/docs/cli/email.rst000066400000000000000000000004501415376457700204260ustar00rootroot00000000000000.. _cli_email: Email Commands ================= .. click:: SoftLayer.CLI.email.list:cli :prog: email list :show-nested: .. click:: SoftLayer.CLI.email.detail:cli :prog: email detail :show-nested: .. click:: SoftLayer.CLI.email.edit:cli :prog: email edit :show-nested:softlayer-python-5.9.8/docs/cli/event_log.rst000066400000000000000000000020501415376457700213170ustar00rootroot00000000000000.. _cli_event_log: Event-Log Commands ==================== .. click:: SoftLayer.CLI.event_log.get:cli :prog: event-log get :show-nested: There are usually quite a few events on an account, so be careful when using the `--limit -1` option. The command will automatically break requests out into smaller sub-requests, but this command may take a very long time to complete. It will however print out data as it comes in. .. click:: SoftLayer.CLI.event_log.types:cli :prog: event-log types :show-nested: Currently the types are as follows, more may be added in the future. :: :......................: : types : :......................: : Account : : CDN : : User : : Bare Metal Instance : : API Authentication : : Server : : CCI : : Image : : Bluemix LB : : Facility : : Cloud Object Storage : : Security Group : :......................:softlayer-python-5.9.8/docs/cli/file.rst000066400000000000000000000064321415376457700202640ustar00rootroot00000000000000.. _cli_file: File Commands ============= .. click:: SoftLayer.CLI.file.access.authorize:cli :prog: file access-authorize :show-nested: .. click:: SoftLayer.CLI.file.access.list:cli :prog: file access-list :show-nested: .. click:: SoftLayer.CLI.file.access.revoke:cli :prog: file access-revoke :show-nested: .. click:: SoftLayer.CLI.file.replication.failback:cli :prog: file replica-failback :show-nested: .. click:: SoftLayer.CLI.file.replication.failover:cli :prog: file replica-failover :show-nested: .. click:: SoftLayer.CLI.file.replication.order:cli :prog: file replica-order :show-nested: .. click:: SoftLayer.CLI.file.replication.partners:cli :prog: file replica-partners :show-nested: .. click:: SoftLayer.CLI.file.replication.locations:cli :prog: file replica-locations :show-nested: .. click:: SoftLayer.CLI.file.snapshot.cancel:cli :prog: file snapshot-cancel :show-nested: .. click:: SoftLayer.CLI.file.snapshot.create:cli :prog: file snapshot-create :show-nested: .. click:: SoftLayer.CLI.file.snapshot.delete:cli :prog: file snapshot-delete :show-nested: .. click:: SoftLayer.CLI.file.snapshot.disable:cli :prog: file snapshot-disable :show-nested: .. click:: SoftLayer.CLI.file.snapshot.enable:cli :prog: file snapshot-enable :show-nested: .. click:: SoftLayer.CLI.file.snapshot.list:cli :prog: file snapshot-list :show-nested: .. click:: SoftLayer.CLI.file.snapshot.order:cli :prog: file snapshot-order :show-nested: .. click:: SoftLayer.CLI.file.snapshot.restore:cli :prog: file snapshot-restore :show-nested: .. click:: SoftLayer.CLI.file.cancel:cli :prog: file volume-cancel :show-nested: .. click:: SoftLayer.CLI.file.count:cli :prog: file volume-count :show-nested: .. click:: SoftLayer.CLI.file.detail:cli :prog: file volume-detail :show-nested: .. click:: SoftLayer.CLI.file.duplicate:cli :prog: file volume-duplicate :show-nested: .. click:: SoftLayer.CLI.file.list:cli :prog: file volume-list :show-nested: .. click:: SoftLayer.CLI.file.modify:cli :prog: file volume-modify :show-nested: .. click:: SoftLayer.CLI.file.order:cli :prog: file volume-order :show-nested: .. click:: SoftLayer.CLI.file.limit:cli :prog: file volume-limits :show-nested: .. click:: SoftLayer.CLI.file.refresh:cli :prog: file volume-refresh :show-nested: .. click:: SoftLayer.CLI.file.convert:cli :prog: file volume-convert :show-nested: .. click:: SoftLayer.CLI.file.snapshot.schedule_list:cli :prog: file snapshot-schedule-list :show-nested: .. click:: SoftLayer.CLI.file.refresh:cli :prog: file volume-refresh :show-nested: .. click:: SoftLayer.CLI.file.convert:cli :prog: file volume-convert :show-nested: .. click:: SoftLayer.CLI.file.set_note:cli :prog: file volume-set-note :show-nested: .. click:: SoftLayer.CLI.file.replication.disaster_recovery_failover:cli :prog: file disaster-recovery-failover :show-nested: .. click:: SoftLayer.CLI.file.snapshot.set_notify_status:cli :prog: file snapshot-set-notification :show-nested: .. click:: SoftLayer.CLI.file.snapshot.get_notify_status:cli :prog: file snapshot-get-notification-status :show-nested: softlayer-python-5.9.8/docs/cli/firewall.rst000066400000000000000000000007641415376457700211540ustar00rootroot00000000000000.. _cli_firewall: Firewall Management =================== .. click:: SoftLayer.CLI.firewall.add:cli :prog: firewall add :show-nested: .. click:: SoftLayer.CLI.firewall.cancel:cli :prog: firewall cancel :show-nested: .. click:: SoftLayer.CLI.firewall.detail:cli :prog: firewall detail :show-nested: .. click:: SoftLayer.CLI.firewall.edit:cli :prog: firewall edit :show-nested: .. click:: SoftLayer.CLI.firewall.list:cli :prog: firewall list :show-nested: softlayer-python-5.9.8/docs/cli/global_ip.rst000066400000000000000000000010031415376457700212620ustar00rootroot00000000000000.. _cli_global_ip: Global IP Addresses =================== .. click:: SoftLayer.CLI.globalip.assign:cli :prog: globalip assign :show-nested: .. click:: SoftLayer.CLI.globalip.cancel:cli :prog: globalip cancel :show-nested: .. click:: SoftLayer.CLI.globalip.create:cli :prog: globalip create :show-nested: .. click:: SoftLayer.CLI.globalip.list:cli :prog: globalip list :show-nested: .. click:: SoftLayer.CLI.globalip.unassign:cli :prog: globalip unassign :show-nested: softlayer-python-5.9.8/docs/cli/hardware.rst000066400000000000000000000073731415376457700211470ustar00rootroot00000000000000.. _cli_hardware: Interacting with Hardware ============================== .. click:: SoftLayer.CLI.hardware.bandwidth:cli :prog: hardware bandwidth :show-nested: .. click:: SoftLayer.CLI.hardware.cancel_reasons:cli :prog: hardware cancel-reasons :show-nested: .. click:: SoftLayer.CLI.hardware.cancel:cli :prog: hardware cancel :show-nested: .. click:: SoftLayer.CLI.hardware.create_options:cli :prog: hardware create-options :show-nested: .. click:: SoftLayer.CLI.hardware.create:cli :prog: hardware create :show-nested: Provides some basic functionality to order a server. `slcli order` has a more full featured method of ordering servers. This command only supports the FAST_PROVISION type. As of v5.9.0 please use the `--network` option for specifying port speed, as that allows a bit more granularity for choosing your networking type. .. click:: SoftLayer.CLI.hardware.credentials:cli :prog: hardware credentials :show-nested: .. click:: SoftLayer.CLI.hardware.detail:cli :prog: hardware detail :show-nested: .. click:: SoftLayer.CLI.hardware.billing:cli :prog: hardware billing :show-nested: .. click:: SoftLayer.CLI.hardware.edit:cli :prog: hardware edit :show-nested: **Note :** Using multiple ' **:** ' can cause an error. $ slcli hw edit 123456 --tag "cloud:service:db2whoc, cloud:svcplan:flex, cloud:svcenv:prod, cloud:bmixenv:fra" TransportError(0): ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response',)) When setting port speed, use "-1" to indicate best possible configuration. Using 10/100/1000/10000 on a server with a redundant interface may result the interface entering a degraded state. See `setPublicNetworkInterfaceSpeed `_ for more information. .. click:: SoftLayer.CLI.hardware.list:cli :prog: hardware list :show-nested: .. click:: SoftLayer.CLI.hardware.power:power_cycle :prog: hardware power-cycle :show-nested: .. click:: SoftLayer.CLI.hardware.power:power_off :prog: hardware power-off :show-nested: .. click:: SoftLayer.CLI.hardware.power:power_on :prog: hardware power-on :show-nested: .. click:: SoftLayer.CLI.hardware.power:reboot :prog: hardware reboot :show-nested: .. click:: SoftLayer.CLI.hardware.reload:cli :prog: hardware reload :show-nested: .. click:: SoftLayer.CLI.hardware.power:rescue :prog: hardware rescue .. click:: SoftLayer.CLI.hardware.reflash_firmware:cli :prog: hardware reflash-firmware :show-nested: Reflash here means the current version of the firmware running on your server will be re-flashed onto the selected hardware. This does require a reboot. See `slcli hw update-firmware` if you want the newest version. .. click:: SoftLayer.CLI.hardware.update_firmware:cli :prog: hardware update-firmware :show-nested: This function updates the firmware of a server. If already at the latest version, no software is installed. .. click:: SoftLayer.CLI.hardware.toggle_ipmi:cli :prog: hardware toggle-ipmi :show-nested: .. click:: SoftLayer.CLI.hardware.ready:cli :prog: hardware ready :show-nested: .. click:: SoftLayer.CLI.hardware.dns:cli :prog: hardware dns-sync :show-nested: .. click:: SoftLayer.CLI.hardware.storage:cli :prog: hardware storage :show-nested: .. click:: SoftLayer.CLI.hardware.guests:cli :prog: hardware guests :show-nested: .. click:: SoftLayer.CLI.hardware.authorize_storage:cli :prog: hardware authorize-storage :show-nested: .. click:: SoftLayer.CLI.hardware.upgrade:cli :prog: hardware upgrade :show-nested: .. click:: SoftLayer.CLI.hardware.sensor:cli :prog: hardware sensor :show-nested: softlayer-python-5.9.8/docs/cli/image.rst000066400000000000000000000012131415376457700204170ustar00rootroot00000000000000.. _cli_image: Disk Image Commands =================== .. click:: SoftLayer.CLI.image.delete:cli :prog: image delete :show-nested: .. click:: SoftLayer.CLI.image.detail:cli :prog: image detail :show-nested: .. click:: SoftLayer.CLI.image.edit:cli :prog: image edit :show-nested: .. click:: SoftLayer.CLI.image.list:cli :prog: image list :show-nested: .. click:: SoftLayer.CLI.image.import:cli :prog: image import :show-nested: .. click:: SoftLayer.CLI.image.export:cli :prog: image export :show-nested: .. click:: SoftLayer.CLI.image.datacenter:cli :prog: image datacenter :show-nested: softlayer-python-5.9.8/docs/cli/ipsec.rst000066400000000000000000000276111415376457700204520ustar00rootroot00000000000000.. _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 ---------- .. click:: SoftLayer.CLI.vpn.ipsec.list:cli :prog: ipsec list :show-nested: 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 ------------ .. click:: SoftLayer.CLI.vpn.ipsec.detail:cli :prog: ipsec detail :show-nested: 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 ------------ .. click:: SoftLayer.CLI.vpn.ipsec.update:cli :prog: ipsec update :show-nested: 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 --------------- .. click:: SoftLayer.CLI.vpn.ipsec.configure:cli :prog: ipsec configure :show-nested: 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 ---------------- .. click:: SoftLayer.CLI.vpn.ipsec.subnet.add:cli :prog: ipsec subnet-add :show-nested: 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 ------------------- .. click:: SoftLayer.CLI.vpn.ipsec.subnet.remove:cli :prog: ipsec subnet-remove :show-nested: 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 --------------------- .. click:: SoftLayer.CLI.vpn.ipsec.translation.add:cli :prog: ipsec translation-add :show-nested: 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 ------------------------ .. click:: SoftLayer.CLI.vpn.ipsec.translation.remove:cli :prog: ipsec translation-remove :show-nested: 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 ------------------------ .. click:: SoftLayer.CLI.vpn.ipsec.translation.update:cli :prog: ipsec translation-update :show-nested: 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.9.8/docs/cli/licenses.rst000066400000000000000000000005051415376457700211450ustar00rootroot00000000000000.. _cli_licenses: licenses Commands ================= .. click:: SoftLayer.CLI.licenses.create_options:cli :prog: licenses create-options .. click:: SoftLayer.CLI.licenses.create:cli :prog: licenses create :show-nested: .. click:: SoftLayer.CLI.licenses.cancel:cli :prog: licenses cancel :show-nested:softlayer-python-5.9.8/docs/cli/loadbal.rst000066400000000000000000000035171415376457700207440ustar00rootroot00000000000000.. _cli_loadbalancer: LoadBalancers =================================== These commands were added in version `5.8.0 `_ LBaaS Commands ~~~~~~~~~~~~~~ - `LBaaS Product `_ - `LBaaS Documentation `_ .. click:: SoftLayer.CLI.loadbal.detail:cli :prog: loadbal detail :show-nested: .. click:: SoftLayer.CLI.loadbal.list:cli :prog: loadbal list :show-nested: .. click:: SoftLayer.CLI.loadbal.health:cli :prog: loadbal health :show-nested: .. click:: SoftLayer.CLI.loadbal.members:add :prog: loadbal member-add :show-nested: .. click:: SoftLayer.CLI.loadbal.members:remove :prog: loadbal member-del :show-nested: .. click:: SoftLayer.CLI.loadbal.pools:add :prog: loadbal pool-add :show-nested: .. click:: SoftLayer.CLI.loadbal.pools:edit :prog: loadbal pool-edit :show-nested: .. click:: SoftLayer.CLI.loadbal.pools:delete :prog: loadbal pool-del :show-nested: .. click:: SoftLayer.CLI.loadbal.pools:l7pool_add :prog: loadbal l7pool-add :show-nested: .. click:: SoftLayer.CLI.loadbal.pools:l7pool_del :prog: loadbal l7pool-del :show-nested: .. click:: SoftLayer.CLI.loadbal.layer7_policy_list:policies :prog: loadbal l7policies :show-nested: .. click:: SoftLayer.CLI.loadbal.order:order :prog: loadbal order :show-nested: .. click:: SoftLayer.CLI.loadbal.order:order_options :prog: loadbal order-options :show-nested: .. click:: SoftLayer.CLI.loadbal.order:cancel :prog: loadbal cancel :show-nested: NetScaler Commands ~~~~~~~~~~~~~~~~~~ .. click:: SoftLayer.CLI.loadbal.ns_detail:cli :prog: loadbal ns-detail :show-nested: .. click:: SoftLayer.CLI.loadbal.ns_list:cli :prog: loadbal ns-list :show-nested:softlayer-python-5.9.8/docs/cli/nas.rst000066400000000000000000000003321415376457700201170ustar00rootroot00000000000000.. _cli_nas: NAS Commands ============ .. click:: SoftLayer.CLI.nas.list:cli :prog: nas list :show-nested: .. click:: SoftLayer.CLI.nas.credentials:cli :prog: nas credentials :show-nested:softlayer-python-5.9.8/docs/cli/object_storage.rst000066400000000000000000000014331415376457700223330ustar00rootroot00000000000000.. _cli_object_storage: Object Storage Commands ======================= .. click:: SoftLayer.CLI.object_storage.list_accounts:cli :prog: object-storage accounts :show-nested: .. click:: SoftLayer.CLI.object_storage.list_endpoints:cli :prog: object-storage endpoints :show-nested: .. click:: SoftLayer.CLI.object_storage.credential.list:cli :prog: object-storage credential list :show-nested: .. click:: SoftLayer.CLI.object_storage.credential.limit:cli :prog: object-storage credential limit :show-nested: .. click:: SoftLayer.CLI.object_storage.credential.delete:cli :prog: object-storage credential delete :show-nested: .. click:: SoftLayer.CLI.object_storage.credential.create:cli :prog: object-storage credential create :show-nested: softlayer-python-5.9.8/docs/cli/ordering.rst000066400000000000000000000116571415376457700211630ustar00rootroot00000000000000.. _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 .. click:: SoftLayer.CLI.order.package_list:cli :prog: order package-list :show-nested: .. 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 .. click:: SoftLayer.CLI.order.package_locations:cli :prog: order package-locations :show-nested: .. click:: SoftLayer.CLI.order.category_list:cli :prog: order category-list :show-nested: 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 : :........................................:.......................:............: .. click:: SoftLayer.CLI.order.item_list:cli :prog: order item-list :show-nested: 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. .. click:: SoftLayer.CLI.order.preset_list:cli :prog: order preset-list :show-nested: .. click:: SoftLayer.CLI.order.place:cli :prog: order place :show-nested: 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 \ --extras '{"virtualGuests": [{"hostname": "test", "domain": "softlayer.com"}]}' \ --complex-type SoftLayer_Container_Product_Order_Virtual_Guest Quotes ====== .. click:: SoftLayer.CLI.order.quote:cli :prog: order quote :show-nested: .. click:: SoftLayer.CLI.order.quote_list:cli :prog: order quote-list :show-nested: .. click:: SoftLayer.CLI.order.quote_detail:cli :prog: order quote-detail :show-nested: .. click:: SoftLayer.CLI.order.quote_save:cli :prog: order quote-save :show-nested: .. click:: SoftLayer.CLI.order.place_quote:cli :prog: order place-quote :show-nested: Lookup ====== .. click:: SoftLayer.CLI.order.lookup:cli :prog: order lookup :show-nested: softlayer-python-5.9.8/docs/cli/reports.rst000066400000000000000000000005301415376457700210340ustar00rootroot00000000000000.. _cli_reports: Reports ======= There are a few report type commands in the SLCLI. .. click:: SoftLayer.CLI.summary:cli :prog: summary :show-nested: A list of datacenters, and how many servers, VSI, vlans, subnets and public_ips are in each. .. click:: SoftLayer.CLI.report.bandwidth:cli :prog: report bandwidth :show-nested:softlayer-python-5.9.8/docs/cli/security_groups.rst000066400000000000000000000026461415376457700226160ustar00rootroot00000000000000.. _cli_security_groups: Security Groups =============== .. click:: SoftLayer.CLI.securitygroup.list:cli :prog: securitygroup list :show-nested: .. click:: SoftLayer.CLI.securitygroup.detail:cli :prog: securitygroup detail :show-nested: .. click:: SoftLayer.CLI.securitygroup.create:cli :prog: securitygroup create :show-nested: .. click:: SoftLayer.CLI.securitygroup.edit:cli :prog: securitygroup edit :show-nested: .. click:: SoftLayer.CLI.securitygroup.delete:cli :prog: securitygroup delete :show-nested: .. click:: SoftLayer.CLI.securitygroup.rule:rule_list :prog: securitygroup rule-list :show-nested: .. click:: SoftLayer.CLI.securitygroup.rule:add :prog: securitygroup rule-add :show-nested: .. click:: SoftLayer.CLI.securitygroup.rule:edit :prog: securitygroup rule-edit :show-nested: .. click:: SoftLayer.CLI.securitygroup.rule:remove :prog: securitygroup rule-remove :show-nested: .. click:: SoftLayer.CLI.securitygroup.interface:interface_list :prog: securitygroup interface-list :show-nested: .. click:: SoftLayer.CLI.securitygroup.interface:add :prog: securitygroup interface-add :show-nested: .. click:: SoftLayer.CLI.securitygroup.interface:remove :prog: securitygroup interface-remove :show-nested: .. click:: SoftLayer.CLI.securitygroup.event_log:get_by_request_id :prog: securitygroup event-log :show-nested: softlayer-python-5.9.8/docs/cli/sshkey.rst000066400000000000000000000007061415376457700206510ustar00rootroot00000000000000.. _cli_sshkey: SSH Keys ======== .. click:: SoftLayer.CLI.sshkey.add:cli :prog: sshkey add :show-nested: .. click:: SoftLayer.CLI.sshkey.remove:cli :prog: sshkey remove :show-nested: .. click:: SoftLayer.CLI.sshkey.edit:cli :prog: sshkey edit :show-nested: .. click:: SoftLayer.CLI.sshkey.list:cli :prog: sshkey list :show-nested: .. click:: SoftLayer.CLI.sshkey.print:cli :prog: sshkey print :show-nested: softlayer-python-5.9.8/docs/cli/ssl.rst000066400000000000000000000006741415376457700201500ustar00rootroot00000000000000.. _cli_ssl: SSL Certificates ================ .. click:: SoftLayer.CLI.ssl.add:cli :prog: ssl add :show-nested: .. click:: SoftLayer.CLI.ssl.download:cli :prog: ssl download :show-nested: .. click:: SoftLayer.CLI.ssl.edit:cli :prog: ssl edit :show-nested: .. click:: SoftLayer.CLI.ssl.list:cli :prog: ssl list :show-nested: .. click:: SoftLayer.CLI.ssl.remove:cli :prog: ssl remove :show-nested: softlayer-python-5.9.8/docs/cli/subnet.rst000066400000000000000000000011751415376457700206440ustar00rootroot00000000000000.. _cli_subnets: Subnets ======= .. click:: SoftLayer.CLI.subnet.cancel:cli :prog: subnet cancel :show-nested: .. click:: SoftLayer.CLI.subnet.create:cli :prog: subnet create :show-nested: .. click:: SoftLayer.CLI.subnet.detail:cli :prog: subnet detail :show-nested: .. click:: SoftLayer.CLI.subnet.edit:cli :prog: subnet edit :show-nested: .. click:: SoftLayer.CLI.subnet.list:cli :prog: subnet list :show-nested: .. click:: SoftLayer.CLI.subnet.lookup:cli :prog: subnet lookup :show-nested: .. click:: SoftLayer.CLI.subnet.edit_ip:cli :prog: subnet edit-ip :show-nested: softlayer-python-5.9.8/docs/cli/tags.rst000066400000000000000000000016521415376457700203020ustar00rootroot00000000000000.. _cli_tags: Tag Commands ============ These commands will allow you to interact with the **IMS** provier tagging service. The `IBM Global Search and Tagging API `_ can be used to interact with both the **GHOST** provider and **IMS** provider. The **GHOST** provider will handle tags for things outside of the Classic Infrastructure (aka SoftLayer) space. .. click:: SoftLayer.CLI.tags.list:cli :prog: tags list :show-nested: .. click:: SoftLayer.CLI.tags.set:cli :prog: tags set :show-nested: .. click:: SoftLayer.CLI.tags.details:cli :prog: tags details :show-nested: .. click:: SoftLayer.CLI.tags.delete:cli :prog: tags delete :show-nested: .. click:: SoftLayer.CLI.tags.taggable:cli :prog: tags taggable :show-nested: .. click:: SoftLayer.CLI.tags.cleanup:cli :prog: tags cleanup :show-nested: softlayer-python-5.9.8/docs/cli/tickets.rst000066400000000000000000000024711415376457700210120ustar00rootroot00000000000000.. _cli_tickets: Support Tickets ================= The SoftLayer ticket API is used to create "classic" or Infrastructure Support cases. These tickets will still show up in your web portal, but for the more unified case management API, see the `Case Management API `_ .. note:: Windows Git-Bash users might run into issues with `ticket create` and `ticket update` if --body isn't used, as it doesn't report that it is a real TTY to python, so the default editor can not be launched. .. click:: SoftLayer.CLI.ticket.create:cli :prog: ticket create :show-nested: .. click:: SoftLayer.CLI.ticket.detail:cli :prog: ticket detail :show-nested: .. click:: SoftLayer.CLI.ticket.list:cli :prog: ticket list :show-nested: .. click:: SoftLayer.CLI.ticket.update:cli :prog: ticket update :show-nested: .. click:: SoftLayer.CLI.ticket.upload:cli :prog: ticket upload :show-nested: .. click:: SoftLayer.CLI.ticket.subjects:cli :prog: ticket subjects :show-nested: .. click:: SoftLayer.CLI.ticket.summary:cli :prog: ticket summary :show-nested: .. click:: SoftLayer.CLI.ticket.attach:cli :prog: ticket attach :show-nested: .. click:: SoftLayer.CLI.ticket.detach:cli :prog: ticket detach :show-nested: softlayer-python-5.9.8/docs/cli/users.rst000066400000000000000000000021451415376457700205030ustar00rootroot00000000000000.. _cli_user: Users ============= Version 5.6.0 introduces the ability to interact with user accounts from the cli. .. click:: SoftLayer.CLI.user.list:cli :prog: user list :show-nested: .. click:: SoftLayer.CLI.user.detail:cli :prog: user detail :show-nested: .. click:: SoftLayer.CLI.user.notifications:cli :prog: user notifications :show-nested: .. click:: SoftLayer.CLI.user.permissions:cli :prog: user permissions :show-nested: .. click:: SoftLayer.CLI.user.edit_notifications:cli :prog: user edit-notifications :show-nested: .. click:: SoftLayer.CLI.user.edit_permissions:cli :prog: user edit-permissions :show-nested: .. click:: SoftLayer.CLI.user.edit_details:cli :prog: user edit-details :show-nested: .. click:: SoftLayer.CLI.user.create:cli :prog: user create :show-nested: .. click:: SoftLayer.CLI.user.delete:cli :prog: user delete :show-nested: .. click:: SoftLayer.CLI.user.vpn_manual:cli :prog: user vpn-manual :show-nested: .. click:: SoftLayer.CLI.user.vpn_subnet:cli :prog: user vpn-subnet :show-nested: softlayer-python-5.9.8/docs/cli/vlan.rst000066400000000000000000000006621415376457700203040ustar00rootroot00000000000000.. _cli_vlan: VLANs ===== .. click:: SoftLayer.CLI.vlan.create:cli :prog: vlan create :show-nested: .. click:: SoftLayer.CLI.vlan.detail:cli :prog: vlan detail :show-nested: .. click:: SoftLayer.CLI.vlan.edit:cli :prog: vlan edit :show-nested: .. click:: SoftLayer.CLI.vlan.list:cli :prog: vlan list :show-nested: .. click:: SoftLayer.CLI.vlan.cancel:cli :prog: vlan cancel :show-nested: softlayer-python-5.9.8/docs/cli/vs.rst000066400000000000000000000334571415376457700200040ustar00rootroot00000000000000.. _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`. *Some values were ommitted for brevity* :: $ slcli vs create-options :................................:.................................................................................: : name : value : :................................:.................................................................................: : datacenter : ams01 : : : ams03 : : : wdc07 : : flavors (balanced) : B1_1X2X25 : : : B1_1X2X25 : : : B1_1X2X100 : : cpus (standard) : 1,2,4,8,12,16,32,56 : : cpus (dedicated) : 1,2,4,8,16,32,56 : : cpus (dedicated host) : 1,2,4,8,12,16,32,56 : : memory : 1024,2048,4096,6144,8192,12288,16384,32768,49152,65536,131072,247808 : : memory (dedicated host) : 1024,2048,4096,6144,8192,12288,16384,32768,49152,65536,131072,247808 : : os (CENTOS) : CENTOS_5_64 : : : CENTOS_LATEST_64 : : os (CLOUDLINUX) : CLOUDLINUX_5_64 : : : CLOUDLINUX_6_64 : : : CLOUDLINUX_LATEST : : : CLOUDLINUX_LATEST_64 : : os (COREOS) : COREOS_CURRENT_64 : : : COREOS_LATEST : : : COREOS_LATEST_64 : : os (DEBIAN) : DEBIAN_6_64 : : : DEBIAN_LATEST_64 : : os (OTHERUNIXLINUX) : OTHERUNIXLINUX_1_64 : : : OTHERUNIXLINUX_LATEST : : : OTHERUNIXLINUX_LATEST_64 : : os (REDHAT) : REDHAT_5_64 : : : REDHAT_6_64 : : : REDHAT_7_64 : : : REDHAT_LATEST : : : REDHAT_LATEST_64 : : 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 : : local disk(0) : 25,100 : : local disk(2) : 25,100,150,200,300 : : local (dedicated host) disk(0) : 25,100 : : nic (dedicated host) : 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 -f B1_1X2X25 -o DEBIAN_LATEST_64 --datacenter=ams01 --billing=hourly This action will incur charges on your account. Continue? [y/N]: y :..........:.................................:......................................:...........................: : ID : FQDN : guid : Order Date : :..........:.................................:......................................:...........................: : 70112999 : testtesttest.test.com : 1abc7afb-9618-4835-89c9-586f3711d8ea : 2019-01-30T17:16:58-06:00 : :..........:.................................:......................................:...........................: :.........................................................................: : OrderId: 12345678 : :.......:.................................................................: : Cost : Description : :.......:.................................................................: : 0.0 : Debian GNU/Linux 9.x Stretch/Stable - Minimal Install (64 bit) : : 0.0 : 25 GB (SAN) : : 0.0 : Reboot / Remote Console : : 0.0 : 100 Mbps Public & Private Network Uplinks : : 0.0 : 0 GB Bandwidth Allotment : : 0.0 : 1 IP Address : : 0.0 : Host Ping and TCP Service Monitoring : : 0.0 : Email and Ticket : : 0.0 : Automated Reboot from Monitoring : : 0.0 : Unlimited SSL VPN Users & 1 PPTP VPN User per account : : 0.0 : 2 GB : : 0.0 : 1 x 2.0 GHz or higher Core : : 0.000 : Total hourly cost : :.......:.................................................................: 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 : ams01 : 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 : ams01 : : cores : 2 : : memory : 1G : : public_ip : 108.168.200.11 : : private_ip : 10.54.80.200 : : os : Debian : : 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 : :..............:...........................: .. click:: SoftLayer.CLI.virt.bandwidth:cli :prog: virtual bandwidth :show-nested: If no timezone is specified, IMS local time (CST) will be assumed, which might not match your user's selected timezone. .. click:: SoftLayer.CLI.virt.cancel:cli :prog: virtual cancel :show-nested: .. click:: SoftLayer.CLI.virt.capture:cli :prog: virtual capture :show-nested: .. click:: SoftLayer.CLI.virt.create:cli :prog: virtual create :show-nested: .. click:: SoftLayer.CLI.virt.create_options:cli :prog: virtual create-options :show-nested: .. click:: SoftLayer.CLI.virt.dns:cli :prog: virtual dns-sync :show-nested: .. click:: SoftLayer.CLI.virt.edit:cli :prog: virtual edit :show-nested: .. click:: SoftLayer.CLI.virt.list:cli :prog: virtual list :show-nested: .. click:: SoftLayer.CLI.virt.power:pause :prog: virtual pause :show-nested: .. click:: SoftLayer.CLI.virt.power:power_on :prog: virtual power-on :show-nested: .. click:: SoftLayer.CLI.virt.power:power_off :prog: virtual power-off :show-nested: .. click:: SoftLayer.CLI.virt.power:resume :prog: virtual resume :show-nested: .. click:: SoftLayer.CLI.virt.power:rescue :prog: virtual rescue :show-nested: .. click:: SoftLayer.CLI.virt.power:reboot :prog: virtual reboot :show-nested: .. click:: SoftLayer.CLI.virt.ready:cli :prog: virtual ready :show-nested: .. click:: SoftLayer.CLI.virt.upgrade:cli :prog: virtual upgrade :show-nested: .. click:: SoftLayer.CLI.virt.usage:cli :prog: virtual usage :show-nested: .. click:: SoftLayer.CLI.virt.storage:cli :prog: virtual storage .. click:: SoftLayer.CLI.virt.billing:cli :prog: virtual billing :show-nested: .. click:: SoftLayer.CLI.virt.detail:cli :prog: virtual detail :show-nested: .. click:: SoftLayer.CLI.virt.reload:cli :prog: virtual reload :show-nested: .. click:: SoftLayer.CLI.virt.credentials:cli :prog: virtual credentials :show-nested: .. click:: SoftLayer.CLI.virt.migrate:cli :prog: virtual migrate :show-nested: .. click:: SoftLayer.CLI.virt.authorize_storage:cli :prog: virtual authorize-storage :show-nested: Manages the migration of virutal guests. Supports migrating virtual guests on Dedicated Hosts as well. Reserved Capacity ----------------- .. toctree:: :maxdepth: 2 vs/reserved_capacity Placement Groups ---------------- .. toctree:: :maxdepth: 2 vs/placement_group softlayer-python-5.9.8/docs/cli/vs/000077500000000000000000000000001415376457700172365ustar00rootroot00000000000000softlayer-python-5.9.8/docs/cli/vs/placement_group.rst000066400000000000000000000143661415376457700231660ustar00rootroot00000000000000.. _vs_placement_group_user_docs: Working with Placement Groups ============================= A `Placement Group `_ is a way to control which physical servers your virtual servers get provisioned onto. To create a `Virtual_PlacementGroup `_ object, you will need to know the following: - backendRouterId, from `getAvailableRouters `_ - ruleId, from `getAllObjects `_ - name, can be any string, but most be unique on your account Once a placement group is created, you can create new virtual servers in that group. Existing VSIs cannot be moved into a placement group. When ordering a VSI in a placement group, make sure to set the `placementGroupId `_ for each guest in your order. use the --placementgroup option with `vs create` to specify creating a VSI in a specific group. :: $ slcli vs create -H testGroup001 -D test.com -f B1_1X2X25 -d mex01 -o DEBIAN_LATEST --placementgroup testGroup Placement groups can only be deleted once all the virtual guests in the group have been reclaimed. .. _cli_vs_placementgroup_create: vs placementgroup create ------------------------ This command will create a placement group. :: $ slcli vs placementgroup create --name testGroup -b bcr02a.dal06 -r SPREAD Options ^^^^^^^ --name TEXT Name for this new placement group. [required] -b, --backend_router TEXT backendRouter, can be either the hostname or id. [required] -r, --rule TEXT The keyName or Id of the rule to govern this placement group. [required] .. _cli_vs_placementgroup_create_options: vs placementgroup create-options -------------------------------- This command will print out the available routers and rule sets for use in creating a placement group. :: $ slcli vs placementgroup create-options :.................................................: : Available Routers : :..............:..............:...................: : Datacenter : Hostname : Backend Router Id : :..............:..............:...................: : Washington 1 : bcr01.wdc01 : 16358 : : Tokyo 5 : bcr01a.tok05 : 1587015 : :..............:..............:...................: :..............: : Rules : :....:.........: : Id : KeyName : :....:.........: : 1 : SPREAD : :....:.........: .. _cli_vs_placementgroup_delete: vs placementgroup delete ------------------------ This command will remove a placement group. The placement group needs to be empty for this command to succeed. Options ^^^^^^^ --purge Delete all guests in this placement group. The group itself can be deleted once all VMs are fully reclaimed :: $ slcli vs placementgroup delete testGroup You can use the flag --purge to auto-cancel all VSIs in a placement group. You will still need to wait for them to be reclaimed before proceeding to delete the group itself. :: $ slcli vs placementgroup delete testGroup --purge You are about to delete the following guests! issues10691547768562.test.com, issues10691547768572.test.com, issues10691547768552.test.com, issues10691548718280.test.com This action will cancel all guests! Continue? [y/N]: y Deleting issues10691547768562.test.com... Deleting issues10691547768572.test.com... Deleting issues10691547768552.test.com... Deleting issues10691548718280.test.com... .. _cli_vs_placementgroup_list: vs placementgroup list ---------------------- This command will list all placement groups on your account. :: $ slcli vs placementgroup list :..........................................................................................: : Placement Groups : :.......:...................:................:........:........:...........................: : Id : Name : Backend Router : Rule : Guests : Created : :.......:...................:................:........:........:...........................: : 31741 : fotest : bcr01a.tor01 : SPREAD : 1 : 2018-11-22T14:36:10-06:00 : : 64535 : testGroup : bcr01a.mex01 : SPREAD : 3 : 2019-01-17T14:36:42-06:00 : :.......:...................:................:........:........:...........................: .. _cli_vs_placementgroup_detail: vs placementgroup detail ------------------------ This command will provide some detailed information about a specific placement group :: $ slcli vs placementgroup detail testGroup :.......:............:................:........:...........................: : Id : Name : Backend Router : Rule : Created : :.......:............:................:........:...........................: : 64535 : testGroup : bcr01a.mex01 : SPREAD : 2019-01-17T14:36:42-06:00 : :.......:............:................:........:...........................: :..........:........................:...............:..............:.....:........:...........................:.............: : Id : FQDN : Primary IP : Backend IP : CPU : Memory : Provisioned : Transaction : :..........:........................:...............:..............:.....:........:...........................:.............: : 69134895 : testGroup62.test.com : 169.57.70.166 : 10.131.11.32 : 1 : 1024 : 2019-01-17T17:44:50-06:00 : - : : 69134901 : testGroup72.test.com : 169.57.70.184 : 10.131.11.59 : 1 : 1024 : 2019-01-17T17:44:53-06:00 : - : : 69134887 : testGroup52.test.com : 169.57.70.187 : 10.131.11.25 : 1 : 1024 : 2019-01-17T17:44:43-06:00 : - : :..........:........................:...............:..............:.....:........:...........................:.............:softlayer-python-5.9.8/docs/cli/vs/reserved_capacity.rst000066400000000000000000000062411415376457700234670ustar00rootroot00000000000000.. _vs_reserved_capacity_user_docs: Working with Reserved Capacity ============================== There are two main concepts for Reserved Capacity. The `Reserved Capacity Group `_ and the `Reserved Capacity Instance `_ The Reserved Capacity Group, is a set block of capacity set aside for you at the time of the order. It will contain a set number of Instances which are all the same size. Instances can be ordered like normal VSIs, with the exception that you need to include the reservedCapacityGroupId, and it must be the same size as the group you are ordering the instance in. - `About Reserved Capacity `_ - `Reserved Capacity FAQ `_ The SLCLI supports some basic Reserved Capacity Features. .. _cli_vs_capacity_create: vs capacity create ------------------ This command will create a Reserved Capacity Group. .. warning:: **These groups can not be canceled until their contract expires in 1 or 3 years!** :: $ slcli vs capacity create --name test-capacity -d dal13 -b 1411193 -c B1_1X2_1_YEAR_TERM -q 10 vs cacpacity create_options --------------------------- This command will print out the Flavors that can be used to create a Reserved Capacity Group, as well as the backend routers available, as those are needed when creating a new group. vs capacity create_guest ------------------------ This command will create a virtual server (Reserved Capacity Instance) inside of your Reserved Capacity Group. This command works very similar to the `slcli vs create` command. :: $ slcli vs capacity create-guest --capacity-id 1234 --primary-disk 25 -H ABCD -D test.com -o UBUNTU_LATEST_64 --ipv6 -k test-key --test vs capacity detail ------------------ This command will print out some basic information about the specified Reserved Capacity Group. vs capacity list ----------------- This command will list out all Reserved Capacity Groups. a **#** symbol represents a filled instance, and a **-** symbol respresents an empty instance :: $ slcli vs capacity list :............................................................................................................: : Reserved Capacity : :......:......................:............:......................:..............:...........................: : ID : Name : Capacity : Flavor : Location : Created : :......:......................:............:......................:..............:...........................: : 1234 : test-capacity : ####------ : B1.1x2 (1 Year Term) : bcr02a.dal13 : 2018-09-24T16:33:09-06:00 : :......:......................:............:......................:..............:...........................:softlayer-python-5.9.8/docs/cli_directory.rst000066400000000000000000000001521415376457700214220ustar00rootroot00000000000000.. _cli_directory: Command Directory ================= .. toctree:: :maxdepth: 2 :glob: cli/* softlayer-python-5.9.8/docs/conf.py000066400000000000000000000205041415376457700173370ustar00rootroot00000000000000# -*- 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', 'sphinx_click.ext'] # 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'2019, 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.9.8/docs/config_file.rst000066400000000000000000000020731415376457700210370ustar00rootroot00000000000000.. _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 *Cloud.ibm.com Config Example* :: [softlayer] username = apikey api_key = 123cNyhzg45Ab6789ADyzwR_2LAagNVbySgY73tAQOz1 endpoint_url = https://api.softlayer.com/rest/v3.1/ timeout = 40softlayer-python-5.9.8/docs/dev/000077500000000000000000000000001415376457700166155ustar00rootroot00000000000000softlayer-python-5.9.8/docs/dev/cla-corporate.md000066400000000000000000000154551415376457700217040ustar00rootroot00000000000000#### 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.9.8/docs/dev/cla-individual.md000066400000000000000000000151021415376457700220230ustar00rootroot00000000000000#### 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.9.8/docs/dev/cli.rst000066400000000000000000000145641415376457700201300ustar00rootroot00000000000000.. _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") Documenting Commands -------------------- All commands should be documented, luckily there is a sphinx module that makes this pretty easy. If you were adding a summary command to `slcli account` you would find the documentation in `docs/cli/account.rst` and you would just need to add this for your command :: .. click:: SoftLayer.CLI.account.summary:cli :prog: account summary :show-nested: The following REGEX can take the route entry and turn it into a document entry. :: s/^\('([a-z]*):([a-z-]*)', '([a-zA-Z\.:_]*)'\),$/.. click:: $3\n :prog: $1 $2\n :show-nested:\n/ Find:: ^\('([a-z]*):([a-z-]*)', '([a-zA-Z\.:_]*)'\),$ REPLACE:: .. click:: $3 :prog: $1 $2 :show-nested: I tried to get sphinx-click to auto document the ENTIRE slcli, but the results were all on one page, and required a few changes to sphinx-click itself to work. This is due to the fact that most commands in SLCLI use the function name "cli", and some hacks would have to be put inplace to use the path name instead. Architecture ------------ *SLCLI* is the base command, and it starts at *SoftLayer\CLI\core.py*. Commands are loaded from the *SoftLayer\CLI\routes.py* file. How Click figures this out is defined by the *CommandLoader* class in core.py, which is an example of a `MultiCommand `_. There are a few examples of commands that are three levels deep, that use a bit more graceful command loader. - *SoftLayer\CLI\virt\capacity\__init__.py* - *SoftLayer\CLI\virt\placementgroup\__init__.py* - *SoftLayer\CLI\object_storage\credential\__init__.py* These commands are not directly listed in the routes file, because the autoloader doesn't have the ability to parse multiple commands like that. For now it was easier to make the rare thrid level commands have their own special loader than re-write the base command loader to be able to look deeper into the project for commands. softlayer-python-5.9.8/docs/dev/example_module.rst000066400000000000000000000040271415376457700223520ustar00rootroot00000000000000.. _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.9.8/docs/dev/index.rst000066400000000000000000000144641415376457700204670ustar00rootroot00000000000000.. _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. 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 Fixtures ~~~~~~~~ Testing of this project relies quite heavily on fixtures to simulate API calls. When running the unit tests, we use the FixtureTransport class, which instead of making actual API calls, loads data from `/fixtures/SoftLayer_Service_Name.py` and tries to find a variable that matches the method you are calling. When adding new Fixtures you should try to sanitize the data of any account identifiying results, such as account ids, username, and that sort of thing. It is ok to leave the id in place for things like datacenter ids, price ids. To Overwrite a fixture, you can use a mock object to do so. Like either of these two methods: :: # From tests/CLI/modules/vs_capacity_tests.py from SoftLayer.fixtures import SoftLayer_Product_Package def test_create_test(self): item_mock = self.set_mock('SoftLayer_Product_Package', 'getItems') item_mock.return_value = SoftLayer_Product_Package.getItems_RESERVED_CAPACITY def test_detail_pending(self): capacity_mock = self.set_mock('SoftLayer_Virtual_ReservedCapacityGroup', 'getObject') get_object = { 'name': 'test-capacity', 'instances': [] } capacity_mock.return_value = get_object 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 sphinx-build -b html ./ ./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 Autopep8 can fix a lot of the simple flake8 errors about whitespace and indention. :: autopep8 -r -a -v -i --max-line-length 119 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.9.8/docs/index.rst000066400000000000000000000023321415376457700177000ustar00rootroot00000000000000.. SoftLayer API Python Client documentation SoftLayer API Python Client |version| ======================================== `API Docs `_ ``|`` `GitHub `_ ``|`` `Issues `_ ``|`` `Pull Requests `_ ``|`` `PyPI `_ ``|`` 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 cli_directory Contributing ------------ .. toctree:: :maxdepth: 1 :glob: dev/index dev/cli External Links -------------- * `SoftLayer API Documentation `_ * `Source on GitHub `_ * `Issues `_ * `Pull Requests `_ * `PyPI `_ softlayer-python-5.9.8/docs/install.rst000066400000000000000000000033001415376457700202330ustar00rootroot00000000000000.. _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.9.8/docs/requirements.txt000066400000000000000000000000451415376457700213220ustar00rootroot00000000000000sphinx sphinx-click click prettytablesoftlayer-python-5.9.8/fabfile.py000066400000000000000000000034321415376457700170530ustar00rootroot00000000000000import click import os.path import shutil import subprocess import sys from pprint import pprint as pp def make_html(): """Build HTML docs""" click.secho("Building HTML") subprocess.run('make html', cwd='docs', shell=True) def upload(): """Upload distribution to PyPi""" cmd_setup = 'python setup.py sdist bdist_wheel' click.secho("\tRunning %s" % cmd_setup, fg='yellow') subprocess.run(cmd_setup, shell=True) cmd_twine = 'twine upload dist/*' click.secho("\tRunning %s" % cmd_twine, fg='yellow') subprocess.run(cmd_twine, shell=True) def clean(): click.secho("* 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) @click.command() @click.argument('version') @click.option('--force', default=False, is_flag=True, help="Force upload") def release(version, force): """Perform a release. Example: $ python fabfile.py 1.2.3 """ if version.startswith("v"): exit("Version should not start with 'v'") version_str = "v%s" % version clean() subprocess.run("pip install wheel", shell=True) print(" * Uploading to PyPI") upload() make_html() force_option = 'f' if force else '' cmd_tag = "git tag -%sam \"%s\" %s" % (force_option, version_str, version_str) click.secho(" * Tagging Version %s" % version_str) click.secho("\tRunning %s" % cmd_tag, fg='yellow') subprocess.run(cmd_tag, shell=True) cmd_push = "git push upstream %s" % version_str click.secho(" * Pushing Tag to upstream") click.secho("\tRunning %s" % cmd_push, fg='yellow') subprocess.run(cmd_push, shell=True) if __name__ == '__main__': release()softlayer-python-5.9.8/pkg/000077500000000000000000000000001415376457700156705ustar00rootroot00000000000000softlayer-python-5.9.8/pkg/softlayer-python.spec000066400000000000000000000030571415376457700221000ustar00rootroot00000000000000# 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.9.8/setup.cfg000066400000000000000000000002231415376457700167250ustar00rootroot00000000000000[tool:pytest] python_files = *_tests.py filterwarnings = ignore::DeprecationWarning ignore::PendingDeprecationWarning [wheel] universal=1 softlayer-python-5.9.8/setup.py000066400000000000000000000036261415376457700166300ustar00rootroot00000000000000from __future__ import print_function import codecs import os from setuptools import setup, find_packages # pylint: disable=inconsistent-return-statements 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.9.8', description=DESCRIPTION, long_description=LONG_DESCRIPTION, author='SoftLayer, Inc., an IBM Company', author_email='SLDNDeveloperRelations@wwpdl.vnet.ibm.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', ], }, python_requires='>=3.5', install_requires=[ 'ptable >= 0.9.2', 'click >= 7', 'requests >= 2.20.0', 'prompt_toolkit >= 2', 'pygments >= 2.0.0', 'urllib3 >= 1.24' ], keywords=['softlayer', 'cloud', 'slcli'], 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.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', ], ) softlayer-python-5.9.8/snap/000077500000000000000000000000001415376457700160505ustar00rootroot00000000000000softlayer-python-5.9.8/snap/snapcraft.yaml000066400000000000000000000013421415376457700207150ustar00rootroot00000000000000name: slcli adopt-info: slcli summary: A CLI tool to interact with the SoftLayer API. description: | A command-line interface is also included and can be used to manage various SoftLayer products and services. license: MIT base: core20 grade: stable confinement: strict apps: slcli: command: bin/slcli environment: LC_ALL: C.UTF-8 plugs: - home - network - network-bind parts: slcli: source: https://github.com/softlayer/softlayer-python source-type: git plugin: python override-pull: | snapcraftctl pull snapcraftctl set-version "$(git describe --tags | sed 's/^v//')" build-packages: - python3 stage-packages: - python3 softlayer-python-5.9.8/tests/000077500000000000000000000000001415376457700162515ustar00rootroot00000000000000softlayer-python-5.9.8/tests/CLI/000077500000000000000000000000001415376457700166605ustar00rootroot00000000000000softlayer-python-5.9.8/tests/CLI/__init__.py000066400000000000000000000000001415376457700207570ustar00rootroot00000000000000softlayer-python-5.9.8/tests/CLI/core_tests.py000066400000000000000000000115151415376457700214070ustar00rootroot00000000000000""" SoftLayer.tests.CLI.core_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import io import logging import click from unittest import mock as mock from requests.models import Response import SoftLayer from SoftLayer.CLI import core from SoftLayer.CLI import environment from SoftLayer import testing 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) @mock.patch('requests.get') def test_get_latest_version(self, request_get): response = Response() response.status_code = 200 response.json = mock.MagicMock(return_value={"info": {"version": "1.1.1"}}) request_get.return_value = response version = core.get_latest_version() self.assertIn('1.1.1', version) @mock.patch('requests.get') def test_unable_get_latest_version(self, request_get): request_get.side_effect = Exception version = core.get_latest_version() self.assertIn('Unable', version) @mock.patch('SoftLayer.CLI.core.get_latest_version') def test_get_version_message(self, get_latest_version_mock): get_latest_version_mock.return_value = '1.1.1' env = environment.Environment() result = self.run_command(['--version'], env=env) self.assert_no_fail(result) class CoreMainTests(testing.TestCase): @mock.patch('SoftLayer.CLI.core.cli.main') @mock.patch('sys.stdout', new_callable=io.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=io.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=io.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.9.8/tests/CLI/custom_types_tests.py000066400000000000000000000021521415376457700232120ustar00rootroot00000000000000""" 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.9.8/tests/CLI/environment_tests.py000066400000000000000000000052401415376457700230210ustar00rootroot00000000000000""" SoftLayer.tests.CLI.environment_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import click from unittest import mock as 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) @mock.patch('click.prompt') @mock.patch('tkinter.Tk') def test_getpass_issues1436(self, tk, prompt_mock): prompt_mock.return_value = 'àR' self.env.getpass('input') prompt_mock.assert_called_with('input', default=None, hide_input=True) tk.assert_called_with() 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') @mock.patch('click.echo') def test_print_unicode(self, echo): output = "\u3010TEST\u3011 image" # https://docs.python.org/3.6/library/exceptions.html#UnicodeError echo.side_effect = [ UnicodeEncodeError('utf8', output, 0, 1, "Test Exception"), output ] self.env.fout(output) self.assertEqual(2, echo.call_count) def test_format_output_is_json(self): self.env.format = 'jsonraw' self.assertTrue(self.env.format_output_is_json()) softlayer-python-5.9.8/tests/CLI/helper_tests.py000066400000000000000000000406041415376457700217370ustar00rootroot00000000000000# -*- 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 from unittest import mock as mock 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_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): listing = formatting.listing([1, 'two'], separator=':') self.assertEqual([1, 'two'], list(listing)) self.assertEqual(':', listing.separator) listing = formatting.listing([]) self.assertEqual(',', listing.separator) def test_to_python(self): listing = formatting.listing([1, 'two']) result = listing.to_python() self.assertEqual([1, 'two'], result) listing = formatting.listing(x for x in [1, 'two']) result = listing.to_python() self.assertEqual([1, 'two'], result) def test_str(self): listing = formatting.listing([1, 'two']) result = str(listing) self.assertEqual('1,two', result) listing = formatting.listing((x for x in [1, 'two']), separator=':') result = str(listing) 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): self.assertEqual(helpers.resolve_id(lambda r: [12345], 'test'), 12345) def test_resolve_id_none(self): self.assertRaises( exceptions.CLIAbort, helpers.resolve_id, lambda r: [], 'test') def test_resolve_id_multiple(self): self.assertRaises( exceptions.CLIAbort, helpers.resolve_id, lambda r: [12345, 54321], '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']]) def test_format_api_list_with_none_value(self): result = formatting._format_list([{'key': [None, 'value']}, None]) self.assertIsInstance(result, formatting.Table) self.assertEqual(result.columns, ['key']) softlayer-python-5.9.8/tests/CLI/modules/000077500000000000000000000000001415376457700203305ustar00rootroot00000000000000softlayer-python-5.9.8/tests/CLI/modules/__init__.py000066400000000000000000000000001415376457700224270ustar00rootroot00000000000000softlayer-python-5.9.8/tests/CLI/modules/account_tests.py000066400000000000000000000131651415376457700235660ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.account_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Tests for the user cli command """ import json from SoftLayer.fixtures import SoftLayer_Account as SoftLayer_Account from SoftLayer import testing class AccountCLITests(testing.TestCase): def set_up(self): self.SLNOE = 'SoftLayer_Notification_Occurrence_Event' # slcli account event-detail def test_event_detail(self): result = self.run_command(['account', 'event-detail', '1234']) self.assert_no_fail(result) self.assert_called_with(self.SLNOE, 'getObject', identifier='1234') def test_event_details_ack(self): result = self.run_command(['account', 'event-detail', '1234', '--ack']) self.assert_no_fail(result) self.assert_called_with(self.SLNOE, 'getObject', identifier='1234') self.assert_called_with(self.SLNOE, 'acknowledgeNotification', identifier='1234') # slcli account events def test_events(self): result = self.run_command(['account', 'events']) self.assert_no_fail(result) self.assert_called_with(self.SLNOE, 'getAllObjects') def test_event_ack_all(self): result = self.run_command(['account', 'events', '--ack-all']) self.assert_no_fail(result) self.assert_called_with(self.SLNOE, 'getAllObjects') self.assert_called_with(self.SLNOE, 'acknowledgeNotification', identifier=1234) def test_event_jsonraw_output(self): # https://github.com/softlayer/softlayer-python/issues/1545 command = '--format jsonraw account events' command_params = command.split() result = self.run_command(command_params) json_text_tables = result.stdout.split('\n') # removing an extra item due to an additional Newline at the end of the output json_text_tables.pop() # each item in the json_text_tables should be a list for json_text_table in json_text_tables: json_table = json.loads(json_text_table) self.assertIsInstance(json_table, list) # slcli account invoice-detail def test_invoice_detail(self): result = self.run_command(['account', 'invoice-detail', '1234']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Billing_Invoice', 'getInvoiceTopLevelItems', identifier='1234') def test_invoice_detail_details(self): result = self.run_command(['account', 'invoice-detail', '1234', '--details']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Billing_Invoice', 'getInvoiceTopLevelItems', identifier='1234') # slcli account invoices def test_invoices(self): result = self.run_command(['account', 'invoices']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Account', 'getInvoices', limit=50) def test_invoices_limited(self): result = self.run_command(['account', 'invoices', '--limit=10']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Account', 'getInvoices', limit=10) def test_invoices_closed(self): _filter = { 'invoices': { 'createDate': { 'operation': 'orderBy', 'options': [{ 'name': 'sort', 'value': ['DESC'] }] } } } result = self.run_command(['account', 'invoices', '--closed']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Account', 'getInvoices', limit=50, filter=_filter) def test_invoices_all(self): result = self.run_command(['account', 'invoices', '--all']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Account', 'getInvoices', limit=50) def test_single_invoice(self): amock = self.set_mock('SoftLayer_Account', 'getInvoices') amock.return_value = SoftLayer_Account.getInvoices[0] result = self.run_command(['account', 'invoices', '--limit=1']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Account', 'getInvoices', limit=1) # slcli account summary def test_account_summary(self): result = self.run_command(['account', 'summary']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Account', 'getObject') # slcli account billing-items def test_account_billing_items(self): result = self.run_command(['account', 'billing-items']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Account', 'getAllTopLevelBillingItems') # slcli account item-detail def test_account_get_billing_item_detail(self): result = self.run_command(['account', 'item-detail', '12345']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Billing_Item', 'getObject', identifier='12345') # slcli account cancel-item def test_account_cancel_item(self): result = self.run_command(['account', 'cancel-item', '12345']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Billing_Item', 'cancelItem', identifier='12345') def test_acccount_order(self): result = self.run_command(['account', 'orders']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Billing_Order', 'getAllObjects') def test_acccount_licenses(self): result = self.run_command(['account', 'licenses']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Account', 'getActiveVirtualLicenses') self.assert_called_with('SoftLayer_Account', 'getActiveAccountLicenses') softlayer-python-5.9.8/tests/CLI/modules/autoscale_tests.py000066400000000000000000000065751415376457700241210ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.autoscale_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Tests for the autoscale cli command """ import sys from unittest import mock as mock from SoftLayer import fixtures from SoftLayer import testing import tempfile class AutoscaleTests(testing.TestCase): def test_logs_dates(self): result = self.run_command(['autoscale', 'logs', '123456', '-d', '2019-02-02']) print(result) self.assert_no_fail(result) def test_scale_down(self): result = self.run_command(['autoscale', 'scale', '123456', '--down', '--amount', '2']) self.assert_no_fail(result) def test_scale_up(self): result = self.run_command(['autoscale', 'scale', '123456', '--up', '--amount', '2']) self.assert_no_fail(result) def test_scale_to(self): result = self.run_command(['autoscale', 'scale', '789654123', '--down', '--amount', '2']) self.assert_no_fail(result) def test_scale_by_up(self): result = self.run_command(['autoscale', 'scale', '789654123', '--by', '--down', '--amount', '-1']) self.assert_no_fail(result) def test_scale_cancel(self): result = self.run_command(['autoscale', 'scale', '789654123', '--by', '--down', '--amount', '1']) self.assert_no_fail(result) def test_autoscale_list(self): result = self.run_command(['autoscale', 'list']) self.assert_no_fail(result) def test_autoscale_detail(self): result = self.run_command(['autoscale', 'detail', '12222222']) self.assert_no_fail(result) def test_autoscale_tag(self): result = self.run_command(['autoscale', 'tag', '12345']) self.assert_no_fail(result) @mock.patch('SoftLayer.managers.autoscale.AutoScaleManager.edit') def test_autoscale_edit(self, manager): result = self.run_command(['autoscale', 'edit', '12345', '--name', 'test']) self.assert_no_fail(result) manager.assert_called_with('12345', {'name': 'test'}) @mock.patch('SoftLayer.managers.autoscale.AutoScaleManager.edit') def test_autoscale_edit_userdata(self, manager): group = fixtures.SoftLayer_Scale_Group.getObject template = { 'virtualGuestMemberTemplate': group['virtualGuestMemberTemplate'] } template['virtualGuestMemberTemplate']['userData'] = [{'value': 'test'}] result = self.run_command(['autoscale', 'edit', '12345', '--userdata', 'test']) self.assert_no_fail(result) manager.assert_called_with('12345', template) @mock.patch('SoftLayer.managers.autoscale.AutoScaleManager.edit') def test_autoscale_edit_userfile(self, manager): # On windows, python cannot edit a NamedTemporaryFile. if(sys.platform.startswith("win")): self.skipTest("Test doesn't work in Windows") group = fixtures.SoftLayer_Scale_Group.getObject template = { 'virtualGuestMemberTemplate': group['virtualGuestMemberTemplate'] } template['virtualGuestMemberTemplate']['userData'] = [{'value': ''}] with tempfile.NamedTemporaryFile() as userfile: result = self.run_command(['autoscale', 'edit', '12345', '--userfile', userfile.name]) self.assert_no_fail(result) manager.assert_called_with('12345', template) softlayer-python-5.9.8/tests/CLI/modules/block_tests.py000066400000000000000000001044371415376457700232270ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.block_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting from SoftLayer import SoftLayerAPIError from SoftLayer import testing import json from unittest import mock as 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 = 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) isinstance(json.loads(result.output)['IOPs'], float) self.assert_called_with('SoftLayer_Network_Storage', 'getObject', identifier=1234) self.assertEqual({ 'Username': 'username', 'LUN Id': '2', 'Notes': "{'status': 'available'}", '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_detail_name_identifier(self): result = self.run_command(['block', 'volume-detail', 'SL-12345']) expected_filter = { 'iscsiNetworkStorage': { 'serviceResource': { 'type': { 'type': {'operation': '!~ ISCSI'} } }, 'storageType': { 'keyName': {'operation': '*= BLOCK_STORAGE'} }, 'username': {'operation': '_= SL-12345'} } } self.assert_called_with('SoftLayer_Account', 'getIscsiNetworkStorage', filter=expected_filter) self.assert_called_with('SoftLayer_Network_Storage', 'getObject', identifier=100) self.assert_no_fail(result) 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, 'IOPs': None, 'ip_addr': '10.1.2.3', 'lunId': None, 'notes': "{'status': 'available'}", '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_list_notes_format_output_json(self, list_mock): note_mock = 'test ' * 5 list_mock.return_value = [ {'notes': note_mock} ] result = self.run_command(['--format', 'json', 'block', 'volume-list', '--columns', 'notes']) self.assert_no_fail(result) self.assertEqual( [{ 'notes': note_mock, }], json.loads(result.output)) @mock.patch('SoftLayer.BlockStorageManager.list_block_volumes') def test_volume_list_reduced_notes_format_output_table(self, list_mock): note_mock = 'test ' * 10 expected_reduced_note = 'test ' * 4 list_mock.return_value = [ {'notes': note_mock} ] expected_table = formatting.Table(['notes']) expected_table.add_row([expected_reduced_note]) expected_output = formatting.format_output(expected_table)+'\n' result = self.run_command(['--format', 'table', 'block', 'volume-list', '--columns', 'notes']) self.assert_no_fail(result) self.assertEqual(expected_output, result.output) def test_volume_list_order(self): result = self.run_command(['block', 'volume-list', '--order=1234567']) self.assert_no_fail(result) json_result = json.loads(result.output) self.assertEqual(json_result[0]['id'], 100) @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_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' '\nYou may run "slcli block volume-list --order 478" to find this block volume ' 'after it is ready.\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' '\nYou may run "slcli block volume-list --order 478" to find this block volume ' 'after it is ready.\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' '\nYou may run "slcli block volume-list --order 10983647" to find this block volume ' 'after it is ready.\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_assign_subnets_to_acl(self): result = self.run_command(['block', 'subnets-assign', '12345', '--subnet-id=12345678']) self.assert_no_fail(result) def test_remove_subnets_from_acl(self): result = self.run_command(['block', 'subnets-remove', '12345', '--subnet-id=12345678']) self.assert_no_fail(result) def test_get_subnets_in_acl(self): result = self.run_command(['block', 'subnets-list', '12345']) self.assert_no_fail(result) def test_replicant_failover(self): result = self.run_command(['block', 'replica-failover', '12345678', '--replicant-id=5678']) self.assert_no_fail(result) self.assertEqual('Failover to replicant is now in progress.\n', result.output) @mock.patch('SoftLayer.CLI.formatting.confirm') @mock.patch('SoftLayer.BlockStorageManager.disaster_recovery_failover_to_replicant') def test_disaster_recovery_failover(self, disaster_recovery_failover_mock, confirm_mock): confirm_mock.return_value = True disaster_recovery_failover_mock.return_value = True result = self.run_command(['block', 'disaster-recovery-failover', '12345678', '--replicant-id=5678']) self.assert_no_fail(result) self.assertIn('Disaster Recovery 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) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_disaster_recovery_failover_aborted(self, confirm_mock): confirm_mock.return_value = False result = self.run_command(['block', 'disaster-recovery-failover', '12345678', '--replicant-id=5678']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.CLIAbort) def test_replicant_failback(self): result = self.run_command(['block', 'replica-failback', '12345678']) 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']) 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, dependent_duplicate=False) 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) @mock.patch('SoftLayer.BlockStorageManager.list_block_volume_limit') def test_volume_limit(self, list_mock): list_mock.return_value = [ { "datacenterName": "global", "maximumAvailableCount": 300, "provisionedCount": 100 }] result = self.run_command(['block', 'volume-limits']) self.assert_no_fail(result) def test_dupe_refresh(self): result = self.run_command(['block', 'volume-refresh', '102', '103']) self.assert_no_fail(result) def test_dep_dupe_convert(self): result = self.run_command(['block', 'volume-convert', '102']) self.assert_no_fail(result) @mock.patch('SoftLayer.BlockStorageManager.volume_set_note') def test_volume_set_note(self, set_note): set_note.return_value = True result = self.run_command(['block', 'volume-set-note', '102', '--note=testing']) self.assert_no_fail(result) self.assertIn("successfully!", result.output) @mock.patch('SoftLayer.BlockStorageManager.volume_set_note') def test_volume_not_set_note(self, set_note): set_note.return_value = False result = self.run_command(['block', 'volume-set-note', '102', '--note=testing']) self.assert_no_fail(result) self.assertIn("Note could not be set!", result.output) @mock.patch('SoftLayer.BlockStorageManager.get_volume_snapshot_notification_status') def test_snapshot_get_notification_status(self, status): status.side_effect = [None, 1, 0] expected = ['Enabled', 'Enabled', 'Disabled'] for expect in expected: result = self.run_command(['block', 'snapshot-get-notification-status', '999']) self.assert_no_fail(result) self.assertIn(expect, result.output) softlayer-python-5.9.8/tests/CLI/modules/call_api_tests.py000066400000000000000000000300631415376457700236720ustar00rootroot00000000000000""" 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 SoftLayerAPIError 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) self.assertIsNotNone(result.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')) def test_fixture_not_implemented(self): service = 'SoftLayer_Test' method = 'getTest' result = self.run_command(['call-api', service, method]) self.assertEqual(result.exit_code, 1) self.assert_called_with(service, method) self.assertIsInstance(result.exception, SoftLayerAPIError) output = '{} fixture is not implemented'.format(service) self.assertIn(output, result.exception.faultString) def test_fixture_not_implemented_method(self): call_service = 'SoftLayer_Account' call_method = 'getTest' result = self.run_command(['call-api', call_service, call_method]) self.assertEqual(result.exit_code, 1) self.assert_called_with(call_service, call_method) self.assertIsInstance(result.exception, SoftLayerAPIError) output = '%s::%s fixture is not implemented' % (call_service, call_method) self.assertIn(output, result.exception.faultString) def test_fixture_exception(self): call_service = 'SoftLayer_Account' call_method = 'getTest' result = self.run_command(['call-api', call_service, call_method]) try: self.assert_no_fail(result) except Exception as ex: print(ex) self.assertEqual(result.exit_code, 1) self.assert_called_with(call_service, call_method) self.assertIsInstance(result.exception, SoftLayerAPIError) output = '%s::%s fixture is not implemented' % (call_service, call_method) self.assertIn(output, result.exception.faultString) def test_json_filter_validation(self): json_filter = '{"test":"something"}' result = call_api._validate_filter(None, None, json_filter) self.assertEqual(result['test'], 'something') # Valid JSON, but we expect objects, not simple types with pytest.raises(exceptions.CLIAbort): call_api._validate_filter(None, None, '"test"') # Invalid JSON with pytest.raises(exceptions.CLIAbort): call_api._validate_filter(None, None, 'test') # Empty Request result = call_api._validate_filter(None, None, None) self.assertEqual(None, result) def test_json_parameters_validation(self): json_params = ('{"test":"something"}', 'String', 1234, '[{"a":"b"}]', '{funky non [ Json') result = call_api._validate_parameters(None, None, json_params) self.assertEqual(result[0], {"test": "something"}) self.assertEqual(result[1], "String") self.assertEqual(result[2], 1234) self.assertEqual(result[3], [{"a": "b"}]) self.assertEqual(result[4], "{funky non [ Json") def test_filter_with_filter(self): result = self.run_command(['call-api', 'Account', 'getObject', '--filter=nested.property=5432', '--json-filter={"test":"something"}']) self.assertEqual(2, result.exit_code) self.assertEqual(result.exception.message, "--filter and --json-filter cannot be used together.") self.assertIsInstance(result.exception, exceptions.CLIAbort) def test_json_filter(self): pass result = self.run_command(['call-api', 'Account', 'getObject', '--json-filter={"test":"something"}']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Account', 'getObject', filter={"test": "something"}) def test_call_api_orderBy(self): result = self.run_command(['call-api', 'Account', 'getVirtualGuests', '--orderBy', 'virtualGuests.id=DESC', '--mask=virtualGuests.typeId,maxCpu', '-f', 'virtualGuests.typeId=1']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Account', 'getVirtualGuests', filter={ 'virtualGuests': {'id': { 'operation': 'orderBy', 'options': [{ 'name': 'sort', 'value': ['DESC']}]}, 'typeId': {'operation': 1}} }) softlayer-python-5.9.8/tests/CLI/modules/cdn_tests.py000066400000000000000000000127771415376457700227060ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.cdn_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import json from SoftLayer.CLI import exceptions from SoftLayer import testing 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), [{'cname': 'cdnakauuiet7s6u6.cdnedge.bluemix.net', 'domain': 'test.example.com', 'origin': '1.1.1.1', 'status': 'CNAME_CONFIGURATION', 'unique_id': '9934111111111', 'vendor': 'akamai'}] ) def test_detail_account(self): result = self.run_command(['cdn', 'detail', '--history=30', '1245']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), {'hit_radio': '0.0 %', 'hostname': 'test.example.com', 'origin': '1.1.1.1', 'origin_type': 'HOST_SERVER', 'path': '/', 'protocol': 'HTTP', 'provider': 'akamai', 'status': 'CNAME_CONFIGURATION', 'total_bandwidth': '0.0 GB', 'total_hits': '0', 'unique_id': '9934111111111'} ) def test_purge_content(self): result = self.run_command(['cdn', 'purge', '1234', '/article/file.txt']) self.assert_no_fail(result) def test_list_origins(self): result = self.run_command(['cdn', 'origin-list', '1234']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), [{'HTTP Port': 80, 'Origin': '10.10.10.1', 'Path': '/example', 'Status': 'RUNNING'}, {'HTTP Port': 80, 'Origin': '10.10.10.1', 'Path': '/example1', 'Status': 'RUNNING'}]) def test_add_origin_server(self): result = self.run_command( ['cdn', 'origin-add', '-t', 'server', '-H=test.example.com', '-p', 80, '-o', 'web', '-c=include-all', '1234', '10.10.10.1', '/example/videos2']) self.assert_no_fail(result) def test_add_origin_storage(self): result = self.run_command(['cdn', 'origin-add', '-t', 'storage', '-b=test-bucket', '-H=test.example.com', '-p', 80, '-o', 'web', '-c=include-all', '1234', '10.10.10.1', '/example/videos2']) self.assert_no_fail(result) def test_add_origin_without_storage(self): result = self.run_command(['cdn', 'origin-add', '-t', 'storage', '-H=test.example.com', '-p', 80, '-o', 'web', '-c=include-all', '1234', '10.10.10.1', '/example/videos2']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.ArgumentError) def test_add_origin_storage_with_file_extensions(self): result = self.run_command( ['cdn', 'origin-add', '-t', 'storage', '-b=test-bucket', '-e', 'jpg', '-H=test.example.com', '-p', 80, '-o', 'web', '-c=include-all', '1234', '10.10.10.1', '/example/videos2']) self.assert_no_fail(result) def test_remove_origin(self): result = self.run_command(['cdn', 'origin-remove', '1234', '/example1']) self.assert_no_fail(result) self.assertEqual(result.output, "Origin with path /example1 has been deleted\n") def test_edit_header(self): result = self.run_command(['cdn', 'edit', 'test.example.com', '--header=www.test.com']) self.assert_no_fail(result) header_result = json.loads(result.output) self.assertEqual('www.test.com', header_result['Header']) def test_edit_http_port(self): result = self.run_command(['cdn', 'edit', 'test.example.com', '--http-port=83']) self.assert_no_fail(result) header_result = json.loads(result.output) self.assertEqual(83, header_result['Http Port']) def test_edit_respect_headers(self): result = self.run_command(['cdn', 'edit', 'test.example.com', '--respect-headers=1']) self.assert_no_fail(result) header_result = json.loads(result.output) self.assertEqual(True, header_result['Respect Headers']) def test_edit_cache(self): result = self.run_command(['cdn', 'edit', 'test.example.com', '--cache', 'include-specified', '--cache', 'test']) self.assert_no_fail(result) header_result = json.loads(result.output) self.assertEqual('include: test', header_result['Cache key optimization']) def test_edit_cache_by_uniqueId(self): result = self.run_command(['cdn', 'edit', '9934111111111', '--cache', 'include-specified', '--cache', 'test']) self.assert_no_fail(result) header_result = json.loads(result.output) self.assertEqual('include: test', header_result['Cache key optimization']) softlayer-python-5.9.8/tests/CLI/modules/config_tests.py000066400000000000000000000166521415376457700234030ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.config_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import json import os import sys import tempfile from unittest import mock as 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) self.config_file = "./test_config_file" def tearDown(self): # Windows doesn't let you write and read from temp files # So use a real file instead. if os.path.exists(self.config_file): os.remove(self.config_file) @mock.patch('SoftLayer.Client') @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, client): client.return_value = self.env.client 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 = ['public', 'user', 0] result = self.run_command(['--config=%s' % config_file.name, 'config', 'setup']) self.assert_no_fail(result) self.assertIn('Configuration Updated Successfully', result.output) contents = config_file.read().decode("utf-8") self.assertIn('[softlayer]', contents) self.assertIn('username = user', contents) self.assertIn('api_key = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', contents) self.assertIn('endpoint_url = %s' % consts.API_PUBLIC_ENDPOINT, contents) @mock.patch('SoftLayer.Client') @mock.patch('SoftLayer.CLI.formatting.confirm') @mock.patch('SoftLayer.CLI.environment.Environment.getpass') @mock.patch('SoftLayer.CLI.environment.Environment.input') def test_setup_float_timeout(self, mocked_input, getpass, confirm_mock, client): client.return_value = self.env.client confirm_mock.return_value = True getpass.return_value = 'A' * 64 mocked_input.side_effect = ['public', 'user', 10.0] result = self.run_command(['--config=%s' % self.config_file, 'config', 'setup']) self.assert_no_fail(result) self.assertIn('Configuration Updated Successfully', result.output) with open(self.config_file, 'r') as config_file: contents = config_file.read() self.assertIn('[softlayer]', contents) self.assertIn('username = user', contents) self.assertIn('api_key = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', contents) self.assertIn('endpoint_url = %s' % consts.API_PUBLIC_ENDPOINT, contents) self.assertNotIn('timeout = 10.0\n', contents) self.assertIn('timeout = 10\n', contents) @mock.patch('SoftLayer.Client') @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, client): client.return_value = self.env.client with tempfile.NamedTemporaryFile() as config_file: confirm_mock.return_value = False getpass.return_value = 'A' * 64 mocked_input.side_effect = ['public', 'user', 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_github_1074(self, mocked_input, getpass): """Tests to make sure directly using an endpoint works""" mocked_input.side_effect = ['test-endpoint'] endpoint_url = config.get_endpoint_url(self.env) self.assertEqual(endpoint_url, 'test-endpoint') @mock.patch('SoftLayer.CLI.environment.Environment.getpass') @mock.patch('SoftLayer.CLI.environment.Environment.input') def test_get_endpoint(self, mocked_input, getpass): """Tests to make sure directly using an endpoint works""" mocked_input.side_effect = ['private', 'custom', 'test.com', 'public', 'test-endpoint'] # private endpoint_url = config.get_endpoint_url(self.env) self.assertEqual(endpoint_url, consts.API_PRIVATE_ENDPOINT) # custom - test.com endpoint_url = config.get_endpoint_url(self.env) self.assertEqual(endpoint_url, 'test.com') # public endpoint_url = config.get_endpoint_url(self.env) self.assertEqual(endpoint_url, consts.API_PUBLIC_ENDPOINT) # test-endpoint endpoint_url = config.get_endpoint_url(self.env) self.assertEqual(endpoint_url, 'test-endpoint') @mock.patch('SoftLayer.CLI.environment.Environment.input') @mock.patch('SoftLayer.CLI.config.setup.get_sso_url') @mock.patch('SoftLayer.CLI.config.setup.get_accounts') @mock.patch('SoftLayer.API.IAMClient.authenticate_with_passcode') @mock.patch('SoftLayer.API.IAMClient.refresh_iam_token') @mock.patch('SoftLayer.API.IAMClient.call') def test_sso_login(self, api_call, token, passcode, get_accounts, get_sso_url, mocked_input): """Tests to make sure directly using an endpoint works""" mocked_input.side_effect = ['n', '123qweasd'] get_sso_url.return_value = "https://test.com/" get_accounts.return_value = {"account_id": 12345, "ims_id": 5555} passcode.return_value = {"access_token": "aassddffggh", "refresh_token": "qqqqqqq"} token.return_value = {"access_token": "zzzzzz", "refresh_token": "fffffff"} test_key = "zz112233" user_object_1 = { "apiAuthenticationKeys": [{"authenticationKey": test_key}], "username": "testerson", "id": 99} api_call.side_effect = [user_object_1] user, apikey = config.sso_login(self.env) self.assertEqual("testerson", user) self.assertEqual(test_key, apikey) softlayer-python-5.9.8/tests/CLI/modules/dedicatedhost_tests.py000066400000000000000000000421361415376457700247360ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.dedicatedhosts_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import json from unittest import mock as mock 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': 12345, 'memoryCapacity': 242, 'name': 'test-dedicated' }] ) 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', '12345', '--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': 'test.com', 'hostname': 'test-dedicated', 'id': 12345, 'uuid': 'F9329795-4220-4B0A-B970-C86B950667FA' }], 'id': 12345, 'memory capacity': 242, 'modify date': '2017-11-06T11:38:20-06:00', 'name': 'test-dedicated', 'owner': 'test-dedicated', 'price_rate': 1515.556, 'router hostname': 'bcr01a.dal05', 'router id': 12345} ) 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': 'test.com', 'hostname': 'test-dedicated', 'id': 12345, 'uuid': 'F9329795-4220-4B0A-B970-C86B950667FA'}], 'id': 12345, 'memory capacity': 242, 'modify date': '2017-11-06T11:38:20-06:00', 'name': 'test-dedicated', 'owner': None, 'price_rate': 0, 'router hostname': 'bcr01a.dal05', 'router id': 12345} ) 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=test-dedicated', '--domain=test.com', '--datacenter=dal05', '--flavor=56_CORES_X_242_RAM_X_1_4_TB', '--billing=hourly']) self.assert_no_fail(result) args = ({ 'hardware': [{ 'domain': 'test.com', 'primaryBackendNetworkComponent': { 'router': { 'id': 12345 } }, 'hostname': 'test-dedicated' }], 'useHourlyPricing': True, 'location': 'DALLAS05', 'packageId': 813, 'complexType': 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost', 'prices': [{ 'id': 200269 }], 'quantity': 1},) self.assert_called_with('SoftLayer_Product_Order', 'placeOrder', args=args) def test_create_with_gpu(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.getAllObjectsDHGpu result = self.run_command(['dedicatedhost', 'create', '--hostname=test-dedicated', '--domain=test.com', '--datacenter=dal05', '--flavor=56_CORES_X_484_RAM_X_1_5_TB_X_2_GPU_P100', '--billing=hourly']) self.assert_no_fail(result) args = ({ 'hardware': [{ 'domain': 'test.com', 'primaryBackendNetworkComponent': { 'router': { 'id': 12345 } }, 'hostname': 'test-dedicated' }], '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=test-dedicated', '--domain=test.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': 'test-dedicated', 'domain': 'test.com', 'primaryBackendNetworkComponent': { 'router': { 'id': 12345 } } }], '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=test-dedicated', '--domain=test.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': 'test-dedicated', 'domain': 'test.com', 'primaryBackendNetworkComponent': { 'router': { 'id': 12345 } } }], '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=test-dedicated', '--domain=test.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_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=test-dedicated', '--domain=test.com', '--datacenter=dal05', '--flavor=56_CORES_X_242_RAM_X_1_4_TB', '--billing=hourly']) self.assertIsInstance(result.exception, exceptions.ArgumentError) args = ({ 'hardware': [{ 'domain': 'test.com', 'primaryBackendNetworkComponent': { 'router': { 'id': 12345 } }, 'hostname': 'test-dedicated' }], '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) @mock.patch('SoftLayer.DedicatedHostManager.cancel_host') def test_cancel_host(self, cancel_mock): result = self.run_command(['--really', 'dedicatedhost', 'cancel', '12345']) self.assert_no_fail(result) cancel_mock.assert_called_with(12345) self.assertEqual(str(result.output), 'Dedicated Host 12345 was cancelled\n') def test_cancel_host_abort(self): result = self.run_command(['dedicatedhost', 'cancel', '12345']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.CLIAbort) def test_cancel_guests(self): vs1 = {'id': 987, 'fullyQualifiedDomainName': 'foobar.example.com'} vs2 = {'id': 654, 'fullyQualifiedDomainName': 'wombat.example.com'} guests = self.set_mock('SoftLayer_Virtual_DedicatedHost', 'getGuests') guests.return_value = [vs1, vs2] vs_status1 = {'id': 987, 'server name': 'foobar.example.com', 'status': 'Cancelled'} vs_status2 = {'id': 654, 'server name': 'wombat.example.com', 'status': 'Cancelled'} expected_result = [vs_status1, vs_status2] result = self.run_command(['--really', 'dedicatedhost', 'cancel-guests', '12345']) self.assert_no_fail(result) self.assertEqual(expected_result, json.loads(result.output)) def test_cancel_guests_empty_list(self): guests = self.set_mock('SoftLayer_Virtual_DedicatedHost', 'getGuests') guests.return_value = [] result = self.run_command(['--really', 'dedicatedhost', 'cancel-guests', '12345']) self.assert_no_fail(result) self.assertEqual(str(result.output), 'There is not any guest into the dedicated host 12345\n') def test_cancel_guests_abort(self): result = self.run_command(['dedicatedhost', 'cancel-guests', '12345']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.CLIAbort) def test_list_guests(self): result = self.run_command(['dh', 'list-guests', '123', '--tag=tag']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), [{'hostname': 'vs-test1', 'domain': 'test.sftlyr.ws', 'primary_ip': '172.16.240.2', 'id': 200, 'power_state': 'Running', 'backend_ip': '10.45.19.37'}, {'hostname': 'vs-test2', 'domain': 'test.sftlyr.ws', 'primary_ip': '172.16.240.7', 'id': 202, 'power_state': 'Running', 'backend_ip': '10.45.19.35'}]) def _get_cancel_guests_return(self): vs_status1 = {'id': 123, 'fqdn': 'foobar.example.com', 'status': 'Cancelled'} vs_status2 = {'id': 456, 'fqdn': 'wombat.example.com', 'status': 'Cancelled'} return [vs_status1, vs_status2] softlayer-python-5.9.8/tests/CLI/modules/dns_tests.py000066400000000000000000000272341415376457700227200ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.dns_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import json import os.path import sys from unittest import mock as 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) actual_output = json.loads(result.output) self.assertEqual(actual_output[0]['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', 'hostname', 'A', 'data', '--zone=1234', '--ttl=100']) self.assert_no_fail(result) self.assertEqual(str(result.output), 'A record added successfully\n') def test_add_record_mx(self): result = self.run_command(['dns', 'record-add', 'hostname', 'MX', 'data', '--zone=1234', '--ttl=100', '--priority=25']) self.assert_no_fail(result) self.assertEqual(str(result.output), 'MX record added successfully\n') def test_add_record_srv(self): result = self.run_command(['dns', 'record-add', 'hostname', 'SRV', 'data', '--zone=1234', '--protocol=udp', '--port=88', '--ttl=100', '--weight=5']) self.assert_no_fail(result) self.assertEqual(str(result.output), 'SRV record added successfully\n') def test_add_record_ptr(self): result = self.run_command(['dns', 'record-add', '192.168.1.1', 'PTR', 'hostname', '--ttl=100']) self.assert_no_fail(result) self.assertEqual(str(result.output), 'PTR record added successfully\n') def test_add_record_abort(self): result = self.run_command(['dns', 'record-add', 'hostname', 'A', 'data', '--ttl=100']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.CLIAbort) self.assertEqual(result.exception.message, "A isn't a valid record type or zone is missing") @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) def test_issues_999(self): """Makes sure certain zones with a None host record are pritable""" # SRV records can have a None `host` record, or just a plain missing one. fake_records = [ { 'data': '1.2.3.4', 'id': 137416416, 'ttl': 900, 'type': 'srv' } ] record_mock = self.set_mock('SoftLayer_Dns_Domain', 'getResourceRecords') record_mock.return_value = fake_records result = self.run_command(['dns', 'record-list', '1234']) self.assert_no_fail(result) actual_output = json.loads(result.output)[0] self.assertEqual(actual_output['id'], 137416416) self.assertEqual(actual_output['record'], '') def test_list_zones_no_update(self): pyversion = sys.version_info fake_zones = [ { 'name': 'example.com', 'id': 12345, 'serial': 2014030728, 'updateDate': None} ] domains_mock = self.set_mock('SoftLayer_Account', 'getDomains') domains_mock.return_value = fake_zones result = self.run_command(['dns', 'zone-list']) self.assert_no_fail(result) actual_output = json.loads(result.output) if pyversion.major >= 3 and pyversion.minor >= 7: self.assertEqual(actual_output[0]['updated'], '2014-03-07 00:00') else: self.assertEqual(actual_output[0]['updated'], '2014-03-07T00:00:00-06:00') softlayer-python-5.9.8/tests/CLI/modules/email_tests.py000066400000000000000000000023001415376457700232060ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.email_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Tests for the user cli command """ from SoftLayer import testing class EmailCLITests(testing.TestCase): def test_list(self): result = self.run_command(['email', 'list']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Account', 'getNetworkMessageDeliveryAccounts') def test_detail(self): result = self.run_command(['email', 'detail', '1232123']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Network_Message_Delivery_Email_Sendgrid', 'getObject') def test_edit(self): result = self.run_command(['email', 'edit', '1232123', '--username=test@ibm.com', '--email=test@ibm.com', '--password=test123456789']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Network_Message_Delivery_Email_Sendgrid', 'editObject') self.assert_called_with('SoftLayer_Network_Message_Delivery_Email_Sendgrid', 'updateEmailAddress') softlayer-python-5.9.8/tests/CLI/modules/event_log_tests.py000066400000000000000000000056551415376457700241210ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.event_log_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import json from SoftLayer import testing class EventLogTests(testing.TestCase): def test_get_event_log_with_metadata(self): result = self.run_command(['event-log', 'get', '--metadata']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Event_Log', 'getAllObjects') self.assertIn('Metadata', result.output) def test_get_event_log_without_metadata(self): result = self.run_command(['event-log', 'get', '--no-metadata']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Event_Log', 'getAllObjects') self.assert_called_with('SoftLayer_User_Customer', 'getObject', identifier=400) self.assertNotIn('Metadata', result.output) def test_get_event_log_empty(self): mock = self.set_mock('SoftLayer_Event_Log', 'getAllObjects') mock.return_value = None result = self.run_command(['event-log', 'get']) expected = 'Event, Object, Type, Date, Username\n' \ 'No logs available for filter {}.\n' self.assert_no_fail(result) self.assertEqual(expected, result.output) def test_get_event_log_over_limit(self): result = self.run_command(['event-log', 'get', '-l 1']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Event_Log', 'getAllObjects') self.assertEqual(2, result.output.count("\n")) def test_get_event_log_types(self): expected = [ { "types": {"value": "Account"} }, { "types": {"value": "CDN"} }, { "types": {"value": "User"} }, { "types": {"value": "Bare Metal Instance"} }, { "types": {"value": "API Authentication"} }, { "types": {"value": "Server"} }, { "types": {"value": "CCI"} }, { "types": {"value": "Image"} }, { "types": {"value": "Bluemix LB"} }, { "types": {"value": "Facility"} }, { "types": {"value": "Cloud Object Storage"} }, { "types": {"value": "Security Group"} } ] result = self.run_command(['event-log', 'types']) self.assert_no_fail(result) self.assertEqual(expected, json.loads(result.output)) def test_get_unlimited_events(self): result = self.run_command(['event-log', 'get', '-l -1']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Event_Log', 'getAllObjects') self.assertEqual(8, result.output.count("\n")) softlayer-python-5.9.8/tests/CLI/modules/file_tests.py000066400000000000000000001016171415376457700230510ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.file_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting from SoftLayer import SoftLayerError from SoftLayer import testing import json from unittest import mock as 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', 'notes': None, 'rep_partner_count': None }], json.loads(result.output)) def test_volume_list_order(self): result = self.run_command(['file', 'volume-list', '--order=1234567']) self.assert_no_fail(result) json_result = json.loads(result.output) self.assertEqual(json_result[0]['id'], 1) @mock.patch('SoftLayer.FileStorageManager.list_file_volumes') def test_volume_list_notes_format_output_json(self, list_mock): note_mock = 'test ' * 5 list_mock.return_value = [ {'notes': note_mock} ] result = self.run_command(['--format', 'json', 'file', 'volume-list', '--columns', 'notes']) self.assert_no_fail(result) self.assertEqual( [{ 'notes': note_mock, }], json.loads(result.output)) @mock.patch('SoftLayer.FileStorageManager.list_file_volumes') def test_volume_list_reduced_notes_format_output_table(self, list_mock): note_mock = 'test ' * 10 expected_reduced_note = 'test ' * 4 list_mock.return_value = [ {'notes': note_mock} ] expected_table = formatting.Table(['notes']) expected_table.add_row([expected_reduced_note]) expected_output = formatting.format_output(expected_table) + '\n' result = self.run_command(['--format', 'table', 'file', 'volume-list', '--columns', 'notes']) self.assert_no_fail(result) self.assertEqual(expected_output, 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_cancel_with_billing_item(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_Network_Storage', 'getObject') def test_volume_cancel_without_billing_item(self): p_mock = self.set_mock('SoftLayer_Network_Storage', 'getObject') p_mock.return_value = { "accountId": 1234, "capacityGb": 20, "createDate": "2015-04-29T06:55:55-07:00", "id": 11111, "nasType": "NAS", "username": "SL01SEV307608_1" } result = self.run_command([ '--really', 'file', 'volume-cancel', '1234']) self.assertIsInstance(result.exception, SoftLayerError) 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, 'Notes': "{'status': 'available'}", '# 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_detail_name_identifier(self): result = self.run_command(['file', 'volume-detail', 'SL-12345']) expected_filter = { 'nasNetworkStorage': { 'serviceResource': { 'type': { 'type': {'operation': '!~ NAS'} } }, 'storageType': { 'keyName': {'operation': '*= FILE_STORAGE'} }, 'username': {'operation': '_= SL-12345'}}} self.assert_called_with('SoftLayer_Account', 'getNasNetworkStorage', filter=expected_filter) self.assert_called_with('SoftLayer_Network_Storage', 'getObject', identifier=1) self.assert_no_fail(result) 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_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' '\nYou may run "slcli file volume-list --order 478" to find this file volume after it is ' 'ready.\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' '\nYou may run "slcli file volume-list --order 478" to find this file volume after it is ' 'ready.\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' '\nYou may run "slcli file volume-list --order 479" to find this file volume after it is ' 'ready.\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']) 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) @mock.patch('SoftLayer.CLI.formatting.confirm') @mock.patch('SoftLayer.FileStorageManager.disaster_recovery_failover_to_replicant') def test_disaster_recovery_failover(self, disaster_recovery_failover_mock, confirm_mock): confirm_mock.return_value = True disaster_recovery_failover_mock.return_value = True result = self.run_command(['file', 'disaster-recovery-failover', '12345678', '--replicant-id=5678']) self.assert_no_fail(result) self.assertIn('Disaster Recovery Failover to replicant is now in progress.\n', result.output) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_disaster_recovery_failover_aborted(self, confirm_mock): confirm_mock.return_value = False result = self.run_command(['file', 'disaster-recovery-failover', '12345678', '--replicant-id=5678']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.CLIAbort) def test_replicant_failback(self): result = self.run_command(['file', 'replica-failback', '12345678']) 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']) 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, dependent_duplicate=False) 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) @mock.patch('SoftLayer.FileStorageManager.list_file_volume_limit') def test_volume_limit(self, list_mock): list_mock.return_value = [ { 'datacenterName': 'global', 'maximumAvailableCount': 300, 'provisionedCount': 100 }] result = self.run_command(['file', 'volume-limits']) self.assert_no_fail(result) def test_dupe_refresh(self): result = self.run_command(['file', 'volume-refresh', '102', '103']) self.assert_no_fail(result) def test_dep_dupe_convert(self): result = self.run_command(['file', 'volume-convert', '102']) self.assert_no_fail(result) @mock.patch('SoftLayer.FileStorageManager.volume_set_note') def test_volume_set_note(self, set_note): set_note.return_value = True result = self.run_command(['file', 'volume-set-note', '102', '--note=testing']) self.assert_no_fail(result) self.assertIn("successfully!", result.output) @mock.patch('SoftLayer.FileStorageManager.volume_set_note') def test_volume_not_set_note(self, set_note): set_note.return_value = False result = self.run_command(['file', 'volume-set-note', '102', '--note=testing']) self.assert_no_fail(result) self.assertIn("Note could not be set!", result.output) @mock.patch('SoftLayer.FileStorageManager.get_volume_snapshot_notification_status') def test_snapshot_get_notification_status(self, status): status.side_effect = [None, 1, 0] expected = ['Enabled', 'Enabled', 'Disabled'] for expect in expected: result = self.run_command(['file', 'snapshot-get-notification-status', '999']) self.assert_no_fail(result) self.assertIn(expect, result.output) softlayer-python-5.9.8/tests/CLI/modules/firewall_tests.py000066400000000000000000000110711415376457700237310ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.firewall_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import json from unittest import mock as mock 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), [{'features': ['HA'], 'firewall id': 'vlan:1234', 'server/vlan id': 1, 'type': 'VLAN - dedicated'}, {'features': ['HA'], 'firewall id': 'vlan:23456', 'server/vlan id': 3, 'type': 'VLAN - dedicated'}, {'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'}]) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_add_vs(self, confirm_mock): confirm_mock.return_value = True result = self.run_command(['firewall', 'add', '1000', '--firewall-type=vlan', '-ha']) self.assert_no_fail(result) self.assertIn("Firewall is being created!", result.output) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_add_vlan(self, confirm_mock): confirm_mock.return_value = True result = self.run_command(['firewall', 'add', '1000', '--firewall-type=vs']) self.assert_no_fail(result) self.assertIn("Firewall is being created!", result.output) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_add_server(self, confirm_mock): confirm_mock.return_value = True result = self.run_command(['firewall', 'add', '1000', '--firewall-type=server']) self.assert_no_fail(result) self.assertIn("Firewall is being created!", result.output) def test_detail(self): result = self.run_command(['firewall', 'detail', 'vlan:1234']) self.assert_no_fail(result) json_result = json.loads(result.output) self.assertEqual(json_result['rules'][0]['action'], 'permit') self.assertEqual(json.loads(result.output), {'datacenter': 'Amsterdam 1', 'id': 3130, 'networkVlan': 'testvlan', 'networkVlaniD': 371028, 'primaryIpAddress': '192.155.239.146', 'rules': [{'#': 1, 'action': 'permit', 'dest': 'any on server:80-80', 'dest_mask': '255.255.255.255', 'protocol': 'tcp', 'src_ip': '0.0.0.0', 'src_mask': '0.0.0.0'}, {'#': 2, 'action': 'permit', 'dest': 'any on server:1-65535', 'dest_mask': '255.255.255.255', 'protocol': 'tmp', 'src_ip': '193.212.1.10', 'src_mask': '255.255.255.255'}, {'#': 3, 'action': 'permit', 'dest': 'any on server:80-800', 'dest_mask': '255.255.255.255', 'protocol': 'tcp', 'src_ip': '0.0.0.0', 'src_mask': '0.0.0.0'}]}) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_cancel_firewall(self, confirm_mock): confirm_mock.return_value = True result = self.run_command(['firewall', 'cancel', 'vlan:1234']) self.assert_no_fail(result) self.assertIn("Firewall with id vlan:1234 is being cancelled!", result.output) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_edit(self, confirm_mock): confirm_mock.return_value = True result = self.run_command(['firewall', 'edit', 'vlan:1234']) print(result.output) softlayer-python-5.9.8/tests/CLI/modules/globalip_tests.py000066400000000000000000000057671415376457700237340ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.globalip_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ from unittest import mock as 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)'}]) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create(self, confirm_mock): confirm_mock.return_value = True result = self.run_command(['globalip', 'create', '-v6']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), [{ "item": "this is a thing", "cost": "2.00" }, { "item": "Total monthly cost", "cost": "2.00" }]) def test_ip_unassign(self): result = self.run_command(['globalip', 'unassign', '1']) self.assert_no_fail(result) self.assertEqual(result.output, "") softlayer-python-5.9.8/tests/CLI/modules/image_tests.py000066400000000000000000000033601415376457700232100ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.image_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ from SoftLayer import testing class ImageTests(testing.TestCase): def test_detail(self): result = self.run_command(['image', 'detail', '100']) self.assert_no_fail(result) def test_delete(self): result = self.run_command(['image', 'delete', '100']) self.assert_no_fail(result) def test_edit_note(self): result = self.run_command(['image', 'edit', '100', '--note=test']) self.assert_no_fail(result) def test_edit_name(self): result = self.run_command(['image', 'edit', '100', '--name=test']) self.assert_no_fail(result) def test_edit_tag(self): result = self.run_command(['image', 'edit', '100', '--tag=test']) self.assert_no_fail(result) def test_import(self): result = self.run_command(['image', 'import', '100', 'swift://test']) self.assert_no_fail(result) def test_export(self): result = self.run_command(['image', 'export', '100', 'swift://test']) self.assert_no_fail(result) def test_list(self): result = self.run_command(['image', 'list']) self.assert_no_fail(result) def test_datacenter_add(self): result = self.run_command(['image', 'datacenter', '100', '--add', 'ams01']) self.assert_no_fail(result) def test_datacenter_remove(self): result = self.run_command(['image', 'datacenter', '100', '--remove', 'ams01']) self.assert_no_fail(result) def test_datacenter_remove_fails(self): result = self.run_command(['image', 'datacenter', '100', '--remove']) self.assertEqual(2, result.exit_code) softlayer-python-5.9.8/tests/CLI/modules/ipsec_tests.py000066400000000000000000000616721415376457700232430ustar00rootroot00000000000000""" 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.9.8/tests/CLI/modules/licenses_test.py000066400000000000000000000023631415376457700235520ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.licenses_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ from SoftLayer.fixtures import SoftLayer_Product_Order from SoftLayer.fixtures import SoftLayer_Product_Package from SoftLayer import testing class LicensesTests(testing.TestCase): def test_create(self): _mock = self.set_mock('SoftLayer_Product_Package', 'getItems') _mock.return_value = SoftLayer_Product_Package.getItems_vmware order_mock = self.set_mock('SoftLayer_Product_Order', 'placeOrder') order_mock.return_value = SoftLayer_Product_Order.wmware_placeOrder result = self.run_command(['licenses', 'create', '-k', 'VMWARE_VSAN_ENTERPRISE_TIER_III_65_124_TB_6_X_2', '-d dal03']) self.assert_no_fail(result) def test_cancel(self): result = self.run_command(['licenses', 'cancel', 'ABCDE-6CJ8L-J8R9H-000R0-CDR70', '--immediate']) result = self.run_command(['licenses', 'create-options']) self.assert_no_fail(result) softlayer-python-5.9.8/tests/CLI/modules/loadbal_tests.py000066400000000000000000000351341415376457700235300ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.loadbal ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ from unittest import mock as mock import SoftLayer from SoftLayer.CLI.exceptions import ArgumentError from SoftLayer import exceptions from SoftLayer.fixtures import SoftLayer_Network_LBaaS_LoadBalancer from SoftLayer.fixtures import SoftLayer_Product_Package from SoftLayer import testing class LoadBalancerTests(testing.TestCase): def test_cli_list(self): result = self.run_command(['loadbal', 'list']) self.assert_no_fail(result) def test_cli_list_failed(self): mock_item = self.set_mock('SoftLayer_Network_LBaaS_LoadBalancer', 'getAllObjects') mock_item.return_value = None result = self.run_command(['loadbal', 'list']) self.assert_no_fail(result) def test_pool(self): result = self.run_command(['loadbal', 'pool-add', '1111111', '-f 1000', '-b 220', '-c 100']) self.assert_no_fail(result) def test_pool_sticky(self): result = self.run_command(['loadbal', 'pool-add', '1111111', '-f 1000', '-b 220', '-s']) self.assert_no_fail(result) def test_pool_1(self): result = self.run_command(['loadbal', 'pool-add', '1111111', '-f 1000', '-b 220']) self.assert_no_fail(result) def test_pool_uuid(self): result = self.run_command(['loadbal', 'pool-add', '13d08cd1-5533-47b4-b71c-4b6b9dc10000', '-f 910', '-b 210', '-c 100']) self.assert_no_fail(result) def test_delete_pool(self): result = self.run_command(['loadbal', 'pool-del', '111111', 'b3a3fdf7-8c16-4e87-aa73-decff510000']) self.assert_no_fail(result) def test_edit_pool(self): result = self.run_command(['loadbal', 'pool-edit', '111111', '370a9f12-b3be-47b3-bfa5-8e460010000', '-f 510', '-b 256', '-c 5', '-t 10']) self.assert_no_fail(result) def test_add_7p(self): result = self.run_command(['loadbal', 'l7pool-add', '0a2da082-4474-4e16-9f02-4de959210000', '-n test', '-S 10.175.106.180:265:20', '-s']) self.assert_no_fail(result) def test_add_7p_server(self): result = self.run_command(['loadbal', 'l7pool-add', '111111', '-S 10.175.106.180:265:20', '-n test', '-s']) self.assert_no_fail(result) def test_del_7p(self): result = self.run_command(['loadbal', 'l7pool-del', '123456']) self.assert_no_fail(result) def test_add_7p_server_fail(self): result = self.run_command(['loadbal', 'l7pool-add', '123456', '-S 10.175.106.180:265:20:20', '-n test', '-s']) self.assertIsInstance(result.exception, ArgumentError) @mock.patch('SoftLayer.LoadBalancerManager.add_lb_listener') def test_pool_fail(self, add_lb_pool): add_lb_pool.side_effect = SoftLayer.exceptions.SoftLayerAPIError(mock.ANY, mock) result = self.run_command(['loadbal', 'pool-add', '123456', '-f 1000', '-b 220', '-c 100']) self.assert_no_fail(result) @mock.patch('SoftLayer.LoadBalancerManager.add_lb_listener') def test_pool_sticky_fail(self, add_lb_pool): add_lb_pool.side_effect = SoftLayer.exceptions.SoftLayerAPIError(mock.ANY, mock) result = self.run_command(['loadbal', 'pool-add', '123456', '-f 1000', '-b 220', '-s']) self.assert_no_fail(result) @mock.patch('SoftLayer.LoadBalancerManager.add_lb_listener') def test_pool_1_fail(self, add_lb_pool): add_lb_pool.side_effect = SoftLayer.exceptions.SoftLayerAPIError(mock.ANY, mock) result = self.run_command(['loadbal', 'pool-add', '123456', '-f 1000', '-b 220']) self.assert_no_fail(result) @mock.patch('SoftLayer.LoadBalancerManager.add_lb_listener') def test_pool_uuid_fail(self, add_lb_pool): add_lb_pool.side_effect = SoftLayer.exceptions.SoftLayerAPIError(mock.ANY, mock) result = self.run_command( ['loadbal', 'pool-add', '13d08cd1-5533-47b4-b71c-4b6b9dc10000', '-f 910', '-b 210', '-c 100']) self.assert_no_fail(result) @mock.patch('SoftLayer.LoadBalancerManager.remove_lb_listener') def test_delete_pool_fail(self, del_lb_pool): del_lb_pool.side_effect = SoftLayer.exceptions.SoftLayerAPIError(mock.ANY, mock) result = self.run_command(['loadbal', 'pool-del', '123456', 'b3a3fdf7-8c16-4e87-aa73-decff510000']) self.assert_no_fail(result) @mock.patch('SoftLayer.LoadBalancerManager.add_lb_listener') def test_edit_pool_fail(self, edit_lb_pool): edit_lb_pool.side_effect = SoftLayer.exceptions.SoftLayerAPIError(mock.ANY, mock) result = self.run_command(['loadbal', 'pool-edit', '815248', '370a9f12-b3be-47b3-bfa5-8e10000c013f', '-f 510', '-b 256', '-c 5']) self.assert_no_fail(result) @mock.patch('SoftLayer.LoadBalancerManager.add_lb_l7_pool') def test_add_7p_fail(self, add_lb_17_pool): add_lb_17_pool.side_effect = SoftLayer.exceptions.SoftLayerAPIError(mock.ANY, mock) result = self.run_command(['loadbal', 'l7pool-add', '0a2da082-4474-4e16-9f02-4de10009b85', '-n test', '-S 10.175.106.180:265:20', '-s']) self.assert_no_fail(result) @mock.patch('SoftLayer.LoadBalancerManager.del_lb_l7_pool') def test_del_7p_fail(self, del_lb_17_pool): del_lb_17_pool.side_effect = SoftLayer.exceptions.SoftLayerAPIError(mock.ANY, mock) result = self.run_command(['loadbal', 'l7pool-del', '123456']) self.assert_no_fail(result) @mock.patch('SoftLayer.CLI.loadbal.members.click') def test_lb_member_add_private(self, click): lbaas_id = '1111111' member_ip_address = '10.0.0.1' result = self.run_command(['loadbal', 'member-add', '--private', '-m', member_ip_address, lbaas_id]) output = 'Member {} added'.format(member_ip_address) self.assert_no_fail(result) click.secho.assert_called_with(output, fg='green') @mock.patch('SoftLayer.CLI.loadbal.members.click') def test_lb_member_add_public(self, click): lbaas_id = '1111111' member_ip_address = '10.0.0.1' result = self.run_command(['loadbal', 'member-add', '--public', '-m', member_ip_address, lbaas_id]) output = 'Member {} added'.format(member_ip_address) self.assert_no_fail(result) click.secho.assert_called_with(output, fg='green') @mock.patch('SoftLayer.LoadBalancerManager.add_lb_member') def test_lb_member_add_public_fails(self, add_lb_member): lbaas_id = '1111111' member_ip_address = '10.0.0.1' fault_string = 'publicIpAddress must be a string' add_lb_member.side_effect = exceptions.SoftLayerAPIError(mock.ANY, fault_string) result = self.run_command(['loadbal', 'member-add', '--public', '-m', member_ip_address, lbaas_id]) self.assertIn('This LB requires a Public IP address for its members and none was supplied', result.output) self.assertIn("ERROR: {}".format(fault_string), result.output) @mock.patch('SoftLayer.LoadBalancerManager.add_lb_member') def test_lb_member_add_private_fails(self, add_lb_member): lbaas_id = '1111111' member_ip_address = '10.0.0.1' fault_string = 'privateIpAddress must be a string' add_lb_member.side_effect = exceptions.SoftLayerAPIError(mock.ANY, fault_string) result = self.run_command(['loadbal', 'member-add', '--private', '-m', member_ip_address, lbaas_id]) self.assertIn('This LB requires a Private IP address for its members and none was supplied', result.output) self.assertIn("ERROR: {}".format(fault_string), result.output) @mock.patch('SoftLayer.managers.load_balancer.LoadBalancerManager.delete_lb_member') def test_lb_member_del_fails(self, delete): lbaas_id = '1111111' lbaas_member_uuid = "x123x123-123x-123x-123x-123a123b123c" delete.side_effect = exceptions.SoftLayerAPIError(mock.ANY, mock.ANY) result = self.run_command(['loadbal', 'member-del', '-m', lbaas_member_uuid, lbaas_id]) self.assertIn("ERROR:", result.output) @mock.patch('SoftLayer.CLI.loadbal.members.click') def test_lb_member_del(self, click): lbaas_id = '1111111' lbaas_member_uuid = "x123x123-123x-123x-123x-123a123b123c" result = self.run_command(['loadbal', 'member-del', '-m', lbaas_member_uuid, lbaas_id]) output = 'Member {} removed'.format(lbaas_member_uuid) self.assert_no_fail(result) click.secho.assert_called_with(output, fg='green') def test_lb_l7policies_list(self): command = 'loadbal l7policies' result = self.run_command(command.split(' ')) self.assert_no_fail(result) def test_lb_l7policies_protocol_list(self): command = 'loadbal l7policies -p 123456' result = self.run_command(command.split(' ')) self.assert_no_fail(result) @mock.patch('SoftLayer.CLI.loadbal.health.click') def test_lb_health_manage(self, click): lb_id = '1111111' lb_check_uuid = '222222ab-bbcc-4f32-9b31-1b6d3a1959c8' result = self.run_command(['loadbal', 'health', lb_id, '--uuid', lb_check_uuid, '-i', '60', '-r', '10', '-t', '10', '-u', '/']) self.assert_no_fail(result) output = 'Health Check {} updated successfully'.format(lb_check_uuid) click.secho.assert_called_with(output, fg='green') def test_lb_health_manage_args_time_fails(self): result = self.run_command(['lb', 'health', '1111111', '--uuid', '222222ab-bbcc-4f32-9b31-1b6d3a1959c8']) self.assertIsInstance(result.exception, ArgumentError) @mock.patch('SoftLayer.LoadBalancerManager.get_lb') def test_lb_health_update_tcp_url_fails(self, get_lb): get_lb.return_value = SoftLayer_Network_LBaaS_LoadBalancer.getObject get_lb.return_value['listeners'][0]['defaultPool']['protocol'] = 'TCP' result = self.run_command(['lb', 'health', '1111111', '--uuid', '222222ab-bbcc-4f32-9b31-1b6d3a1959c8', '-i', '60', '-r', '10', '-t', '10', '-u', '/']) self.assertIsInstance(result.exception, ArgumentError) @mock.patch('SoftLayer.LoadBalancerManager.update_lb_health_monitors') def test_lb_health_update_fails(self, update_lb_health_monitors): update_lb_health_monitors.side_effect = exceptions.SoftLayerAPIError(mock.ANY, mock.ANY) result = self.run_command(['lb', 'health', '1111111', '--uuid', '222222ab-bbcc-4f32-9b31-1b6d3a1959c8', '-i', '60', '-r', '10', '-t', '10', '-u', '/']) self.assertIn("ERROR:", result.output) def test_lb_detail(self): result = self.run_command(['lb', 'detail', '1111111']) self.assert_no_fail(result) self.assertIn('Id', result.output) self.assertIn('UUI', result.output) self.assertIn('Address', result.output) def test_lb_detail_by_name(self): name = SoftLayer_Network_LBaaS_LoadBalancer.getObject.get('name') result = self.run_command(['lb', 'detail', name]) self.assert_no_fail(result) def test_lb_detail_uuid(self): uuid = SoftLayer_Network_LBaaS_LoadBalancer.getObject.get('uuid') result = self.run_command(['lb', 'detail', uuid]) self.assert_no_fail(result) def test_order(self): result = self.run_command(['loadbal', 'order', '--name', 'test', '--datacenter', 'par01', '--label', 'labeltest', '--subnet', '759282']) self.assert_no_fail(result) def test_order_with_frontend(self): result = self.run_command(['loadbal', 'order', '--name', 'test', '--datacenter', 'par01', '--label', 'labeltest', '--frontend', 'TCP:80', '--backend', 'TCP:80', '--subnet', '759282']) self.assert_no_fail(result) def test_order_with_backend(self): result = self.run_command(['loadbal', 'order', '--name', 'test', '--datacenter', 'par01', '--label', 'labeltest', '--backend', 'HTTP:80', '--subnet', '759282']) self.assert_no_fail(result) def test_order_backend_fail(self): result = self.run_command(['loadbal', 'order', '--name', 'test', '--datacenter', 'par01', '--label', 'labeltest', '--backend', 'HTTP', '--subnet', '759282']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, ArgumentError) def test_verify_order(self): result = self.run_command(['loadbal', 'order', '--verify', '--name', 'test', '--datacenter', 'par01', '--label', 'labeltest', '--subnet', '759282']) self.assert_no_fail(result) def test_order_options(self): fault_string = 'Use `slcli lb order-options --datacenter `' \ ' to find pricing information and private subnets for that specific site.' result = self.run_command(['loadbal', 'order-options']) self.assertIn(fault_string, result.output) def test_order_options_with_datacenter(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = SoftLayer_Product_Package.getAllObjectsLoadbal result = self.run_command(['loadbal', 'order-options', '--datacenter', 'ams03']) self.assert_no_fail(result) def test_cancel(self): result = self.run_command(['loadbal', 'cancel', '11111']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Network_LBaaS_LoadBalancer', 'cancelLoadBalancer') @mock.patch('SoftLayer.LoadBalancerManager.cancel_lbaas') def test_cancel_fail(self, cancel_lbaas): fault_string = 'Id must be string' cancel_lbaas.side_effect = exceptions.SoftLayerAPIError(mock.ANY, fault_string) result = self.run_command(['loadbal', 'cancel', '11111']) self.assertIn("ERROR: {}".format(fault_string), result.output) def test_ns_list(self): result = self.run_command(['loadbal', 'ns-list']) self.assert_no_fail(result) def test_ns_list_empty(self): mock = self.set_mock('SoftLayer_Account', 'getApplicationDeliveryControllers') mock.return_value = [] result = self.run_command(['loadbal', 'ns-list']) self.assert_no_fail(result) def test_ns_detail(self): result = self.run_command(['loadbal', 'ns-detail', '11111']) self.assert_no_fail(result) softlayer-python-5.9.8/tests/CLI/modules/nas_tests.py000066400000000000000000000027061415376457700227120ustar00rootroot00000000000000""" 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}]) def test_nas_credentials(self): result = self.run_command(['nas', 'credentials', '12345']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), [{ 'password': '', 'username': 'username' }]) def test_server_credentials_exception_password_not_found(self): mock = self.set_mock('SoftLayer_Network_Storage', 'getObject') mock.return_value = { "accountId": 11111, "capacityGb": 20, "id": 22222, "nasType": "NAS", "serviceProviderId": 1, "username": "SL01SEV307", "credentials": [] } result = self.run_command(['nas', 'credentials', '12345']) self.assertEqual( 'None', str(result.exception) ) softlayer-python-5.9.8/tests/CLI/modules/object_storage_tests.py000066400000000000000000000063031415376457700251200ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.object_storage_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import json from unittest import mock as mock 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), [{'apiType': 'S3', 'id': 12345, 'name': 'SLOS12345-1'}, {'apiType': 'Swift', '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/'}]) def test_create_credential(self): result = self.run_command(['object-storage', 'credential', 'create', '100']) self.assert_no_fail(result) @mock.patch('SoftLayer.CLI.helpers.resolve_id') def test_create_credential_by_username(self, resolve_id_mock): resolve_id_mock.return_value = 100 result = self.run_command(['object-storage', 'credential', 'create', 'test']) self.assert_no_fail(result) def test_delete_credential(self): result = self.run_command(['object-storage', 'credential', 'delete', '-c', 100, '100']) self.assert_no_fail(result) self.assertEqual(result.output, 'True\n') @mock.patch('SoftLayer.CLI.helpers.resolve_id') def test_delete_credential_by_username(self, resolve_id_mock): resolve_id_mock.return_value = 100 result = self.run_command(['object-storage', 'credential', 'delete', 'test']) self.assert_no_fail(result) def test_limit_credential(self): result = self.run_command(['object-storage', 'credential', 'limit', '100']) self.assert_no_fail(result) self.assertIn('limit', result.output) @mock.patch('SoftLayer.CLI.helpers.resolve_id') def test_limit_credential_by_username(self, resolve_id_mock): resolve_id_mock.return_value = 100 result = self.run_command(['object-storage', 'credential', 'limit', 'test']) self.assert_no_fail(result) def test_list_credential(self): result = self.run_command(['object-storage', 'credential', 'list', '100']) self.assert_no_fail(result) @mock.patch('SoftLayer.CLI.helpers.resolve_id') def test_list_credential_by_username(self, resolve_id_mock): resolve_id_mock.return_value = 100 result = self.run_command(['object-storage', 'credential', 'list', 'test']) self.assert_no_fail(result) softlayer-python-5.9.8/tests/CLI/modules/order_tests.py000066400000000000000000000540071415376457700232450ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.order_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import json import sys import tempfile from SoftLayer.CLI import exceptions 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') self.assertIn('description2', result.output) self.assertIn('testing', result.output) self.assertIn('item2', result.output) def test_item_list_prices(self): result = self.run_command(['order', 'item-list', 'package', '--prices']) self.assert_no_fail(result) output = json.loads(result.output) self.assertEqual(output[0][0]['priceId'], 1007) self.assertEqual(output[0][1]['Restriction'], '- - - -') self.assertEqual(output[0][1]['keyName'], 'KeyName015') self.assert_called_with('SoftLayer_Product_Package', 'getItems') def test_item_list_location_keyname(self): result = self.run_command(['order', 'item-list', 'package', '--prices', 'DALLAS13', ]) self.assert_no_fail(result) output = json.loads(result.output) self.assertEqual(output[0][0]['Hourly'], 0.0) self.assertEqual(output[0][1]['keyName'], 'KeyName015') self.assertEqual(output[0][1]['priceId'], 1144) self.assert_called_with('SoftLayer_Product_Package', 'getItemPrices') def test_item_list_location_name(self): result = self.run_command(['order', 'item-list', 'package', '--prices', 'dal13', ]) self.assert_no_fail(result) output = json.loads(result.output) self.assertEqual(output[0][0]['Hourly'], 0.0) self.assertEqual(output[0][1]['keyName'], 'KeyName015') self.assertEqual(output[0][1]['priceId'], 1144) self.assert_called_with('SoftLayer_Product_Package', 'getItemPrices') def test_item_list_category_keyword(self): result = self.run_command(['order', 'item-list', 'package', '--prices', 'dal13', '-c', 'os', '-k' 'test']) self.assert_no_fail(result) output = json.loads(result.output) self.assertEqual(output[0][0]['Hourly'], 0.0) self.assertEqual(output[0][1]['keyName'], 'KeyName015') self.assertEqual(output[0][1]['priceId'], 1144) self.assert_called_with('SoftLayer_Product_Package', 'getItemPrices') def test_package_list(self): p_mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') p_mock.return_value = _get_all_packages() _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 = [{'id': 1, 'name': 'package1', 'keyName': 'PACKAGE1', 'type': 'BARE_METAL_CPU'}, {'id': 2, 'name': 'package2', 'keyName': 'PACKAGE2', 'type': 'BARE_METAL_CPU'}] self.assertEqual(expected_results, json.loads(result.output)) def test_package_list_keyword(self): p_mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') p_mock.return_value = _get_all_packages() _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 = [{'id': 1, 'name': 'package1', 'keyName': 'PACKAGE1', 'type': 'BARE_METAL_CPU'}, {'id': 2, 'name': 'package2', 'keyName': 'PACKAGE2', 'type': 'BARE_METAL_CPU'}] self.assertEqual(expected_results, json.loads(result.output)) def test_package_list_type(self): p_mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') p_mock.return_value = _get_all_packages() _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 = [{'id': 1, 'name': 'package1', 'keyName': 'PACKAGE1', 'type': 'BARE_METAL_CPU'}, {'id': 2, '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_place_with_quantity(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', '--quantity=2', '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_place_extras_parameter_fail(self): result = self.run_command(['-y', 'order', 'place', 'package', 'DALLAS13', 'ITEM1', '--extras', '{"device":[']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.CLIAbort) def test_place_quote(self): order_date = '2018-04-04 07:39:20' expiration_date = '2018-05-04 07:39:20' quote_name = 'foobar' order = {'orderDate': order_date, 'quote': { 'id': 1234, 'name': quote_name, 'expirationDate': expiration_date, 'status': 'PENDING' }} place_quote_mock = self.set_mock('SoftLayer_Product_Order', 'placeQuote') items_mock = self.set_mock('SoftLayer_Product_Package', 'getItems') place_quote_mock.return_value = order items_mock.return_value = self._get_order_items() result = self.run_command(['order', 'place-quote', '--name', 'foobar', 'package', 'DALLAS13', 'ITEM1', '--complex-type', 'SoftLayer_Container_Product_Order_Thing']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Product_Order', 'placeQuote') self.assertEqual({'id': 1234, 'name': quote_name, 'created': order_date, 'expires': expiration_date, 'status': 'PENDING'}, json.loads(result.output)) def test_place_quote_extras_parameter_fail(self): result = self.run_command(['-y', 'order', 'place-quote', 'package', 'DALLAS13', 'ITEM1', '--extras', '{"device":[']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.CLIAbort) 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['orderContainers'][0]['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['orderContainers'][0]['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_preset_list_keywork(self): result = self.run_command(['order', 'preset-list', 'package', '--keyword', 'testKeyWord']) _filter = {'activePresets': {'name': {'operation': '*= testKeyWord'}}} self.assert_no_fail(result) self.assert_called_with('SoftLayer_Product_Package', 'getActivePresets', filter=_filter) 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 test_quote_verify(self): result = self.run_command([ 'order', 'quote', '12345', '--verify', '--fqdn', 'test01.test.com', '--complex-type', 'SoftLayer_Container_Product_Order_Virtual_Guest']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Billing_Order_Quote', 'verifyOrder', identifier='12345') def test_quote_verify_image(self): result = self.run_command([ 'order', 'quote', '12345', '--verify', '--fqdn', 'test01.test.com', '--image', '1234', '--complex-type', 'SoftLayer_Container_Product_Order_Virtual_Guest']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Virtual_Guest_Block_Device_Template_Group', 'getObject', identifier='1234') self.assert_called_with('SoftLayer_Billing_Order_Quote', 'verifyOrder', identifier='12345') verify_call = self.calls('SoftLayer_Billing_Order_Quote', 'verifyOrder') verify_args = getattr(verify_call[0], 'args')[0] self.assertEqual('0B5DEAF4-643D-46CA-A695-CECBE8832C9D', verify_args['imageTemplateGlobalIdentifier']) def test_quote_verify_image_guid(self): result = self.run_command([ 'order', 'quote', '12345', '--verify', '--fqdn', 'test01.test.com', '--image', '0B5DEAF4-643D-46CA-A695-CECBE8832C9D', '--complex-type', 'SoftLayer_Container_Product_Order_Virtual_Guest']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Billing_Order_Quote', 'verifyOrder', identifier='12345') verify_call = self.calls('SoftLayer_Billing_Order_Quote', 'verifyOrder') verify_args = getattr(verify_call[0], 'args')[0] self.assertEqual('0B5DEAF4-643D-46CA-A695-CECBE8832C9D', verify_args['imageTemplateGlobalIdentifier']) def test_quote_verify_userdata(self): result = self.run_command([ 'order', 'quote', '12345', '--verify', '--fqdn', 'test01.test.com', '--userdata', 'aaaa1234', '--complex-type', 'SoftLayer_Container_Product_Order_Virtual_Guest']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Billing_Order_Quote', 'verifyOrder', identifier='12345') verify_call = self.calls('SoftLayer_Billing_Order_Quote', 'verifyOrder') verify_args = getattr(verify_call[0], 'args')[0] self.assertEqual([{'value': 'aaaa1234'}], verify_args['hardware'][0]['userData']) def test_quote_verify_userdata_file(self): if (sys.platform.startswith("win")): self.skipTest("TempFile tests doesn't work in Windows") with tempfile.NamedTemporaryFile() as userfile: userfile.write(b"some data") userfile.flush() result = self.run_command([ 'order', 'quote', '12345', '--verify', '--fqdn', 'test01.test.com', '--userfile', userfile.name, '--complex-type', 'SoftLayer_Container_Product_Order_Virtual_Guest']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Billing_Order_Quote', 'verifyOrder', identifier='12345') verify_call = self.calls('SoftLayer_Billing_Order_Quote', 'verifyOrder') verify_args = getattr(verify_call[0], 'args')[0] self.assertEqual([{'value': 'some data'}], verify_args['hardware'][0]['userData']) def test_quote_verify_sshkey(self): result = self.run_command([ 'order', 'quote', '12345', '--verify', '--fqdn', 'test01.test.com', '--key', 'Test 1', '--complex-type', 'SoftLayer_Container_Product_Order_Virtual_Guest']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Account', 'getSshKeys') self.assert_called_with('SoftLayer_Billing_Order_Quote', 'verifyOrder', identifier='12345') verify_call = self.calls('SoftLayer_Billing_Order_Quote', 'verifyOrder') verify_args = getattr(verify_call[0], 'args')[0] self.assertEqual(['100'], verify_args['sshKeys']) def test_quote_verify_postinstall_others(self): result = self.run_command([ 'order', 'quote', '12345', '--verify', '--fqdn', 'test01.test.com', '--quantity', '2', '--postinstall', 'https://127.0.0.1/test.sh', '--complex-type', 'SoftLayer_Container_Product_Order_Virtual_Guest']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Billing_Order_Quote', 'verifyOrder', identifier='12345') verify_call = self.calls('SoftLayer_Billing_Order_Quote', 'verifyOrder') verify_args = getattr(verify_call[0], 'args')[0] self.assertEqual(['https://127.0.0.1/test.sh'], verify_args['provisionScripts']) self.assertEqual(2, verify_args['quantity']) def test_quote_place(self): result = self.run_command([ 'order', 'quote', '12345', '--fqdn', 'test01.test.com', '--complex-type', 'SoftLayer_Container_Product_Order_Virtual_Guest']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Billing_Order_Quote', 'placeOrder', identifier='12345') def test_quote_detail(self): result = self.run_command(['order', 'quote-detail', '12345']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Billing_Order_Quote', 'getObject', identifier='12345') def test_quote_save(self): result = self.run_command(['order', 'quote-save', '12345']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Billing_Order_Quote', 'saveQuote', identifier='12345') def test_quote_list(self): result = self.run_command(['order', 'quote-list']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Account', 'getActiveQuotes') def _get_order_items(self): item1 = {'keyName': 'ITEM1', 'description': 'description1', 'itemCategory': {'categoryCode': 'cat1'}, 'prices': [{'id': 1111, 'locationGroupId': None, 'categories': [{'categoryCode': 'cat1'}]}]} item2 = {'keyName': 'ITEM2', 'description': 'description2', 'itemCategory': {'categoryCode': 'cat2'}, 'prices': [{'id': 2222, 'locationGroupId': None, 'categories': [{'categoryCode': 'cat2'}]}]} 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 {'orderContainers': [{'prices': [price1, price2]}]} def test_order_lookup(self): result = self.run_command(['order', 'lookup', '12345']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Billing_Order', 'getObject', identifier='12345') self.assertIn('Ordered By', result.output) self.assertIn('Initial Invoice', result.output) def _get_all_packages(): package_type = {'keyName': 'BARE_METAL_CPU'} all_packages = [ {'id': 1, 'name': 'package1', 'keyName': 'PACKAGE1', 'type': package_type, 'isActive': 1}, {'id': 2, 'name': 'package2', 'keyName': 'PACKAGE2', 'type': package_type, 'isActive': 1}, {'id': 3, 'name': 'package2', 'keyName': 'PACKAGE2', 'type': package_type, 'isActive': 0} ] return all_packages softlayer-python-5.9.8/tests/CLI/modules/report_tests.py000066400000000000000000000354431415376457700234500ustar00rootroot00000000000000""" 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([ { 'hostname': 'pool1', 'pool': None, 'private_in': 30, 'private_out': 40, 'public_in': 10, 'public_out': 20, 'type': 'pool' }, { 'hostname': 'pool3', 'pool': None, 'private_in': 30, 'private_out': 40, 'public_in': 10, 'public_out': 20, 'type': 'pool' }, { 'hostname': 'host1', 'pool': None, 'private_in': 30, 'private_out': 40, 'public_in': 10, 'public_out': 20, 'type': 'hardware' }, { 'hostname': 'host3', 'pool': None, 'private_in': 30, 'private_out': 40, 'public_in': 10, 'public_out': 20, 'type': 'hardware' }, { 'hostname': 'host1', 'pool': None, 'private_in': 30, 'private_out': 40, 'public_in': 10, 'public_out': 20, 'type': 'virtual' }, { 'hostname': 'host3', 'pool': 2, 'private_in': 30, 'private_out': 40, 'public_in': 10, 'public_out': 20, 'type': 'virtual'}], 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) def test_virtual_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, }] 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', '--virtual', ]) self.assert_no_fail(result) stripped_output = '[' + result.output.split('[', 1)[1] self.assertEqual([ { 'hostname': 'pool1', 'pool': None, 'private_in': 30, 'private_out': 40, 'public_in': 10, 'public_out': 20, 'type': 'pool', }, { 'hostname': 'pool3', 'pool': None, 'private_in': 30, 'private_out': 40, 'public_in': 10, 'public_out': 20, 'type': 'pool', }, { 'hostname': 'host1', 'pool': None, 'private_in': 30, 'private_out': 40, 'public_in': 10, 'public_out': 20, 'type': 'virtual', }, { 'hostname': 'host3', 'pool': 2, 'private_in': 30, 'private_out': 40, 'public_in': 10, 'public_out': 20, 'type': 'virtual', }], json.loads(stripped_output), ) self.assertEqual( 4, 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=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) def test_server_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}, }] 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', '--server', ]) self.assert_no_fail(result) stripped_output = '[' + result.output.split('[', 1)[1] self.assertEqual([ { 'hostname': 'pool1', 'pool': None, 'private_in': 30, 'private_out': 40, 'public_in': 10, 'public_out': 20, 'type': 'pool', }, { 'hostname': 'pool3', 'pool': None, 'private_in': 30, 'private_out': 40, 'public_in': 10, 'public_out': 20, 'type': 'pool', }, { 'hostname': 'host1', 'pool': None, 'private_in': 30, 'private_out': 40, 'public_in': 10, 'public_out': 20, 'type': 'hardware', }, { 'hostname': 'host3', 'pool': None, 'private_in': 30, 'private_out': 40, 'public_in': 10, 'public_out': 20, 'type': 'hardware', }, ], json.loads(stripped_output), ) self.assertEqual( 4, len(self.calls('SoftLayer_Metric_Tracking_Object', 'getSummaryData')), ) self.assert_called_with('SoftLayer_Metric_Tracking_Object', 'getSummaryData', identifier=101) self.assert_called_with('SoftLayer_Metric_Tracking_Object', 'getSummaryData', identifier=103) 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.9.8/tests/CLI/modules/securitygroup_tests.py000066400000000000000000000340051415376457700250520ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.securitygroup_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import json from unittest import mock as mock import SoftLayer from SoftLayer import testing class SecurityGroupTests(testing.TestCase): def set_up(self): self.network = SoftLayer.NetworkManager(self.client) 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, 'createDate': None, 'modifyDate': None}], json.loads(result.output)) def test_securitygroup_rule_add(self): result = self.run_command(['sg', 'rule-add', '100', '--direction=ingress']) json.loads(result.output) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Network_SecurityGroup', 'addRules', identifier='100', args=([{'direction': 'ingress'}],)) self.assertEqual([{"requestId": "addRules", "rules": "[{'direction': 'ingress', " "'portRangeMax': '', " "'portRangeMin': '', " "'ethertype': 'IPv4', " "'securityGroupId': 100, 'remoteGroupId': '', " "'id': 100}]"}], json.loads(result.output)) 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'}],)) self.assertEqual([{'requestId': 'editRules'}], json.loads(result.output)) 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'],)) self.assertEqual([{'requestId': 'removeRules'}], json.loads(result.output)) 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'],)) self.assertEqual([{'requestId': 'interfaceAdd'}], json.loads(result.output)) 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'],)) self.assertEqual([{'requestId': 'interfaceRemove'}], json.loads(result.output)) 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) @mock.patch('SoftLayer.NetworkManager.get_event_logs_by_request_id') def test_securitygroup_get_by_request_id(self, event_mock): event_mock.return_value = [ { 'accountId': 100, 'eventCreateDate': '2017-10-18T09:40:32.238869-05:00', 'eventName': 'Security Group Added', 'ipAddress': '192.168.0.1', 'label': 'test.softlayer.com', 'metaData': '{"securityGroupId":"200",' '"securityGroupName":"test_SG",' '"networkComponentId":"100",' '"networkInterfaceType":"public",' '"requestId":"96c9b47b9e102d2e1d81fba"}', 'objectId': 300, 'objectName': 'CCI', 'traceId': '59e767e03a57e', 'userId': 400, 'userType': 'CUSTOMER', 'username': 'user' }, { 'accountId': 100, 'eventCreateDate': '2017-10-18T10:42:13.089536-05:00', 'eventName': 'Security Group Rule(s) Removed', 'ipAddress': '192.168.0.1', 'label': 'test_SG', 'metaData': '{"requestId":"96c9b47b9e102d2e1d81fba",' '"rules":[{"ruleId":"800",' '"remoteIp":null,"remoteGroupId":null,"direction":"ingress",' '"ethertype":"IPv4",' '"portRangeMin":2000,"portRangeMax":2001,"protocol":"tcp"}]}', 'objectId': 700, 'objectName': 'Security Group', 'traceId': '59e7765515e28', 'userId': 400, 'userType': 'CUSTOMER', 'username': 'user' } ] expected = [ { 'date': '2017-10-18T09:40:32.238869-05:00', 'event': 'Security Group Added', 'label': 'test.softlayer.com', 'metadata': json.dumps(json.loads( '{"networkComponentId": "100",' '"networkInterfaceType": "public",' '"requestId": "96c9b47b9e102d2e1d81fba",' '"securityGroupId": "200",' '"securityGroupName": "test_SG"}' ), indent=4, sort_keys=True ) }, { 'date': '2017-10-18T10:42:13.089536-05:00', 'event': 'Security Group Rule(s) Removed', 'label': 'test_SG', 'metadata': json.dumps(json.loads( '{"requestId": "96c9b47b9e102d2e1d81fba",' '"rules": [{"direction": "ingress",' '"ethertype": "IPv4",' '"portRangeMax": 2001,' '"portRangeMin": 2000,' '"protocol": "tcp",' '"remoteGroupId": null,' '"remoteIp": null,' '"ruleId": "800"}]}' ), indent=4, sort_keys=True ) } ] result = self.run_command(['sg', 'event-log', '96c9b47b9e102d2e1d81fba']) self.assertEqual(expected, json.loads(result.output)) softlayer-python-5.9.8/tests/CLI/modules/server_tests.py000066400000000000000000001216611415376457700234410ustar00rootroot00000000000000""" 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 json import sys import tempfile from unittest import mock as mock from SoftLayer.CLI import exceptions from SoftLayer.fixtures import SoftLayer_Product_Order from SoftLayer import SoftLayerError from SoftLayer import testing 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_credentials(self): mock = self.set_mock('SoftLayer_Hardware_Server', 'getObject') mock.return_value = { "accountId": 11111, "domain": "chechu.com", "fullyQualifiedDomainName": "host3.vmware.chechu.com", "hardwareStatusId": 5, "hostname": "host3.vmware", "id": 12345, "softwareComponents": [{"passwords": [ { "password": "abc123", "username": "root" } ]}] } result = self.run_command(['hardware', 'credentials', '12345']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), [{ 'username': 'root', 'password': 'abc123' }]) def test_server_credentials_exception_passwords_not_found(self): mock = self.set_mock('SoftLayer_Hardware_Server', 'getObject') mock.return_value = { "accountId": 11111, "domain": "chechu.com", "fullyQualifiedDomainName": "host3.vmware.chechu.com", "hardwareStatusId": 5, "hostname": "host3.vmware", "id": 12345, "softwareComponents": [{}] } result = self.run_command(['hardware', 'credentials', '12345']) self.assertEqual( 'No passwords found in softwareComponents', str(result.exception) ) def test_server_credentials_exception_password_not_found(self): mock = self.set_mock('SoftLayer_Hardware_Server', 'getObject') mock.return_value = { "accountId": 11111, "domain": "chechu.com", "fullyQualifiedDomainName": "host3.vmware.chechu.com", "hardwareStatusId": 5, "hostname": "host3.vmware", "id": 12345, "softwareComponents": [ { "hardwareId": 22222, "id": 333333, "passwords": [{}] } ] } result = self.run_command(['hardware', 'credentials', '12345']) self.assertEqual( 'None', str(result.exception) ) def test_server_details(self): result = self.run_command(['server', 'detail', '1234', '--passwords', '--price']) self.assert_no_fail(result) output = json.loads(result.output) self.assertEqual(output['notes'], 'These are test notes.') self.assertEqual(output['prices'][0]['Recurring Price'], 16.08) self.assertEqual(output['remote users'][0]['password'], 'abc123') self.assertEqual(output['users'][0]['username'], 'root') self.assertEqual(output['vlans'][0]['number'], 1800) self.assertEqual(output['owner'], 'chechu') self.assertEqual(output['Bandwidth'][0]['Allotment'], '250') 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_detail_empty_allotment(self): mock = self.set_mock('SoftLayer_Hardware_Server', 'getBandwidthAllotmentDetail') mock.return_value = None result = self.run_command(['server', 'detail', '100']) self.assert_no_fail(result) self.assertEqual( json.loads(result.output)['Bandwidth'][0]['Allotment'], '-', ) def test_detail_drives(self): mock = self.set_mock('SoftLayer_Hardware_Server', 'getHardDrives') mock.return_value = [ { "id": 11111, "serialNumber": "z1w4sdf", "hardwareComponentModel": { "capacity": "1000", "description": "SATAIII:2000:8300:Constellation", "id": 111, "manufacturer": "Seagate", "name": "Constellation ES", "hardwareGenericComponentModel": { "capacity": "1000", "units": "GB", "hardwareComponentType": { "id": 1, "keyName": "HARD_DRIVE", "type": "Hard Drive", "typeParentId": 5 } } } } ] result = self.run_command(['server', 'detail', '100']) self.assert_no_fail(result) output = json.loads(result.output) self.assertEqual(output['drives'][0]['Capacity'], '1000 GB') self.assertEqual(output['drives'][0]['Name'], 'Seagate Constellation ES') self.assertEqual(output['drives'][0]['Serial #'], 'z1w4sdf') 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], False) # LVM switch result = self.run_command(['--really', 'server', 'reload', '12345', '--lvm']) self.assert_no_fail(result) reload_mock.assert_called_with(12345, None, [], True) # 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) output = json.loads(result.output) self.assertEqual(output[0][0]['Value'], 'wdc07') def test_create_options_prices(self): result = self.run_command(['server', 'create-options', '--prices']) self.assert_no_fail(result) output = json.loads(result.output) self.assertEqual(output[2][0]['Monthly'], str(0.1)) self.assertEqual(output[2][0]['Key'], 'OS_UBUNTU_14_04_LTS_TRUSTY_TAHR_64_BIT') def test_create_options_location(self): result = self.run_command(['server', 'create-options', '--prices', 'dal13']) self.assert_no_fail(result) output = json.loads(result.output) self.assertEqual(output[2][0]['Monthly'], str(0.1)) self.assertEqual(output[2][0]['Key'], 'OS_UBUNTU_14_04_LTS_TRUSTY_TAHR_64_BIT') @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=OS_UBUNTU_14_04_LTS_TRUSTY_TAHR_64_BIT', '--no-public', '--key=10', ]) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), {'id': 98765, 'created': '2013-08-02 15:23:47'}) @mock.patch('SoftLayer.CLI.template.export_to_template') def test_create_server_with_export(self, export_mock): 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_once() @mock.patch('SoftLayer.HardwareManager.place_order') def test_create_server_with_router(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=OS_UBUNTU_14_04_LTS_TRUSTY_TAHR_64_BIT', '--router-private=123', '--router-public=1234' ]) self.assert_no_fail(result) 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) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_reflash_firmware(self, confirm_mock): confirm_mock.return_value = True result = self.run_command(['server', 'reflash-firmware', '1000']) self.assert_no_fail(result) self.assertEqual(result.output, "") self.assert_called_with('SoftLayer_Hardware_Server', 'createFirmwareReflashTransaction', args=((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', '--redundant', '--private-speed=100', '--degraded', '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, 'redundant'],), identifier=100, ) self.assert_called_with( 'SoftLayer_Hardware_Server', 'setPrivateNetworkInterfaceSpeed', args=([100, 'degraded'],), 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') def test_toggle_ipmi_on(self): mock.return_value = True result = self.run_command(['server', 'toggle-ipmi', '--enable', '12345']) self.assert_no_fail(result) self.assertEqual(result.output, 'True\n') def test_toggle_ipmi_off(self): mock.return_value = True result = self.run_command(['server', 'toggle-ipmi', '--disable', '12345']) self.assert_no_fail(result) self.assertEqual(result.output, 'True\n') def test_bandwidth_hw(self): if sys.version_info < (3, 6): self.skipTest("Test requires python 3.6+") result = self.run_command(['server', 'bandwidth', '100', '--start_date=2019-01-01', '--end_date=2019-02-01']) self.assert_no_fail(result) date = '2019-05-20 23:00' # number of characters from the end of output to break so json can parse properly pivot = 157 # only pyhon 3.7 supports the timezone format slapi uses if sys.version_info < (3, 7): date = '2019-05-20T23:00:00-06:00' pivot = 166 # Since this is 2 tables, it gets returned as invalid json like "[{}][{}]"" instead of "[[{}],[{}]]" # so we just do some hacky string substitution to pull out the respective arrays that can be jsonifyied output_summary = json.loads(result.output[0:-pivot]) output_list = json.loads(result.output[-pivot:]) self.assertEqual(output_summary[0]['Average MBps'], 0.3841) self.assertEqual(output_summary[1]['Max Date'], date) self.assertEqual(output_summary[2]['Max GB'], 0.1172) self.assertEqual(output_summary[3]['Sum GB'], 0.0009) self.assertEqual(output_list[0]['Date'], date) self.assertEqual(output_list[0]['Pub In'], 1.3503) def test_bandwidth_hw_quite(self): result = self.run_command(['server', 'bandwidth', '100', '--start_date=2019-01-01', '--end_date=2019-02-01', '-q']) self.assert_no_fail(result) date = '2019-05-20 23:00' # only pyhon 3.7 supports the timezone format slapi uses if sys.version_info < (3, 7): date = '2019-05-20T23:00:00-06:00' output_summary = json.loads(result.output) self.assertEqual(output_summary[0]['Average MBps'], 0.3841) self.assertEqual(output_summary[1]['Max Date'], date) self.assertEqual(output_summary[2]['Max GB'], 0.1172) self.assertEqual(output_summary[3]['Sum GB'], 0.0009) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_dns_sync_both(self, confirm_mock): confirm_mock.return_value = True getReverseDomainRecords = self.set_mock('SoftLayer_Hardware_Server', 'getReverseDomainRecords') getReverseDomainRecords.return_value = [{ 'networkAddress': '172.16.1.100', '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': 'hardware-test1', 'domainId': 12345, # from SoftLayer_Account::getDomains 'data': '172.16.1.100', 'ttl': 7200 },) createPTRargs = ({ 'type': 'ptr', 'host': '100', 'domainId': 123456, 'data': 'hardware-test1.test.sftlyr.ws', 'ttl': 7200 },) result = self.run_command(['hw', 'dns-sync', '1000']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Dns_Domain', 'getResourceRecords') self.assert_called_with('SoftLayer_Hardware_Server', '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 = [] server = self.set_mock('SoftLayer_Hardware_Server', 'getObject') test_server = { 'id': 1000, 'hostname': 'hardware-test1', 'domain': 'sftlyr.ws', 'primaryIpAddress': '172.16.1.100', 'fullyQualifiedDomainName': 'hw-test1.sftlyr.ws', "primaryNetworkComponent": {} } server.return_value = test_server result = self.run_command(['hw', 'dns-sync', '--aaaa-record', '1000']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.CLIAbort) test_server['primaryNetworkComponent'] = { 'primaryVersion6IpAddressRecord': { 'ipAddress': '2607:f0d0:1b01:0023:0000:0000:0000:0004' } } createV6args = ({ 'type': 'aaaa', 'host': 'hardware-test1', 'domainId': 12345, # from SoftLayer_Account::getDomains 'data': '2607:f0d0:1b01:0023:0000:0000:0000:0004', 'ttl': 7200 },) server.return_value = test_server result = self.run_command(['hw', 'dns-sync', '--aaaa-record', '1000']) 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': 'hardware-test1', 'type': 'aaaa' } getResourceRecords = self.set_mock('SoftLayer_Dns_Domain', 'getResourceRecords') getResourceRecords.return_value = [v6Record] editArgs = (v6Record,) result = self.run_command(['hw', 'dns-sync', '--aaaa-record', '1000']) 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(['hw', 'dns-sync', '--aaaa-record', '1000']) self.assertEqual(result.exit_code, 1) self.assertIsInstance(result.exception, SoftLayerError) @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': 'hardware-test1', 'type': 'a'} ] editArgs = ( {'type': 'a', 'host': 'hardware-test1', 'data': '172.16.1.100', 'id': 1, 'ttl': 7200}, ) result = self.run_command(['hw', 'dns-sync', '-a', '1000']) 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': 'hardware-test1', 'type': 'a'}, {'id': 2, 'ttl': 7200, 'data': '1.1.1.1', 'host': 'hardware-test1', 'type': 'a'} ] result = self.run_command(['hw', 'dns-sync', '-a', '1000']) self.assertEqual(result.exit_code, 1) self.assertIsInstance(result.exception, SoftLayerError) @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_Hardware_Server', 'getReverseDomainRecords') getReverseDomainRecords.return_value = [{ 'networkAddress': '172.16.1.100', 'name': '100.1.16.172.in-addr.arpa', 'resourceRecords': [{'data': 'test.softlayer.com.', 'id': 123, 'host': '100'}], 'updateDate': '2013-09-11T14:36:57-07:00', 'serial': 1234665663, 'id': 123456, }] editArgs = ({'host': '100', 'data': 'hardware-test1.test.sftlyr.ws', 'id': 123, 'ttl': 7200},) result = self.run_command(['hw', 'dns-sync', '--ptr', '1000']) 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(['hw', 'dns-sync', '-a', '1000']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.CLIAbort) guest = self.set_mock('SoftLayer_Hardware_Server', 'getObject') test_guest = { 'id': 1000, 'primaryIpAddress': '', 'hostname': 'hardware-test1', 'domain': 'sftlyr.ws', 'fullyQualifiedDomainName': 'hardware-test1.sftlyr.ws', "primaryNetworkComponent": {} } guest.return_value = test_guest result = self.run_command(['hw', 'dns-sync', '-a', '1000']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.CLIAbort) def test_hardware_storage(self): result = self.run_command( ['hw', 'storage', '100']) self.assert_no_fail(result) def test_billing(self): result = self.run_command(['hw', 'billing', '123456']) hardware_server_billing = { 'Billing Item Id': 6327, 'Id': '123456', 'Provision Date': None, 'Recurring Fee': 1.54, 'Total': 16.08, 'prices': [ { 'Item': 'test', 'Recurring Price': 1 }, { 'Item': 'test2', 'Recurring Price': 2 }, ] } self.assert_no_fail(result) self.assertEqual(json.loads(result.output), hardware_server_billing) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create_hw_no_confirm(self, confirm_mock): confirm_mock.return_value = False result = self.run_command(['hw', 'create', '--hostname=test', '--size=S1270_8GB_2X1TBSATA_NORAID', '--domain=example.com', '--datacenter=TEST00', '--network=TEST_NETWORK', '--os=UBUNTU_12_64']) self.assertEqual(result.exit_code, 2) def test_get_hardware_guests(self): result = self.run_command(['hw', 'guests', '123456']) self.assert_no_fail(result) def test_hardware_guests_empty(self): mock = self.set_mock('SoftLayer_Virtual_Host', 'getGuests') mock.return_value = None result = self.run_command(['hw', 'guests', '123456']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.CLIAbort) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_authorize_hw_no_confirm(self, confirm_mock): confirm_mock.return_value = False result = self.run_command(['hw', 'authorize-storage', '-u', '1234']) self.assertEqual(result.exit_code, 2) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_authorize_hw_empty(self, confirm_mock): confirm_mock.return_value = True storage_result = self.set_mock('SoftLayer_Account', 'getNetworkStorage') storage_result.return_value = [] result = self.run_command(['hw', 'authorize-storage', '--username-storage=#', '1234']) self.assertEqual(str(result.exception), "The Storage with username: # was not found, " "please enter a valid storage username") def test_authorize_hw(self): result = self.run_command(['hw', 'authorize-storage', '--username-storage=SL01SEL301234-11', '1234']) self.assert_no_fail(result) def test_upgrade_no_options(self, ): result = self.run_command(['hw', 'upgrade', '100']) 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(['hw', 'upgrade', '100', '--memory=1']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.CLIAbort) def test_upgrade_test(self): order_mock = self.set_mock('SoftLayer_Product_Order', 'verifyOrder') order_mock.return_value = SoftLayer_Product_Order.hardware_verifyOrder result = self.run_command(['hw', 'upgrade', '100', '--test', '--memory=32', '--public-bandwidth=500', '--drive-controller=RAID', '--network=10000 Redundant']) self.assert_no_fail(result) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_upgrade_add_disk(self, confirm_mock): confirm_mock.return_value = True order_mock = self.set_mock('SoftLayer_Product_Order', 'placeOrder') order_mock.return_value = SoftLayer_Product_Order.hardware_placeOrder result = self.run_command(['hw', 'upgrade', '100', '--add-disk=1000', '2']) self.assert_no_fail(result) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_upgrade_resize_disk(self, confirm_mock): confirm_mock.return_value = True order_mock = self.set_mock('SoftLayer_Product_Order', 'placeOrder') order_mock.return_value = SoftLayer_Product_Order.hardware_placeOrder result = self.run_command(['hw', 'upgrade', '100', '--resize-disk=1000', '1']) self.assert_no_fail(result) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_upgrade_disk_not_price_found(self, confirm_mock): confirm_mock.return_value = False result = self.run_command(['hw', 'upgrade', '100', '--add-disk=1000', '3']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.CLIAbort) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_upgrade_disk_already_exist(self, confirm_mock): confirm_mock.return_value = False result = self.run_command(['hw', 'upgrade', '100', '--add-disk=1000', '1']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.CLIAbort) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_upgrade_disk_does_not_exist(self, confirm_mock): confirm_mock.return_value = False result = self.run_command(['hw', 'upgrade', '100', '--resize-disk=1000', '3']) 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 order_mock = self.set_mock('SoftLayer_Product_Order', 'placeOrder') order_mock.return_value = SoftLayer_Product_Order.hardware_placeOrder result = self.run_command(['hw', 'upgrade', '100', '--memory=32', '--public-bandwidth=500', '--drive-controller=RAID', '--network=10000 Redundant']) self.assert_no_fail(result) def test_components(self): result = self.run_command(['hardware', 'detail', '100', '--components']) self.assert_no_fail(result) def test_sensor(self): result = self.run_command(['hardware', 'sensor', '100']) self.assert_no_fail(result) def test_sensor_discrete(self): result = self.run_command(['hardware', 'sensor', '100', '--discrete']) self.assert_no_fail(result) softlayer-python-5.9.8/tests/CLI/modules/sshkey_tests.py000066400000000000000000000116471415376457700234430ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.sshkey_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import json import os.path import sys import tempfile from unittest import mock as 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.9.8/tests/CLI/modules/ssl_tests.py000066400000000000000000000020721415376457700227260ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.ssl_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ from SoftLayer import testing import json from unittest import mock as mock class SslTests(testing.TestCase): def test_list(self): result = self.run_command(['ssl', 'list', '--status', 'all']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), [ { "id": 1234, "common_name": "cert", "days_until_expire": 0, "notes": None } ]) @mock.patch('SoftLayer.CLI.formatting.no_going_back') def test_remove(self, confirm_mock): confirm_mock.return_value = True result = self.run_command(['ssl', 'remove', '123456']) self.assert_no_fail(result) self.assertEqual(result.exit_code, 0) def test_download(self): result = self.run_command(['ssl', 'download', '123456']) self.assert_no_fail(result) self.assertEqual(result.exit_code, 0) softlayer-python-5.9.8/tests/CLI/modules/subnet_tests.py000066400000000000000000000165421415376457700234340ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.subnet_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import json from unittest import mock as mock import SoftLayer from SoftLayer.fixtures import SoftLayer_Product_Order from SoftLayer.fixtures import SoftLayer_Product_Package from SoftLayer import testing class SubnetTests(testing.TestCase): def test_detail(self): result = self.run_command(['subnet', 'detail', '1234']) subnet = json.loads(result.output) self.assert_no_fail(result) self.assertEqual(subnet.get('id'), 1234) self.assertEqual(subnet.get('identifier'), '1.2.3.4/26') def test_list(self): result = self.run_command(['subnet', 'list']) self.assert_no_fail(result) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create_subnet_ipv4(self, confirm_mock): confirm_mock.return_value = True item_mock = self.set_mock('SoftLayer_Product_Package', 'getItems') item_mock.return_value = SoftLayer_Product_Package.getItems place_mock = self.set_mock('SoftLayer_Product_Order', 'placeOrder') place_mock.return_value = SoftLayer_Product_Order.placeOrder result = self.run_command(['subnet', 'create', 'private', '8', '12346']) self.assert_no_fail(result) output = [ {'Item': 'Total monthly cost', 'cost': '0.00'} ] self.assertEqual(output, json.loads(result.output)) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create_subnet_ipv6(self, confirm_mock): confirm_mock.return_value = True item_mock = self.set_mock('SoftLayer_Product_Package', 'getItems') item_mock.return_value = SoftLayer_Product_Package.getItems place_mock = self.set_mock('SoftLayer_Product_Order', 'verifyOrder') place_mock.return_value = SoftLayer_Product_Order.verifyOrder result = self.run_command(['subnet', 'create', '--v6', 'public', '64', '12346', '--test']) self.assert_no_fail(result) output = [ {'Item': 'this is a thing', 'cost': '2.00'}, {'Item': 'Total monthly cost', 'cost': '2.00'} ] self.assertEqual(output, json.loads(result.output)) def test_create_subnet_no_prices_found(self): item_mock = self.set_mock('SoftLayer_Product_Package', 'getItems') item_mock.return_value = SoftLayer_Product_Package.getItems verify_mock = self.set_mock('SoftLayer_Product_Order', 'verifyOrder') verify_mock.side_effect = SoftLayer.SoftLayerAPIError('SoftLayer_Exception', 'Price not found') result = self.run_command(['subnet', 'create', '--v6', 'public', '32', '12346', '--test']) self.assertRaises(SoftLayer.SoftLayerAPIError, verify_mock) self.assertIn('Unable to order 32 public ipv6', result.exception.message, ) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create_subnet_static(self, confirm_mock): confirm_mock.return_value = True item_mock = self.set_mock('SoftLayer_Product_Package', 'getItems') item_mock.return_value = SoftLayer_Product_Package.getItems place_mock = self.set_mock('SoftLayer_Product_Order', 'placeOrder') place_mock.return_value = SoftLayer_Product_Order.placeOrder result = self.run_command(['subnet', 'create', 'static', '2', '12346']) self.assert_no_fail(result) output = [ {'Item': 'Total monthly cost', 'cost': '0.00'} ] self.assertEqual(output, json.loads(result.output)) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create_subnet_static_ipv6(self, confirm_mock): confirm_mock.return_value = True item_mock = self.set_mock('SoftLayer_Product_Package', 'getItems') item_mock.return_value = SoftLayer_Product_Package.getItems place_mock = self.set_mock('SoftLayer_Product_Order', 'verifyOrder') place_mock.return_value = SoftLayer_Product_Order.verifyOrder result = self.run_command(['subnet', 'create', '--v6', 'static', '64', '12346', '--test']) self.assert_no_fail(result) output = [ {'Item': 'this is a thing', 'cost': '2.00'}, {'Item': 'Total monthly cost', 'cost': '2.00'} ] self.assertEqual(output, json.loads(result.output)) @mock.patch('SoftLayer.CLI.subnet.edit.click') def test_subnet_set_tags(self, click): result = self.run_command(['subnet', 'edit', '1234', '--tags=tag1,tag2']) click.secho.assert_called_with('Set tags successfully', fg='green') self.assert_no_fail(result) self.assert_called_with('SoftLayer_Network_Subnet', 'setTags', identifier=1234, args=("tag1,tag2",)) @mock.patch('SoftLayer.CLI.subnet.edit.click') def test_subnet_edit_note(self, click): result = self.run_command(['subnet', 'edit', '1234', '--note=test']) click.secho.assert_called_with('Edit note successfully', fg='green') self.assert_no_fail(result) self.assert_called_with('SoftLayer_Network_Subnet', 'editNote', identifier=1234, args=("test",)) @mock.patch('SoftLayer.CLI.subnet.edit.click') def test_subnet_set_tags_failure(self, click): mock = self.set_mock('SoftLayer_Network_Subnet', 'setTags') mock.return_value = False result = self.run_command(['subnet', 'edit', '1234', '--tags=tag1,tag2']) click.secho.assert_called_with('Failed to set tags', fg='red') self.assert_no_fail(result) self.assert_called_with('SoftLayer_Network_Subnet', 'setTags', identifier=1234, args=("tag1,tag2",)) @mock.patch('SoftLayer.CLI.subnet.edit.click') def test_edit_note_failure(self, click): mock = self.set_mock('SoftLayer_Network_Subnet', 'editNote') mock.return_value = False result = self.run_command(['subnet', 'edit', '1234', '--note=test']) click.secho.assert_called_with('Failed to edit note', fg='red') self.assert_no_fail(result) self.assert_called_with('SoftLayer_Network_Subnet', 'editNote', identifier=1234, args=("test",)) def test_editrou_Ip(self): result = self.run_command(['subnet', 'edit-ip', '16.26.26.26', '--note=test']) self.assert_no_fail(result) self.assertTrue(result) def test_editrou_Id(self): result = self.run_command(['subnet', 'edit-ip', '123456', '--note=test']) self.assert_no_fail(result) self.assertTrue(result) def test_lookup(self): result = self.run_command(['subnet', 'lookup', '1.2.3.10']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), {'device': { 'id': 12856, 'name': 'unit.test.com', 'type': 'server'}, "id": 12345, "ip": "10.0.1.37", "subnet": { "id": 258369, "identifier": "10.0.1.38/26", "netmask": "255.255.255.192", "gateway": "10.47.16.129", "type": "PRIMARY" }}) @mock.patch('SoftLayer.CLI.formatting.no_going_back') def test_cancel(self, confirm_mock): confirm_mock.return_value = True result = self.run_command(['subnet', 'cancel', '1234']) self.assert_no_fail(result) def test_cancel_fail(self): result = self.run_command(['subnet', 'cancel', '1234']) self.assertEqual(result.exit_code, 2) softlayer-python-5.9.8/tests/CLI/modules/summary_tests.py000066400000000000000000000012321415376457700236170ustar00rootroot00000000000000""" 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.9.8/tests/CLI/modules/tag_tests.py000066400000000000000000000130161415376457700227000ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.tag_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Tests for the user cli command """ from unittest import mock as mock from SoftLayer.exceptions import SoftLayerAPIError from SoftLayer import testing class TagCLITests(testing.TestCase): def test_list(self): result = self.run_command(['tags', 'list']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Tag', 'getUnattachedTagsForCurrentUser') self.assert_called_with('SoftLayer_Tag', 'getAttachedTagsForCurrentUser') self.assertIn('coreos', result.output) def test_list_detail(self): result = self.run_command(['tags', 'list', '-d']) self.assert_no_fail(result) self.assertIn('"vs-test1.test.sftlyr.ws', result.output) # From fixtures/virutal_guest.getObject # self.assert_called_with('SoftLayer_Tag', 'getUnattachedTagsForCurrentUser') self.assert_called_with('SoftLayer_Tag', 'getAttachedTagsForCurrentUser') self.assert_called_with('SoftLayer_Tag', 'getReferences', identifier=1286571) self.assert_called_with('SoftLayer_Virtual_Guest', 'getObject', identifier=33488921) def test_list_detail_ungettable(self): mock = self.set_mock('SoftLayer_Virtual_Guest', 'getObject') mock.side_effect = SoftLayerAPIError(404, "TEST ERROR") result = self.run_command(['tags', 'list', '-d']) self.assert_no_fail(result) self.assertIn("TEST ERROR", result.output) # From fixtures/virutal_guest.getObject # self.assert_called_with('SoftLayer_Tag', 'getUnattachedTagsForCurrentUser') self.assert_called_with('SoftLayer_Tag', 'getAttachedTagsForCurrentUser') self.assert_called_with('SoftLayer_Tag', 'getReferences', identifier=1286571) self.assert_called_with('SoftLayer_Virtual_Guest', 'getObject', identifier=33488921) @mock.patch('SoftLayer.CLI.tags.set.click') def test_set_tags(self, click): result = self.run_command(['tags', 'set', '--tags=tag1,tag2', '--key-name=GUEST', '--resource-id=100']) click.secho.assert_called_with('Set tags successfully', fg='green') self.assert_no_fail(result) self.assert_called_with('SoftLayer_Tag', 'setTags', args=("tag1,tag2", "GUEST", 100), ) @mock.patch('SoftLayer.CLI.tags.set.click') def test_set_tags_failure(self, click): mock = self.set_mock('SoftLayer_Tag', 'setTags') mock.return_value = False result = self.run_command(['tags', 'set', '--tags=tag1,tag2', '--key-name=GUEST', '--resource-id=100']) click.secho.assert_called_with('Failed to set tags', fg='red') self.assert_no_fail(result) self.assert_called_with('SoftLayer_Tag', 'setTags', args=("tag1,tag2", "GUEST", 100), ) def test_details_by_name(self): tag_name = 'bs_test_instance' result = self.run_command(['tags', 'details', tag_name]) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Tag', 'getTagByTagName', args=(tag_name,)) def test_details_by_id(self): tag_id = '1286571' result = self.run_command(['tags', 'details', tag_id]) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Tag', 'getObject', identifier=tag_id) def test_deleteTags_by_name(self): result = self.run_command(['tags', 'delete', 'test']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Tag', 'deleteTag', args=('test',)) def test_deleteTags_by_id(self): result = self.run_command(['tags', 'delete', '123456']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Tag', 'getObject', identifier='123456') self.assert_called_with('SoftLayer_Tag', 'deleteTag', args=('bs_test_instance',)) def test_deleteTags_by_number_name(self): result = self.run_command(['tags', 'delete', '123456', '--name']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Tag', 'deleteTag', args=('123456',)) @mock.patch('SoftLayer.CLI.tags.delete.click') def test_deleteTags_fail(self, click): mock = self.set_mock('SoftLayer_Tag', 'deleteTag') mock.return_value = False result = self.run_command(['tags', 'delete', '123456', '--name']) click.secho.assert_called_with('Failed to remove tag 123456', fg='red') self.assert_no_fail(result) self.assert_called_with('SoftLayer_Tag', 'deleteTag', args=('123456',)) def test_taggable(self): result = self.run_command(['tags', 'taggable']) self.assert_no_fail(result) self.assertIn('"host14.vmware.test.com', result.output) self.assert_called_with('SoftLayer_Tag', 'getAllTagTypes') self.assert_called_with('SoftLayer_Search', 'advancedSearch', args=('_objectType:SoftLayer_Hardware',)) def test_cleanup(self): result = self.run_command(['tags', 'cleanup']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Tag', 'getUnattachedTagsForCurrentUser') self.assert_called_with('SoftLayer_Tag', 'deleteTag', args=('coreos',)) def test_cleanup_dry(self): result = self.run_command(['tags', 'cleanup', '-d']) self.assert_no_fail(result) self.assertIn('(Dry Run)', result.output) self.assert_called_with('SoftLayer_Tag', 'getUnattachedTagsForCurrentUser') self.assertEqual([], self.calls(service='SoftLayer_Tag', method='deleteTag')) softlayer-python-5.9.8/tests/CLI/modules/ticket_tests.py000066400000000000000000000341401415376457700234110ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.ticket_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import json from unittest import mock as mock from SoftLayer.CLI import exceptions from SoftLayer.CLI import formatting from SoftLayer.CLI import ticket from SoftLayer.managers import TicketManager from SoftLayer import testing class FakeTTY(): """A fake object to fake STD input""" def __init__(self, isatty=False, read="Default Output"): """Sets isatty and read""" self._isatty = isatty self._read = read def isatty(self): """returns self.isatty""" return self._isatty def read(self): """returns self.read""" return self._read class TicketTests(testing.TestCase): def test_list(self): result = self.run_command(['ticket', 'list']) expected = [{ 'assigned_user': 'John Smith', 'Case_Number': 'CS123456', 'id': 102, 'last_edited': '2013-08-01T14:16:47-07:00', 'priority': 0, 'status': 'Open', 'title': 'Cloud Instance Cancellation - 08/01/13', 'updates': 0}] self.assert_no_fail(result) self.assertEqual(expected, json.loads(result.output)) def test_detail(self): result = self.run_command(['ticket', 'detail', '1']) expected = { 'Case_Number': 'CS123456', 'created': '2013-08-01T14:14:04-07:00', 'edited': '2013-08-01T14:16:47-07:00', 'id': 100, 'priority': 'No Priority', 'status': 'Closed', 'title': 'Cloud Instance Cancellation - 08/01/13', 'update 1': 'a bot says something', 'update 2': 'By John Smith user says something', 'update 3': 'By emp1 (Employee) employee 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, 'assignedUserId': 12345, 'title': 'Test'}, 'ticket body') self.assert_called_with('SoftLayer_Ticket', 'createStandardTicket', args=args) def test_create_with_priority(self): result = self.run_command(['ticket', 'create', '--title=Test', '--subject-id=1000', '--body=ticket body', '--priority=1']) self.assert_no_fail(result) args = ({'subjectId': 1000, 'assignedUserId': 12345, 'title': 'Test', 'priority': 1}, '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, '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') @mock.patch('click.get_text_stream') def test_create_no_body(self, isatty_mock, edit_mock): fake_tty = FakeTTY(True, "TEST") isatty_mock.return_value = fake_tty 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, 'assignedUserId': 12345, 'title': 'Test'}, 'ticket body') self.assert_called_with('SoftLayer_Ticket', 'createStandardTicket', args=args) @mock.patch('click.get_text_stream') def test_create_no_body_stdin(self, isatty_mock): fake_tty = FakeTTY(False, "TEST TICKET BODY") isatty_mock.return_value = fake_tty result = self.run_command(['ticket', 'create', '--title=Test', '--subject-id=1000']) print(result.output) self.assert_no_fail(result) args = ({'subjectId': 1000, 'assignedUserId': 12345, 'title': 'Test'}, '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) def test_init_ticket_results(self): ticket_mgr = TicketManager(self.client) ticket_table = ticket.get_ticket_results(ticket_mgr, 100) self.assert_called_with('SoftLayer_Ticket', 'getObject', identifier=100) self.assertIsInstance(ticket_table, formatting.KeyValueTable) ticket_object = ticket_table.to_python() self.assertEqual('No Priority', ticket_object['priority']) self.assertEqual(100, ticket_object['id']) def test_init_ticket_results_asigned_user(self): mock = self.set_mock('SoftLayer_Ticket', 'getObject') mock.return_value = { "serviceProviderResourceId": "CS12345", "id": 100, "title": "Simple Title", "priority": 1, "assignedUser": { "firstName": "Test", "lastName": "User" }, "status": { "name": "Closed" }, "createDate": "2013-08-01T14:14:04-07:00", "lastEditDate": "2013-08-01T14:16:47-07:00", "updates": [{'entry': 'a bot says something'}] } ticket_mgr = TicketManager(self.client) ticket_table = ticket.get_ticket_results(ticket_mgr, 100) self.assert_called_with('SoftLayer_Ticket', 'getObject', identifier=100) self.assertIsInstance(ticket_table, formatting.KeyValueTable) ticket_object = ticket_table.to_python() self.assertEqual('Severity 1 - Critical Impact / Service Down', ticket_object['priority']) self.assertEqual('Test User', ticket_object['user']) def test_ticket_summary(self): mock = self.set_mock('SoftLayer_Account', 'getObject') mock.return_value = { 'openTicketCount': 1, 'closedTicketCount': 2, 'openBillingTicketCount': 3, 'openOtherTicketCount': 4, 'openSalesTicketCount': 5, 'openSupportTicketCount': 6, 'openAccountingTicketCount': 7 } expected = [ {'Status': 'Open', 'count': [ {'Type': 'Accounting', 'count': 7}, {'Type': 'Billing', 'count': 3}, {'Type': 'Sales', 'count': 5}, {'Type': 'Support', 'count': 6}, {'Type': 'Other', 'count': 4}, {'Type': 'Total', 'count': 1}]}, {'Status': 'Closed', 'count': 2} ] result = self.run_command(['ticket', 'summary']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Account', 'getObject') self.assertEqual(expected, json.loads(result.output)) def test_ticket_update(self): result = self.run_command(['ticket', 'update', '100', '--body=Testing']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Ticket', 'addUpdate', args=({'entry': 'Testing'},), identifier=100) @mock.patch('click.edit') @mock.patch('click.get_text_stream') def test_ticket_update_no_body(self, isatty_mock, edit_mock): fake_tty = FakeTTY(True, "TEST TICKET BODY") isatty_mock.return_value = fake_tty edit_mock.return_value = 'Testing1' result = self.run_command(['ticket', 'update', '100']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Ticket', 'addUpdate', args=({'entry': 'Testing1'},), identifier=100) @mock.patch('click.get_text_stream') def test_ticket_update_no_body_stdin(self, isatty_mock): fake_tty = FakeTTY(False, "TEST TICKET BODY") isatty_mock.return_value = fake_tty result = self.run_command(['ticket', 'update', '100']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Ticket', 'addUpdate', args=({'entry': 'TEST TICKET BODY'},), identifier=100) def test_ticket_json(self): result = self.run_command(['--format=json', 'ticket', 'detail', '1']) expected = {'Case_Number': 'CS123456', 'created': '2013-08-01T14:14:04-07:00', 'edited': '2013-08-01T14:16:47-07:00', 'id': 100, 'priority': 'No Priority', 'status': 'Closed', 'title': 'Cloud Instance Cancellation - 08/01/13', 'update 1': 'a bot says something', 'update 2': 'By John Smith user says something', 'update 3': 'By emp1 (Employee) employee says something'} self.assert_no_fail(result) self.assertEqual(json.loads(result.output), expected) softlayer-python-5.9.8/tests/CLI/modules/user_tests.py000066400000000000000000000363141415376457700231110ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.user_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Tests for the user cli command """ import json import sys import unittest from unittest import mock as mock from SoftLayer import testing class UserCLITests(testing.TestCase): """User list tests""" def test_user_list(self): result = self.run_command(['user', 'list']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Account', 'getUsers') def test_user_list_only_id(self): result = self.run_command(['user', 'list', '--columns=id']) self.assert_no_fail(result) self.assertEqual([{"id": 11100}, {"id": 11111}], json.loads(result.output)) """User detail tests""" def test_detail(self): result = self.run_command(['user', 'detail', '11100']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_User_Customer', 'getObject') def test_detail_keys(self): result = self.run_command(['user', 'detail', '11100', '-k']) self.assert_no_fail(result) self.assertIn('APIKEY', result.output) def test_detail_permissions(self): result = self.run_command(['user', 'detail', '11100', '-p']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_User_Customer', 'getPermissions') self.assertIn('ACCESS_ALL_HARDWARE', result.output) def test_detail_hardware(self): result = self.run_command(['user', 'detail', '11100', '-h']) self.assert_no_fail(result) self.assert_called_with( 'SoftLayer_User_Customer', 'getObject', identifier=11100, mask='mask[id, hardware, dedicatedHosts]' ) def test_detail_virtual(self): result = self.run_command(['user', 'detail', '11100', '-v']) self.assert_no_fail(result) self.assert_called_with( 'SoftLayer_User_Customer', 'getObject', identifier=11100, mask='mask[id, virtualGuests]' ) def test_detail_logins(self): result = self.run_command(['user', 'detail', '11100', '-l']) self.assert_no_fail(result) self.assert_called_with( 'SoftLayer_User_Customer', 'getLoginAttempts', identifier=11100 ) def test_detail_events(self): result = self.run_command(['user', 'detail', '11100', '-e']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Event_Log', 'getAllObjects') def test_print_hardware_access(self): mock = self.set_mock('SoftLayer_User_Customer', 'getObject') mock.return_value = { 'accountId': 12345, 'address1': '315 Test Street', 'city': 'Houston', 'companyName': 'SoftLayer Development Community', 'country': 'US', 'displayName': 'Test', 'email': 'test@us.ibm.com', 'firstName': 'Test', 'id': 244956, 'lastName': 'Testerson', 'postalCode': '77002', 'state': 'TX', 'statusDate': None, 'hardware': [ {'id': 1234, 'fullyQualifiedDomainName': 'test.test.test', 'provisionDate': '2018-05-08T15:28:32-06:00', 'primaryBackendIpAddress': '175.125.126.118', 'primaryIpAddress': '175.125.126.118'} ], 'dedicatedHosts': [ {'id': 1234, 'fullyQualifiedDomainName': 'test.test.test', 'provisionDate': '2018-05-08T15:28:32-06:00', 'primaryBackendIpAddress': '175.125.126.118', 'primaryIpAddress': '175.125.126.118'} ], } result = self.run_command(['user', 'detail', '11100', '-h']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_User_Customer', 'getObject', identifier=11100, mask="mask[id, hardware, dedicatedHosts]") """User permissions tests""" def test_permissions_list(self): result = self.run_command(['user', 'permissions', '11100']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_User_Customer_CustomerPermission_Permission', 'getAllObjects') self.assert_called_with( 'SoftLayer_User_Customer', 'getObject', identifier=11100, mask='mask[id, permissions, isMasterUserFlag, roles]' ) """User edit-permissions tests""" def test_edit_perms_on(self): result = self.run_command(['user', 'edit-permissions', '11100', '--enable', '-p', 'TEST']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_User_Customer', 'addBulkPortalPermission', identifier=11100) def test_edit_perms_on_bad(self): result = self.run_command(['user', 'edit-permissions', '11100', '--enable', '-p', 'TEST_NOt_exist']) self.assertEqual(result.exit_code, 1) def test_edit_perms_off(self): result = self.run_command(['user', 'edit-permissions', '11100', '--disable', '-p', 'TEST']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_User_Customer', 'removeBulkPortalPermission', identifier=11100) @mock.patch('SoftLayer.CLI.user.edit_permissions.click') def test_edit_perms_off_failure(self, click): permission_mock = self.set_mock('SoftLayer_User_Customer', 'removeBulkPortalPermission') permission_mock.return_value = False result = self.run_command(['user', 'edit-permissions', '11100', '--disable', '-p', 'TEST']) click.secho.assert_called_with('Failed to update permissions: TEST', fg='red') self.assert_no_fail(result) self.assert_called_with('SoftLayer_User_Customer', 'removeBulkPortalPermission', identifier=11100) def test_edit_perms_from_user(self): result = self.run_command(['user', 'edit-permissions', '11100', '-u', '1234']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_User_Customer', 'getPermissions', identifier=1234) self.assert_called_with('SoftLayer_User_Customer', 'removeBulkPortalPermission', identifier=11100) self.assert_called_with('SoftLayer_User_Customer', 'addBulkPortalPermission', identifier=11100) """User create tests""" @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create_user(self, confirm_mock): confirm_mock.return_value = True result = self.run_command(['user', 'create', 'test', '-e', 'test@us.ibm.com', '-p', 'testword']) self.assert_no_fail(result) self.assertIn('test@us.ibm.com', result.output) self.assert_called_with('SoftLayer_Account', 'getCurrentUser') self.assert_called_with('SoftLayer_User_Customer', 'createObject', args=mock.ANY) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create_user_no_confirm(self, confirm_mock): confirm_mock.return_value = False result = self.run_command(['user', 'create', 'test', '-e', 'test@us.ibm.com', '-p', 'testword']) self.assertEqual(result.exit_code, 2) @unittest.skipIf(sys.version_info < (3, 6), "Secrets module only exists in version 3.6+") @mock.patch('secrets.choice') @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create_user_generate_password_36(self, confirm_mock, secrets): secrets.return_value = 'Q' confirm_mock.return_value = True result = self.run_command(['user', 'create', 'test', '-e', 'test@us.ibm.com', '-p', 'generate']) self.assert_no_fail(result) self.assertIn('test@us.ibm.com', result.output) self.assertIn('QQQQQQQQQQQQQQQQQQQQQQ', result.output) self.assert_called_with('SoftLayer_Account', 'getCurrentUser') self.assert_called_with('SoftLayer_User_Customer', 'createObject', args=mock.ANY) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create_user_generate_password_2(self, confirm_mock): if sys.version_info >= (3, 6): self.skipTest("Python needs to be < 3.6 for this test.") confirm_mock.return_value = True result = self.run_command(['user', 'create', 'test', '-e', 'test@us.ibm.com', '-p', 'generate']) self.assertIn(result.output, "ImportError") @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create_user_and_apikey(self, confirm_mock): confirm_mock.return_value = True result = self.run_command(['user', 'create', 'test', '-e', 'test@us.ibm.com']) self.assert_no_fail(result) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create_user_with_template(self, confirm_mock): confirm_mock.return_value = True result = self.run_command(['user', 'create', 'test', '-e', 'test@us.ibm.com', '-t', '{"firstName": "Supermand"}']) self.assertIn('Supermand', result.output) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create_user_with_bad_template(self, confirm_mock): confirm_mock.return_value = True result = self.run_command(['user', 'create', 'test', '-e', 'test@us.ibm.com', '-t', '{firstName: "Supermand"}']) self.assertIn("Argument Error", result.exception.message) self.assertEqual(result.exit_code, 2) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create_user_with_no_confirm(self, confirm_mock): confirm_mock.return_value = False result = self.run_command(['user', 'create', 'test', '-e', 'test@us.ibm.com']) self.assertIn("Canceling creation!", result.exception.message) self.assertEqual(result.exit_code, 2) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create_user_from_user(self, confirm_mock): confirm_mock.return_value = True result = self.run_command(['user', 'create', 'test', '-e', 'test@us.ibm.com', '-u', '1234']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_User_Customer', 'getObject', identifier=1234) """User edit-details tests""" @mock.patch('SoftLayer.CLI.user.edit_details.click') def test_edit_details(self, click): result = self.run_command(['user', 'edit-details', '1234', '-t', '{"firstName":"Supermand"}']) click.secho.assert_called_with('1234 updated successfully', fg='green') self.assert_no_fail(result) self.assert_called_with('SoftLayer_User_Customer', 'editObject', args=({'firstName': 'Supermand'},), identifier=1234) @mock.patch('SoftLayer.CLI.user.edit_details.click') def test_edit_details_failure(self, click): mock = self.set_mock('SoftLayer_User_Customer', 'editObject') mock.return_value = False result = self.run_command(['user', 'edit-details', '1234', '-t', '{"firstName":"Supermand"}']) click.secho.assert_called_with('Failed to update 1234', fg='red') self.assert_no_fail(result) self.assert_called_with('SoftLayer_User_Customer', 'editObject', args=({'firstName': 'Supermand'},), identifier=1234) def test_edit_details_bad_json(self): result = self.run_command(['user', 'edit-details', '1234', '-t', '{firstName:"Supermand"}']) self.assertIn("Argument Error", result.exception.message) self.assertEqual(result.exit_code, 2) """User delete tests""" @mock.patch('SoftLayer.CLI.user.delete.click') def test_delete(self, click): result = self.run_command(['user', 'delete', '12345']) click.secho.assert_called_with('12345 deleted successfully', fg='green') self.assert_no_fail(result) self.assert_called_with('SoftLayer_User_Customer', 'editObject', args=({'userStatusId': 1021},), identifier=12345) @mock.patch('SoftLayer.CLI.user.delete.click') def test_delete_failure(self, click): mock = self.set_mock('SoftLayer_User_Customer', 'editObject') mock.return_value = False result = self.run_command(['user', 'delete', '12345']) click.secho.assert_called_with('Failed to delete 12345', fg='red') self.assert_no_fail(result) self.assert_called_with('SoftLayer_User_Customer', 'editObject', args=({'userStatusId': 1021},), identifier=12345) """User vpn manual config tests""" @mock.patch('SoftLayer.CLI.user.vpn_manual.click') def test_vpn_manual(self, click): result = self.run_command(['user', 'vpn-manual', '12345', '--enable']) click.secho.assert_called_with('12345 vpn manual config enable', fg='green') self.assert_no_fail(result) def test_vpn_manual_fail(self): mock = self.set_mock('SoftLayer_User_Customer', 'editObject') mock.return_value = False result = self.run_command(['user', 'vpn-manual', '12345', '--enable']) self.assert_no_fail(result) """User vpn subnet tests""" @mock.patch('SoftLayer.CLI.user.vpn_subnet.click') def test_vpn_subnet_add(self, click): result = self.run_command(['user', 'vpn-subnet', '12345', '--add', '1234']) click.secho.assert_called_with('12345 updated successfully', fg='green') self.assert_no_fail(result) def test_vpn_subnet_add_fail(self): mock = self.set_mock('SoftLayer_Network_Service_Vpn_Overrides', 'createObjects') mock.return_value = False result = self.run_command(['user', 'vpn-subnet', '12345', '--add', '1234']) self.assert_no_fail(result) @mock.patch('SoftLayer.CLI.user.vpn_subnet.click') def test_vpn_subnet_remove(self, click): result = self.run_command(['user', 'vpn-subnet', '12345', '--remove', '1234']) click.secho.assert_called_with('12345 updated successfully', fg='green') self.assert_no_fail(result) """User notification tests""" def test_notificacions_list(self): result = self.run_command(['user', 'notifications']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Email_Subscription', 'getAllObjects', mask='mask[enabled]') """User edit-notification tests""" def test_edit_notification_on(self): result = self.run_command(['user', 'edit-notifications', '--enable', 'Test notification']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Email_Subscription', 'enable', identifier=111) def test_edit_notification_on_bad(self): result = self.run_command(['user', 'edit-notifications', '--enable', 'Test not exist']) self.assertEqual(result.exit_code, 1) def test_edit_notifications_off(self): result = self.run_command(['user', 'edit-notifications', '--disable', 'Test notification']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Email_Subscription', 'disable', identifier=111) @mock.patch('SoftLayer.CLI.user.edit_notifications.click') def test_edit_notification_off_failure(self, click): notification = self.set_mock('SoftLayer_Email_Subscription', 'disable') notification.return_value = False result = self.run_command(['user', 'edit-notifications', '--disable', 'Test notification']) click.secho.assert_called_with('Failed to update notifications: Test notification', fg='red') self.assert_no_fail(result) self.assert_called_with('SoftLayer_Email_Subscription', 'disable', identifier=111) softlayer-python-5.9.8/tests/CLI/modules/vlan_tests.py000066400000000000000000000174021415376457700230700ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.vlan_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import json from unittest import mock as mock from SoftLayer.fixtures import SoftLayer_Product_Order from SoftLayer.fixtures import SoftLayer_Product_Package from SoftLayer import testing class VlanTests(testing.TestCase): def test_detail(self): result = self.run_command(['vlan', 'detail', '1234']) self.assert_no_fail(result) def test_detail_no_vs(self): result = self.run_command(['vlan', 'detail', '1234', '--no-vs']) self.assert_no_fail(result) def test_detail_no_hardware(self): result = self.run_command(['vlan', 'detail', '1234', '--no-hardware']) self.assert_no_fail(result) def test_subnet_list(self): vlan_mock = self.set_mock('SoftLayer_Network_Vlan', 'getObject') getObject = { 'primaryRouter': { 'datacenter': {'id': 1234, 'longName': 'TestDC'}, 'fullyQualifiedDomainName': 'fcr01.TestDC' }, 'id': 1234, 'vlanNumber': 4444, 'firewallInterfaces': None, 'subnets': [ { 'id': 99, 'networkIdentifier': 1111111, 'netmask': '255.255.255.0', 'gateway': '12.12.12.12', 'subnetType': 'TEST', 'usableIpAddressCount': 1 } ] } vlan_mock.return_value = getObject result = self.run_command(['vlan', 'detail', '1234']) self.assert_no_fail(result) def test_detail_hardware_without_hostname(self): vlan_mock = self.set_mock('SoftLayer_Network_Vlan', 'getObject') getObject = { 'primaryRouter': { 'datacenter': {'id': 1234, 'longName': 'TestDC'}, 'fullyQualifiedDomainName': 'fcr01.TestDC' }, 'id': 1234, 'vlanNumber': 4444, 'firewallInterfaces': None, 'subnets': [], 'hardware': [ {'a_hardware': 'that_has_none_of_the_expected_attributes_provided'}, {'domain': 'example.com', 'networkManagementIpAddress': '10.171.202.131', 'hardwareStatus': {'status': 'ACTIVE', 'id': 5}, 'notes': '', 'hostname': 'hw1', 'hardwareStatusId': 5, 'globalIdentifier': 'f6ea716a-41d8-4c52-bb2e-48d63105f4b0', 'primaryIpAddress': '169.60.169.169', 'primaryBackendIpAddress': '10.171.202.130', 'id': 826425, 'privateIpAddress': '10.171.202.130', 'fullyQualifiedDomainName': 'hw1.example.com'} ] } vlan_mock.return_value = getObject result = self.run_command(['vlan', 'detail', '1234']) self.assert_no_fail(result) @mock.patch('SoftLayer.CLI.vlan.edit.click') def test_vlan_edit(self, click): result = self.run_command(['vlan', 'edit', '--name=nameTest', '--note=noteTest', '--tags=tag1,tag2', '100']) click.secho.assert_called_with('Vlan edited successfully', fg='green') self.assert_no_fail(result) self.assert_called_with('SoftLayer_Network_Vlan', 'editObject', identifier=100) @mock.patch('SoftLayer.CLI.vlan.edit.click') def test_vlan_edit_failure(self, click): mock = self.set_mock('SoftLayer_Network_Vlan', 'editObject') mock.return_value = False result = self.run_command(['vlan', 'edit', '--name=nameTest', '--note=noteTest', '--tags=tag1,tag2', '100']) click.secho.assert_called_with('Failed to edit the vlan', fg='red') self.assert_no_fail(result) self.assert_called_with('SoftLayer_Network_Vlan', 'editObject', identifier=100) def test_vlan_detail_firewall(self): vlan_mock = self.set_mock('SoftLayer_Network_Vlan', 'getObject') get_object = { 'primaryRouter': { 'datacenter': {'id': 1234, 'longName': 'TestDC'}, 'fullyQualifiedDomainName': 'fcr01.TestDC' }, 'id': 1234, 'vlanNumber': 4444, 'networkVlanFirewall': { 'datacenter': {'id': 1234, 'longName': 'TestDC'}, 'fullyQualifiedDomainName': 'fcr01.TestDC' }, } vlan_mock.return_value = get_object result = self.run_command(['vlan', 'detail', '1234']) self.assert_no_fail(result) def test_vlan_detail_gateway(self): vlan_mock = self.set_mock('SoftLayer_Network_Vlan', 'getObject') get_object = { 'primaryRouter': { 'datacenter': {'id': 1234, 'longName': 'TestDC'}, 'fullyQualifiedDomainName': 'fcr01.TestDC' }, 'id': 1234, 'vlanNumber': 4444, 'attachedNetworkGateway': { 'id': 54321, "name": 'support' }, } vlan_mock.return_value = get_object result = self.run_command(['vlan', 'detail', '1234']) self.assert_no_fail(result) def test_vlan_list(self): result = self.run_command(['vlan', 'list']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Account', 'getNetworkVlans') def test_create_vlan(self): _mock = self.set_mock('SoftLayer_Product_Package', 'getItems') _mock.return_value = SoftLayer_Product_Package.getItemsVLAN order_mock = self.set_mock('SoftLayer_Product_Order', 'placeOrder') order_mock.return_value = SoftLayer_Product_Order.vlan_placeOrder result = self.run_command(['vlan', 'create', '--name', 'test', '-d TEST00', '--network', 'public', '--billing', 'hourly' ]) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), {'id': 123456, 'created': '2021-06-02 15:23:47', 'name': 'test'}) def test_create_vlan_pod(self): _mock = self.set_mock('SoftLayer_Product_Package', 'getItems') _mock.return_value = SoftLayer_Product_Package.getItemsVLAN order_mock = self.set_mock('SoftLayer_Product_Order', 'placeOrder') order_mock.return_value = SoftLayer_Product_Order.vlan_placeOrder result = self.run_command(['vlan', 'create', '--name', 'test', '-p', 'TEST00.pod2', '--network', 'public', '--billing', 'hourly' ]) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), {'id': 123456, 'created': '2021-06-02 15:23:47', 'name': 'test'}) @mock.patch('SoftLayer.CLI.formatting.no_going_back') def test_vlan_cancel(self, confirm_mock): confirm_mock.return_value = True mock = self.set_mock('SoftLayer_Network_Vlan', 'getCancelFailureReasons') mock.return_value = [] result = self.run_command(['vlan', 'cancel', '1234']) self.assert_no_fail(result) @mock.patch('SoftLayer.CLI.formatting.no_going_back') def test_vlan_cancel_error(self, confirm_mock): confirm_mock.return_value = True result = self.run_command(['vlan', 'cancel', '1234']) self.assertTrue(result.exit_code, 2) @mock.patch('SoftLayer.CLI.formatting.no_going_back') def test_vlan_cancel_fail(self, confirm_mock): confirm_mock.return_value = False result = self.run_command(['vlan', 'cancel', '1234']) self.assertTrue(result.exit_code, 2) softlayer-python-5.9.8/tests/CLI/modules/vs/000077500000000000000000000000001415376457700207605ustar00rootroot00000000000000softlayer-python-5.9.8/tests/CLI/modules/vs/__init__.py000066400000000000000000000000001415376457700230570ustar00rootroot00000000000000softlayer-python-5.9.8/tests/CLI/modules/vs/vs_capacity_tests.py000066400000000000000000000077521415376457700250740ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.vs.vs_capacity_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import json from SoftLayer.fixtures import SoftLayer_Product_Order from SoftLayer.fixtures import SoftLayer_Product_Package from SoftLayer import testing class VSCapacityTests(testing.TestCase): def test_list(self): result = self.run_command(['vs', 'capacity', 'list']) self.assert_no_fail(result) def test_list_no_billing(self): account_mock = self.set_mock('SoftLayer_Account', 'getReservedCapacityGroups') account_mock.return_value = [ { 'id': 3103, 'name': 'test-capacity', 'createDate': '2018-09-24T16:33:09-06:00', 'availableInstanceCount': 1, 'instanceCount': 3, 'occupiedInstanceCount': 1, 'backendRouter': { 'hostname': 'bcr02a.dal13', }, 'instances': [{'id': 3501}] } ] result = self.run_command(['vs', 'capacity', 'list']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output)[0]['Flavor'], 'Unknown Billing Item') def test_detail(self): result = self.run_command(['vs', 'capacity', 'detail', '1234']) self.assert_no_fail(result) def test_detail_pending(self): # Instances don't have a billing item if they haven't been approved yet. capacity_mock = self.set_mock('SoftLayer_Virtual_ReservedCapacityGroup', 'getObject') get_object = { 'name': 'test-capacity', 'instances': [ { 'createDate': '2018-09-24T16:33:09-06:00', 'guestId': 62159257, 'id': 3501, } ] } capacity_mock.return_value = get_object result = self.run_command(['vs', 'capacity', 'detail', '1234']) self.assert_no_fail(result) def test_create_test(self): item_mock = self.set_mock('SoftLayer_Product_Package', 'getItems') item_mock.return_value = SoftLayer_Product_Package.getItems_RESERVED_CAPACITY order_mock = self.set_mock('SoftLayer_Product_Order', 'verifyOrder') order_mock.return_value = SoftLayer_Product_Order.rsc_verifyOrder result = self.run_command(['vs', 'capacity', 'create', '--name=TEST', '--test', '--backend_router_id=1234', '--flavor=B1_1X2_1_YEAR_TERM', '--instances=10']) self.assert_no_fail(result) def test_create(self): item_mock = self.set_mock('SoftLayer_Product_Package', 'getItems') item_mock.return_value = SoftLayer_Product_Package.getItems_RESERVED_CAPACITY order_mock = self.set_mock('SoftLayer_Product_Order', 'placeOrder') order_mock.return_value = SoftLayer_Product_Order.rsc_placeOrder result = self.run_command(['vs', 'capacity', 'create', '--name=TEST', '--instances=10', '--backend_router_id=1234', '--flavor=B1_1X2_1_YEAR_TERM']) self.assert_no_fail(result) def test_create_options(self): result = self.run_command(['vs', 'capacity', 'create_options']) self.assert_no_fail(result) def test_create_guest_test(self): result = self.run_command(['vs', 'capacity', 'create-guest', '--capacity-id=3103', '--primary-disk=25', '-H ABCDEFG', '-D test_list.com', '-o UBUNTU_LATEST_64', '-kTest 1', '--test']) self.assert_no_fail(result) def test_create_guest(self): order_mock = self.set_mock('SoftLayer_Product_Order', 'placeOrder') order_mock.return_value = SoftLayer_Product_Order.rsi_placeOrder result = self.run_command(['vs', 'capacity', 'create-guest', '--capacity-id=3103', '--primary-disk=25', '-H ABCDEFG', '-D test_list.com', '-o UBUNTU_LATEST_64', '-kTest 1']) self.assert_no_fail(result) softlayer-python-5.9.8/tests/CLI/modules/vs/vs_create_tests.py000066400000000000000000000765341415376457700245460ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.vs.vs_create_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import sys import tempfile from unittest import mock as mock from SoftLayer.fixtures import SoftLayer_Product_Package as SoftLayer_Product_Package from SoftLayer import testing class VirtCreateTests(testing.TestCase): @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.assertIn('"guid": "1a2b3c-1701"', result.output) self.assert_called_with('SoftLayer_Product_Order', 'placeOrder') 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', 'generateOrderTemplate', args=args) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create_vlan_subnet(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', '--billing=hourly', '--datacenter=dal05', '--vlan-private=577940', '--subnet-private=478700', '--vlan-public=1639255', '--subnet-public=297614', '--tag=dev', '--tag=green']) self.assert_no_fail(result) self.assertIn('"guid": "1a2b3c-1701"', result.output) self.assert_called_with('SoftLayer_Product_Order', 'placeOrder') args = ({ 'startCpus': 2, 'maxMemory': 1024, 'hostname': 'host', 'domain': 'example.com', 'localDiskFlag': True, 'hourlyBillingFlag': True, 'supplementalCreateObjectOptions': {'bootMode': None}, 'operatingSystemReferenceCode': 'UBUNTU_LATEST', 'datacenter': {'name': 'dal05'}, 'primaryBackendNetworkComponent': { 'networkVlan': { 'id': 577940, 'primarySubnet': {'id': 478700} } }, 'primaryNetworkComponent': { 'networkVlan': { 'id': 1639255, 'primarySubnet': {'id': 297614} } } },) self.assert_called_with('SoftLayer_Virtual_Guest', 'generateOrderTemplate', args=args) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create_by_router(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', '--billing=hourly', '--datacenter=dal05', '--router-private=577940', '--router-public=1639255', '--tag=dev', '--tag=green']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Product_Order', 'placeOrder') args = ({ 'startCpus': 2, 'maxMemory': 1024, 'hostname': 'host', 'domain': 'example.com', 'localDiskFlag': True, 'hourlyBillingFlag': True, 'supplementalCreateObjectOptions': {'bootMode': None}, 'operatingSystemReferenceCode': 'UBUNTU_LATEST', 'datacenter': {'name': 'dal05'}, 'primaryBackendNetworkComponent': { 'router': { 'id': 577940 } }, 'primaryNetworkComponent': { 'router': { 'id': 1639255 } } },) self.assert_called_with('SoftLayer_Virtual_Guest', 'generateOrderTemplate', args=args) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create_with_wait_ready(self, confirm_mock): mock = self.set_mock('SoftLayer_Virtual_Guest', 'getObject') mock.return_value = { "provisionDate": "2018-06-10T12:00:00-05:00", "id": 100 } 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', '--wait=1']) self.assert_no_fail(result) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create_with_wait_not_ready(self, confirm_mock): mock = self.set_mock('SoftLayer_Virtual_Guest', 'getObject') mock.return_value = { "ready": False, "guid": "1a2b3c-1701", "id": 100, "created": "2018-06-10 12:00:00" } 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', '--wait=1']) self.assertEqual(result.exit_code, 1) @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.assertIn('"guid": "1a2b3c-1701"', result.output) self.assert_called_with('SoftLayer_Product_Order', 'placeOrder') @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create_with_integer_image_guid(self, confirm_mock): confirm_mock.return_value = True result = self.run_command(['vs', 'create', '--cpu=2', '--domain=example.com', '--hostname=host', '--image=aaaa1111bbbb2222', '--memory=1', '--network=100', '--billing=hourly', '--datacenter=dal05']) self.assert_no_fail(result) self.assertIn('"guid": "1a2b3c-1701"', result.output) args = ({ 'startCpus': 2, 'maxMemory': 1024, 'hostname': 'host', 'domain': 'example.com', 'localDiskFlag': True, 'hourlyBillingFlag': True, 'supplementalCreateObjectOptions': {'bootMode': None}, 'blockDeviceTemplateGroup': {'globalIdentifier': 'aaaa1111bbbb2222'}, 'datacenter': {'name': 'dal05'}, 'networkComponents': [{'maxSpeed': 100}] },) self.assert_called_with('SoftLayer_Virtual_Guest', 'generateOrderTemplate', args=args) self.assert_called_with('SoftLayer_Product_Order', 'placeOrder') @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.assertIn('"guid": "1a2b3c-1701"', result.output) self.assert_called_with('SoftLayer_Product_Order', 'placeOrder') 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', 'generateOrderTemplate', args=args) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create_with_flavor_and_memory(self, confirm_mock): confirm_mock.return_value = True result = self.run_command(['vs', 'create', '--domain=example.com', '--hostname=host', '--os=UBUNTU_LATEST', '--network=100', '--datacenter=TEST00', '--flavor=BL_1X2X25', '--memory=2048MB']) self.assertEqual(result.exit_code, 2) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create_with_dedicated_and_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', '--datacenter=TEST00', '--dedicated', '--flavor=BL_1X2X25']) self.assertEqual(result.exit_code, 2) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create_with_hostid_and_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', '--datacenter=dal05', '--host-id=100', '--flavor=BL_1X2X25']) self.assertEqual(result.exit_code, 2) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create_with_flavor_and_cpu(self, confirm_mock): confirm_mock.return_value = True result = self.run_command(['vs', 'create', '--domain=example.com', '--hostname=host', '--os=UBUNTU_LATEST', '--network=100', '--datacenter=TEST00', '--flavor=BL_1X2X25', '--cpu=2']) self.assertEqual(result.exit_code, 2) @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.assertIn('"guid": "1a2b3c-1701"', result.output) # Argument testing Example order_call = self.calls('SoftLayer_Product_Order', 'placeOrder') order_args = getattr(order_call[0], 'args')[0] self.assertEqual(123, order_args['hostId']) self.assert_called_with('SoftLayer_Product_Order', 'placeOrder') template_args = ({ 'startCpus': 2, 'maxMemory': 1024, 'hostname': 'host', 'domain': 'example.com', 'localDiskFlag': True, 'hourlyBillingFlag': True, 'supplementalCreateObjectOptions': {'bootMode': None}, 'dedicatedHost': {'id': 123}, 'operatingSystemReferenceCode': 'UBUNTU_LATEST', 'datacenter': {'name': 'dal05'}, 'networkComponents': [{'maxSpeed': 100}] },) self.assert_called_with('SoftLayer_Virtual_Guest', 'generateOrderTemplate', args=template_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.assertIn('"guid": "1a2b3c-1701"', result.output) self.assert_called_with('SoftLayer_Product_Order', 'placeOrder') 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', 'generateOrderTemplate', args=args) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create_like_tags(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': {}, 'tagReferences': [{'tag': {'name': 'production'}}], } confirm_mock.return_value = True result = self.run_command(['vs', 'create', '--like=123', '--san', '--billing=hourly']) self.assert_no_fail(result) self.assertIn('"guid": "1a2b3c-1701"', result.output) self.assert_called_with('SoftLayer_Product_Order', 'placeOrder') _args = ('production',) self.assert_called_with('SoftLayer_Virtual_Guest', 'setTags', identifier=1234567, args=_args) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create_like_image(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': {}}}, 'blockDeviceTemplateGroup': {'globalIdentifier': 'aaa1xxx1122233'}, '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.assertIn('"guid": "1a2b3c-1701"', result.output) self.assert_called_with('SoftLayer_Product_Order', 'placeOrder') args = ({'datacenter': {'name': 'dal05'}, 'domain': 'test.sftlyr.ws', 'hourlyBillingFlag': True, 'hostname': 'vs-test-like', 'startCpus': 2, 'maxMemory': 1024, 'localDiskFlag': False, 'blockDeviceTemplateGroup': {'globalIdentifier': 'aaa1xxx1122233'}, 'networkComponents': [{'maxSpeed': 100}], 'supplementalCreateObjectOptions': {'bootMode': None}},) self.assert_called_with('SoftLayer_Virtual_Guest', 'generateOrderTemplate', 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.assertIn('"guid": "1a2b3c-1701"', result.output) self.assert_called_with('SoftLayer_Product_Order', 'placeOrder') 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', 'generateOrderTemplate', args=args) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create_like_transient(self, confirm_mock): mock = self.set_mock('SoftLayer_Virtual_Guest', 'getObject') mock.return_value = { 'hostname': 'vs-test-like', 'domain': 'test.sftlyr.ws', '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, 'transientGuestFlag': True, 'userData': {} } confirm_mock.return_value = True result = self.run_command(['vs', 'create', '--like=123']) self.assert_no_fail(result) self.assertIn('"guid": "1a2b3c-1701"', result.output) self.assert_called_with('SoftLayer_Product_Order', 'placeOrder') args = ({'datacenter': {'name': 'dal05'}, 'domain': 'test.sftlyr.ws', 'hourlyBillingFlag': True, 'hostname': 'vs-test-like', 'startCpus': None, 'maxMemory': None, 'localDiskFlag': None, 'transientGuestFlag': True, 'supplementalCreateObjectOptions': { 'bootMode': None, 'flavorKeyName': 'B1_1X2X25'}, 'operatingSystemReferenceCode': 'UBUNTU_LATEST', 'networkComponents': [{'maxSpeed': 100}]},) self.assert_called_with('SoftLayer_Virtual_Guest', 'generateOrderTemplate', args=args) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create_vs_test(self, confirm_mock): confirm_mock.return_value = True result = self.run_command(['vs', 'create', '--test', '--hostname', 'TEST', '--domain', 'TESTING', '--cpu', '1', '--memory', '2048MB', '--datacenter', 'TEST00', '--os', 'UBUNTU_LATEST']) self.assertEqual(result.exit_code, 0) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create_vs_flavor_test(self, confirm_mock): confirm_mock.return_value = True result = self.run_command(['vs', 'create', '--test', '--hostname', 'TEST', '--domain', 'TESTING', '--flavor', 'B1_2X8X25', '--datacenter', 'TEST00', '--os', 'UBUNTU_LATEST']) self.assert_no_fail(result) self.assertEqual(result.exit_code, 0) def test_create_vs_bad_memory(self): result = self.run_command(['vs', 'create', '--hostname', 'TEST', '--domain', 'TESTING', '--cpu', '1', '--memory', '2034MB', '--flavor', 'B1_2X8X25', '--datacenter', 'TEST00']) self.assertEqual(2, result.exit_code) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create_vs_transient(self, confirm_mock): confirm_mock.return_value = True result = self.run_command(['vs', 'create', '--hostname', 'TEST', '--domain', 'TESTING', '--flavor', 'B1_2X8X25', '--datacenter', 'TEST00', '--transient', '--os', 'UBUNTU_LATEST']) self.assert_no_fail(result) self.assertEqual(0, result.exit_code) def test_create_vs_bad_transient_monthly(self): result = self.run_command(['vs', 'create', '--hostname', 'TEST', '--domain', 'TESTING', '--flavor', 'B1_2X8X25', '--datacenter', 'TEST00', '--transient', '--billing', 'monthly', '--os', 'UBUNTU_LATEST']) self.assertEqual(2, result.exit_code) def test_create_vs_bad_transient_dedicated(self): result = self.run_command(['vs', 'create', '--hostname', 'TEST', '--domain', 'TESTING', '--flavor', 'B1_2X8X25', '--datacenter', 'TEST00', '--transient', '--dedicated', '--os', 'UBUNTU_LATEST']) self.assertEqual(2, result.exit_code) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create_with_ipv6(self, confirm_mock): amock = self.set_mock('SoftLayer_Product_Package', 'getItems') amock.return_value = SoftLayer_Product_Package.getItems_1_IPV6_ADDRESS result = self.run_command(['vs', 'create', '--test', '--hostname', 'TEST', '--domain', 'TESTING', '--flavor', 'B1_2X8X25', '--datacenter', 'TEST00', '--os', 'UBUNTU_LATEST', '--ipv6']) self.assert_no_fail(result) self.assertEqual(result.exit_code, 0) self.assert_called_with('SoftLayer_Product_Order', 'verifyOrder') args = ({ 'startCpus': None, 'maxMemory': None, 'hostname': 'TEST', 'domain': 'TESTING', 'localDiskFlag': None, 'hourlyBillingFlag': True, 'supplementalCreateObjectOptions': { 'bootMode': None, 'flavorKeyName': 'B1_2X8X25' }, 'operatingSystemReferenceCode': 'UBUNTU_LATEST', 'datacenter': { 'name': 'TEST00' } }, ) self.assert_called_with('SoftLayer_Virtual_Guest', 'generateOrderTemplate', args=args) self.assertEqual([], self.calls('SoftLayer_Virtual_Guest', 'setTags')) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create_with_ipv6_no_test(self, confirm_mock): confirm_mock.return_value = True amock = self.set_mock('SoftLayer_Product_Package', 'getItems') amock.return_value = SoftLayer_Product_Package.getItems_1_IPV6_ADDRESS result = self.run_command(['vs', 'create', '--hostname', 'TEST', '--domain', 'TESTING', '--flavor', 'B1_2X8X25', '--datacenter', 'TEST00', '--os', 'UBUNTU_LATEST', '--ipv6']) self.assert_no_fail(result) self.assertEqual(result.exit_code, 0) self.assert_called_with('SoftLayer_Product_Order', 'placeOrder') self.assertEqual([], self.calls('SoftLayer_Virtual_Guest', 'setTags')) @mock.patch('SoftLayer.CLI.formatting.no_going_back') def test_create_with_ipv6_no_prices(self, confirm_mock): """Test makes sure create fails if ipv6 price cannot be found. Since its hard to test if the price ids gets added to placeOrder call, this test juse makes sure that code block isn't being skipped """ confirm_mock.return_value = True amock = self.set_mock('SoftLayer_Product_Package', 'getItems') amock.return_value = SoftLayer_Product_Package.getItemsVS result = self.run_command(['vs', 'create', '--test', '--hostname', 'TEST', '--domain', 'TESTING', '--flavor', 'B1_2X8X25', '--datacenter', 'TEST00', '--os', 'UBUNTU_LATEST', '--ipv6']) self.assertEqual(result.exit_code, 1) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create_vs_no_confirm(self, confirm_mock): confirm_mock.return_value = False result = self.run_command(['vs', 'create', '--hostname', 'TEST', '--domain', 'TESTING', '--flavor', 'B1_2X8X25', '--datacenter', 'TEST00', '--os', 'UBUNTU_LATEST']) self.assertEqual(result.exit_code, 2) def test_create_vs_export(self): if(sys.platform.startswith("win")): self.skipTest("Test doesn't work in Windows") with tempfile.NamedTemporaryFile() as config_file: result = self.run_command(['vs', 'create', '--hostname', 'TEST', '--export', config_file.name, '--domain', 'TESTING', '--flavor', 'B1_2X8X25', '--datacenter', 'TEST00', '--os', 'UBUNTU_LATEST']) self.assert_no_fail(result) self.assertIn('Successfully exported options to a template file.', result.output) contents = config_file.read().decode("utf-8") self.assertIn('hostname=TEST', contents) self.assertIn('flavor=B1_2X8X25', contents) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create_with_userdata(self, confirm_mock): result = self.run_command(['vs', 'create', '--hostname', 'TEST', '--domain', 'TESTING', '--flavor', 'B1_2X8X25', '--datacenter', 'TEST00', '--os', 'UBUNTU_LATEST', '--userdata', 'This is my user data ok']) self.assert_no_fail(result) expected_guest = [ { 'domain': 'test.local', 'hostname': 'test', 'userData': [{'value': 'This is my user data ok'}] } ] # Returns a list of API calls that hit SL_Product_Order::placeOrder api_call = self.calls('SoftLayer_Product_Order', 'placeOrder') # Doing this because the placeOrder args are huge and mostly not needed to test self.assertEqual(api_call[0].args[0]['virtualGuests'], expected_guest) softlayer-python-5.9.8/tests/CLI/modules/vs/vs_placement_tests.py000066400000000000000000000116741415376457700252450ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.vs_placement_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ from unittest import mock as mock from SoftLayer import testing class VSPlacementTests(testing.TestCase): def test_create_options(self): result = self.run_command(['vs', 'placementgroup', 'create-options']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Virtual_PlacementGroup', 'getAvailableRouters') self.assert_called_with('SoftLayer_Virtual_PlacementGroup_Rule', 'getAllObjects') self.assertEqual([], self.calls('SoftLayer_Virtual_PlacementGroup', 'createObject')) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_create_group(self, confirm_mock): confirm_mock.return_value = True result = self.run_command(['vs', 'placementgroup', 'create', '--name=test', '--backend_router=1', '--rule=2']) create_args = { 'name': 'test', 'backendRouterId': 1, 'ruleId': 2 } self.assert_no_fail(result) self.assert_called_with('SoftLayer_Virtual_PlacementGroup', 'createObject', args=(create_args,)) self.assertEqual([], self.calls('SoftLayer_Virtual_PlacementGroup', 'getAvailableRouters')) def test_list_groups(self): result = self.run_command(['vs', 'placementgroup', 'list']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Account', 'getPlacementGroups') def test_detail_group_id(self): result = self.run_command(['vs', 'placementgroup', 'detail', '12345']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Virtual_PlacementGroup', 'getObject', identifier=12345) def test_detail_group_name(self): result = self.run_command(['vs', 'placementgroup', 'detail', 'test']) self.assert_no_fail(result) group_filter = { 'placementGroups': { 'name': {'operation': 'test'} } } self.assert_called_with('SoftLayer_Account', 'getPlacementGroups', filter=group_filter) self.assert_called_with('SoftLayer_Virtual_PlacementGroup', 'getObject', identifier=12345) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_delete_group_id(self, confirm_mock): confirm_mock.return_value = True result = self.run_command(['vs', 'placementgroup', 'delete', '12345']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Virtual_PlacementGroup', 'deleteObject', identifier=12345) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_delete_group_id_cancel(self, confirm_mock): confirm_mock.return_value = False result = self.run_command(['vs', 'placementgroup', 'delete', '12345']) self.assertEqual(result.exit_code, 2) self.assertEqual([], self.calls('SoftLayer_Virtual_PlacementGroup', 'deleteObject')) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_delete_group_name(self, confirm_mock): confirm_mock.return_value = True result = self.run_command(['vs', 'placementgroup', 'delete', 'test']) group_filter = { 'placementGroups': { 'name': {'operation': 'test'} } } self.assert_no_fail(result) self.assert_called_with('SoftLayer_Account', 'getPlacementGroups', filter=group_filter) self.assert_called_with('SoftLayer_Virtual_PlacementGroup', 'deleteObject', identifier=12345) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_delete_group_purge(self, confirm_mock): confirm_mock.return_value = True result = self.run_command(['vs', 'placementgroup', 'delete', '1234', '--purge']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Virtual_PlacementGroup', 'getObject') self.assert_called_with('SoftLayer_Virtual_Guest', 'deleteObject', identifier=69131875) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_delete_group_purge_cancel(self, confirm_mock): confirm_mock.return_value = False result = self.run_command(['vs', 'placementgroup', 'delete', '1234', '--purge']) self.assertEqual(result.exit_code, 2) self.assertEqual([], self.calls('SoftLayer_Virtual_Guest', 'deleteObject')) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_delete_group_purge_nothing(self, confirm_mock): group_mock = self.set_mock('SoftLayer_Virtual_PlacementGroup', 'getObject') group_mock.return_value = { "id": 1234, "name": "test-group", "guests": [], } confirm_mock.return_value = True result = self.run_command(['vs', 'placementgroup', 'delete', '1234', '--purge']) self.assertEqual(result.exit_code, 2) self.assert_called_with('SoftLayer_Virtual_PlacementGroup', 'getObject') self.assertEqual([], self.calls('SoftLayer_Virtual_Guest', 'deleteObject')) softlayer-python-5.9.8/tests/CLI/modules/vs/vs_tests.py000066400000000000000000001153411415376457700232110ustar00rootroot00000000000000""" SoftLayer.tests.CLI.modules.vs_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import json import sys from unittest import mock as mock from SoftLayer.CLI import exceptions from SoftLayer.fixtures import SoftLayer_Virtual_Guest as SoftLayer_Virtual_Guest from SoftLayer import SoftLayerAPIError from SoftLayer import SoftLayerError from SoftLayer import testing class VirtTests(testing.TestCase): @mock.patch('SoftLayer.CLI.formatting.confirm') def test_rescue_vs(self, confirm_mock): confirm_mock.return_value = True result = self.run_command(['vs', 'rescue', '100']) self.assert_no_fail(result) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_rescue_vs_no_confirm(self, confirm_mock): confirm_mock.return_value = False result = self.run_command(['vs', 'rescue', '100']) self.assertEqual(result.exit_code, 2) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_reboot_vs_default(self, confirm_mock): mock = self.set_mock('SoftLayer_Virtual_Guest', 'rebootDefault') mock.return_value = 'true' confirm_mock.return_value = True result = self.run_command(['vs', 'reboot', '100']) self.assert_no_fail(result) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_reboot_vs_no_confirm(self, confirm_mock): mock = self.set_mock('SoftLayer_Virtual_Guest', 'rebootDefault') mock.return_value = 'true' confirm_mock.return_value = False result = self.run_command(['vs', 'reboot', '100']) self.assertEqual(result.exit_code, 2) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_reboot_vs_soft(self, confirm_mock): mock = self.set_mock('SoftLayer_Virtual_Guest', 'rebootSoft') mock.return_value = 'true' confirm_mock.return_value = True result = self.run_command(['vs', 'reboot', '--soft', '100']) self.assert_no_fail(result) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_reboot_vs_hard(self, confirm_mock): mock = self.set_mock('SoftLayer_Virtual_Guest', 'rebootHard') mock.return_value = 'true' confirm_mock.return_value = True result = self.run_command(['vs', 'reboot', '--hard', '100']) self.assert_no_fail(result) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_power_vs_off_soft(self, confirm_mock): mock = self.set_mock('SoftLayer_Virtual_Guest', 'powerOffSoft') mock.return_value = 'true' confirm_mock.return_value = True result = self.run_command(['vs', 'power-off', '100']) self.assert_no_fail(result) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_power_off_vs_no_confirm(self, confirm_mock): mock = self.set_mock('SoftLayer_Virtual_Guest', 'powerOffSoft') mock.return_value = 'true' confirm_mock.return_value = False result = self.run_command(['vs', 'power-off', '100']) self.assertEqual(result.exit_code, 2) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_power_off_vs_hard(self, confirm_mock): mock = self.set_mock('SoftLayer_Virtual_Guest', 'powerOff') mock.return_value = 'true' confirm_mock.return_value = True result = self.run_command(['vs', 'power-off', '--hard', '100']) self.assert_no_fail(result) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_power_on_vs(self, confirm_mock): mock = self.set_mock('SoftLayer_Virtual_Guest', 'powerOn') mock.return_value = 'true' confirm_mock.return_value = True result = self.run_command(['vs', 'power-on', '100']) self.assert_no_fail(result) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_pause_vs(self, confirm_mock): mock = self.set_mock('SoftLayer_Virtual_Guest', 'pause') mock.return_value = 'true' confirm_mock.return_value = True result = self.run_command(['vs', 'pause', '100']) self.assert_no_fail(result) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_pause_vs_no_confirm(self, confirm_mock): mock = self.set_mock('SoftLayer_Virtual_Guest', 'pause') mock.return_value = 'true' confirm_mock.return_value = False result = self.run_command(['vs', 'pause', '100']) self.assertEqual(result.exit_code, 2) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_resume_vs(self, confirm_mock): mock = self.set_mock('SoftLayer_Virtual_Guest', 'resume') mock.return_value = 'true' confirm_mock.return_value = True result = self.run_command(['vs', 'resume', '100']) self.assert_no_fail(result) def test_list_vs(self): result = self.run_command(['vs', 'list', '--tag=tag']) self.assert_no_fail(result) @mock.patch('SoftLayer.utils.lookup') def test_detail_vs_empty_billing(self, mock_lookup): def mock_lookup_func(dic, key, *keys): if key == 'billingItem': return [] if keys: return mock_lookup_func(dic.get(key, {}), keys[0], *keys[1:]) return dic.get(key) mock_lookup.side_effect = mock_lookup_func result = self.run_command(['vs', 'detail', '100', '--passwords', '--price']) self.assert_no_fail(result) output = json.loads(result.output) self.assertEqual(output['owner'], None) def test_detail_vs(self): result = self.run_command(['vs', 'detail', '100', '--passwords', '--price']) self.assert_no_fail(result) output = json.loads(result.output) self.assertEqual(output['notes'], 'notes') self.assertEqual(output['price_rate'], 6.54) self.assertEqual(output['users'][0]['username'], 'user') self.assertEqual(output['vlans'][0]['number'], 23) self.assertEqual(output['owner'], 'chechu') self.assertEqual(output['Bandwidth'][0]['Allotment'], '250') 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_empty_allotment(self): mock = self.set_mock('SoftLayer_Virtual_Guest', 'getBandwidthAllotmentDetail') mock.return_value = None result = self.run_command(['vs', 'detail', '100']) self.assert_no_fail(result) self.assertEqual( json.loads(result.output)['Bandwidth'][0]['Allotment'], '-', ) def test_detail_drives_system(self): mock = self.set_mock('SoftLayer_Virtual_Guest', 'getBlockDevices') mock.return_value = [ { "createDate": "2018-10-06T04:27:35-06:00", "device": "0", "id": 11111, "mountType": "Disk", "diskImage": { "capacity": 100, "description": "adns.vmware.com", "id": 72222, "name": "adns.vmware.com", "units": "GB", } } ] result = self.run_command(['vs', 'detail', '100']) self.assert_no_fail(result) output = json.loads(result.output) self.assertEqual(output['drives'][0]['Capacity'], '100 GB') self.assertEqual(output['drives'][0]['Name'], 'Disk') self.assertEqual(output['drives'][0]['Type'], 'System') def test_detail_drives_swap(self): mock = self.set_mock('SoftLayer_Virtual_Guest', 'getBlockDevices') mock.return_value = [ { "device": "1", "id": 22222, "mountType": "Disk", "statusId": 1, "diskImage": { "capacity": 2, "description": "6211111-SWAP", "id": 33333, "name": "6211111-SWAP", "units": "GB", } } ] result = self.run_command(['vs', 'detail', '100']) self.assert_no_fail(result) output = json.loads(result.output) self.assertEqual(output['drives'][0]['Capacity'], '2 GB') self.assertEqual(output['drives'][0]['Name'], 'Disk') self.assertEqual(output['drives'][0]['Type'], 'Swap') 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_detail_vs_security_group(self): vg_return = SoftLayer_Virtual_Guest.getObject sec_group = [ { 'id': 35386715, 'name': 'eth', 'port': 0, 'speed': 100, 'status': 'ACTIVE', 'primaryIpAddress': '10.175.106.149', 'securityGroupBindings': [ { 'id': 1620971, 'networkComponentId': 35386715, 'securityGroupId': 128321, 'securityGroup': { 'id': 128321, 'name': 'allow_all' } } ] } ] vg_return['networkComponents'] = sec_group mock = self.set_mock('SoftLayer_Virtual_Guest', 'getObject') mock.return_value = vg_return result = self.run_command(['vs', 'detail', '100']) self.assert_no_fail(result) output = json.loads(result.output) self.assertEqual(output['security_groups'][0]['id'], 128321) self.assertEqual(output['security_groups'][0]['name'], 'allow_all') self.assertEqual(output['security_groups'][0]['interface'], 'PRIVATE') def test_detail_vs_ptr_error(self): mock = self.set_mock('SoftLayer_Virtual_Guest', 'getReverseDomainRecords') mock.side_effect = SoftLayerAPIError("SoftLayer_Exception", "Not Found") result = self.run_command(['vs', 'detail', '100']) self.assert_no_fail(result) output = json.loads(result.output) self.assertEqual(output.get('ptr', None), None) def test_create_options(self): result = self.run_command(['vs', 'create-options', '--vsi-type', 'TRANSIENT_CLOUD_SERVER']) self.assert_no_fail(result) def test_create_options_prices(self): result = self.run_command(['vs', 'create-options', '--prices', '--vsi-type', 'TRANSIENT_CLOUD_SERVER']) self.assert_no_fail(result) def test_create_options_prices_location(self): result = self.run_command(['vs', 'create-options', '--prices', 'dal13', '--vsi-type', 'TRANSIENT_CLOUD_SERVER']) self.assert_no_fail(result) @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': 12345, # from SoftLayer_Account::getDomains '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': 12345, '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, 1) self.assertIsInstance(result.exception, SoftLayerError) @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, 1) self.assertIsInstance(result.exception, SoftLayerError) @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}]) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_upgrade_disk(self, confirm_mock): confirm_mock.return_value = True result = self.run_command(['vs', 'upgrade', '100', '--flavor=M1_64X512X100', '--resize-disk=10', '1', '--resize-disk=10', '2']) 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.assertEqual(799, order_container['presetId']) self.assertIn({'id': 100}, order_container['virtualGuests']) self.assertEqual(order_container['virtualGuests'], [{'id': 100}]) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_upgrade_disk_error(self, confirm_mock): confirm_mock.return_value = True result = self.run_command(['vs', 'upgrade', '100', '--flavor=M1_64X512X100', '--resize-disk=1000', '1', '--resize-disk=10', '2']) self.assertEqual(result.exit_code, 1) self.assertIsInstance(result.exception, SoftLayerAPIError) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_upgrade_with_flavor(self, confirm_mock): confirm_mock.return_value = True result = self.run_command(['vs', 'upgrade', '100', '--flavor=M1_64X512X100']) 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.assertEqual(799, order_container['presetId']) self.assertIn({'id': 100}, order_container['virtualGuests']) self.assertEqual(order_container['virtualGuests'], [{'id': 100}]) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_upgrade_with_add_disk(self, confirm_mock): confirm_mock.return_value = True result = self.run_command(['vs', 'upgrade', '100', '--add-disk=10', '--add-disk=10']) 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': 100}, order_container['virtualGuests']) self.assertEqual(order_container['virtualGuests'], [{'id': 100}]) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_upgrade_with_cpu_memory_and_flavor(self, confirm_mock): confirm_mock.return_value = True result = self.run_command(['vs', 'upgrade', '100', '--cpu=4', '--memory=1024', '--flavor=M1_64X512X100']) self.assertEqual(result.exit_code, 1) self.assertIsInstance(result.exception, ValueError) 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') @mock.patch('SoftLayer.CLI.formatting.no_going_back') def test_reload(self, confirm_mock): mock = self.set_mock('SoftLayer_Virtual_Guest', 'reloadCurrentOperatingSystemConfguration') confirm_mock.return_value = True mock.return_value = 'true' result = self.run_command(['vs', 'reload', '--postinstall', '100', '--key', '100', '--image', '100', '100']) self.assert_no_fail(result) @mock.patch('SoftLayer.CLI.formatting.no_going_back') def test_reload_no_confirm(self, confirm_mock): mock = self.set_mock('SoftLayer_Virtual_Guest', 'reloadCurrentOperatingSystemConfiguration') confirm_mock.return_value = False mock.return_value = 'false' result = self.run_command(['vs', 'reload', '--postinstall', '100', '--key', '100', '--image', '100', '100']) self.assertEqual(result.exit_code, 2) @mock.patch('SoftLayer.CLI.formatting.no_going_back') def test_cancel(self, confirm_mock): confirm_mock.return_value = True result = self.run_command(['vs', 'cancel', '100']) self.assert_no_fail(result) @mock.patch('SoftLayer.CLI.formatting.no_going_back') def test_cancel_no_confirm(self, confirm_mock): confirm_mock.return_value = False result = self.run_command(['vs', 'cancel', '100']) self.assertEqual(result.exit_code, 2) def test_vs_capture(self): result = self.run_command(['vs', 'capture', '100', '--name', 'TestName']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Virtual_Guest', 'createArchiveTransaction', identifier=100) @mock.patch('SoftLayer.CLI.formatting.no_going_back') def test_usage_no_confirm(self, confirm_mock): confirm_mock.return_value = False result = self.run_command(['vs', 'usage', '100']) self.assertEqual(result.exit_code, 2) def test_usage_vs(self): result = self.run_command( ['vs', 'usage', '100']) self.assertEqual(result.exit_code, 2) def test_usage_vs_cpu(self): result = self.run_command( ['vs', 'usage', '100', '--start_date=2019-3-4', '--end_date=2019-4-2', '--valid_type=CPU0', '--summary_period=300']) self.assert_no_fail(result) def test_usage_vs_cpu_lower_case(self): result = self.run_command( ['vs', 'usage', '100', '--start_date=2019-3-4', '--end_date=2019-4-2', '--valid_type=cpu0', '--summary_period=300']) self.assert_no_fail(result) def test_usage_vs_memory(self): result = self.run_command( ['vs', 'usage', '100', '--start_date=2019-3-4', '--end_date=2019-4-2', '--valid_type=MEMORY_USAGE', '--summary_period=300']) self.assert_no_fail(result) def test_usage_metric_data_empty(self): usage_vs = self.set_mock('SoftLayer_Metric_Tracking_Object', 'getSummaryData') test_usage = [] usage_vs.return_value = test_usage result = self.run_command( ['vs', 'usage', '100', '--start_date=2019-3-4', '--end_date=2019-4-2', '--valid_type=CPU0', '--summary_period=300']) self.assertEqual(result.exit_code, 2) self.assertIsInstance(result.exception, exceptions.CLIAbort) def test_bandwidth_vs(self): self.skipTest("Skipping until VIRT-11733 is released") if sys.version_info < (3, 6): self.skipTest("Test requires python 3.6+") result = self.run_command(['vs', 'bandwidth', '100', '--start_date=2019-01-01', '--end_date=2019-02-01']) self.assert_no_fail(result) date = '2019-05-20 23:00' # number of characters from the end of output to break so json can parse properly pivot = 157 # only pyhon 3.7 supports the timezone format slapi uses if sys.version_info < (3, 7): date = '2019-05-20T23:00:00-06:00' pivot = 166 # Since this is 2 tables, it gets returned as invalid json like "[{}][{}]"" instead of "[[{}],[{}]]" # so we just do some hacky string substitution to pull out the respective arrays that can be jsonifyied output_summary = json.loads(result.output[0:-pivot]) output_list = json.loads(result.output[-pivot:]) self.assertEqual(output_summary[0]['Average MBps'], 0.3841) self.assertEqual(output_summary[1]['Max Date'], date) self.assertEqual(output_summary[2]['Max GB'], 0.1172) self.assertEqual(output_summary[3]['Sum GB'], 0.0009) self.assertEqual(output_list[0]['Date'], date) self.assertEqual(output_list[0]['Pub In'], 1.3503) def test_bandwidth_vs_quite(self): self.skipTest("Skipping until VIRT-11733 is released") result = self.run_command(['vs', 'bandwidth', '100', '--start_date=2019-01-01', '--end_date=2019-02-01', '-q']) self.assert_no_fail(result) date = '2019-05-20 23:00' # only pyhon 3.7 supports the timezone format slapi uses if sys.version_info < (3, 7): date = '2019-05-20T23:00:00-06:00' output_summary = json.loads(result.output) self.assertEqual(output_summary[0]['Average MBps'], 0.3841) self.assertEqual(output_summary[1]['Max Date'], date) self.assertEqual(output_summary[2]['Max GB'], 0.1172) self.assertEqual(output_summary[3]['Sum GB'], 0.0009) def test_vs_storage(self): result = self.run_command( ['vs', 'storage', '100']) self.assert_no_fail(result) def test_billing(self): result = self.run_command(['vs', 'billing', '123456']) vir_billing = { 'Billing Item Id': 6327, 'Id': '123456', 'Provision Date': None, 'Recurring Fee': None, 'Total': 1.54, 'prices': [ {'Description': '1 GB', 'Recurring Price': 1}, {'Description': 'Reboot / Remote Console', 'Recurring Price': 1}, {'Description': '1 Gbps Public & Private Network Uplinks', 'Recurring Price': 1}, {'Description': '1 Gbps Public Uplink', 'Recurring Price': 1}, {'Description': '1 Gbps Private Uplink', 'Recurring Price': 1} ] } self.assert_no_fail(result) self.assertEqual(json.loads(result.output), vir_billing) def test_vs_migrate_list(self): result = self.run_command(['vs', 'migrate']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Account', 'getVirtualGuests') self.assert_not_called_with('SoftLayer_Virtual_Guest', 'migrate') self.assert_not_called_with('SoftLayer_Virtual_Guest', 'migrateDedicatedHost') def test_vs_migrate_list_empty(self): mock = self.set_mock('SoftLayer_Account', 'getVirtualGuests') mock.return_value = [] result = self.run_command(['vs', 'migrate']) self.assert_no_fail(result) self.assert_called_with('SoftLayer_Account', 'getVirtualGuests') self.assert_not_called_with('SoftLayer_Virtual_Guest', 'migrate') self.assert_not_called_with('SoftLayer_Virtual_Guest', 'migrateDedicatedHost') self.assertIn("No guests require migration at this time", result.output) def test_vs_migrate_guest(self): result = self.run_command(['vs', 'migrate', '-g', '100']) self.assert_no_fail(result) self.assertIn('Started a migration on', result.output) self.assert_not_called_with('SoftLayer_Account', 'getVirtualGuests') self.assert_called_with('SoftLayer_Virtual_Guest', 'migrate', identifier=100) self.assert_not_called_with('SoftLayer_Virtual_Guest', 'migrateDedicatedHost') def test_vs_migrate_all(self): result = self.run_command(['vs', 'migrate', '-a']) self.assert_no_fail(result) self.assertIn('Started a migration on', result.output) self.assert_called_with('SoftLayer_Virtual_Guest', 'migrate', identifier=100) self.assert_called_with('SoftLayer_Virtual_Guest', 'migrate', identifier=104) self.assert_not_called_with('SoftLayer_Virtual_Guest', 'migrateDedicatedHost') def test_vs_migrate_all_empty(self): mock = self.set_mock('SoftLayer_Account', 'getVirtualGuests') mock.return_value = [] result = self.run_command(['vs', 'migrate', '-a']) self.assert_no_fail(result) self.assertIn('No guests require migration at this time', result.output) def test_vs_migrate_dedicated(self): result = self.run_command(['vs', 'migrate', '-g', '100', '-h', '999']) self.assert_no_fail(result) self.assertIn('Started a migration on', result.output) self.assert_not_called_with('SoftLayer_Account', 'getVirtualGuests') self.assert_not_called_with('SoftLayer_Virtual_Guest', 'migrate', identifier=100) self.assert_not_called_with('SoftLayer_Virtual_Guest', 'migrateDedicatedHost', args=(999), identifier=100) def test_vs_migrate_exception(self): ex = SoftLayerAPIError('SoftLayer_Exception', 'PROBLEM') mock = self.set_mock('SoftLayer_Virtual_Guest', 'migrate') mock.side_effect = ex result = self.run_command(['vs', 'migrate', '-g', '100']) self.assert_no_fail(result) self.assertIn('Failed to migrate', result.output) self.assert_not_called_with('SoftLayer_Account', 'getVirtualGuests') self.assert_called_with('SoftLayer_Virtual_Guest', 'migrate', identifier=100) self.assert_not_called_with('SoftLayer_Virtual_Guest', 'migrateDedicatedHost', args=(999), identifier=100) def test_list_vsi(self): result = self.run_command(['vs', 'list', '--hardware']) self.assert_no_fail(result) def test_credentail(self): result = self.run_command(['vs', 'credentials', '100']) self.assert_no_fail(result) self.assertEqual(json.loads(result.output), [{ "username": "user", "password": "pass" }]) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_authorize_storage_vs_no_confirm(self, confirm_mock): confirm_mock.return_value = False result = self.run_command(['vs', 'authorize-storage', '-u', '1234']) self.assertEqual(result.exit_code, 2) @mock.patch('SoftLayer.CLI.formatting.confirm') def test_authorize_vs_empty(self, confirm_mock): confirm_mock.return_value = True storage_result = self.set_mock('SoftLayer_Account', 'getNetworkStorage') storage_result.return_value = [] result = self.run_command(['vs', 'authorize-storage', '--username-storage=#', '1234']) self.assertEqual(str(result.exception), "The Storage with username: # was not found, " "please enter a valid storage username") def test_authorize_storage_vs(self): result = self.run_command(['vs', 'authorize-storage', '--username-storage=SL01SEL301234-11', '1234']) self.assert_no_fail(result) def test_authorize_portable_storage_vs(self): mock = self.set_mock('SoftLayer_Virtual_Guest', 'attachDiskImage') mock.return_value = { "createDate": "2021-03-22T13:15:31-06:00", "id": 1234567 } result = self.run_command(['vs', 'authorize-storage', '--portable-id=12345', '1234']) self.assert_no_fail(result) def test_authorize_volume_and_portable_storage_vs(self): result = self.run_command(['vs', 'authorize-storage', '--username-storage=SL01SEL301234-11', '--portable-id=12345', '1234']) self.assert_no_fail(result) softlayer-python-5.9.8/tests/__init__.py000066400000000000000000000000001415376457700203500ustar00rootroot00000000000000softlayer-python-5.9.8/tests/api_tests.py000066400000000000000000000277441415376457700206340ustar00rootroot00000000000000""" SoftLayer.tests.api_tests ~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ from unittest import mock as mock import SoftLayer import SoftLayer.API from SoftLayer import testing from SoftLayer import transports class Initialization(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_simple_call_2(self): mock = self.set_mock('SoftLayer_SERVICE', 'METHOD') mock.return_value = {"test": "result"} resp = self.client.call('SERVICE', 'METHOD', {'networkComponents': [{'maxSpeed': 100}]}) self.assertEqual(resp, {"test": "result"}) self.assert_called_with('SoftLayer_SERVICE', 'METHOD', mask=None, filter=None, identifier=None, args=({'networkComponents': [{'maxSpeed': 100}]},), 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 = [ transports.SoftLayerListResult(range(100), 125), transports.SoftLayerListResult(range(100, 125), 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 = [ transports.SoftLayerListResult(range(100), 201), transports.SoftLayerListResult(range(100, 200), 201), transports.SoftLayerListResult([], 201) ] 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 = [ transports.SoftLayerListResult(range(0, 25), 30), transports.SoftLayerListResult(range(25, 30), 30) ] result = list(self.client.iter_call( 'SERVICE', 'METHOD', iter=True, limit=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=25, 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() _call.side_effect = [ transports.SoftLayerListResult(range(0, 25), 30), transports.SoftLayerListResult(range(25, 30), 30) ] result = list(self.client.iter_call('SERVICE', 'METHOD', 'ARG', iter=True, limit=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=25, offset=37), ]) # Chunk size of 0 is invalid self.assertRaises( AttributeError, lambda: list(self.client.iter_call('SERVICE', 'METHOD', iter=True, limit=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') def test_special_services(self): # Tests for the special classes that don't need to start with SoftLayer_ self.client.call('BluePages_Search', 'findBluePagesProfile') self.assert_called_with('BluePages_Search', 'findBluePagesProfile') 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.9.8/tests/auth_tests.py000066400000000000000000000050301415376457700210040ustar00rootroot00000000000000""" 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.9.8/tests/basic_tests.py000066400000000000000000000143761415376457700211410ustar00rootroot00000000000000""" 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()) def test_dict_merge(self): filter1 = {"virtualGuests": {"hostname": {"operation": "etst"}}} filter2 = {"virtualGuests": {"id": {"operation": "orderBy", "options": [{"name": "sort", "value": ["DESC"]}]}}} result = SoftLayer.utils.dict_merge(filter1, filter2) self.assertEqual(result['virtualGuests']['id']['operation'], 'orderBy') self.assertNotIn('id', filter1['virtualGuests']) self.assertEqual(result['virtualGuests']['hostname']['operation'], 'etst') 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.9.8/tests/config_tests.py000066400000000000000000000071231415376457700213150ustar00rootroot00000000000000""" SoftLayer.tests.config_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ from unittest import mock as 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('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('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('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.9.8/tests/conftest.py000066400000000000000000000000711415376457700204460ustar00rootroot00000000000000import logging logging.basicConfig(level=logging.DEBUG) softlayer-python-5.9.8/tests/decoration_tests.py000066400000000000000000000052121415376457700221740ustar00rootroot00000000000000""" SoftLayer.tests.decoration_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import logging from unittest import mock as 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.9.8/tests/functional_tests.py000066400000000000000000000066101415376457700222120ustar00rootroot00000000000000""" 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.9.8/tests/managers/000077500000000000000000000000001415376457700200465ustar00rootroot00000000000000softlayer-python-5.9.8/tests/managers/__init__.py000066400000000000000000000000001415376457700221450ustar00rootroot00000000000000softlayer-python-5.9.8/tests/managers/account_tests.py000066400000000000000000000146021415376457700233010ustar00rootroot00000000000000""" SoftLayer.tests.managers.account_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ """ from SoftLayer.managers.account import AccountManager as AccountManager from SoftLayer import SoftLayerAPIError from SoftLayer import testing class AccountManagerTests(testing.TestCase): def set_up(self): self.manager = AccountManager(self.client) self.SLNOE = 'SoftLayer_Notification_Occurrence_Event' def test_get_summary(self): self.manager.get_summary() self.assert_called_with('SoftLayer_Account', 'getObject') def test_get_planned_upcoming_events(self): self.manager.get_upcoming_events("PLANNED") self.assert_called_with(self.SLNOE, 'getAllObjects') def test_get_unplanned_upcoming_events(self): self.manager.get_upcoming_events("UNPLANNED_INCIDENT") self.assert_called_with(self.SLNOE, 'getAllObjects') def test_get_announcement_upcoming_events(self): self.manager.get_upcoming_events("ANNOUNCEMENT") self.assert_called_with(self.SLNOE, 'getAllObjects') def test_add_planned_event_filter(self): event_type = 'PLANNED' _filter = { 'notificationOccurrenceEventType': { 'keyName': { 'operation': event_type } } } self.manager.add_event_filter(_filter, event_type) def test_add_unplanned_event_filter(self): event_type = 'UNPLANNED_INCIDENT' _filter = { 'notificationOccurrenceEventType': { 'keyName': { 'operation': event_type } } } self.manager.add_event_filter(_filter, event_type) def test_add_announcement_event_filter(self): event_type = 'ANNOUNCEMENT' _filter = { 'notificationOccurrenceEventType': { 'keyName': { 'operation': event_type } } } self.manager.add_event_filter(_filter, event_type) def test_ack_event(self): self.manager.ack_event(12345) self.assert_called_with(self.SLNOE, 'acknowledgeNotification', identifier=12345) def test_get_event(self): self.manager.get_event(12345) self.assert_called_with(self.SLNOE, 'getObject', identifier=12345) def test_get_invoices(self): self.manager.get_invoices() self.assert_called_with('SoftLayer_Account', 'getInvoices') def test_get_invoices_closed(self): self.manager.get_invoices(closed=True) _filter = { 'invoices': { 'createDate': { 'operation': 'orderBy', 'options': [{ 'name': 'sort', 'value': ['DESC'] }] } } } self.assert_called_with('SoftLayer_Account', 'getInvoices', filter=_filter) def test_get_billing_items(self): self.manager.get_billing_items(12345) self.assert_called_with('SoftLayer_Billing_Invoice', 'getInvoiceTopLevelItems') def test_get_account_billing_items(self): self.manager.get_account_billing_items() object_filter = { "allTopLevelBillingItems": { "cancellationDate": { "operation": "is null" }, "createDate": { 'operation': 'orderBy', 'options': [{ 'name': 'sort', 'value': ['ASC'] }] } } } self.assert_called_with('SoftLayer_Account', 'getAllTopLevelBillingItems', offset=0, limit=100, filter=object_filter) self.manager.get_account_billing_items(mask="id") self.assert_called_with('SoftLayer_Account', 'getAllTopLevelBillingItems', mask="mask[id]") def test_get_billing_item(self): self.manager.get_billing_item(12345) self.assert_called_with('SoftLayer_Billing_Item', 'getObject', identifier=12345) self.manager.get_billing_item(12345, mask="id") self.assert_called_with('SoftLayer_Billing_Item', 'getObject', identifier=12345, mask="mask[id]") def test_cancel_item(self): self.manager.cancel_item(12345) reason = "No longer needed" note = "Cancelled by testAccount with the SLCLI" self.assert_called_with('SoftLayer_Billing_Item', 'cancelItem', args=(False, True, reason, note), identifier=12345) reason = "TEST" note = "note test" self.manager.cancel_item(12345, reason, note) self.assert_called_with('SoftLayer_Billing_Item', 'cancelItem', args=(False, True, reason, note), identifier=12345) def test_get_billing_item_from_invoice(self): self.manager.get_billing_item_from_invoice(12345) self.assert_called_with('SoftLayer_Billing_Invoice_Item', 'getBillingItem', identifier=12345) def test_get_item_details_with_billing_item_id(self): self.manager.get_item_detail(12345) self.assert_called_with('SoftLayer_Billing_Item', 'getObject', identifier=12345) def test_get_item_details_with_invoice_item_id(self): mock = self.set_mock('SoftLayer_Billing_Item', 'getObject') mock.side_effect = SoftLayerAPIError(404, "Unable to find object with id of '123456'.") self.manager.get_item_detail(123456) self.assert_called_with('SoftLayer_Billing_Item', 'getObject', identifier=123456) self.assert_called_with('SoftLayer_Billing_Invoice_Item', 'getBillingItem', identifier=123456) def test_get_routers(self): self.manager.get_routers() self.assert_called_with("SoftLayer_Account", "getRouters") def test_get_active_account_licenses(self): self.manager.get_active_account_licenses() self.assert_called_with("SoftLayer_Account", "getActiveAccountLicenses") def test_get_active_virtual_licenses(self): self.manager.get_active_virtual_licenses() self.assert_called_with("SoftLayer_Account", "getActiveVirtualLicenses") def test_get_routers_with_datacenter(self): self.manager.get_routers(location='dal13') object_filter = {'routers': {'topLevelLocation': {'name': {'operation': 'dal13'}}}} self.assert_called_with("SoftLayer_Account", "getRouters", filter=object_filter) softlayer-python-5.9.8/tests/managers/autoscale_tests.py000066400000000000000000000064421415376457700236300ustar00rootroot00000000000000""" SoftLayer.tests.managers.autoscale_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ from SoftLayer.managers.autoscale import AutoScaleManager from SoftLayer import testing class AutoScaleTests(testing.TestCase): def set_up(self): self.autoscale = AutoScaleManager(self.client) def test_autoscale_list(self): self.autoscale.list() self.assert_called_with( 'SoftLayer_Account', 'getScaleGroups' ) def test_autoscale_list_with_mask(self): self.autoscale.list(mask='mask[status,virtualGuestMemberCount]') self.assert_called_with( 'SoftLayer_Account', 'getScaleGroups' ) def test_autoscale_details(self): self.autoscale.details(11111) self.assert_called_with( 'SoftLayer_Scale_Group', 'getObject', identifier=11111 ) def test_autoscale_details_with_mask(self): self.autoscale.details(11111, mask='mask[virtualGuestMembers[id,virtualGuest[hostname,domain,provisionDate]], ' 'terminationPolicy,virtualGuestMemberCount]') self.assert_called_with( 'SoftLayer_Scale_Group', 'getObject', identifier=11111 ) def test_autoscale_policy(self): self.autoscale.get_policy(11111) self.assert_called_with( 'SoftLayer_Scale_Policy', 'getObject', identifier=11111 ) def test_autoscale_policy_with_mask(self): self.autoscale.get_policy(11111, mask='mask[cooldown, createDate, id, name, actions, triggers[type]]') self.assert_called_with( 'SoftLayer_Scale_Policy', 'getObject', identifier=11111 ) def test_autoscale_scale(self): self.autoscale.scale(11111, 3) self.assert_called_with( 'SoftLayer_Scale_Group', 'scale', identifier=11111 ) def test_autoscale_scaleTo(self): self.autoscale.scale_to(11111, 3) self.assert_called_with( 'SoftLayer_Scale_Group', 'scaleTo', identifier=11111 ) def test_autoscale_getLogs(self): self.autoscale.get_logs(11111) self.assert_called_with( 'SoftLayer_Scale_Group', 'getLogs', identifier=11111 ) def test_autoscale_get_virtual_guests(self): self.autoscale.get_virtual_guests(11111) self.assert_called_with( 'SoftLayer_Scale_Group', 'getVirtualGuestMembers', identifier=11111, mask=None ) def test_autoscale_get_virtual_guests_mask(self): test_mask = "mask[id]" self.autoscale.get_virtual_guests(11111, mask=test_mask) self.assert_called_with( 'SoftLayer_Scale_Group', 'getVirtualGuestMembers', identifier=11111, mask=test_mask ) def test_edit_object(self): template = {'name': 'test'} self.autoscale.edit(12345, template) self.assert_called_with( 'SoftLayer_Scale_Group', 'editObject', args=(template,), identifier=12345) softlayer-python-5.9.8/tests/managers/block_tests.py000066400000000000000000001156241415376457700227450ustar00rootroot00000000000000""" SoftLayer.tests.managers.block_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import copy import SoftLayer from SoftLayer import exceptions from SoftLayer.fixtures import SoftLayer_Account from SoftLayer.fixtures import SoftLayer_Network_Storage from SoftLayer.fixtures import SoftLayer_Network_Storage_Allowed_Host from SoftLayer.fixtures import SoftLayer_Product_Order from SoftLayer.fixtures import SoftLayer_Product_Package 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_cancel_block_volume_exception_billing_item_not_found(self): mock = self.set_mock('SoftLayer_Network_Storage', 'getObject') mock.return_value = { 'capacityGb': 20, 'createDate': '2017-06-20T14:24:21-06:00', 'nasType': 'ISCSI', 'storageTypeId': '7', 'serviceResourceName': 'PerfStor Aggr aggr_staasdal0601_pc01' } exception = self.assertRaises( exceptions.SoftLayerError, self.block.cancel_block_volume, 12345, immediate=True ) self.assertEqual( 'Storage Volume was already cancelled', str(exception) ) def test_cancel_block_volume_billing_item_found(self): mock = self.set_mock('SoftLayer_Network_Storage', 'getObject') mock.return_value = { 'capacityGb': 20, 'createDate': '2017-06-20T14:24:21-06:00', 'nasType': 'ISCSI', 'storageTypeId': '7', 'serviceResourceName': 'PerfStor Aggr aggr_staasdal0601_pc01', '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(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]]],' \ 'notes' 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(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_additional_filter_order(self): result = self.block.list_block_volumes(order=1234567) self.assertEqual(SoftLayer_Account.getIscsiNetworkStorage, result) expected_filter = { 'iscsiNetworkStorage': { 'storageType': { 'keyName': {'operation': '*= BLOCK_STORAGE'} }, 'serviceResource': { 'type': { 'type': {'operation': '!~ ISCSI'} } }, 'billingItem': { 'orderItem': { 'order': { 'id': {'operation': 1234567}}}} } } 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(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(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(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(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) self.assertEqual( SoftLayer_Network_Storage.failoverToReplicant, result) self.assert_called_with( 'SoftLayer_Network_Storage', 'failoverToReplicant', args=(5678,), identifier=1234, ) def test_disaster_recovery_failover(self): result = self.block.disaster_recovery_failover_to_replicant(1234, 5678) self.assertEqual( SoftLayer_Network_Storage.disasterRecoveryFailoverToReplicant, result) self.assert_called_with( 'SoftLayer_Network_Storage', 'disasterRecoveryFailoverToReplicant', args=(5678,), identifier=1234, ) def test_replicant_failback(self): result = self.block.failback_from_replicant(1234) self.assertEqual( SoftLayer_Network_Storage.failbackFromReplicant, result) self.assert_called_with( 'SoftLayer_Network_Storage', 'failbackFromReplicant', 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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = 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(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(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(SoftLayer_Network_Storage. removeAccessFromHostList, result) self.assert_called_with( 'SoftLayer_Network_Storage', 'removeAccessFromHostList', identifier=50) def test_assign_subnets_to_acl(self): result = self.block.assign_subnets_to_acl( 12345, subnet_ids=[12345678]) self.assertEqual(SoftLayer_Network_Storage_Allowed_Host. assignSubnetsToAcl, result) self.assert_called_with( 'SoftLayer_Network_Storage_Allowed_Host', 'assignSubnetsToAcl', identifier=12345) def test_remove_subnets_from_acl(self): result = self.block.remove_subnets_from_acl( 12345, subnet_ids=[12345678]) self.assertEqual(SoftLayer_Network_Storage_Allowed_Host. removeSubnetsFromAcl, result) self.assert_called_with( 'SoftLayer_Network_Storage_Allowed_Host', 'removeSubnetsFromAcl', identifier=12345) def test_get_subnets_in_acl(self): result = self.block.get_subnets_in_acl(12345) self.assertEqual(SoftLayer_Network_Storage_Allowed_Host. getSubnetsInAcl, result) self.assert_called_with( 'SoftLayer_Network_Storage_Allowed_Host', 'getSubnetsInAcl', identifier=12345) def test_create_snapshot(self): result = self.block.create_snapshot(123, 'hello world') self.assertEqual(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( 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(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(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( 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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = 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(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_package = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock_package.return_value = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = 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(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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(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_depdupe(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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, dependent_duplicate=True ) self.assertEqual(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, 'isDependentDuplicateFlag': 1 },)) def test_order_block_duplicate_endurance_no_duplicate_snapshot(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = 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(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = 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(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = 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(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') def test_list_block_volume_limit(self): result = self.block.list_block_volume_limit() self.assertEqual(SoftLayer_Network_Storage.getVolumeCountLimits, result) def test_get_ids_from_username(self): result = self.block._get_ids_from_username("test") self.assert_called_with('SoftLayer_Account', 'getIscsiNetworkStorage') self.assertEqual([100], result) def test_get_ids_from_username_empty(self): mock = self.set_mock('SoftLayer_Account', 'getIscsiNetworkStorage') mock.return_value = [] result = self.block._get_ids_from_username("test") self.assert_called_with('SoftLayer_Account', 'getIscsiNetworkStorage') self.assertEqual([], result) def test_refresh_block_dupe(self): result = self.block.refresh_dupe(123, snapshot_id=321) self.assertEqual(SoftLayer_Network_Storage.refreshDuplicate, result) self.assert_called_with( 'SoftLayer_Network_Storage', 'refreshDuplicate', identifier=123 ) def test_convert_block_depdupe(self): result = self.block.convert_dep_dupe(123) self.assertEqual(SoftLayer_Network_Storage.convertCloneDependentToIndependent, result) self.assert_called_with( 'SoftLayer_Network_Storage', 'convertCloneDependentToIndependent', identifier=123 ) softlayer-python-5.9.8/tests/managers/cdn_tests.py000066400000000000000000000127531415376457700224160ustar00rootroot00000000000000""" SoftLayer.tests.managers.cdn_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ 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): self.cdn_client.list_cdn() self.assert_called_with('SoftLayer_Network_CdnMarketplace_Configuration_Mapping', 'listDomainMappings') def test_detail_cdn(self): self.cdn_client.get_cdn("12345") args = ("12345",) self.assert_called_with('SoftLayer_Network_CdnMarketplace_Configuration_Mapping', 'listDomainMappingByUniqueId', args=args) def test_detail_usage_metric(self): self.cdn_client.get_usage_metrics(12345, history=30, frequency="aggregate") args = (12345, self.cdn_client.start_data, self.cdn_client.end_date, "aggregate") self.assert_called_with('SoftLayer_Network_CdnMarketplace_Metrics', 'getMappingUsageMetrics', args=args) def test_get_origins(self): self.cdn_client.get_origins("12345") self.assert_called_with('SoftLayer_Network_CdnMarketplace_Configuration_Mapping_Path', 'listOriginPath') def test_add_origin(self): self.cdn_client.add_origin("12345", "10.10.10.1", "/example/videos", origin_type="server", header="test.example.com", port=80, protocol='http', optimize_for="web", cache_query="include all") args = ({ 'uniqueId': "12345", 'origin': '10.10.10.1', 'path': '/example/videos', 'originType': 'HOST_SERVER', 'header': 'test.example.com', 'httpPort': 80, 'protocol': 'HTTP', 'performanceConfiguration': 'General web delivery', 'cacheKeyQueryRule': "include all" },) self.assert_called_with('SoftLayer_Network_CdnMarketplace_Configuration_Mapping_Path', 'createOriginPath', args=args) def test_add_origin_with_bucket_and_file_extension(self): self.cdn_client.add_origin("12345", "10.10.10.1", "/example/videos", origin_type="storage", bucket_name="test-bucket", file_extensions="jpg", header="test.example.com", port=80, protocol='http', optimize_for="web", cache_query="include all") args = ({ 'uniqueId': "12345", 'origin': '10.10.10.1', 'path': '/example/videos', 'originType': 'OBJECT_STORAGE', 'header': 'test.example.com', 'httpPort': 80, 'protocol': 'HTTP', 'bucketName': 'test-bucket', 'fileExtension': 'jpg', 'performanceConfiguration': 'General web delivery', 'cacheKeyQueryRule': "include all" },) self.assert_called_with('SoftLayer_Network_CdnMarketplace_Configuration_Mapping_Path', 'createOriginPath', args=args) def test_remove_origin(self): self.cdn_client.remove_origin("12345", "/example1") args = ("12345", "/example1") self.assert_called_with('SoftLayer_Network_CdnMarketplace_Configuration_Mapping_Path', 'deleteOriginPath', args=args) def test_purge_content(self): self.cdn_client.purge_content("12345", "/example1") args = ("12345", "/example1") self.assert_called_with('SoftLayer_Network_CdnMarketplace_Configuration_Cache_Purge', 'createPurge', args=args) def test_cdn_edit(self): identifier = '9934111111111' header = 'www.test.com' result = self.cdn_client.edit(identifier, header=header) self.assertEqual(fixtures.SoftLayer_Network_CdnMarketplace_Configuration_Mapping. updateDomainMapping, result) self.assert_called_with( 'SoftLayer_Network_CdnMarketplace_Configuration_Mapping', 'updateDomainMapping', args=({ 'uniqueId': '9934111111111', 'originType': 'HOST_SERVER', 'protocol': 'HTTP', 'path': '/', 'vendorName': 'akamai', 'cname': 'cdnakauuiet7s6u6.cdnedge.bluemix.net', 'domain': 'test.example.com', 'httpPort': 80, 'header': 'www.test.com', 'origin': '1.1.1.1' },) ) def test_cdn_instance_by_hostname(self): hostname = 'test.example.com' result = self.cdn_client._get_ids_from_hostname(hostname) expected_result = fixtures.SoftLayer_Network_CdnMarketplace_Configuration_Mapping.listDomainMappings self.assertEqual(expected_result[0]['uniqueId'], result[0]) self.assert_called_with( 'SoftLayer_Network_CdnMarketplace_Configuration_Mapping', 'listDomainMappings',) softlayer-python-5.9.8/tests/managers/dedicated_host_tests.py000066400000000000000000000570221415376457700246130ustar00rootroot00000000000000""" SoftLayer.tests.managers.dedicated_host_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ from unittest import mock as 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': 12345 } }, 'domain': u'test.com', 'hostname': u'test' } ], 'useHourlyPricing': True, 'location': 'AMSTERDAM', 'packageId': 813, 'complexType': 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost', 'prices': [ { 'id': 12345 } ], '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_place_order_with_gpu(self): create_dict = self.dedicated_host._generate_create_dict = mock.Mock() values = { 'hardware': [ { 'primaryBackendNetworkComponent': { 'router': { 'id': 12345 } }, 'domain': u'test.com', 'hostname': u'test' } ], 'useHourlyPricing': True, 'location': 'AMSTERDAM', 'packageId': 813, 'complexType': 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost', 'prices': [ { 'id': 12345 } ], 'quantity': 1 } create_dict.return_value = values location = 'dal05' hostname = 'test' domain = 'test.com' hourly = True flavor = '56_CORES_X_484_RAM_X_1_5_TB_X_2_GPU_P100' 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': 12345 } }, 'domain': 'test.com', 'hostname': 'test' } ], 'useHourlyPricing': True, 'location': 'AMSTERDAM', 'packageId': 813, 'complexType': 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost', 'prices': [ { 'id': 12345 } ], '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': 12345 } }, 'domain': 'test.com', 'hostname': 'test' } ], 'useHourlyPricing': True, 'location': 'DALLAS05', 'packageId': 813, 'complexType': 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost', 'prices': [ { 'id': 12345 } ], '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 = 12345 location = 'dal05' router = 12345 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': 12345 } }, 'domain': 'test.com', 'hostname': 'test' } ], 'useHourlyPricing': True, 'location': 'DALLAS05', 'packageId': 813, 'complexType': 'SoftLayer_Container_Product_Order_Virtual_DedicatedHost', 'prices': [ { 'id': 12345 } ], 'quantity': 1 } self.assertEqual(results, testResults) def test_get_package(self): mask = ''' items[ id, description, prices, capacity, keyName, itemCategory[categoryCode], bundleItems[capacity,keyName,categories[categoryCode],hardwareGenericComponentModel[id, hardwareComponentType[keyName]]] ], 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 = 12345 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', 'keyName': '1_4_TB_LOCAL_STORAGE_DEDICATED_HOST_CAPACITY', 'categories': [{ 'categoryCode': 'dedicated_host_disk' }] }, { 'capacity': '242', 'keyName': '242_GB_RAM', 'categories': [{ 'categoryCode': 'dedicated_host_ram' }] }], 'capacity': '56', 'description': '56 Cores X 242 RAM X 1.2 TB', 'id': 12345, 'itemCategory': { 'categoryCode': 'dedicated_virtual_hosts' }, 'keyName': '56_CORES_X_242_RAM_X_1_4_TB', 'prices': [{ 'hourlyRecurringFee': '3.164', 'id': 12345, 'itemId': 12345, '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': 12345, '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 = 12345 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 test_cancel_host(self): result = self.dedicated_host.cancel_host(789) self.assertEqual(result, True) self.assert_called_with('SoftLayer_Virtual_DedicatedHost', 'deleteObject', identifier=789) def test_cancel_guests(self): vs1 = {'id': 987, 'fullyQualifiedDomainName': 'foobar.example.com'} vs2 = {'id': 654, 'fullyQualifiedDomainName': 'wombat.example.com'} self.dedicated_host.host = mock.Mock() self.dedicated_host.host.getGuests.return_value = [vs1, vs2] # Expected result vs_status1 = {'id': 987, 'fqdn': 'foobar.example.com', 'status': 'Cancelled'} vs_status2 = {'id': 654, 'fqdn': 'wombat.example.com', 'status': 'Cancelled'} delete_status = [vs_status1, vs_status2] result = self.dedicated_host.cancel_guests(789) self.assertEqual(result, delete_status) def test_cancel_guests_empty_list(self): self.dedicated_host.host = mock.Mock() self.dedicated_host.host.getGuests.return_value = [] result = self.dedicated_host.cancel_guests(789) self.assertEqual(result, []) def test_delete_guest(self): result = self.dedicated_host._delete_guest(123) self.assertEqual(result, 'Cancelled') # delete_guest should return the exception message in case it fails error_raised = SoftLayer.SoftLayerAPIError('SL Exception', 'SL message') self.dedicated_host.guest = mock.Mock() self.dedicated_host.guest.deleteObject.side_effect = error_raised result = self.dedicated_host._delete_guest(369) self.assertEqual(result, 'Exception: SL message') def _get_routers_sample(self): routers = [ { 'hostname': 'bcr01a.dal05', 'id': 12345 }, { 'hostname': 'bcr02a.dal05', 'id': 12346 }, { 'hostname': 'bcr03a.dal05', 'id': 12347 }, { 'hostname': 'bcr04a.dal05', 'id': 12348 } ] return routers def _get_package(self): package = { "items": [ { "capacity": "56", "description": "56 Cores X 242 RAM X 1.2 TB", "bundleItems": [ { "capacity": "1200", "keyName": "1_4_TB_LOCAL_STORAGE_DEDICATED_HOST_CAPACITY", "categories": [ { "categoryCode": "dedicated_host_disk" } ] }, { "capacity": "242", "keyName": "242_GB_RAM", "categories": [ { "categoryCode": "dedicated_host_ram" } ] } ], "prices": [ { "itemId": 12345, "recurringFee": "2099", "hourlyRecurringFee": "3.164", "id": 12345, } ], "keyName": "56_CORES_X_242_RAM_X_1_4_TB", "id": 12345, "itemCategory": { "categoryCode": "dedicated_virtual_hosts" }, } ], "regions": [ { "location": { "locationPackageDetails": [ { "locationId": 12345, "packageId": 813 } ], "location": { "id": 12345, "name": "ams01", "longName": "Amsterdam 1" } }, "keyname": "AMSTERDAM", "description": "AMS01 - Amsterdam", "sortOrder": 0 }, { "location": { "locationPackageDetails": [ { "isAvailable": 1, "locationId": 12345, "packageId": 813 } ], "location": { "id": 12345, "name": "dal05", "longName": "Dallas 5" } }, "keyname": "DALLAS05", "description": "DAL05 - Dallas", } ], "id": 813, "description": "Dedicated Host" } return package def test_list_guests(self): results = self.dedicated_host.list_guests(12345) for result in results: self.assertIn(result['id'], [200, 202]) self.assert_called_with('SoftLayer_Virtual_DedicatedHost', 'getGuests', identifier=12345) def test_list_guests_with_filters(self): self.dedicated_host.list_guests(12345, tags=['tag1', 'tag2'], cpus=2, memory=1024, hostname='hostname', domain='example.com', nic_speed=100, public_ip='1.2.3.4', private_ip='4.3.2.1') _filter = { 'guests': { 'domain': {'operation': '_= example.com'}, 'tagReferences': { 'tag': {'name': { 'operation': 'in', 'options': [{ 'name': 'data', 'value': ['tag1', 'tag2']}]}}}, 'maxCpu': {'operation': 2}, '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_Virtual_DedicatedHost', 'getGuests', identifier=12345, filter=_filter) softlayer-python-5.9.8/tests/managers/dns_tests.py000066400000000000000000000165401415376457700224340ustar00rootroot00000000000000""" 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_create_record_mx(self): res = self.dns_client.create_record_mx(1, 'test', 'testing', ttl=1200, priority=21) self.assert_called_with('SoftLayer_Dns_Domain_ResourceRecord', 'createObject', args=({ 'domainId': 1, 'ttl': 1200, 'host': 'test', 'type': 'MX', 'data': 'testing', 'mxPriority': 21 },)) self.assertEqual(res, {'name': 'example.com'}) def test_create_record_srv(self): res = self.dns_client.create_record_srv(1, 'record', 'test_data', 'SLS', 8080, 'foobar', ttl=1200, priority=21, weight=15) self.assert_called_with('SoftLayer_Dns_Domain_ResourceRecord', 'createObject', args=({ 'complexType': 'SoftLayer_Dns_Domain_ResourceRecord_SrvType', 'domainId': 1, 'ttl': 1200, 'host': 'record', 'type': 'SRV', 'data': 'test_data', 'priority': 21, 'weight': 15, 'service': 'foobar', 'port': 8080, 'protocol': 'SLS' },)) self.assertEqual(res, {'name': 'example.com'}) def test_create_record_ptr(self): res = self.dns_client.create_record_ptr('test', 'testing', ttl=1200) self.assert_called_with('SoftLayer_Dns_Domain_ResourceRecord', 'createObject', args=({'ttl': 1200, 'host': 'test', 'type': 'PTR', 'data': 'testing'},)) self.assertEqual(res, {'name': 'example.com'}) def test_generate_create_dict(self): data = self.dns_client._generate_create_dict('foo', 'pmx', 'bar', 60, domainId=1234, mxPriority=18, port=80, protocol='TCP', weight=25) assert_data = { 'host': 'foo', 'data': 'bar', 'ttl': 60, 'type': 'pmx', 'domainId': 1234, 'mxPriority': 18, 'port': 80, 'protocol': 'TCP', 'weight': 25 } self.assertEqual(data, assert_data) 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_records(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) def test_get_record(self): record_id = 1234 record = self.dns_client.get_record(record_id) self.assertEqual(record['id'], 12345) self.assert_called_with('SoftLayer_Dns_Domain_ResourceRecord', 'getObject', identifier=record_id) softlayer-python-5.9.8/tests/managers/email_tests.py000066400000000000000000000024341415376457700227340ustar00rootroot00000000000000""" SoftLayer.tests.managers.email_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ """ from SoftLayer.managers.email import EmailManager from SoftLayer import testing class EmailManagerTests(testing.TestCase): def test_get_AccountOverview(self): self.manager = EmailManager(self.client) self.manager.get_account_overview(1232123) self.assert_called_with('SoftLayer_Network_Message_Delivery_Email_Sendgrid', 'getAccountOverview') def test_get_statistics(self): self.manager = EmailManager(self.client) self.manager.get_statistics(1232123, 6) self.assert_called_with('SoftLayer_Network_Message_Delivery_Email_Sendgrid', 'getStatistics') def test_get_object(self): self.manager = EmailManager(self.client) self.manager.get_instance(1232123) self.assert_called_with('SoftLayer_Network_Message_Delivery_Email_Sendgrid', 'getObject') def test_update_email_address(self): self.manager = EmailManager(self.client) self.manager.update_email(1232123, 'test@ibm.com') self.assert_called_with('SoftLayer_Network_Message_Delivery_Email_Sendgrid', 'updateEmailAddress') softlayer-python-5.9.8/tests/managers/event_log_tests.py000066400000000000000000000173621415376457700236350ustar00rootroot00000000000000""" SoftLayer.tests.managers.event_log_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import SoftLayer from SoftLayer import fixtures from SoftLayer import testing class EventLogTests(testing.TestCase): def set_up(self): self.event_log = SoftLayer.EventLogManager(self.client) def test_get_event_logs(self): # Cast to list to force generator to get all objects result = list(self.event_log.get_event_logs()) expected = fixtures.SoftLayer_Event_Log.getAllObjects self.assertEqual(expected, result) self.assert_called_with('SoftLayer_Event_Log', 'getAllObjects') def test_get_event_logs_no_iteration(self): # Cast to list to force generator to get all objects result = self.event_log.get_event_logs(iterator=False) expected = fixtures.SoftLayer_Event_Log.getAllObjects self.assertEqual(expected, result) self.assert_called_with('SoftLayer_Event_Log', 'getAllObjects') def test_get_event_log_types(self): result = self.event_log.get_event_log_types() expected = fixtures.SoftLayer_Event_Log.getAllEventObjectNames self.assertEqual(expected, result) self.assert_called_with('SoftLayer_Event_Log', 'getAllEventObjectNames') def test_build_filter_no_args(self): result = self.event_log.build_filter(None, None, None, None, None, None) self.assertEqual(result, {}) def test_build_filter_min_date(self): expected = { 'eventCreateDate': { 'operation': 'greaterThanDate', 'options': [ { 'name': 'date', 'value': [ '2017-10-30T00:00:00.000000+00:00' ] } ] } } result = self.event_log.build_filter('10/30/2017', None, None, None, None, None) self.assertEqual(expected, result) def test_build_filter_max_date(self): expected = { 'eventCreateDate': { 'operation': 'lessThanDate', 'options': [ { 'name': 'date', 'value': [ '2017-10-31T00:00:00.000000+00:00' ] } ] } } result = self.event_log.build_filter(None, '10/31/2017', None, None, None, None) self.assertEqual(expected, result) def test_build_filter_min_max_date(self): expected = { 'eventCreateDate': { 'operation': 'betweenDate', 'options': [ { 'name': 'startDate', 'value': [ '2017-10-30T00:00:00.000000+00:00' ] }, { 'name': 'endDate', 'value': [ '2017-10-31T00:00:00.000000+00:00' ] } ] } } result = self.event_log.build_filter('10/30/2017', '10/31/2017', None, None, None, None) self.assertEqual(expected, result) def test_build_filter_min_date_pos_utc(self): expected = { 'eventCreateDate': { 'operation': 'greaterThanDate', 'options': [ { 'name': 'date', 'value': [ '2017-10-30T00:00:00.000000+05:00' ] } ] } } result = self.event_log.build_filter('10/30/2017', None, None, None, None, '+0500') self.assertEqual(expected, result) def test_build_filter_max_date_pos_utc(self): expected = { 'eventCreateDate': { 'operation': 'lessThanDate', 'options': [ { 'name': 'date', 'value': [ '2017-10-31T00:00:00.000000+05:00' ] } ] } } result = self.event_log.build_filter(None, '10/31/2017', None, None, None, '+0500') self.assertEqual(expected, result) def test_build_filter_min_max_date_pos_utc(self): expected = { 'eventCreateDate': { 'operation': 'betweenDate', 'options': [ { 'name': 'startDate', 'value': [ '2017-10-30T00:00:00.000000+05:00' ] }, { 'name': 'endDate', 'value': [ '2017-10-31T00:00:00.000000+05:00' ] } ] } } result = self.event_log.build_filter('10/30/2017', '10/31/2017', None, None, None, '+0500') self.assertEqual(expected, result) def test_build_filter_min_date_neg_utc(self): expected = { 'eventCreateDate': { 'operation': 'greaterThanDate', 'options': [ { 'name': 'date', 'value': [ '2017-10-30T00:00:00.000000-03:00' ] } ] } } result = self.event_log.build_filter('10/30/2017', None, None, None, None, '-0300') self.assertEqual(expected, result) def test_build_filter_max_date_neg_utc(self): expected = { 'eventCreateDate': { 'operation': 'lessThanDate', 'options': [ { 'name': 'date', 'value': [ '2017-10-31T00:00:00.000000-03:00' ] } ] } } result = self.event_log.build_filter(None, '10/31/2017', None, None, None, '-0300') self.assertEqual(expected, result) def test_build_filter_min_max_date_neg_utc(self): expected = { 'eventCreateDate': { 'operation': 'betweenDate', 'options': [ { 'name': 'startDate', 'value': [ '2017-10-30T00:00:00.000000-03:00' ] }, { 'name': 'endDate', 'value': [ '2017-10-31T00:00:00.000000-03:00' ] } ] } } result = self.event_log.build_filter('10/30/2017', '10/31/2017', None, None, None, '-0300') self.assertEqual(expected, result) def test_build_filter_name(self): expected = {'eventName': {'operation': 'Add Security Group'}} result = self.event_log.build_filter(None, None, 'Add Security Group', None, None, None) self.assertEqual(expected, result) def test_build_filter_id(self): expected = {'objectId': {'operation': 1}} result = self.event_log.build_filter(None, None, None, 1, None, None) self.assertEqual(expected, result) def test_build_filter_type(self): expected = {'objectName': {'operation': 'CCI'}} result = self.event_log.build_filter(None, None, None, None, 'CCI', None) self.assertEqual(expected, result) softlayer-python-5.9.8/tests/managers/file_tests.py000066400000000000000000001037501415376457700225670ustar00rootroot00000000000000""" SoftLayer.tests.managers.file_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import copy import SoftLayer from SoftLayer import exceptions from SoftLayer.fixtures import SoftLayer_Account from SoftLayer.fixtures import SoftLayer_Network_Storage from SoftLayer.fixtures import SoftLayer_Product_Order from SoftLayer.fixtures import SoftLayer_Product_Package 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(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(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(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(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( 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(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]]],' \ 'notes' 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(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(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) self.assertEqual( SoftLayer_Network_Storage.failoverToReplicant, result) self.assert_called_with( 'SoftLayer_Network_Storage', 'failoverToReplicant', args=(5678,), identifier=1234, ) def test_disaster_recovery_failover(self): result = self.file.disaster_recovery_failover_to_replicant(1234, 5678) self.assertEqual( SoftLayer_Network_Storage.disasterRecoveryFailoverToReplicant, result) self.assert_called_with( 'SoftLayer_Network_Storage', 'disasterRecoveryFailoverToReplicant', args=(5678,), identifier=1234, ) def test_replicant_failback(self): result = self.file.failback_from_replicant(1234) self.assertEqual( SoftLayer_Network_Storage.failbackFromReplicant, result) self.assert_called_with( 'SoftLayer_Network_Storage', 'failbackFromReplicant', 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(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(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_additional_filter_order(self): result = self.file.list_file_volumes(order=1234567) self.assertEqual(SoftLayer_Account.getNasNetworkStorage, result) expected_filter = { 'nasNetworkStorage': { 'storageType': { 'keyName': {'operation': '*= FILE_STORAGE'} }, 'serviceResource': { 'type': { 'type': {'operation': '!~ NAS'} } }, 'billingItem': { 'orderItem': { 'order': { 'id': {'operation': 1234567}}}} } } 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(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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(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_depdupe(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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, dependent_duplicate=True ) self.assertEqual(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, 'isDependentDuplicateFlag': 1 },)) def test_order_file_duplicate_endurance(self): mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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(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_list_file_volume_limit(self): result = self.file.list_file_volume_limit() self.assertEqual(SoftLayer_Network_Storage.getVolumeCountLimits, result) def test_get_ids_from_username(self): result = self.file._get_ids_from_username("test") self.assert_called_with('SoftLayer_Account', 'getNasNetworkStorage') self.assertEqual([1], result) def test_get_ids_from_username_empty(self): mock = self.set_mock('SoftLayer_Account', 'getNasNetworkStorage') mock.return_value = [] result = self.file._get_ids_from_username("test") self.assert_called_with('SoftLayer_Account', 'getNasNetworkStorage') self.assertEqual([], result) def test_refresh_file_dupe(self): result = self.file.refresh_dupe(123, snapshot_id=321) self.assertEqual(SoftLayer_Network_Storage.refreshDuplicate, result) self.assert_called_with( 'SoftLayer_Network_Storage', 'refreshDuplicate', identifier=123 ) def test_convert_file_depdupe(self): result = self.file.convert_dep_dupe(123) self.assertEqual(SoftLayer_Network_Storage.convertCloneDependentToIndependent, result) self.assert_called_with( 'SoftLayer_Network_Storage', 'convertCloneDependentToIndependent', identifier=123 ) softlayer-python-5.9.8/tests/managers/firewall_tests.py000066400000000000000000000305541415376457700234560ustar00rootroot00000000000000""" 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.9.8/tests/managers/hardware_tests.py000066400000000000000000001226521415376457700234470ustar00rootroot00000000000000""" SoftLayer.tests.managers.hardware_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import copy from unittest import mock as 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): # Cast result back to list because list_hardware is now a generator 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) result = self.hardware.reload(100, lvm=True) self.assertEqual(result, 'OK') self.assert_called_with('SoftLayer_Hardware_Server', 'reloadOperatingSystem', args=('FORCE', {'lvmFlag': True}), identifier=100) def test_get_create_options(self): options = self.hardware.get_create_options() extras = {'key': '1_IPV6_ADDRESS', 'name': '1 IPv6 Address'} locations = {'key': 'wdc07', 'name': 'Washington 7'} operating_systems = { 'key': 'OS_UBUNTU_14_04_LTS_TRUSTY_TAHR_64_BIT', 'name': 'Ubuntu / 14.04-64', 'referenceCode': 'UBUNTU_14_64' } port_speeds = { 'key': '10', 'name': '10 Mbps Public & Private Network Uplinks' } sizes = { 'key': 'M1_64X512X25', 'name': 'M1.64x512x25', 'hourlyRecurringFee': 0.0, 'recurringFee': 0.0 } self.assertEqual(options['extras'][0]['key'], extras['key']) self.assertEqual(options['locations'][0], locations) self.assertEqual(options['operating_systems'][0]['referenceCode'], operating_systems['referenceCode']) self.assertEqual(options['port_speeds'][0]['name'], port_speeds['name']) self.assertEqual(options['sizes'][0], sizes) def test_get_create_options_prices(self): options = self.hardware.get_create_options() extras = {'key': '1_IPV6_ADDRESS', 'name': '1 IPv6 Address', 'prices': [ { 'hourlyRecurringFee': '0', 'id': 272, 'locationGroupId': '', 'recurringFee': '0', } ] } locations = {'key': 'wdc07', 'name': 'Washington 7'} operating_systems = { 'key': 'OS_UBUNTU_14_04_LTS_TRUSTY_TAHR_64_BIT', 'name': 'Ubuntu / 14.04-64', 'referenceCode': 'UBUNTU_14_64', 'prices': [ { 'hourlyRecurringFee': '0', 'id': 272, 'locationGroupId': '', 'recurringFee': '0', } ] } port_speeds = { 'key': '10', 'name': '10 Mbps Public & Private Network Uplinks', 'prices': [ { 'hourlyRecurringFee': '0', 'id': 272, 'locationGroupId': '', 'recurringFee': '0', } ] } sizes = { 'key': 'M1_64X512X25', 'name': 'M1.64x512x25', 'hourlyRecurringFee': 0.0, 'recurringFee': 0.0 } self.assertEqual(options['extras'][0]['prices'][0]['hourlyRecurringFee'], extras['prices'][0]['hourlyRecurringFee']) self.assertEqual(options['locations'][0], locations) self.assertEqual(options['operating_systems'][0]['prices'][0]['locationGroupId'], operating_systems['prices'][0]['locationGroupId']) self.assertEqual(options['port_speeds'][0]['prices'][0]['id'], port_speeds['prices'][0]['id']) self.assertEqual(options['sizes'][0], sizes) def test_get_create_options_prices_by_location(self): options = self.hardware.get_create_options('wdc07') extras = {'key': '1_IPV6_ADDRESS', 'name': '1 IPv6 Address', 'prices': [ { 'hourlyRecurringFee': '0', 'id': 272, 'locationGroupId': '', 'recurringFee': '0', } ] } locations = {'key': 'wdc07', 'name': 'Washington 7'} operating_systems = { 'key': 'OS_UBUNTU_14_04_LTS_TRUSTY_TAHR_64_BIT', 'name': 'Ubuntu / 14.04-64', 'referenceCode': 'UBUNTU_14_64', 'prices': [ { 'hourlyRecurringFee': '0', 'id': 272, 'locationGroupId': '', 'recurringFee': '0', } ] } port_speeds = { 'key': '10', 'name': '10 Mbps Public & Private Network Uplinks', 'prices': [ { 'hourlyRecurringFee': '0', 'id': 272, 'locationGroupId': '', 'recurringFee': '0', } ] } sizes = { 'key': 'M1_64X512X25', 'name': 'M1.64x512x25', 'hourlyRecurringFee': 0.0, 'recurringFee': 0.0 } self.assertEqual(options['extras'][0]['prices'][0]['hourlyRecurringFee'], extras['prices'][0]['hourlyRecurringFee']) self.assertEqual(options['locations'][0], locations) self.assertEqual(options['operating_systems'][0]['prices'][0]['locationGroupId'], operating_systems['prices'][0]['locationGroupId']) self.assertEqual(options['port_speeds'][0]['prices'][0]['id'], port_speeds['prices'][0]['id']) self.assertEqual(options['sizes'][0], sizes) def test_get_hardware_item_prices(self): options = self.hardware.get_hardware_item_prices("MONTREAL") item_prices = [ { "hourlyRecurringFee": ".093", "id": 204015, "recurringFee": "62", "item": { "description": "4 x 2.0 GHz or higher Cores", "id": 859, "keyName": "GUEST_CORES_4", }, "pricingLocationGroup": { "id": 503, "locations": [ { "id": 449610, "longName": "Montreal 1", "name": "mon01", "regions": [ { "description": "MON01 - Montreal", "keyname": "MONTREAL", } ] } ] } } ] self.assertEqual(options[0]['item']['keyName'], item_prices[0]['item']['keyName']) self.assertEqual(options[0]['hourlyRecurringFee'], item_prices[0]['hourlyRecurringFee']) 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_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(self): args = { 'size': 'S1270_8GB_2X1TBSATA_NORAID', 'hostname': 'unicorn', 'domain': 'giggles.woo', 'location': 'wdc07', 'os': 'OS_UBUNTU_14_04_LTS_TRUSTY_TAHR_64_BIT', 'port_speed': 10, 'hourly': True, 'extras': ['1_IPV6_ADDRESS'], 'post_uri': 'http://example.com/script.php', 'ssh_keys': [10], } package = 'BARE_METAL_SERVER' location = 'wdc07' item_keynames = [ '1_IP_ADDRESS', 'UNLIMITED_SSL_VPN_USERS_1_PPTP_VPN_USER_PER_ACCOUNT', 'REBOOT_KVM_OVER_IP', 'OS_UBUNTU_14_04_LTS_TRUSTY_TAHR_64_BIT', 'BANDWIDTH_0_GB_2', '10_MBPS_PUBLIC_PRIVATE_NETWORK_UPLINKS', '1_IPV6_ADDRESS' ] hourly = True preset_keyname = 'S1270_8GB_2X1TBSATA_NORAID' extras = { 'hardware': [{ 'domain': 'giggles.woo', 'hostname': 'unicorn', }], 'provisionScripts': ['http://example.com/script.php'], 'sshKeys': [{'sshKeyIds': [10]}] } data = self.hardware._generate_create_dict(**args) self.assertEqual(package, data['package_keyname']) self.assertEqual(location, data['location']) for keyname in item_keynames: self.assertIn(keyname, data['item_keynames']) self.assertEqual(extras, data['extras']) self.assertEqual(preset_keyname, data['preset_keyname']) self.assertEqual(hourly, data['hourly']) def test_generate_create_dict_by_router_network_component(self): args = { 'size': 'S1270_8GB_2X1TBSATA_NORAID', 'hostname': 'unicorn', 'domain': 'giggles.woo', 'location': 'wdc07', 'os': 'OS_UBUNTU_14_04_LTS_TRUSTY_TAHR_64_BIT', 'port_speed': 10, 'hourly': True, 'extras': ['1_IPV6_ADDRESS'], 'public_router': 1111, 'private_router': 1234 } extras = { 'hardware': [{ 'domain': 'giggles.woo', 'hostname': 'unicorn', 'primaryNetworkComponent': { "router": { "id": 1111 } }, 'primaryBackendNetworkComponent': { "router": { "id": 1234 } } }] } data = self.hardware._generate_create_dict(**args) self.assertEqual(extras, data['extras']) def test_generate_create_dict_network_key(self): args = { 'size': 'S1270_8GB_2X1TBSATA_NORAID', 'hostname': 'test1', 'domain': 'test.com', 'location': 'wdc07', 'os': 'OS_UBUNTU_14_04_LTS_TRUSTY_TAHR_64_BIT', 'network': 'NETWORKING', 'hourly': True, 'extras': ['1_IPV6_ADDRESS'], 'post_uri': 'http://example.com/script.php', 'ssh_keys': [10], } data = self.hardware._generate_create_dict(**args) self.assertIn('NETWORKING', data['item_keynames']) @mock.patch('SoftLayer.managers.ordering.OrderingManager.verify_order') @mock.patch('SoftLayer.managers.hardware.HardwareManager._generate_create_dict') def test_verify_order(self, create_dict, verify_order): create_dict.return_value = {'test': 1, 'verify': 1} verify_order.return_value = {'test': 2} result = self.hardware.verify_order(test=1, verify=1) self.assertEqual(2, result['test']) create_dict.assert_called_once_with(test=1, verify=1) verify_order.assert_called_once_with(test=1, verify=1) @mock.patch('SoftLayer.managers.ordering.OrderingManager.place_order') @mock.patch('SoftLayer.managers.hardware.HardwareManager._generate_create_dict') def test_place_order(self, create_dict, place_order): create_dict.return_value = {'test': 1, 'verify': 1} place_order.return_value = {'test': 1} result = self.hardware.place_order(test=1, verify=1) self.assertEqual(1, result['test']) create_dict.assert_called_once_with(test=1, verify=1) place_order.assert_called_once_with(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}, 'openCancellationTicket': {'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}, 'openCancellationTicket': {'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): mock = self.set_mock('SoftLayer_Hardware_Server', 'getObject') mock.return_value = {'id': 987, 'billingItem': {'id': 6327}, 'openCancellationTicket': {'id': 4567}} 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, 'openCancellationTicket': {'id': 1234}} ex = self.assertRaises(SoftLayer.SoftLayerError, self.hardware.cancel_hardware, 6327) self.assertEqual("Ticket #1234 already exists for this server", str(ex)) def test_cancel_hardwareno_billing_item_or_ticket(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("Cannot locate billing for the server. The server may already be cancelled.", str(ex)) def test_cancel_hardware_monthly_now(self): mock = self.set_mock('SoftLayer_Hardware_Server', 'getObject') mock.return_value = {'id': 987, 'billingItem': {'id': 1234}, 'openCancellationTicket': {'id': 4567}, 'hourlyBillingFlag': False} with self.assertLogs('SoftLayer.managers.hardware', level='INFO') as logs: result = self.hardware.cancel_hardware(987, immediate=True) # should be 2 infom essages here self.assertEqual(len(logs.records), 2) self.assertEqual(result, True) self.assert_called_with('SoftLayer_Billing_Item', 'cancelItem', identifier=1234, args=(False, False, 'No longer needed', '')) cancel_message = "Please reclaim this server ASAP, it is no longer needed. Thankyou." self.assert_called_with('SoftLayer_Ticket', 'addUpdate', identifier=4567, args=({'entry': cancel_message},)) def test_cancel_hardware_monthly_whenever(self): mock = self.set_mock('SoftLayer_Hardware_Server', 'getObject') mock.return_value = {'id': 987, 'billingItem': {'id': 6327}, 'openCancellationTicket': {'id': 4567}} with self.assertLogs('SoftLayer.managers.hardware', level='INFO') as logs: result = self.hardware.cancel_hardware(987, immediate=False) # should be 2 infom essages here self.assertEqual(len(logs.records), 1) self.assertEqual(result, True) self.assert_called_with('SoftLayer_Billing_Item', 'cancelItem', identifier=6327, args=(False, False, 'No longer needed', '')) def test_cancel_running_transaction(self): mock = self.set_mock('SoftLayer_Hardware_Server', 'getObject') mock.return_value = {'id': 987, 'billingItem': {'id': 6327}, 'activeTransaction': {'id': 4567}} self.assertRaises(SoftLayer.SoftLayerError, self.hardware.cancel_hardware, 12345) def test_change_port_speed_public(self): self.hardware.change_port_speed(2, True, 100, 'degraded') self.assert_called_with('SoftLayer_Hardware_Server', 'setPublicNetworkInterfaceSpeed', identifier=2, args=([100, 'degraded'],)) def test_change_port_speed_private(self): self.hardware.change_port_speed(2, False, 10, 'redundant') self.assert_called_with('SoftLayer_Hardware_Server', 'setPrivateNetworkInterfaceSpeed', identifier=2, args=([10, 'redundant'],)) 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)) def test_reflash_firmware(self): result = self.hardware.reflash_firmware(100) self.assertEqual(result, True) self.assert_called_with('SoftLayer_Hardware_Server', 'createFirmwareReflashTransaction', identifier=100, args=(1, 1, 1)) def test_reflash_firmware_selective(self): result = self.hardware.reflash_firmware(100, raid_controller=False, bios=False) self.assertEqual(result, True) self.assert_called_with('SoftLayer_Hardware_Server', 'createFirmwareReflashTransaction', identifier=100, args=(1, 0, 0)) def test_get_tracking_id(self): result = self.hardware.get_tracking_id(1234) self.assert_called_with('SoftLayer_Hardware_Server', 'getMetricTrackingObjectId') self.assertEqual(result, 1000) def test_get_bandwidth_data(self): result = self.hardware.get_bandwidth_data(1234, '2019-01-01', '2019-02-01', 'public', 1000) self.assert_called_with('SoftLayer_Metric_Tracking_Object', 'getBandwidthData', args=('2019-01-01', '2019-02-01', 'public', 1000), identifier=1000) self.assertEqual(result[0]['type'], 'cpu0') def test_get_bandwidth_allocation(self): result = self.hardware.get_bandwidth_allocation(1234) self.assert_called_with('SoftLayer_Hardware_Server', 'getBandwidthAllotmentDetail', identifier=1234) self.assert_called_with('SoftLayer_Hardware_Server', 'getBillingCycleBandwidthUsage', identifier=1234) self.assertEqual(result['allotment']['amount'], '250') self.assertEqual(result['usage'][0]['amountIn'], '.448') def test_get_bandwidth_allocation_with_allotment(self): mock = self.set_mock('SoftLayer_Hardware_Server', 'getBandwidthAllotmentDetail') mock.return_value = { "allocationId": 11111, "id": 22222, "allocation": { "amount": "2000" } } result = self.hardware.get_bandwidth_allocation(1234) self.assertEqual(2000, int(result['allotment']['amount'])) def test_get_bandwidth_allocation_no_allotment(self): mock = self.set_mock('SoftLayer_Hardware_Server', 'getBandwidthAllotmentDetail') mock.return_value = None result = self.hardware.get_bandwidth_allocation(1234) self.assertEqual(None, result['allotment']) def test_get_storage_iscsi_details(self): mock = self.set_mock('SoftLayer_Hardware_Server', 'getAttachedNetworkStorages') mock.return_value = [ { "accountId": 11111, "capacityGb": 12000, "id": 3777123, "nasType": "ISCSI", "username": "SL02SEL31111-9", } ] result = self.hardware.get_storage_details(1234, 'ISCSI') self.assertEqual([{ "accountId": 11111, "capacityGb": 12000, "id": 3777123, "nasType": "ISCSI", "username": "SL02SEL31111-9", }], result) def test_get_storage_iscsi_empty_details(self): mock = self.set_mock('SoftLayer_Hardware_Server', 'getAttachedNetworkStorages') mock.return_value = [] result = self.hardware.get_storage_details(1234, 'ISCSI') self.assertEqual([], result) def test_get_storage_nas_details(self): mock = self.set_mock('SoftLayer_Hardware_Server', 'getAttachedNetworkStorages') mock.return_value = [ { "accountId": 11111, "capacityGb": 12000, "id": 3777111, "nasType": "NAS", "username": "SL02SEL32222-9", } ] result = self.hardware.get_storage_details(1234, 'NAS') self.assertEqual([{ "accountId": 11111, "capacityGb": 12000, "id": 3777111, "nasType": "NAS", "username": "SL02SEL32222-9", }], result) def test_get_storage_nas_empty_details(self): mock = self.set_mock('SoftLayer_Hardware_Server', 'getAttachedNetworkStorages') mock.return_value = [] result = self.hardware.get_storage_details(1234, 'NAS') self.assertEqual([], result) def test_get_storage_credentials(self): mock = self.set_mock('SoftLayer_Hardware_Server', 'getAllowedHost') mock.return_value = { "accountId": 11111, "id": 33333, "name": "iqn.2020-03.com.ibm:sl02su11111-v62941551", "resourceTableName": "HARDWARE", "credential": { "accountId": "11111", "id": 44444, "password": "SjFDCpHrjskfj", "username": "SL02SU11111-V62941551" } } result = self.hardware.get_storage_credentials(1234) self.assertEqual({ "accountId": 11111, "id": 33333, "name": "iqn.2020-03.com.ibm:sl02su11111-v62941551", "resourceTableName": "HARDWARE", "credential": { "accountId": "11111", "id": 44444, "password": "SjFDCpHrjskfj", "username": "SL02SU11111-V62941551" } }, result) def test_get_none_storage_credentials(self): mock = self.set_mock('SoftLayer_Hardware_Server', 'getAllowedHost') mock.return_value = None result = self.hardware.get_storage_credentials(1234) self.assertEqual(None, result) def test_get_hard_drives(self): mock = self.set_mock('SoftLayer_Hardware_Server', 'getHardDrives') mock.return_value = [ { "id": 11111, "serialNumber": "z1w4sdf", "serviceProviderId": 1, "hardwareComponentModel": { "capacity": "1000", "description": "SATAIII:2000:8300:Constellation", "id": 111, "manufacturer": "Seagate", "name": "Constellation ES", "hardwareGenericComponentModel": { "capacity": "1000", "units": "GB", "hardwareComponentType": { "id": 1, "keyName": "HARD_DRIVE", "type": "Hard Drive", "typeParentId": 5 } } } } ] result = self.hardware.get_hard_drives(1234) self.assertEqual([ { "id": 11111, "serialNumber": "z1w4sdf", "serviceProviderId": 1, "hardwareComponentModel": { "capacity": "1000", "description": "SATAIII:2000:8300:Constellation", "id": 111, "manufacturer": "Seagate", "name": "Constellation ES", "hardwareGenericComponentModel": { "capacity": "1000", "units": "GB", "hardwareComponentType": { "id": 1, "keyName": "HARD_DRIVE", "type": "Hard Drive", "typeParentId": 5 } } } } ], result) def test_get_hard_drive_empty(self): mock = self.set_mock('SoftLayer_Hardware_Server', 'getHardDrives') mock.return_value = [] result = self.hardware.get_hard_drives(1234) self.assertEqual([], result) def test_get_hardware_guests_empty_virtualHost(self): mock = self.set_mock('SoftLayer_Hardware_Server', 'getVirtualHost') mock.return_value = None result = self.hardware.get_hardware_guests(1234) self.assertEqual(None, result) def test_get_hardware_guests(self): mock = self.set_mock('SoftLayer_Virtual_Host', 'getGuests') mock.return_value = [ { "accountId": 11111, "hostname": "NSX-T Manager", "id": 22222, "maxCpu": 16, "maxCpuUnits": "CORE", "maxMemory": 49152, "powerState": { "keyName": "RUNNING", "name": "Running" }, "status": { "keyName": "ACTIVE", "name": "Active" } }] result = self.hardware.get_hardware_guests(1234) self.assertEqual("NSX-T Manager", result[0]['hostname']) def test_authorize_storage(self): options = self.hardware.authorize_storage(1234, "SL01SEL301234-11") self.assertEqual(True, options) def test_authorize_storage_empty(self): mock = self.set_mock('SoftLayer_Account', 'getNetworkStorage') mock.return_value = [] self.assertRaises(SoftLayer.exceptions.SoftLayerError, self.hardware.authorize_storage, 1234, "#") def test_get_price_id_memory_capacity(self): upgrade_prices = [ {'categories': [{'categoryCode': 'ram'}], 'item': {'capacity': 1}, 'id': 99} ] result = self.hardware._get_prices_for_upgrade_option(upgrade_prices, 'memory', 1) self.assertEqual(99, result) def test_get_price_id_mismatch_capacity(self): upgrade_prices = [ {'categories': [{'categoryCode': 'ram1'}], 'item': {'capacity': 1}, 'id': 90}, {'categories': [{'categoryCode': 'ram'}], 'item': {'capacity': 2}, 'id': 91}, {'categories': [{'categoryCode': 'ram'}], 'item': {'capacity': 1}, 'id': 92}, ] result = self.hardware._get_prices_for_upgrade_option(upgrade_prices, 'memory', 1) self.assertEqual(92, result) def test_get_price_id_disk_capacity(self): upgrade_prices = [ {'categories': [{'categoryCode': 'disk1'}], 'item': {'capacity': 1}, 'id': 99} ] result = self.hardware._get_prices_for_upgrade_option(upgrade_prices, 'disk1', 1) self.assertEqual(99, result['id']) def test_upgrade(self): result = self.hardware.upgrade(1, memory=32) self.assertEqual(result['orderId'], 1234) 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': 209391}]) def test_upgrade_add_disk(self): disk_list = list() disks = {'description': 'add_disk', 'capacity': 1000, 'number': 2} disk_list.append(disks) result = self.hardware.upgrade(1, disk=disk_list) self.assertEqual(result['orderId'], 1234) 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'][0]['id'], 49759) def test_upgrade_resize_disk(self): disk_list = list() disks = {'description': 'resize_disk', 'capacity': 1000, 'number': 1} disk_list.append(disks) result = self.hardware.upgrade(1, disk=disk_list) self.assertEqual(result['orderId'], 1234) 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'][0]['id'], 49759) def test_upgrade_blank(self): result = self.hardware.upgrade(1) self.assertEqual(result, None) self.assertEqual(self.calls('SoftLayer_Product_Order', 'placeOrder'), []) def test_upgrade_full(self): result = self.hardware.upgrade(1, memory=32, nic_speed="10000 Redundant", drive_controller="RAID", public_bandwidth=500, test=False) self.assertEqual(result['orderId'], 1234) self.assert_called_with('SoftLayer_Product_Order', 'placeOrder') call = self.calls('SoftLayer_Product_Order', 'placeOrder')[0] order_container = call.args[0] self.assertIn({'id': 209391}, order_container['prices']) self.assertIn({'id': 21525}, order_container['prices']) self.assertIn({'id': 22482}, order_container['prices']) self.assertIn({'id': 50357}, order_container['prices']) def test_get_components(self): result = self.hardware.get_components(1234) components = [{'hardwareComponentModelId': 147, 'hardwareId': 1234, 'id': 369, 'modifyDate': '2017-11-10T16:59:38-06:00', 'serviceProviderId': 1, 'hardwareComponentModel': {'name': 'IMM2 - Onboard', 'firmwares': [{'createDate': '2020-09-24T13:46:29-06:00', 'version': '5.60'}, {'createDate': '2019-10-14T16:51:12-06:00', 'version': '5.10'}]}}] self.assert_called_with('SoftLayer_Hardware_Server', 'getComponents') self.assertEqual(result, components) self.assertEqual(result[0]['hardwareId'], 1234) self.assertEqual(result[0]['hardwareComponentModel']['name'], 'IMM2 - Onboard') self.assertEqual(result[0]['hardwareComponentModel']['firmwares'][0]['version'], '5.60') def test_sensor(self): self.hardware.get_sensors(100) self.assert_called_with('SoftLayer_Hardware', 'getSensorData') class HardwareHelperTests(testing.TestCase): def set_up(self): self.items = [ { "itemCategory": {"categoryCode": "port_speed"}, "capacity": 100, "attributes": [ {'attributeTypeKeyName': 'NON_LACP'}, {'attributeTypeKeyName': 'IS_PRIVATE_NETWORK_ONLY'} ], "keyName": "ITEM_1", "prices": [{"id": 1, "locationGroupId": 100}] }, { "itemCategory": {"categoryCode": "port_speed"}, "capacity": 200, "attributes": [ {'attributeTypeKeyName': 'YES_LACP'}, {'attributeTypeKeyName': 'IS_PRIVATE_NETWORK_ONLY'} ], "keyName": "ITEM_2", "prices": [{"id": 1, "locationGroupId": 151}] }, { "itemCategory": {"categoryCode": "port_speed"}, "capacity": 200, "attributes": [ {'attributeTypeKeyName': 'YES_LACP'}, {'attributeTypeKeyName': 'IS_PRIVATE_NETWORK_ONLY'} ], "keyName": "ITEM_3", "prices": [{"id": 1, "locationGroupId": 51}] }, { "itemCategory": {"categoryCode": "bandwidth"}, "capacity": 0.0, "attributes": [], "keyName": "HOURLY_BANDWIDTH_1", "prices": [{"id": 1, "locationGroupId": 51, "hourlyRecurringFee": 1.0, "recurringFee": 1.0}] }, { "itemCategory": {"categoryCode": "bandwidth"}, "capacity": 10.0, "attributes": [], "keyName": "MONTHLY_BANDWIDTH_1", "prices": [{"id": 1, "locationGroupId": 151, "recurringFee": 1.0}] }, { "itemCategory": {"categoryCode": "bandwidth"}, "capacity": 10.0, "attributes": [], "keyName": "MONTHLY_BANDWIDTH_2", "prices": [{"id": 1, "locationGroupId": 51, "recurringFee": 1.0}] }, ] self.location = {'location': {'location': {'priceGroups': [{'id': 50}, {'id': 51}]}}} def test_bandwidth_key(self): result = managers.hardware._get_bandwidth_key(self.items, True, False, self.location) self.assertEqual('HOURLY_BANDWIDTH_1', result) result = managers.hardware._get_bandwidth_key(self.items, False, True, self.location) self.assertEqual('HOURLY_BANDWIDTH_1', result) result = managers.hardware._get_bandwidth_key(self.items, False, False, self.location) self.assertEqual('MONTHLY_BANDWIDTH_2', result) ex = self.assertRaises(SoftLayer.SoftLayerError, managers.hardware._get_bandwidth_key, [], True, False, self.location) self.assertEqual("Could not find valid price for bandwidth option", str(ex)) def test_port_speed_key(self): result = managers.hardware._get_port_speed_key(self.items, 200, True, self.location) self.assertEqual("ITEM_3", result) def test_port_speed_key_exception(self): items = [] location = {} ex = self.assertRaises(SoftLayer.SoftLayerError, managers.hardware._get_port_speed_key, items, 999, False, location) self.assertEqual("Could not find valid price for port speed: '999'", str(ex)) def test_matches_location(self): price = {'id': 1, 'locationGroupId': 51, 'recurringFee': 99} self.assertTrue(managers.hardware._matches_location(price, self.location)) price['locationGroupId'] = 99999 self.assertFalse(managers.hardware._matches_location(price, self.location)) def test_is_bonded(self): item_non_lacp = {'attributes': [{'attributeTypeKeyName': 'NON_LACP'}]} item_lacp = {'attributes': [{'attributeTypeKeyName': 'YES_LACP'}]} self.assertFalse(managers.hardware._is_bonded(item_non_lacp)) self.assertTrue(managers.hardware._is_bonded(item_lacp)) def test_is_private(self): item_private = {'attributes': [{'attributeTypeKeyName': 'IS_PRIVATE_NETWORK_ONLY'}]} item_public = {'attributes': [{'attributeTypeKeyName': 'NOT_PRIVATE_NETWORK_ONLY'}]} self.assertTrue(managers.hardware._is_private_port_speed_item(item_private)) self.assertFalse(managers.hardware._is_private_port_speed_item(item_public)) softlayer-python-5.9.8/tests/managers/image_tests.py000066400000000000000000000200131415376457700227200ustar00rootroot00000000000000""" SoftLayer.tests.managers.image_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import SoftLayer from SoftLayer import exceptions 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_import_image_cos(self): self.image.import_image_from_uri(name='test_image', note='testimage', uri='cos://some_uri', os_code='UBUNTU_LATEST', ibm_api_key='some_ibm_key', root_key_crn='some_root_key_crn', wrapped_dek='some_dek', cloud_init=False, byol=False, is_encrypted=False ) self.assert_called_with( IMAGE_SERVICE, 'createFromIcos', args=({'name': 'test_image', 'note': 'testimage', 'operatingSystemReferenceCode': 'UBUNTU_LATEST', 'uri': 'cos://some_uri', 'ibmApiKey': 'some_ibm_key', 'crkCrn': 'some_root_key_crn', 'wrappedDek': 'some_dek', 'cloudInit': False, 'byol': False, 'isEncrypted': False },)) 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) def test_export_image_cos(self): self.image.export_image_to_uri(1234, 'cos://someuri', ibm_api_key='someApiKey') self.assert_called_with( IMAGE_SERVICE, 'copyToIcos', args=({'uri': 'cos://someuri', 'ibmApiKey': 'someApiKey'},), identifier=1234) def test_add_locations_image(self): locations = ['ams01'] self.image.add_locations(100, locations) self.assert_called_with(IMAGE_SERVICE, 'addLocations', identifier=100) def test_add_locations_fail(self): locations = ['test'] self.assertRaises( exceptions.SoftLayerError, self.image.add_locations, 100, locations ) def test_remove_locations_image(self): locations = ['ams01'] self.image.remove_locations(100, locations) self.assert_called_with(IMAGE_SERVICE, 'removeLocations', identifier=100) def test_get_locations_id_fails(self): locations = ['test'] self.assertRaises( exceptions.SoftLayerError, self.image.get_locations_list, 100, locations ) softlayer-python-5.9.8/tests/managers/ipsec_tests.py000066400000000000000000000361751415376457700227610ustar00rootroot00000000000000""" SoftLayer.tests.managers.ipsec_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ from unittest.mock import MagicMock as 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.9.8/tests/managers/loadbal_tests.py000066400000000000000000000213371415376457700232460ustar00rootroot00000000000000""" SoftLayer.tests.managers.loadbal_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. A lot of these tests will use junk data because the manager just passes them directly to the API. """ import SoftLayer from SoftLayer import exceptions from SoftLayer.fixtures import SoftLayer_Network_LBaaS_LoadBalancer from SoftLayer import testing class LoadBalancerTests(testing.TestCase): def set_up(self): self.lb_mgr = SoftLayer.LoadBalancerManager(self.client) def test_get_adcs(self): self.lb_mgr.get_adcs() self.assert_called_with('SoftLayer_Account', 'getApplicationDeliveryControllers') def test_get_adc_masks(self): self.lb_mgr.get_adcs(mask="mask[id]") self.assert_called_with('SoftLayer_Account', 'getApplicationDeliveryControllers', mask="mask[id]") def test_get_adc(self): self.lb_mgr.get_adc(1234) self.assert_called_with('SoftLayer_Network_Application_Delivery_Controller', 'getObject', identifier=1234) def test_get_adc_mask(self): self.lb_mgr.get_adc(1234, mask="mask[id]") self.assert_called_with('SoftLayer_Network_Application_Delivery_Controller', 'getObject', identifier=1234, mask="mask[id]") def test_get_lbaas(self): self.lb_mgr.get_lbaas() self.assert_called_with('SoftLayer_Network_LBaaS_LoadBalancer', 'getAllObjects') def test_get_lbaas_mask(self): self.lb_mgr.get_lbaas(mask="mask[id]") self.assert_called_with('SoftLayer_Network_LBaaS_LoadBalancer', 'getAllObjects', mask="mask[id]") def test_get_lb(self): lb = self.lb_mgr.get_lb(1234) self.assert_called_with('SoftLayer_Network_LBaaS_LoadBalancer', 'getObject', identifier=1234) self.assert_called_with('SoftLayer_Network_LBaaS_LoadBalancer', 'getLoadBalancerMemberHealth', args=(lb.get('uuid'),)) self.assertIsNotNone(lb['health']) def test_get_lb_mask(self): lb = self.lb_mgr.get_lb(1234, mask="mask[id]") self.assert_called_with('SoftLayer_Network_LBaaS_LoadBalancer', 'getObject', identifier=1234, mask="mask[id]") self.assert_called_with('SoftLayer_Network_LBaaS_LoadBalancer', 'getLoadBalancerMemberHealth', args=(lb.get('uuid'),)) self.assertIsNotNone(lb['health']) def test_updated_lb_health(self): uuid = '1234' check = {'backendPort': '80'} self.lb_mgr.update_lb_health_monitors(uuid, check) self.assert_called_with('SoftLayer_Network_LBaaS_HealthMonitor', 'updateLoadBalancerHealthMonitors', args=(uuid, check)) def test_get_lbaas_uuid_id_uuid(self): uuid = '1a1aa111-4474-4e16-9f02-4de959229b85' my_id = 1111111 lb_uuid, lb_id = self.lb_mgr.get_lbaas_uuid_id(uuid) self.assert_called_with('SoftLayer_Network_LBaaS_LoadBalancer', 'getLoadBalancer', args=(uuid,)) self.assertEqual(lb_uuid, uuid) self.assertEqual(lb_id, my_id) def test_get_lbaas_uuid_id_id(self): uuid = '1a1aa111-4474-4e16-9f02-4de959229b85' my_id = 1111111 lb_uuid, lb_id = self.lb_mgr.get_lbaas_uuid_id(my_id) self.assert_called_with('SoftLayer_Network_LBaaS_LoadBalancer', 'getObject', identifier=my_id) self.assertEqual(lb_uuid, uuid) self.assertEqual(lb_id, my_id) def test_get_lbaas_uuid_id_name(self): uuid = '1a1aa111-4474-4e16-9f02-4de959229b85' my_id = 1111111 name = 'test-01' lb_uuid, lb_id = self.lb_mgr.get_lbaas_uuid_id(name) self.assert_called_with('SoftLayer_Network_LBaaS_LoadBalancer', 'getAllObjects') self.assertEqual(lb_uuid, uuid) self.assertEqual(lb_id, my_id) def test_delete_lb_member(self): uuid = 'aa-bb-cc' member_id = 'dd-ee-ff' self.lb_mgr.delete_lb_member(uuid, member_id) self.assert_called_with('SoftLayer_Network_LBaaS_Member', 'deleteLoadBalancerMembers', args=(uuid, [member_id])) def test_add_lb_member(self): uuid = 'aa-bb-cc' member = {'privateIpAddress': '1.2.3.4'} self.lb_mgr.add_lb_member(uuid, member) self.assert_called_with('SoftLayer_Network_LBaaS_Member', 'addLoadBalancerMembers', args=(uuid, [member])) def test_add_lb_listener(self): uuid = 'aa-bb-cc' listener = {'id': 1} self.lb_mgr.add_lb_listener(uuid, listener) self.assert_called_with('SoftLayer_Network_LBaaS_Listener', 'updateLoadBalancerProtocols', args=(uuid, [listener])) def test_get_l7policies(self): my_id = 1111111 self.lb_mgr.get_l7policies(my_id) self.assert_called_with('SoftLayer_Network_LBaaS_Listener', 'getL7Policies', identifier=my_id) def test_get_all_l7policies(self): policies = self.lb_mgr.get_all_l7policies() self.assert_called_with('SoftLayer_Network_LBaaS_LoadBalancer', 'getAllObjects') self.assertIsInstance(policies, dict) def test_add_lb_l7_pool(self): uuid = 'aa-bb-cc' pool = {'id': 1} members = {'id': 1} health = {'id': 1} session = {'id': 1} self.lb_mgr.add_lb_l7_pool(uuid, pool, members, health, session) self.assert_called_with('SoftLayer_Network_LBaaS_L7Pool', 'createL7Pool', args=(uuid, pool, members, health, session)) def test_del_lb_l7_pool(self): uuid = 'aa-bb-cc' self.lb_mgr.del_lb_l7_pool(uuid) self.assert_called_with('SoftLayer_Network_LBaaS_L7Pool', 'deleteObject', identifier=uuid) def test_remove_lb_listener(self): uuid = 'aa-bb-cc' listener = 'dd-ee-ff' self.lb_mgr.remove_lb_listener(uuid, listener) self.assert_called_with('SoftLayer_Network_LBaaS_Listener', 'deleteLoadBalancerProtocols', args=(uuid, [listener])) def test_order_lbaas(self): datacenter = 'tes01' name = 'test-lb' desc = 'my lb' protocols = {'frontendPort': 80, 'frontendProtocol': 'HTTP'} subnet_id = 12345 public = True verify = False package = [ { 'id': 805, 'keyNake': 'LBAAS', 'itemPrices': [ { 'id': 1, 'name': 'A test price', 'locationGroupId': None }, { 'id': 2, 'name': 'A test price 2', 'locationGroupId': 123 } ] } ] mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') mock.return_value = package order_data = { 'complexType': 'SoftLayer_Container_Product_Order_Network_LoadBalancer_AsAService', 'name': name, 'description': desc, 'location': datacenter, 'packageId': package[0]['id'], 'useHourlyPricing': True, # Required since LBaaS is an hourly service 'prices': [{'id': package[0]['itemPrices'][0]['id']}], 'protocolConfigurations': protocols, 'subnets': [{'id': subnet_id}], 'isPublic': public } self.lb_mgr.order_lbaas(datacenter, name, desc, protocols, subnet_id, public, verify) self.assert_called_with('SoftLayer_Product_Order', 'placeOrder', args=(order_data,)) self.assert_called_with('SoftLayer_Product_Package', 'getAllObjects') verify = True self.lb_mgr.order_lbaas(datacenter, name, desc, protocols, subnet_id, public, verify) self.assert_called_with('SoftLayer_Product_Order', 'verifyOrder') def test_lbaas_order_options(self): self.lb_mgr.lbaas_order_options() self.assert_called_with('SoftLayer_Product_Package', 'getAllObjects') def test_cancel_lbaas(self): uuid = 'aa-bb-cc' self.lb_mgr.cancel_lbaas(uuid) self.assert_called_with('SoftLayer_Network_LBaaS_LoadBalancer', 'cancelLoadBalancer', args=(uuid,)) def test_get_lbaas_by_name(self): name = SoftLayer_Network_LBaaS_LoadBalancer.getObject.get('name') load_bal = self.lb_mgr.get_lbaas_by_name(name) self.assert_called_with('SoftLayer_Network_LBaaS_LoadBalancer', 'getAllObjects') self.assertIsNotNone(load_bal) def test_get_lbaas_by_name_fails(self): load_bal_mock = self.set_mock('SoftLayer_Network_LBaaS_LoadBalancer', 'getAllObjects') load_bal_mock.return_value = [] name = 'test' self.assertRaises(exceptions.SoftLayerError, self.lb_mgr.get_lbaas_by_name, name) softlayer-python-5.9.8/tests/managers/metadata_tests.py000066400000000000000000000060251415376457700234250ustar00rootroot00000000000000""" 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.9.8/tests/managers/network_tests.py000066400000000000000000000641431415376457700233430ustar00rootroot00000000000000""" SoftLayer.tests.managers.network_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import sys import unittest from unittest import mock as mock 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_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, endpoint_id=1234, version=4, test_order=True) self.assertEqual(fixtures.SoftLayer_Product_Order.verifyOrder, result) result = self.network.add_subnet('public', quantity=4, endpoint_id=1234, version=4, test_order=False) self.assertEqual(fixtures.SoftLayer_Product_Order.placeOrder, result) result = self.network.add_subnet('global', test_order=True) self.assertEqual(fixtures.SoftLayer_Product_Order.verifyOrder, result) result = self.network.add_subnet('static', quantity=8, endpoint_id=1234, version=4, 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, endpoint_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_set_tags_subnet(self): subnet_id = 1234 tags = 'tags1,tag2' result = self.network.set_tags_subnet(subnet_id, tags) self.assertEqual(result, True) self.assert_called_with('SoftLayer_Network_Subnet', 'setTags', identifier=subnet_id, args=(tags,)) def test_edit_note_subnet(self): subnet_id = 1234 note = 'test note' result = self.network.edit_note_subnet(subnet_id, note) self.assertEqual(result, True) self.assert_called_with('SoftLayer_Network_Subnet', 'editNote', identifier=subnet_id, args=(note,)) 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_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_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', '111']) 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) def test_get_event_logs_by_request_id(self): expected = [ { 'accountId': 100, 'eventCreateDate': '2017-10-18T09:40:32.238869-05:00', 'eventName': 'Security Group Added', 'ipAddress': '192.168.0.1', 'label': 'test.softlayer.com', 'metaData': '{"securityGroupId":"200",' '"securityGroupName":"test_SG",' '"networkComponentId":"100",' '"networkInterfaceType":"public",' '"requestId":"96c9b47b9e102d2e1d81fba"}', 'objectId': 300, 'objectName': 'CCI', 'traceId': '59e767e03a57e', 'userId': 400, 'userType': 'CUSTOMER', 'username': 'user' }, { 'accountId': 100, 'eventCreateDate': '2017-10-18T10:42:13.089536-05:00', 'eventName': 'Security Group Rule(s) Removed', 'ipAddress': '192.168.0.1', 'label': 'test_SG', 'metaData': '{"requestId":"96c9b47b9e102d2e1d81fba",' '"rules":[{"ruleId":"800",' '"remoteIp":null,"remoteGroupId":null,"direction":"ingress",' '"ethertype":"IPv4",' '"portRangeMin":2000,"portRangeMax":2001,"protocol":"tcp"}]}', 'objectId': 700, 'objectName': 'Security Group', 'traceId': '59e7765515e28', 'userId': 400, 'userType': 'CUSTOMER', 'username': 'user' } ] with mock.patch.object(self.network, '_get_cci_event_logs') as cci_mock: with mock.patch.object(self.network, '_get_security_group_event_logs') as sg_mock: cci_mock.return_value = [ { 'accountId': 100, 'eventCreateDate': '2017-10-18T09:40:32.238869-05:00', 'eventName': 'Security Group Added', 'ipAddress': '192.168.0.1', 'label': 'test.softlayer.com', 'metaData': '{"securityGroupId":"200",' '"securityGroupName":"test_SG",' '"networkComponentId":"100",' '"networkInterfaceType":"public",' '"requestId":"96c9b47b9e102d2e1d81fba"}', 'objectId': 300, 'objectName': 'CCI', 'traceId': '59e767e03a57e', 'userId': 400, 'userType': 'CUSTOMER', 'username': 'user' }, { 'accountId': 100, 'eventCreateDate': '2017-10-23T14:22:36.221541-05:00', 'eventName': 'Disable Port', 'ipAddress': '192.168.0.1', 'label': 'test.softlayer.com', 'metaData': '', 'objectId': 300, 'objectName': 'CCI', 'traceId': '100', 'userId': '', 'userType': 'SYSTEM' }, { 'accountId': 100, 'eventCreateDate': '2017-10-18T09:40:41.830338-05:00', 'eventName': 'Security Group Rule Added', 'ipAddress': '192.168.0.1', 'label': 'test.softlayer.com', 'metaData': '{"securityGroupId":"200",' '"securityGroupName":"test_SG",' '"networkComponentId":"100",' '"networkInterfaceType":"public",' '"requestId":"53d0b91d392864e062f4958",' '"rules":[{"ruleId":"100",' '"remoteIp":null,"remoteGroupId":null,"direction":"ingress",' '"ethertype":"IPv4",' '"portRangeMin":2000,"portRangeMax":2001,"protocol":"tcp"}]}', 'objectId': 300, 'objectName': 'CCI', 'traceId': '59e767e9c2184', 'userId': 400, 'userType': 'CUSTOMER', 'username': 'user' } ] sg_mock.return_value = [ { 'accountId': 100, 'eventCreateDate': '2017-10-18T10:42:13.089536-05:00', 'eventName': 'Security Group Rule(s) Removed', 'ipAddress': '192.168.0.1', 'label': 'test_SG', 'metaData': '{"requestId":"96c9b47b9e102d2e1d81fba",' '"rules":[{"ruleId":"800",' '"remoteIp":null,"remoteGroupId":null,"direction":"ingress",' '"ethertype":"IPv4",' '"portRangeMin":2000,"portRangeMax":2001,"protocol":"tcp"}]}', 'objectId': 700, 'objectName': 'Security Group', 'traceId': '59e7765515e28', 'userId': 400, 'userType': 'CUSTOMER', 'username': 'user' }, { 'accountId': 100, 'eventCreateDate': '2017-10-18T10:42:11.679736-05:00', 'eventName': 'Network Component Removed from Security Group', 'ipAddress': '192.168.0.1', 'label': 'test_SG', 'metaData': '{"requestId":"6b9a87a9ab8ac9a22e87a00",' '"fullyQualifiedDomainName":"test.softlayer.com",' '"networkComponentId":"100",' '"networkInterfaceType":"public"}', 'objectId': 700, 'objectName': 'Security Group', 'traceId': '59e77653a1e5f', 'userId': 400, 'userType': 'CUSTOMER', 'username': 'user' } ] result = self.network.get_event_logs_by_request_id('96c9b47b9e102d2e1d81fba') self.assertEqual(expected, result) @unittest.skipIf(sys.version_info < (3, 6), "__next__ doesn't work in python 2") def test_get_security_group_event_logs(self): result = self.network._get_security_group_event_logs() # Event log now returns a generator, so you have to get a result for it to make an API call log = result.__next__() _filter = {'objectName': {'operation': 'Security Group'}} self.assert_called_with('SoftLayer_Event_Log', 'getAllObjects', filter=_filter) self.assertEqual(100, log['accountId']) @unittest.skipIf(sys.version_info < (3, 6), "__next__ doesn't work in python 2") def test_get_cci_event_logs(self): result = self.network._get_cci_event_logs() # Event log now returns a generator, so you have to get a result for it to make an API call log = result.__next__() _filter = {'objectName': {'operation': 'CCI'}} self.assert_called_with('SoftLayer_Event_Log', 'getAllObjects', filter=_filter) self.assertEqual(100, log['accountId']) def test_vlan_edit(self): vlan_id = 100 name = "test" note = "test note" tags = "tag1,tag2" self.network.edit(vlan_id, name, note, tags) self.assert_called_with('SoftLayer_Network_Vlan', 'editObject') def test_get_all_pods(self): self.network.get_pods() self.assert_called_with('SoftLayer_Network_Pod', 'getAllObjects') softlayer-python-5.9.8/tests/managers/object_storage_tests.py000066400000000000000000000103571415376457700246420ustar00rootroot00000000000000""" SoftLayer.tests.managers.object_storage_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import SoftLayer from SoftLayer import fixtures from SoftLayer import SoftLayerError 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, []) def test_create_credential(self): accounts = self.set_mock('SoftLayer_Network_Storage_Hub_Cleversafe_Account', 'credentialCreate') accounts.return_value = { "id": 1103123, "password": "nwUEUsx6PiEoN0B1Xe9z9hUCyXMkAF", "username": "XfHhBNBPlPdlWyaP", "type": { "name": "S3 Compatible Signature" } } credential = self.object_storage.create_credential(100) self.assertEqual(credential, { "id": 1103123, "password": "nwUEUsx6PiEoN0B1Xe9z9hUCyXMkAF", "username": "XfHhBNBPlPdlWyaP", "type": { "name": "S3 Compatible Signature" } }) def test_delete_credential(self): accounts = self.set_mock('SoftLayer_Network_Storage_Hub_Cleversafe_Account', 'credentialDelete') accounts.return_value = True credential = self.object_storage.delete_credential(100) self.assertEqual(credential, True) def test_limit_credential(self): accounts = self.set_mock('SoftLayer_Network_Storage_Hub_Cleversafe_Account', 'getCredentialLimit') accounts.return_value = 2 credential = self.object_storage.limit_credential(100) self.assertEqual(credential, 2) def test_list_credential(self): credentials = self.object_storage.list_credential(100) self.assertIsInstance(credentials, list) self.assert_called_with('SoftLayer_Network_Storage_Hub_Cleversafe_Account', 'getCredentials', identifier=100) def test_resolve_ids(self): accounts = self.set_mock('SoftLayer_Account', 'getHubNetworkStorage') accounts.return_value = [{'id': 12345, 'username': 'test'}] identifier = self.object_storage.resolve_ids('test') self.assertEqual(identifier, [12345]) self.assert_called_with('SoftLayer_Account', 'getHubNetworkStorage') def test_resolve_ids_fail_multiple(self): accounts = self.set_mock('SoftLayer_Account', 'getHubNetworkStorage') accounts.return_value = [{'id': 12345, 'username': 'test'}, {'id': 12345, 'username': 'test'}] self.assertRaises(SoftLayerError, self.object_storage.resolve_ids, 'test') def test_resolve_ids_fail_no_found(self): accounts = self.set_mock('SoftLayer_Account', 'getHubNetworkStorage') accounts.return_value = [] self.assertRaises(SoftLayerError, self.object_storage.resolve_ids, 'test') softlayer-python-5.9.8/tests/managers/ordering_tests.py000066400000000000000000001156211415376457700234610ustar00rootroot00000000000000""" SoftLayer.tests.managers.ordering_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ from unittest import mock as 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_preset_prices(self): result = self.ordering.get_preset_prices(405) self.assertEqual(result, fixtures.SoftLayer_Product_Package_Preset.getObject) self.assert_called_with('SoftLayer_Product_Package_Preset', 'getObject') def test_get_item_prices(self): result = self.ordering.get_item_prices(835) self.assertEqual(result, fixtures.SoftLayer_Product_Package.getItemPrices) self.assert_called_with('SoftLayer_Product_Package', 'getItemPrices') 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) self.assertEqual(1, container['quantity']) self.assert_called_with('SoftLayer_Billing_Order_Quote', 'getRecalculatedOrderContainer') 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_save_quote(self): saved_quote = self.ordering.save_quote(1234) quote_service = self.ordering.client['Billing_Order_Quote'] quote_fixture = quote_service.getObject(id=1234) self.assertEqual(saved_quote, quote_fixture) self.assert_called_with('SoftLayer_Billing_Order_Quote', 'saveQuote', identifier=1234) def test_verify_quote(self): extras = { 'hardware': [{ 'hostname': 'test1', 'domain': 'example.com' }] } result = self.ordering.verify_quote(1234, extras) self.assertEqual(result, fixtures.SoftLayer_Billing_Order_Quote.verifyOrder) self.assert_called_with('SoftLayer_Billing_Order_Quote', 'verifyOrder') def test_order_quote_virtual_guest(self): guest_quote = { 'orderContainers': [{ 'presetId': '', 'prices': [{ 'id': 1921 }], 'quantity': 1, 'packageId': 46, 'useHourlyPricing': '', }], } extras = { 'hardware': [{ 'hostname': 'test1', 'domain': 'example.com' }] } mock = self.set_mock('SoftLayer_Billing_Order_Quote', 'getRecalculatedOrderContainer') mock.return_value = guest_quote result = self.ordering.order_quote(1234, extras) self.assertEqual(result, fixtures.SoftLayer_Billing_Order_Quote.placeOrder) self.assert_called_with('SoftLayer_Billing_Order_Quote', 'placeOrder') def test_generate_order_template(self): extras = {'hardware': [{'hostname': 'test1', 'domain': 'example.com'}]} result = self.ordering.generate_order_template(1234, extras, quantity=1) self.assert_called_with('SoftLayer_Billing_Order_Quote', 'getRecalculatedOrderContainer') self.assertEqual(result['hardware'][0]['domain'], 'example.com') def test_generate_order_template_virtual(self): extras = { 'hardware': [{ 'hostname': 'test1', 'domain': 'example.com' }], 'testProperty': 100 } result = self.ordering.generate_order_template(1234, extras, quantity=1) self.assert_called_with('SoftLayer_Billing_Order_Quote', 'getRecalculatedOrderContainer') self.assertEqual(result['testProperty'], 100) 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_categories_filters(self): p_mock = self.set_mock('SoftLayer_Product_Package', 'getConfiguration') p_mock.return_value = ['cat1', 'cat2'] fake_filter = {'test': {'operation': 1234}} 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', filter=fake_filter) self.assert_called_with('SoftLayer_Product_Package', 'getConfiguration', filter=fake_filter) 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 } }, 'accountRestrictedActivePresets': { '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 } }, 'accountRestrictedActivePresets': { '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): category1 = {'categoryCode': 'cat1'} price1 = {'id': 1234, 'locationGroupId': None, 'categories': [{"categoryCode": "guest_core"}], 'itemCategory': [category1]} item1 = {'id': 1111, 'keyName': 'ITEM1', 'itemCategory': category1, 'prices': [price1]} category2 = {'categoryCode': 'cat2'} price2 = {'id': 5678, 'locationGroupId': None, 'categories': [category2]} item2 = {'id': 2222, 'keyName': 'ITEM2', 'itemCategory': category2, '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'], "8") list_mock.assert_called_once_with('PACKAGE_KEYNAME', mask='id, description, capacity, itemCategory, keyName, ' 'prices[categories]') self.assertEqual([price1['id'], price2['id']], prices) def test_get_price_id_list_no_core(self): category1 = {'categoryCode': 'cat1'} price1 = {'id': 1234, 'locationGroupId': None, 'categories': [{"categoryCode": "guest_core"}], 'itemCategory': [category1]} item1 = {'id': 1111, 'keyName': 'ITEM1', 'itemCategory': category1, 'prices': [price1]} category2 = {'categoryCode': 'cat2'} price2 = {'id': 5678, 'locationGroupId': None, 'categories': [category2]} item2 = {'id': 2222, 'keyName': 'ITEM2', 'itemCategory': category2, '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'], None) list_mock.assert_called_once_with('PACKAGE_KEYNAME', mask='id, description, capacity, itemCategory, keyName, ' 'prices[categories]') self.assertEqual([price1['id'], price2['id']], prices) def test_get_price_id_list_item_not_found(self): category1 = {'categoryCode': 'cat1'} price1 = {'id': 1234, 'locationGroupId': '', 'categories': [category1]} item1 = {'id': 1111, 'keyName': 'ITEM1', 'itemCategory': category1, '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'], "8") list_mock.assert_called_once_with('PACKAGE_KEYNAME', mask='id, description, capacity, itemCategory, keyName, ' 'prices[categories]') self.assertEqual("Item ITEM2 does not exist for package PACKAGE_KEYNAME", str(exc)) def test_get_price_id_list_gpu_items_with_two_categories(self): # Specific for GPU prices which are differentiated by their category (gpu0, gpu1) price1 = {'id': 1234, 'locationGroupId': None, 'categories': [{'categoryCode': 'gpu1'}]} price2 = {'id': 5678, 'locationGroupId': None, 'categories': [{'categoryCode': 'gpu0'}]} item1 = {'id': 1111, 'keyName': 'ITEM1', 'itemCategory': {'categoryCode': 'gpu0'}, 'prices': [price1, price2]} with mock.patch.object(self.ordering, 'list_items') as list_mock: list_mock.return_value = [item1, item1] prices = self.ordering.get_price_id_list('PACKAGE_KEYNAME', ['ITEM1', 'ITEM1'], "8") list_mock.assert_called_once_with('PACKAGE_KEYNAME', mask='id, description, capacity, itemCategory, ' 'keyName, ' 'prices[categories]') self.assertEqual([price2['id'], price1['id']], prices) 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 = {'orderContainers': [ {'complexType': 'SoftLayer_Container_Foo', 'location': 1854895, '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, 8) self.assertEqual(expected_order, order) def test_generate_order_with_quantity(self): pkg = 'PACKAGE_KEYNAME' quantity = 2 items = ['ITEM1', 'ITEM2'] extras = {"hardware": [{"hostname": "test01", "domain": "example.com"}, {"hostname": "test02", "domain": "example.com"}]} complex_type = 'My_Type' expected_order = {'orderContainers': [ {'complexType': 'My_Type', 'hardware': [{'domain': 'example.com', 'hostname': 'test01'}, {'domain': 'example.com', 'hostname': 'test02'}], 'location': 1854895, 'packageId': 1234, 'prices': [{'id': 1111}, {'id': 2222}], 'quantity': 2, '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, quantity=quantity, extras=extras) mock_pkg.assert_called_once_with(pkg, mask='id') mock_preset.assert_not_called() mock_get_ids.assert_called_once_with(pkg, items, None) self.assertEqual(expected_order, order) def test_generate_order(self): pkg = 'PACKAGE_KEYNAME' items = ['ITEM1', 'ITEM2'] complex_type = 'My_Type' expected_order = {'orderContainers': [ {'complexType': 'My_Type', 'location': 1854895, '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, None) 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_place_order_with_quantity(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 = {"hardware": [{"hostname": "test01", "domain": "example.com"}, {"hostname": "test02", "domain": "example.com"}]} quantity = 2 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_place_quote(self): ord_mock = self.set_mock('SoftLayer_Product_Order', 'placeQuote') ord_mock.return_value = {'id': 1234} pkg = 'PACKAGE_KEYNAME' location = 'DALLAS13' items = ['ITEM1', 'ITEM2'] hourly = False preset_keyname = 'PRESET' complex_type = 'Complex_Type' extras = {'foo': 'bar'} quantity = 1 name = 'wombat' send_email = True with mock.patch.object(self.ordering, 'generate_order') as gen_mock: gen_mock.return_value = {'order': {}} order = self.ordering.place_quote(pkg, location, items, preset_keyname=preset_keyname, complex_type=complex_type, extras=extras, quantity=quantity, quote_name=name, send_email=send_email) 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 def test_get_location_id_short(self): locations = self.set_mock('SoftLayer_Location', 'getDatacenters') locations.return_value = [{'id': 1854895, 'name': 'dal13', 'regions': [{'keyname': 'DALLAS13'}]}] dc_id = self.ordering.get_location_id('dal13') self.assertEqual(1854895, dc_id) def test_get_location_id_keyname(self): locations = self.set_mock('SoftLayer_Location', 'getDatacenters') locations.return_value = [{'id': 1854895, 'name': 'dal13', 'regions': [{'keyname': 'DALLAS13'}]}] dc_id = self.ordering.get_location_id('DALLAS13') self.assertEqual(1854895, dc_id) def test_get_location_id_exception(self): locations = self.set_mock('SoftLayer_Location', 'getDatacenters') locations.return_value = [] self.assertRaises(exceptions.SoftLayerError, self.ordering.get_location_id, "BURMUDA") def test_get_location_id_int(self): dc_id = self.ordering.get_location_id(1234) self.assertEqual(1234, dc_id) def test_get_location_id_NONE(self): dc_id = self.ordering.get_location_id("NONE") self.assertEqual(0, dc_id) def test_location_group_id_none(self): # RestTransport uses None for empty locationGroupId category1 = {'categoryCode': 'cat1'} price1 = {'id': 1234, 'locationGroupId': None, 'categories': [category1]} item1 = {'id': 1111, 'keyName': 'ITEM1', 'itemCategory': category1, 'prices': [price1]} category2 = {'categoryCode': 'cat2'} price2 = {'id': 5678, 'locationGroupId': None, 'categories': [category2]} item2 = {'id': 2222, 'keyName': 'ITEM2', 'itemCategory': category2, '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'], "8") list_mock.assert_called_once_with('PACKAGE_KEYNAME', mask='id, description, capacity, itemCategory, keyName, ' 'prices[categories]') self.assertEqual([price1['id'], price2['id']], prices) def test_location_groud_id_empty(self): # XMLRPCtransport uses '' for empty locationGroupId category1 = {'categoryCode': 'cat1'} price1 = {'id': 1234, 'locationGroupId': '', 'categories': [category1]} item1 = {'id': 1111, 'keyName': 'ITEM1', 'itemCategory': category1, 'prices': [price1]} category2 = {'categoryCode': 'cat2'} price2 = {'id': 5678, 'locationGroupId': "", 'categories': [category2]} item2 = {'id': 2222, 'keyName': 'ITEM2', 'itemCategory': category2, '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'], "8") list_mock.assert_called_once_with('PACKAGE_KEYNAME', mask='id, description, capacity, itemCategory, keyName, ' 'prices[categories]') self.assertEqual([price1['id'], price2['id']], prices) def test_get_item_price_id_without_capacity_restriction(self): category1 = {'categoryCode': 'cat1'} category2 = {'categoryCode': 'cat2'} prices = [{'id': 1234, 'locationGroupId': '', 'categories': [category1]}, {'id': 2222, 'locationGroupId': 509, 'categories': [category2]}] price_id = self.ordering.get_item_price_id("8", prices) self.assertEqual(1234, price_id) def test_get_item_price_id_core_with_capacity_restriction(self): category1 = {'categoryCode': 'cat1'} price1 = [{'id': 1234, 'locationGroupId': '', "capacityRestrictionMaximum": "16", "capacityRestrictionMinimum": "1", "capacityRestrictionType": "CORE", 'categories': [category1]}, {'id': 2222, 'locationGroupId': '', "capacityRestrictionMaximum": "56", "capacityRestrictionMinimum": "36", "capacityRestrictionType": "CORE", 'categories': [category1]}] price_id = self.ordering.get_item_price_id("8", price1) self.assertEqual(1234, price_id) def test_get_item_price_id_storage_with_capacity_restriction(self): category1 = {'categoryCode': 'cat1'} price1 = [{'id': 1234, 'locationGroupId': '', "capacityRestrictionMaximum": "16", "capacityRestrictionMinimum": "1", "capacityRestrictionType": "STORAGE_SPACE", 'categories': [category1]}, {'id': 2222, 'locationGroupId': '', "capacityRestrictionMaximum": "56", "capacityRestrictionMinimum": "36", "capacityRestrictionType": "STORAGE_SPACE", 'categories': [category1]}] price_id = self.ordering.get_item_price_id("8", price1) self.assertEqual(1234, price_id) def test_get_item_price_id_processor_with_capacity_restriction(self): category1 = {'categoryCode': 'cat1'} price1 = [{'id': 1234, 'locationGroupId': '', "capacityRestrictionMaximum": "1", "capacityRestrictionMinimum": "1", "capacityRestrictionType": "PROCESSOR", 'categories': [category1]}] price_id = self.ordering.get_item_price_id("8", price1) self.assertEqual(1234, price_id) def test_issues1067(self): # https://github.com/softlayer/softlayer-python/issues/1067 item_mock = self.set_mock('SoftLayer_Product_Package', 'getItems') item_mock_return = [ { 'id': 10453, 'itemCategory': {'categoryCode': 'server'}, "description": "Dual Intel Xeon Silver 4110 (16 Cores, 2.10 GHz)", 'keyName': 'INTEL_INTEL_XEON_4110_2_10', 'prices': [ { 'capacityRestrictionType': 'PROCESSOR', 'categories': [{'categoryCode': 'os'}], 'id': 201161, 'locationGroupId': None, 'recurringFee': '250', 'setupFee': '0' } ] } ] item_mock.return_value = item_mock_return item_keynames = ['INTEL_INTEL_XEON_4110_2_10'] package = 'DUAL_INTEL_XEON_PROCESSOR_SCALABLE_FAMILY_4_DRIVES' result = self.ordering.get_price_id_list(package, item_keynames, None) self.assertIn(201161, result) def test_clean_quote_verify(self): extras = { 'hardware': [{ 'hostname': 'test1', 'domain': 'example.com' }], 'testPropertyEmpty': '', 'testPropertyNone': None } result = self.ordering.verify_quote(1234, extras) self.assertEqual(result, fixtures.SoftLayer_Billing_Order_Quote.verifyOrder) self.assert_called_with('SoftLayer_Billing_Order_Quote', 'verifyOrder') call = self.calls('SoftLayer_Billing_Order_Quote', 'verifyOrder')[0] order_container = call.args[0] self.assertNotIn('testPropertyEmpty', order_container) self.assertNotIn('testPropertyNone', order_container) self.assertNotIn('reservedCapacityId', order_container) def test_get_item_capacity_core(self): items = [{ "capacity": "1", "id": 6131, "keyName": "OS_RHEL_7_X_LAMP_64_BIT", }, { "capacity": "1", "id": 10201, "keyName": "GUEST_CORE_1_DEDICATED", }] item_capacity = self.ordering.get_item_capacity(items, ['GUEST_CORE_1_DEDICATED', 'OS_RHEL_7_X_LAMP_64_BIT']) self.assertEqual(1, int(item_capacity)) def test_get_item_capacity_storage(self): items = [{ "capacity": "1", "id": 6131, "keyName": "STORAGE_SPACE_FOR_2_IOPS_PER_GB", }, { "capacity": "1", "id": 10201, "keyName": "READHEAVY_TIER", }] item_capacity = self.ordering.get_item_capacity(items, ['READHEAVY_TIER', 'STORAGE_SPACE_FOR_2_IOPS_PER_GB']) self.assertEqual(1, int(item_capacity)) def test_get_item_capacity_intel(self): items = [{ "capacity": "1", "id": 6131, "description": "Dual Intel Xeon E5-2690 v3 (24 Cores, 2.60 GHz)", "keyName": "INTEL_XEON_2690_2_60", }, { "capacity": "1", "id": 10201, "keyName": "GUEST_CORE_1_DEDICATED", }] item_capacity = self.ordering.get_item_capacity(items, ['INTEL_XEON_2690_2_60', 'BANDWIDTH_20000_GB']) self.assertEqual(24, int(item_capacity)) def test_get_oder_detail_mask(self): order_id = 12345 test_mask = 'mask[id]' self.ordering.get_order_detail(order_id, mask=test_mask) self.assert_called_with('SoftLayer_Billing_Order', 'getObject', identifier=order_id, mask=test_mask) def test_get_oder_detail_default_mask(self): order_id = 12345 _default_mask = ( 'mask[orderTotalAmount,orderApprovalDate,' 'initialInvoice[id,amount,invoiceTotalAmount,' 'invoiceTopLevelItems[id, description, hostName, domainName, oneTimeAfterTaxAmount,' 'recurringAfterTaxAmount, createDate,' 'categoryCode,' 'category[name],' 'location[name],' 'children[id, category[name], description, oneTimeAfterTaxAmount,recurringAfterTaxAmount]]],' 'items[description],userRecord[displayName,userStatus]]') self.ordering.get_order_detail(order_id) self.assert_called_with('SoftLayer_Billing_Order', 'getObject', identifier=order_id, mask=_default_mask) def test_get_item_prices_by_location_name(self): object_mask = "filteredMask[pricingLocationGroup[locations]]" object_filter = { "itemPrices": {"pricingLocationGroup": {"locations": {"name": {"operation": 'dal13'}}}}} self.ordering.get_item_prices_by_location('dal13', 'TEST') self.assert_called_with('SoftLayer_Product_Package', 'getItemPrices', mask=object_mask, filter=object_filter) def test_get_item_prices_by_location_keyname(self): object_mask = "filteredMask[pricingLocationGroup[locations]]" object_filter = { "itemPrices": {"pricingLocationGroup": {"locations": {"name": {"operation": 'dal13'}}}}} self.ordering.get_item_prices_by_location('DALLAS13', 'TEST') self.assert_called_with('SoftLayer_Product_Package', 'getItemPrices', mask=object_mask, filter=object_filter) def test_resolve_location_name(self): location_name_expected = 'dal13' object_mask = "mask[regions]" location_name = self.ordering.resolve_location_name('DALLAS13') self.assertEqual(location_name, location_name_expected) self.assert_called_with('SoftLayer_Location', 'getDatacenters', mask=object_mask) def test_resolve_location_name_by_keyname(self): location_name_expected = 'dal13' object_mask = "mask[regions]" location_name = self.ordering.resolve_location_name('DALLAS13') self.assertEqual(location_name, location_name_expected) self.assert_called_with('SoftLayer_Location', 'getDatacenters', mask=object_mask) def test_resolve_location_name_by_name(self): location_name_expected = 'dal13' object_mask = "mask[regions]" location_name = self.ordering.resolve_location_name('dal13') self.assertEqual(location_name, location_name_expected) self.assert_called_with('SoftLayer_Location', 'getDatacenters', mask=object_mask) def test_resolve_location_name_invalid(self): exc = self.assertRaises(exceptions.SoftLayerError, self.ordering.resolve_location_name, None) self.assertIn("Invalid location", str(exc)) def test_resolve_location_name_not_exist(self): exc = self.assertRaises(exceptions.SoftLayerError, self.ordering.resolve_location_name, "UNKNOWN_LOCATION_TEST") self.assertIn("does not exist", str(exc)) # https://github.com/softlayer/softlayer-python/issues/1425 # Issues relating to checking prices based of the price.term relationship def test_issues1425_zeroterm(self): category1 = {'categoryCode': 'cat1'} price1 = {'id': 1234, 'locationGroupId': '', "capacityRestrictionMaximum": "16", "capacityRestrictionMinimum": "1", "capacityRestrictionType": "STORAGE_SPACE", 'categories': [category1], 'termLength': 36} price2 = {'id': 45678, 'locationGroupId': '', "capacityRestrictionMaximum": "16", "capacityRestrictionMinimum": "1", "capacityRestrictionType": "STORAGE_SPACE", 'categories': [category1], 'termLength': 0} # Test 0 termLength price_id = self.ordering.get_item_price_id("8", [price2, price1]) self.assertEqual(45678, price_id) # Test None termLength price2['termLength'] = None price_id = self.ordering.get_item_price_id("8", [price2, price1]) self.assertEqual(45678, price_id) # Test '' termLength price2['termLength'] = '' price_id = self.ordering.get_item_price_id("8", [price2, price1]) self.assertEqual(45678, price_id) def test_issues1425_nonzeroterm(self): category1 = {'categoryCode': 'cat1'} price1 = {'id': 1234, 'locationGroupId': '', "capacityRestrictionMaximum": "16", "capacityRestrictionMinimum": "1", "capacityRestrictionType": "STORAGE_SPACE", 'categories': [category1], 'termLength': 36} price2 = {'id': 45678, 'locationGroupId': '', "capacityRestrictionMaximum": "16", "capacityRestrictionMinimum": "1", "capacityRestrictionType": "STORAGE_SPACE", 'categories': [category1], 'termLength': 0} # Test 36 termLength price_id = self.ordering.get_item_price_id("8", [price2, price1], 36) self.assertEqual(1234, price_id) # Test None-existing price for term price_id = self.ordering.get_item_price_id("8", [price2, price1], 37) self.assertEqual(None, price_id) softlayer-python-5.9.8/tests/managers/sshkey_tests.py000066400000000000000000000037501415376457700231550ustar00rootroot00000000000000""" 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.9.8/tests/managers/ssl_tests.py000066400000000000000000000057541415376457700224560ustar00rootroot00000000000000""" 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.9.8/tests/managers/storage_generic_tests.py000066400000000000000000000025441415376457700250070ustar00rootroot00000000000000""" SoftLayer.tests.managers.storage_generic_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import SoftLayer from SoftLayer import testing class StorageGenericTests(testing.TestCase): def set_up(self): self.storage = SoftLayer.managers.storage.StorageManager(self.client) def test_get_volume_snapshot_notification_status(self): mock = self.set_mock('SoftLayer_Network_Storage', 'getSnapshotNotificationStatus') # These are the values we expect from the API as of 2021-12-01, FBLOCK4193 mock.side_effect = [None, '1', '0'] expected = [1, 1, 0] for expect in expected: result = self.storage.get_volume_snapshot_notification_status(12345) self.assert_called_with('SoftLayer_Network_Storage', 'getSnapshotNotificationStatus', identifier=12345) self.assertEqual(expect, result) def test_set_volume_snapshot_notification(self): mock = self.set_mock('SoftLayer_Network_Storage', 'setSnapshotNotification') mock.return_value = None result = self.storage.set_volume_snapshot_notification(12345, False) self.assert_called_with('SoftLayer_Network_Storage', 'setSnapshotNotification', identifier=12345, args=(False,)) self.assertEqual(None, result) softlayer-python-5.9.8/tests/managers/storage_utils_tests.py000066400000000000000000004267111415376457700245410ustar00rootroot00000000000000""" SoftLayer.tests.managers.storage_utils_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import copy import SoftLayer from SoftLayer import exceptions from SoftLayer.fixtures import SoftLayer_Network_Storage from SoftLayer.fixtures import SoftLayer_Product_Package 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() self.assertEqual([], host_templates) def test_populate_host_templates_empty_arrays_given(self): host_templates = storage_utils.populate_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( 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 = [ SoftLayer_Product_Package.SAAS_PACKAGE, 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 = [SoftLayer_Product_Package.SAAS_PACKAGE] result = storage_utils.get_package(self.block, 'storage_as_a_service') self.assertEqual( 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_empty(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) def test_find_price_by_category_none(self): package = { 'items': [ {'capacity': '0', 'prices': [ {'id': 189433, 'categories': [ {'categoryCode': 'storage_as_a_service'} ], 'locationGroupId': None} ]} ]} 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(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(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = 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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = 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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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 = [ SoftLayer_Product_Package.ENTERPRISE_PACKAGE ] mock_volume = 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 = [ SoftLayer_Product_Package.ENTERPRISE_PACKAGE ] mock_volume = 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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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 = [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_rest(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 = [SoftLayer_Product_Package.SAAS_REST_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 = [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 = [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 = [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 = [ 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 = [ 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 = [ 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 = [ 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(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(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(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 = 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(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(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = 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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = 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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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 = [ SoftLayer_Product_Package.ENTERPRISE_PACKAGE ] mock_volume = 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 = [ SoftLayer_Product_Package.ENTERPRISE_PACKAGE ] mock_volume = 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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = 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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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(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(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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 = [SoftLayer_Product_Package.SAAS_PACKAGE] mock_volume = copy.deepcopy(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.9.8/tests/managers/tag_tests.py000066400000000000000000000222351415376457700224210ustar00rootroot00000000000000""" SoftLayer.tests.managers.tag_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ from SoftLayer.exceptions import SoftLayerAPIError from SoftLayer.managers import tags from SoftLayer import testing class TagTests(testing.TestCase): def set_up(self): self.tag_manager = tags.TagManager(self.client) self.test_mask = "mask[id]" def test_list_tags(self): result = self.tag_manager.list_tags() self.assert_called_with('SoftLayer_Tag', 'getUnattachedTagsForCurrentUser') self.assert_called_with('SoftLayer_Tag', 'getAttachedTagsForCurrentUser') self.assertIn('attached', result.keys()) self.assertIn('unattached', result.keys()) def test_list_tags_mask(self): result = self.tag_manager.list_tags(mask=self.test_mask) self.assert_called_with('SoftLayer_Tag', 'getUnattachedTagsForCurrentUser', mask=self.test_mask) self.assert_called_with('SoftLayer_Tag', 'getAttachedTagsForCurrentUser', mask=self.test_mask) self.assertIn('attached', result.keys()) self.assertIn('unattached', result.keys()) def test_unattached_tags(self): result = self.tag_manager.get_unattached_tags() self.assertEqual('coreos', result[0].get('name')) self.assert_called_with('SoftLayer_Tag', 'getUnattachedTagsForCurrentUser', mask=None) def test_unattached_tags_mask(self): result = self.tag_manager.get_unattached_tags(mask=self.test_mask) self.assertEqual('coreos', result[0].get('name')) self.assert_called_with('SoftLayer_Tag', 'getUnattachedTagsForCurrentUser', mask=self.test_mask) def test_attached_tags(self): result = self.tag_manager.get_attached_tags() self.assertEqual('bs_test_instance', result[0].get('name')) self.assert_called_with('SoftLayer_Tag', 'getAttachedTagsForCurrentUser', mask=None) def test_attached_tags_mask(self): result = self.tag_manager.get_attached_tags(mask=self.test_mask) self.assertEqual('bs_test_instance', result[0].get('name')) self.assert_called_with('SoftLayer_Tag', 'getAttachedTagsForCurrentUser', mask=self.test_mask) def test_get_tag_references(self): tag_id = 1286571 result = self.tag_manager.get_tag_references(tag_id) self.assertEqual(tag_id, result[0].get('tagId')) self.assert_called_with('SoftLayer_Tag', 'getReferences', identifier=tag_id) def test_get_tag_references_mask(self): tag_id = 1286571 result = self.tag_manager.get_tag_references(tag_id, mask=self.test_mask) self.assertEqual(tag_id, result[0].get('tagId')) self.assert_called_with('SoftLayer_Tag', 'getReferences', identifier=tag_id, mask=self.test_mask) def test_reference_lookup_hardware(self): resource_id = 12345 tag_type = 'HARDWARE' self.tag_manager.reference_lookup(resource_id, tag_type) self.assert_called_with('SoftLayer_Hardware', 'getObject', identifier=resource_id) def test_reference_lookup_guest(self): resource_id = 12345 tag_type = 'GUEST' self.tag_manager.reference_lookup(resource_id, tag_type) self.assert_called_with('SoftLayer_Virtual_Guest', 'getObject', identifier=resource_id) def test_reference_lookup_app_delivery(self): resource_id = 12345 tag_type = 'APPLICATION_DELIVERY_CONTROLLER' self.tag_manager.reference_lookup(resource_id, tag_type) self.assert_called_with('SoftLayer_Network_Application_Delivery_Controller', 'getObject', identifier=resource_id) def test_reference_lookup_dedicated(self): resource_id = 12345 tag_type = 'DEDICATED_HOST' self.tag_manager.reference_lookup(resource_id, tag_type) self.assert_called_with('SoftLayer_Virtual_DedicatedHost', 'getObject', identifier=resource_id) def test_reference_lookup_document(self): resource_id = 12345 tag_type = 'ACCOUNT_DOCUMENT' exception = self.assertRaises( SoftLayerAPIError, self.tag_manager.reference_lookup, resource_id, tag_type ) self.assertEqual(exception.faultCode, 404) self.assertEqual(exception.reason, "Unable to lookup ACCOUNT_DOCUMENT types") def test_set_tags(self): tags = "tag1,tag2" key_name = "GUEST" resource_id = 100 self.tag_manager.set_tags(tags, key_name, resource_id) self.assert_called_with('SoftLayer_Tag', 'setTags') def test_get_tag(self): tag_id = 1286571 result = self.tag_manager.get_tag(tag_id) self.assertEqual(tag_id, result.get('id')) self.assert_called_with('SoftLayer_Tag', 'getObject', identifier=tag_id) def test_get_tag_mask(self): tag_id = 1286571 result = self.tag_manager.get_tag(tag_id, mask=self.test_mask) self.assertEqual(tag_id, result.get('id')) self.assert_called_with('SoftLayer_Tag', 'getObject', identifier=tag_id, mask=self.test_mask) def test_get_tag_by_name(self): tag_name = 'bs_test_instance' result = self.tag_manager.get_tag_by_name(tag_name) args = (tag_name,) self.assertEqual(tag_name, result[0].get('name')) self.assert_called_with('SoftLayer_Tag', 'getTagByTagName', args=args) def test_get_tag_by_name_mask(self): tag_name = 'bs_test_instance' result = self.tag_manager.get_tag_by_name(tag_name, mask=self.test_mask) args = (tag_name,) self.assertEqual(tag_name, result[0].get('name')) self.assert_called_with('SoftLayer_Tag', 'getTagByTagName', mask=self.test_mask, args=args) def test_taggable_by_type_main(self): result = self.tag_manager.taggable_by_type("HARDWARE") self.assertEqual("SoftLayer_Hardware", result[0].get('resourceType')) self.assert_called_with('SoftLayer_Search', 'advancedSearch', args=('_objectType:SoftLayer_Hardware',)) def test_taggable_by_type_ticket(self): mock = self.set_mock('SoftLayer_Search', 'advancedSearch') mock.return_value = [ { "resourceType": "SoftLayer_Ticket", "resource": { "domain": "vmware.test.com", } } ] result = self.tag_manager.taggable_by_type("TICKET") self.assertEqual("SoftLayer_Ticket", result[0].get('resourceType')) self.assert_called_with('SoftLayer_Search', 'advancedSearch', args=('_objectType:SoftLayer_Ticket status.name: open',)) def test_taggable_by_type_image_template(self): result = self.tag_manager.taggable_by_type("IMAGE_TEMPLATE") self.assertEqual("Virtual_Guest_Block_Device_Template_Group", result[0].get('resourceType')) self.assert_called_with('SoftLayer_Account', 'getPrivateBlockDeviceTemplateGroups') def test_taggable_by_type_network_subnet(self): result = self.tag_manager.taggable_by_type("NETWORK_SUBNET") self.assertEqual("Network_Subnet", result[0].get('resourceType')) self.assert_called_with('SoftLayer_Account', 'getSubnets') def test_type_to_service(self): in_out = [ {'input': 'ACCOUNT_DOCUMENT', 'output': None}, {'input': 'APPLICATION_DELIVERY_CONTROLLER', 'output': 'Network_Application_Delivery_Controller'}, {'input': 'GUEST', 'output': 'Virtual_Guest'}, {'input': 'DEDICATED_HOST', 'output': 'Virtual_DedicatedHost'}, {'input': 'IMAGE_TEMPLATE', 'output': 'Virtual_Guest_Block_Device_Template_Group'}, {'input': 'HARDWARE', 'output': 'Hardware'}, {'input': 'NETWORK_VLAN', 'output': 'Network_Vlan'}, ] for test in in_out: result = self.tag_manager.type_to_service(test.get('input')) self.assertEqual(test.get('output'), result) def test_get_resource_name(self): resource = { 'primaryIpAddress': '4.4.4.4', 'vlanNumber': '12345', 'name': 'testName', 'subject': 'TEST SUBJECT', 'networkIdentifier': '127.0.0.1', 'fullyQualifiedDomainName': 'test.test.com' } in_out = [ {'input': 'NETWORK_VLAN_FIREWALL', 'output': resource.get('primaryIpAddress')}, {'input': 'NETWORK_VLAN', 'output': "{} ({})".format(resource.get('vlanNumber'), resource.get('name'))}, {'input': 'IMAGE_TEMPLATE', 'output': resource.get('name')}, {'input': 'APPLICATION_DELIVERY_CONTROLLER', 'output': resource.get('name')}, {'input': 'TICKET', 'output': resource.get('subjet')}, {'input': 'NETWORK_SUBNET', 'output': resource.get('networkIdentifier')}, {'input': 'HARDWARE', 'output': resource.get('fullyQualifiedDomainName')}, ] for test in in_out: result = self.tag_manager.get_resource_name(resource, test.get('input')) self.assertEqual(test.get('output'), result) softlayer-python-5.9.8/tests/managers/ticket_tests.py000066400000000000000000000102021415376457700231200ustar00rootroot00000000000000""" 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_tickets_false(self): exception = self.assertRaises(ValueError, self.ticket.list_tickets, open_status=False, closed_status=False) self.assertEqual('open_status and closed_status cannot both be False', str(exception)) 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, "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.9.8/tests/managers/user_tests.py000066400000000000000000000310141415376457700226170ustar00rootroot00000000000000""" SoftLayer.tests.managers.user_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ """ import datetime from unittest import mock as mock import SoftLayer from SoftLayer import exceptions from SoftLayer import testing real_datetime_class = datetime.datetime def mock_datetime(target, datetime_module): """A way to use specific datetimes in tests. Just mocking datetime doesn't work because of pypy https://solidgeargroup.com/mocking-the-time """ class DatetimeSubclassMeta(type): @classmethod def __instancecheck__(mcs, obj): return isinstance(obj, real_datetime_class) class BaseMockedDatetime(real_datetime_class): @classmethod def now(cls, tz=None): return target.replace(tzinfo=tz) @classmethod def utcnow(cls): return target @classmethod def today(cls): return target # Python2 & Python3-compatible metaclass MockedDatetime = DatetimeSubclassMeta('datetime', (BaseMockedDatetime,), {}) return mock.patch.object(datetime_module, 'datetime', MockedDatetime) class UserManagerTests(testing.TestCase): def set_up(self): self.manager = SoftLayer.UserManager(self.client) def test_list_user_defaults(self): self.manager.list_users() self.assert_called_with('SoftLayer_Account', 'getUsers', mask=mock.ANY) def test_list_user_mask(self): self.manager.list_users(objectmask="mask[id]") self.assert_called_with('SoftLayer_Account', 'getUsers', mask="mask[id]") def test_list_user_filter(self): test_filter = {'id': {'operation': 1234}} self.manager.list_users(objectfilter=test_filter) self.assert_called_with('SoftLayer_Account', 'getUsers', filter=test_filter) def test_get_user_default(self): self.manager.get_user(1234) self.assert_called_with('SoftLayer_User_Customer', 'getObject', identifier=1234, mask="mask[userStatus[name], parent[id, username]]") def test_get_user_mask(self): self.manager.get_user(1234, objectmask="mask[id]") self.assert_called_with('SoftLayer_User_Customer', 'getObject', identifier=1234, mask="mask[id]") def test_get_all_permissions(self): self.manager.get_all_permissions() self.assert_called_with('SoftLayer_User_Customer_CustomerPermission_Permission', 'getAllObjects') def test_add_permissions(self): self.manager.add_permissions(1234, ['TEST']) expected_args = ( [{'keyName': 'TEST'}], ) self.assert_called_with('SoftLayer_User_Customer', 'addBulkPortalPermission', args=expected_args, identifier=1234) def test_remove_permissions(self): self.manager.remove_permissions(1234, ['TEST']) expected_args = ( [{'keyName': 'TEST'}], ) self.assert_called_with('SoftLayer_User_Customer', 'removeBulkPortalPermission', args=expected_args, identifier=1234) def test_get_logins_default(self): target = datetime.datetime(2018, 5, 15) with mock_datetime(target, datetime): self.manager.get_logins(1234) expected_filter = { 'loginAttempts': { 'createDate': { 'operation': 'greaterThanDate', 'options': [{'name': 'date', 'value': ['04/15/2018 0:0:0']}] } } } self.assert_called_with('SoftLayer_User_Customer', 'getLoginAttempts', filter=expected_filter) def test_get_events_default(self): target = datetime.datetime(2018, 5, 15) with mock_datetime(target, datetime): self.manager.get_events(1234) expected_filter = { 'userId': { 'operation': 1234 }, 'eventCreateDate': { 'operation': 'greaterThanDate', 'options': [{'name': 'date', 'value': ['2018-04-15T00:00:00']}] } } self.assert_called_with('SoftLayer_Event_Log', 'getAllObjects', filter=expected_filter) def test_get_events_empty(self): event_mock = self.set_mock('SoftLayer_Event_Log', 'getAllObjects') event_mock.return_value = None result = self.manager.get_events(1234) self.assert_called_with('SoftLayer_Event_Log', 'getAllObjects', filter=mock.ANY) self.assertEqual([{'eventName': 'No Events Found'}], result) @mock.patch('SoftLayer.managers.user.UserManager.get_user_permissions') def test_permissions_from_user(self, user_permissions): user_permissions.return_value = [ {"keyName": "TICKET_VIEW"}, {"keyName": "TEST"} ] removed_permissions = [ {'keyName': 'ACCESS_ALL_HARDWARE'}, {'keyName': 'ACCESS_ALL_HARDWARE'}, {'keyName': 'ACCOUNT_SUMMARY_VIEW'}, {'keyName': 'ADD_SERVICE_STORAGE'}, {'keyName': 'TEST_3'}, {'keyName': 'TEST_4'} ] self.manager.permissions_from_user(1234, 5678) self.assert_called_with('SoftLayer_User_Customer', 'addBulkPortalPermission', args=(user_permissions.return_value,)) self.assert_called_with('SoftLayer_User_Customer', 'removeBulkPortalPermission', args=(removed_permissions,)) def test_get_id_from_username_one_match(self): account_mock = self.set_mock('SoftLayer_Account', 'getUsers') account_mock.return_value = [{'id': 1234}] user_id = self.manager._get_id_from_username('testUser') expected_filter = {'users': {'username': {'operation': '_= testUser'}}} self.assert_called_with('SoftLayer_Account', 'getUsers', filter=expected_filter, mask="mask[id, username]") self.assertEqual([1234], user_id) def test_get_id_from_username_multiple_match(self): account_mock = self.set_mock('SoftLayer_Account', 'getUsers') account_mock.return_value = [{'id': 1234}, {'id': 4567}] self.assertRaises(exceptions.SoftLayerError, self.manager._get_id_from_username, 'testUser') def test_get_id_from_username_zero_match(self): account_mock = self.set_mock('SoftLayer_Account', 'getUsers') account_mock.return_value = [] self.assertRaises(exceptions.SoftLayerError, self.manager._get_id_from_username, 'testUser') def test_format_permission_object(self): result = self.manager.format_permission_object(['TEST']) self.assert_called_with('SoftLayer_User_Customer_CustomerPermission_Permission', 'getAllObjects') self.assertEqual([{'keyName': 'TEST'}], result) def test_format_permission_object_all(self): expected = [ {'key': 'T_2', 'keyName': 'TEST', 'name': 'A Testing Permission'}, {'key': 'T_1', 'keyName': 'TICKET_VIEW', 'name': 'View Tickets'} ] service_name = 'SoftLayer_User_Customer_CustomerPermission_Permission' permission_mock = self.set_mock(service_name, 'getAllObjects') permission_mock.return_value = expected result = self.manager.format_permission_object(['ALL']) self.assert_called_with(service_name, 'getAllObjects') self.assertEqual(expected, result) def test_get_current_user(self): result = self.manager.get_current_user() self.assert_called_with('SoftLayer_Account', 'getCurrentUser', mask=mock.ANY) self.assertEqual(result['id'], 12345) def test_get_current_user_mask(self): result = self.manager.get_current_user(objectmask="mask[id]") self.assert_called_with('SoftLayer_Account', 'getCurrentUser', mask="mask[id]") self.assertEqual(result['id'], 12345) def test_create_user_handle_paas_exception(self): user_template = {"username": "foobar", "email": "foobar@example.com"} self.manager.user_service = mock.Mock() # FaultCode IS NOT SoftLayer_Exception_User_Customer_DelegateIamIdInvitationToPaas any_error = exceptions.SoftLayerAPIError("SoftLayer_Exception_User_Customer", "This exception indicates an error") self.manager.user_service.createObject.side_effect = any_error try: self.manager.create_user(user_template, "Pass@123") except exceptions.SoftLayerAPIError as ex: self.assertEqual(ex.faultCode, "SoftLayer_Exception_User_Customer") self.assertEqual(ex.faultString, "This exception indicates an error") # FaultCode is SoftLayer_Exception_User_Customer_DelegateIamIdInvitationToPaas paas_error = exceptions.SoftLayerAPIError("SoftLayer_Exception_User_Customer_DelegateIamIdInvitationToPaas", "This exception does NOT indicate an error") self.manager.user_service.createObject.side_effect = paas_error try: self.manager.create_user(user_template, "Pass@123") except exceptions.SoftLayerError as ex: self.assertEqual(ex.args[0], "Your request for a new user was received, but it needs to be processed by " "the Platform Services API first. Barring any errors on the Platform Services " "side, your new user should be created shortly.") def test_vpn_manual(self): user_id = 1234 self.manager.vpn_manual(user_id, True) self.assert_called_with('SoftLayer_User_Customer', 'editObject', identifier=user_id) def test_vpn_subnet_add(self): user_id = 1234 subnet_id = 1234 expected_args = ( [{"userId": user_id, "subnetId": subnet_id}], ) self.manager.vpn_subnet_add(user_id, [subnet_id]) self.assert_called_with('SoftLayer_Network_Service_Vpn_Overrides', 'createObjects', args=expected_args) self.assert_called_with('SoftLayer_User_Customer', 'updateVpnUser', identifier=user_id) def test_vpn_subnet_remove(self): user_id = 1234 subnet_id = 1234 overrides = [{'id': 3661234, 'subnetId': subnet_id}] expected_args = ( overrides, ) self.manager.vpn_subnet_remove(user_id, [subnet_id]) self.assert_called_with('SoftLayer_Network_Service_Vpn_Overrides', 'deleteObjects', args=expected_args) self.assert_called_with('SoftLayer_User_Customer', 'updateVpnUser', identifier=user_id) def test_get_all_notifications(self): self.manager.get_all_notifications() self.assert_called_with('SoftLayer_Email_Subscription', 'getAllObjects') def test_enable_notifications(self): self.manager.enable_notifications(['Test notification']) self.assert_called_with('SoftLayer_Email_Subscription', 'enable', identifier=111) def test_disable_notifications(self): self.manager.disable_notifications(['Test notification']) self.assert_called_with('SoftLayer_Email_Subscription', 'disable', identifier=111) def test_enable_notifications_fail(self): notification = self.set_mock('SoftLayer_Email_Subscription', 'enable') notification.return_value = False result = self.manager.enable_notifications(['Test notification']) self.assert_called_with('SoftLayer_Email_Subscription', 'enable', identifier=111) self.assertFalse(result) def test_disable_notifications_fail(self): notification = self.set_mock('SoftLayer_Email_Subscription', 'disable') notification.return_value = False result = self.manager.disable_notifications(['Test notification']) self.assert_called_with('SoftLayer_Email_Subscription', 'disable', identifier=111) self.assertFalse(result) def test_gather_notifications(self): expected_result = [ {'description': 'Testing description.', 'enabled': True, 'id': 111, 'name': 'Test notification' } ] result = self.manager.gather_notifications(['Test notification']) self.assert_called_with('SoftLayer_Email_Subscription', 'getAllObjects', mask='mask[enabled]') self.assertEqual(result, expected_result) def test_gather_notifications_fail(self): ex = self.assertRaises(SoftLayer.SoftLayerError, self.manager.gather_notifications, ['Test not exit']) self.assertEqual("Test not exit is not a valid notification name", str(ex)) softlayer-python-5.9.8/tests/managers/vs/000077500000000000000000000000001415376457700204765ustar00rootroot00000000000000softlayer-python-5.9.8/tests/managers/vs/__init__.py000066400000000000000000000000001415376457700225750ustar00rootroot00000000000000softlayer-python-5.9.8/tests/managers/vs/vs_capacity_tests.py000066400000000000000000000175161415376457700246110ustar00rootroot00000000000000""" SoftLayer.tests.managers.vs.vs_capacity_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ from unittest import mock as mock import SoftLayer from SoftLayer import fixtures from SoftLayer.fixtures import SoftLayer_Product_Package from SoftLayer import testing class VSManagerCapacityTests(testing.TestCase): def set_up(self): self.manager = SoftLayer.CapacityManager(self.client) amock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects') amock.return_value = fixtures.SoftLayer_Product_Package.RESERVED_CAPACITY def test_list(self): self.manager.list() self.assert_called_with('SoftLayer_Account', 'getReservedCapacityGroups') def test_get_object(self): self.manager.get_object(100) self.assert_called_with('SoftLayer_Virtual_ReservedCapacityGroup', 'getObject', identifier=100) def test_get_object_mask(self): mask = "mask[id]" self.manager.get_object(100, mask=mask) self.assert_called_with('SoftLayer_Virtual_ReservedCapacityGroup', 'getObject', identifier=100, mask=mask) def test_get_create_options(self): self.manager.get_create_options() self.assert_called_with('SoftLayer_Product_Package', 'getItems', identifier=1059, mask=mock.ANY) def test_get_available_routers(self): result = self.manager.get_available_routers() package_filter = {'keyName': {'operation': 'RESERVED_CAPACITY'}} self.assert_called_with('SoftLayer_Product_Package', 'getAllObjects', mask=mock.ANY, filter=package_filter) self.assert_called_with('SoftLayer_Product_Package', 'getRegions', mask=mock.ANY) self.assert_called_with('SoftLayer_Network_Pod', 'getAllObjects') self.assertEqual(result[0]['keyname'], 'WASHINGTON07') def test_get_available_routers_search(self): result = self.manager.get_available_routers('wdc07') package_filter = {'keyName': {'operation': 'RESERVED_CAPACITY'}} pod_filter = {'datacenterName': {'operation': 'wdc07'}} self.assert_called_with('SoftLayer_Product_Package', 'getAllObjects', mask=mock.ANY, filter=package_filter) self.assert_called_with('SoftLayer_Product_Package', 'getRegions', mask=mock.ANY) self.assert_called_with('SoftLayer_Network_Pod', 'getAllObjects', filter=pod_filter) self.assertEqual(result[0]['keyname'], 'WASHINGTON07') def test_create(self): item_mock = self.set_mock('SoftLayer_Product_Package', 'getItems') item_mock.return_value = SoftLayer_Product_Package.getItems_RESERVED_CAPACITY self.manager.create( name='TEST', backend_router_id=1, flavor='B1_1X2_1_YEAR_TERM', instances=5) expected_args = { 'orderContainers': [ { 'backendRouterId': 1, 'name': 'TEST', 'packageId': 1059, 'location': 0, 'quantity': 5, 'useHourlyPricing': True, 'complexType': 'SoftLayer_Container_Product_Order_Virtual_ReservedCapacity', 'prices': [{'id': 217561} ] } ] } self.assert_called_with('SoftLayer_Product_Package', 'getAllObjects') self.assert_called_with('SoftLayer_Product_Package', 'getItems', identifier=1059) self.assert_called_with('SoftLayer_Product_Order', 'placeOrder', args=(expected_args,)) def test_create_test(self): item_mock = self.set_mock('SoftLayer_Product_Package', 'getItems') item_mock.return_value = SoftLayer_Product_Package.getItems_RESERVED_CAPACITY self.manager.create( name='TEST', backend_router_id=1, flavor='B1_1X2_1_YEAR_TERM', instances=5, test=True) expected_args = { 'orderContainers': [ { 'backendRouterId': 1, 'name': 'TEST', 'packageId': 1059, 'location': 0, 'quantity': 5, 'useHourlyPricing': True, 'complexType': 'SoftLayer_Container_Product_Order_Virtual_ReservedCapacity', 'prices': [{'id': 217561}], } ] } self.assert_called_with('SoftLayer_Product_Package', 'getAllObjects') self.assert_called_with('SoftLayer_Product_Package', 'getItems', identifier=1059) self.assert_called_with('SoftLayer_Product_Order', 'verifyOrder', args=(expected_args,)) def test_create_guest(self): amock = self.set_mock('SoftLayer_Product_Package', 'getItems') amock.return_value = fixtures.SoftLayer_Product_Package.getItems_1_IPV6_ADDRESS guest_object = { 'boot_mode': None, 'disks': (), 'domain': 'test.com', 'hostname': 'A1538172419', 'ipv6': True, 'local_disk': None, 'os_code': 'UBUNTU_LATEST_64', 'primary_disk': '25', 'private': False, 'private_subnet': None, 'public_subnet': None, 'ssh_keys': [1234] } self.manager.create_guest(123, False, guest_object) expectedGenerate = { 'startCpus': None, 'maxMemory': None, 'hostname': 'A1538172419', 'domain': 'test.com', 'hourlyBillingFlag': False, 'supplementalCreateObjectOptions': { 'bootMode': None, 'flavorKeyName': 'B1_1X2X25' }, 'operatingSystemReferenceCode': 'UBUNTU_LATEST_64', 'datacenter': {'name': 'dal13'}, 'sshKeys': [{'id': 1234}], 'localDiskFlag': False } self.assert_called_with('SoftLayer_Virtual_ReservedCapacityGroup', 'getObject', mask=mock.ANY) self.assert_called_with('SoftLayer_Virtual_Guest', 'generateOrderTemplate', args=(expectedGenerate,)) self.assert_called_with('SoftLayer_Product_Package', 'getAllObjects') # id=1059 comes from fixtures.SoftLayer_Product_Order.RESERVED_CAPACITY, production is 859 self.assert_called_with('SoftLayer_Product_Package', 'getItems', identifier=1059) self.assert_called_with('SoftLayer_Product_Order', 'placeOrder') def test_create_guest_no_flavor(self): guest_object = { 'boot_mode': None, 'disks': (), 'domain': 'test.com', 'hostname': 'A1538172419', 'hourly': True, 'ipv6': True, 'local_disk': None, 'os_code': 'UBUNTU_LATEST_64', 'private': False, 'private_subnet': None, 'public_subnet': None, 'ssh_keys': [1234] } self.assertRaises(SoftLayer.SoftLayerError, self.manager.create_guest, 123, False, guest_object) def test_create_guest_testing(self): amock = self.set_mock('SoftLayer_Product_Package', 'getItems') amock.return_value = fixtures.SoftLayer_Product_Package.getItems_1_IPV6_ADDRESS guest_object = { 'boot_mode': None, 'disks': (), 'domain': 'test.com', 'hostname': 'A1538172419', 'hourly': True, 'ipv6': True, 'local_disk': None, 'os_code': 'UBUNTU_LATEST_64', 'primary_disk': '25', 'private': False, 'private_subnet': None, 'public_subnet': None, 'ssh_keys': [1234] } self.manager.create_guest(123, True, guest_object) self.assert_called_with('SoftLayer_Product_Order', 'verifyOrder') def test_flavor_string(self): from SoftLayer.managers.vs_capacity import _flavor_string as _flavor_string result = _flavor_string('B1_1X2_1_YEAR_TERM', '25') self.assertEqual('B1_1X2X25', result) softlayer-python-5.9.8/tests/managers/vs/vs_order_tests.py000066400000000000000000000250241415376457700241200ustar00rootroot00000000000000""" SoftLayer.tests.managers.vs.vs_order_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ These tests deal with ordering in the VS manager. :license: MIT, see LICENSE for more details. """ from unittest import mock as mock import SoftLayer from SoftLayer import exceptions from SoftLayer import fixtures from SoftLayer import testing class VSOrderTests(testing.TestCase): def set_up(self): self.vs = SoftLayer.VSManager(self.client) @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},)) 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_with_flavor(self): # Testing Upgrade with parameter preset result = self.vs.upgrade(1, preset="M1_64X512X100", 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.assertEqual(799, order_container['presetId']) self.assertIn({'id': 1}, order_container['virtualGuests']) 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) def test__get_price_id_for_upgrade_find_private_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='4', public=False) self.assertEqual(1007, price_id) def test_upgrade_mem_and_preset_exception(self): self.assertRaises( ValueError, self.vs.upgrade, 1234, memory=10, preset="M1_64X512X100" ) def test_upgrade_cpu_and_preset_exception(self): self.assertRaises( ValueError, self.vs.upgrade, 1234, cpus=10, preset="M1_64X512X100" ) @mock.patch('SoftLayer.managers.vs.VSManager._get_price_id_for_upgrade_option') def test_upgrade_no_price_exception(self, get_price): get_price.return_value = None self.assertRaises( exceptions.SoftLayerError, self.vs.upgrade, 1234, memory=1, ) @mock.patch('SoftLayer.managers.vs.VSManager._generate_create_dict') def test_order_guest(self, create_dict): create_dict.return_value = {'test': 1, 'verify': 1} guest = {'test': 1, 'verify': 1, 'tags': ['First']} result = self.vs.order_guest(guest, test=False) create_dict.assert_called_once_with(test=1, verify=1) self.assertEqual(1234, result['orderId']) self.assert_called_with('SoftLayer_Virtual_Guest', 'generateOrderTemplate') self.assert_called_with('SoftLayer_Product_Order', 'placeOrder') self.assert_called_with('SoftLayer_Virtual_Guest', 'setTags', identifier=1234567) @mock.patch('SoftLayer.managers.vs.VSManager._generate_create_dict') def test_order_guest_verify(self, create_dict): create_dict.return_value = {'test': 1, 'verify': 1} guest = {'test': 1, 'verify': 1, 'tags': ['First']} result = self.vs.order_guest(guest, test=True) create_dict.assert_called_once_with(test=1, verify=1) self.assertEqual(1234, result['orderId']) self.assert_called_with('SoftLayer_Virtual_Guest', 'generateOrderTemplate') self.assert_called_with('SoftLayer_Product_Order', 'verifyOrder') @mock.patch('SoftLayer.managers.vs.VSManager._generate_create_dict') def test_order_guest_ipv6(self, create_dict): amock = self.set_mock('SoftLayer_Product_Package', 'getItems') amock.return_value = fixtures.SoftLayer_Product_Package.getItems_1_IPV6_ADDRESS create_dict.return_value = {'test': 1, 'verify': 1} guest = {'test': 1, 'verify': 1, 'tags': ['First'], 'ipv6': True} result = self.vs.order_guest(guest, test=True) self.assertEqual(1234, result['orderId']) self.assert_called_with('SoftLayer_Virtual_Guest', 'generateOrderTemplate') self.assert_called_with('SoftLayer_Product_Package', 'getItems', identifier=200) self.assert_called_with('SoftLayer_Product_Order', 'verifyOrder') @mock.patch('SoftLayer.managers.vs.VSManager._generate_create_dict') def test_order_guest_placement_group(self, create_dict): create_dict.return_value = {'test': 1, 'verify': 1} guest = {'test': 1, 'verify': 1, 'placement_id': 5} result = self.vs.order_guest(guest, test=True) call = self.calls('SoftLayer_Product_Order', 'verifyOrder')[0] order_container = call.args[0] self.assertEqual(1234, result['orderId']) self.assertEqual(5, order_container['virtualGuests'][0]['placementGroupId']) self.assert_called_with('SoftLayer_Virtual_Guest', 'generateOrderTemplate') self.assert_called_with('SoftLayer_Product_Order', 'verifyOrder') def test_get_price_id_empty(self): upgrade_prices = [ {'categories': None, 'item': None}, {'categories': [{'categoryCode': 'ram'}], 'item': None}, {'categories': None, 'item': {'capacity': 1}}, ] result = self.vs._get_price_id_for_upgrade_option(upgrade_prices, 'memory', 1) self.assertEqual(None, result) def test_get_price_id_memory_capacity(self): upgrade_prices = [ {'categories': [{'categoryCode': 'ram'}], 'item': {'capacity': 1}, 'id': 99} ] result = self.vs._get_price_id_for_upgrade_option(upgrade_prices, 'memory', 1) self.assertEqual(99, result) def test_get_price_id_mismatch_capacity(self): upgrade_prices = [ {'categories': [{'categoryCode': 'ram1'}], 'item': {'capacity': 1}, 'id': 90}, {'categories': [{'categoryCode': 'ram'}], 'item': {'capacity': 2}, 'id': 91}, {'categories': [{'categoryCode': 'ram'}], 'item': {'capacity': 1}, 'id': 92}, ] result = self.vs._get_price_id_for_upgrade_option(upgrade_prices, 'memory', 1) self.assertEqual(92, result) softlayer-python-5.9.8/tests/managers/vs/vs_placement_tests.py000066400000000000000000000053141415376457700247550ustar00rootroot00000000000000""" SoftLayer.tests.managers.vs.vs_placement_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ from unittest import mock as mock from SoftLayer.managers.vs_placement import PlacementManager from SoftLayer import testing class VSPlacementManagerTests(testing.TestCase): def set_up(self): self.manager = PlacementManager(self.client) def test_list(self): self.manager.list() self.assert_called_with('SoftLayer_Account', 'getPlacementGroups', mask=mock.ANY) def test_list_mask(self): mask = "mask[id]" self.manager.list(mask) self.assert_called_with('SoftLayer_Account', 'getPlacementGroups', mask=mask) def test_create(self): placement_object = { 'backendRouter': 1234, 'name': 'myName', 'ruleId': 1 } self.manager.create(placement_object) self.assert_called_with('SoftLayer_Virtual_PlacementGroup', 'createObject', args=(placement_object,)) def test_get_object(self): self.manager.get_object(1234) self.assert_called_with('SoftLayer_Virtual_PlacementGroup', 'getObject', identifier=1234, mask=mock.ANY) def test_get_object_with_mask(self): mask = "mask[id]" self.manager.get_object(1234, mask) self.assert_called_with('SoftLayer_Virtual_PlacementGroup', 'getObject', identifier=1234, mask=mask) def test_delete(self): self.manager.delete(1234) self.assert_called_with('SoftLayer_Virtual_PlacementGroup', 'deleteObject', identifier=1234) def test_get_id_from_name(self): self.manager._get_id_from_name('test') _filter = { 'placementGroups': { 'name': {'operation': 'test'} } } self.assert_called_with('SoftLayer_Account', 'getPlacementGroups', filter=_filter, mask="mask[id, name]") def test_get_rule_id_from_name(self): result = self.manager.get_rule_id_from_name('SPREAD') self.assertEqual(result[0], 1) result = self.manager.get_rule_id_from_name('SpReAd') self.assertEqual(result[0], 1) def test_get_rule_id_from_name_failure(self): result = self.manager.get_rule_id_from_name('SPREAD1') self.assertEqual(result, []) def test_router_search(self): result = self.manager.get_backend_router_id_from_hostname('bcr01a.ams01') self.assertEqual(result[0], 117917) result = self.manager.get_backend_router_id_from_hostname('bcr01A.AMS01') self.assertEqual(result[0], 117917) def test_router_search_failure(self): result = self.manager.get_backend_router_id_from_hostname('1234.ams01') self.assertEqual(result, []) softlayer-python-5.9.8/tests/managers/vs/vs_tests.py000066400000000000000000001312311415376457700227230ustar00rootroot00000000000000""" SoftLayer.tests.managers.vs.vs_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ from unittest import mock as 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', transient=False, ) _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'}, 'transientGuestFlag': {'operation': False}, } } 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): options = self.vs.get_create_options() extras = {'key': '1_IPV6_ADDRESS', 'name': '1 IPv6 Address'} locations = {'key': 'wdc07', 'name': 'Washington 7'} operating_systems = { 'key': 'OS_UBUNTU_14_04_LTS_TRUSTY_TAHR_64_BIT', 'name': 'Ubuntu / 14.04-64', 'referenceCode': 'UBUNTU_14_64' } port_speeds = { 'key': '10', 'name': '10 Mbps Public & Private Network Uplinks' } sizes = { 'key': 'M1_64X512X25', 'name': 'M1.64x512x25', 'hourlyRecurringFee': 0.0, 'recurringFee': 0.0 } self.assertEqual(options['extras'][0]['key'], extras['key']) self.assertEqual(options['locations'][0], locations) self.assertEqual(options['operating_systems'][0]['referenceCode'], operating_systems['referenceCode']) self.assertEqual(options['port_speed'][0]['name'], port_speeds['name']) self.assertEqual(options['sizes'][0], sizes) def test_get_create_options_prices_by_location(self): options = self.vs.get_create_options('wdc07') extras = {'key': '1_IPV6_ADDRESS', 'name': '1 IPv6 Address', 'prices': [ { 'hourlyRecurringFee': '0', 'id': 272, 'locationGroupId': '', 'recurringFee': '0', } ] } locations = {'key': 'wdc07', 'name': 'Washington 7'} operating_systems = { 'key': 'OS_UBUNTU_14_04_LTS_TRUSTY_TAHR_64_BIT', 'name': 'Ubuntu / 14.04-64', 'referenceCode': 'UBUNTU_14_64', 'prices': [ { 'hourlyRecurringFee': '0', 'id': 272, 'locationGroupId': '', 'recurringFee': '0', } ] } port_speeds = { 'key': '10', 'name': '10 Mbps Public & Private Network Uplinks', 'prices': [ { 'hourlyRecurringFee': '0', 'id': 272, 'locationGroupId': '', 'recurringFee': '0', } ] } sizes = { 'key': 'M1_64X512X25', 'name': 'M1.64x512x25', 'hourlyRecurringFee': 0.0, 'recurringFee': 0.0 } self.assertEqual(options['extras'][0]['prices'][0]['hourlyRecurringFee'], extras['prices'][0]['hourlyRecurringFee']) self.assertEqual(options['locations'][0], locations) self.assertEqual(options['operating_systems'][0]['prices'][0]['locationGroupId'], operating_systems['prices'][0]['locationGroupId']) self.assertEqual(options['port_speed'][0]['prices'][0]['id'], port_speeds['prices'][0]['id']) self.assertEqual(options['sizes'][0], sizes) 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_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_public_vlan_with_public_subnet(self): data = self.vs._generate_create_dict( cpus=1, memory=1, hostname='test', domain='example.com', os_code="STRING", public_vlan=1, public_subnet=1 ) assert_data = { 'startCpus': 1, 'maxMemory': 1, 'hostname': 'test', 'domain': 'example.com', 'localDiskFlag': True, 'operatingSystemReferenceCode': "STRING", 'hourlyBillingFlag': True, 'primaryNetworkComponent': {'networkVlan': {'id': 1, 'primarySubnet': {'id': 1}}}, 'supplementalCreateObjectOptions': {'bootMode': None}, } self.assertEqual(data, assert_data) def test_generate_private_vlan_with_private_subnet(self): data = self.vs._generate_create_dict( cpus=1, memory=1, hostname='test', domain='example.com', os_code="STRING", private_vlan=1, private_subnet=1 ) assert_data = { 'startCpus': 1, 'maxMemory': 1, 'hostname': 'test', 'domain': 'example.com', 'localDiskFlag': True, 'operatingSystemReferenceCode': "STRING", 'hourlyBillingFlag': True, 'primaryBackendNetworkComponent': {'networkVlan': {'id': 1, 'primarySubnet': {'id': 1}}}, 'supplementalCreateObjectOptions': {'bootMode': None}, } self.assertEqual(data, assert_data) def test_generate_private_vlan_subnet_public_vlan_subnet(self): data = self.vs._generate_create_dict( cpus=1, memory=1, hostname='test', domain='example.com', os_code="STRING", private_vlan=1, private_subnet=1, public_vlan=1, public_subnet=1, ) assert_data = { 'startCpus': 1, 'maxMemory': 1, 'hostname': 'test', 'domain': 'example.com', 'localDiskFlag': True, 'operatingSystemReferenceCode': "STRING", 'hourlyBillingFlag': True, 'primaryBackendNetworkComponent': {'networkVlan': {'id': 1, 'primarySubnet': {'id': 1}}}, 'primaryNetworkComponent': {'networkVlan': {'id': 1, 'primarySubnet': {'id': 1}}}, 'supplementalCreateObjectOptions': {'bootMode': None}, } self.assertEqual(data, assert_data) def test_generate_private_subnet(self): actual = self.assertRaises( exceptions.SoftLayerError, self.vs._generate_create_dict, cpus=1, memory=1, hostname='test', domain='example.com', os_code="STRING", private_subnet=1, ) self.assertEqual(str(actual), "You need to specify a private_vlan with private_subnet") def test_generate_public_subnet(self): actual = self.assertRaises( exceptions.SoftLayerError, self.vs._generate_create_dict, cpus=1, memory=1, hostname='test', domain='example.com', os_code="STRING", public_subnet=1, ) self.assertEqual(str(actual), "You need to specify a public_vlan with public_subnet") 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_by_router_and_vlan(self): actual = self.assertRaises( exceptions.SoftLayerError, self.vs._generate_create_dict, cpus=1, memory=1, hostname='test', domain='example.com', os_code="STRING", private_router=1, private_vlan=1 ) self.assertEqual(str(actual), "You have to select network vlan or network vlan with a subnet or only router, " "not all options") def test_generate_by_router_and_subnet(self): actual = self.assertRaises( exceptions.SoftLayerError, self.vs._generate_create_dict, cpus=1, memory=1, hostname='test', domain='example.com', os_code="STRING", private_router=1, private_subnet=1 ) self.assertEqual(str(actual), "You have to select network vlan or network vlan with a subnet or only router, " "not all options") def test_generate_sec_group(self): data = self.vs._generate_create_dict( cpus=1, memory=1, hostname='test', domain='test.com', os_code="OS", public_security_groups=[1, 2, 3], private_security_groups=[4, 5, 6] ) pub_sec_binding = data['primaryNetworkComponent']['securityGroupBindings'] prv_sec_binding = data['primaryBackendNetworkComponent']['securityGroupBindings'] # Public self.assertEqual(pub_sec_binding[0]['securityGroup']['id'], 1) self.assertEqual(pub_sec_binding[1]['securityGroup']['id'], 2) self.assertEqual(pub_sec_binding[2]['securityGroup']['id'], 3) # Private self.assertEqual(prv_sec_binding[0]['securityGroup']['id'], 4) self.assertEqual(prv_sec_binding[1]['securityGroup']['id'], 5) self.assertEqual(prv_sec_binding[2]['securityGroup']['id'], 6) def test_create_network_components_vlan_subnet_private_vlan_subnet_public(self): data = self.vs._create_network_components( private_vlan=1, private_subnet=1, public_vlan=1, public_subnet=1, ) assert_data = { 'primaryBackendNetworkComponent': {'networkVlan': {'id': 1, 'primarySubnet': {'id': 1}}}, 'primaryNetworkComponent': {'networkVlan': {'id': 1, 'primarySubnet': {'id': 1}}}, } self.assertEqual(data, assert_data) def test_create_network_components_by_routers(self): data = self.vs._create_network_components( private_router=1, public_router=1 ) assert_data = { 'primaryBackendNetworkComponent': {'router': {'id': 1}}, 'primaryNetworkComponent': {'router': {'id': 1}}, } self.assertEqual(data, assert_data) def test_create_network_components_by_routers_and_vlan(self): actual = self.assertRaises( exceptions.SoftLayerError, self.vs._create_network_components, private_router=1, public_router=1, private_vlan=1 ) self.assertEqual(str(actual), "You have to select network vlan or network vlan with a subnet or only router, " "not all options") def test_create_network_components_vlan_subnet_private(self): data = self.vs._create_network_components( private_vlan=1, private_subnet=1, ) assert_data = { 'primaryBackendNetworkComponent': {'networkVlan': {'id': 1, 'primarySubnet': {'id': 1}}}, } self.assertEqual(data, assert_data) def test_create_network_components_vlan_subnet_public(self): data = self.vs._create_network_components( public_vlan=1, public_subnet=1, ) assert_data = { 'primaryNetworkComponent': {'networkVlan': {'id': 1, 'primarySubnet': {'id': 1}}}, } self.assertEqual(data, assert_data) def test_create_network_components_private_subnet(self): actual = self.assertRaises( exceptions.SoftLayerError, self.vs._create_network_components, private_subnet=1, ) self.assertEqual(str(actual), "You need to specify a private_vlan with private_subnet") def test_create_network_components_public_subnet(self): actual = self.assertRaises( exceptions.SoftLayerError, self.vs._create_network_components, public_subnet=1, ) self.assertEqual(str(actual), "You need to specify a public_vlan with public_subnet") 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_usage_vs_cpu(self): result = self.vs.get_summary_data_usage('100', start_date='2019-3-4', end_date='2019-4-2', valid_type='CPU0', summary_period=300) expected = fixtures.SoftLayer_Metric_Tracking_Object.getSummaryData self.assertEqual(result, expected) args = ('2019-3-4', '2019-4-2', [{"keyName": "CPU0", "summaryType": "max"}], 300) self.assert_called_with('SoftLayer_Metric_Tracking_Object', 'getSummaryData', args=args, identifier=1000) def test_usage_vs_memory(self): result = self.vs.get_summary_data_usage('100', start_date='2019-3-4', end_date='2019-4-2', valid_type='MEMORY_USAGE', summary_period=300) expected = fixtures.SoftLayer_Metric_Tracking_Object.getSummaryData self.assertEqual(result, expected) args = ('2019-3-4', '2019-4-2', [{"keyName": "MEMORY_USAGE", "summaryType": "max"}], 300) self.assert_called_with('SoftLayer_Metric_Tracking_Object', 'getSummaryData', args=args, identifier=1000) def test_get_tracking_id(self): result = self.vs.get_tracking_id(1234) self.assert_called_with('SoftLayer_Virtual_Guest', 'getMetricTrackingObjectId') self.assertEqual(result, 1000) def test_get_bandwidth_data(self): result = self.vs.get_bandwidth_data(1234, '2019-01-01', '2019-02-01', 'public', 1000) self.assert_called_with('SoftLayer_Metric_Tracking_Object', 'getBandwidthData', args=('2019-01-01', '2019-02-01', 'public', 1000), identifier=1000) self.assertEqual(result[0]['type'], 'cpu0') def test_get_bandwidth_allocation(self): result = self.vs.get_bandwidth_allocation(1234) self.assert_called_with('SoftLayer_Virtual_Guest', 'getBandwidthAllotmentDetail', identifier=1234) self.assert_called_with('SoftLayer_Virtual_Guest', 'getBillingCycleBandwidthUsage', identifier=1234) self.assertEqual(result['allotment']['amount'], '250') self.assertEqual(result['usage'][0]['amountIn'], '.448') def test_get_bandwidth_allocation_no_allotment(self): mock = self.set_mock('SoftLayer_Virtual_Guest', 'getBandwidthAllotmentDetail') mock.return_value = None result = self.vs.get_bandwidth_allocation(1234) self.assertEqual(None, result['allotment']) def test_get_bandwidth_allocation_with_allotment(self): mock = self.set_mock('SoftLayer_Virtual_Guest', 'getBandwidthAllotmentDetail') mock.return_value = { "allocationId": 11111, "id": 22222, "allocation": { "amount": "2000" } } result = self.vs.get_bandwidth_allocation(1234) self.assertEqual(2000, int(result['allotment']['amount'])) def test_get_storage_iscsi_details(self): mock = self.set_mock('SoftLayer_Virtual_Guest', 'getAttachedNetworkStorages') mock.return_value = [ { "accountId": 11111, "capacityGb": 12000, "id": 3777123, "nasType": "ISCSI", "username": "SL02SEL31111-9", } ] result = self.vs.get_storage_details(1234, 'ISCSI') self.assertEqual([{ "accountId": 11111, "capacityGb": 12000, "id": 3777123, "nasType": "ISCSI", "username": "SL02SEL31111-9", }], result) def test_get_storage_iscsi_empty_details(self): mock = self.set_mock('SoftLayer_Virtual_Guest', 'getAttachedNetworkStorages') mock.return_value = [] result = self.vs.get_storage_details(1234, 'ISCSI') self.assertEqual([], result) def test_get_storage_nas_details(self): mock = self.set_mock('SoftLayer_Virtual_Guest', 'getAttachedNetworkStorages') mock.return_value = [ { "accountId": 11111, "capacityGb": 12000, "id": 3777111, "nasType": "NAS", "username": "SL02SEL32222-9", } ] result = self.vs.get_storage_details(1234, 'NAS') self.assertEqual([{ "accountId": 11111, "capacityGb": 12000, "id": 3777111, "nasType": "NAS", "username": "SL02SEL32222-9", }], result) def test_get_storage_nas_empty_details(self): mock = self.set_mock('SoftLayer_Virtual_Guest', 'getAttachedNetworkStorages') mock.return_value = [] result = self.vs.get_storage_details(1234, 'NAS') self.assertEqual([], result) def test_get_storage_credentials(self): mock = self.set_mock('SoftLayer_Virtual_Guest', 'getAllowedHost') mock.return_value = { "accountId": 11111, "id": 33333, "name": "iqn.2020-03.com.ibm:sl02su11111-v62941551", "resourceTableName": "VIRTUAL_GUEST", "credential": { "accountId": "11111", "id": 44444, "password": "SjFDCpHrjskfj", "username": "SL02SU11111-V62941551" } } result = self.vs.get_storage_credentials(1234) self.assertEqual({ "accountId": 11111, "id": 33333, "name": "iqn.2020-03.com.ibm:sl02su11111-v62941551", "resourceTableName": "VIRTUAL_GUEST", "credential": { "accountId": "11111", "id": 44444, "password": "SjFDCpHrjskfj", "username": "SL02SU11111-V62941551" } }, result) def test_get_none_storage_credentials(self): mock = self.set_mock('SoftLayer_Virtual_Guest', 'getAllowedHost') mock.return_value = None result = self.vs.get_storage_credentials(1234) self.assertEqual(None, result) def test_get_portable_storage(self): result = self.vs.get_portable_storage(1234) self.assert_called_with('SoftLayer_Account', 'getPortableStorageVolumes') self.assertEqual([ { "capacity": 200, "createDate": "2018-10-06T04:27:59-06:00", "description": "Disk 2", "id": 11111, "modifyDate": "", "name": "Disk 2", "parentId": "", "storageRepositoryId": 22222, "typeId": 241, "units": "GB", "uuid": "fd477feb-bf32-408e-882f-02540gghgh111" } ], result) def test_get_portable_storage_empty(self): mock = self.set_mock('SoftLayer_Account', 'getPortableStorageVolumes') mock.return_value = [] result = self.vs.get_portable_storage(1234) self.assertEqual([], result) def test_get_local_disks_system(self): mock = self.set_mock('SoftLayer_Virtual_Guest', 'getBlockDevices') mock.return_value = [ { "createDate": "2018-10-06T04:27:35-06:00", "device": "0", "id": 11111, "mountType": "Disk", "diskImage": { "capacity": 100, "description": "adns.vmware.com", "id": 72222, "name": "adns.vmware.com", "units": "GB", } } ] result = self.vs.get_local_disks(1234) self.assertEqual([ { "createDate": "2018-10-06T04:27:35-06:00", "device": "0", "id": 11111, "mountType": "Disk", "diskImage": { "capacity": 100, "description": "adns.vmware.com", "id": 72222, "name": "adns.vmware.com", "units": "GB", } } ], result) def test_get_local_disks_empty(self): mock = self.set_mock('SoftLayer_Virtual_Guest', 'getBlockDevices') mock.return_value = [] result = self.vs.get_local_disks(1234) self.assertEqual([], result) def test_get_local_disks_swap(self): mock = self.set_mock('SoftLayer_Virtual_Guest', 'getBlockDevices') mock.return_value = [ { "device": "1", "id": 22222, "mountType": "Disk", "statusId": 1, "diskImage": { "capacity": 2, "description": "6211111-SWAP", "id": 33333, "name": "6211111-SWAP", "units": "GB", } } ] result = self.vs.get_local_disks(1234) self.assertEqual([ { "device": "1", "id": 22222, "mountType": "Disk", "statusId": 1, "diskImage": { "capacity": 2, "description": "6211111-SWAP", "id": 33333, "name": "6211111-SWAP", "units": "GB", } } ], result) def test_migrate(self): result = self.vs.migrate(1234) self.assertTrue(result) self.assert_called_with('SoftLayer_Virtual_Guest', 'migrate', identifier=1234) def test_migrate_dedicated(self): result = self.vs.migrate_dedicated(1234, 5555) self.assertTrue(result) self.assert_called_with('SoftLayer_Virtual_Guest', 'migrateDedicatedHost', args=(5555,), identifier=1234) def test_get_hardware_guests(self): mock = self.set_mock('SoftLayer_Account', 'getHardware') mock.return_value = [{ "accountId": 11111, "domain": "vmware.chechu.com", "hostname": "host14", "id": 22222, "virtualHost": { "accountId": 11111, "id": 33333, "name": "host14.vmware.chechu.com", "guests": [ { "accountId": 11111, "hostname": "NSX-T Manager", "id": 44444, "maxCpu": 16, "maxCpuUnits": "CORE", "maxMemory": 49152, "powerState": { "keyName": "RUNNING", "name": "Running" }, "status": { "keyName": "ACTIVE", "name": "Active" } }]}}] result = self.vs.get_hardware_guests() self.assertEqual("NSX-T Manager", result[0]['virtualHost']['guests'][0]['hostname']) def test_authorize_storage(self): options = self.vs.authorize_storage(1234, "SL01SEL301234-11") self.assertEqual(True, options) def test_authorize_storage_empty(self): mock = self.set_mock('SoftLayer_Account', 'getNetworkStorage') mock.return_value = [] self.assertRaises(SoftLayer.exceptions.SoftLayerError, self.vs.authorize_storage, 1234, "#") def test_authorize_portable_storage(self): options = self.vs.attach_portable_storage(1234, 1234567) self.assertEqual(1234567, options['id']) softlayer-python-5.9.8/tests/managers/vs/vs_waiting_for_ready_tests.py000066400000000000000000000145321415376457700265030ustar00rootroot00000000000000""" SoftLayer.tests.managers.vs.vs_waiting_for_ready_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ from unittest import mock as mock import SoftLayer from SoftLayer import exceptions from SoftLayer import testing 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_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.ServerError(504, "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.9.8/tests/resources/000077500000000000000000000000001415376457700202635ustar00rootroot00000000000000softlayer-python-5.9.8/tests/resources/attachment_upload000066400000000000000000000000241415376457700236760ustar00rootroot00000000000000ticket attached datasoftlayer-python-5.9.8/tests/transport_tests.py000066400000000000000000000724761415376457700221210ustar00rootroot00000000000000""" SoftLayer.tests.transport_tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :license: MIT, see LICENSE for more details. """ import io import json from unittest import mock as mock import warnings import pytest import requests import SoftLayer from SoftLayer import consts from SoftLayer import testing from SoftLayer import transports def get_xmlrpc_response(): response = requests.Response() list_body = 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://something9999999999999999999999.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 '''.encode() req = transports.Request() req.service = 'SoftLayer_Service' req.method = 'getObject' resp = self.transport(req) request.assert_called_with('POST', 'http://something9999999999999999999999.com/SoftLayer_Service', headers={'Content-Type': 'application/xml', 'User-Agent': consts.USER_AGENT}, proxies=None, data=data, timeout=None, cert=None, verify=True, auth=None) 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, auth=None) @mock.patch('SoftLayer.transports.requests.Session.request') def test_identifier(self, request): request.return_value = self.response req = transports.Request() req.endpoint = "http://something9999999999999999999999.com" req.service = "SoftLayer_Service" req.method = "getObject" req.identifier = 1234 self.transport(req) _, kwargs = request.call_args self.assertIn( """ id 1234 """.encode(), 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://something9999999999999999999999.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 """.encode(), 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://something9999999999999999999999.com" req.service = "SoftLayer_Service" req.method = "getObject" req.limit = 10 self.transport(req) args, kwargs = request.call_args self.assertIn(""" resultLimit """.encode(), kwargs['data']) self.assertIn("""limit 10 """.encode(), 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://something9999999999999999999999.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 """.encode(), 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://something9999999999999999999999.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]".encode(), 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://something9999999999999999999999.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]]".encode(), kwargs['data']) @mock.patch('SoftLayer.transports.requests.Session.request') def test_mask_call_filteredMask(self, request): request.return_value = self.response req = transports.Request() req.endpoint = "http://something9999999999999999999999.com" req.service = "SoftLayer_Service" req.method = "getObject" req.mask = "filteredMask[something[nested]]" self.transport(req) args, kwargs = request.call_args self.assertIn( "filteredMask[something[nested]]".encode(), 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://something9999999999999999999999.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".encode(), 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) def test_print_reproduceable(self): req = transports.Request() req.url = "https://test.com" req.payload = "testing" req.transport_headers = {"test-headers": 'aaaa'} output_text = self.transport.print_reproduceable(req) self.assertIn("https://test.com", output_text) @mock.patch('SoftLayer.transports.requests.Session.request') @mock.patch('requests.auth.HTTPBasicAuth') def test_ibm_id_call(self, auth, request): request.return_value = self.response data = ''' getObject headers '''.encode() req = transports.Request() req.service = 'SoftLayer_Service' req.method = 'getObject' req.transport_user = 'apikey' req.transport_password = '1234567890qweasdzxc' resp = self.transport(req) auth.assert_called_with('apikey', '1234567890qweasdzxc') request.assert_called_with('POST', 'http://something9999999999999999999999.com/SoftLayer_Service', headers={'Content-Type': 'application/xml', 'User-Agent': consts.USER_AGENT}, proxies=None, data=data, timeout=None, cert=None, verify=True, auth=mock.ANY) self.assertEqual(resp, []) self.assertIsInstance(resp, transports.SoftLayerListResult) self.assertEqual(resp.total_count, 10) @mock.patch('SoftLayer.transports.requests.Session.request') def test_call_large_number_response(self, request): response = requests.Response() body = b''' bytesUsed 2666148982056 ''' response.raw = io.BytesIO(body) response.headers['SoftLayer-Total-Items'] = 1 response.status_code = 200 request.return_value = response req = transports.Request() req.service = 'SoftLayer_Service' req.method = 'getObject' resp = self.transport(req) self.assertEqual(resp[0]['bytesUsed'], 2666148982056) @mock.patch('SoftLayer.transports.requests.Session.request') def test_nonascii_characters(self, request): request.return_value = self.response hostname = 'testé' data = ''' getObject headers hostname testé '''.encode() req = transports.Request() req.service = 'SoftLayer_Service' req.method = 'getObject' req.args = ({'hostname': hostname},) req.transport_user = "testUser" req.transport_password = "testApiKey" resp = self.transport(req) request.assert_called_with('POST', 'http://something9999999999999999999999.com/SoftLayer_Service', headers={'Content-Type': 'application/xml', 'User-Agent': consts.USER_AGENT}, proxies=None, data=data, timeout=None, cert=None, verify=True, auth=mock.ANY) self.assertEqual(resp, []) self.assertIsInstance(resp, transports.SoftLayerListResult) self.assertEqual(resp.total_count, 10) @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://something9999999999999999999999.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://something9999999999999999999999.com/SoftLayer_Service', data=mock.ANY, headers=mock.ANY, cert=mock.ANY, proxies=mock.ANY, timeout=mock.ANY, verify=expected, auth=None) class TestRestAPICall(testing.TestCase): def set_up(self): self.transport = transports.RestTransport( endpoint_url='http://something9999999999999999999999.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://something9999999999999999999999.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_http_and_json_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) @mock.patch('SoftLayer.transports.requests.Session.request') def test_http_and_empty_error(self, request): # Test JSON Error e = requests.HTTPError('error') e.response = mock.MagicMock() e.response.status_code = 404 e.response.text = '' request().raise_for_status.side_effect = e req = transports.Request() req.service = 'SoftLayer_Service' req.method = 'Resource' self.assertRaises(SoftLayer.SoftLayerAPIError, self.transport, req) @mock.patch('SoftLayer.transports.requests.Session.request') def test_empty_error(self, request): # Test empty response error. request().text = '' req = transports.Request() req.service = 'SoftLayer_Service' req.method = 'Resource' self.assertRaises(SoftLayer.SoftLayerAPIError, self.transport, req) @mock.patch('SoftLayer.transports.requests.Session.request') def test_json_error(self, request): # Test non-json response error. request().text = 'Not JSON' 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://something9999999999999999999999.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://something9999999999999999999999.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://something9999999999999999999999.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_args_bytes(self, request): request().text = '{}' req = transports.Request() req.service = 'SoftLayer_Service' req.method = 'getObject' req.args = ('test', b'asdf') resp = self.transport(req) self.assertEqual(resp, {}) request.assert_called_with( 'POST', 'http://something9999999999999999999999.com/SoftLayer_Service/getObject.json', headers=mock.ANY, auth=None, data='{"parameters": ["test", "YXNkZg=="]}', 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://something9999999999999999999999.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://something9999999999999999999999.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://something9999999999999999999999.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://something9999999999999999999999.com/SoftLayer_Service/2/getObject.json', headers=mock.ANY, auth=None, data=None, params={'resultLimit': '5,10'}, 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://something9999999999999999999999.com/SoftLayer_Service/2/getObject.json', headers=mock.ANY, auth=mock.ANY, data=None, params={}, verify=True, cert=None, proxies=None, timeout=None) def test_print_reproduceable(self): req = transports.Request() req.url = "https://test.com" req.payload = "testing" req.transport_headers = {"test-headers": 'aaaa'} output_text = self.transport.print_reproduceable(req) self.assertIn("https://test.com", output_text) def test_complex_encoder_bytes(self): to_encode = { 'test': ['array', 0, 1, False], 'bytes': b'ASDASDASD' } result = json.dumps(to_encode, cls=transports.ComplexEncoder) # result = '{"test": ["array", 0, 1, false], "bytes": "QVNEQVNEQVNE"}' # encode doesn't always encode in the same order, so testing exact match SOMETIMES breaks. self.assertIn("QVNEQVNEQVNE", result) 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) class TestTimingTransport(testing.TestCase): def set_up(self): fixture_transport = transports.FixtureTransport() self.transport = transports.TimingTransport(fixture_transport) def test_call(self): req = transports.Request() req.service = 'SoftLayer_Account' req.method = 'getObject' resp = self.transport(req) self.assertEqual(resp['accountId'], 1234) def test_get_last_calls(self): req = transports.Request() req.service = 'SoftLayer_Account' req.method = 'getObject' resp = self.transport(req) self.assertEqual(resp['accountId'], 1234) calls = self.transport.get_last_calls() self.assertEqual(calls[0][0].service, 'SoftLayer_Account') def test_print_reproduceable(self): req = transports.Request() req.service = 'SoftLayer_Account' req.method = 'getObject' output_text = self.transport.print_reproduceable(req) self.assertEqual('SoftLayer_Account', output_text) class TestDebugTransport(testing.TestCase): def set_up(self): fixture_transport = transports.FixtureTransport() self.transport = transports.DebugTransport(fixture_transport) req = transports.Request() req.service = 'SoftLayer_Account' req.method = 'getObject' self.req = req def test_call(self): resp = self.transport(self.req) self.assertEqual(resp['accountId'], 1234) def test_get_last_calls(self): resp = self.transport(self.req) self.assertEqual(resp['accountId'], 1234) calls = self.transport.get_last_calls() self.assertEqual(calls[0].service, 'SoftLayer_Account') def test_print_reproduceable(self): req = transports.Request() req.service = 'SoftLayer_Account' req.method = 'getObject' output_text = self.transport.print_reproduceable(self.req) self.assertEqual('SoftLayer_Account', output_text) def test_print_reproduceable_post(self): req = transports.Request() req.url = "https://test.com" req.payload = "testing" req.transport_headers = {"test-headers": 'aaaa'} req.args = 'createObject' rest_transport = transports.RestTransport() transport = transports.DebugTransport(rest_transport) output_text = transport.print_reproduceable(req) self.assertIn("https://test.com", output_text) self.assertIn("-X POST", output_text) @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' rest_transport = transports.RestTransport() transport = transports.DebugTransport(rest_transport) self.assertRaises(SoftLayer.SoftLayerAPIError, transport, req) calls = transport.get_last_calls() self.assertEqual(404, calls[0].exception.faultCode) softlayer-python-5.9.8/tools/000077500000000000000000000000001415376457700162475ustar00rootroot00000000000000softlayer-python-5.9.8/tools/requirements.txt000066400000000000000000000001431415376457700215310ustar00rootroot00000000000000ptable >= 0.9.2 click >= 7 requests >= 2.20.0 prompt_toolkit >= 2 pygments >= 2.0.0 urllib3 >= 1.24softlayer-python-5.9.8/tools/test-requirements.txt000066400000000000000000000002171415376457700225100ustar00rootroot00000000000000tox coveralls pytest pytest-cov mock sphinx ptable >= 0.9.2 click >= 7 requests >= 2.20.0 prompt_toolkit >= 2 pygments >= 2.0.0 urllib3 >= 1.24softlayer-python-5.9.8/tox.ini000066400000000000000000000032251415376457700164240ustar00rootroot00000000000000[tox] envlist = py36,py37,py38,py39,pypy3,analysis,coverage,docs [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 \ -d useless-object-inheritance \ -d consider-using-in \ -d consider-using-dict-comprehension \ -d useless-import-alias \ -d consider-using-f-string \ --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 [testenv:docs] commands = python ./docCheck.py