pax_global_header00006660000000000000000000000064132607622050014515gustar00rootroot0000000000000052 comment=3d7b72b334560f3063c4da4945946214a04a0d8c profitbricks-sdk-python-4.1.3/000077500000000000000000000000001326076220500163215ustar00rootroot00000000000000profitbricks-sdk-python-4.1.3/.gitignore000066400000000000000000000014201326076220500203060ustar00rootroot00000000000000# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] # C extensions *.so # Distribution / packaging .Python env/ build/ develop-eggs/ dist/ downloads/ eggs/ lib/ lib64/ parts/ sdist/ var/ *.egg-info/ .installed.cfg *.egg # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .coverage .cache nosetests.xml coverage.xml # Result files *.csv # Translations *.mo *.pot # Django stuff: *.log # Sphinx documentation docs/_build/ # PyBuilder target/ examples/live_test.py *.iml .idea/ # Eclipse/PyDev metadatas /.project /.pydevproject profitbricks-sdk-python-4.1.3/Dockerfile000066400000000000000000000002501326076220500203100ustar00rootroot00000000000000FROM python:latest WORKDIR /usr/src COPY . /usr/src RUN pip install -r /usr/src/requirements.txt USER nobody CMD ["python", "-m", "unittest", "discover", "tests"] profitbricks-sdk-python-4.1.3/LICENSE000066400000000000000000000260631326076220500173350ustar00rootroot00000000000000Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2015 ProfitBricks GmbH Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. profitbricks-sdk-python-4.1.3/MANIFEST.in000066400000000000000000000001131326076220500200520ustar00rootroot00000000000000include examples/*.py include tests/*.py include LICENSE include README.md profitbricks-sdk-python-4.1.3/README.md000066400000000000000000002460031326076220500176050ustar00rootroot00000000000000# Python SDK Version: profitbricks-sdk-python **4.1.2** ## Table of Contents * [Description](#description) * [Getting Started](#getting-started) * [Installation](#installation) * [Authenticating](#authenticating) * [Error Handling](#error-handling) * [Reference](#reference) * [Data Centers](#data-centers) * [List Data Centers](#list-data-centers) * [Retrieve a Data Center](#retrieve-a-data-center) * [Create a Data Center](#create-a-data-center) * [Update a Data Center](#update-a-data-center) * [Delete a Data Center](#delete-a-data-center) * [Locations](#locations) * [List Locations](#list-locations) * [Get a Location](#get-a-location) * [Servers](#servers) * [List Servers](#list-servers) * [Retrieve a Server](#retrieve-a-server) * [Create a Server](#create-a-server) * [Update a Server](#update-a-server) * [Delete a Server](#delete-a-server) * [List Attached Volumes](#list-attached-volumes) * [Attach a Volume](#attach-a-volume) * [Retrieve an Attached Volume](#retrieve-an-attached-volume) * [Detach a Volume](#detach-a-volume) * [List Attached CD-ROMs](#list-attached-cd-roms) * [Attach a CD-ROM](#attach-a-cd-rom) * [Retrieve an Attached CD-ROM](#retrieve-an-attached-cd-rom) * [Detach a CD-ROM](#detach-a-cd-rom) * [Reboot a Server](#reboot-a-server) * [Start a Server](#start-a-server) * [Stop a Server](#stop-a-server) * [Images](#images) * [List Images](#list-images) * [Get an Image](#get-an-image) * [Update an Image](#update-an-image) * [Delete an Image](#delete-an-image) * [Volumes](#volumes) * [List Volumes](#list-volumes) * [Get a Volume](#get-a-volume) * [Create a Volume](#create-a-volume) * [Update a Volume](#update-a-volume) * [Delete a Volume](#delete-a-volume) * [Create a Volume Snapshot](#create-a-volume-snapshot) * [Restore a Volume Snapshot](#restore-a-volume-snapshot) * [Snapshots](#snapshots) * [List Snapshots](#list-snapshots) * [Get a Snapshot](#get-a-snapshot) * [Update a Snapshot](#update-a-snapshot) * [Delete a Snapshot](#delete-a-snapshot) * [IP Blocks](#ip-blocks) * [List IP Blocks](#list-ip-blocks) * [Get an IP Block](#get-an-ip-block) * [Create an IP Block](#create-an-ip-block) * [Delete an IP Block](#delete-an-ip-block) * [LANs](#lans) * [List LANs](#list-lans) * [Create a LAN](#create-a-lan) * [Get a LAN](#get-a-lan) * [Get LAN Members](#get-lan-members) * [Update a LAN](#update-a-lan) * [Delete a LAN](#delete-a-lan) * [Network Interfaces (NICs)](#network-interfaces-nics) * [List NICs](#list-nics) * [Get a NIC](#get-a-nic) * [Create a NIC](#create-a-nic) * [Update a NIC](#update-a-nic) * [Delete a NIC](#delete-a-nic) * [Firewall Rules](#firewall-rules) * [List Firewall Rules](#list-firewall-rules) * [Get a Firewall Rule](#get-a-firewall-rule) * [Create a Firewall Rule](#create-a-firewall-rule) * [Update a Firewall Rule](#update-a-firewall-rule) * [Delete a Firewall Rule](#delete-a-firewall-rule) * [Load Balancers](#load-balancers) * [List Load Balancers](#list-load-balancers) * [Get a Load Balancer](#get-a-load-balancer) * [Create a Load Balancer](#create-a-load-balancer) * [Update a Load Balancer](#update-a-load-balancer) * [List Load Balanced NICs](#list-load-balanced-nics) * [Get a Load Balanced NIC](#get-a-load-balanced-nic) * [Associate NIC to a Load Balancer](#associate-nic-to-a-load-balancer) * [Remove a NIC Association](#remove-a-nic-association) * [User Management](#user-management) * [List Groups](#list-groups) * [Get a Group](#get-a-group) * [Create a Group](#create-a-group) * [Update a Group](#update-a-group) * [Delete a Group](#delete-a-group) * [List Shares](#list-shares) * [Get a Share](#get-a-share) * [Add a Share](#add-a-share) * [Update a Share](#update-a-share) * [Delete a Share](#delete-a-share) * [List Users](#list-users) * [Get a User](#get-a-user) * [Create a User](#create-a-user) * [Update a User](#update-a-user) * [Delete a User](#delete-a-user) * [List Users in a Group](#list-users-in-a-group) * [Add User to Group](#add-user-to-group) * [Remove User from a Group](#remove-user-from-a-group) * [List Resources](#list-resources) * [Get a Resource](#get-a-resource) * [Contract Resources](#contract-resources) * [List Contract Resources](#list-contract-resources) * [Requests](#requests) * [List Requests](#list-requests) * [Get a Request](#get-a-request) * [Get a Request Status](#get-a-request-status) * [Examples](#examples) * [List All Data Centers](#list-all-data-centers) * [Search for Images](#search-for-images) * [Reserve an IP Block](#reserve-an-ip-block) * [Wait for Resources](#wait-for-resources) * [Component Build](#component-build) * [Composite Build](#composite-build) * [Support](#support) * [Testing](#testing) * [Contributing](#contributing) ## Description The ProfitBricks SDK for Python provides you with access to the ProfitBricks Cloud API. The client library supports both simple and complex requests. It is designed for developers who are building applications in Python. This guide will walk you through getting setup with the library and performing various actions against the API. The SDK for Python wraps the ProfitBricks Cloud API. All API operations are performed over SSL and authenticated using your ProfitBricks portal credentials. The API can be accessed within an instance running in ProfitBricks or directly over the Internet from any application that can send an HTTPS request and receive an HTTPS response. ## Getting Started Before you begin you will need to have [signed-up](https://www.profitbricks.com/signup) for a ProfitBricks account. The credentials you setup during sign-up will be used to authenticate against the Cloud API. #### Installation The ProfitBricks SDK for Python is available on [PyPi](https://pypi.python.org/pypi/profitbricks). You can install the latest stable version using `pip`: pip install profitbricks Done! #### Authenticating Connecting to ProfitBricks is handled by first setting up your authentication credentials. from profitbricks.client import ProfitBricksService client = ProfitBricksService( username='YOUR_USERNAME', password='YOUR_PASSWORD') Replace the values for *YOUR_USERNAME* and *YOUR_PASSWORD* with the ProfitBricks credentials you established during sign-up. You can now use `client` for any future request. #### Error Handling The SDK will raise custom exceptions when the Cloud API returns an error. There are five exception types: | Exception | HTTP Code | Description | |---|:-:|---| | PBNotAuthorizedError | 401 | The supplied user credentials are invalid. | | PBNotFoundError | 404 | The requested resource cannot be found. | | PBValidationError | 422 | The request body includes invalid JSON. | | PBRateLimitExceededError | 429 | The Cloud API rate limit has been exceeded. | | PBError | Other | A generic exception for all other status codes. | ## Reference This section provides details on all the available operations and the arguments they accept. Brief code snippets demonstrating usage are also included. `client` is the `ProfitBricksService` class imported `from profitbricks.client import ProfitBricksService` #### Depth Many of the *get_* or *list_* operations will accept an optional *depth* argument. Setting this to a value between 0 and 5 affects the amount of data that is returned. The detail returned varies somewhat depending on the resource being queried, however it generally follows this pattern. | Depth | Description | |:-:|---| | 0 | Only direct properties are included. Children are not included. | | 1 | Direct properties and children's references are returned. | | 2 | Direct properties and children's properties are returned. | | 3 | Direct properties, children's properties, and descendant's references are returned. | | 4 | Direct properties, children's properties, and descendant's properties are returned. | | 5 | Returns all available properties. | This SDK sets the *depth=1* by default as that works well in the majority of cases. You may find that setting *depth* to a lower or higher value could simplify a later operation by reducing or increasing the data available in the response object. ## Data Centers Virtual Data Centers (VDCs) are the foundation of the ProfitBricks platform. VDCs act as logical containers for all other objects you will be creating, e.g., servers. You can provision as many VDCs as you want. VDCs have their own private network and are logically segmented from each other to create isolation. #### List Data Centers This operation will list all currently provisioned VDCs that your account credentials provide access to. There are no request arguments that need to be supplied. You may supply the optional *depth* argument. | Name | Required | Type | Description | |---|:-:|---|---| | depth | no | int | An integer value of 0 - 5 that affects the amount of detail returned. See the [Depth](#depth) section. | Call `list_datacenters`: response = client.list_datacenters() --- #### Retrieve a Data Center Use this to retrieve details about a specific VDC. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | depth | no | int | An integer value of 0 - 5 that affects the amount of detail returned. See the [Depth](#depth) section. | Pass the arguments to `get_datacenter`: response = client.get_datacenter(datacenter_id='UUID') --- #### Create a Data Center Use this operation to create a new VDC. You can create a "simple" VDC by supplying just the required *name* and *location* arguments. This operation also has the capability of provisioning a "complex" VDC by supplying additional arguments for servers, volumes, LANs, and/or load balancers. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter | **yes** | object | A [Datacenter object](#datacenter-resource-object) describing the VDC being created. | Build the `Datacenter` resource object: datacenter = Datacenter( name='Data Center Name', description='My new data center', location='de/fkb') Pass the object to `create_datacenter`: response = client.create_datacenter(datacenter=datacenter) #### Datacenter Resource Object | Name | Required | Type | Description | |---|:-:|---|---| | name | **yes** | string | The name of the VDC. | | location | **yes** | string | The physical ProfitBricks location where the VDC will be created. | | description | no | string | A description for the VDC, e.g. staging, production. | | servers | no | list | A list of one or more [Server objects](#server-resource-object) to be created. | | volumes | no | list | A list of one or more [Volume objects](#volume-resource-object) to be created. | | lans | no | list | A list of one or more [LAN objects](#lan-resource-object) to be created. | | loadbalancers | no | list | A list of one or more [LoadBalancer objects](#load-balancer-resource-object) to be created. | The following table outlines the locations currently supported: | Value| Country | City | |---|---|---| | us/las | United States | Las Vegas | | us/ewr | United States | Newark | | de/fra | Germany | Frankfurt | | de/fkb | Germany | Karlsruhe | **NOTES**: * The value for `name` cannot contain the following characters: (@, /, , |, ‘’, ‘). * You cannot change the VDC `location` once it has been provisioned. --- #### Update a Data Center After retrieving a VDC, either by ID or as a create response object, you can change its properties by calling the `update_datacenter` method. Some arguments may not be changed using `update_datacenter`. The following table describes the available request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | name | no | string | The new name of the VDC. | | description | no | string | The new description of the VDC. | Pass the arguments to `update_datacenter`: response = client.update_datacenter( datacenter_id='UUID', name='New Name' description='New description') --- #### Delete a Data Center This will remove all objects within the VDC and remove the VDC object itself. **NOTE**: This is a highly destructive operation which should be used with extreme caution! The following table describes the available request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC that you want to delete. | Pass the argument to `delete_datacenter`: response = client.delete_datacenter(datacenter_id='UUID') --- ## Locations Locations are the physical ProfitBricks data centers where you can provision your VDCs. #### List Locations The `list_locations` operation will return the list of currently available locations. There are no request arguments to supply. response = client.list_locations() --- #### Get a Location Retrieves the attributes of a specific location. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | location_id | **yes** | string | The ID consisting of country/city. | Pass the argument to `get_location`: client.get_location('us/las') --- ## Servers #### List Servers You can retrieve a list of all the servers provisioned inside a specific VDC. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | depth | no | int | An integer value of 0 - 5 that affects the amount of detail returned. See the [Depth](#depth) section. | Pass the arguments to `list_servers`: response = client.list_servers(datacenter_id='UUID') --- #### Retrieve a Server Returns information about a specific server such as its configuration, provisioning status, etc. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | server_id | **yes** | string | The ID of the server. | | depth | no | int | An integer value of 0 - 5 that affects the amount of detail returned. See the [Depth](#depth) section. | Pass the arguments to `get_server`: response = client.get_server( datacenter_id='UUID', server_id='UUID') --- #### Create a Server Creates a server within an existing VDC. You can configure additional properties such as specifying a boot volume and connecting the server to a LAN. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | server | **yes** | object | A [Server object](#server-resource-object) describing the server being created. | Build a [Server](#server-resource-object) object: server = Server( name='Server Name', cores=1, ram=2048, description='My new server', location='de/fkb') Pass the object and other arguments to `create_server`: response = client.create_server( datacenter_id='UUID', server=server) #### Server Resource Object | Name | Required | Type | Description | |---|:-:|---|---| | name | **yes** | string | The name of the server. | | cores | **yes** | int | The total number of cores for the server. | | ram | **yes** | int | The amount of memory for the server in MB, e.g. 2048. Size must be specified in multiples of 256 MB with a minimum of 256 MB; however, if you set `ram_hot_plug` to *True* then you must use a minimum of 1024 MB. | | availability_zone | no | string | The availability zone in which the server should exist. | | cpu_family | no | string | Sets the CPU type. "AMD_OPTERON" or "INTEL_XEON". Defaults to "AMD_OPTERON". | | boot_volume_id | no | string | A volume ID that the server will boot from. If not *null* then `boot_cdrom` has to be *null*. | | boot_cdrom | no | string | A CD-ROM image ID used for booting. If not *null* then `boot_volume_id` has to be *null*. | | attach_volumes | no | list | A list of existing volume IDs that you want to connect to the server. | | create_volumes | no | list | One or more [Volume objects](#volume-resource-object) that you want to create and attach to the server.| | nics | no | list | One or more [NIC objects](#nic-resource-object) that you wish to create at the time the server is provisioned. | The following table outlines the server availability zones currently supported: | Availability Zone | Comment | |---|---| | AUTO | Automatically Selected Zone | | ZONE_1 | Fire Zone 1 | | ZONE_2 | Fire Zone 2 | --- #### Update a Server Perform updates to the attributes of a server. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | server_id | **yes** | string | The ID of the server. | | name | no | string | The name of the server. | | cores | no | int | The number of cores for the server. | | ram | no | int | The amount of memory in the server. | | availability_zone | no | string | The new availability zone for the server. | | cpu_family | no | string | Sets the CPU type. "AMD_OPTERON" or "INTEL_XEON". Defaults to "AMD_OPTERON". | | boot_volume_id | no | string | A volume ID used for booting. If not *null* then `boot_cdrom` has to be *null*. | | boot_cdrom | no | string | A CD-ROM image ID used for booting. If not *null* then `boot_volume_id` has to be *null*. | Pass the arguments to `update_server`: response = client.update_server( datacenter_id='UUID', server_id='UUID', name='New Name') --- #### Delete a Server This will remove a server from a VDC. **NOTE**: This will not automatically remove the storage volume(s) attached to a server. A separate operation is required to delete a storage volume. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | server_id | **yes** | string | The ID of the server that will be deleted. | Pass the arguments to `delete_server`: response = client.delete_server( datacenter_id='UUID', server_id='UUID') --- #### List Attached Volumes Retrieves a list of volumes attached to the server. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | server_id | **yes** | string | The ID of the server. | | depth | no | int | An integer value of 0 - 5 that affects the amount of detail returned. See the [Depth](#depth) section. | Pass the arguments to `get_attached_volumes`: response = client.get_attached_volumes( datacenter_id='UUID', server_id='UUID') --- #### Attach a Volume This will attach a pre-existing storage volume to the server. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | server_id | **yes** | string | The ID of the server. | | volume_id | **yes** | string | The ID of a storage volume. | Pass the arguments to `attach_volume`: response = client.attach_volume( datacenter_id='UUID', server_id='UUID', volume_id='UUID') --- #### Retrieve an Attached Volume This will retrieve the properties of an attached volume. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | server_id | **yes** | string | The ID of the server. | | volume_id | **yes** | string | The ID of the attached volume. | Pass the arguments to `get_attached_volume`: response = client.get_attached_volume( datacenter_id='UUID', server_id='UUID', volume_id='UUID') --- #### Detach a Volume This will detach the volume from the server. Depending on the volume `hot_unplug` settings, this may result in the server being rebooted. If `disc_virtio_hot_unplug` has been set to *true*, then a reboot should not be required. This will **NOT** delete the volume from your VDC. You will need to make a separate request to delete a volume. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | server_id | **yes** | string | The ID of the server. | | volume_id | **yes** | string | The ID of the attached volume. | Pass the arguments to `detach_volume`: response = client.detach_volume( datacenter_id='UUID', server_id='UUID', volume_id='UUID') --- #### List Attached CD-ROMs Retrieves a list of CD-ROMs attached to a server. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | server_id | **yes** | string | The ID of the server. | | depth | no | int | An integer value of 0 - 5 that affects the amount of detail returned. See the [Depth](#depth) section. | Pass the arguments to `get_attached_cdroms`: response = client.get_attached_cdroms( datacenter_id='UUID', server_id='UUID') --- #### Attach a CD-ROM You can attach a CD-ROM to an existing server. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | server_id | **yes** | string | The ID of the server. | | cdrom_id | **yes** | string | The ID of a CD-ROM. | Pass the arguments to `attach_cdrom`: response = client.attach_cdrom( datacenter_id='UUID', server_id='UUID', cdrom_id='UUID') --- #### Retrieve an Attached CD-ROM You can retrieve a specific CD-ROM attached to the server. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | server_id | **yes** | string | The ID of the server. | | cdrom_id | **yes** | string | The ID of the attached CD-ROM. | Pass the arguments to `get_attached_cdrom`: response = client.get_attached_cdrom( datacenter_id='UUID', server_id='UUID', cdrom_id='UUID') --- #### Detach a CD-ROM This will detach a CD-ROM from the server. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | server_id | **yes** | string | The ID of the server. | | cdrom_id | **yes** | string | The ID of the attached CD-ROM. | Pass the arguments to `detach_cdrom`: response = client.detach_cdrom( datacenter_id='UUID', server_id='UUID', cdrom_id='UUID') --- #### Reboot a Server This will force a hard reboot of the server. Do not use this method if you want to gracefully reboot the machine. This is the equivalent of powering off the machine and turning it back on. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | server_id | **yes** | string | The ID of the server. | Pass the arguments to `reboot_server`: response = client.reboot_server( datacenter_id='UUID', server_id='UUID') --- #### Start a Server This will start a server. If a DHCP assigned public IP was deallocated when the server was stopped, then a new IP will be assigned. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | server_id | **yes** | string | The ID of the server. | Pass the arguments to `start_server`: response = client.start_server( datacenter_id='UUID', server_id='UUID') --- #### Stop a Server This will stop a server. The machine will be forcefully powered off, billing will cease, and the public IP, if one is allocated, will be deallocated. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | server_id | **yes** | string | The ID of the server. | Pass the arguments to `stop_server`: response = client.stop_server( datacenter_id='UUID', server_id='UUID') --- ## Images #### List Images Retrieve a list of images. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | depth | no | int | An integer value of 0 - 5 that affects the amount of detail returned. See the [Depth](#depth) section. | Pass the arguments to `list_images`: response = client.list_images() --- #### Get an Image Retrieves the attributes of a specific image. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | image_id | **yes** | string | The ID of the image. | Pass the arguments to `get_image`: response = client.get_image('UUID') --- #### Update an Image Updates the attributes of a specific user created image. You **CANNOT** update the properties of a public image supplied by ProfitBricks. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | image_id | **yes** | string | The ID of the image. | | name | no | string | The name of the image. | | description | no | string | The description of the image. | | licence_type | no | string | The snapshot's licence type: LINUX, WINDOWS, WINDOWS2016, UNKNOWN or OTHER. | | cpu_hot_plug | no | bool | This volume is capable of CPU hot plug (no reboot required) | | cpu_hot_unplug | no | bool | This volume is capable of CPU hot unplug (no reboot required) | | ram_hot_plug | no | bool | This volume is capable of memory hot plug (no reboot required) | | ram_hot_unplug | no | bool | This volume is capable of memory hot unplug (no reboot required) | | nic_hot_plug | no | bool | This volume is capable of NIC hot plug (no reboot required) | | nic_hot_unplug | no | bool | This volume is capable of NIC hot unplug (no reboot required) | | disc_virtio_hot_plug | no | bool | This volume is capable of VirtIO drive hot plug (no reboot required) | | disc_virtio_hot_unplug | no | bool | This volume is capable of VirtIO drive hot unplug (no reboot required) | | disc_scsi_hot_plug | no | bool | This volume is capable of SCSI drive hot plug (no reboot required) | | disc_scsi_hot_unplug | no | bool | This volume is capable of SCSI drive hot unplug (no reboot required) | You can change an image's properties by calling the `update_image` method: response = client.update_image( image_id='UUID', name='New Name', description='New description', licence_type='LINUX') --- #### Delete an Image Deletes a specific user created image. You cannot delete public images supplied by ProfitBricks. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | image_id | **yes** | string | The ID of the image. | Pass the arguments to `delete_image`: response = client.delete_image('UUID') --- ## Volumes #### List Volumes Retrieve a list of volumes within the VDC. If you want to retrieve a list of volumes attached to a server please see the [List Attached Volumes](#list-attached-volumes) entry in the Server section for details. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | depth | no | int | An integer value of 0 - 5 that affects the amount of detail returned. See the [Depth](#depth) section. | Pass the arguments to `list_volumes`: response = client.list_volumes(datacenter_id='UUID') --- #### Get a Volume Retrieves the attributes of a given volume. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | volume_id | **yes** | string | The ID of the volume. | Pass the arguments to `get_volume`: response = client.get_volume( datacenter_id='UUID', volume_id='UUID') --- #### Create a Volume Creates a volume within the VDC. This will NOT attach the volume to a server. Please see the [Attach a Volume](#attach-a-volume) entry in the Server section for details on how to attach storage volumes. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | volume | **yes** | object | A [Volume object](#volume-resource-object) you wish to create. | Build the `Volume` resource object: volume = Volume( name='name', size=20, bus='VIRTIO', type='HDD', licence_type='LINUX', availability_zone='ZONE_3') Pass the object and arguments to `create_volume`: response = client.create_volume( datacenter_id='UUID', volume=volume) #### Volume Resource Object | Name | Required | Type | Description | |---|:-:|---|---| | name | no | string | The name of the volume. | | size | **yes** | int | The size of the volume in GB. | | bus | no | string | The bus type of the volume (VIRTIO or IDE). Default: VIRTIO. | | image | **yes** | string | The image or snapshot ID. Can be left empty for a data volume, however you'll need to set the `licence_type`. Default: *null* | | image_alias | **yes** | string | The alias of the image. | | type | **yes** | string | The volume type, HDD or SSD. Default: HDD| | licence_type | **yes** | string | The licence type of the volume. Options: LINUX, WINDOWS, WINDOWS2016, UNKNOWN, OTHER. Default: UNKNOWN | | image_password | **yes** | string | A password to set on the volume for the appropriate root or administrative account. This field may only be set in creation requests. When reading, it always returns *null*. The password has to contain 8-50 characters. Only these characters are allowed: [abcdefghjkmnpqrstuvxABCDEFGHJKLMNPQRSTUVX23456789] | | ssh_keys | **yes** | string | SSH keys to allow access to the volume via SSH. | | availability_zone | no | string | The storage availability zone assigned to the volume. Valid values: AUTO, ZONE_1, ZONE_2, or ZONE_3. This only applies to HDD volumes. Leave blank or set to AUTO when provisioning SSD volumes. | The following table outlines the various licence types you can define: | Licence Type | Comment | |---|---| | WINDOWS2016 | Use this for the Microsoft Windows Server 2016 operating system. | | WINDOWS | Use this for the Microsoft Windows Server 2008 and 2012 operating systems. | | LINUX |Use this for Linux distributions such as CentOS, Ubuntu, Debian, etc. | | OTHER | Use this for any volumes that do not match one of the other licence types. | | UNKNOWN | This value may be inherited when you've uploaded an image and haven't set the license type. Use one of the options above instead. | The following table outlines the storage availability zones currently supported: | Availability Zone | Comment | |---|---| | AUTO | Automatically Selected Zone | | ZONE_1 | Fire Zone 1 | | ZONE_2 | Fire Zone 2 | | ZONE_3 | Fire Zone 3 | **Note:** You will need to provide either the `image` or the `licence_type` arguments when creating a volume. A `licence_type` is required, but if `image` is supplied, it is already set and cannot be changed. Either the `image_password` or `ssh_keys` arguments need to be supplied when creating a volume using one of the official ProfitBricks images. Only official ProfitBricks provided images support the `ssh_keys` and `image_password` arguments. --- #### Update a Volume You can update various attributes of an existing volume; however, some restrictions are in place: You can increase the size of an existing storage volume. You cannot reduce the size of an existing storage volume. The volume size will be increased without requiring a reboot if the relevant hot plug settings (`disc_virtio_hot_plug`, `disc_virtio_hot_unplug`, etc.) have been set to *true*. The additional capacity is not added automatically added to any partition, therefore you will need to handle that inside the OS afterwards. Once you have increased the volume size you cannot decrease the volume size. Since an existing volume is being modified, none of the request arguments are specifically required as long as the changes being made satisfy the requirements for creating a volume. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | volume_id | **yes** | string | The ID of the volume. | | name | no | string | The name of the volume. | | size | no | int | The size of the volume in GB. You may only increase the `size` when updating. | | bus | no | string | The bus type of the volume (VIRTIO or IDE). Default: VIRTIO. | | licence_type | no | string | The licence type of the volume. Options: LINUX, WINDOWS, WINDOWS2016, UNKNOWN, OTHER. You may get an error trying to update `licence_type` depending on the `image` that was used to create the volume. For example, you cannot update the `licence_type` for a volume created from a ProfitBricks supplied OS image. | **Note**: Trying to change the `image`, `type`, or `availability_zone` in an update request will result in an error. Pass the arguments to `update_volume`: response = client.update_volume( datacenter_id='UUID', volume_id='UUID', size=6, name='New Name') --- #### Delete a Volume Deletes the specified volume. This will result in the volume being removed from your data center. Use this with caution. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | volume_id | **yes** | string | The ID of the volume. | Pass the arguments to `delete_volume`: response = client.delete_volume( datacenter_id='UUID', volume_id='UUID') --- #### Create a Volume Snapshot Creates a snapshot of a volume within the VDC. You can use a snapshot to create a new storage volume or to restore a storage volume. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | volume_id | **yes** | string | The ID of the volume. | | name | no | string | The name of the snapshot. | | description | no | string | The description of the snapshot. | Pass the arguments to `create_snapshot`: response = client.create_snapshot( datacenter_id='UUID', volume_id='UUID', name='Snapshot Name', description='Snapshot description') --- #### Restore a Volume Snapshot This will restore a snapshot onto a volume. A snapshot is created as just another image that can be used to create new volumes or to restore an existing volume. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | volume_id | **yes** | string | The ID of the volume. | | snapshot_id | **yes** | string | The ID of the snapshot. | Pass the arguments to `restore_snapshot`: response = client.restore_snapshot( datacenter_id='UUID', volume_id='UUID', snapshot_id='UUID') --- ## Snapshots #### List Snapshots You can retrieve a list of all available snapshots. | Name | Required | Type | Description | |---|:-:|---|---| | depth | no | int | An integer value of 0 - 5 that affects the amount of detail returned. See the [Depth](#depth) section. | Pass the arguments to `list_snapshots`: response = client.list_snapshots() --- #### Get a Snapshot Retrieves the attributes of a specific snapshot. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | snapshot_id | **yes** | string | The ID of the snapshot. | Pass the arguments to `get_snapshot`: response = client.get_snapshot(snapshot_id='UUID') --- #### Update a Snapshot Perform updates to attributes of a snapshot. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | snapshot_id | **yes** | string | The ID of the snapshot. | | name | no | string | The name of the snapshot. | | description | no | string | The description of the snapshot. | | licence_type | no | string | The snapshot's licence type: LINUX, WINDOWS, WINDOWS2016, UNKNOWN or OTHER. | | cpu_hot_plug | no | bool | This volume is capable of CPU hot plug (no reboot required) | | cpu_hot_unplug | no | bool | This volume is capable of CPU hot unplug (no reboot required) | | ram_hot_plug | no | bool | This volume is capable of memory hot plug (no reboot required) | | ram_hot_unplug | no | bool | This volume is capable of memory hot unplug (no reboot required) | | nic_hot_plug | no | bool | This volume is capable of NIC hot plug (no reboot required) | | nic_hot_unplug | no | bool | This volume is capable of NIC hot unplug (no reboot required) | | disc_virtio_hot_plug | no | bool | This volume is capable of VirtIO drive hot plug (no reboot required) | | disc_virtio_hot_unplug | no | bool | This volume is capable of VirtIO drive hot unplug (no reboot required) | | disc_scsi_hot_plug | no | bool | This volume is capable of SCSI drive hot plug (no reboot required) | | disc_scsi_hot_unplug | no | bool | This volume is capable of SCSI drive hot unplug (no reboot required) | Pass the arguments to `update_snapshot`: response = client.update_snapshot( snapshot_id='UUID', name='New Name', description='New description') --- #### Delete a Snapshot Deletes the specified snapshot. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | snapshot_id | **yes** | string | The ID of the snapshot. | Pass the arguments to `delete_snapshot`: response = client.delete_snapshot(snapshot_id='deleting_snapshot_id') --- ## IP Blocks The IP block operations assist with managing reserved /static public IP addresses. #### List IP Blocks Retrieve a list of available IP blocks. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | depth | no | int | An integer value of 0 - 5 that affects the amount of detail returned. See the [Depth](#depth) section. | Pass the arguments to `list_ipblocks`: response = client.list_ipblocks() --- #### Get an IP Block Retrieves the attributes of a specific IP block. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | ipblock_id | **yes** | string | The ID of the IP block. | Pass the arguments to `get_ipblock`: response = client.get_ipblock('UUID') --- #### Create an IP Block Creates an IP block. Creating an IP block is a bit different than some of the other available create operations. IP blocks are not attached to a particular VDC, but rather to a location. Therefore, you must specify a valid `location` along with a `size` argument indicating the number of IP addresses you want to reserve in the IP block. Any resources using an IP address from an IP block must be in the same `location`. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | ipblock | **yes** | object | An [IPBlock object](#ipblock-resource-object) you wish to create. | To create an IP block, define the `IPBlock` resource object: ipblock = IPBlock( name='IP Block Name', size=4, location='de/fkb') Pass it to `reserve_ipblock`: response = client.reserve_ipblock(ipblock) #### IPBlock Resource Object | Name | Required | Type | Description | |---|:-:|---|---| | location | **yes** | string | This must be one of the locations: us/las, us/ewr, de/fra, de/fkb. | | size | **yes** | int | The size of the IP block you want. | | name | no | string | A descriptive name for the IP block | The following table outlines the locations currently supported: | Value| Country | City | |---|---|---| | us/las | United States | Las Vegas | | us/ewr | United States | Newark | | de/fra | Germany | Frankfurt | | de/fkb | Germany | Karlsruhe | --- #### Delete an IP Block Deletes the specified IP Block. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | ipblock_id | **yes** | string | The ID of the IP block. | Pass the arguments to `delete_ipblock`: response = client.delete_ipblock('UUID') --- ## LANs #### List LANs Retrieve a list of LANs within the VDC. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | depth | no | int | An integer value of 0 - 5 that affects the amount of detail returned. See the [Depth](#depth) section. | Pass the arguments to `list_lans`: response = client.list_lans(datacenter_id='UUID') --- #### Create a LAN Creates a LAN within a VDC. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | lan | **yes** | object | A [LAN object](#lan-resource-object) describing the LAN to create. | Create the `LAN` resource object: lan = LAN( name='LAN Name', public=True) Pass the object and arguments to `create_lan`: response = client.create_lan( datacenter_id='UUID', lan=lan) #### LAN Resource Object | Name | Required | Type | Description | |---|:-:|---|---| | name | no | string | The name of your LAN. | | public | **Yes** | bool | Boolean indicating if the LAN faces the public Internet or not. | | nics | no | list | One or more NIC IDs attached to the LAN. | --- #### Get a LAN Retrieves the attributes of a given LAN. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | lan_id | **yes** | int | The ID of the LAN. | | depth | no | int | An integer value of 0 - 5 that affects the amount of detail returned. See the [Depth](#depth) section. | Pass the arguments to `get_lan`: response = client.get_lan( datacenter_id='UUID', lan_id=ID) --- #### Get LAN Members Retrieves the list of NICs that are part of the LAN. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | lan_id | **yes** | int | The ID of the LAN. | | depth | no | int | An integer value of 0 - 5 that affects the amount of detail returned. See the [Depth](#depth) section. | Pass the arguments to `get_lan_members`: response = client.get_lan_members( datacenter_id='UUID', lan_id=ID) --- #### Update a LAN Perform updates to attributes of a LAN. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | lan_id | **yes** | int | The ID of the LAN. | | name | no | string | A descriptive name for the LAN. | | public | no | bool | Boolean indicating if the LAN faces the public Internet or not. | | ip_failover | no | list | A list of IP fail-over dicts. | Pass the arguments to `update_lan`: ip_failover = dict() ip_failover['ip'] = 'IP_address' ip_failover['nicUuid'] = 'UUID' response = client.update_lan( datacenter_id='UUID', lan_id=ID, name='New LAN Name', public=True, ip_failover=[ip_failover]) --- #### Delete a LAN Deletes the specified LAN. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | lan_id | **yes** | string | The ID of the LAN. | Pass the arguments to `delete_lan`: response = client.delete_lan( datacenter_id='datacenter_id', lan_id=ID) --- ## Network Interfaces (NICs) #### List NICs Retrieve a list of LANs within the VDC. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | server_id | **yes** | string | The ID of the server. | | depth | no | int | An integer value of 0 - 5 that affects the amount of detail returned. See the [Depth](#depth) section. | Pass the arguments to `list_nics`: response = client.list_nics( datacenter_id='UUID', server_id='UUID') --- #### Get a NIC Retrieves the attributes of a given NIC. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | server_id | **yes** | string | The ID of the server. | | nic_id | **yes** | string | The ID of the NIC. | | depth | no | int | An integer value of 0 - 5 that affects the amount of detail returned. See the [Depth](#depth) section. | Pass the arguments to `get_nic`: response = client.get_nic( datacenter_id='UUID', server_id='UUID', nic_id='UUID') --- #### Create a NIC Adds a NIC to the target server. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | server_id | **yes** | string| The ID of the server. | | nic | **yes** | object | A [NIC object](#nic-resource-object) describing the NIC to be created. | Create the `NIC` resource object: nic = NIC( name='NIC Name', dhcp=True, lan=1, nat=False) Pass the object and arguments to `create_nic`: response = client.create_nic( datacenter_id='UUID', server_id='UUID', nic=nic) #### NIC Resource Object | Name | Required | Type | Description | |---|:-:|---|---| | name | no | string | The name of the NIC. | | ips | no | list | IP addresses assigned to the NIC. | | dhcp | no | bool | Set to *false* if you wish to disable DHCP on the NIC. Default: *true*. | | lan | **yes** | int | The LAN ID the NIC will sit on. If the LAN ID does not exist it will be created. | | nat | no | bool | Indicates the private IP address has outbound access to the public internet. | | firewall_active | no | bool | Set this to *true* to enable the ProfitBricks firewall, *false* to disable. | | firewall_rules | no | list | A list of [FirewallRule objects](#firewall-rule-resource-object) to be created with the NIC. | --- #### Update a NIC You can update -- in full or partially -- various attributes on the NIC; however, some restrictions are in place: The primary address of a NIC connected to a load balancer can only be changed by changing the IP of the load balancer. You can also add additional reserved, public IPs to the NIC. The user can specify and assign private IPs manually. Valid IP addresses for private networks are 10.0.0.0/8, 172.16.0.0/12 or 192.168.0.0/16. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | server_id | **yes** | string| The ID of the server. | | nic_id | **yes** | string| The ID of the NIC. | | name | no | string | The name of the NIC. | | ips | no | list | IPs assigned to the NIC represented as a list of strings. | | dhcp | no | bool | Boolean value that indicates if the NIC is using DHCP or not. | | lan | no | int | The LAN ID the NIC sits on. | | nat | no | bool | Indicates the private IP address has outbound access to the public internet. | | firewall_active | no | bool | Set this to *true* to enable the ProfitBricks firewall, *false* to disable. | Pass the arguments to `update_nic`: response = client.update_nic( datacenter_id='UUID', server_id='UUID', nic_id='UUID', name='New Name') --- #### Delete a NIC Deletes the specified NIC. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | server_id | **yes** | string| The ID of the server. | | nic_id | **yes** | string| The ID of the NIC. | Pass the arguments to `delete_nic`: response = client.delete_nic( datacenter_id='UUID', server_id='UUID', nic_id='UUID') --- ## Firewall Rules #### List Firewall Rules Retrieves a list of firewall rules associated with a particular NIC. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | server_id | **yes** | string | The ID of the server. | | nic_id | **yes** | string | The ID of the NIC. | | depth | no | int | An integer value of 0 - 5 that affects the amount of detail returned. See the [Depth](#depth) section. | Pass the arguments to `get_firewall_rules`: response = client.get_firewall_rules( datacenter_id='UUID', server_id='UUID', nic_id='UUID') --- #### Get a Firewall Rule Retrieves the attributes of a given firewall rule. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | server_id | **yes** | string | The ID of the server. | | nic_id | **yes** | string | The ID of the NIC. | | firewall_rule_id | **yes** | string | The ID of the firewall rule. | Pass the arguments to `get_firewall_rule`: response = client.get_firewall_rule( datacenter_id='UUID', loadbalancer_id='UUID', nic_id='UUID', firewall_rule_id='UUID') --- #### Create a Firewall Rule This will add a firewall rule to the NIC. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | server_id | **yes** | string | The ID of the server. | | nic_id | **yes** | string | The ID of the NIC. | | firewall_rule | **yes** | object | A [FirewallRule object](#firewall-rule-resource-object) describing the firewall rule to be created. | Create the `FirewallRule` resource object: fwrule = FirewallRule( name='Allow SSH', protocol='TCP', source_mac='01:23:45:67:89:00', port_range_start=22, port_range_end=22) Pass the object and arguments to `create_firewall_rule`: response = client.create_firewall_rule( datacenter_id='UUID', loadbalancer_id='UUID', nic_id='UUID', firewall_rule=fwrule) #### Firewall Rule Resource Object | Name | Required | Type | Description | |---|:-:|---|---| | name | no | string | The name of the firewall rule. | | protocol | **yes** | string | The protocol for the rule: TCP, UDP, ICMP, ANY. | | source_mac | no | string | Only traffic originating from the respective MAC address is allowed. Valid format: aa:bb:cc:dd:ee:ff. A *null* value allows all source MAC address. | | source_ip | no | string | Only traffic originating from the respective IPv4 address is allowed. A *null* value allows all source IPs. | | target_ip | no | string | In case the target NIC has multiple IP addresses, only traffic directed to the respective IP address of the NIC is allowed. A *null* value allows all target IPs. | | port_range_start | no | string | Defines the start range of the allowed port (from 1 to 65534) if protocol TCP or UDP is chosen. Leave `port_range_start` and `port_range_end` value as *null* to allow all ports. | | port_range_end | no | string | Defines the end range of the allowed port (from 1 to 65534) if the protocol TCP or UDP is chosen. Leave `port_range_start` and `port_range_end` value as *null* to allow all ports. | | icmp_type | no | string | Defines the allowed type (from 0 to 254) if the protocol ICMP is chosen. A *null* value allows all types. | | icmp_code | no | string | Defines the allowed code (from 0 to 254) if protocol ICMP is chosen. A *null* value allows all codes. | --- #### Update a Firewall Rule Perform updates to an existing firewall rule. You will notice that some arguments, such as `protocol` cannot be updated. If the `protocol` needs to be changed, you can [delete](#delete-a-firewall-rule) the firewall rule and then [create](#create-a-firewall-rule) new one to replace it. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | server_id | **yes** | string | The ID of the server. | | nic_id | **yes** | string | The ID of the NIC. | | firewall_rule_id | **yes** | string | The ID of the firewall rule. | | name | no | string | The name of the firewall rule. | | source_mac | no | string | Only traffic originating from the respective MAC address is allowed. Valid format: aa:bb:cc:dd:ee:ff. A *null* value allows all source MAC address. | | source_ip | no | string | Only traffic originating from the respective IPv4 address is allowed. A *null* value allows all source IPs. | | target_ip | no | string | In case the target NIC has multiple IP addresses, only traffic directed to the respective IP address of the NIC is allowed. A *null* value allows all target IPs. | | port_range_start | no | string | Defines the start range of the allowed port (from 1 to 65534) if protocol TCP or UDP is chosen. Leave `port_range_start` and `port_range_end` value as *null* to allow all ports. | | port_range_end | no | string | Defines the end range of the allowed port (from 1 to 65534) if the protocol TCP or UDP is chosen. Leave `port_range_start` and `port_range_end` value as *null* to allow all ports. | | icmp_type | no | string | Defines the allowed type (from 0 to 254) if the protocol ICMP is chosen. A *null* value allows all types. | | icmp_code | no | string | Defines the allowed code (from 0 to 254) if protocol ICMP is chosen. A *null* value allows all codes. | Pass the arguments to `update_firewall_rule`: response = client.update_firewall_rule( datacenter_id='UUID', loadbalancer_id='UUID', nic_id='UUID', firewall_rule_id='UUID', name="Updated Name") --- #### Delete a Firewall Rule Removes a firewall rule. | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | server_id | **yes** | string | The ID of the server. | | nic_id | **yes** | string | The ID of the NIC. | | firewall_rule_id | **yes** | string | The ID of the firewall rule. | Pass the arguments to `delete_firewall_rule`: response = client.delete_firewall_rule( datacenter_id='UUID', server_id='UUID', nic_id='UUID', firewall_rule_id='UUID') --- ## Load Balancers #### List Load Balancers Retrieve a list of load balancers within the data center. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | depth | no | int | An integer value of 0 - 5 that affects the amount of detail returned. See the [Depth](#depth) section. | Pass the arguments to `list_loadbalancers`: response = client.list_loadbalancers(datacenter_id='UUID') --- #### Get a Load Balancer Retrieves the attributes of a given load balancer. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | loadbalancer_id | **yes** | string | The ID of the load balancer. | Pass the arguments to `get_loadbalancer`: response = client.get_loadbalancer( datacenter_id='UUID', loadbalancer_id='UUID') --- #### Create a Load Balancer Creates a load balancer within the VDC. Load balancers can be used for public or private IP traffic. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | loadbalancer | **yes** | object | A [LoadBalancer object](#load-balancer-resource-object) describing the load balancer to be created. | Create the `LoadBalancer` resource object: loadbalancer = LoadBalancer( name='Load Balancer Name', dhcp=True) Pass the object and arguments to `create_loadbalancer`: response = client.create_loadbalancer( datacenter_id='UUID', loadbalancer=loadbalancer) #### Load Balancer Resource Object | Name | Required | Type | Description | |---|:-:|---|---| | name | **yes** | string | The name of the load balancer. | | ip | no | string | IPv4 address of the load balancer. All attached NICs will inherit this IP. | | dhcp | no | bool | Indicates if the load balancer will reserve an IP using DHCP. | | balancednics | no | list | List of NIC IDs taking part in load-balancing. All balanced NICs inherit the IP of the load balancer. | --- #### Update a Load Balancer Perform updates to attributes of a load balancer. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | name | no | string | The name of the load balancer. | | ip | no | string | The IP of the load balancer. | | dhcp | no | bool | Indicates if the load balancer will reserve an IP using DHCP. | Pass the arguments to `update_loadbalancer`: response = client.update_loadbalancer( datacenter_id='UUID', loadbalancer_id='UUID', name="New Name") --- #### Delete a Load Balancer Deletes the specified load balancer. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | load_balancer_id | **yes** | string | The ID of the load balancer. | Pass the arguments to `delete_loadbalancer`: response = client.delete_loadbalancer( datacenter_id='UUID', loadbalancer_id='UUID') --- #### List Load Balanced NICs This will retrieve a list of NICs associated with the load balancer. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | loadbalancer_id | **yes** | string | The ID of the load balancer. | | depth | no | int | An integer value of 0 - 5 that affects the amount of detail returned. See the [Depth](#depth) section. | Pass the arguments to `get_loadbalancer_members`: response = client.get_loadbalancer_members( datacenter_id='UUID', loadbalancer_id='UUID') --- #### Get a Load Balanced NIC Retrieves the attributes of a given load balanced NIC. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | loadbalancer_id | **yes** | string | The ID of the load balancer. | | nic_id | **yes** | string | The ID of the NIC. | | depth | no | int | An integer value of 0 - 5 that affects the amount of detail returned. See the [Depth](#depth) section. | Pass the arguments to `get_loadbalanced_nic`: response = client.get_loadbalanced_nic( datacenter_id='UUID', loadbalancer_id='UUID', nic_id='UUID') --- #### Associate NIC to a Load Balancer This will associate a NIC to a load balancer, enabling the NIC to participate in load-balancing. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | loadbalancer_id | **yes** | string | The ID of the load balancer. | | nic_id | **yes** | string | The ID of the NIC. | Pass the arguments to `add_loadbalanced_nics`: response = client.add_loadbalanced_nics( datacenter_id='UUID', loadbalancer_id='UUID', nic_id='UUID') --- #### Remove a NIC Association Removes the association of a NIC with a load balancer. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | datacenter_id | **yes** | string | The ID of the VDC. | | loadbalancer_id | **yes** | string | The ID of the load balancer. | | nic_id | **yes** | string | The ID of the NIC you are removing from the load balancer. | Pass the arguments to `remove_loadbalanced_nic`: response = client.remove_loadbalanced_nic( datacenter_id='UUID', loadbalancer_id='UUID', nic_id='UUID') --- ## User Management #### List Groups Retrieves a list of all groups. | Name | Required | Type | Description | |---|:-:|---|---| | depth | no | int | An integer value of 0 - 5 that affects the amount of detail returned. | response = client.list_groups() --- #### Get a Group Retrieves the attributes of a given group. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | group_id | **yes** | string | The ID of the group. | | depth | no | int | An integer value of 0 - 5 that affects the amount of detail returned. | response = client.get_group(group_id='UUID') --- #### Create a Group Creates a new group and set group privileges. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | name | **yes** | string | The ID of the group. | | create_datacenter | no | bool | Indicates if the group is allowed to create virtual data centers. | | create_snapshot | no | bool | Indicates if the group is allowed to create snapshots. | | reserve_ip | no | bool | Indicates if the group is allowed to reserve IP addresses. | | access_activity_log | no | bool | Indicates if the group is allowed to access activity log. | group = Group( name='my-group', create_datacenter=True, create_snapshot=False, reserve_ip=True, access_activity_log=False) response = client.create_group(group) --- #### Update a Group Updates a group's name or privileges. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | group_id | **yes** | string | The ID of the group. | | name | **yes** | string | The ID of the group. | | create_datacenter | no | bool | Indicates if the group is allowed to create virtual data centers. | | create_snapshot | no | bool | Indicates if the group is allowed to create snapshots. | | reserve_ip | no | bool | Indicates if the group is allowed to reserve IP addresses. | | access_activity_log | no | bool | Indicates if the group is allowed to access activity log. | response = client.update_group( group_id='UUID', name='my-group', create_datacenter=False, create_snapshot=True, reserve_ip=False, access_activity_log=True) --- #### Delete a Group Deletes the specified group. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | group_id | **yes** | string | The ID of the group. | response = client.delete_group(group_id='UUID') --- #### List Shares Retrieves a list of all shares though a group. | Name | Required | Type | Description | |---|:-:|---|---| | group_id | **yes** | string | The ID of the group. | | depth | no | int | An integer value of 0 - 5 that affects the amount of detail returned. | response = client.list_shares(group_id='UUID') --- #### Get a Share Retrieves a specific resource share available to a group. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | group_id | **yes** | string | The ID of the group. | | resource_id | **yes** | string | The ID of the resource. | | depth | no | int | An integer value of 0 - 5 that affects the amount of detail returned. | response = client.get_share( group_id='UUID', resource_id='UUID') --- #### Add a Share Shares a resource through a group. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | group_id | **yes** | string | The ID of the group. | | resource_id | **yes** | string | The ID of the resource. | | edit_privilege | no | string | Indicates that the group has permission to edit privileges on the resource. | | share_privilege | no | string | Indicates that the group has permission to share the resource. | response = client.add_share( group_id='UUID', resource_id='UUID', edit_privilege=True, share_privilege=True) --- #### Update a Share Updates the permissions of a group for a resource share. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | group_id | **yes** | string | The ID of the group. | | resource_id | **yes** | string | The ID of the resource. | | edit_privilege | no | string | Indicates that the group has permission to edit privileges on the resource. | | share_privilege | no | string | Indicates that the group has permission to share the resource. | response = client.update_share( group_id='UUID', resource_id='UUID', edit_privilege=True, share_privilege=True) --- #### Delete a Share Removes a resource share from a group. | Name | Required | Type | Description | |---|:-:|---|---| | group_id | **yes** | string | The ID of the group. | | resource_id | **yes** | string | The ID of the resource. | response = client.delete_share( group_id='UUID', resource_id='UUID') --- #### List Users Retrieves a list of all users. | Name | Required | Type | Description | |---|:-:|---|---| | depth | no | int | An integer value of 0 - 5 that affects the amount of detail returned. | response = client.list_users() --- #### Get a User Retrieves a single user. | Name | Required | Type | Description | |---|:-:|---|---| | user_id | **yes** | string | The ID of the user. | | depth | no | int | An integer value of 0 - 5 that affects the amount of detail returned. | response = client.get_user(user_id='UUID') --- #### Create a User Creates a new user. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | firstname | **yes** | string | A name for the user. | | lastname | **yes** | bool | A name for the user. | | email | **yes** | bool | An e-mail address for the user. | | password | **yes** | bool | A password for the user. | | administrator | no | bool | Assigns the user have administrative rights. | | force_sec_auth | no | bool | Indicates if secure (two-factor) authentication should be forced for the user. | user = User( firstname='John', lastname='Doe', email='no-reply@example.com', password='secretpassword123', administrator=True, force_sec_auth=False) response = client.create_user(user) --- #### Update a User Updates an existing user. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | user_id | **yes** | string | The ID of the user. | | firstname | **yes** | string | A name for the user. | | lastname | **yes** | bool | A name for the user. | | email | **yes** | bool | An e-mail address for the user. | | administrator | **yes** | bool | Assigns the user have administrative rights. | | force_sec_auth | **yes** | bool | Indicates if secure (two-factor) authentication should be forced for the user. | response = client.update_user( user_id='UUID', firstname='John', lastname='Doe', email='no-reply@example.com', administrator=True, force_sec_auth=False) --- #### Delete a User Removes a user. | Name | Required | Type | Description | |---|:-:|---|---| | user_id | **yes** | string | The ID of the user. | response = client.delete_user(user_id='UUID') --- #### List Users in a Group Retrieves a list of all users that are members of a particular group. | Name | Required | Type | Description | |---|:-:|---|---| | group_id | **yes** | string | The ID of the group. | | depth | no | int | An integer value of 0 - 5 that affects the amount of detail returned. | response = client.list_group_users(group_id='UUID') --- #### Add User to Group Adds an existing user to a group. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | group_id | **yes** | string | The ID of the group. | | user_id | **yes** | string | The ID of the user. | response = client.add_group_user( group_id='UUID', user_id='UUID') --- #### Remove User from a Group Removes a user from a group. | Name | Required | Type | Description | |---|:-:|---|---| | group_id | **yes** | string | The ID of the group. | | user_id | **yes** | string | The ID of the user. | response = client.remove_group_user( group_id='UUID', user_id='UUID') --- #### List Resources Retrieves a list of all resources. Alternatively, Retrieves all resources of a particular type. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | resource_type | no | string | The resource type: `datacenter`, `image`, `snapshot` or `ipblock`. | | depth | no | int | An integer value of 0 - 5 that affects the amount of detail returned. | response = client.list_resources() response = client.list_resources(resource_type='snapshot') --- #### Get a Resource Retrieves a single resource of a particular type. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | resource_type | **yes** | string | The resource type: `datacenter`, `image`, `snapshot` or `ipblock`. | | resource_id | **yes** | string | The ID of the resource. | | depth | no | int | An integer value of 0 - 5 that affects the amount of detail returned. | response = client.get_resource(resource_id='UUID') response = client.get_resource( resource_type='datacenter', resource_id='UUID') --- ## Contract Resources #### List Contract Resources Retrieves information about the resource limits for a particular contract and the current resource usage. | Name | Required | Type | Description | |---|:-:|---|---| | depth | no | int | An integer value of 0 - 5 that affects the amount of detail returned. | response = client.list_contracts() --- ## Requests Each call to the ProfitBricks Cloud API is assigned a request ID. These operations can be used to get information about the requests that have been submitted and their current status. #### List Requests Retrieve a list of requests. | Name | Required | Type | Description | |---|:-:|---|---| | depth | no | int | An integer value of 0 - 5 that affects the amount of detail returned. See the [Depth](#depth) section. | Pass the arguments to `list_requests`: response = client.list_requests() --- #### Get a Request Retrieves the attributes of a specific request. This operation shares the same `get_request` method used for getting request status, however the response it determined by the boolean value you pass for *status*. To get details about the request itself, you want to pass a *status* of *False*. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | request_id | **yes** | string | The ID of the request. | | status | **yes** | bool | Set to *False* to have the request details returned. | Pass the arguments to `get_request`: response = client.get_request( request_id='UUID', status=False) --- #### Get a Request Status Retrieves the status of a request. This operation shares the same `get_request` method used for getting the details of a request, however the response it determined by the boolean value you pass for *status*. To get the request status, you want to pass a *status* of *True*. The following table describes the request arguments: | Name | Required | Type | Description | |---|:-:|---|---| | request_id | **yes** | string | The ID of the request. | | status | **yes** | bool | Set to *True* to have the status of the request returned. | Pass the arguments to `get_request`: response = client.get_request( request_id='UUID', status=True) --- ## Examples Below are some examples using the SDK for Python. These examples will assume credentials are being set with environment variables: export PROFITBRICKS_USERNAME=username export PROFITBRICKS_PASSWORD=password #### List All Data Centers This simple example will list all data centers under an account. #!/usr/bin/python import json import os from profitbricks.client import ProfitBricksService # Instatiate ProfitBricks connection client = ProfitBricksService( username=os.getenv('PROFITBRICKS_USERNAME'), password=os.getenv('PROFITBRICKS_PASSWORD')) # List data centers datacenters = client.list_datacenters() print json.dumps(datacenters, indent=4) #### Search for Images The following example will provide a method for retrieving a list of images based on a partial case-insensitive name and location match. #!/usr/bin/python import os from profitbricks.client import ProfitBricksService def find_image(conn, name, location): ''' Find image by partial name and location ''' images = [] for item in conn.list_images()['items']: if (item['properties']['location'] == location and item['properties']['imageType'] == 'HDD' and name.lower() in item['properties']['name'].lower()): images.append(item) return images # Instantiate ProfitBricks connection client = ProfitBricksService( username=os.getenv('PROFITBRICKS_USERNAME'), password=os.getenv('PROFITBRICKS_PASSWORD')) # Search criteria based on partial case-insensitive name and location name = 'Ubuntu' location = 'de/fkb' # Find images based on above search criteria for image in find_image(client, name, location): print "{0}\t{1}\t{2}".format( image['id'], image['properties']['name'], image['properties']['location']) #### Reserve an IP Block Here we will reserve a public IP block. #!/usr/bin/python import json import os from profitbricks.client import ProfitBricksService, IPBlock # Instatiate ProfitBricks connection client = ProfitBricksService( username=os.getenv('PROFITBRICKS_USERNAME'), password=os.getenv('PROFITBRICKS_PASSWORD')) ipblock = IPBlock(location='us/las', size=5) response = client.reserve_ipblock(ipblock) print json.dumps(response, indent=4) #### Wait for Resources The remaining examples will require dependent resources. A volume cannot be attached to a server before the server and volume are finished provisioning. Therefore, we require the `wait_for_completion` method that will stop and wait for the server and volume to finish provisioning before attaching the volume to the server. #### Component Build ProfitBricks allows servers to be built by their individual components. That is, by connecting customized components such as servers, volumes, and NICs together. For example, a server can be provisioned in one request followed by one or more NICs and volumes in following requests. The volumes can then be attached separately to the server. It is important to note that you will need to wait for each individual component to finish provisioning before it can be used in subsequent operations. This behavior is demonstrated below. #!/usr/bin/python import json import os from profitbricks.client import ProfitBricksService from profitbricks.client import ( Datacenter, LAN, Server, NIC, Volume, FirewallRule) client = ProfitBricksService( username=os.getenv('PROFITBRICKS_USERNAME'), password=os.getenv('PROFITBRICKS_PASSWORD')) timeout = 1800 # Create data center datacenter = Datacenter( name='Python SDK Data Center', description='Python SDK data center', location='us/las') response = client.create_datacenter(datacenter=datacenter) client.wait_for_completion(response, timeout) datacenter_id = response['id'] # Create public LAN lan = LAN(name="Public LAN", public=True) response = client.create_lan(datacenter_id, lan=lan) client.wait_for_completion(response, timeout) lan_id = response['id'] # Create server server = Server( name='Python SDK Server', ram=4096, cores=4, cpu_family='INTEL_XEON') response = client.create_server(datacenter_id=datacenter_id, server=server) client.wait_for_completion(response, timeout) server_id = response['id'] # Create public NIC nic = NIC( name='Public NIC', dhcp=True, lan=lan_id, firewall_active=True, nat=False) response = client.create_nic( datacenter_id=datacenter_id, server_id=server_id, nic=nic) client.wait_for_completion(response, timeout) nic_id = response['id'] # Create firwall rule fwrule = FirewallRule( name='Allow SSH', protocol='TCP', source_ip='0.0.0.0', port_range_start=22, port_range_end=22, icmp_type=None) response = client.create_firewall_rule( datacenter_id=datacenter_id, server_id=server_id, nic_id=nic_id, firewall_rule=fwrule) client.wait_for_completion(response, timeout) # Create system volume volume1 = Volume( name='System Volume', size=20, image='0d4f97f0-1689-11e7-97ce-525400f64d8d', bus='VIRTIO', type='HDD', ssh_keys=['ssh-rsa AAAAB3NzaC1yc2EAAAADAQ...'], image_password='s3cr3tpass0rd', availability_zone='ZONE_3') response = client.create_volume( datacenter_id=datacenter_id, volume=volume1) client.wait_for_completion(response, timeout) volume1_id = response['id'] # Attach system volume response = client.attach_volume( datacenter_id=datacenter_id, server_id=server_id, volume_id=volume1_id) client.wait_for_completion(response, timeout) # Create data volume volume2 = Volume( name='Data Volume', size=100, type='SSD', bus='VIRTIO', license_type='OTHER') response = client.create_volume( datacenter_id=datacenter_id, volume=volume2) client.wait_for_completion(response, timeout) volume2_id = response['id'] # Attach data volume response = client.attach_volume( datacenter_id=datacenter_id, server_id=server_id, volume_id=volume2_id) client.wait_for_completion(response, timeout) live_datacenter = client.get_datacenter(datacenter_id=datacenter_id, depth=5) print json.dumps(live_datacenter, indent=4) #### Composite Build The ProfitBricks platform also allows fully operational servers to be provisioned with a single request. This is accomplished by nesting related resources. Multiple servers, volumes, LANs, and load balancers can be nested under a data center, multiple NICs and volumes can be nested under servers, and firewall rules under NICs. This example will demonstrate composite resources. #!/usr/bin/python import json import os from profitbricks.client import ProfitBricksService from profitbricks.client import Datacenter, Server, NIC, Volume, FirewallRule # Instatiate ProfitBricks connection client = ProfitBricksService( username=os.getenv('PROFITBRICKS_USERNAME'), password=os.getenv('PROFITBRICKS_PASSWORD')) # Define a firewall rule fwrule1 = FirewallRule( name='Allow SSH', protocol='TCP', source_ip='0.0.0.0', port_range_start=22, port_range_end=22, icmp_type=None) # Define a public NIC nic1 = NIC( name='Public NIC', dhcp=True, lan=1, firewall_active=True, firewall_rules=[fwrule1], nat=False) # Define a private NIC nic2 = NIC( name='Private NIC', dhcp=True, lan=2) # Define a system volume volume1 = Volume( name='System Volume', size=20, image='0d4f97f0-1689-11e7-97ce-525400f64d8d', bus='VIRTIO', type='HDD', ssh_keys=['ssh-rsa AAAAB3NzaC1yc2EAAAADAQ...'], image_password='s3cr3tpass0rd', availability_zone='ZONE_3') # Define a data volume volume2 = Volume( name='Data Volume', size=100, type='SSD', bus='VIRTIO', licence_type='OTHER') # Define a server with associated NICs and volumes server = Server( name='Python SDK Server', ram=4096, cores=4, cpu_family='INTEL_XEON', nics=[nic1, nic2], create_volumes=[volume1, volume2]) # Define a data center with the server datacenter = Datacenter( name='Python SDK Data Center', description='Python SDK data center', location='us/las', servers=[server]) # Initiate the data center and nested resource provisioning response = client.create_datacenter(datacenter) # Wait for the data center and nested resources to finish provisioning client.wait_for_completion(response) datacenter_id = response['id'] # Set the first LAN to public response = client.update_lan( datacenter_id=datacenter_id, lan_id=1, name='Public LAN', public=True) client.wait_for_completion(response) # Print the data center properties and nested resources response = client.get_datacenter(datacenter_id=datacenter_id, depth=5) print json.dumps(response, indent=4) ## Support You can find additional examples in the repository `examples` directory. If you find any issues, please let us know via the [DevOps Central community](https://devops.profitbricks.com) or [GitHub's issue system](https://github.com/profitbricks/profitbricks-sdk-python/issues) and we'll check it out. ## Testing You can find a full list of tests inside the `tests` folder. To run all available tests: export PROFITBRICKS_USERNAME=username export PROFITBRICKS_PASSWORD=password pip install -r requirements.txt python -m unittest discover tests To run a single test: python -m unittest discover tests test_datacenter.py ## Contributing 1. Fork it ( https://github.com/profitbricks/profitbricks-sdk-python/fork ) 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Commit your changes (`git commit -am 'Add some feature'`) 4. Push to the branch (`git push origin my-new-feature`) 5. Create a new Pull Request profitbricks-sdk-python-4.1.3/examples/000077500000000000000000000000001326076220500201375ustar00rootroot00000000000000profitbricks-sdk-python-4.1.3/examples/datacenter_example.py000066400000000000000000000064701326076220500243450ustar00rootroot00000000000000#!/usr/bin/python3 # Copyright 2015-2017 ProfitBricks GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import os """List Datacenters """ from profitbricks.client import ProfitBricksService client = ProfitBricksService( username=os.getenv('PROFITBRICKS_USERNAME'), password=os.getenv('PROFITBRICKS_PASSWORD')) datacenters = client.list_datacenters() for d in datacenters['items']: vdc = client.get_datacenter(d['id']) name = vdc['properties']['name'] datacenter_id = vdc['id'] break """Get Datacenter """ from profitbricks.client import ProfitBricksService # noqa datacenter_id = '700e1cab-99b2-4c30-ba8c-1d273ddba022' client = ProfitBricksService( username='username', password='password') datacenter = client.get_datacenter( datacenter_id=datacenter_id) """Create Simple Datacenter """ from profitbricks.client import ProfitBricksService # noqa from profitbricks.client import Datacenter, Volume, Server # noqa i = Datacenter( name='dc1', description='My New Datacenter', location='de/fkb' ) response = client.create_datacenter(datacenter=i) """Create Complex Datacenter """ from profitbricks.client import ProfitBricksService # noqa from profitbricks.client import Datacenter, LAN, NIC, LoadBalancer, FirewallRule # noqa image_id = 'df8382a1-0f40-11e6-ab6b-52540005ab80' fwrule1 = FirewallRule( name='Open SSH port', protocol='TCP', source_mac='01:23:45:67:89:00', port_range_start=22, port_range_end=22 ) fwrule2 = FirewallRule( name='Allow PING', protocol='ICMP', icmp_type=8, icmp_code=0 ) fw_rules = [fwrule1, fwrule2] nic1 = NIC( name='nic1', ips=['10.2.2.3'], dhcp='true', lan=1, firewall_active=True, firewall_rules=fw_rules ) nic2 = NIC( name='nic2', ips=['10.2.3.4'], dhcp='true', lan=1, firewall_active=True, firewall_rules=fw_rules ) nics = [nic1, nic2] volume1 = Volume( name='volume1', size=56, image=image_id, bus='VIRTIO', image_password="test1234" ) volume2 = Volume( name='volume2', size=56, image=image_id, bus='VIRTIO', image_password="test1234" ) volumes = [volume2] server1 = Server( name='My New Server1', ram=4096, cores=4, nics=nics, create_volumes=[volume1] ) servers = [server1] lan1 = LAN( name='public Lan 4', public=True ) lan2 = LAN( name='public Lan 5', public=True ) lans = [lan1, lan2] loadbalancer1 = LoadBalancer( name='LB01', ip='10.2.2.5', dhcp=False) loadbalancers = [loadbalancer1] d = Datacenter( name='My New Datacenter', description='Production environment', location='de/fkb', servers=servers, volumes=volumes, lans=lans, loadbalancers=loadbalancers ) response = client.create_datacenter(datacenter=d) del_response = client.delete_datacenter(response['id']) profitbricks-sdk-python-4.1.3/examples/image_examples.py000066400000000000000000000035241326076220500234750ustar00rootroot00000000000000#!/usr/bin/python3 # Copyright 2015-2017 ProfitBricks GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """List Images """ from profitbricks.client import ProfitBricksService client = ProfitBricksService( username='username', password='password') images = client.list_images() print(images) """ Update Image Valid image parameters are: * name (str) * description (str) * licence_type (one of 'LINUX', 'WINDOWS' or 'UNKNOWN') * cpu_hot_plug (bool) * ram_hot_plug (bool) * nic_hot_plug (bool) * nic_hot_unplug (bool) * disc_virtio_hot_plug (bool) * disc_virtio_hot_unplug (bool) * disc_scsi_hot_plug (bool) * disc_scsi_hot_unplug (bool) """ from profitbricks.client import ProfitBricksService # noqa client = ProfitBricksService( username='username', password='password') image_id = '7df81087-5835-41c6-a10b-3e098593bbd2' image = client.update_image( image_id, name='New name', description="Centos 7 with NGnix", licence_type='LINUX', cpu_hot_plug=True, ram_hot_plug=True, nic_hot_plug=True, nic_hot_unplug=True, disc_virtio_hot_plug=True, disc_virtio_hot_unplug=True) """Delete Image """ from profitbricks.client import ProfitBricksService # noqa client = ProfitBricksService( username='username', password='password') image_id = '7df81087-5835-41c6-a10b-3e098593bbd2' image = client.delete_image(image_id) profitbricks-sdk-python-4.1.3/examples/ipblock_examples.py000066400000000000000000000022731326076220500240360ustar00rootroot00000000000000#!/usr/bin/python3 # Copyright 2015-2017 ProfitBricks GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import os """List IPBlocks """ from profitbricks.client import ProfitBricksService client = ProfitBricksService( username=os.getenv('PROFITBRICKS_USERNAME'), password=os.getenv('PROFITBRICKS_PASSWORD')) ipblocks = client.list_ipblocks() print(ipblocks) """Reserve IPBlock """ from profitbricks.client import ProfitBricksService, IPBlock # noqa i = IPBlock(name='py-test', location='de/fra', size=1) ipblock = client.reserve_ipblock(i) """Release IPBlock """ from profitbricks.client import ProfitBricksService # noqa ipblock_id = ipblock['id'] ipblock = client.delete_ipblock(ipblock_id) profitbricks-sdk-python-4.1.3/examples/lan_example.py000066400000000000000000000036651326076220500230100ustar00rootroot00000000000000#!/usr/bin/python3 # Copyright 2015-2017 ProfitBricks GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """List LANs """ from profitbricks.client import ProfitBricksService datacenter_id = '700e1cab-99b2-4c30-ba8c-1d273ddba022' client = ProfitBricksService( username='username', password='password') lans = client.list_lans(datacenter_id=datacenter_id) print(lans) """Create Complex LAN """ from profitbricks.client import ProfitBricksService, LAN # noqa lan_id = '4' datacenter_id = '700e1cab-99b2-4c30-ba8c-1d273ddba022' client = ProfitBricksService( username='username', password='password') nics = ['', ''] i = LAN( name='public Lan 4', public=True, nics=nics) response = client.create_lan(datacenter_id=datacenter_id, lan=i) """Create LAN """ from profitbricks.client import ProfitBricksService, LAN # noqa lan_id = '4' datacenter_id = '700e1cab-99b2-4c30-ba8c-1d273ddba022' client = ProfitBricksService( username='username', password='password') i = LAN( name='public Lan 4', public=True) response = client.create_lan(datacenter_id=datacenter_id, lan=i) """Get LAN Members """ from profitbricks.client import ProfitBricksService # noqa lan_id = '4' datacenter_id = '700e1cab-99b2-4c30-ba8c-1d273ddba022' client = ProfitBricksService( username='username', password='password') members = client.get_lan_members(datacenter_id=datacenter_id, lan_id=lan_id) profitbricks-sdk-python-4.1.3/examples/nic_examples.py000066400000000000000000000044651326076220500231710ustar00rootroot00000000000000#!/usr/bin/python3 # Copyright 2015-2017 ProfitBricks GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """List NICs """ from profitbricks.client import ProfitBricksService datacenter_id = '700e1cab-99b2-4c30-ba8c-1d273ddba022' server_id = '700e1cab-99b2-4c30-ba8c-1d273ddba023' client = ProfitBricksService( username='username', password='password') nics = client.list_nics( datacenter_id=datacenter_id, server_id=server_id) for n in nics['items']: print(n['properties']['name']) """Create NIC """ from profitbricks.client import ProfitBricksService, NIC # noqa datacenter_id = '700e1cab-99b2-4c30-ba8c-1d273ddba022' server_id = '700e1cab-99b2-4c30-ba8c-1d273ddba023' client = ProfitBricksService( username='username', password='password') i = NIC( name='nic1', ips=['10.2.2.3', '10.2.3.4'], dhcp='true', lan=1, firewall_active=True ) response = client.create_nic( datacenter_id=datacenter_id, server_id=server_id, nic=i) """Create NIC with FirewallRules """ from profitbricks.client import ProfitBricksService, FirewallRule, NIC # noqa datacenter_id = '700e1cab-99b2-4c30-ba8c-1d273ddba022' server_id = '700e1cab-99b2-4c30-ba8c-1d273ddba023' nic_id = '' client = ProfitBricksService( username='username', password='password') fwrule1 = FirewallRule( name='Open SSH port', protocol='TCP', source_mac='01:23:45:67:89:00', port_range_start=22, port_range_end=22 ) fwrule2 = FirewallRule( name='Allow PING', protocol='ICMP', icmp_type=8, icmp_code=0 ) fw_rules = [fwrule1, fwrule2] i = NIC( name='nic1', ips=['10.2.2.3', '10.2.3.4'], dhcp='true', lan=1, firewall_active=True, firewall_rules=fw_rules ) response = client.create_nic( datacenter_id=datacenter_id, server_id=server_id, nic=i) profitbricks-sdk-python-4.1.3/examples/pb-api-shell000077500000000000000000000066571326076220500223600ustar00rootroot00000000000000#!/usr/bin/python3 # Copyright (C) 2015-2017, ProfitBricks GmbH # Authors: Benjamin Drung # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # pylint: disable=invalid-name """Interactive Python shell for the ProfitBricks REST API""" import argparse import code import logging import profitbricks import profitbricks.client def main(): """Main function of the interactive Python shell for the ProfitBricks REST API""" parser = argparse.ArgumentParser() parser.add_argument('-b', '--base-uri', default=profitbricks.API_HOST, help="Base URI for the REST API (default: %(default)s)") parser.add_argument('-c', dest="cmd", help="program passed in as string") parser.add_argument('-u', '--username', help="ProfitBricks user name (i.e. email address, " "default: read from config)") args = parser.parse_args() logging.getLogger("requests").setLevel(logging.WARNING) logger = logging.getLogger("pb-api-shell") client = profitbricks.client.ProfitBricksService( username=args.username, host_base=args.base_uri, client_user_agent='pb-api-shell/1.0') if args.cmd is None: banner = ("The ProfitBricks client can be accessed through the 'client' object.\n" "Other available objects: Datacenter, FirewallRule, Group, IPBlock, LAN,\n" " LoadBalancer, NIC, Server, Snapshot, User, Volume") variables = { 'client': client, 'Datacenter': profitbricks.client.Datacenter, 'FirewallRule': profitbricks.client.FirewallRule, 'Group': profitbricks.client.Group, 'IPBlock': profitbricks.client.IPBlock, 'LAN': profitbricks.client.LAN, 'LoadBalancer': profitbricks.client.LoadBalancer, 'NIC': profitbricks.client.NIC, 'Server': profitbricks.client.Server, 'Snapshot': profitbricks.client.Snapshot, 'User': profitbricks.client.User, 'Volume': profitbricks.client.Volume, } shell = None try: from IPython.terminal.embed import InteractiveShellEmbed shell = InteractiveShellEmbed(banner2=banner, user_ns=variables) except ImportError: logger.warning("IPython not available. Using normal Python shell.") if shell: shell() else: try: import readline except ImportError: logger.info("readline module not available. No tab-completion enabled.") else: import rlcompleter # noqa, pylint: disable=unused-variable readline.parse_and_bind("tab: complete") console = code.InteractiveConsole(locals=variables) console.interact(banner) else: eval(args.cmd) # pylint: disable=eval-used if __name__ == '__main__': main() profitbricks-sdk-python-4.1.3/examples/pb_addNewServer.py000066400000000000000000000234761326076220500235770ustar00rootroot00000000000000#!/usr/bin/python # encoding: utf-8 # Copyright 2016-2017 ProfitBricks GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ''' pb_addNewServer -- adds a new server to a data center pb_addNewServer is a tool to add a new server/storage composite to an existing data center. The server may be set up by a disk or cdrom image. @author: Jürgen Buchhammer @copyright: 2016 ProfitBricks GmbH. All rights reserved. @license: Apache License 2.0 @contact: juergen.buchhammer@profitbricks.com @deffield updated: Updated ''' import sys import os import traceback from time import sleep from datetime import datetime from argparse import ArgumentParser from argparse import RawDescriptionHelpFormatter from base64 import b64encode, b64decode from profitbricks.client import ProfitBricksService, Server, Volume, NIC __all__ = [] __version__ = 0.1 __date__ = '2016-02-16' __updated__ = '2016-02-16' class CLIError(Exception): '''Generic exception to raise and log different fatal errors.''' def __init__(self, msg): super(CLIError).__init__(type(self)) self.msg = "E: %s" % msg def __str__(self): return self.msg def __unicode__(self): return self.msg # end class CLIError def getLogin(filename, user, passwd): ''' write user/passwd to login file or get them from file. This method is not Py3 safe (byte vs. str) ''' if filename is None: return (user, passwd) if os.path.exists(filename): print("Using file {} for Login".format(filename)) with open(filename, "r") as loginfile: encoded_cred = loginfile.read() decoded_cred = b64decode(encoded_cred) login = decoded_cred.split(':', 1) return (login[0], login[1]) else: if user is None or passwd is None: raise ValueError("user and password must not be None") print("Writing file {} for Login".format(filename)) with open(filename, "w") as loginfile: encoded_cred = b64encode(user+":"+passwd) loginfile.write(encoded_cred) return (user, passwd) # end getLogin() def wait_for_request(pbclient, request_id, timeout=0, initial_wait=5, scaleup=10): ''' Waits for a request to finish until timeout. timeout==0 is interpreted as infinite wait time. Returns a tuple (return code, request status, message) where return code 0 : request successful 1 : request failed -1 : timeout exceeded The wait_period is increased every scaleup steps to adjust for long running requests. ''' total_wait = 0 wait_period = initial_wait next_scaleup = scaleup * wait_period wait = True while wait: request_status = pbclient.get_request(request_id, status=True) state = request_status['metadata']['status'] if state == "DONE": return(0, state, request_status['metadata']['message']) if state == 'FAILED': return(1, state, request_status['metadata']['message']) if verbose > 0: print("Request '{}' is in state '{}'. Sleeping for {} seconds..." .format(request_id, state, wait_period)) sleep(wait_period) total_wait += wait_period if timeout != 0 and total_wait > timeout: wait = False next_scaleup -= wait_period if next_scaleup == 0: wait_period += initial_wait next_scaleup = scaleup * wait_period if verbose > 0: print("scaling up wait_period to {}, next change in {} seconds" .format(wait_period, next_scaleup)) # end while(wait) return(-1, state, "request not finished before timeout") # end wait_for_request() def main(argv=None): '''Parse command line options and create a server/volume composite.''' if argv is None: argv = sys.argv else: sys.argv.extend(argv) program_name = os.path.basename(sys.argv[0]) program_version = "v%s" % __version__ program_build_date = str(__updated__) program_version_message = '%%(prog)s %s (%s)' % (program_version, program_build_date) program_shortdesc = __import__('__main__').__doc__.split("\n")[1] program_license = '''%s Created by J. Buchhammer on %s. Copyright 2016 ProfitBricks GmbH. All rights reserved. Licensed under the Apache License 2.0 http://www.apache.org/licenses/LICENSE-2.0 Distributed on an "AS IS" basis without warranties or conditions of any kind, either express or implied. USAGE ''' % (program_shortdesc, str(__date__)) try: # Setup argument parser parser = ArgumentParser(description=program_license, formatter_class=RawDescriptionHelpFormatter) parser.add_argument('-u', '--user', dest='user', help='the login name') parser.add_argument('-p', '--password', dest='password', help='the login password') parser.add_argument('-L', '--Login', dest='loginfile', default=None, help='the login file to use') parser.add_argument('-d', '--datacenterid', dest='datacenterid', required=True, default=None, help='datacenter of the new server') parser.add_argument('-l', '--lanid', dest='lanid', required=True, default=None, help='LAN of the new server') parser.add_argument('-n', '--name', dest='servername', default="SRV_"+datetime.now().isoformat(), help='name of the new server') parser.add_argument('-c', '--cores', dest='cores', type=int, default=2, help='CPU cores') parser.add_argument('-r', '--ram', dest='ram', type=int, default=4, help='RAM in GB') parser.add_argument('-s', '--storage', dest='storage', type=int, default=4, help='storage in GB') parser.add_argument('-b', '--boot', dest='bootdevice', default="HDD", help='boot device') parser.add_argument('-i', '--imageid', dest='imageid', default=None, help='installation image') parser.add_argument('-P', '--imagepassword', dest='imgpassword', default=None, help='the image password') parser.add_argument('-v', '--verbose', dest="verbose", action="count", help="set verbosity level [default: %(default)s]") parser.add_argument('-V', '--version', action='version', version=program_version_message) # Process arguments args = parser.parse_args() global verbose verbose = args.verbose dc_id = args.datacenterid lan_id = args.lanid servername = args.servername if verbose > 0: print("Verbose mode on") print("start {} with args {}".format(program_name, str(args))) # Test images (location de/fra) # CDROM: 7fc885b3-c9a6-11e5-aa10-52540005ab80 # debian-8.3.0-amd64-netinst.iso # HDD: 28007a6d-c88a-11e5-aa10-52540005ab80 # CentOS-7-server-2016-02-01 hdimage = args.imageid cdimage = None if args.bootdevice == "CDROM": hdimage = None cdimage = args.imageid print("using boot device {} with image {}" .format(args.bootdevice, args.imageid)) (user, password) = getLogin(args.loginfile, args.user, args.password) if user is None or password is None: raise ValueError("user or password resolved to None") pbclient = ProfitBricksService(user, password) first_nic = NIC(name="local", ips=[], dhcp=True, lan=lan_id) volume = Volume(name=servername+"-Disk", size=args.storage, image=hdimage, image_password=args.imgpassword) server = Server(name=servername, cores=args.cores, ram=args.ram*1024, create_volumes=[volume], nics=[first_nic], boot_cdrom=cdimage) print("creating server..") if verbose > 0: print("SERVER: {}".format(str(server))) response = pbclient.create_server(dc_id, server) print("wait for provisioning..") wait_for_request(pbclient, response["requestId"]) server_id = response['id'] print("Server provisioned with ID {}".format(server_id)) nics = pbclient.list_nics(dc_id, server_id, 1) # server should have exactly one nic, but we only test empty nic list if len(nics['items']) == 0: raise CLIError("No NICs found for newly created server {}" .format(server_id)) nic0 = nics['items'][0] if verbose > 0: print("NIC0: {}".format(str(nic0))) (nic_id, nic_mac) = (nic0['id'], nic0['properties']['mac']) print("NIC of new Server has ID {} and MAC {}".format(nic_id, nic_mac)) print("{} finished w/o errors".format(program_name)) return 0 except KeyboardInterrupt: # handle keyboard interrupt # return 0 except Exception: traceback.print_exc() sys.stderr.write("\n" + program_name + ": for help use --help\n") return 2 # end main() if __name__ == "__main__": sys.exit(main()) profitbricks-sdk-python-4.1.3/examples/pb_controlServerState.py000066400000000000000000000327451326076220500250550ustar00rootroot00000000000000#!/usr/bin/python # encoding: utf-8 # Copyright 2016-2017 ProfitBricks GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ''' pb_controlServerState -- control a server's state (start/stop) pb_controlServerState is a tool to control the state of a server/VM by stop/start actions @author: Jürgen Buchhammer @copyright: 2016 ProfitBricks GmbH. All rights reserved. @license: Apache License 2.0 @contact: juergen.buchhammer@profitbricks.com @deffield updated: Updated ''' import sys import os import traceback import time from argparse import ArgumentParser from argparse import RawDescriptionHelpFormatter from base64 import b64encode, b64decode from subprocess import call from profitbricks.client import ProfitBricksService __all__ = [] __version__ = 0.1 __date__ = '2016-02-25' __updated__ = '2016-02-25' class CLIError(Exception): '''Generic exception to raise and log different fatal errors.''' def __init__(self, msg): super(CLIError).__init__(type(self)) self.msg = "E: %s" % msg def __str__(self): return self.msg def __unicode__(self): return self.msg # end class CLIError def getLogin(filename, user, passwd): ''' write user/passwd to login file or get them from file. This method is not Py3 safe (byte vs. str) ''' if filename is None: return (user, passwd) if os.path.exists(filename): print("Using file {} for Login".format(filename)) with open(filename, "r") as loginfile: encoded_cred = loginfile.read() # print("encoded: {}".format(encoded_cred)) decoded_cred = b64decode(encoded_cred) login = decoded_cred.split(':', 1) return (login[0], login[1]) else: if user is None or passwd is None: raise ValueError("user and password must not be None") print("Writing file {} for Login".format(filename)) with open(filename, "w") as loginfile: encoded_cred = b64encode(user+":"+passwd) # print("encoded: {}".format(encoded_cred)) loginfile.write(encoded_cred) return (user, passwd) # end getLogin() def getServerInfo(pbclient=None, dc_id=None): ''' gets info of servers of a data center''' if pbclient is None: raise ValueError("argument 'pbclient' must not be None") if dc_id is None: raise ValueError("argument 'dc_id' must not be None") # list of all found server's info server_info = [] # depth 1 is enough for props/meta servers = pbclient.list_servers(dc_id, 1) for server in servers['items']: props = server['properties'] info = dict(id=server['id'], name=props['name'], state=server['metadata']['state'], vmstate=props['vmState']) server_info.append(info) # end for(servers) return(server_info) # end getServerInfo() def select_where(info=None, select=None, **where): if info is None: raise ValueError("argument 'info' must not be None") if len(info) == 0: return [] if select is None: select = info[0].keys() server_info = [] for old_si in info: w_matches = all(old_si[wk] == wv for (wk, wv) in where.items()) new_si = {k: v for (k, v) in old_si.items() if k in select and w_matches} if len(new_si) > 0: server_info.append(new_si) # end for(info) return(server_info) # end select_where() def getServerStates(pbclient=None, dc_id=None, serverid=None, servername=None): ''' gets states of a server''' if pbclient is None: raise ValueError("argument 'pbclient' must not be None") if dc_id is None: raise ValueError("argument 'dc_id' must not be None") server = None if serverid is None: if servername is None: raise ValueError("one of 'serverid' or 'servername' must be specified") # so, arg.servername is set (to whatever) server_info = select_where(getServerInfo(pbclient, dc_id), ['id', 'name', 'state', 'vmstate'], name=servername) if len(server_info) > 1: raise NameError("ambiguous server name '{}'".format(servername)) if len(server_info) == 1: server = server_info[0] else: # get by ID may also fail if it's removed # in this case, catch exception (message 404) and be quiet for a while # unfortunately this has changed from Py2 to Py3 try: server_info = pbclient.get_server(dc_id, serverid, 1) server = dict(id=server_info['id'], name=server_info['properties']['name'], state=server_info['metadata']['state'], vmstate=server_info['properties']['vmState']) except Exception: ex = sys.exc_info()[1] if ex.args[0] is not None and ex.args[0] == 404: print("Server w/ ID {} not found".format(serverid)) server = None else: raise ex # end try/except # end if/else(serverid) return server # end getServerStates() def wait_for_server(pbclient=None, dc_id=None, serverid=None, indicator='state', state='AVAILABLE', timeout=300): ''' wait for a server/VM to reach a defined state for a specified time indicator := {state|vmstate} specifies if server or VM stat is tested state specifies the status the indicator should have ''' if pbclient is None: raise ValueError("argument 'pbclient' must not be None") if dc_id is None: raise ValueError("argument 'dc_id' must not be None") if serverid is None: raise ValueError("argument 'serverid' must not be None") total_sleep_time = 0 seconds = 5 while total_sleep_time < timeout: time.sleep(seconds) total_sleep_time += seconds if total_sleep_time == 60: # Increase polling interval after one minute seconds = 10 elif total_sleep_time == 600: # Increase polling interval after 10 minutes seconds = 20 server = getServerStates(pbclient, dc_id, serverid) if server[indicator] == state: break # end while(total_sleep_time) return server # end wait_for_server() def main(argv=None): '''Command line options.''' if argv is None: argv = sys.argv else: sys.argv.extend(argv) program_name = os.path.basename(sys.argv[0]) program_version = "v%s" % __version__ program_build_date = str(__updated__) program_version_message = '%%(prog)s %s (%s)' % (program_version, program_build_date) program_shortdesc = __import__('__main__').__doc__.split("\n")[1] program_license = '''%s Created by J. Buchhammer on %s. Copyright 2016 ProfitBricks GmbH. All rights reserved. Licensed under the Apache License 2.0 http://www.apache.org/licenses/LICENSE-2.0 Distributed on an "AS IS" basis without warranties or conditions of any kind, either express or implied. USAGE ''' % (program_shortdesc, str(__date__)) try: # Setup argument parser parser = ArgumentParser(description=program_license, formatter_class=RawDescriptionHelpFormatter) parser.add_argument('-u', '--user', dest='user', help='the login name') parser.add_argument('-p', '--password', dest='password', help='the login password') parser.add_argument('-L', '--Login', dest='loginfile', default=None, help='the login file to use') parser.add_argument('-d', '--datacenterid', dest='dc_id', required=True, default=None, help='datacenter of the server') parser.add_argument('-s', '--serverid', dest='serverid', default=None, help='ID of the server') parser.add_argument('-n', '--name', dest='servername', default=None, help='name of the server') parser.add_argument('-a', '--action', dest='action', default=None, required=True, help='what to do with the server') parser.add_argument('-C', '--command', dest='command', default=None, help='remote shell command to use for shutdown') parser.add_argument('-v', '--verbose', dest="verbose", action="count", help="set verbosity level [default: %(default)s]") parser.add_argument('-V', '--version', action='version', version=program_version_message) # Process arguments args = parser.parse_args() global verbose verbose = args.verbose dc_id = args.dc_id if verbose > 0: print("Verbose mode on") # normalize action action = args.action.upper() actions = set(['POWERON', 'POWEROFF', 'START', 'SHUTOFF']) if action not in actions: parser.error("action must be on of {}".format(str(actions))) if args.serverid is None and args.servername is None: parser.error("one of 'serverid' or 'name' must be specified") (user, password) = getLogin(args.loginfile, args.user, args.password) if user is None or password is None: raise ValueError("user or password resolved to None") pbclient = ProfitBricksService(user, password) server = getServerStates(pbclient, dc_id, args.serverid, args.servername) if server is None: raise Exception(1, "specified server not found") print("using server {}(id={}) in state {}, {}" .format(server['name'], server['id'], server['state'], server['vmstate'])) # !!! stop/start/reboot_server() simply return 'True' !!! # this implies, that there's NO response nor requestId to track! if action == 'POWEROFF': if server['state'] == 'INACTIVE': print("server is already powered off") else: # currently use 'forced' poweroff if server['vmstate'] != 'SHUTOFF': print("VM is in state {}, {} may lead to inconsistent state" .format(server['vmstate'], action)) if args.command is None: print("no command specified for shutdown of VM") else: print("executing {}".format(args.command)) cmdrc = call(args.command, shell=True) print("executing {} returned {}" .format(args.command, cmdrc)) pbclient.stop_server(dc_id, server['id']) server = wait_for_server(pbclient, dc_id, server['id'], state='INACTIVE', timeout=300) elif action == 'POWERON': if server['vmstate'] == 'RUNNING': print("VM is already up and running") else: pbclient.start_server(dc_id, server['id']) server = wait_for_server(pbclient, dc_id, server['id'], indicator='vmstate', state='RUNNING', timeout=300) elif action == 'START': # this is the same as POWERON if server['vmstate'] == 'RUNNING': print("VM is already up and running") else: pbclient.start_server(dc_id, server['id']) server = wait_for_server(pbclient, dc_id, server['id'], indicator='vmstate', state='RUNNING', timeout=300) elif action == 'SHUTOFF': if server['vmstate'] == 'SHUTOFF': print("VM is already shut off") else: if args.command is None: print("no command specified for shutdown of VM") else: print("executing {}".format(args.command)) cmdrc = call(args.command, shell=True) print("executing {} returned {}" .format(args.command, cmdrc)) server = wait_for_server(pbclient, dc_id, server['id'], indicator='vmstate', state='SHUTOFF', timeout=300) # end if/else(action) print("server {}(id={}) now in state {}, {}" .format(server['name'], server['id'], server['state'], server['vmstate'])) except KeyboardInterrupt: # handle keyboard interrupt # return 0 except Exception: traceback.print_exc() sys.stderr.write("\n" + program_name + ": for help use --help\n") return 2 # end main() if __name__ == "__main__": sys.exit(main()) profitbricks-sdk-python-4.1.3/examples/pb_createDatacenter.py000066400000000000000000000571341326076220500244420ustar00rootroot00000000000000#!/usr/bin/python # encoding: utf-8 # Copyright 2016-2017 ProfitBricks GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ''' pb_createDatacenter is a sample script to create a complex datacenter. @author: Jürgen Buchhammer @copyright: 2016 ProfitBricks GmbH. All rights reserved. @license: Apache License 2.0 @contact: juergen.buchhammer@profitbricks.com @deffield updated: Updated ''' import sys import os from argparse import ArgumentParser from argparse import RawDescriptionHelpFormatter from time import sleep import json from base64 import b64decode, b64encode from profitbricks.client import ProfitBricksService from profitbricks.client import Datacenter, Volume, Server from profitbricks.client import LAN, NIC, FirewallRule __version__ = 0.2 __date__ = '2016-09-26' __updated__ = __date__ def getLogin(filename, user, passwd): ''' write user/passwd to login file or get them from file. This method is not Py3 safe (byte vs. str) ''' if filename is None: return (user, passwd) isPy2 = sys.version_info[0] == 2 if os.path.exists(filename): print("Using file {} for Login".format(filename)) with open(filename, "r") as loginfile: encoded_cred = loginfile.read() print("encoded: {}".format(encoded_cred)) if isPy2: decoded_cred = b64decode(encoded_cred) else: decoded_cred = b64decode(encoded_cred).decode('utf-8') login = decoded_cred.split(':', 1) return (login[0], login[1]) else: if user is None or passwd is None: raise ValueError("user and password must not be None") print("Writing file {} for Login".format(filename)) with open(filename, "wb") as loginfile: creds = user+":"+passwd if isPy2: encoded_cred = b64encode(creds) else: encoded_cred = b64encode(creds.encode('utf-8')) print("encoded: {}".format(encoded_cred)) loginfile.write(encoded_cred) return (user, passwd) # end getLogin() def wait_for_request(pbclient, request_id, timeout=0, initial_wait=5, scaleup=10): ''' Waits for a request to finish until timeout. timeout==0 is interpreted as infinite wait time. Returns a tuple (return code, request status, message) where return code 0 : request successful 1 : request failed -1 : timeout exceeded The wait_period is increased every scaleup steps to adjust for long running requests. ''' total_wait = 0 wait_period = initial_wait next_scaleup = scaleup * wait_period wait = True while wait: request_status = pbclient.get_request(request_id, status=True) state = request_status['metadata']['status'] if state == "DONE": return(0, state, request_status['metadata']['message']) if state == 'FAILED': return(1, state, request_status['metadata']['message']) print("Request '{}' is in state '{}'. Sleeping for {} seconds..." .format(request_id, state, wait_period)) sleep(wait_period) total_wait += wait_period if timeout != 0 and total_wait > timeout: wait = False next_scaleup -= wait_period if next_scaleup == 0: wait_period += initial_wait next_scaleup = scaleup * wait_period print("scaling up wait_period to {}, next change in {} seconds" .format(wait_period, next_scaleup)) # end while(wait) return(-1, state, "request not finished before timeout") # end wait_for_request() def wait_for_requests(pbclient, request_ids=[], timeout=0, initial_wait=5, scaleup=10): ''' Waits for a list of requests to finish until timeout. timeout==0 is interpreted as infinite wait time. Returns a dict of request_id -> result. result is a tuple (return code, request status, message) where return code 0 : request successful 1 : request failed -1 : timeout exceeded The wait_period is increased every scaleup steps to adjust for long running requests. ''' done = dict() if len(request_ids) == 0: print("empty request list") return done total_wait = 0 wait_period = initial_wait next_scaleup = scaleup * wait_period wait = True while wait: for request_id in request_ids: if request_id in done: continue request_status = pbclient.get_request(request_id, status=True) state = request_status['metadata']['status'] if state == "DONE": done[request_id] = (0, state, request_status['metadata']['message']) print("Request '{}' is in state '{}'.".format(request_id, state)) if state == 'FAILED': done[request_id] = (1, state, request_status['metadata']['message']) print("Request '{}' is in state '{}'.".format(request_id, state)) # end for(request_ids) if len(done) == len(request_ids): wait = False else: print("{} of {} requests are finished. Sleeping for {} seconds..." .format(len(done), len(request_ids), wait_period)) sleep(wait_period) total_wait += wait_period if timeout != 0 and total_wait > timeout: wait = False next_scaleup -= wait_period if next_scaleup == 0: wait_period += initial_wait next_scaleup = scaleup * wait_period print("scaling up wait_period to {}, next change in {} seconds" .format(wait_period, next_scaleup)) # end if/else(done) # end while(wait) if len(done) != len(request_ids): for request_id in request_ids: if request_id in done: continue done[request_id] = (-1, state, "request not finished before timeout") return done # end wait_for_requests() def create_datacenter_dict(pbclient, datacenter): """ Creates a Datacenter dict -- both simple and complex are supported. This is copied from createDatacenter() and uses private methods. """ server_items = [] volume_items = [] lan_items = [] loadbalancer_items = [] entities = dict() properties = { "name": datacenter.name, "location": datacenter.location, } ' Optional Properties' if datacenter.description: properties['description'] = datacenter.description ' Servers ' if len(datacenter.servers) > 0: for server in datacenter.servers: server_items.append(pbclient._create_server_dict(server)) servers = { "items": server_items } server_entities = { "servers": servers } entities.update(server_entities) ' Volumes ' if len(datacenter.volumes) > 0: for volume in datacenter.volumes: volume_items.append(pbclient._create_volume_dict(volume)) volumes = { "items": volume_items } volume_entities = { "volumes": volumes } entities.update(volume_entities) ' Load Balancers ' if len(datacenter.loadbalancers) > 0: for loadbalancer in datacenter.loadbalancers: loadbalancer_items.append( pbclient._create_loadbalancer_dict( loadbalancer ) ) loadbalancers = { "items": loadbalancer_items } loadbalancer_entities = { "loadbalancers": loadbalancers } entities.update(loadbalancer_entities) ' LANs ' if len(datacenter.lans) > 0: for lan in datacenter.lans: lan_items.append( pbclient._create_lan_dict(lan) ) lans = { "items": lan_items } lan_entities = { "lans": lans } entities.update(lan_entities) if len(entities) == 0: raw = { "properties": properties, } else: raw = { "properties": properties, "entities": entities } return raw # end create_datacenter_dict() def write_dc_definition(pbclient, dcdef=None, filename=None): with open(filename, 'w') as outfile: json.dump(dcdef, outfile, indent=2) return 0 # end write_dc_definition() def read_dc_definition(pbclient, filename=None): with open(filename) as infile: dcdef = json.load(infile) return dcdef # end read_dc_definition() # -- build objects from internal dict structures -- def getDatacenterObject(defdict=None): if defdict is None or not type(defdict) is dict or len(defdict.keys()) == 0: raise ValueError("argument 'defdict' must be non-empty dict") props = dict() for k, v in defdict['properties'].items(): # no renaming needed here, but this accounts for optional props too props[k] = v apiobj = Datacenter(**props) return(apiobj) # end getDatacenterObject() def getVolumeObject(defdict=None): if defdict is None or not type(defdict) is dict or len(defdict.keys()) == 0: raise ValueError("argument 'defdict' must be non-empty dict") # TODO: can we set deviceNumber too? Nope, not supported in Volume() # AARGH! some of Volume's fields have different names -> need to convert # so make a copy and let source as is props = dict() for k, v in defdict['properties'].items(): if k == 'type': props['disk_type'] = v continue if k == 'imagePassword': props['image_password'] = v continue if k == 'licenceType': props['licence_type'] = v continue if k == 'sshKeys': props['ssh_keys'] = v continue props[k] = v # end for(defdict) apiobj = Volume(**props) return(apiobj) # end getVolumeObject() def getServerObject(defdict=None): if defdict is None or not type(defdict) is dict or len(defdict.keys()) == 0: raise ValueError("argument 'defdict' must be non-empty dict") # AARGH! some of Servers's fields have different names -> need to convert manually # so make a copy and let source as is props = dict() for k, v in defdict['properties'].items(): if k == 'availabilityZone': props['availability_zone'] = v continue if k == 'bootCdrom': props['boot_cdrom'] = v continue if k == 'cpuFamily': props['cpu_family'] = v continue # -- TODO: if volumes entries have an ID -> attach, else create w/ properties of volume # if k == 'bootVolume': # props['boot_volume_id'] = v # continue # if k == 'volumes': # props['create_volumes'] = v # continue # if k == 'volumes': # props['attach_volumes'] = v # continue # Server() has no kwargs, so we must exactly match the known keywords if k in ['name', 'cores', 'ram']: props[k] = v # end for(defdict) apiobj = Server(**props) return(apiobj) # end getServerObject() def getNICObject(defdict=None): if defdict is None or not type(defdict) is dict or len(defdict.keys()) == 0: raise ValueError("argument 'defdict' must be non-empty dict") # AARGH! some of NIC's fields have different names -> need to convert manually # so make a copy and let source as is props = dict() for k, v in defdict['properties'].items(): if k == 'firewallActive': props['firewall_active'] = v continue if k == 'firewallrules': # this needs more to do: # if we have rules we must get rules objects too, irrespective of being active if len(v) != 0: rules = [getFwRuleObject(rule) for rule in v] props['firewall_rules'] = rules continue props[k] = v # end for(defdict) apiobj = NIC(**props) return(apiobj) # end getNICObject() def getFwRuleObject(defdict=None): if defdict is None or not type(defdict) is dict or len(defdict.keys()) == 0: raise ValueError("argument 'defdict' must be non-empty dict") # AARGH! some of NIC's fields have different names -> need to convert manually # so make a copy and let source as is props = dict() for k, v in defdict['properties'].items(): if k == 'sourceMac': props['source_mac'] = v continue if k == 'sourceIp': props['source_ip'] = v continue if k == 'targetIp': props['target_ip'] = v continue if k == 'portRangeStart': props['port_range_start'] = v continue if k == 'portRangeEnd': props['port_range_end'] = v continue if k == 'icmpType': props['icmp_type'] = v continue if k == 'icmpCode': props['icmp_code'] = v continue props[k] = v # end for(defdict) apiobj = FirewallRule(**props) return(apiobj) # end getFwRuleObject() def getLANObject(defdict=None): if defdict is None or not type(defdict) is dict or len(defdict.keys()) == 0: raise ValueError("argument 'defdict' must be non-empty dict") props = dict() for k, v in defdict['properties'].items(): # no renaming needed here, but this accounts for optional props too props[k] = v # end for(defdict) apiobj = LAN(**props) return(apiobj) # end getLANObject() # -- the (far too long) main method -- def main(argv=None): '''Parse command line options and create a server/volume composite.''' if argv is None: argv = sys.argv else: sys.argv.extend(argv) program_name = os.path.basename(sys.argv[0]) program_version = "v%s" % __version__ program_build_date = str(__updated__) program_version_message = '%%(prog)s %s (%s)' % (program_version, program_build_date) program_shortdesc = __import__('__main__').__doc__.split("\n")[1] program_license = '''%s Created by J. Buchhammer on %s. Copyright 2016 ProfitBricks GmbH. All rights reserved. Licensed under the Apache License 2.0 http://www.apache.org/licenses/LICENSE-2.0 Distributed on an "AS IS" basis without warranties or conditions of any kind, either express or implied. USAGE ''' % (program_shortdesc, str(__date__)) # Setup argument parser parser = ArgumentParser(description=program_license, formatter_class=RawDescriptionHelpFormatter) parser.add_argument('-u', '--user', dest='user', help='the login name') parser.add_argument('-p', '--password', dest='password', help='the login password') parser.add_argument('-L', '--Login', dest='loginfile', default=None, help='the login file to use') parser.add_argument('-i', '--infile', dest='infile', default=None, required=True, help='the input file name') parser.add_argument('-D', '--DCname', dest='dcname', default=None, help='new datacenter name') # TODO: add/overwrite image password for creation # parser.add_argument('-P', '--imagepassword', dest='imgpassword', # default=None, help='the image password') parser.add_argument('-v', '--verbose', dest="verbose", action="count", default=0, help="set verbosity level [default: %(default)s]") parser.add_argument('-V', '--version', action='version', version=program_version_message) # Process arguments args = parser.parse_args() global verbose verbose = args.verbose if verbose > 0: print("Verbose mode on") print("start {} with args {}".format(program_name, str(args))) (user, password) = getLogin(args.loginfile, args.user, args.password) if user is None or password is None: raise ValueError("user or password resolved to None") pbclient = ProfitBricksService(user, password) usefile = args.infile print("read dc from {}".format(usefile)) dcdef = read_dc_definition(pbclient, usefile) if verbose > 0: print("using DC-DEF {}".format(json.dumps(dcdef))) # setup dc: # + create empty dc # + create volumes (unattached), map uuid to servers (custom dict) # + create servers # + create nics # + attach volumes if 'custom' in dcdef and 'id' in dcdef['custom']: dc_id = dcdef['custom']['id'] print("using existing DC w/ id {}".format(str(dc_id))) else: if args.dcname is not None: print("Overwrite DC name w/ '{}'".format(args.dcname)) dcdef['properties']['name'] = args.dcname dc = getDatacenterObject(dcdef) # print("create DC {}".format(str(dc))) response = pbclient.create_datacenter(dc) dc_id = response['id'] if 'custom' not in dcdef: dcdef['custom'] = dict() dcdef['custom']['id'] = dc_id result = wait_for_request(pbclient, response['requestId']) print("wait loop returned {}".format(result)) tmpfile = usefile+".tmp_postdc" write_dc_definition(pbclient, dcdef, tmpfile) requests = [] print("create Volumes {}".format(str(dc))) # we do NOT consider dangling volumes, only server-attached ones for server in dcdef['entities']['servers']['items']: print("- server {}".format(server['properties']['name'])) if 'volumes' not in server['entities']: print(" server {} has no volumes".format(server['properties']['name'])) continue for volume in server['entities']['volumes']['items']: if 'custom' in volume and 'id' in volume['custom']: vol_id = volume['custom']['id'] print("using existing volume w/ id {}".format(str(vol_id))) else: dcvol = getVolumeObject(volume) print("OBJ: {}".format(str(dcvol))) response = pbclient.create_volume(dc_id, dcvol) volume.update({'custom': {'id': response['id']}}) requests.append(response['requestId']) # end for(volume) # end for(server) if len(requests) != 0: result = wait_for_requests(pbclient, requests, initial_wait=10, scaleup=15) print("wait loop returned {}".format(str(result))) tmpfile = usefile+".tmp_postvol" write_dc_definition(pbclient, dcdef, tmpfile) else: print("all volumes existed already") requests = [] print("create Servers {}".format(str(dc))) # we do NOT consider dangling volumes, only server-attached ones for server in dcdef['entities']['servers']['items']: print("- server {}".format(server['properties']['name'])) if 'custom' in server and 'id' in server['custom']: srv_id = server['custom']['id'] print("using existing server w/ id {}".format(str(srv_id))) else: dcsrv = getServerObject(server) print("OBJ: {}".format(str(dcsrv))) response = pbclient.create_server(dc_id, dcsrv) server.update({'custom': {'id': response['id']}}) requests.append(response['requestId']) # end for(server) if len(requests) != 0: result = wait_for_requests(pbclient, requests, initial_wait=10, scaleup=15) print("wait loop returned {}".format(str(result))) tmpfile = usefile+".tmp_postsrv" write_dc_definition(pbclient, dcdef, tmpfile) else: print("all servers existed already") # TODO: only do this if we have lan entities requests = [] # Huuh, looks like we get unpredictable order for LANs! # Nope, order of creation determines the LAN id, # thus we better wait for each request print("create LANs {}".format(str(dc))) for lan in dcdef['entities']['lans']['items']: print("- lan {}".format(lan['properties']['name'])) dclan = getLANObject(lan) print("OBJ: {}".format(str(dclan))) response = pbclient.create_lan(dc_id, dclan) lan.update({'custom': {'id': response['id']}}) result = wait_for_request(pbclient, response['requestId']) print("wait loop returned {}".format(str(result))) # end for(lan) tmpfile = usefile+".tmp_postlan" write_dc_definition(pbclient, dcdef, tmpfile) requests = [] # Attention: # NICs appear in OS in the order, they are created. # But DCD rearranges the display by ascending MAC addresses. # This does not change the OS order. # MAC may not be available from create response, # thus we wait for each request :-( print("create NICs {}".format(str(dc))) for server in dcdef['entities']['servers']['items']: print("- server {}".format(server['properties']['name'])) srv_id = server['custom']['id'] if 'nics' not in server['entities']: print(" server {} has no NICs".format(server['properties']['name'])) continue macmap = dict() for nic in server['entities']['nics']['items']: dcnic = getNICObject(nic) response = pbclient.create_nic(dc_id, srv_id, dcnic) # print("dcnic response {}".format(str(response))) # mac = response['properties']['mac'] # we don't get it here !? nic_id = response['id'] result = wait_for_request(pbclient, response['requestId']) print("wait loop returned {}".format(str(result))) response = pbclient.get_nic(dc_id, srv_id, nic_id, 2) mac = response['properties']['mac'] print("dcnic has MAC {} for {}".format(mac, nic_id)) macmap[mac] = nic_id # end for(nic) macs = sorted(macmap) print("macs will be displayed by DCD in th following order:") for mac in macs: print("mac {} -> id{}".format(mac, macmap[mac])) # end for(server) tmpfile = usefile+".tmp_postnic" write_dc_definition(pbclient, dcdef, tmpfile) requests = [] # don't know if we get a race here too, so better wait for each request :-/ print("attach volumes {}".format(str(dc))) for server in dcdef['entities']['servers']['items']: print("- server {}".format(server['properties']['name'])) if 'volumes' not in server['entities']: print(" server {} has no volumes".format(server['properties']['name'])) continue srv_id = server['custom']['id'] for volume in server['entities']['volumes']['items']: print("OBJ: {}".format(volume['properties']['name'])) response = pbclient.attach_volume(dc_id, srv_id, volume['custom']['id']) result = wait_for_request(pbclient, response['requestId']) print("wait loop returned {}".format(str(result))) # end for(volume) # end for(server) tmpfile = usefile+".tmp_postatt" write_dc_definition(pbclient, dcdef, tmpfile) # TODO: do we need to set boot volume for each server? # looks like it's working without return 0 # end main() if __name__ == "__main__": sys.exit(main()) profitbricks-sdk-python-4.1.3/examples/pb_datacenter_inventory.py000077500000000000000000000460671326076220500254410ustar00rootroot00000000000000#!/usr/bin/python # encoding: utf-8 # Copyright 2016-2017 ProfitBricks GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ''' pb_datacenter_inventory -- dump inventory of your Profitbricks data centers to CSV files pb_datacenter_inventory is a sample script to get an inventory overview of your data centers via Profitbricks REST-API. It collects some basic information on - reserved ip blocks: Location,RscType,RscID,State,Size,IP addresses - images and snapshots Visibility(public|private),Location,RscType,SubType(CDROM|HDD),RscID, RscName,State,LicType,Size,Created,Modified - servers DCID,DCName,Location,RscType,RscID,RscName,State,LicType,Cores,RAM, # NICs,# Volumes,(Total) Storage,---,Created,Modified and storage DCID,DCName,Location,RscType,RscID,RscName,State,LicType,---,---,---,---, (Total) Storage,Connected to,Created,Modified - networking infrastructure (LANs/NICs) DCID,DCName,Location,LAN ID,LAN name,public?,State,# NICs,NIC ID, MAC address,DHCP?,IP(s),NIC name,Firewall?,Connected to,ID,Name from all your datacenters. This data is written to a resource specific CSV file which can be used for reports or automatic post processing. @author: Jürgen Buchhammer @copyright: 2016 ProfitBricks GmbH. All rights reserved. @license: license @contact: juergen.buchhammer@profitbricks.com @deffield updated: Updated ''' import sys import os import traceback import re import pprint from argparse import ArgumentParser from argparse import RawDescriptionHelpFormatter from getpass import getpass import csv from profitbricks.client import ProfitBricksService __all__ = [] __version__ = 0.2 __date__ = '2016-01-15' __updated__ = '2016-01-15' verbose = 0 DEBUG = 1 def pp(value): """ Returns a pretty print string of the given value. @return: pretty print string @rtype: str """ pretty_printer = pprint.PrettyPrinter(indent=4) return pretty_printer.pformat(value) class CLIError(Exception): '''Generic exception to raise and log different fatal errors.''' def __init__(self, msg): super(CLIError).__init__(type(self)) self.msg = "E: %s" % msg def __str__(self): return self.msg def __unicode__(self): return self.msg def get_dc_inventory(pbclient, dc=None): ''' gets inventory of one data center''' if pbclient is None: raise ValueError("argument 'pbclient' must not be None") if dc is None: raise ValueError("argument 'dc' must not be None") dc_inv = [] # inventory list to return dcid = dc['id'] # dc_data contains dc specific columns dc_data = [dcid, dc['properties']['name'], dc['properties']['location']] # first get the servers # this will build a hash to relate volumes to servers later # depth 3 is enough to get into volume/nic level plus details servers = pbclient.list_servers(dcid, 3) print("found %i servers in data center %s" % (len(servers['items']), dc['properties']['name'])) if verbose > 2: print(str(servers)) # this will build a hash to relate volumes to servers later bound_vols = dict() # hash volume-to-server relations for server in servers['items']: if verbose > 2: print("SERVER: %s" % str(server)) serverid = server['id'] # server_data contains server specific columns for later output server_data = [ server['type'], serverid, server['properties']['name'], server['metadata']['state'] ] # OS is determined by boot device (volume||cdrom), not a server property. # Might even be unspecified bootOS = "NONE" bootdev = server['properties']['bootVolume'] if bootdev is None: bootdev = server['properties']['bootCdrom'] print("server %s has boot device %s" % (serverid, "CDROM")) if bootdev is None: print("server %s has NO boot device" % (serverid)) else: bootOS = bootdev['properties']['licenceType'] server_data += [bootOS, server['properties']['cores'], server['properties']['ram']] server_vols = server['entities']['volumes']['items'] n_volumes = len(server_vols) total_disk = 0 licence_type = "" for vol in server_vols: total_disk += vol['properties']['size'] licence_type = str(vol['properties']['licenceType']) bound_vols[vol['id']] = serverid if verbose: print("volume %s is connected to %s w/ OS %s" % ( vol['id'], bound_vols[vol['id']], licence_type)) server_nics = server['entities']['nics']['items'] n_nics = len(server_nics) server_data += [ n_nics, n_volumes, total_disk, "", server['metadata']['createdDate'], server['metadata']['lastModifiedDate'] ] dc_inv.append(dc_data + server_data) # end for(servers) # and now the volumes... volumes = pbclient.list_volumes(dcid, 2) # depth 2 gives max. details for volume in volumes['items']: if verbose > 2: print("VOLUME: %s" % str(volume)) volid = volume['id'] vol_data = [ volume['type'], volid, volume['properties']['name'], volume['metadata']['state'], volume['properties']['licenceType'], "", "", "", "", volume['properties']['size'] ] connect = 'NONE' if volid in bound_vols: connect = bound_vols[volid] vol_data += [ connect, volume['metadata']['createdDate'], volume['metadata']['lastModifiedDate'] ] dc_inv.append(dc_data + vol_data) # end for(volumes) return dc_inv # end get_dc_inventory() def get_images(pbclient): if pbclient is None: raise ValueError("argument 'pbclient' must not be None") print("getting images..") images = pbclient.list_images() print("found %i images" % len(images['items'])) img_inv = [] for image in images['items']: if verbose > 2: print("IMAGE: %s" % str(image)) img_data = [ ('public' if image['properties']['public'] else 'private'), image['properties']['location'], image['type'], image['properties']['imageType'], image['id'], image['properties']['name'], image['metadata']['state'], image['properties']['licenceType'], image['properties']['size'], image['metadata']['createdDate'], image['metadata']['lastModifiedDate'] ] img_inv.append(img_data) return img_inv # end get_images() def get_snapshots(pbclient): if pbclient is None: raise ValueError("argument 'pbclient' must not be None") print("getting snapshots..") snapshots = pbclient.list_snapshots() print("found %i snapshots" % len(snapshots['items'])) snap_inv = [] for snap in snapshots['items']: if verbose > 2: print("SNAPSHOT: %s" % str(snap)) snap_data = [ "private", snap['properties']['location'], snap['type'], "HDD", snap['id'], snap['properties']['name'], snap['metadata']['state'], snap['properties']['licenceType'], snap['properties']['size'], snap['metadata']['createdDate'], snap['metadata']['lastModifiedDate'] ] # print("SNAPSHOT: %s" % str(snap_data)) snap_inv.append(snap_data) # end for(snapshots) return snap_inv # end get_snapshots() def get_ipblocks(pbclient): if pbclient is None: raise ValueError("argument 'pbclient' must not be None") print("getting IP blocks..") ipblocks = pbclient.list_ipblocks() print("found %i IP blocks" % len(ipblocks['items'])) ip_inv = [] if verbose > 1: print(str(ipblocks)) for block in ipblocks['items']: ip_data = [ block['properties']['location'], block['type'], block['id'], block['metadata']['state'], block['properties']['size'] ] ip_data.extend(block['properties']['ips']) # print str(ip_data) ip_inv.append(ip_data) # end for(ipblocks) return ip_inv # end get_ipblocks() def get_dc_network(pbclient, dc=None): ''' gets inventory of one data center''' if pbclient is None: raise ValueError("argument 'pbclient' must not be None") if dc is None: raise ValueError("argument 'dc' must not be None") print("getting networks..") dcid = dc['id'] # dc_data contains dc specific columns dc_data = [dcid, dc['properties']['name'], dc['properties']['location']] lbs = pbclient.list_loadbalancers(dcid, 2) # build lookup hash for loadbalancer's ID->name lbnames = dict([(lb['id'], lb['properties']['name']) for lb in lbs['items']]) if verbose > 2: print("LBs: %s" % (str(lbs))) lans = pbclient.list_lans(dcid, 3) lan_inv = [] # lookup hash for server's ID->name servernames = dict() for lan in lans['items']: if verbose > 1: print("LAN: %s" % str(lan)) lan_data = dc_data + [ "LAN "+lan['id'], lan['properties']['name'], lan['properties']['public'], lan['metadata']['state'] ] nics = lan['entities']['nics']['items'] lan_data.append(len(nics)) if len(nics) > 0: for nic in nics: nic_props = nic['properties'] # get the serverid of this nic by href # !!! HUUUUH this might also be a loadbalancer ID, # although it's '/servers//...' !!! serverid = re.sub(r'^.*servers/([^/]+)/nics.*', r'\1', nic['href']) if serverid in lbnames: servertype = "LB" servername = lbnames[serverid] print("server entry for %s is LOADBALANCER %s" % (serverid, servername)) else: servertype = "Server" if serverid not in servernames: if verbose: print("add server entry for %s" % serverid) server = pbclient.get_server(dcid, serverid, 0) servernames[serverid] = server['properties']['name'] servername = servernames[serverid] # end if/else(serverid) ips = [str(ip) for ip in nic_props['ips']] nic_data = [ nic['id'], nic_props['mac'], nic_props['dhcp'], ips, nic_props['name'], nic_props['firewallActive'], servertype, serverid, servername ] lan_inv.append(lan_data+nic_data) # end for(nics) else: lan_inv.append(lan_data) # end for(lans) return lan_inv # end get_networks() def get_requests(pbclient): if pbclient is None: raise ValueError("argument 'pbclient' must not be None") print("getting requests..") requests = pbclient.list_requests() # higher depth gives no more details (API-doc BUG) print("found %i requests" % len(requests['items'])) if verbose: print("global: %s" % str(requests)) for req in requests['items']: reqid = req['id'] # False: get request (w/ body), True: get request status (DONE,..) req_data = pbclient.get_request(reqid) # False: get request (w/ body), True: get request status (DONE,..) req_stat = pbclient.get_request(reqid, True) if verbose > 2: print("REQ: %s\n %s" % (str(req_data), str(req_stat))) if verbose > 1: print( "request %s|%s|%s" % ( str(reqid), str(req_data['metadata']), str(req_stat['metadata']) ) ) # end for(requests) # end get_requests() def main(argv=None): # IGNORE:C0111 '''Command line options.''' if argv is None: argv = sys.argv else: sys.argv.extend(argv) program_name = os.path.basename(sys.argv[0]) program_version = "v%s" % __version__ program_build_date = str(__updated__) program_version_message = '%%(prog)s %s (%s)' % (program_version, program_build_date) program_shortdesc = __import__('__main__').__doc__.split("\n")[1] program_license = '''%s Created by J.Buchhammer on %s. Copyright 2016 ProfitBricks GmbH. All rights reserved. Licensed under the Apache License 2.0 http://www.apache.org/licenses/LICENSE-2.0 Distributed on an "AS IS" basis without warranties or conditions of any kind, either express or implied. USAGE ''' % (program_shortdesc, str(__date__)) try: # Setup argument parser parser = ArgumentParser( description=program_license, formatter_class=RawDescriptionHelpFormatter) parser.add_argument( '-u', '--user', dest='user', required=True, help='the login name') parser.add_argument( '-p', '--password', dest='password', help='the login password') parser.add_argument( '-d', '--datacenter', '--datacenterid', dest='datacenterid', nargs='?', const='*', help='show server/storage of datacenter(s)') parser.add_argument( '-i', '--image', dest='show_images', action="store_true", help='show images and snapshots') parser.add_argument( '-b', '--ipblock', dest='show_ipblocks', action="store_true", help='show reserved IP blocks') parser.add_argument( '-n', '--network', dest='show_networks', action="store_true", help='show network assignments') # parser.add_argument( # '-r', '--request', dest='show_requests', action="store_true", # help='show requests') parser.add_argument( "-v", "--verbose", dest="verbose", action="count", default=0, help="set verbosity level [default: %(default)s]") parser.add_argument( '-V', '--version', action='version', version=program_version_message) # Process arguments args = parser.parse_args() global verbose verbose = args.verbose # this is a global to be used in methods user = args.user password = args.password datacenterid = args.datacenterid print("Welcome to PB-API %s\n" % user) if password is None: password = getpass() if verbose > 0: print("Verbose mode on") print("using python ", sys.version_info) pbclient = ProfitBricksService(user, password) if datacenterid is not None: datacenters = {} if datacenterid == '*': # the default depth=1 is sufficient, higher values don't provide more details datacenters = pbclient.list_datacenters() else: datacenters['items'] = [] datacenters['items'] = [pbclient.get_datacenter(datacenterid, 1)] if verbose > 1: print(pp(datacenters)) print("retrieved %i datacenters " % len(datacenters['items'])) # dump inventory to file with open("pb_datacenter_inventory.csv", 'w') as csvfile: csvwriter = csv.writer(csvfile, delimiter=';', lineterminator='\n') csvwriter.writerow([ 'DCID', 'DCName', 'Loc', 'RscType', 'RscID', 'RscName', 'State', 'LicType', 'Cores', 'RAM', '# NICs', '# Volumes', '(Total) Storage', 'Connected to', 'Created', 'Modified' ]) for dc in datacenters['items']: try: dc_inv = get_dc_inventory(pbclient, dc) if verbose: print("DC %s has %i inventory entries" % (dc['id'], len(dc_inv))) for row in dc_inv: csvwriter.writerow(row) except Exception: traceback.print_exc() exit(2) # end for(datacenters) if args.show_images: with open("pb_datacenter_images.csv", 'w') as csvfile: csvwriter = csv.writer(csvfile, delimiter=';', lineterminator='\n') csvwriter.writerow([ 'Visibility', 'Loc', 'RscType', 'SubType', 'RscID', 'RscName', 'State', 'LicType', 'Size', 'Created', 'Modified' ]) img_inv = get_images(pbclient) for row in img_inv: csvwriter.writerow(row) snap_inv = get_snapshots(pbclient) for row in snap_inv: csvwriter.writerow(row) if args.show_ipblocks: with open("pb_datacenter_ipblocks.csv", 'w') as csvfile: csvwriter = csv.writer(csvfile, delimiter=';', lineterminator='\n') csvwriter.writerow([ 'Loc', 'RscType', 'RscID', 'State', 'Size', 'IP addresses']) ipblocks = get_ipblocks(pbclient) for row in ipblocks: csvwriter.writerow(row) # file is automatically closed after with block if args.show_networks: # the default depth=1 is sufficient, higher values don't provide more details datacenters = pbclient.list_datacenters() print("retrieved %i datacenters " % len(datacenters['items'])) with open("pb_datacenter_networks.csv", 'w') as csvfile: csvwriter = csv.writer(csvfile, delimiter=';', lineterminator='\n') csvwriter.writerow([ 'DCID', 'DCName', 'Loc', 'LAN ID', 'LAN name', 'public', 'State', '# NICs', 'NIC ID', 'MAC address', 'DHCP', 'IP(s)', 'NIC name', 'Firewall', 'Connected to', 'ID', 'Name']) for dc in datacenters['items']: try: dc_net = get_dc_network(pbclient, dc) if verbose: print("DC %s has %i network entries" % (dc['id'], len(dc_net))) for row in dc_net: csvwriter.writerow(row) except Exception: traceback.print_exc() exit(2) # end for(datacenters) # just for fun: # if args.show_requests: # get_requests(pbclient) print("%s finished w/o errors" % program_name) return 0 except KeyboardInterrupt: # handle keyboard interrupt return 0 except Exception: traceback.print_exc() sys.stderr.write("\n" + program_name + ": for help use --help\n") return 2 if __name__ == "__main__": sys.exit(main()) profitbricks-sdk-python-4.1.3/examples/pb_deleteServer.py000066400000000000000000000300251326076220500236230ustar00rootroot00000000000000#!/usr/bin/python # encoding: utf-8 # Copyright 2016-2017 ProfitBricks GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ''' pb_deleteServer -- remove a server completely pb_deleteServer is a tool to completely remove a server and attached volumes. @author: Jürgen Buchhammer @copyright: 2016 ProfitBricks GmbH. All rights reserved. @license: Apache License 2.0 @contact: juergen.buchhammer@profitbricks.com @deffield updated: Updated ''' import sys import os import traceback import time from argparse import ArgumentParser from argparse import RawDescriptionHelpFormatter from base64 import b64encode, b64decode from subprocess import call from profitbricks.client import ProfitBricksService __all__ = [] __version__ = 0.1 __date__ = '2016-02-25' __updated__ = '2016-02-25' class CLIError(Exception): '''Generic exception to raise and log different fatal errors.''' def __init__(self, msg): super(CLIError).__init__(type(self)) self.msg = "E: %s" % msg def __str__(self): return self.msg def __unicode__(self): return self.msg # end class CLIError def getLogin(filename, user, passwd): ''' write user/passwd to login file or get them from file. This method is not Py3 safe (byte vs. str) ''' if filename is None: return (user, passwd) if os.path.exists(filename): print("Using file {} for Login".format(filename)) with open(filename, "r") as loginfile: encoded_cred = loginfile.read() # print("encoded: {}".format(encoded_cred)) decoded_cred = b64decode(encoded_cred) login = decoded_cred.split(':', 1) return (login[0], login[1]) else: if user is None or passwd is None: raise ValueError("user and password must not be None") print("Writing file {} for Login".format(filename)) with open(filename, "w") as loginfile: encoded_cred = b64encode(user+":"+passwd) # print("encoded: {}".format(encoded_cred)) loginfile.write(encoded_cred) return (user, passwd) # end getLogin() def getServerInfo(pbclient=None, dc_id=None): ''' gets info of servers of a data center''' if pbclient is None: raise ValueError("argument 'pbclient' must not be None") if dc_id is None: raise ValueError("argument 'dc_id' must not be None") # list of all found server's info server_info = [] # depth 1 is enough for props/meta servers = pbclient.list_servers(dc_id, 1) for server in servers['items']: props = server['properties'] info = dict(id=server['id'], name=props['name'], state=server['metadata']['state'], vmstate=props['vmState']) server_info.append(info) # end for(servers) return(server_info) # end getServerInfo() def select_where(info=None, select=None, **where): if info is None: raise ValueError("argument 'info' must not be None") if len(info) == 0: return [] if select is None: select = info[0].keys() server_info = [] for old_si in info: w_matches = all(old_si[wk] == wv for (wk, wv) in where.items()) new_si = {k: v for (k, v) in old_si.items() if k in select and w_matches} if len(new_si) > 0: server_info.append(new_si) # end for(info) return(server_info) # end select_where() def getServerStates(pbclient=None, dc_id=None, serverid=None, servername=None): ''' gets states of a server''' if pbclient is None: raise ValueError("argument 'pbclient' must not be None") if dc_id is None: raise ValueError("argument 'dc_id' must not be None") server = None if serverid is None: if servername is None: raise ValueError("one of 'serverid' or 'servername' must be specified") # so, arg.servername is set (to whatever) server_info = select_where(getServerInfo(pbclient, dc_id), ['id', 'name', 'state', 'vmstate'], name=servername) if len(server_info) > 1: raise NameError("ambiguous server name '{}'".format(servername)) if len(server_info) == 1: server = server_info[0] else: # get by ID may also fail if it's removed # in this case, catch exception (message 404) and be quiet for a while # unfortunately this has changed from Py2 to Py3 try: server_info = pbclient.get_server(dc_id, serverid, 1) server = dict(id=server_info['id'], name=server_info['properties']['name'], state=server_info['metadata']['state'], vmstate=server_info['properties']['vmState']) except Exception: ex = sys.exc_info()[1] if ex.args[0] is not None and ex.args[0] == 404: print("Server w/ ID {} not found".format(serverid)) server = None else: raise ex # end try/except # end if/else(serverid) return server # end getServerStates() def wait_for_server(pbclient=None, dc_id=None, serverid=None, indicator='state', state='AVAILABLE', timeout=300): ''' wait for a server/VM to reach a defined state for a specified time indicator := {state|vmstate} specifies if server or VM stat is tested state specifies the status the indicator should have ''' if pbclient is None: raise ValueError("argument 'pbclient' must not be None") if dc_id is None: raise ValueError("argument 'dc_id' must not be None") if serverid is None: raise ValueError("argument 'serverid' must not be None") total_sleep_time = 0 seconds = 5 while total_sleep_time < timeout: time.sleep(seconds) total_sleep_time += seconds if total_sleep_time == 60: # Increase polling interval after one minute seconds = 10 elif total_sleep_time == 600: # Increase polling interval after 10 minutes seconds = 20 server = getServerStates(pbclient, dc_id, serverid) if server[indicator] == state: break # end while(total_sleep_time) return server # end wait_for_server() def wait_for_datacenter(client, data_center_id): ''' Poll the data center to become available (for the next provisionig job) ''' total_sleep_time = 0 seconds = 5 while True: state = client.get_datacenter(data_center_id)['metadata']['state'] if verbose: print("datacenter is {}".format(state)) if state == "AVAILABLE": break time.sleep(seconds) total_sleep_time += seconds if total_sleep_time == 60: # Increase polling interval after one minute seconds = 10 elif total_sleep_time == 600: # Increase polling interval after 10 minutes seconds = 20 # end wait_for_datacenter() def main(argv=None): '''Command line options.''' if argv is None: argv = sys.argv else: sys.argv.extend(argv) program_name = os.path.basename(sys.argv[0]) program_version = "v%s" % __version__ program_build_date = str(__updated__) program_version_message = '%%(prog)s %s (%s)' % (program_version, program_build_date) program_shortdesc = __import__('__main__').__doc__.split("\n")[1] program_license = '''%s Created by J. Buchhammer on %s. Copyright 2016 ProfitBricks GmbH. All rights reserved. Licensed under the Apache License 2.0 http://www.apache.org/licenses/LICENSE-2.0 Distributed on an "AS IS" basis without warranties or conditions of any kind, either express or implied. USAGE ''' % (program_shortdesc, str(__date__)) try: # Setup argument parser parser = ArgumentParser(description=program_license, formatter_class=RawDescriptionHelpFormatter) parser.add_argument('-u', '--user', dest='user', help='the login name') parser.add_argument('-p', '--password', dest='password', help='the login password') parser.add_argument('-L', '--Login', dest='loginfile', default=None, help='the login file to use') parser.add_argument('-d', '--datacenterid', dest='dc_id', required=True, default=None, help='datacenter of the server') parser.add_argument('-s', '--serverid', dest='serverid', default=None, help='ID of the server') parser.add_argument('-n', '--name', dest='servername', default=None, help='name of the server') parser.add_argument('-C', '--command', dest='command', default=None, help='remote shell command to use for shutdown') parser.add_argument('-v', '--verbose', dest="verbose", action="count", help="set verbosity level [default: %(default)s]") parser.add_argument('-V', '--version', action='version', version=program_version_message) # Process arguments args = parser.parse_args() global verbose verbose = args.verbose dc_id = args.dc_id if verbose > 0: print("Verbose mode on") if args.serverid is None and args.servername is None: parser.error("one of 'serverid' or 'name' must be specified") (user, password) = getLogin(args.loginfile, args.user, args.password) if user is None or password is None: raise ValueError("user or password resolved to None") pbclient = ProfitBricksService(user, password) server = getServerStates(pbclient, dc_id, args.serverid, args.servername) if server is None: raise Exception(1, "specified server not found") print("using server {}(id={}) in state {}, {}" .format(server['name'], server['id'], server['state'], server['vmstate'])) # ! stop/start/reboot_server() simply return 'True' ! # this implies, that there's NO response nor requestId to track! if server['vmstate'] == 'SHUTOFF': print("VM is already shut off") else: if args.command is None: print("no command specified for shutdown of VM") else: print("executing {}".format(args.command)) cmdrc = call(args.command, shell=True) print("executing {} returned {}".format(args.command, cmdrc)) server = wait_for_server(pbclient, dc_id, server['id'], indicator='vmstate', state='SHUTOFF', timeout=300) # first we have to delete all attached volumes volumes = pbclient.get_attached_volumes(dc_id, server['id'], 0) for vol in volumes['items']: print("deleting volume {} of server {}" .format(vol['id'], server['name'])) pbclient.delete_volume(dc_id, vol['id']) pbclient.delete_server(dc_id, server['id']) wait_for_datacenter(pbclient, dc_id) except KeyboardInterrupt: # handle keyboard interrupt # return 0 except Exception: traceback.print_exc() sys.stderr.write("\n" + program_name + ": for help use --help\n") return 2 # end main() if __name__ == "__main__": sys.exit(main()) profitbricks-sdk-python-4.1.3/examples/pb_importVM.py000066400000000000000000000660651326076220500227640ustar00rootroot00000000000000#!/usr/bin/python # encoding: utf-8 # Copyright 2016-2017 ProfitBricks GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ''' pb_importVM -- import a 3rd party VM pb_importVM is a tool to create a VM that comes from another virtualization solution. It reads out the meta data accompanying the disk files and creates a VM out of it. The disk images must be uploaded already. @author: Jürgen Buchhammer @copyright: 2016 ProfitBricks GmbH. All rights reserved. @license: Apache License 2.0 @contact: juergen.buchhammer@profitbricks.com @deffield updated: Updated ''' import sys import os import traceback from argparse import ArgumentParser from argparse import RawDescriptionHelpFormatter from time import sleep from base64 import b64decode, b64encode import xml.etree.ElementTree as ET from profitbricks.client import ProfitBricksService from profitbricks.client import Datacenter, Volume, Server from profitbricks.client import NIC __all__ = [] __version__ = 0.1 __date__ = '2016-09-30' __updated__ = '2016-09-30' class CLIError(Exception): '''Generic exception to raise and log different fatal errors.''' def __init__(self, msg): super(CLIError).__init__(type(self)) self.msg = "E: %s" % msg def __str__(self): return self.msg def __unicode__(self): return self.msg # end class CLIError def getLogin(filename, user, passwd): ''' write user/passwd to login file or get them from file. This method is not Py3 safe (byte vs. str) ''' if filename is None: return (user, passwd) isPy2 = sys.version_info[0] == 2 if os.path.exists(filename): print("Using file {} for Login".format(filename)) with open(filename, "r") as loginfile: encoded_cred = loginfile.read() print("encoded: {}".format(encoded_cred)) if isPy2: decoded_cred = b64decode(encoded_cred) else: decoded_cred = b64decode(encoded_cred).decode('utf-8') login = decoded_cred.split(':', 1) return (login[0], login[1]) else: if user is None or passwd is None: raise ValueError("user and password must not be None") print("Writing file {} for Login".format(filename)) with open(filename, "wb") as loginfile: creds = user+":"+passwd if isPy2: encoded_cred = b64encode(creds) else: encoded_cred = b64encode(creds.encode('utf-8')) print("encoded: {}".format(encoded_cred)) loginfile.write(encoded_cred) return (user, passwd) # end getLogin() def wait_for_request(pbclient, request_id, timeout=0, initial_wait=5, scaleup=10): ''' Waits for a request to finish until timeout. timeout==0 is interpreted as infinite wait time. Returns a tuple (return code, request status, message) where return code 0 : request successful 1 : request failed -1 : timeout exceeded The wait_period is increased every scaleup steps to adjust for long running requests. ''' total_wait = 0 wait_period = initial_wait next_scaleup = scaleup * wait_period wait = True while wait: request_status = pbclient.get_request(request_id, status=True) state = request_status['metadata']['status'] if state == "DONE": return(0, state, request_status['metadata']['message']) if state == 'FAILED': return(1, state, request_status['metadata']['message']) print("Request '{}' is in state '{}'. Sleeping for {} seconds..." .format(request_id, state, wait_period)) sleep(wait_period) total_wait += wait_period if timeout != 0 and total_wait > timeout: wait = False next_scaleup -= wait_period if next_scaleup == 0: wait_period += initial_wait next_scaleup = scaleup * wait_period print("scaling up wait_period to {}, next change in {} seconds" .format(wait_period, next_scaleup)) # end while(wait) return(-1, state, "request not finished before timeout") # end wait_for_request() def wait_for_requests(pbclient, request_ids=[], timeout=0, initial_wait=5, scaleup=10): ''' Waits for a list of requests to finish until timeout. timeout==0 is interpreted as infinite wait time. Returns a dict of request_id -> result. result is a tuple (return code, request status, message) where return code 0 : request successful 1 : request failed -1 : timeout exceeded The wait_period is increased every scaleup steps to adjust for long running requests. ''' done = dict() if len(request_ids) == 0: print("empty request list") return done total_wait = 0 wait_period = initial_wait next_scaleup = scaleup * wait_period wait = True while wait: for request_id in request_ids: if request_id in done: continue request_status = pbclient.get_request(request_id, status=True) state = request_status['metadata']['status'] if state == "DONE": done[request_id] = (0, state, request_status['metadata']['message']) print("Request '{}' is in state '{}'.".format(request_id, state)) if state == 'FAILED': done[request_id] = (1, state, request_status['metadata']['message']) print("Request '{}' is in state '{}'.".format(request_id, state)) # end for(request_ids) if len(done) == len(request_ids): wait = False else: print("{} of {} requests are finished. Sleeping for {} seconds..." .format(len(done), len(request_ids), wait_period)) sleep(wait_period) total_wait += wait_period if timeout != 0 and total_wait > timeout: wait = False next_scaleup -= wait_period if next_scaleup == 0: wait_period += initial_wait next_scaleup = scaleup * wait_period print("scaling up wait_period to {}, next change in {} seconds" .format(wait_period, next_scaleup)) # end if/else(done) # end while(wait) if len(done) != len(request_ids): for request_id in request_ids: if request_id in done: continue done[request_id] = (-1, state, "request not finished before timeout") return done # end wait_for_requests() def get_disk_image_by_name(pbclient, location, image_name): """ Returns all disk images within a location with a given image name. The name must match exactly. The list may be empty. """ all_images = pbclient.list_images() matching = [i for i in all_images['items'] if i['properties']['name'] == image_name and i['properties']['imageType'] == "HDD" and i['properties']['location'] == location] return matching # end get_disk_image_by_name() # -- OVF parsing -- class OFVData(): '''OFV meta data - the data is only available after calling OFVData.parse''' def __init__(self, file=None): '''OVF meta data initializer''' # namespaces, dflt is default namespace xmlns, others are xmlns: # @TODO: read these from because version may change # e.g. OVF 2.0 exists: xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/2" self._ns = { # 'dflt': "http://schemas.dmtf.org/ovf/envelope/1", # same as ovf!?! 'cim': "http://schemas.dmtf.org/wbem/wscim/1/common", 'ovf': "http://schemas.dmtf.org/ovf/envelope/1", 'rasd': "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/" "CIM_ResourceAllocationSettingData", 'vmw': "http://www.vmware.com/schema/ovf", 'vssd': "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/" "CIM_VirtualSystemSettingData" } self.file = file self.root = None self.name = None self.osid = None self.licenseType = "OTHER" self.cpus = None self.ram = None self.disks = [] self.lans = dict() self.nics = [] self.resourceTypes = { '1': 'Other', '2': 'Computer System', '3': 'Processor', '4': 'Memory', '5': 'IDE Controller', '6': 'Parallel SCSI HBA', '7': 'FC HBA', '8': 'iSCSI HBA', '9': 'IB HCA', '10': 'Ethernet Adapter', '11': 'Other Network Adapter', '12': 'I/O Slot', '13': 'I/O Device', '14': 'Floppy Drive', '15': 'CD Drive', '16': 'DVD drive', '17': 'Disk Drive', '18': 'Tape Drive', '19': 'Storage Extent', '20': 'Other storage device', '21': 'Serial port', '22': 'Parallel port', '23': 'USB Controller', '24': 'Graphics controller', '25': 'IEEE 1394 Controller', '26': 'Partitionable Unit', '27': 'Base Partitionable Unit', '28': 'Power', '29': 'Cooling Capacity', '30': 'Ethernet Switch Port', '31': 'Logical Disk', '32': 'Storage Volume', '33': 'Ethernet Connection', '..': 'DMTF reserved', '0x8000..0xFFFF': 'Vendor Reserved' } # end resourceType self.osTypeOther = { '0': 'Unknown', '1': 'Other', '2': 'MACOS', '3': 'ATTUNIX', '4': 'DGUX', '5': 'DECNT', '6': 'Tru64 UNIX', '7': 'OpenVMS', '8': 'HPUX', '9': 'AIX', '10': 'MVS', '11': 'OS400', '12': 'OS/2', '13': 'JavaVM', '14': 'MSDOS', '15': 'WIN3x', '16': 'WIN95', '17': 'WIN98', '18': 'WINNT', '19': 'WINCE', '20': 'NCR3000', '21': 'NetWare', '22': 'OSF', '23': 'DC/OS', '24': 'Reliant UNIX', '25': 'SCO UnixWare', '26': 'SCO OpenServer', '27': 'Sequent', '28': 'IRIX', '29': 'Solaris', '30': 'SunOS', '31': 'U6000', '32': 'ASERIES', '33': 'HP NonStop OS', '34': 'HP NonStop OSS', '35': 'BS2000', '37': 'Lynx', '38': 'XENIX', '39': 'VM', '40': 'Interactive UNIX', '41': 'BSDUNIX', '42': 'FreeBSD', '43': 'NetBSD', '44': 'GNU Hurd', '45': 'OS9', '46': 'MACH Kernel', '47': 'Inferno', '48': 'QNX', '49': 'EPOC', '50': 'IxWorks', '51': 'VxWorks', '52': 'MiNT', '53': 'BeOS', '54': 'HP MPE', '55': 'NextStep', '56': 'PalmPilot', '57': 'Rhapsody', '59': 'Dedicated', '60': 'OS/390', '61': 'VSE', '62': 'TPF', '64': 'Caldera Open UNIX', '65': 'OpenBSD', '66': 'Not Applicable', '68': 'z/OS', '78': 'FreeBSD 64-Bit', '81': 'Solaris 64-Bit', '86': 'Novell OES', '87': 'Novell Linux Desktop', '88': 'Sun Java Desktop System', '102': 'Other 64-Bit', '104': 'VMware ESXi', '110': 'eComStation 32-bitx', } # end osTypeOther self.osTypeLinux = { '36': 'LINUX', '79': 'RedHat Enterprise Linux', '80': 'RedHat Enterprise Linux 64-Bit', '82': 'SUSE', '83': 'SUSE 64-Bit', '84': 'SLES', '85': 'SLES 64-Bit', '89': 'Mandriva', '90': 'Mandriva 64-Bit', '91': 'TurboLinux', '92': 'TurboLinux 64-Bit', '93': 'Ubuntu', '94': 'Ubuntu 64-Bit', '95': 'Debian', '96': 'Debian 64-Bit', '97': 'Linux 2.4.x', '98': 'Linux 2.4.x 64-Bit', '99': 'Linux 2.6.x', '100': 'Linux 2.6.x 64-Bit', '101': 'Linux 64-Bit', '106': 'CentOS 32-bit', '107': 'CentOS 64-bit', '108': 'Oracle Linux 32-bit', '109': 'Oracle Linux 64-bit', } # end osTypeLinux self.osTypeWindows = { '58': 'Windows 2000', '63': 'Windows (R) Me', '67': 'Windows XP', '69': 'Microsoft Windows Server 2003', '70': 'Microsoft Windows Server 2003 64-Bit', '71': 'Windows XP 64-Bit', '72': 'Windows XP Embedded', '73': 'Windows Vista', '74': 'Windows Vista 64-Bit', '75': 'Windows Embedded for Point of Service', '76': 'Microsoft Windows Server 2008', '77': 'Microsoft Windows Server 2008 64-Bit', '103': 'Microsoft Windows Server 2008 R2', '105': 'Microsoft Windows 7', '111': 'Microsoft Windows Server 2011', '113': 'Microsoft Windows Server 2012', '114': 'Microsoft Windows 8', '115': 'Microsoft Windows 8 64-bit', '116': 'Microsoft Windows Server 2012 R2' } # end osTypeWindows # end __init__() def parse(self): tree = ET.parse(self.file) self.root = tree.getroot() print("parsed file, root element is '{} w/ attributes {}" .format(self.root.tag, self.root.attrib)) self._collect_system_data() self._collect_disk_data() self._collect_nic_data() # end parse() def _nsattr(self, attr, ns=None): ''' returns an attribute name w/ namespace prefix''' if ns is None: return attr else: return '{'+self._ns[ns]+'}'+attr # end _nsattr() def _collect_system_data(self): virtsys = self.root.find('ovf:VirtualSystem', self._ns) self.name = virtsys.find('ovf:Name', self._ns).text virtos = virtsys.find('ovf:OperatingSystemSection', self._ns) self.osid = virtos.get(self._nsattr('id', 'ovf')) if self.osid in self.osTypeLinux: self.licenseType = "LINUX" osname = self.osTypeLinux[self.osid] else: if self.osid in self.osTypeWindows: self.licenseType = "WINDOWS" osname = self.osTypeWindows[self.osid] else: osname = self.osTypeOther[self.osid] print("VM '{}' has {}-type OS '{}'(id:{})" .format(self.name, self.licenseType, osname, self.osid)) virtcpu = virtsys.find('./ovf:VirtualHardwareSection/ovf:Item/[rasd:ResourceType="3"]', self._ns) self.cpus = virtcpu.find('rasd:VirtualQuantity', self._ns).text # !!! VMware also as vmw:CoresPerSocket !!! # we currently exclude this, so there may be cores missing in VM! virtmem = virtsys.find('./ovf:VirtualHardwareSection/ovf:Item/[rasd:ResourceType="4"]', self._ns) # we assume that RAM is specified in MB (or 'byte * 2^20') self.ram = virtmem.find('rasd:VirtualQuantity', self._ns).text print("VM '{}' has {} CPUs and {} MB RAM" .format(self.name, self.cpus, self.ram)) # end _collect_system_data() # get the disks def _collect_disk_data(self): filerefs = self.root.findall('./ovf:References/ovf:File', self._ns) files = dict() for ref in filerefs: name = ref.get(self._nsattr('href', 'ovf')) fileid = ref.get(self._nsattr('id', 'ovf')) files[fileid] = name print("found filerefs {}".format(files)) diskrefs = self.root.findall('./ovf:DiskSection/ovf:Disk', self._ns) disks = dict() for ref in diskrefs: # Note: we assume ovf:capacityAllocationUnits="byte * 2^30" == GiB capacity = ref.get(self._nsattr('capacity', 'ovf')) # reference to file references above fref = ref.get(self._nsattr('fileRef', 'ovf')) # the virt. HW section refers to '/disk/vmdisk1' not 'vmdisk1' diskid = 'ovf:/disk/'+ref.get(self._nsattr('diskId', 'ovf')) # we resolve fref here, we only need the name from now on disks[diskid] = {'capacity': capacity, 'file': files[fref]} print("found disks {}".format(disks)) virtsys = self.root.find('ovf:VirtualSystem', self._ns) virthds = virtsys.findall('./ovf:VirtualHardwareSection/ovf:Item/[rasd:ResourceType="17"]', self._ns) devices = dict() for hdd in virthds: # print("hdd is {}".format(hdd)) diskref = hdd.find('rasd:HostResource', self._ns).text address = hdd.find('rasd:AddressOnParent', self._ns) if address is None: print("no address for sorting found for {}, use InstanceId" .format(diskref)) devNr = hdd.find('rasd:InstanceId', self._ns).text print("disk {} has InstanceId {}".format(diskref, devNr)) else: devNr = address.text print("disk {} has address {}".format(diskref, devNr)) devices[devNr] = disks[diskref] print("devices : {}".format(devices)) self.disks = [devices[dev_no] for dev_no in sorted(devices)] print("disks : {}".format(self.disks)) # end _collect_disk_data() # get the nics def _collect_nic_data(self): # the NetworkSection contains only name and description # maybe that helps for better LAN assignment one day vnets = self.root.findall('./ovf:NetworkSection/ovf:Network', self._ns) lanid = 1 for net in vnets: self.lans[net.get(self._nsattr('name', 'ovf'))] = lanid lanid += 1 print("LANs found: {}".format(self.lans)) virtsys = self.root.find('ovf:VirtualSystem', self._ns) virtnics = virtsys.findall( './ovf:VirtualHardwareSection/ovf:Item/[rasd:ResourceType="10"]', self._ns) devices = dict() for nic in virtnics: # print("nic is {}".format(nic)) nicname = nic.find('rasd:ElementName', self._ns).text connection = nic.find('rasd:Connection', self._ns).text address = nic.find('rasd:AddressOnParent', self._ns) if address is None: print("no address for sorting found for {}, use InstanceId" .format(nicname)) devNr = nic.find('rasd:InstanceId', self._ns).text print("nic '{}' has InstanceId {}".format(nicname, devNr)) else: devNr = address.text print("nic '{}' has address {}".format(nicname, devNr)) devices[devNr] = {'nic': nicname, 'lan': connection, 'lanid': self.lans[connection]} print("devices : {}".format(devices)) self.nics = [devices[dev_no] for dev_no in sorted(devices)] print("nics : {}".format(self.nics)) # end _collect_nic_data() # end class OVFData # -- MAIN -- def main(argv=None): '''Command line options.''' if argv is None: argv = sys.argv else: sys.argv.extend(argv) program_name = os.path.basename(sys.argv[0]) program_version = "v%s" % __version__ program_build_date = str(__updated__) program_version_message = '%%(prog)s %s (%s)' % (program_version, program_build_date) program_shortdesc = __import__('__main__').__doc__.split("\n")[1] program_license = '''%s Created by Jürgen Buchhammer on %s. Copyright 2016 ProfitBricks GmbH. All rights reserved. Licensed under the Apache License 2.0 http://www.apache.org/licenses/LICENSE-2.0 Distributed on an "AS IS" basis without warranties or conditions of any kind, either express or implied. USAGE ''' % (program_shortdesc, str(__date__)) try: # Setup argument parser parser = ArgumentParser(description=program_license, formatter_class=RawDescriptionHelpFormatter) parser.add_argument('-u', '--user', dest='user', help='the login name') parser.add_argument('-p', '--password', dest='password', help='the login password') parser.add_argument('-L', '--Login', dest='loginfile', default=None, help='the login file to use') parser.add_argument('-t', '--type', dest='metatype', default="OVF", help='type of VM meta data') parser.add_argument('-m', '--metadata', dest='metafile', required=True, default=None, help='meta data file') parser.add_argument('-d', '--datacenterid', dest='datacenterid', default=None, help='datacenter of the new server') parser.add_argument('-D', '--DCname', dest='dcname', default=None, help='new datacenter name') parser.add_argument('-l', '--location', dest='location', default=None, help='location for new datacenter') parser.add_argument('-v', '--verbose', dest="verbose", action="count", default=0, help="set verbosity level [default: %(default)s]") parser.add_argument('-V', '--version', action='version', version=program_version_message) # Process arguments args = parser.parse_args() global verbose verbose = args.verbose if verbose > 0: print("Verbose mode on") print("start {} with args {}".format(program_name, str(args))) (user, password) = getLogin(args.loginfile, args.user, args.password) if user is None or password is None: raise ValueError("user or password resolved to None") pbclient = ProfitBricksService(user, password) if args.metatype == 'OVF': metadata = OFVData(args.metafile) metadata.parse() else: sys.stderr.write("Metadata type '{}' is not supported" .format(args.metatype)) return 1 # we need the DC first to have the location defined dc_id = None if args.datacenterid is None: if args.dcname is None or args.location is None: sys.stderr.write("Either '-d ' or '-D -l ' must be specified") return 1 # else: we will create the DC later after parsing the meta data else: dc_id = args.datacenterid if dc_id is None: location = args.location dc = Datacenter(name=args.dcname, location=location, description="created by pb_importVM") print("create new DC {}".format(str(dc))) response = pbclient.create_datacenter(dc) dc_id = response['id'] result = wait_for_request(pbclient, response['requestId']) print("wait loop returned {}".format(result)) else: dc = pbclient.get_datacenter(dc_id) location = dc['properties']['location'] print("use existing DC {} in location {}" .format(dc['properties']['name'], location)) # check if images exist for disk in metadata.disks: disk_name = disk['file'] images = get_disk_image_by_name(pbclient, location, disk_name) if len(images) == 0: raise ValueError("No HDD image with name '{}' found in location {}" .format(disk_name, location)) if len(images) > 1: raise ValueError("Ambigous image name '{}' in location {}" .format(disk_name, location)) disk['image'] = images[0]['id'] # now we're ready to create the VM # Server server = Server(name=metadata.name, cores=metadata.cpus, ram=metadata.ram) print("create server {}".format(str(Server))) response = pbclient.create_server(dc_id, server) srv_id = response['id'] result = wait_for_request(pbclient, response['requestId']) print("wait loop returned {}".format(str(result))) # NICs (note that createing LANs may be implicit) for nic in metadata.nics: dcnic = NIC(name=nic['nic'], lan=nic['lanid']) print("create NIC {}".format(str(dcnic))) response = pbclient.create_nic(dc_id, srv_id, dcnic) nic_id = response['id'] result = wait_for_request(pbclient, response['requestId']) print("wait loop returned {}".format(str(result))) response = pbclient.get_nic(dc_id, srv_id, nic_id, 2) mac = response['properties']['mac'] print("dcnic has MAC {} for {}".format(mac, nic_id)) # end for(nics) # Volumes (we use the image name as volume name too requests = [] for disk in metadata.disks: dcvol = Volume(name=disk['file'], size=disk['capacity'], image=disk['image'], licence_type=metadata.licenseType) print("create Volume {}".format(str(dcvol))) response = pbclient.create_volume(dc_id, dcvol) requests.append(response['requestId']) disk['volume_id'] = response['id'] # end for(disks) if len(requests) != 0: result = wait_for_requests(pbclient, requests, initial_wait=10, scaleup=15) print("wait loop returned {}".format(str(result))) for disk in metadata.disks: print("attach volume {}".format(disk)) response = pbclient.attach_volume(dc_id, srv_id, disk['volume_id']) result = wait_for_request(pbclient, response['requestId']) print("wait loop returned {}".format(str(result))) # end for(disks) print("import of VM succesfully finished") return 0 except KeyboardInterrupt: # handle keyboard interrupt return 0 except Exception: traceback.print_exc() sys.stderr.write("\n" + program_name + ": for help use --help\n") return 2 # end main() if __name__ == "__main__": sys.exit(main()) profitbricks-sdk-python-4.1.3/examples/pb_snapshotDatacenter.py000066400000000000000000000557211326076220500250360ustar00rootroot00000000000000#!/usr/bin/python # encoding: utf-8 # Copyright 2016-2017 ProfitBricks GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ''' pb_snapshotDatacenter is a sample script to make a snapshot of a complete datacenter. pb_snapshotDatacenter - makes a snap shot of each server volume - dumps the datacenter inventory to a file You can use this information to restore or clone a datacenter or use it as a starting point for new data centers. @author: Jürgen Buchhammer @copyright: 2016 ProfitBricks GmbH. All rights reserved. @license: Apache License 2.0 @contact: juergen.buchhammer@profitbricks.com @deffield updated: Updated ''' import sys import os import traceback from argparse import ArgumentParser from argparse import RawDescriptionHelpFormatter from datetime import datetime from time import sleep import json from base64 import b64decode, b64encode from profitbricks.client import ProfitBricksService __all__ = [] __version__ = 0.1 __date__ = '2016-09-02' __updated__ = '2016-09-02' class CLIError(Exception): '''Generic exception to raise and log different fatal errors.''' def __init__(self, msg): super(CLIError).__init__(type(self)) self.msg = "E: %s" % msg def __str__(self): return self.msg def __unicode__(self): return self.msg # end class CLIError def getLogin(filename, user, passwd): ''' write user/passwd to login file or get them from file. This method is not Py3 safe (byte vs. str) ''' if filename is None: return (user, passwd) isPy2 = sys.version_info[0] == 2 if os.path.exists(filename): print("Using file {} for Login".format(filename)) with open(filename, "r") as loginfile: encoded_cred = loginfile.read() print("encoded: {}".format(encoded_cred)) if isPy2: decoded_cred = b64decode(encoded_cred) else: decoded_cred = b64decode(encoded_cred).decode('utf-8') login = decoded_cred.split(':', 1) return (login[0], login[1]) else: if user is None or passwd is None: raise ValueError("user and password must not be None") print("Writing file {} for Login".format(filename)) with open(filename, "wb") as loginfile: creds = user+":"+passwd if isPy2: encoded_cred = b64encode(creds) else: encoded_cred = b64encode(creds.encode('utf-8')) print("encoded: {}".format(encoded_cred)) loginfile.write(encoded_cred) return (user, passwd) # end getLogin() def write_dc_definition(pbclient, dcdef=None, filename=None): with open(filename, 'w') as outfile: json.dump(dcdef, outfile, indent=2) return 0 # end write_dc_definition() def read_dc_definition(pbclient, filename=None): with open(filename) as infile: dcdef = json.load(infile) return dcdef # end read_dc_definition() # --- request status (see pb_createDatacenter.py) def wait_for_request(pbclient, request_id, timeout=0, initial_wait=5, scaleup=10): ''' Waits for a request to finish until timeout. timeout==0 is interpreted as infinite wait time. Returns a tuple (return code, request status, message) where return code 0 : request successful 1 : request failed -1 : timeout exceeded The wait_period is increased every scaleup steps to adjust for long running requests. ''' total_wait = 0 wait_period = initial_wait next_scaleup = scaleup * wait_period wait = True while wait: request_status = pbclient.get_request(request_id, status=True) state = request_status['metadata']['status'] if state == "DONE": return(0, state, request_status['metadata']['message']) if state == 'FAILED': return(1, state, request_status['metadata']['message']) print("Request '{}' is in state '{}'. Sleeping for {} seconds..." .format(request_id, state, wait_period)) sleep(wait_period) total_wait += wait_period if timeout != 0 and total_wait > timeout: wait = False next_scaleup -= wait_period if next_scaleup == 0: wait_period += initial_wait next_scaleup = scaleup * wait_period print("scaling up wait_period to {}, next change in {} seconds" .format(wait_period, next_scaleup)) # end while(wait) return(-1, state, "request not finished before timeout") # end wait_for_request() def wait_for_requests(pbclient, request_ids=[], timeout=0, initial_wait=5, scaleup=10): ''' Waits for a list of requests to finish until timeout. timeout==0 is interpreted as infinite wait time. Returns a dict of request_id -> result. result is a tuple (return code, request status, message) where return code 0 : request successful 1 : request failed -1 : timeout exceeded The wait_period is increased every scaleup steps to adjust for long running requests. ''' done = dict() if len(request_ids) == 0: print("empty request list") return done total_wait = 0 wait_period = initial_wait next_scaleup = scaleup * wait_period wait = True while wait: for request_id in request_ids: if request_id in done: continue request_status = pbclient.get_request(request_id, status=True) state = request_status['metadata']['status'] if state == "DONE": done[request_id] = (0, state, request_status['metadata']['message']) print("Request '{}' is in state '{}'.".format(request_id, state)) if state == 'FAILED': done[request_id] = (1, state, request_status['metadata']['message']) print("Request '{}' is in state '{}'.".format(request_id, state)) # end for(request_ids) if len(done) == len(request_ids): wait = False else: print("{} of {} requests are finished. Sleeping for {} seconds..." .format(len(done), len(request_ids), wait_period)) sleep(wait_period) total_wait += wait_period if timeout != 0 and total_wait > timeout: wait = False next_scaleup -= wait_period if next_scaleup == 0: wait_period += initial_wait next_scaleup = scaleup * wait_period print("scaling up wait_period to {}, next change in {} seconds" .format(wait_period, next_scaleup)) # end if/else(done) # end while(wait) if len(done) != len(request_ids): for request_id in request_ids: if request_id in done: continue done[request_id] = (-1, state, "request not finished before timeout") return done # end wait_for_requests() # ---- server states and control (see pb_controlServerStates.py) def getServerInfo(pbclient=None, dc_id=None): ''' gets info of servers of a data center''' if pbclient is None: raise ValueError("argument 'pbclient' must not be None") if dc_id is None: raise ValueError("argument 'dc_id' must not be None") # list of all found server's info server_info = [] # depth 1 is enough for props/meta servers = pbclient.list_servers(dc_id, 1) for server in servers['items']: props = server['properties'] info = dict(id=server['id'], name=props['name'], state=server['metadata']['state'], vmstate=props['vmState']) server_info.append(info) # end for(servers) return(server_info) # end getServerInfo() def select_where(info=None, select=None, **where): if info is None: raise ValueError("argument 'info' must not be None") if len(info) == 0: return [] if select is None: select = info[0].keys() server_info = [] for old_si in info: w_matches = all(old_si[wk] == wv for (wk, wv) in where.items()) new_si = {k: v for (k, v) in old_si.items() if k in select and w_matches} if len(new_si) > 0: server_info.append(new_si) # end for(info) return(server_info) # end select_where() def getServerStates(pbclient=None, dc_id=None, serverid=None, servername=None): ''' gets states of a server''' if pbclient is None: raise ValueError("argument 'pbclient' must not be None") if dc_id is None: raise ValueError("argument 'dc_id' must not be None") server = None if serverid is None: if servername is None: raise ValueError("one of 'serverid' or 'servername' must be specified") # so, arg.servername is set (to whatever) server_info = select_where(getServerInfo(pbclient, dc_id), ['id', 'name', 'state', 'vmstate'], name=servername) if len(server_info) > 1: raise NameError("ambiguous server name '{}'".format(servername)) if len(server_info) == 1: server = server_info[0] else: # get by ID may also fail if it's removed # in this case, catch exception (message 404) and be quiet for a while # unfortunately this has changed from Py2 to Py3 try: server_info = pbclient.get_server(dc_id, serverid, 1) server = dict(id=server_info['id'], name=server_info['properties']['name'], state=server_info['metadata']['state'], vmstate=server_info['properties']['vmState']) except Exception: ex = sys.exc_info()[1] if ex.args[0] is not None and ex.args[0] == 404: print("Server w/ ID {} not found".format(serverid)) server = None else: raise ex # end try/except # end if/else(serverid) return server # end getServerStates() def wait_for_server(pbclient=None, dc_id=None, serverid=None, indicator='state', state='AVAILABLE', timeout=300): ''' wait for a server/VM to reach a defined state for a specified time indicator := {state|vmstate} specifies if server or VM stat is tested state specifies the status the indicator should have ''' if pbclient is None: raise ValueError("argument 'pbclient' must not be None") if dc_id is None: raise ValueError("argument 'dc_id' must not be None") if serverid is None: raise ValueError("argument 'serverid' must not be None") total_sleep_time = 0 seconds = 5 while total_sleep_time < timeout: sleep(seconds) total_sleep_time += seconds if total_sleep_time == 60: # Increase polling interval after one minute seconds = 10 elif total_sleep_time == 600: # Increase polling interval after 10 minutes seconds = 20 server = getServerStates(pbclient, dc_id, serverid) if server[indicator] == state: break # end while(total_sleep_time) return server # end wait_for_server() def controlServerState(pbclient=None, dc_id=None, serverid=None, servername=None, action=None): server = getServerStates(pbclient, dc_id, serverid, servername) if server is None: raise Exception(1, "specified server not found") print("using server {}(id={}) in state {}, {}" .format(server['name'], server['id'], server['state'], server['vmstate'])) # !!! stop/start/reboot_server() simply return 'True' !!! # this implies, that there's NO response nor requestId to track! if action == 'POWEROFF': if server['state'] == 'INACTIVE': print("server is already powered off") else: # currently use 'forced' poweroff if server['vmstate'] != 'SHUTOFF': print("VM is in state {}, {} may lead to inconsistent state" .format(server['vmstate'], action)) pbclient.stop_server(dc_id, server['id']) server = wait_for_server(pbclient, dc_id, server['id'], state='INACTIVE', timeout=300) elif action == 'POWERON': if server['vmstate'] == 'RUNNING': print("VM is already up and running") else: pbclient.start_server(dc_id, server['id']) server = wait_for_server(pbclient, dc_id, server['id'], indicator='vmstate', state='RUNNING', timeout=300) elif action == 'START': # this is the same as POWERON if server['vmstate'] == 'RUNNING': print("VM is already up and running") else: pbclient.start_server(dc_id, server['id']) server = wait_for_server(pbclient, dc_id, server['id'], indicator='vmstate', state='RUNNING', timeout=300) elif action == 'SHUTOFF': if server['vmstate'] == 'SHUTOFF': print("VM is already shut off") else: print("no command specified for shutdown of VM") # end if/else(action) print("server {}(id={}) now in state {}, {}" .format(server['name'], server['id'], server['state'], server['vmstate'])) # end controlServerState() def main(argv=None): '''Parse command line options and dump a datacenter to snapshots and file.''' if argv is None: argv = sys.argv else: sys.argv.extend(argv) program_name = os.path.basename(sys.argv[0]) program_version = "v%s" % __version__ program_build_date = str(__updated__) program_version_message = '%%(prog)s %s (%s)' % (program_version, program_build_date) program_shortdesc = __import__('__main__').__doc__.split("\n")[1] program_license = '''%s Created by J. Buchhammer on %s. Copyright 2016 ProfitBricks GmbH. All rights reserved. Licensed under the Apache License 2.0 http://www.apache.org/licenses/LICENSE-2.0 Distributed on an "AS IS" basis without warranties or conditions of any kind, either express or implied. USAGE ''' % (program_shortdesc, str(__date__)) try: # Setup argument parser parser = ArgumentParser(description=program_license, formatter_class=RawDescriptionHelpFormatter) parser.add_argument('-u', '--user', dest='user', help='the login name') parser.add_argument('-p', '--password', dest='password', help='the login password') parser.add_argument('-L', '--Login', dest='loginfile', default=None, help='the login file to use') parser.add_argument('-d', '--datacenterid', dest='dc_id', required=True, default=None, help='datacenter ID of the server') parser.add_argument('-o', '--outfile', dest='outfile', default='dc-def_'+datetime.now().strftime('%Y-%m-%d_%H%M%S'), help='the output file name') parser.add_argument('-S', '--Stopalways', dest='stopalways', action='store_true', help='power off even when VM is running') parser.add_argument('-v', '--verbose', dest="verbose", action="count", default=0, help="set verbosity level [default: %(default)s]") parser.add_argument('-V', '--version', action='version', version=program_version_message) # Process arguments args = parser.parse_args() global verbose verbose = args.verbose if verbose > 0: print("Verbose mode on") print("start {} with args {}".format(program_name, str(args))) outfile = args.outfile if outfile.endswith(".json"): outfile = os.path.splitext(outfile) print("Using output file base name '{}'".format(outfile)) (user, password) = getLogin(args.loginfile, args.user, args.password) if user is None or password is None: raise ValueError("user or password resolved to None") pbclient = ProfitBricksService(user, password) dc_id = args.dc_id # first get all server's VM and OS state to see if we can start srv_info = getServerInfo(pbclient, dc_id) srvon = 0 for server in srv_info: if server['vmstate'] != 'SHUTOFF': print("VM {} is in state {}, but should be SHUTOFF" .format(server['name'], server['vmstate'])) srvon += 1 # end for(srv_info) if srvon > 0 and not args.stopalways: print("shutdown running OS before trying again") return 1 # now power off all VMs before starting the snapshots for server in srv_info: controlServerState(pbclient, dc_id, server['id'], action='POWEROFF') # now let's go dcdef = pbclient.get_datacenter(dc_id, 5) print("starting dump of datacenter {}".format(dcdef['properties']['name'])) dcdef_file = outfile+'_source.json' print("write source dc to {}".format(dcdef_file)) write_dc_definition(pbclient, dcdef, dcdef_file) print("get existing Snapshots") # first get existing snapshots known_snapshots = dict() snapshots = pbclient.list_snapshots() for snap in snapshots['items']: print("SNAP : {}".format(json.dumps(snap))) known_snapshots[snap['properties']['name']] = snap['id'] print("create Snapshots, this may take a while ..") # we do NOT consider dangling volumes, only server-attached ones vol_snapshots = dict() # map volume id==snapshot name snapshot id for server in dcdef['entities']['servers']['items']: print("- server {}".format(server['properties']['name'])) if 'volumes' not in server['entities']: print(" server {} has no volumes" .format(server['properties']['name'])) continue # The volumes are attached by order of creation # Thus we must sort them to keep the order in the clone print("setting volume order by deviceNumber") volumes = server['entities']['volumes']['items'] new_order = sorted(volumes, key=lambda vol: vol['properties']['deviceNumber']) server['entities']['volumes']['items'] = new_order for volume in server['entities']['volumes']['items']: vol_id = volume['id'] # this will be the name too if vol_id in known_snapshots: print("use existing snapshot {} of volume {}" .format(vol_id, volume['properties']['name'])) vol_snapshots[vol_id] = known_snapshots[vol_id] else: print("taking snapshot {} of volume {}" .format(vol_id, volume['properties']['name'])) response = pbclient.create_snapshot(dc_id, vol_id, vol_id, "auto-created by pb_snapshotDatacenter") # response has no request id, need to check metadata state (BUSY, AVAILABLE..) vol_snapshots[vol_id] = response['id'] print("snapshot in progress: {}".format(str(response))) # end for(volume) # end for(server) print("Waiting for snapshots to complete") snapdone = dict() while len(snapdone) != len(vol_snapshots): sleep(10) for snap_id in vol_snapshots.values(): print("looking for {}".format(snap_id)) if snap_id in snapdone: continue snapshot = pbclient.get_snapshot(snap_id) print("snapshot {} is in state {}" .format(snap_id, snapshot['metadata']['state'])) if snapshot['metadata']['state'] == 'AVAILABLE': snapdone[snap_id] = snapshot['metadata']['state'] # end for(vol_snapshots) # end while(snapdone) # now replace the volumes image IDs print("setting snapshot id to volumes") for server in dcdef['entities']['servers']['items']: print("- server {}".format(server['properties']['name'])) if 'volumes' not in server['entities']: print(" server {} has no volumes" .format(server['properties']['name'])) continue for volume in server['entities']['volumes']['items']: vol_id = volume['id'] # this will be the name too volume['properties']['image'] = vol_snapshots[vol_id] # end for(volume) # end for(server) # As it came out, the LAN id is rearranged by order of creation # Thus we must sort the LANs to keep the order in the clone print("setting LAN order by id") lans = dcdef['entities']['lans']['items'] new_order = sorted(lans, key=lambda lan: lan['id']) dcdef['entities']['lans']['items'] = new_order # now sort unordered NICs by MAC and save the dcdef # reason is, that NICs seem to be ordered by MAC, but API response # doesn't guarantee the order, which we need for re-creation print("setting NIC order by MAC") for server in dcdef['entities']['servers']['items']: print("- server {}".format(server['properties']['name'])) if 'nics' not in server['entities']: print(" server {} has no nics" .format(server['properties']['name'])) continue nics = server['entities']['nics']['items'] # print("NICs before {}".format(json.dumps(nics))) new_order = sorted(nics, key=lambda nic: nic['properties']['mac']) # print("NICs after {}".format(json.dumps(new_order))) server['entities']['nics']['items'] = new_order # end for(server) dcdef_file = outfile+'.json' print("write snapshot dc to {}".format(dcdef_file)) write_dc_definition(pbclient, dcdef, dcdef_file) return 0 except KeyboardInterrupt: # handle keyboard interrupt return 0 except Exception: traceback.print_exc() sys.stderr.write("\n" + program_name + ": for help use --help\n") return 2 if __name__ == "__main__": sys.exit(main()) profitbricks-sdk-python-4.1.3/examples/server_example.py000066400000000000000000000205141326076220500235340ustar00rootroot00000000000000#!/usr/bin/python3 # Copyright 2015-2017 ProfitBricks GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Create Simple Server """ from profitbricks.client import ProfitBricksService from profitbricks.client import Server server_id = '700e1cab-99b2-4c30-ba8c-1d273ddba023' datacenter_id = '700e1cab-99b2-4c30-ba8c-1d273ddba022' client = ProfitBricksService( username='username', password='password') i = Server( name='server', ram=4096, cores=4 ) response = client.create_server( datacenter_id=datacenter_id, server=i) """Create Complex Server """ from profitbricks.client import ProfitBricksService # noqa from profitbricks.client import Server, NIC, Volume # noqa server_id = '700e1cab-99b2-4c30-ba8c-1d273ddba023' datacenter_id = '700e1cab-99b2-4c30-ba8c-1d273ddba022' image_id = '226ed8c0-a2fe-11e4-b187-5f1f641608c8' client = ProfitBricksService( username='username', password='password') nic1 = NIC( name='nic1', ips=['10.2.2.5'], dhcp='true', lan=1, firewall_active=True, ) nic2 = NIC( name='nic2', ips=['10.2.3.6'], dhcp='true', lan=1, firewall_active=True, ) volume1 = Volume( name='volume6', size=56, image=image_id, bus='VIRTIO' ) volume2 = Volume( name='volume7', size=56, image=image_id, bus='VIRTIO' ) nics = [nic1, nic2] create_volumes = [volume1, volume2] i = Server( name='server11', ram=4096, cores=4, nics=nics, create_volumes=create_volumes ) response = client.create_server( datacenter_id=datacenter_id, server=i) """Create Server with Existing Volume """ from profitbricks.client import ProfitBricksService # noqa from profitbricks.client import Server # noqa server_id = '700e1cab-99b2-4c30-ba8c-1d273ddba023' datacenter_id = '700e1cab-99b2-4c30-ba8c-1d273ddba022' volume_id = '700e1cab-99b2-4c30-ba8c-1d273ddba044' client = ProfitBricksService( username='username', password='password') attach_volumes = [volume_id] i = Server( name='server1', ram=4096, cores=4, attach_volumes=attach_volumes ) response = client.create_server( datacenter_id=datacenter_id, server=i) """Create Server with New Volumes """ from profitbricks.client import ProfitBricksService # noqa from profitbricks.client import Server, Volume # noqa server_id = '700e1cab-99b2-4c30-ba8c-1d273ddba023' volume_id = '700e1cab-99b2-4c30-ba8c-1d273ddba044' datacenter_id = 'c4fd1f8a-65e0-42cb-b8fa-ff7e87c3071b' image_id = '27500669-d81b-11e4-aea4-52540066fee9' client = ProfitBricksService( username='username', password='password') volume1 = Volume( name='volume11', size=56, image=image_id, bus='VIRTIO' ) volume2 = Volume( name='volume21', size=56, image=image_id, bus='VIRTIO' ) create_volumes = [volume1, volume2] i = Server( name='server12', ram=4096, cores=4, create_volumes=create_volumes ) response = client.create_server( datacenter_id=datacenter_id, server=i) """Create Server with NICs Only """ from profitbricks.client import ProfitBricksService # noqa from profitbricks.client import Server, NIC # noqa datacenter_id = '700e1cab-99b2-4c30-ba8c-1d273ddba022' client = ProfitBricksService( username='username', password='password') nic1 = NIC( name='nic1', ips=['10.2.2.3'], dhcp='true', lan=1, firewall_active=True, ) nic2 = NIC( name='nic2', ips=['10.2.3.4'], dhcp='true', lan=1, firewall_active=True, ) nics = [nic1, nic2] i = Server( name='server87', ram=4096, cores=4, nics=nics ) response = client.create_server( datacenter_id=datacenter_id, server=i) """Create Server with Two Existing Volumes """ from profitbricks.client import ProfitBricksService # noqa from profitbricks.client import Server # noqa datacenter_id = '700e1cab-99b2-4c30-ba8c-1d273ddba022' volume_id1 = '700e1cab-99b2-4c30-ba8c-1d273ddba023' volume_id2 = '800e1cab-99b2-4c30-ba8c-1d273ddba024' client = ProfitBricksService( username='username', password='password') attach_volumes = [volume_id1, volume_id2] i = Server( name='server1', ram=4096, cores=4, attach_volumes=attach_volumes ) response = client.create_server( datacenter_id=datacenter_id, server=i) """Create Server with Boot Volume """ from profitbricks.client import ProfitBricksService # noqa from profitbricks.client import Server # noqa datacenter_id = '700e1cab-99b2-4c30-ba8c-1d273ddba022' volume_id1 = '700e1cab-99b2-4c30-ba8c-1d273ddba023' volume_id2 = '800e1cab-99b2-4c30-ba8c-1d273ddba024' boot_volume_id = '800e1cab-99b2-4c30-ba8c-1d273ddba024' client = ProfitBricksService( username='username', password='password') i = Server( name='server14', ram=4096, cores=4, boot_volume_id=boot_volume_id ) response = client.create_server( datacenter_id=datacenter_id, server=i) """Create Server with Existing Volumes and NICs """ from profitbricks.client import ProfitBricksService # noqa from profitbricks.client import Server, NIC # noqa datacenter_id = '700e1cab-99b2-4c30-ba8c-1d273ddba022' volume_id1 = '700e1cab-99b2-4c30-ba8c-1d273ddba023' volume_id2 = '800e1cab-99b2-4c30-ba8c-1d273ddba024' boot_volume_id = '800e1cab-99b2-4c30-ba8c-1d273ddba024' client = ProfitBricksService( username='username', password='password') attach_volumes = [volume_id1, volume_id2] nic1 = NIC( name='nic1', ips=['10.2.2.3'], dhcp='true', lan=1, firewall_active=True, ) nic2 = NIC( name='nic2', ips=['10.2.3.4'], dhcp='true', lan=1, firewall_active=True, ) nics = [nic1, nic2] i = Server( name='server1', ram=4096, cores=4, boot_volume_id=boot_volume_id, attach_volumes=attach_volumes, nics=nics ) response = client.create_server( datacenter_id=datacenter_id, server=i) """Start Server """ from profitbricks.client import ProfitBricksService # noqa datacenter_id = '700e1cab-99b2-4c30-ba8c-1d273ddba022' server_id = '700e1cab-99b2-4c30-ba8c-1d273ddba023' client = ProfitBricksService( username='username', password='password') server = client.start_server( datacenter_id=datacenter_id, server_id=server_id) """Stop Server """ from profitbricks.client import ProfitBricksService # noqa datacenter_id = '700e1cab-99b2-4c30-ba8c-1d273ddba022' server_id = '700e1cab-99b2-4c30-ba8c-1d273ddba023' client = ProfitBricksService( username='username', password='password') server = client.stop_server( datacenter_id=datacenter_id, server_id=server_id) """Reboot Server """ from profitbricks.client import ProfitBricksService # noqa datacenter_id = '700e1cab-99b2-4c30-ba8c-1d273ddba022' server_id = '700e1cab-99b2-4c30-ba8c-1d273ddba023' client = ProfitBricksService( username='username', password='password') server = client.reboot_server( datacenter_id=datacenter_id, server_id=server_id) """List Servers """ from profitbricks.client import ProfitBricksService # noqa datacenter_id = '700e1cab-99b2-4c30-ba8c-1d273ddba022' client = ProfitBricksService( username='username', password='password') servers = client.list_servers(datacenter_id=datacenter_id) """Delete Server """ from profitbricks.client import ProfitBricksService # noqa datacenter_id = '700e1cab-99b2-4c30-ba8c-1d273ddba022' server_id = '700e1cab-99b2-4c30-ba8c-1d273ddba023' client = ProfitBricksService( username='username', password='password') server = client.delete_server( datacenter_id=datacenter_id, server_id=server_id) """Update Server """ from profitbricks.client import ProfitBricksService # noqa datacenter_id = '700e1cab-99b2-4c30-ba8c-1d273ddba022' server_id = '700e1cab-99b2-4c30-ba8c-1d273ddba023' client = ProfitBricksService( username='username', password='password') server = client.update_server( datacenter_id=datacenter_id, server_id=server_id, cores=35, ram=2048) profitbricks-sdk-python-4.1.3/examples/snapshot_examples.py000066400000000000000000000043721326076220500242540ustar00rootroot00000000000000#!/usr/bin/python3 # Copyright 2015-2017 ProfitBricks GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """List Snapshots """ from __future__ import print_function from profitbricks.client import ProfitBricksService client = ProfitBricksService( username='username', password='password') snapshots = client.list_snapshots() for s in snapshots['items']: print(s['properties']['name']) """Get Snapshot """ from profitbricks.client import ProfitBricksService # noqa snapshot_id = '7df81087-5835-41c6-a10b-3e098593bba4' client = ProfitBricksService( username='username', password='password') snapshot = client.get_snapshot( snapshot_id=snapshot_id) """ Update Snapshot Valid snapshot parameters are: * name (str) * description (str) * licence_type (one of 'LINUX', 'WINDOWS' or 'UNKNOWN') * cpu_hot_plug (bool) * ram_hot_plug (bool) * nic_hot_plug (bool) * nic_hot_unplug (bool) * disc_virtio_hot_plug (bool) * disc_virtio_hot_unplug (bool) * disc_scsi_hot_plug (bool) * disc_scsi_hot_unplug (bool) """ from profitbricks.client import ProfitBricksService # noqa client = ProfitBricksService( username='username', password='password') snapshot_id = 'd084aa0a-f9ab-41d5-9316-18163bc416ef' image = client.update_snapshot( snapshot_id, name='New name', description="Backup of volume XYZ", licence_type='LINUX', cpu_hot_plug=True, ram_hot_plug=True, nic_hot_plug=True, nic_hot_unplug=True, disc_virtio_hot_plug=True, disc_virtio_hot_unplug=True) """Remove Snapshot """ from profitbricks.client import ProfitBricksService # noqa snapshot_id = '7df81087-5835-41c6-a10b-3e098593bba4' client = ProfitBricksService( username='username', password='password') snapshot = client.delete_snapshot( snapshot_id=snapshot_id) profitbricks-sdk-python-4.1.3/examples/user_management_examples.py000066400000000000000000000036201326076220500255620ustar00rootroot00000000000000# Copyright 2015-2017 ProfitBricks GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import os from profitbricks.client import ProfitBricksService, Group, User # Instantiate ProfitBricks connection client = ProfitBricksService( username=os.getenv('PROFITBRICKS_USERNAME'), password=os.getenv('PROFITBRICKS_PASSWORD')) """Create a group """ request = Group( name='demo-group', create_datacenter=True, create_snapshot=False, reserve_ip=True, access_activity_log=False) group = client.create_group(request) """List groups """ groups = client.list_groups() """Create a user """ user_request = User( firstname='John', lastname='Doe', email='demo-user@example.com', password='SecretPassword123', administrator=True, force_sec_auth=False) user = client.create_user(user_request) """List users """ users = client.list_users() """Add user to group """ # gu = client.add_group_user(group_id=group['id'], user_id=user['id']) # print json.dumps(gu, indent=4) """List group users """ gus = client.list_group_users(group_id=group['id']) """Delete group """ response = client.delete_group(group['id']) """Delete user """ response = client.delete_user(user['id']) """List all resources """ # listing all resources under an admin user may take a while resources = client.list_resources() """List ipblock resources """ ipblock_resources = client.list_resources(resource_type='ipblock') profitbricks-sdk-python-4.1.3/examples/volume_example.py000066400000000000000000000044231326076220500235360ustar00rootroot00000000000000#!/usr/bin/python3 # Copyright 2015-2017 ProfitBricks GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Create volume """ from profitbricks.client import ProfitBricksService, Volume datacenter_id = '700e1cab-99b2-4c30-ba8c-1d273ddba022' client = ProfitBricksService( username='username', password='password') i = Volume( name='Explicitly created volume', size=56, image='', bus='VIRTIO') response = client.create_volume( datacenter_id=datacenter_id, volume=i) """Create snapshot """ from profitbricks.client import ProfitBricksService # noqa datacenter_id = '700e1cab-99b2-4c30-ba8c-1d273ddba022' volume_id = '700e1cab-99b2-4c30-ba8c-1d273ddba025' client = ProfitBricksService( username='username', password='password') volume = client.create_snapshot( datacenter_id=datacenter_id, volume_id=volume_id, name='', description='') """Restore Snapshot """ from profitbricks.client import ProfitBricksService # noqa datacenter_id = '700e1cab-99b2-4c30-ba8c-1d273ddba022' volume_id = '700e1cab-99b2-4c30-ba8c-1d273ddba025' snapshot_id = '7df81087-5835-41c6-a10b-3e098593bba4' client = ProfitBricksService( username='username', password='password') response = client.restore_snapshot( datacenter_id=datacenter_id, volume_id=volume_id, snapshot_id=snapshot_id) """Update Volume """ from profitbricks.client import ProfitBricksService # noqa datacenter_id = '700e1cab-99b2-4c30-ba8c-1d273ddba022' volume_id = '700e1cab-99b2-4c30-ba8c-1d273ddba025' client = ProfitBricksService( username='username', password='password') volume = client.update_volume( datacenter_id=datacenter_id, volume_id=volume_id, size=100, name='Resized storage to 100 GB') profitbricks-sdk-python-4.1.3/profitbricks/000077500000000000000000000000001326076220500210225ustar00rootroot00000000000000profitbricks-sdk-python-4.1.3/profitbricks/__init__.py000066400000000000000000000013351326076220500231350ustar00rootroot00000000000000# Copyright 2015-2017 ProfitBricks GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ProfitBricks API Client Library for Python""" __version__ = '4.1.2' API_HOST = 'https://api.profitbricks.com/cloudapi/v4' API_VERSION = '4.0' profitbricks-sdk-python-4.1.3/profitbricks/client.py000066400000000000000000002763521326076220500226710ustar00rootroot00000000000000# Copyright 2015-2017 ProfitBricks GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import getpass import json import logging import os import re import time import requests import six try: import configparser except ImportError: import ConfigParser as configparser from six.moves.urllib.parse import urlencode try: import keyring HAS_KEYRING = True except ImportError: HAS_KEYRING = False from profitbricks import ( API_HOST, __version__ ) from profitbricks.errors import ( PBNotAuthorizedError, PBNotFoundError, PBValidationError, PBRateLimitExceededError, PBError, PBFailedRequest, PBTimeoutError, ) from .utils import ask, find_item_by_name _LIBRARY_NAME = "profitbricks-sdk-python" # ProfitBricks Object Classes class ProfitBricksService(object): """ ProfitBricksClient Base Class """ def __init__(self, username=None, password=None, host_base=API_HOST, host_cert=None, ssl_verify=True, headers=None, client_user_agent=None, use_config=True, use_keyring=HAS_KEYRING, config_filename=None): if headers is None: headers = dict() self._config = None self._config_filename = None self.keyring_identificator = '%s (%s)' % (re.sub("/v\d+$", '', host_base), _LIBRARY_NAME) self.host_base = host_base self.host_cert = host_cert self.verify = ssl_verify self.headers = headers self.username = self._get_username(username, use_config, config_filename) self.password = self._get_password(password, use_config, config_filename, use_keyring) self.user_agent = '{}/{}'.format(_LIBRARY_NAME, __version__) if client_user_agent: self.user_agent = client_user_agent + ' ' + self.user_agent def _read_config(self, filename=None): """ Read the user configuration """ if filename: self._config_filename = filename else: try: import appdirs except ImportError: raise Exception("Missing dependency for determining config path. Please install " "the 'appdirs' Python module.") self._config_filename = appdirs.user_config_dir(_LIBRARY_NAME, "ProfitBricks") + ".ini" if not self._config: self._config = configparser.ConfigParser() self._config.optionxform = str self._config.read(self._config_filename) def _save_config(self, filename=None): """ Save the given user configuration. """ if filename is None: filename = self._config_filename parent_path = os.path.dirname(filename) if not os.path.isdir(parent_path): os.makedirs(parent_path) with open(filename, "w") as configfile: self._config.write(configfile) def _get_username(self, username=None, use_config=True, config_filename=None): """Determine the username If a username is given, this name is used. Otherwise the configuration file will be consulted if `use_config` is set to True. The user is asked for the username if the username is not available. Then the username is stored in the configuration file. :param username: Username (used directly if given) :type username: ``str`` :param use_config: Whether to read username from configuration file :type use_config: ``bool`` :param config_filename: Path to the configuration file :type config_filename: ``str`` """ if not username and use_config: if self._config is None: self._read_config(config_filename) username = self._config.get("credentials", "username", fallback=None) if not username: username = input("Please enter your username: ").strip() while not username: username = input("No username specified. Please enter your username: ").strip() if 'credendials' not in self._config: self._config.add_section('credentials') self._config.set("credentials", "username", username) self._save_config() return username def _get_password(self, password, use_config=True, config_filename=None, use_keyring=HAS_KEYRING): """ Determine the user password If the password is given, this password is used. Otherwise this function will try to get the password from the user's keyring if `use_keyring` is set to True. :param username: Username (used directly if given) :type username: ``str`` :param use_config: Whether to read username from configuration file :type use_config: ``bool`` :param config_filename: Path to the configuration file :type config_filename: ``str`` """ if not password and use_config: if self._config is None: self._read_config(config_filename) password = self._config.get("credentials", "password", fallback=None) if not password and use_keyring: logger = logging.getLogger(__name__) question = ("Please enter your password for {} on {}: ".format(self.username, self.host_base)) if HAS_KEYRING: password = keyring.get_password(self.keyring_identificator, self.username) if password is None: password = getpass.getpass(question) try: keyring.set_password(self.keyring_identificator, self.username, password) except keyring.errors.PasswordSetError as error: logger.warning("Storing password in keyring '%s' failed: %s", self.keyring_identificator, error) else: logger.warning("Install the 'keyring' Python module to store your password " "securely in your keyring!") password = self._config.get("credentials", "password", fallback=None) if password is None: password = getpass.getpass(question) store_plaintext_passwords = self._config.get( "preferences", "store-plaintext-passwords", fallback=None) if store_plaintext_passwords != "no": question = ("Do you want to store your password in plain text in " + self._config_filename()) answer = ask(question, ["yes", "no", "never"], "no") if answer == "yes": self._config.set("credentials", "password", password) self._save_config() elif answer == "never": if "preferences" not in self._config: self._config.add_section("preferences") self._config.set("preferences", "store-plaintext-passwords", "no") self._save_config() return password # Contract Resources Functions def list_contracts(self, depth=1): """ Retrieves information about the resource limits for a particular contract and the current resource usage. """ response = self._perform_request('/contracts?depth=' + str(depth)) return response # Data Center Functions def get_datacenter(self, datacenter_id, depth=1): """ Retrieves a data center by its ID. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param depth: The depth of the response data. :type depth: ``int`` """ response = self._perform_request( '/datacenters/%s?depth=%s' % (datacenter_id, str(depth))) return response def get_datacenter_by_name(self, name, depth=1): """ Retrieves a data center by its name. Either returns the data center response or raises an Exception if no or more than one data center was found with the name. The search for the name is done in this relaxing way: - exact name match - case-insentive name match - data center starts with the name - data center starts with the name (case insensitive) - name appears in the data center name - name appears in the data center name (case insensitive) :param name: The name of the data center. :type name: ``str`` :param depth: The depth of the response data. :type depth: ``int`` """ all_data_centers = self.list_datacenters(depth=depth)['items'] data_center = find_item_by_name(all_data_centers, lambda i: i['properties']['name'], name) if len(data_center) == 0: raise NameError("No data center found with name " "containing '{name}'.".format(name=name)) if len(data_center) > 1: raise NameError("Found {n} data centers with the name '{name}': {names}".format( n=len(data_center), name=name, names=", ".join(d['properties']['name'] for d in data_center) )) return data_center[0] def list_datacenters(self, depth=1): """ Retrieves a list of all data centers. """ response = self._perform_request('/datacenters?depth=' + str(depth)) return response def delete_datacenter(self, datacenter_id): """ Removes the data center and all its components such as servers, NICs, load balancers, volumes. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` """ response = self._perform_request( url='/datacenters/%s' % (datacenter_id), method='DELETE') return response def create_datacenter(self, datacenter): """ Creates a data center -- both simple and complex are supported. """ server_items = [] volume_items = [] lan_items = [] loadbalancer_items = [] entities = dict() properties = { "name": datacenter.name } # Omit 'location', if not provided, to receive # a meaningful error message. if datacenter.location: properties['location'] = datacenter.location # Optional Properties if datacenter.description: properties['description'] = datacenter.description # Servers if len(datacenter.servers) > 0: for server in datacenter.servers: server_items.append(self._create_server_dict(server)) servers = { "items": server_items } server_entities = { "servers": servers } entities.update(server_entities) # Volumes if len(datacenter.volumes) > 0: for volume in datacenter.volumes: volume_items.append(self._create_volume_dict(volume)) volumes = { "items": volume_items } volume_entities = { "volumes": volumes } entities.update(volume_entities) # Load Balancers if len(datacenter.loadbalancers) > 0: for loadbalancer in datacenter.loadbalancers: loadbalancer_items.append( self._create_loadbalancer_dict( loadbalancer ) ) loadbalancers = { "items": loadbalancer_items } loadbalancer_entities = { "loadbalancers": loadbalancers } entities.update(loadbalancer_entities) # LANs if len(datacenter.lans) > 0: for lan in datacenter.lans: lan_items.append( self._create_lan_dict(lan) ) lans = { "items": lan_items } lan_entities = { "lans": lans } entities.update(lan_entities) if len(entities) == 0: raw = { "properties": properties, } else: raw = { "properties": properties, "entities": entities } data = json.dumps(raw) response = self._perform_request( url='/datacenters', method='POST', data=data) return response def update_datacenter(self, datacenter_id, **kwargs): """ Updates a data center with the parameters provided. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` """ data = {} for attr in kwargs.keys(): data[self._underscore_to_camelcase(attr)] = kwargs[attr] response = self._perform_request( url='/datacenters/%s' % ( datacenter_id), method='PATCH', data=json.dumps(data)) return response # Firewall Rule Functions def get_firewall_rule(self, datacenter_id, server_id, nic_id, firewall_rule_id): """ Retrieves a single firewall rule by ID. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param server_id: The unique ID of the server. :type server_id: ``str`` :param nic_id: The unique ID of the NIC. :type nic_id: ``str`` :param firewall_rule_id: The unique ID of the firewall rule. :type firewall_rule_id: ``str`` """ response = self._perform_request( '/datacenters/%s/servers/%s/nics/%s/firewallrules/%s' % ( datacenter_id, server_id, nic_id, firewall_rule_id)) return response def get_firewall_rules(self, datacenter_id, server_id, nic_id, depth=1): """ Retrieves a list of firewall rules available in the account. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param server_id: The unique ID of the server. :type server_id: ``str`` :param nic_id: The unique ID of the NIC. :type nic_id: ``str`` :param depth: The depth of the response data. :type depth: ``int`` """ response = self._perform_request( '/datacenters/%s/servers/%s/nics/%s/firewallrules?depth=%s' % ( datacenter_id, server_id, nic_id, str(depth))) return response def delete_firewall_rule(self, datacenter_id, server_id, nic_id, firewall_rule_id): """ Removes a firewall rule from the NIC. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param server_id: The unique ID of the server. :type server_id: ``str`` :param nic_id: The unique ID of the NIC. :type nic_id: ``str`` :param firewall_rule_id: The unique ID of the firewall rule. :type firewall_rule_id: ``str`` """ response = self._perform_request( url='/datacenters/%s/servers/%s/nics/%s/firewallrules/%s' % ( datacenter_id, server_id, nic_id, firewall_rule_id), method='DELETE') return response def create_firewall_rule(self, datacenter_id, server_id, nic_id, firewall_rule): """ Creates a firewall rule on the specified NIC and server. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param server_id: The unique ID of the server. :type server_id: ``str`` :param nic_id: The unique ID of the NIC. :type nic_id: ``str`` :param firewall_rule: A firewall rule dict. :type firewall_rule: ``dict`` """ properties = { "name": firewall_rule.name } if firewall_rule.protocol: properties['protocol'] = firewall_rule.protocol # Optional Properties if firewall_rule.source_mac: properties['sourceMac'] = firewall_rule.source_mac if firewall_rule.source_ip: properties['sourceIp'] = firewall_rule.source_ip if firewall_rule.target_ip: properties['targetIp'] = firewall_rule.target_ip if firewall_rule.port_range_start: properties['portRangeStart'] = firewall_rule.port_range_start if firewall_rule.port_range_end: properties['portRangeEnd'] = firewall_rule.port_range_end if firewall_rule.icmp_type: properties['icmpType'] = firewall_rule.icmp_type if firewall_rule.icmp_code: properties['icmpCode'] = firewall_rule.icmp_code data = { "properties": properties } response = self._perform_request( url='/datacenters/%s/servers/%s/nics/%s/firewallrules' % ( datacenter_id, server_id, nic_id), method='POST', data=json.dumps(data)) return response def update_firewall_rule(self, datacenter_id, server_id, nic_id, firewall_rule_id, **kwargs): """ Updates a firewall rule. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param server_id: The unique ID of the server. :type server_id: ``str`` :param nic_id: The unique ID of the NIC. :type nic_id: ``str`` :param firewall_rule_id: The unique ID of the firewall rule. :type firewall_rule_id: ``str`` """ data = {} for attr in kwargs.keys(): data[self._underscore_to_camelcase(attr)] = kwargs[attr] if attr == 'source_mac': data['sourceMac'] = kwargs[attr] elif attr == 'source_ip': data['sourceIp'] = kwargs[attr] elif attr == 'target_ip': data['targetIp'] = kwargs[attr] elif attr == 'port_range_start': data['portRangeStart'] = kwargs[attr] elif attr == 'port_range_end': data['portRangeEnd'] = kwargs[attr] elif attr == 'icmp_type': data['icmpType'] = kwargs[attr] elif attr == 'icmp_code': data['icmpCode'] = kwargs[attr] else: data[self._underscore_to_camelcase(attr)] = kwargs[attr] response = self._perform_request( url='/datacenters/%s/servers/%s/nics/%s/firewallrules/%s' % ( datacenter_id, server_id, nic_id, firewall_rule_id), method='PATCH', data=json.dumps(data)) return response # Image Functions def get_image(self, image_id): """ Retrieves a single image by ID. :param image_id: The unique ID of the image. :type image_id: ``str`` """ response = self._perform_request('/images/%s' % image_id) return response def list_images(self, depth=1): """ Retrieves a list of images available in the data center. :param depth: The depth of the response data. :type depth: ``int`` """ response = self._perform_request('/images?depth=' + str(depth)) return response def delete_image(self, image_id): """ Removes only user created images. :param image_id: The unique ID of the image. :type image_id: ``str`` """ response = self._perform_request(url='/images/' + image_id, method='DELETE') return response def update_image(self, image_id, **kwargs): """ Replace all properties of an image. """ data = {} for attr in kwargs.keys(): data[self._underscore_to_camelcase(attr)] = kwargs[attr] response = self._perform_request(url='/images/' + image_id, method='PATCH', data=json.dumps(data)) return response # IP block Functions def get_ipblock(self, ipblock_id): """ Retrieves a single IP block by ID. :param ipblock_id: The unique ID of the IP block. :type ipblock_id: ``str`` """ response = self._perform_request('/ipblocks/%s' % ipblock_id) return response def list_ipblocks(self, depth=1): """ Retrieves a list of IP blocks available in the account. """ response = self._perform_request('/ipblocks?depth=%s' % str(depth)) return response def delete_ipblock(self, ipblock_id): """ Removes a single IP block from your account. :param ipblock_id: The unique ID of the IP block. :type ipblock_id: ``str`` """ response = self._perform_request( url='/ipblocks/' + ipblock_id, method='DELETE') return response def reserve_ipblock(self, ipblock): """ Reserves an IP block within your account. """ properties = { "name": ipblock.name } if ipblock.location: properties['location'] = ipblock.location if ipblock.size: properties['size'] = str(ipblock.size) raw = { "properties": properties, } data = self._underscore_to_camelcase(json.dumps(raw)) response = self._perform_request( url='/ipblocks', method='POST', data=data) return response # LAN Functions def get_lan(self, datacenter_id, lan_id, depth=1): """ Retrieves a single LAN by ID. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param lan_id: The unique ID of the LAN. :type lan_id: ``str`` :param depth: The depth of the response data. :type depth: ``int`` """ response = self._perform_request( '/datacenters/%s/lans/%s?depth=%s' % ( datacenter_id, lan_id, str(depth))) return response def list_lans(self, datacenter_id, depth=1): """ Retrieves a list of LANs available in the account. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param depth: The depth of the response data. :type depth: ``int`` """ response = self._perform_request( '/datacenters/%s/lans?depth=%s' % ( datacenter_id, str(depth))) return response def delete_lan(self, datacenter_id, lan_id): """ Removes a LAN from the data center. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param lan_id: The unique ID of the LAN. :type lan_id: ``str`` """ response = self._perform_request( url='/datacenters/%s/lans/%s' % ( datacenter_id, lan_id), method='DELETE') return response def create_lan(self, datacenter_id, lan): """ Creates a LAN in the data center. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param lan: The LAN object to be created. :type lan: ``dict`` """ data = self._underscore_to_camelcase( json.dumps( self._create_lan_dict(lan) ) ) response = self._perform_request( url='/datacenters/%s/lans' % datacenter_id, method='POST', data=data) return response def update_lan(self, datacenter_id, lan_id, name=None, public=None, ip_failover=None): """ Updates a LAN :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param lan_id: The unique ID of the LAN. :type lan_id: ``str`` :param name: The new name of the LAN. :type name: ``str`` :param public: Indicates if the LAN is public. :type public: ``bool`` :param ip_failover: A list of IP fail-over dicts. :type ip_failover: ``list`` """ data = {} if name: data['name'] = name if public is not None: data['public'] = public if ip_failover: data['ipFailover'] = ip_failover response = self._perform_request( url='/datacenters/%s/lans/%s' % (datacenter_id, lan_id), method='PATCH', data=json.dumps(data)) return response def get_lan_members(self, datacenter_id, lan_id, depth=1): """ Retrieves the list of NICs that are part of the LAN. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param lan_id: The unique ID of the LAN. :type lan_id: ``str`` """ response = self._perform_request( '/datacenters/%s/lans/%s/nics?depth=%s' % ( datacenter_id, lan_id, str(depth))) return response # Load balancer Functions def get_loadbalancer(self, datacenter_id, loadbalancer_id): """ Retrieves a single load balancer by ID. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param loadbalancer_id: The unique ID of the load balancer. :type loadbalancer_id: ``str`` """ response = self._perform_request( '/datacenters/%s/loadbalancers/%s' % ( datacenter_id, loadbalancer_id)) return response def list_loadbalancers(self, datacenter_id, depth=1): """ Retrieves a list of load balancers in the data center. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param depth: The depth of the response data. :type depth: ``int`` """ response = self._perform_request( '/datacenters/%s/loadbalancers?depth=%s' % ( datacenter_id, str(depth))) return response def delete_loadbalancer(self, datacenter_id, loadbalancer_id): """ Removes the load balancer from the data center. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param loadbalancer_id: The unique ID of the load balancer. :type loadbalancer_id: ``str`` """ response = self._perform_request( url='/datacenters/%s/loadbalancers/%s' % ( datacenter_id, loadbalancer_id), method='DELETE') return response def create_loadbalancer(self, datacenter_id, loadbalancer): """ Creates a load balancer within the specified data center. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param loadbalancer: The load balancer object to be created. :type loadbalancer: ``dict`` """ data = self._underscore_to_camelcase( json.dumps( self._create_loadbalancer_dict(loadbalancer) ) ) response = self._perform_request( url='/datacenters/%s/loadbalancers' % datacenter_id, method='POST', data=data) return response def update_loadbalancer(self, datacenter_id, loadbalancer_id, **kwargs): """ Updates a load balancer :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param loadbalancer_id: The unique ID of the load balancer. :type loadbalancer_id: ``str`` """ data = {} for attr in kwargs.keys(): data[self._underscore_to_camelcase(attr)] = kwargs[attr] response = self._perform_request( url='/datacenters/%s/loadbalancers/%s' % (datacenter_id, loadbalancer_id), method='PATCH', data=json.dumps(data)) return response def get_loadbalancer_members(self, datacenter_id, loadbalancer_id, depth=1): """ Retrieves the list of NICs that are associated with a load balancer. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param loadbalancer_id: The unique ID of the load balancer. :type loadbalancer_id: ``str`` :param depth: The depth of the response data. :type depth: ``int`` """ response = self._perform_request( '/datacenters/%s/loadbalancers/%s/balancednics?depth=%s' % ( datacenter_id, loadbalancer_id, str(depth))) return response def add_loadbalanced_nics(self, datacenter_id, loadbalancer_id, nic_id): """ Associates a NIC with the given load balancer. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param loadbalancer_id: The unique ID of the load balancer. :type loadbalancer_id: ``str`` :param nic_id: The ID of the NIC. :type nic_id: ``str`` """ data = '{ "id": "' + nic_id + '" }' response = self._perform_request( url='/datacenters/%s/loadbalancers/%s/balancednics' % ( datacenter_id, loadbalancer_id), method='POST', data=data) return response def get_loadbalanced_nic(self, datacenter_id, loadbalancer_id, nic_id, depth=1): """ Gets the properties of a load balanced NIC. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param loadbalancer_id: The unique ID of the load balancer. :type loadbalancer_id: ``str`` :param nic_id: The unique ID of the NIC. :type nic_id: ``str`` :param depth: The depth of the response data. :type depth: ``int`` """ response = self._perform_request( '/datacenters/%s/loadbalancers/%s/balancednics/%s?depth=%s' % ( datacenter_id, loadbalancer_id, nic_id, str(depth))) return response def remove_loadbalanced_nic(self, datacenter_id, loadbalancer_id, nic_id): """ Removes a NIC from the load balancer. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param loadbalancer_id: The unique ID of the load balancer. :type loadbalancer_id: ``str`` :param nic_id: The unique ID of the NIC. :type nic_id: ``str`` """ response = self._perform_request( url='/datacenters/%s/loadbalancers/%s/balancednics/%s' % ( datacenter_id, loadbalancer_id, nic_id), method='DELETE') return response # Location Functions def get_location(self, location_id, depth=0): """ Retrieves a single location by ID. :param location_id: The unique ID of the location. :type location_id: ``str`` """ response = self._perform_request('/locations/%s?depth=%s' % (location_id, depth)) return response def list_locations(self, depth=0): """ Retrieves a list of locations available in the account. """ response = self._perform_request('/locations?depth=%s' % (depth)) return response # NIC Functions def get_nic(self, datacenter_id, server_id, nic_id, depth=1): """ Retrieves a NIC by its ID. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param server_id: The unique ID of the server. :type server_id: ``str`` :param nic_id: The unique ID of the NIC. :type nic_id: ``str`` :param depth: The depth of the response data. :type depth: ``int`` """ response = self._perform_request( '/datacenters/%s/servers/%s/nics/%s?depth=%s' % ( datacenter_id, server_id, nic_id, str(depth))) return response def list_nics(self, datacenter_id, server_id, depth=1): """ Retrieves a list of all NICs bound to the specified server. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param server_id: The unique ID of the server. :type server_id: ``str`` :param depth: The depth of the response data. :type depth: ``int`` """ response = self._perform_request( '/datacenters/%s/servers/%s/nics?depth=%s' % ( datacenter_id, server_id, str(depth))) return response def delete_nic(self, datacenter_id, server_id, nic_id): """ Removes a NIC from the server. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param server_id: The unique ID of the server. :type server_id: ``str`` :param nic_id: The unique ID of the NIC. :type nic_id: ``str`` """ response = self._perform_request( url='/datacenters/%s/servers/%s/nics/%s' % ( datacenter_id, server_id, nic_id), method='DELETE') return response def create_nic(self, datacenter_id, server_id, nic): """ Creates a NIC on the specified server. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param server_id: The unique ID of the server. :type server_id: ``str`` :param nic: A NIC dict. :type nic: ``dict`` """ data = json.dumps(self._create_nic_dict(nic)) response = self._perform_request( url='/datacenters/%s/servers/%s/nics' % ( datacenter_id, server_id), method='POST', data=data) return response def update_nic(self, datacenter_id, server_id, nic_id, **kwargs): """ Updates a NIC with the parameters provided. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param server_id: The unique ID of the server. :type server_id: ``str`` :param nic_id: The unique ID of the NIC. :type nic_id: ``str`` """ data = {} for attr in kwargs.keys(): data[self._underscore_to_camelcase(attr)] = kwargs[attr] response = self._perform_request( url='/datacenters/%s/servers/%s/nics/%s' % ( datacenter_id, server_id, nic_id), method='PATCH', data=json.dumps(data)) return response # Request Functions def get_request(self, request_id, status=False): """ Retrieves a single request by ID. :param request_id: The unique ID of the request. :type request_id: ``str`` :param status: Retreive the full status of the request. :type status: ``bool`` """ if status: response = self._perform_request( '/requests/' + request_id + '/status') else: response = self._perform_request( '/requests/%s' % request_id) return response def list_requests(self, depth=1): """ Retrieves a list of requests available in the account. """ response = self._perform_request( '/requests?depth=%s' % str(depth)) return response # Server Functions def get_server(self, datacenter_id, server_id, depth=1): """ Retrieves a server by its ID. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param server_id: The unique ID of the server. :type server_id: ``str`` :param depth: The depth of the response data. :type depth: ``int`` """ response = self._perform_request( '/datacenters/%s/servers/%s?depth=%s' % ( datacenter_id, server_id, str(depth))) return response def list_servers(self, datacenter_id, depth=1): """ Retrieves a list of all servers bound to the specified data center. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param depth: The depth of the response data. :type depth: ``int`` """ response = self._perform_request( '/datacenters/%s/servers?depth=%s' % (datacenter_id, str(depth))) return response def delete_server(self, datacenter_id, server_id): """ Removes the server from your data center. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param server_id: The unique ID of the server. :type server_id: ``str`` """ response = self._perform_request( url='/datacenters/%s/servers/%s' % ( datacenter_id, server_id), method='DELETE') return response def create_server(self, datacenter_id, server): """ Creates a server within the data center. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param server: A dict of the server to be created. :type server: ``dict`` """ data = json.dumps(self._create_server_dict(server)) response = self._perform_request( url='/datacenters/%s/servers' % (datacenter_id), method='POST', data=data) return response def update_server(self, datacenter_id, server_id, **kwargs): """ Updates a server with the parameters provided. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param server_id: The unique ID of the server. :type server_id: ``str`` """ data = {} for attr in kwargs.keys(): if attr == 'boot_volume': boot_volume_properties = { "id": kwargs[attr] } boot_volume_entities = { "bootVolume": boot_volume_properties } data.update(boot_volume_entities) else: data[self._underscore_to_camelcase(attr)] = kwargs[attr] response = self._perform_request( url='/datacenters/%s/servers/%s' % ( datacenter_id, server_id), method='PATCH', data=json.dumps(data)) return response def get_attached_volumes(self, datacenter_id, server_id, depth=1): """ Retrieves a list of volumes attached to the server. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param server_id: The unique ID of the server. :type server_id: ``str`` :param depth: The depth of the response data. :type depth: ``int`` """ response = self._perform_request( '/datacenters/%s/servers/%s/volumes?depth=%s' % ( datacenter_id, server_id, str(depth))) return response def get_attached_volume(self, datacenter_id, server_id, volume_id): """ Retrieves volume information. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param server_id: The unique ID of the server. :type server_id: ``str`` :param volume_id: The unique ID of the volume. :type volume_id: ``str`` """ response = self._perform_request( '/datacenters/%s/servers/%s/volumes/%s' % ( datacenter_id, server_id, volume_id)) return response def attach_volume(self, datacenter_id, server_id, volume_id): """ Attaches a volume to a server. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param server_id: The unique ID of the server. :type server_id: ``str`` :param volume_id: The unique ID of the volume. :type volume_id: ``str`` """ data = '{ "id": "' + volume_id + '" }' response = self._perform_request( url='/datacenters/%s/servers/%s/volumes' % ( datacenter_id, server_id), method='POST', data=data) return response def detach_volume(self, datacenter_id, server_id, volume_id): """ Detaches a volume from a server. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param server_id: The unique ID of the server. :type server_id: ``str`` :param volume_id: The unique ID of the volume. :type volume_id: ``str`` """ response = self._perform_request( url='/datacenters/%s/servers/%s/volumes/%s' % ( datacenter_id, server_id, volume_id), method='DELETE') return response def get_attached_cdroms(self, datacenter_id, server_id, depth=1): """ Retrieves a list of CDROMs attached to the server. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param server_id: The unique ID of the server. :type server_id: ``str`` :param depth: The depth of the response data. :type depth: ``int`` """ response = self._perform_request( '/datacenters/%s/servers/%s/cdroms?depth=%s' % ( datacenter_id, server_id, str(depth))) return response def get_attached_cdrom(self, datacenter_id, server_id, cdrom_id): """ Retrieves an attached CDROM. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param server_id: The unique ID of the server. :type server_id: ``str`` :param cdrom_id: The unique ID of the CDROM. :type cdrom_id: ``str`` """ response = self._perform_request( '/datacenters/%s/servers/%s/cdroms/%s' % ( datacenter_id, server_id, cdrom_id)) return response def attach_cdrom(self, datacenter_id, server_id, cdrom_id): """ Attaches a CDROM to a server. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param server_id: The unique ID of the server. :type server_id: ``str`` :param cdrom_id: The unique ID of the CDROM. :type cdrom_id: ``str`` """ data = '{ "id": "' + cdrom_id + '" }' response = self._perform_request( url='/datacenters/%s/servers/%s/cdroms' % ( datacenter_id, server_id), method='POST', data=data) return response def detach_cdrom(self, datacenter_id, server_id, cdrom_id): """ Detaches a volume from a server. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param server_id: The unique ID of the server. :type server_id: ``str`` :param cdrom_id: The unique ID of the CDROM. :type cdrom_id: ``str`` """ response = self._perform_request( url='/datacenters/%s/servers/%s/cdroms/%s' % ( datacenter_id, server_id, cdrom_id), method='DELETE') return response def start_server(self, datacenter_id, server_id): """ Starts the server. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param server_id: The unique ID of the server. :type server_id: ``str`` """ response = self._perform_request( url='/datacenters/%s/servers/%s/start' % ( datacenter_id, server_id), method='POST-ACTION') return response def stop_server(self, datacenter_id, server_id): """ Stops the server. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param server_id: The unique ID of the server. :type server_id: ``str`` """ response = self._perform_request( url='/datacenters/%s/servers/%s/stop' % ( datacenter_id, server_id), method='POST-ACTION') return response def reboot_server(self, datacenter_id, server_id): """ Reboots the server. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param server_id: The unique ID of the server. :type server_id: ``str`` """ response = self._perform_request( url='/datacenters/%s/servers/%s/reboot' % ( datacenter_id, server_id), method='POST-ACTION') return response # Snapshot Functions def get_snapshot(self, snapshot_id): """ Retrieves a single snapshot by ID. :param snapshot_id: The unique ID of the snapshot. :type snapshot_id: ``str`` """ response = self._perform_request('/snapshots/%s' % snapshot_id) return response def list_snapshots(self, depth=1): """ Retrieves a list of snapshots available in the account. """ response = self._perform_request( '/snapshots?depth=%s' % str(depth)) return response def delete_snapshot(self, snapshot_id): """ Removes a snapshot from your account. :param snapshot_id: The unique ID of the snapshot. :type snapshot_id: ``str`` """ response = self._perform_request( url='/snapshots/' + snapshot_id, method='DELETE') return response def update_snapshot(self, snapshot_id, **kwargs): """ Removes a snapshot from your account. :param snapshot_id: The unique ID of the snapshot. :type snapshot_id: ``str`` """ data = {} for attr in kwargs.keys(): data[self._underscore_to_camelcase(attr)] = kwargs[attr] response = self._perform_request( url='/snapshots/' + snapshot_id, method='PATCH', data=json.dumps(data)) return response def create_snapshot(self, datacenter_id, volume_id, name=None, description=None): """ Creates a snapshot of the specified volume. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param volume_id: The unique ID of the volume. :type volume_id: ``str`` :param name: The name given to the volume. :type name: ``str`` :param description: The description given to the volume. :type description: ``str`` """ data = {'name': name, 'description': description} response = self._perform_request( '/datacenters/%s/volumes/%s/create-snapshot' % ( datacenter_id, volume_id), method='POST-ACTION-JSON', data=urlencode(data)) return response def restore_snapshot(self, datacenter_id, volume_id, snapshot_id): """ Restores a snapshot to the specified volume. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param volume_id: The unique ID of the volume. :type volume_id: ``str`` :param snapshot_id: The unique ID of the snapshot. :type snapshot_id: ``str`` """ data = {'snapshotId': snapshot_id} response = self._perform_request( url='/datacenters/%s/volumes/%s/restore-snapshot' % ( datacenter_id, volume_id), method='POST-ACTION', data=urlencode(data)) return response def remove_snapshot(self, snapshot_id): """ Removes a snapshot. :param snapshot_id: The ID of the snapshot you wish to remove. :type snapshot_id: ``str`` """ response = self._perform_request( url='/snapshots/' + snapshot_id, method='DELETE') return response # User Management Functions def list_groups(self, depth=1): """ Retrieves a list of all groups. :param depth: The depth of the response data. :type depth: ``int`` """ response = self._perform_request('/um/groups?depth=' + str(depth)) return response def get_group(self, group_id, depth=1): """ Retrieves a single group by ID. :param group_id: The unique ID of the group. :type group_id: ``str`` :param depth: The depth of the response data. :type depth: ``int`` """ response = self._perform_request( '/um/groups/%s?depth=%s' % (group_id, str(depth))) return response def create_group(self, group): """ Creates a new group and set group privileges. :param group: The group object to be created. :type group: ``dict`` """ data = json.dumps(self._create_group_dict(group)) response = self._perform_request( url='/um/groups', method='POST', data=data) return response def update_group(self, group_id, **kwargs): """ Updates a group. :param group_id: The unique ID of the group. :type group_id: ``str`` """ properties = {} # make the key camel-case transformable if 'create_datacenter' in kwargs: kwargs['create_data_center'] = kwargs.pop('create_datacenter') for attr in kwargs.keys(): properties[self._underscore_to_camelcase(attr)] = kwargs[attr] data = { "properties": properties } response = self._perform_request( url='/um/groups/%s' % group_id, method='PUT', data=json.dumps(data)) return response def delete_group(self, group_id): """ Removes a group. :param group_id: The unique ID of the group. :type group_id: ``str`` """ response = self._perform_request( url='/um/groups/%s' % group_id, method='DELETE') return response def list_shares(self, group_id, depth=1): """ Retrieves a list of all shares though a group. :param group_id: The unique ID of the group. :type group_id: ``str`` :param depth: The depth of the response data. :type depth: ``int`` """ response = self._perform_request( '/um/groups/%s/shares?depth=%s' % (group_id, str(depth))) return response def get_share(self, group_id, resource_id, depth=1): """ Retrieves a specific resource share available to a group. :param group_id: The unique ID of the group. :type group_id: ``str`` :param resource_id: The unique ID of the resource. :type resource_id: ``str`` :param depth: The depth of the response data. :type depth: ``int`` """ response = self._perform_request( '/um/groups/%s/shares/%s?depth=%s' % (group_id, resource_id, str(depth))) return response def add_share(self, group_id, resource_id, **kwargs): """ Shares a resource through a group. :param group_id: The unique ID of the group. :type group_id: ``str`` :param resource_id: The unique ID of the resource. :type resource_id: ``str`` """ properties = {} for attr in kwargs.keys(): properties[self._underscore_to_camelcase(attr)] = kwargs[attr] data = { "properties": properties } response = self._perform_request( url='/um/groups/%s/shares/%s' % (group_id, resource_id), method='POST', data=json.dumps(data)) return response def update_share(self, group_id, resource_id, **kwargs): """ Updates the permissions of a group for a resource share. :param group_id: The unique ID of the group. :type group_id: ``str`` :param resource_id: The unique ID of the resource. :type resource_id: ``str`` """ properties = {} for attr in kwargs.keys(): properties[self._underscore_to_camelcase(attr)] = kwargs[attr] data = { "properties": properties } response = self._perform_request( url='/um/groups/%s/shares/%s' % (group_id, resource_id), method='PUT', data=json.dumps(data)) return response def delete_share(self, group_id, resource_id): """ Removes a resource share from a group. :param group_id: The unique ID of the group. :type group_id: ``str`` :param resource_id: The unique ID of the resource. :type resource_id: ``str`` """ response = self._perform_request( url='/um/groups/%s/shares/%s' % (group_id, resource_id), method='DELETE') return response def list_users(self, depth=1): """ Retrieves a list of all users. :param depth: The depth of the response data. :type depth: ``int`` """ response = self._perform_request('/um/users?depth=' + str(depth)) return response def get_user(self, user_id, depth=1): """ Retrieves a single user by ID. :param user_id: The unique ID of the user. :type user_id: ``str`` :param depth: The depth of the response data. :type depth: ``int`` """ response = self._perform_request( '/um/users/%s?depth=%s' % (user_id, str(depth))) return response def create_user(self, user): """ Creates a new user. :param user: The user object to be created. :type user: ``dict`` """ data = self._create_user_dict(user=user) response = self._perform_request( url='/um/users', method='POST', data=json.dumps(data)) return response def update_user(self, user_id, **kwargs): """ Updates a user. :param user_id: The unique ID of the user. :type user_id: ``str`` """ properties = {} for attr in kwargs.keys(): properties[self._underscore_to_camelcase(attr)] = kwargs[attr] data = { "properties": properties } response = self._perform_request( url='/um/users/%s' % user_id, method='PUT', data=json.dumps(data)) return response def delete_user(self, user_id): """ Removes a user. :param user_id: The unique ID of the user. :type user_id: ``str`` """ response = self._perform_request( url='/um/users/%s' % user_id, method='DELETE') return response def list_group_users(self, group_id, depth=1): """ Retrieves a list of all users that are members of a particular group. :param group_id: The unique ID of the group. :type group_id: ``str`` :param depth: The depth of the response data. :type depth: ``int`` """ response = self._perform_request( '/um/groups/%s/users?depth=%s' % (group_id, str(depth))) return response def add_group_user(self, group_id, user_id): """ Adds an existing user to a group. :param group_id: The unique ID of the group. :type group_id: ``str`` :param user_id: The unique ID of the user. :type user_id: ``str`` """ data = { "id": user_id } response = self._perform_request( url='/um/groups/%s/users' % group_id, method='POST', data=json.dumps(data)) return response def remove_group_user(self, group_id, user_id): """ Removes a user from a group. :param group_id: The unique ID of the group. :type group_id: ``str`` :param user_id: The unique ID of the user. :type user_id: ``str`` """ response = self._perform_request( url='/um/groups/%s/users/%s' % (group_id, user_id), method='DELETE') return response def list_resources(self, resource_type=None, depth=1): """ Retrieves a list of all resources. :param resource_type: The resource type: datacenter, image, snapshot or ipblock. Default is None, i.e., all resources are listed. :type resource_type: ``str`` :param depth: The depth of the response data. :type depth: ``int`` """ if resource_type is not None: response = self._perform_request( '/um/resources/%s?depth=%s' % (resource_type, str(depth))) else: response = self._perform_request( '/um/resources?depth=' + str(depth)) return response def get_resource(self, resource_type, resource_id, depth=1): """ Retrieves a single resource of a particular type. :param resource_type: The resource type: datacenter, image, snapshot or ipblock. :type resource_type: ``str`` :param resource_id: The unique ID of the resource. :type resource_id: ``str`` :param depth: The depth of the response data. :type depth: ``int`` """ response = self._perform_request( '/um/resources/%s/%s?depth=%s' % ( resource_type, resource_id, str(depth))) return response # Volume Functions def get_volume(self, datacenter_id, volume_id): """ Retrieves a single volume by ID. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param volume_id: The unique ID of the volume. :type volume_id: ``str`` """ response = self._perform_request( '/datacenters/%s/volumes/%s' % (datacenter_id, volume_id)) return response def list_volumes(self, datacenter_id, depth=1): """ Retrieves a list of volumes in the data center. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param depth: The depth of the response data. :type depth: ``int`` """ response = self._perform_request( '/datacenters/%s/volumes?depth=%s' % (datacenter_id, str(depth))) return response def delete_volume(self, datacenter_id, volume_id): """ Removes a volume from the data center. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param volume_id: The unique ID of the volume. :type volume_id: ``str`` """ response = self._perform_request( url='/datacenters/%s/volumes/%s' % ( datacenter_id, volume_id), method='DELETE') return response def create_volume(self, datacenter_id, volume): """ Creates a volume within the specified data center. :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param volume: A volume dict. :type volume: ``dict`` """ data = (json.dumps(self._create_volume_dict(volume))) response = self._perform_request( url='/datacenters/%s/volumes' % datacenter_id, method='POST', data=data) return response def update_volume(self, datacenter_id, volume_id, **kwargs): """ Updates a volume :param datacenter_id: The unique ID of the data center. :type datacenter_id: ``str`` :param volume_id: The unique ID of the volume. :type volume_id: ``str`` """ data = {} for attr in kwargs.keys(): data[self._underscore_to_camelcase(attr)] = kwargs[attr] response = self._perform_request( url='/datacenters/%s/volumes/%s' % ( datacenter_id, volume_id), method='PATCH', data=json.dumps(data)) return response def wait_for_completion(self, response, timeout=3600, initial_wait=5, scaleup=10): """ Poll resource request status until resource is provisioned. :param response: A response dict, which needs to have a 'requestId' item. :type response: ``dict`` :param timeout: Maximum waiting time in seconds. None means infinite waiting time. :type timeout: ``int`` :param initial_wait: Initial polling interval in seconds. :type initial_wait: ``int`` :param scaleup: Double polling interval every scaleup steps, which will be doubled. :type scaleup: ``int`` """ if not response: return logger = logging.getLogger(__name__) wait_period = initial_wait next_increase = time.time() + wait_period * scaleup if timeout: timeout = time.time() + timeout while True: request = self.get_request(request_id=response['requestId'], status=True) if request['metadata']['status'] == 'DONE': break elif request['metadata']['status'] == 'FAILED': raise PBFailedRequest( 'Request {0} failed to complete: {1}'.format( response['requestId'], request['metadata']['message']), response['requestId'] ) current_time = time.time() if timeout and current_time > timeout: raise PBTimeoutError('Timed out waiting for request {0}.'.format( response['requestId']), response['requestId']) if current_time > next_increase: wait_period *= 2 next_increase = time.time() + wait_period * scaleup scaleup *= 2 logger.info("Request %s is in state '%s'. Sleeping for %i seconds...", response['requestId'], request['metadata']['status'], wait_period) time.sleep(wait_period) def _wrapped_request(self, method, url, params=None, data=None, headers=None, cookies=None, files=None, auth=None, timeout=None, allow_redirects=True, proxies=None, hooks=None, stream=None): headers.update(self.headers) session = requests.Session() return session.request(method, url, params, data, headers, cookies, files, auth, timeout, allow_redirects, proxies, hooks, stream, self.verify, self.host_cert) def _perform_request(self, url, method='GET', data=None, headers=None): if headers is None: headers = dict() auth = (self.username, self.password) url = self._build_url(url) headers.update({'User-Agent': self.user_agent}) if method == 'POST' or method == 'PUT': response = self._wrapped_request(method, url, auth=auth, data=data, headers=headers) headers.update({'Content-Type': 'application/json'}) elif method == 'POST-ACTION-JSON' or method == 'POST-ACTION': headers.update({'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}) response = self._wrapped_request('POST', url, auth=auth, data=data, headers=headers) if response.status_code == 202 and method == 'POST-ACTION': return True elif response.status_code == 401: raise response.raise_for_status() elif method == 'PATCH': headers.update({'Content-Type': 'application/json'}) response = self._wrapped_request(method, url, auth=auth, data=data, headers=headers) else: headers.update({'Content-Type': 'application/json'}) response = self._wrapped_request(method, url, auth=auth, params=data, headers=headers) if method == 'DELETE': if response.status_code == 202: return True try: if not response.ok: err = response.json() code = err['httpStatus'] msg = err['messages'] if response.status_code == 401: raise PBNotAuthorizedError(code, msg, url) if response.status_code == 404: raise PBNotFoundError(code, msg, url) if response.status_code == 422: raise PBValidationError(code, msg, url) if response.status_code == 429: raise PBRateLimitExceededError(code, msg, url) else: raise PBError(code, msg, url) except ValueError: raise Exception('Failed to parse the response', response.text) json_response = response.json() if 'location' in response.headers: json_response['requestId'] = self._request_id(response.headers) return json_response @staticmethod def _request_id(headers): # The request URL has currently the format: # {host_base}/requests/{request ID}/status # Thus search for a UUID. match = re.search('/requests/([-A-Fa-f0-9]+)/', headers['location']) if match: return match.group(1) else: raise Exception("Failed to extract request ID from response " "header 'location': '{location}'".format(location=headers['location'])) def _build_url(self, uri): url = self.host_base + uri return url @staticmethod def _b(s, encoding='utf-8'): """ Returns the given string as a string of bytes. That means in Python2 as a str object, and in Python3 as a bytes object. Raises a TypeError, if it cannot be converted. """ if six.PY2: # This is Python2 if isinstance(s, str): return s elif isinstance(s, unicode): # noqa, pylint: disable=undefined-variable return s.encode(encoding) else: # And this is Python3 if isinstance(s, bytes): return s elif isinstance(s, str): return s.encode(encoding) raise TypeError("Invalid argument %r for _b()" % (s,)) @staticmethod def _underscore_to_camelcase(value): """ Convert Python snake case back to mixed case. """ def camelcase(): yield str.lower while True: yield str.capitalize c = camelcase() return "".join(next(c)(x) if x else '_' for x in value.split("_")) @staticmethod def _create_lan_dict(lan): items = [] entities = dict() properties = { "name": lan.name } # Optional Properties if lan.public is not None: properties['public'] = str(lan.public).lower() if len(lan.nics) > 0: for nic in lan.nics: nics_properties = { "id": nic } items.append(nics_properties) item_entities = { "items": items } nics_entities = { "nics": item_entities } entities.update(nics_entities) if len(entities) == 0: raw = { "properties": properties, } else: raw = { "properties": properties, "entities": entities } return raw @staticmethod def _create_loadbalancer_dict(loadbalancer): items = [] entities = dict() properties = {} if loadbalancer.name: properties['name'] = loadbalancer.name # Optional Properties if loadbalancer.ip: properties['ip'] = loadbalancer.ip if loadbalancer.dhcp is not None: properties['dhcp'] = str(loadbalancer.dhcp).lower() if len(loadbalancer.balancednics) > 0: for nic in loadbalancer.balancednics: balancednic_properties = { "id": nic } items.append(balancednic_properties) item_entities = { "items": items } balancednics_entities = { "balancednics": item_entities } entities.update(balancednics_entities) if len(loadbalancer.balancednics) == 0: raw = { "properties": properties, } else: raw = { "properties": properties, "entities": entities } return raw def _create_nic_dict(self, nic): items = [] properties = { "name": nic.name } if nic.lan: properties['lan'] = nic.lan # Optional Properties if nic.nat: properties['nat'] = nic.nat if nic.ips: properties['ips'] = nic.ips if nic.dhcp is not None: properties['dhcp'] = nic.dhcp if nic.firewall_active is not None: properties['firewallActive'] = nic.firewall_active if len(nic.firewall_rules) > 0: for rule in nic.firewall_rules: items.append(self._create_firewallrules_dict(rule)) rules = { "items": items } entities = { "firewallrules": rules } if len(nic.firewall_rules) == 0: raw = { "properties": properties, } else: raw = { "properties": properties, "entities": entities } return raw @staticmethod def _create_firewallrules_dict(rule): properties = {} if rule.name: properties['name'] = rule.name if rule.protocol: properties['protocol'] = rule.protocol if rule.source_mac: properties['sourceMac'] = rule.source_mac if rule.source_ip: properties['sourceIp'] = rule.source_ip if rule.target_ip: properties['targetIp'] = rule.target_ip if rule.port_range_start: properties['portRangeStart'] = rule.port_range_start if rule.port_range_end: properties['portRangeEnd'] = rule.port_range_end if rule.icmp_type: properties['icmpType'] = rule.icmp_type if rule.icmp_code: properties['icmpCode'] = rule.icmp_code raw = { "properties": properties } return raw def _create_server_dict(self, server): volume_items = [] nic_items = [] entities = dict() properties = { "name": server.name } # Omit required attributes, if not provided, # to receive a proper error message. if server.ram: properties['ram'] = server.ram if server.cores: properties['cores'] = server.cores # Optional Properties if server.availability_zone: properties['availabilityZone'] = server.availability_zone if server.boot_cdrom: properties['bootCdrom'] = server.boot_cdrom if server.boot_volume_id: boot_volume = { "id": server.boot_volume_id } properties['bootVolume'] = boot_volume if server.cpu_family: properties['cpuFamily'] = server.cpu_family if len(server.create_volumes) > 0: for volume in server.create_volumes: volume_items.append(self._create_volume_dict(volume)) volumes = { "items": volume_items } volume_entities = { "volumes": volumes } entities.update(volume_entities) if len(server.nics) > 0: for nic in server.nics: nic_items.append(self._create_nic_dict(nic)) nics = { "items": nic_items } nic_entities = { "nics": nics } entities.update(nic_entities) # Attach Existing Volume(s) if len(server.attach_volumes) > 0: for volume in server.attach_volumes: volume_properties = { "id": volume } volume_items.append(volume_properties) volumes = { "items": volume_items } volume_entities = { "volumes": volumes } entities.update(volume_entities) if len(entities) == 0: raw = { "properties": properties, } else: raw = { "properties": properties, "entities": entities } return raw @staticmethod def _create_volume_dict(volume): properties = { "name": volume.name } # Omit 'size' attributes, if not provided, # to receive a proper error message. if volume.size: properties['size'] = int(volume.size) # Optional Properties if volume.availability_zone: properties['availabilityZone'] = volume.availability_zone if volume.image: properties['image'] = volume.image if volume.image_alias: properties['imageAlias'] = volume.image_alias if volume.bus: properties['bus'] = volume.bus if volume.disk_type: properties['type'] = volume.disk_type if volume.image is None and volume.image_alias is None: properties['licenceType'] = volume.licence_type # if volume.licence_type: # properties['licenceType'] = volume.licence_type if volume.image_password: properties['imagePassword'] = volume.image_password if volume.ssh_keys: properties['sshKeys'] = volume.ssh_keys raw = { "properties": properties } return raw @staticmethod def _create_group_dict(group): properties = {} if group.name: properties['name'] = group.name # Optional Properties if group.reserve_ip: properties['reserveIp'] = group.reserve_ip if group.create_snapshot: properties['createSnapshot'] = group.create_snapshot if group.create_datacenter: properties['createDataCenter'] = \ group.create_datacenter if group.access_activity_log: properties['accessActivityLog'] = \ group.access_activity_log raw = { "properties": properties } return raw @staticmethod def _create_user_dict(user): properties = {} if user.firstname: properties['firstname'] = user.firstname if user.lastname: properties['lastname'] = user.lastname if user.email: properties['email'] = user.email if user.password: properties['password'] = user.password # Optional Properties if user.administrator: properties['administrator'] = user.administrator if user.force_sec_auth: properties['forceSecAuth'] = user.force_sec_auth raw = { "properties": properties } return raw class Datacenter(object): def __init__(self, name=None, location=None, description=None, volumes=None, servers=None, lans=None, loadbalancers=None): """ The Datacenter class initializer. :param name: The data center name.. :type name: ``str`` :param location: The data center geographical location. :type location: ``str`` :param description: Optional description. :type description: ``str`` :param volumes: List of volume dicts. :type volumes: ``list`` :param servers: List of server dicts. :type servers: ``list`` :param lans: List of LAN dicts. :type lans: ``list`` :param loadbalancers: List of load balancer dicts. :type loadbalancers: ``list`` """ if volumes is None: volumes = [] if servers is None: servers = [] if lans is None: lans = [] if loadbalancers is None: loadbalancers = [] self.name = name self.description = description self.location = location self.servers = servers self.volumes = volumes self.lans = lans self.loadbalancers = loadbalancers def __repr__(self): return ((' ...>') % (self.name, self.location, self.description)) class FirewallRule(object): def __init__(self, name=None, protocol=None, source_mac=None, source_ip=None, target_ip=None, port_range_start=None, port_range_end=None, icmp_type=None, icmp_code=None): """ FirewallRule class initializer. :param name: The name of the firewall rule. :type name: ``str`` :param protocol: Either TCP or UDP :type protocol: ``str`` :param source_mac: Source MAC you want to restrict. :type source_mac: ``str`` :param source_ip: Source IP you want to restrict. :type source_ip: ``str`` :param target_ip: Target IP you want to restrict. :type target_ip: ``str`` :param port_range_start: Optional port range. :type port_range_start: ``str`` :param port_range_end: Optional port range. :type port_range_end: ``str`` :param icmp_type: Defines the allowed type. :type icmp_type: ``str`` :param icmp_code: Defines the allowed code. :type icmp_code: ``str`` """ self.name = name self.protocol = protocol self.source_mac = source_mac self.source_ip = source_ip self.target_ip = target_ip self.port_range_start = port_range_start self.port_range_end = port_range_end if icmp_type is not None: icmp_type = str(icmp_type) self.icmp_type = icmp_type if icmp_code is not None: icmp_code = str(icmp_code) self.icmp_code = icmp_code def __repr__(self): return ((' ...>') % (self.name, self.protocol, self.source_mac, self.source_ip, self.target_ip, self.port_range_start, self.port_range_end, self.icmp_type, self.icmp_code)) class IPBlock(object): def __init__(self, name=None, location=None, size=None): """ IPBlock class initializer. :param name: The name of the IP block. :type name: ``str`` :param location: The location for the IP block. :type location: ``str`` :param size: The number of IPs in the block. :type size: ``str`` """ self.name = name self.location = location self.size = size def __repr__(self): return (('') % (self.location, self.size)) class LAN(object): """ This is the main class for managing LAN resources. """ def __init__(self, name=None, public=None, nics=None): """ LAN class initializer. :param name: The name of the LAN. :type name: ``str`` :param public: Indicates if the LAN is public. :type public: ``bool`` :param nics: A list of NICs :type nics: ``list`` """ if nics is None: nics = [] self.name = name self.public = public self.nics = nics def __repr__(self): return ((' ...>') % (self.name, str(self.public))) class LoadBalancer(object): """ This is the main class for managing load balancer resources. """ def __init__(self, name=None, ip=None, dhcp=None, balancednics=None): """ LoadBalancer class initializer. :param name: The name of the load balancer. :type name: ``str`` :param ip: The IP for the load balancer. :type ip: ``str`` :param dhcp: Indicates if the load balancer uses DHCP or not. :type dhcp: ``bool`` :param balancednics: A list of NICs associated with the load balancer. :type balancednics: ``list`` """ if balancednics is None: balancednics = [] self.name = name self.ip = ip self.dhcp = dhcp self.balancednics = balancednics def __repr__(self): return ((' ...>') % (self.name, self.ip, str(self.dhcp))) class NIC(object): def __init__(self, name=None, ips=None, dhcp=None, lan=None, firewall_active=None, firewall_rules=None, nat=None): """ NIC class initializer. :param name: The name of the NIC. :type name: ``str`` :param ips: A list of IPs. :type ips: ``list`` :param dhcp: Enable or disable DHCP. Default is enabled. :type dhcp: ``bool`` :param lan: ID of the LAN in which the NIC should reside. :type lan: ``str`` :param nat: Enable or disable NAT. Default is disabled. :type nat: ``bool`` :param firewall_active: Turns the firewall on or off; default is disabled. :type firewall_active: ``bool`` :param firewall_rules: List of firewall rule dicts. :type firewall_rules: ``list`` """ if firewall_rules is None: firewall_rules = [] self.name = name self.nat = nat self.ips = ips self.dhcp = dhcp self.lan = lan self.firewall_active = firewall_active self.firewall_rules = firewall_rules def __repr__(self): return ((' ...>') % (self.name, self.ips, str(self.dhcp), self.lan, str(self.firewall_active))) class Server(object): """ This is the main class for managing server resources. """ def __init__(self, name=None, cores=None, ram=None, availability_zone=None, boot_volume_id=None, boot_cdrom=None, cpu_family=None, create_volumes=None, attach_volumes=None, nics=None): """ Server class initializer. :param name: The name of your server.. :type name: ``str`` :param cores: The number of cores for the server. :type cores: ``str`` :param ram: The amount of memory for the server. :type ram: ``str`` :param availability_zone: The availability zone for the server. :type availability_zone: ``str`` :param boot_volume_id: The ID of the boot volume. :type boot_volume_id: ``str`` :param boot_cdrom: Attach a CDROM. :type boot_cdrom: ``str`` :param cpu_family: Set the desired CPU type. :type cpu_family: ``str`` :param create_volumes: List of volume dicts to create. :type create_volumes: ``list`` :param attach_volumes: List of volume IDs to attach. :type attach_volumes: ``list`` :param nics: List of NIC dicts to create. :type nics: ``list`` """ if create_volumes is None: create_volumes = [] if attach_volumes is None: attach_volumes = [] if nics is None: nics = [] self.name = name self.cores = cores self.ram = ram self.availability_zone = availability_zone self.boot_volume_id = boot_volume_id self.boot_cdrom = boot_cdrom self.cpu_family = cpu_family self.create_volumes = create_volumes self.attach_volumes = attach_volumes self.nics = nics def __repr__(self): return (('') % (self.name, self.cores, self.ram, self.availability_zone, self.boot_volume_id, self.boot_cdrom)) class Volume(object): def __init__(self, name=None, size=None, bus='VIRTIO', image=None, image_alias=None, disk_type='HDD', licence_type='UNKNOWN', image_password=None, ssh_keys=None, availability_zone='AUTO'): """ Volume class initializer. :param name: The name of the volume. :type name: ``str`` :param size: The size of the volume. :type size: ``str`` :param bus: The bus type. Def. VIRTIO. :type bus: ``str`` :param image: The image ID to use. :type image: ``str`` :param image_alias: An alias of the image to use. :type image_alias: ``str`` :param disk_type: The type of storage. Def. HDD :type disk_type: ``str`` :param licence_type: The licence type. :type licence_type: ``str`` :param ssh_keys: A list of public SSH keys. :type ssh_keys: ``list`` :param availability_zone: The availability zone for the server. :type availability_zone: ``str`` """ if ssh_keys is None: ssh_keys = [] self.name = name self.availability_zone = availability_zone self.size = size self.image = image self.image_alias = image_alias self.bus = bus self.disk_type = disk_type self.licence_type = licence_type self.image_password = image_password self.ssh_keys = ssh_keys def __repr__(self): return (('') % (self.name, str(self.size), self.image, self.image_alias, self.bus, self.disk_type)) class Snapshot(object): def __init__(self, name=None, description=None, licence_type='UNKNOWN', size=None, location=None): """ Snapshot class initializer. :param name: The name of the snapshot. :type name: ``str`` :param name: The description of the snapshot. :type name: ``str`` :param size: The size of the snapshot. :type size: ``str`` :param licence_type: The licence type. :type licence_type: ``str`` """ self.name = name self.description = description self.size = int(size) self.licence_type = licence_type self.location = location def __repr__(self): return (''.format( self.name, self.description, str(self.size), self.location)) class Group(object): def __init__(self, name=None, create_datacenter=None, create_snapshot=None, reserve_ip=None, access_activity_log=None): """ Group class initializer. :param name: The name of the group. :type name: ``str`` :param create_datacenter: Indicates if the group is allowed to create virtual data centers. :type create_datacenter: ``bool`` :param create_snapshot: Indicates if the group is allowed to create snapshots. :type create_snapshot: ``bool`` :param reserve_ip: Indicates if the group is allowed to reserve IP addresses. :type reserve_ip: ``bool`` :param access_activity_log: Indicates if the group is allowed to access activity log. :type access_activity_log: ``bool`` """ self.name = name self.create_datacenter = create_datacenter self.create_snapshot = create_snapshot self.reserve_ip = reserve_ip self.access_activity_log = access_activity_log def __repr__(self): return ('' % (self.name, str(self.create_datacenter), str(self.create_snapshot), str(self.reserve_ip), str(self.access_activity_log))) class User(object): def __init__(self, firstname=None, lastname=None, email=None, password=None, administrator=None, force_sec_auth=None): """ User class initializer. :param firstname: The user's first name. :type firstname: ``str`` :param lastname: The user's last name. :type lastname: ``str`` :param email: The user's email. :type email: ``str`` :param password: A password for the user. :type password: ``str`` :param administrator: Indicates if the user have administrative rights. :type administrator: ``bool`` :param force_sec_auth: Indicates if secure (two-factor) authentication should be forced for the user. :type force_sec_auth: ``bool`` """ self.firstname = firstname self.lastname = lastname self.email = email self.password = password self.administrator = administrator self.force_sec_auth = force_sec_auth def __repr__(self): return ('' % (self.firstname, self.lastname, self.email, self.password, str(self.administrator), str(self.force_sec_auth))) profitbricks-sdk-python-4.1.3/profitbricks/errors.py000066400000000000000000000030211326076220500227040ustar00rootroot00000000000000# Copyright 2015-2017 ProfitBricks GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. class PBError(Exception): """Base error for this module.""" def __init__(self, resp, content, uri=None): self.resp = resp self.content = content self.uri = uri class PBNotAuthorizedError(PBError): """The authorization information provided is not correct""" class PBNotFoundError(PBError): """The ProfitBricks entity was not found""" class PBValidationError(PBError): """The HTTP data provided is not valid""" class PBRateLimitExceededError(PBError): """The number of requests sent have exceeded the allowed API rate limit""" class PBRequestError(Exception): """Base error for request failures""" def __init__(self, msg, request_id): self.msg = msg self.request_id = request_id class PBFailedRequest(PBRequestError): """Raised when a provisioning request failed.""" class PBTimeoutError(PBRequestError): """Raised when a request does not finish in the given time span.""" profitbricks-sdk-python-4.1.3/profitbricks/utils.py000066400000000000000000000066751326076220500225520ustar00rootroot00000000000000# Copyright 2015-2017 ProfitBricks GmbH # Author: Benjamin Drung # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import re def ask(question, options, default): """ Ask the user a question with a list of allowed answers (like yes or no). The user is presented with a question and asked to select an answer from the given options list. The default will be returned if the user enters nothing. The user is asked to repeat his answer if his answer does not match any of the allowed anwsers. :param question: Question to present to the user (without question mark) :type question: ``str`` :param options: List of allowed anwsers :type options: ``list`` :param default: Default answer (if the user enters no text) :type default: ``str`` """ assert default in options question += " ({})? ".format("/".join(o.upper() if o == default else o for o in options)) selected = None while selected not in options: selected = input(question).strip().lower() if selected == "": selected = default else: if selected not in options: question = "Please type '{}'{comma} or '{}': ".format( "', '".join(options[:-1]), options[-1], comma=',' if len(options) > 2 else '', ) return selected def find_item_by_name(list_, namegetter, name): """ Find a item a given list by a matching name. The search for the name is done in this relaxing way: - exact name match - case-insentive name match - attribute starts with the name - attribute starts with the name (case insensitive) - name appears in the attribute - name appears in the attribute (case insensitive) :param list_: A list of elements :type list_: ``list`` :param namegetter: Function that returns the name for a given element in the list :type namegetter: ``function`` :param name: Name to search for :type name: ``str`` """ matching_items = [i for i in list_ if namegetter(i) == name] if len(matching_items) == 0: prog = re.compile(re.escape(name) + '$', re.IGNORECASE) matching_items = [i for i in list_ if prog.match(namegetter(i))] if len(matching_items) == 0: prog = re.compile(re.escape(name)) matching_items = [i for i in list_ if prog.match(namegetter(i))] if len(matching_items) == 0: prog = re.compile(re.escape(name), re.IGNORECASE) matching_items = [i for i in list_ if prog.match(namegetter(i))] if len(matching_items) == 0: prog = re.compile(re.escape(name)) matching_items = [i for i in list_ if prog.search(namegetter(i))] if len(matching_items) == 0: prog = re.compile(re.escape(name), re.IGNORECASE) matching_items = [i for i in list_ if prog.search(namegetter(i))] return matching_items profitbricks-sdk-python-4.1.3/requirements.txt000066400000000000000000000001051326076220500216010ustar00rootroot00000000000000requests>=2.0.0 six>=1.10.0 # Testing requirements pytest responses profitbricks-sdk-python-4.1.3/setup.py000066400000000000000000000061401326076220500200340ustar00rootroot00000000000000#!/usr/bin/python3 # Copyright 2015-2017 ProfitBricks GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ Setup script for the ProfitBricks API Python client. """ from __future__ import print_function import codecs import os import re import sys from setuptools import setup from setuptools.command.test import test as TestCommand here = os.path.abspath(os.path.dirname(__file__)) def read(*parts): return codecs.open(os.path.join(here, *parts), 'r', 'utf-8').read() def find_version(*file_paths): version_file = read(*file_paths) version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", version_file, re.M) if version_match: return version_match.group(1) raise RuntimeError("Unable to find version string.") if os.path.isfile("README.md"): long_desc = read('README.md') else: long_desc = "ProfitBricks API Client Library for Python" class PyTest(TestCommand): def finalize_options(self): TestCommand.finalize_options(self) self.test_args = ['--strict', '--verbose', '--tb=long', 'tests'] self.test_suite = True def run_tests(self): import pytest errno = pytest.main(self.test_args) sys.exit(errno) setup( name='profitbricks', version=find_version('profitbricks', '__init__.py'), description='ProfitBricks API Client Library for Python', long_description=long_desc, author='Matt Baldwin (stackpointcloud.com)', author_email='baldwin@stackpointcloud.com', url='https://github.com/profitbricks/profitbricks-sdk-python', install_requires=['requests>=2.0.0', 'six>=1.10.0'], packages=['profitbricks'], platforms='any', test_suite='profitbricks.test.test_profitbricks', cmdclass={'test': PyTest}, tests_require=['pytest'], license='Apache 2.0', keywords='profitbricks api client cloud', classifiers=['Development Status :: 5 - Production/Stable', 'Natural Language :: English', 'Environment :: Web Environment', 'Intended Audience :: Developers', 'License :: OSI Approved :: Apache Software License', 'Operating System :: POSIX', 'Programming Language :: Python', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 3', 'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: Software Development :: Libraries :: Application Frameworks', 'Topic :: Internet :: WWW/HTTP'], extras_require={ 'testing': ['pytest'], } ) profitbricks-sdk-python-4.1.3/tests/000077500000000000000000000000001326076220500174635ustar00rootroot00000000000000profitbricks-sdk-python-4.1.3/tests/helpers/000077500000000000000000000000001326076220500211255ustar00rootroot00000000000000profitbricks-sdk-python-4.1.3/tests/helpers/__init__.py000066400000000000000000000000001326076220500232240ustar00rootroot00000000000000profitbricks-sdk-python-4.1.3/tests/helpers/configuration.py000066400000000000000000000024421326076220500243500ustar00rootroot00000000000000# Copyright 2015-2017 ProfitBricks GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import os # Default settings for testing. LOCATION = os.getenv('PROFITBRICKS_LOCATION', 'us/las') IMAGE_NAME = 'Ubuntu-16' # Note: Partial image name and case sensitive # Custom HTTP headers # Connection: close - prevents resource warnings in Python 3.4 HEADERS = {'Connection': 'close'} # Import environment variables for credentials. try: os.environ['PROFITBRICKS_USERNAME'] USERNAME = os.getenv('PROFITBRICKS_USERNAME') except KeyError: raise Exception('Please set the environment variable PROFITBRICKS_USERNAME') try: os.environ['PROFITBRICKS_PASSWORD'] PASSWORD = os.getenv('PROFITBRICKS_PASSWORD') except KeyError: raise Exception('Please set the environment variable PROFITBRICKS_PASSWORD') profitbricks-sdk-python-4.1.3/tests/helpers/resources.py000066400000000000000000000105541326076220500235160ustar00rootroot00000000000000# Copyright 2015-2017 ProfitBricks GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import re from helpers import configuration def resource(): return { 'locations': ['us/las', 'us/ewr', 'de/fra', 'de/fkb'], 'licence_type': ['LINUX', 'WINDOWS', 'WINDOWS2016', 'OTHER', 'UNKNOWN'], 'vm_states': ['RUNNING', 'SHUTOFF'], 'uuid_match': '^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$', 'mac_match': re.compile( '^([0-9a-f]{2}[:]){5}([0-9a-f]{2})$'), 'datacenter': { 'name': 'Python SDK Test', 'description': 'Python SDK test datacenter', 'location': configuration.LOCATION }, 'datacenter_composite': { 'name': 'Python SDK Test Composite', 'description': 'Python SDK test composite datacenter', 'location': configuration.LOCATION }, 'server': { 'name': 'Python SDK Test', 'ram': 1024, 'cores': 1, 'cpu_family': 'INTEL_XEON', 'availability_zone': 'ZONE_1' }, 'boot_volume': { 'name': 'Python SDK Test', 'size': 10, 'bus': 'VIRTIO', 'disk_type': 'HDD', 'image_alias': 'ubuntu:latest', 'availability_zone': 'ZONE_1' }, 'volume': { 'name': 'Python SDK Test', 'size': 2, 'bus': 'VIRTIO', 'disk_type': 'HDD', 'licence_type': 'UNKNOWN', 'availability_zone': 'ZONE_1' }, 'volume2': { 'name': 'Python SDK Test', 'size': 2, 'bus': 'VIRTIO', 'disk_type': 'HDD', 'availability_zone': 'ZONE_3', 'ssh_keys': ['ssh-rsa AAAAB3NzaC1'] }, 'snapshot': { 'name': 'Python SDK Test', 'description': 'Python SDK test snapshot', 'size': 2 }, 'nic': { 'name': 'Python SDK Test', 'dhcp': True, 'lan': 1, 'firewall_active': True, 'nat': False }, 'fwrule': { 'name': 'SSH', 'protocol': 'TCP', 'source_mac': '01:23:45:67:89:00', 'source_ip': None, 'target_ip': None, 'port_range_start': 22, 'port_range_end': 22, 'icmp_type': None, 'icmp_code': None, }, 'loadbalancer': { 'name': 'python sdk test', 'dhcp': True }, 'lan': { # REST API converts names to lowercase. 'name': 'python sdk test', 'public': True, }, 'ipblock': { # REST API converts names to lowercase. 'name': 'python sdk test', 'location': configuration.LOCATION, 'size': 1 }, 'group': { 'name': 'Python SDK Test', 'create_datacenter': True, 'create_snapshot': True, 'reserve_ip': True, 'access_activity_log': True }, 'not_found_error': 'Resource does not exist', 'missing_attribute_error': "Attribute '%s' is required" } def find_image(conn, name): ''' Find image by partial name and location. ''' for item in conn.list_images()['items']: if (item['properties']['location'] == configuration.LOCATION and item['properties']['imageType'] == 'HDD' and name in item['properties']['name']): return item def check_detached_cdrom_gone(parent): ''' Check if an attached cdrom is not attached anymore and it throws a PBNotFoundError ''' parent.client.get_attached_cdrom( datacenter_id=parent.datacenter['id'], server_id=parent.server['id'], cdrom_id=parent.test_image1['id']) profitbricks-sdk-python-4.1.3/tests/test_contract_resources.py000066400000000000000000000023251326076220500250050ustar00rootroot00000000000000# Copyright 2015-2017 ProfitBricks GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import unittest from helpers import configuration from profitbricks.client import ProfitBricksService class TestContractResources(unittest.TestCase): @classmethod def setUpClass(self): self.client = ProfitBricksService( username=configuration.USERNAME, password=configuration.PASSWORD, headers=configuration.HEADERS) def test_list_contract_resources(self): contracts = self.client.list_contracts() self.assertEqual(contracts['type'], 'contract') self.assertIsInstance(contracts['properties']['contractNumber'], int) if __name__ == '__main__': unittest.main() profitbricks-sdk-python-4.1.3/tests/test_datacenter.py000066400000000000000000000141521326076220500232110ustar00rootroot00000000000000# Copyright 2015-2017 ProfitBricks GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import unittest import time from helpers import configuration from helpers.resources import resource from profitbricks.client import Server, Volume from six import assertRegex from profitbricks.client import Datacenter from profitbricks.client import ProfitBricksService from profitbricks.errors import PBError, PBNotFoundError class TestDatacenter(unittest.TestCase): @classmethod def setUpClass(self): self.resource = resource() self.client = ProfitBricksService( username=configuration.USERNAME, password=configuration.PASSWORD, headers=configuration.HEADERS) # Create test datacenter. self.datacenter = self.client.create_datacenter( datacenter=Datacenter(**self.resource['datacenter'])) @classmethod def tearDownClass(self): self.client.delete_datacenter(datacenter_id=self.datacenter['id']) def test_list_datacenters(self): datacenters = self.client.list_datacenters() self.assertGreater(len(datacenters), 0) self.assertEqual(datacenters['items'][0]['type'], 'datacenter') def test_get_datacenter(self): datacenter = self.client.get_datacenter( datacenter_id=self.datacenter['id']) assertRegex(self, datacenter['id'], self.resource['uuid_match']) self.assertEqual(datacenter['type'], 'datacenter') self.assertEqual(datacenter['id'], self.datacenter['id']) self.assertEqual(datacenter['properties']['name'], self.resource['datacenter']['name']) self.assertEqual(datacenter['properties']['description'], self.resource['datacenter']['description']) self.assertEqual(datacenter['properties']['location'], self.resource['datacenter']['location']) def test_get_failure(self): try: self.client.get_datacenter(datacenter_id='00000000-0000-0000-0000-000000000000') except PBNotFoundError as e: self.assertIn(self.resource['not_found_error'], e.content[0]['message']) def test_create_failure(self): try: datacenter = Datacenter(name=self.resource['datacenter']['name']) self.client.create_datacenter(datacenter) except PBError as e: self.assertIn(self.resource['missing_attribute_error'] % 'location', e.content[0]['message']) def test_remove_datacenter(self): datacenter = self.client.create_datacenter( datacenter=Datacenter(**self.resource['datacenter'])) self.client.wait_for_completion(datacenter) response = self.client.delete_datacenter( datacenter_id=datacenter['id']) self.assertTrue(response) def test_update_datacenter(self): datacenter = self.client.update_datacenter( datacenter_id=self.datacenter['id'], description=self.resource['datacenter']['name']+' - RENAME') self.client.wait_for_completion(datacenter) time.sleep(10) datacenter = self.client.get_datacenter(datacenter_id=self.datacenter['id']) assertRegex(self, datacenter['id'], self.resource['uuid_match']) self.assertEqual(datacenter['id'], self.datacenter['id']) self.assertEqual(datacenter['properties']['name'], self.resource['datacenter']['name']) self.assertEqual(datacenter['properties']['description'], self.resource['datacenter']['name']+' - RENAME') self.assertEqual(datacenter['properties']['location'], self.resource['datacenter']['location']) self.assertGreater(datacenter['properties']['version'], 1) def test_create_simple(self): datacenter = self.client.create_datacenter( datacenter=Datacenter(**self.resource['datacenter'])) self.client.wait_for_completion(datacenter) self.assertEqual(datacenter['type'], 'datacenter') self.assertEqual(datacenter['properties']['name'], self.resource['datacenter']['name']) self.assertEqual(datacenter['properties']['description'], self.resource['datacenter']['description']) self.assertEqual(datacenter['properties']['location'], self.resource['datacenter']['location']) response = self.client.delete_datacenter( datacenter_id=datacenter['id']) self.assertTrue(response) def test_create_composite(self): datacenter_resource = Datacenter(**self.resource['datacenter_composite']) datacenter_resource.servers = [Server(**self.resource['server'])] datacenter_resource.volumes = [Volume(**self.resource['volume'])] datacenter = self.client.create_datacenter( datacenter=datacenter_resource) self.client.wait_for_completion(datacenter) self.assertEqual(datacenter['type'], 'datacenter') self.assertEqual(datacenter['properties']['name'], self.resource['datacenter_composite']['name']) self.assertEqual(datacenter['properties']['description'], self.resource['datacenter_composite']['description']) self.assertEqual(datacenter['properties']['location'], self.resource['datacenter_composite']['location']) self.assertGreater(len(datacenter['entities']['servers']), 0) self.assertGreater(len(datacenter['entities']['volumes']), 0) response = self.client.delete_datacenter( datacenter_id=datacenter['id']) self.assertTrue(response) if __name__ == '__main__': unittest.main() profitbricks-sdk-python-4.1.3/tests/test_errors.py000066400000000000000000000046271326076220500224210ustar00rootroot00000000000000# Copyright 2015-2017 ProfitBricks GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import unittest from profitbricks.client import ProfitBricksService, Datacenter, Volume from profitbricks.errors import PBError, PBNotAuthorizedError, PBNotFoundError, PBValidationError from helpers import configuration from helpers.resources import resource class TestErrors(unittest.TestCase): @classmethod def setUpClass(self): self.resource = resource() self.client = ProfitBricksService( username=configuration.USERNAME, password=configuration.PASSWORD, headers=configuration.HEADERS) self.datacenter = self.client.create_datacenter( datacenter=Datacenter(**self.resource['datacenter'])) @classmethod def tearDownClass(self): self.client.delete_datacenter(datacenter_id=self.datacenter['id']) def test_pb_not_found(self): try: self.client.get_datacenter("fake_id") except PBError as err: self.assertTrue(isinstance(err, PBNotFoundError)) def test_pb_unauthorized_error(self): try: self.client = ProfitBricksService( username=configuration.USERNAME + "1", password=configuration.PASSWORD, headers=configuration.HEADERS) self.client.list_datacenters() except PBError as err: self.assertTrue(isinstance(err, PBNotAuthorizedError)) def test_pb_validation_error(self): try: i = Volume( name='Explicitly created volume', size=5, disk_type='HDD', image='fake_image_id', bus='VIRTIO') self.client.create_volume(datacenter_id=self.datacenter['id'], volume=i) except PBError as err: self.assertTrue(isinstance(err, PBValidationError)) if __name__ == '__main__': unittest.main() profitbricks-sdk-python-4.1.3/tests/test_firewall.py000066400000000000000000000165541326076220500227140ustar00rootroot00000000000000# Copyright 2015-2017 ProfitBricks GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import unittest from helpers import configuration from helpers.resources import resource from profitbricks.client import ProfitBricksService from profitbricks.client import Datacenter, Server, LAN, NIC, FirewallRule from profitbricks.errors import PBError, PBNotFoundError from six import assertRegex class TestFirewall(unittest.TestCase): @classmethod def setUpClass(self): self.resource = resource() self.client = ProfitBricksService( username=configuration.USERNAME, password=configuration.PASSWORD, headers=configuration.HEADERS) # Create test datacenter. self.datacenter = self.client.create_datacenter( datacenter=Datacenter(**self.resource['datacenter'])) self.client.wait_for_completion(self.datacenter) # Create test LAN. self.lan = self.client.create_lan( datacenter_id=self.datacenter['id'], lan=LAN(**self.resource['lan'])) self.client.wait_for_completion(self.lan) # Create test server. self.server = self.client.create_server( datacenter_id=self.datacenter['id'], server=Server(**self.resource['server'])) self.client.wait_for_completion(self.server) # Create test NIC1. nic1 = NIC(**self.resource['nic']) nic1.lan = self.lan['id'] self.nic1 = self.client.create_nic( datacenter_id=self.datacenter['id'], server_id=self.server['id'], nic=nic1) self.client.wait_for_completion(self.nic1) # Create test Firewall Rule fwrule = FirewallRule(**self.resource['fwrule']) self.fwrule = self.client.create_firewall_rule( datacenter_id=self.datacenter['id'], server_id=self.server['id'], nic_id=self.nic1['id'], firewall_rule=fwrule) self.client.wait_for_completion(self.fwrule) # Create test Firewall Rule 2 fwrule2 = FirewallRule(**self.resource['fwrule']) fwrule2.port_range_start = 8080 fwrule2.port_range_end = 8080 fwrule2.name = "8080" self.fwrule2 = self.client.create_firewall_rule( datacenter_id=self.datacenter['id'], server_id=self.server['id'], nic_id=self.nic1['id'], firewall_rule=fwrule2) self.client.wait_for_completion(self.fwrule2) @classmethod def tearDownClass(self): self.client.delete_datacenter(datacenter_id=self.datacenter['id']) def test_list_fwrules(self): fwrules = self.client.get_firewall_rules( datacenter_id=self.datacenter['id'], server_id=self.server['id'], nic_id=self.nic1['id']) self.assertGreater(len(fwrules), 0) self.assertIn(fwrules['items'][0]['id'], (self.fwrule['id'], self.fwrule2['id'])) self.assertEqual(fwrules['items'][0]['type'], 'firewall-rule') def test_get_fwrule(self): fwrule = self.client.get_firewall_rule( datacenter_id=self.datacenter['id'], server_id=self.server['id'], nic_id=self.nic1['id'], firewall_rule_id=self.fwrule['id']) self.assertEqual(fwrule['type'], 'firewall-rule') self.assertEqual(fwrule['id'], self.fwrule['id']) assertRegex(self, fwrule['id'], self.resource['uuid_match']) self.assertEqual(fwrule['properties']['name'], self.fwrule['properties']['name']) self.assertEqual(fwrule['properties']['protocol'], self.fwrule['properties']['protocol']) self.assertEqual(fwrule['properties']['sourceMac'], self.fwrule['properties']['sourceMac']) self.assertIsNone(fwrule['properties']['sourceIp']) self.assertIsNone(fwrule['properties']['targetIp']) self.assertIsNone(fwrule['properties']['icmpCode']) self.assertIsNone(fwrule['properties']['icmpType']) self.assertEqual(fwrule['properties']['portRangeStart'], self.fwrule['properties']['portRangeStart']) self.assertEqual(fwrule['properties']['portRangeEnd'], self.fwrule['properties']['portRangeEnd']) def test_delete_fwrule(self): fwrule = self.client.delete_firewall_rule( datacenter_id=self.datacenter['id'], server_id=self.server['id'], nic_id=self.nic1['id'], firewall_rule_id=self.fwrule2['id']) self.assertTrue(fwrule) def test_update_fwrule(self): fwrule = self.client.update_firewall_rule( datacenter_id=self.datacenter['id'], server_id=self.server['id'], nic_id=self.nic1['id'], firewall_rule_id=self.fwrule['id'], name=self.resource['fwrule']['name']+' - RENAME') self.assertEqual(fwrule['type'], 'firewall-rule') self.assertEqual(fwrule['properties']['name'], self.resource['fwrule']['name']+' - RENAME') def test_create_fwrule(self): self.assertEqual(self.fwrule['type'], 'firewall-rule') self.assertEqual(self.fwrule['properties']['name'], self.resource['fwrule']['name']) self.assertEqual(self.fwrule['properties']['protocol'], self.resource['fwrule']['protocol']) self.assertEqual(self.fwrule['properties']['sourceMac'], self.resource['fwrule']['source_mac']) self.assertIsNone(self.fwrule['properties']['sourceIp']) self.assertIsNone(self.fwrule['properties']['targetIp']) self.assertIsNone(self.fwrule['properties']['icmpCode']) self.assertIsNone(self.fwrule['properties']['icmpType']) self.assertEqual(self.fwrule['properties']['portRangeStart'], self.resource['fwrule']['port_range_start']) self.assertEqual(self.fwrule['properties']['portRangeEnd'], self.resource['fwrule']['port_range_end']) def test_get_failure(self): try: self.client.get_firewall_rule( datacenter_id=self.datacenter['id'], server_id=self.server['id'], nic_id=self.nic1['id'], firewall_rule_id='00000000-0000-0000-0000-000000000000') except PBNotFoundError as e: self.assertIn(self.resource['not_found_error'], e.content[0]['message']) def test_create_failure(self): try: fwrule = FirewallRule(name=self.resource['fwrule']['name']) self.client.create_firewall_rule( datacenter_id=self.datacenter['id'], server_id=self.server['id'], nic_id=self.nic1['id'], firewall_rule=fwrule) except PBError as e: self.assertIn(self.resource['missing_attribute_error'] % 'protocol', e.content[0]['message']) if __name__ == '__main__': unittest.main() profitbricks-sdk-python-4.1.3/tests/test_image.py000066400000000000000000000077541326076220500221730ustar00rootroot00000000000000# Copyright 2015-2017 ProfitBricks GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import unittest from helpers import configuration from helpers.resources import resource from profitbricks.client import ProfitBricksService from profitbricks.errors import PBNotFoundError from six import assertRegex class TestImage(unittest.TestCase): @classmethod def setUpClass(self): self.resource = resource() self.client = ProfitBricksService( username=configuration.USERNAME, password=configuration.PASSWORD, headers=configuration.HEADERS) # Find an Ubuntu image for testing. for item in self.client.list_images()['items']: if (configuration.IMAGE_NAME in item['properties']['name'] and item['properties']['location'] == configuration.LOCATION): self.image = item def test_list_images(self): images = self.client.list_images() assertRegex(self, images['items'][0]['id'], self.resource['uuid_match']) self.assertGreater(len(images), 0) self.assertEqual(images['items'][0]['type'], 'image') self.assertTrue(self, len(images['items']) > 0) def test_get_image(self): image = self.client.get_image(self.image['id']) self.assertEqual(image['type'], 'image') self.assertEqual(image['id'], self.image['id']) assertRegex(self, image['id'], self.resource['uuid_match']) self.assertGreater(len(image['properties']['name']), 0) self.assertIsNone(image['properties']['description']) self.assertGreater(image['properties']['size'], 0) self.assertNotEqual(image['properties']['name'], "") self.assertIn(image['properties']['location'], self.resource['locations']) self.assertIn(image['properties']['licenceType'], self.resource['licence_type']) self.assertIn(image['properties']['imageType'], ['HDD', 'CDROM']) self.assertIsInstance(image['properties']['imageAliases'], list) self.assertIsInstance(image['properties']['cpuHotPlug'], bool) self.assertIsInstance(image['properties']['cpuHotUnplug'], bool) self.assertIsInstance(image['properties']['ramHotPlug'], bool) self.assertIsInstance(image['properties']['ramHotUnplug'], bool) self.assertIsInstance(image['properties']['nicHotPlug'], bool) self.assertIsInstance(image['properties']['nicHotUnplug'], bool) self.assertIsInstance(image['properties']['discVirtioHotPlug'], bool) self.assertIsInstance(image['properties']['discVirtioHotUnplug'], bool) self.assertIsInstance(image['properties']['discScsiHotPlug'], bool) self.assertIsInstance(image['properties']['discScsiHotUnplug'], bool) self.assertIsInstance(image['properties']['public'], bool) def test_get_failure(self): try: self.client.get_image('00000000-0000-0000-0000-000000000000') except PBNotFoundError as e: self.assertIn(self.resource['not_found_error'], e.content[0]['message']) # A custom image would need to be uploaded and referenced to perform the # following tests. Skipping for now. # def test_delete_image(self): # image = self.client.delete_image(image_id) # self.assertTrue(image) # def test_update_image(self): # image = self.client.update_image( # image_id, # name='New name') # self.assertEqual(image['properties']['name'], 'New name') if __name__ == '__main__': unittest.main() profitbricks-sdk-python-4.1.3/tests/test_ipblock.py000066400000000000000000000074141326076220500225250ustar00rootroot00000000000000# Copyright 2015-2017 ProfitBricks GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import unittest from helpers import configuration from helpers.resources import resource from profitbricks.client import IPBlock from profitbricks.client import ProfitBricksService from profitbricks.errors import PBError, PBNotFoundError from six import assertRegex class TestIPBlock(unittest.TestCase): @classmethod def setUpClass(self): self.resource = resource() self.client = ProfitBricksService( username=configuration.USERNAME, password=configuration.PASSWORD, headers=configuration.HEADERS) ipblock1 = IPBlock(**self.resource['ipblock']) ipblock1.size = 2 self.ipblock1 = self.client.reserve_ipblock(ipblock1) ipblock2 = IPBlock(**self.resource['ipblock']) self.ipblock2 = self.client.reserve_ipblock(ipblock2) @classmethod def tearDownClass(self): self.client.delete_ipblock(self.ipblock1['id']) def test_list_ipblocks(self): ipblocks = self.client.list_ipblocks() assertRegex(self, ipblocks['items'][0]['id'], self.resource['uuid_match']) self.assertGreater(len(ipblocks['items']), 0) self.assertEqual(ipblocks['items'][0]['type'], 'ipblock') self.assertGreater(ipblocks['items'][0]['properties']['size'], 0) self.assertIn(ipblocks['items'][0]['properties']['location'], self.resource['locations']) def test_get_ipblock(self): ipblock = self.client.get_ipblock(self.ipblock1['id']) assertRegex(self, ipblock['id'], self.resource['uuid_match']) self.assertEqual(ipblock['id'], self.ipblock1['id']) self.assertEqual(ipblock['type'], 'ipblock') self.assertEqual(ipblock['properties']['name'], (self.resource['ipblock']['name'])) self.assertEqual(ipblock['properties']['size'], 2) self.assertEqual(len(ipblock['properties']['ips']), 2) self.assertEqual(ipblock['properties']['location'], self.resource['ipblock']['location']) def test_delete_ipblock(self): ipblock = self.client.delete_ipblock(self.ipblock2['id']) self.assertTrue(ipblock) def test_reserve_ipblock(self): ipblock = self.client.reserve_ipblock(IPBlock(**self.resource['ipblock'])) assertRegex(self, ipblock['id'], self.resource['uuid_match']) self.assertEqual(ipblock['properties']['name'], (self.resource['ipblock']['name'])) self.assertEqual(ipblock['properties']['size'], self.resource['ipblock']['size']) self.assertEqual(ipblock['properties']['location'], self.resource['ipblock']['location']) self.client.delete_ipblock(ipblock['id']) def test_get_failure(self): try: self.client.get_ipblock('00000000-0000-0000-0000-000000000000') except PBNotFoundError as e: self.assertIn(self.resource['not_found_error'], e.content[0]['message']) def test_reserve_failure(self): try: ipblock = IPBlock(name=self.resource['ipblock']['name'], size=1) self.client.reserve_ipblock(ipblock) except PBError as e: self.assertIn(self.resource['missing_attribute_error'] % 'location', e.content[0]['message']) if __name__ == '__main__': unittest.main() profitbricks-sdk-python-4.1.3/tests/test_lan.py000066400000000000000000000146531326076220500216570ustar00rootroot00000000000000# Copyright 2015-2017 ProfitBricks GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import unittest from helpers import configuration from helpers.resources import resource from profitbricks.client import ProfitBricksService from profitbricks.client import Datacenter, Server, LAN, NIC from profitbricks.errors import PBNotFoundError from six import assertRegex class TestLan(unittest.TestCase): @classmethod def setUpClass(self): self.resource = resource() self.client = ProfitBricksService( username=configuration.USERNAME, password=configuration.PASSWORD, headers=configuration.HEADERS) # Create test datacenter. self.datacenter = self.client.create_datacenter( datacenter=Datacenter(**self.resource['datacenter'])) self.client.wait_for_completion(self.datacenter) # Create test LAN. self.lan = self.client.create_lan( datacenter_id=self.datacenter['id'], lan=LAN(**self.resource['lan'])) self.client.wait_for_completion(self.lan) # Create test server. self.server = self.client.create_server( datacenter_id=self.datacenter['id'], server=Server(**self.resource['server'])) self.client.wait_for_completion(self.server) # Create test NIC1. nic1 = NIC(**self.resource['nic']) nic1.lan = self.lan['id'] self.nic1 = self.client.create_nic( datacenter_id=self.datacenter['id'], server_id=self.server['id'], nic=nic1) self.client.wait_for_completion(self.nic1) # Create test NIC2. nic2 = NIC(**self.resource['nic']) nic2.lan = self.lan['id'] self.nic2 = self.client.create_nic( datacenter_id=self.datacenter['id'], server_id=self.server['id'], nic=nic2) self.client.wait_for_completion(self.nic2) @classmethod def tearDownClass(self): self.client.delete_datacenter(datacenter_id=self.datacenter['id']) def test_list_lans(self): lans = self.client.list_lans(datacenter_id=self.datacenter['id']) self.assertGreater(len(lans), 0) self.assertEqual(lans['items'][0]['type'], 'lan') self.assertIn(lans['items'][0]['id'], ('1', '2', '3')) self.assertEqual(lans['items'][0]['properties']['name'], self.resource['lan']['name']) self.assertTrue(lans['items'][0]['properties']['public'], self.resource['lan']['public']) def test_get_lan(self): lan = self.client.get_lan(datacenter_id=self.datacenter['id'], lan_id=self.lan['id']) self.assertEqual(lan['type'], 'lan') self.assertEqual(lan['id'], self.lan['id']) self.assertEqual(lan['properties']['name'], self.resource['lan']['name']) self.assertTrue(lan['properties']['public'], self.resource['lan']['public']) def test_remove_lan(self): lan = self.client.create_lan( datacenter_id=self.datacenter['id'], lan=LAN(**self.resource['lan'])) self.client.wait_for_completion(lan) lan = self.client.delete_lan(datacenter_id=self.datacenter['id'], lan_id=lan['id']) self.assertTrue(lan) def test_update_lan(self): lan = self.client.update_lan( datacenter_id=self.datacenter['id'], lan_id=self.lan['id'], name=self.resource['lan']['name'] + ' - RENAME', public=False) self.assertEqual(lan['type'], 'lan') self.assertEqual(lan['properties']['name'], self.resource['lan']['name'] + ' - RENAME') self.assertFalse(lan['properties']['public']) def test_create_lan(self): self.assertEqual(self.lan['id'], '1') self.assertEqual(self.lan['type'], 'lan') self.assertEqual(self.lan['properties']['name'], self.resource['lan']['name']) self.assertEqual(self.lan['properties']['public'], self.resource['lan']['public']) def test_create_complex_lan(self): resource = NIC(**self.resource['nic']) nic1 = self.client.create_nic( datacenter_id=self.datacenter['id'], server_id=self.server['id'], nic=resource) self.client.wait_for_completion(nic1) self.assertFalse(nic1['properties']['nat']) self.assertEqual(nic1['properties']['name'], 'Python SDK Test') self.assertTrue(nic1['properties']['dhcp']) self.assertEqual(nic1['properties']['lan'], 1) self.assertTrue(nic1['properties']['firewallActive']) nics = [nic1['id']] lan = LAN(nics=nics, **self.resource['lan']) response = self.client.create_lan( datacenter_id=self.datacenter['id'], lan=lan) self.client.wait_for_completion(response) self.assertEqual(response['type'], 'lan') self.assertEqual(response['properties']['name'], self.resource['lan']['name']) self.assertTrue(response['properties']['public']) def test_get_lan_members(self): members = self.client.get_lan_members( datacenter_id=self.datacenter['id'], lan_id=self.lan['id']) self.assertGreater(len(members), 0) self.assertEqual(members['items'][0]['type'], 'nic') self.assertEqual(members['items'][0]['properties']['name'], self.resource['nic']['name']) assertRegex(self, members['items'][0]['properties']['mac'], self.resource['mac_match']) def test_get_failure(self): try: self.client.get_lan(datacenter_id=self.datacenter['id'], lan_id=0) except PBNotFoundError as e: self.assertIn(self.resource['not_found_error'], e.content[0]['message']) def test_create_failure(self): try: self.client.create_lan( datacenter_id='00000000-0000-0000-0000-000000000000', lan=LAN()) except PBNotFoundError as e: self.assertIn(self.resource['not_found_error'], e.content[0]['message']) if __name__ == '__main__': unittest.main() profitbricks-sdk-python-4.1.3/tests/test_loadbalancer.py000066400000000000000000000214451326076220500235110ustar00rootroot00000000000000# Copyright 2015-2017 ProfitBricks GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import unittest from helpers import configuration from helpers.resources import resource from profitbricks.client import ProfitBricksService from profitbricks.client import Datacenter, LoadBalancer, LAN, NIC, Server from profitbricks.errors import PBError, PBNotFoundError from six import assertRegex from time import sleep class TestLoadBalancer(unittest.TestCase): @classmethod def setUpClass(self): self.resource = resource() self.client = ProfitBricksService( username=configuration.USERNAME, password=configuration.PASSWORD, headers=configuration.HEADERS) # Create test datacenter. self.datacenter = self.client.create_datacenter( datacenter=Datacenter(**self.resource['datacenter'])) self.client.wait_for_completion(self.datacenter) # Create test LAN. self.lan = self.client.create_lan( datacenter_id=self.datacenter['id'], lan=LAN(**self.resource['lan'])) self.client.wait_for_completion(self.lan) # Create test server. self.server = self.client.create_server( datacenter_id=self.datacenter['id'], server=Server(**self.resource['server'])) self.client.wait_for_completion(self.server) # Create test NIC1. nic1 = NIC(**self.resource['nic']) nic1.lan = self.lan['id'] self.nic1 = self.client.create_nic( datacenter_id=self.datacenter['id'], server_id=self.server['id'], nic=nic1) self.client.wait_for_completion(self.nic1) # Create test NIC2. # nic2 = NIC(**self.resource['nic']) # nic2.lan = self.lan['id'] # self.nic2 = self.client.create_nic( # datacenter_id=self.datacenter['id'], # server_id=self.server['id'], # nic=nic2) # self.client.wait_for_completion(self.nic2) # Create test LoadBalancer loadbalancer = LoadBalancer(**self.resource['loadbalancer']) loadbalancer.balancednics = [self.nic1['id']] self.loadbalancer = self.client.create_loadbalancer( datacenter_id=self.datacenter['id'], loadbalancer=loadbalancer ) self.client.wait_for_completion(self.loadbalancer) # Create test LoadBalancer2 loadbalancer2 = LoadBalancer(**self.resource['loadbalancer']) loadbalancer2.name = "Python SDK Test 2" self.loadbalancer2 = self.client.create_loadbalancer( datacenter_id=self.datacenter['id'], loadbalancer=loadbalancer2 ) self.client.wait_for_completion(self.loadbalancer2) # Create test LoadBalancer3 loadbalancer3 = LoadBalancer(**self.resource['loadbalancer']) loadbalancer3.balancednics = [self.nic1['id']] loadbalancer3.name = "Python SDK Test 3" self.loadbalancer3 = self.client.create_loadbalancer( datacenter_id=self.datacenter['id'], loadbalancer=loadbalancer3 ) self.client.wait_for_completion(self.loadbalancer3) @classmethod def tearDownClass(self): self.client.delete_datacenter(datacenter_id=self.datacenter['id']) def test_list_loadbalancers(self): loadbalancers = self.client.list_loadbalancers( datacenter_id=self.datacenter['id']) self.assertGreater(len(loadbalancers), 0) self.assertIn(loadbalancers['items'][0]['id'], (self.loadbalancer['id'], self.loadbalancer2['id'], self.loadbalancer3['id'])) self.assertEqual(loadbalancers['items'][0]['type'], 'loadbalancer') def test_get_loadbalancer(self): loadbalancer = self.client.get_loadbalancer( datacenter_id=self.datacenter['id'], loadbalancer_id=self.loadbalancer['id']) self.assertEqual(loadbalancer['type'], 'loadbalancer') self.assertEqual(loadbalancer['id'], self.loadbalancer['id']) assertRegex(self, loadbalancer['id'], self.resource['uuid_match']) self.assertEqual(loadbalancer['properties']['name'], self.loadbalancer['properties']['name']) self.assertEqual(loadbalancer['properties']['dhcp'], self.loadbalancer['properties']['dhcp']) self.assertIsNotNone(loadbalancer['properties']['ip']) def test_delete_loadbalancer(self): loadbalancer = self.client.delete_loadbalancer( datacenter_id=self.datacenter['id'], loadbalancer_id=self.loadbalancer2['id']) self.assertTrue(loadbalancer) def test_update_loadbalancer(self): loadbalancer = self.client.update_loadbalancer( datacenter_id=self.datacenter['id'], loadbalancer_id=self.loadbalancer['id'], name=self.resource['loadbalancer']['name']+' - RENAME') self.assertEqual(loadbalancer['type'], 'loadbalancer') self.assertEqual(loadbalancer['properties']['name'], self.resource['loadbalancer']['name']+' - RENAME') def test_create_loadbalancer(self): self.assertEqual(self.loadbalancer['type'], 'loadbalancer') self.assertIsNotNone(self.loadbalancer['entities']['balancednics']) self.assertEqual(self.loadbalancer['properties']['name'], self.resource['loadbalancer']['name']) self.assertEqual(self.loadbalancer['properties']['dhcp'], self.resource['loadbalancer']['dhcp']) def test_associate_nic(self): associated_nic = self.client.add_loadbalanced_nics( datacenter_id=self.datacenter['id'], loadbalancer_id=self.loadbalancer2['id'], nic_id=self.nic1['id']) self.client.wait_for_completion(associated_nic) self.assertEqual(associated_nic['id'], self.nic1['id']) self.assertEqual(associated_nic['properties']['name'], self.nic1['properties']['name']) def test_remove_nic(self): remove_nic = self.client.remove_loadbalanced_nic( datacenter_id=self.datacenter['id'], loadbalancer_id=self.loadbalancer3['id'], nic_id=self.nic1['id']) self.assertTrue(remove_nic) sleep(30) def test_list_balanced_nics(self): balanced_nics = self.client.get_loadbalancer_members( datacenter_id=self.datacenter['id'], loadbalancer_id=self.loadbalancer['id'] ) self.assertGreater(len(balanced_nics['items']), 0) self.assertEqual(balanced_nics['items'][0]['id'], self.nic1['id']) self.assertEqual(balanced_nics['items'][0]['type'], 'nic') def test_get_balanced_nic(self): balanced_nic = self.client.get_loadbalanced_nic( datacenter_id=self.datacenter['id'], loadbalancer_id=self.loadbalancer['id'], nic_id=self.nic1['id']) self.assertEqual(balanced_nic['id'], self.nic1['id']) self.assertEqual(balanced_nic['type'], 'nic') self.assertEqual(balanced_nic['properties']['name'], self.nic1['properties']['name']) self.assertEqual(balanced_nic['properties']['dhcp'], self.nic1['properties']['dhcp']) self.assertIsInstance(balanced_nic['properties']['nat'], bool) self.assertIsInstance(balanced_nic['properties']['firewallActive'], bool) self.assertGreater(len(balanced_nic['properties']['ips']), 0) self.assertIsInstance(balanced_nic['properties']['lan'], int) assertRegex(self, balanced_nic['properties']['mac'], self.resource['mac_match']) def test_get_failure(self): try: self.client.get_loadbalancer( datacenter_id=self.datacenter['id'], loadbalancer_id='00000000-0000-0000-0000-000000000000') except PBNotFoundError as e: self.assertIn(self.resource['not_found_error'], e.content[0]['message']) def test_create_failure(self): try: self.client.create_loadbalancer( datacenter_id=self.datacenter['id'], loadbalancer=LoadBalancer()) except PBError as e: self.assertIn(self.resource['missing_attribute_error'] % 'lan', e.content[0]['message']) if __name__ == '__main__': unittest.main() profitbricks-sdk-python-4.1.3/tests/test_location.py000066400000000000000000000035601326076220500227100ustar00rootroot00000000000000# Copyright 2015-2017 ProfitBricks GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import unittest from helpers import configuration from helpers.resources import resource from profitbricks.client import ProfitBricksService from profitbricks.errors import PBNotFoundError class TestLocation(unittest.TestCase): @classmethod def setUpClass(self): self.resource = resource() self.client = ProfitBricksService( username=configuration.USERNAME, password=configuration.PASSWORD, headers=configuration.HEADERS) def test_list_locations(self): locations = self.client.list_locations() self.assertEqual(len(locations), 4) for location in locations['items']: self.assertEqual(location['type'], 'location') self.assertIn(location['id'], self.resource['locations']) def test_get_location(self): location = self.client.get_location(configuration.LOCATION) self.assertEqual(location['type'], 'location') self.assertEqual(location['id'], configuration.LOCATION) def test_get_failure(self): try: self.client.get_location(location_id='00000000-0000-0000-0000-000000000000') except PBNotFoundError as e: self.assertIn(self.resource['not_found_error'], e.content[0]['message']) if __name__ == '__main__': unittest.main() profitbricks-sdk-python-4.1.3/tests/test_nic.py000066400000000000000000000136451326076220500216560ustar00rootroot00000000000000# Copyright 2015-2017 ProfitBricks GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import unittest from helpers import configuration from helpers.resources import resource from profitbricks.client import ProfitBricksService from profitbricks.client import Datacenter, Server, LAN, NIC from profitbricks.errors import PBError, PBNotFoundError from six import assertRegex class TestNic(unittest.TestCase): @classmethod def setUpClass(self): self.resource = resource() self.client = ProfitBricksService( username=configuration.USERNAME, password=configuration.PASSWORD, headers=configuration.HEADERS) # Create test datacenter. self.datacenter = self.client.create_datacenter( datacenter=Datacenter(**self.resource['datacenter'])) self.client.wait_for_completion(self.datacenter) # Create test LAN. self.lan = self.client.create_lan( datacenter_id=self.datacenter['id'], lan=LAN(name=self.resource['lan']['name'], public=False)) self.client.wait_for_completion(self.lan) # Create test server. self.server = self.client.create_server( datacenter_id=self.datacenter['id'], server=Server(**self.resource['server'])) self.client.wait_for_completion(self.server) # Create test NIC1. nic1 = NIC(**self.resource['nic']) nic1.lan = self.lan['id'] self.ips = ['10.0.0.1'] nic1.ips = self.ips self.nic1 = self.client.create_nic( datacenter_id=self.datacenter['id'], server_id=self.server['id'], nic=nic1) self.client.wait_for_completion(self.nic1) # Create test NIC2. nic2 = NIC(**self.resource['nic']) nic2.lan = self.lan['id'] self.nic2 = self.client.create_nic( datacenter_id=self.datacenter['id'], server_id=self.server['id'], nic=nic2) self.client.wait_for_completion(self.nic2) @classmethod def tearDownClass(self): self.client.delete_datacenter(datacenter_id=self.datacenter['id']) def test_list_nics(self): nics = self.client.list_nics( datacenter_id=self.datacenter['id'], server_id=self.server['id']) self.assertGreater(len(nics), 0) self.assertIn(nics['items'][0]['id'], (self.nic1['id'], self.nic2['id'])) self.assertEqual(nics['items'][0]['type'], 'nic') def test_get_nic(self): nic = self.client.get_nic(datacenter_id=self.datacenter['id'], server_id=self.server['id'], nic_id=self.nic1['id']) self.assertEqual(nic['type'], 'nic') self.assertEqual(nic['id'], self.nic1['id']) assertRegex(self, nic['id'], self.resource['uuid_match']) self.assertEqual(nic['properties']['name'], self.resource['nic']['name']) self.assertEqual(nic['properties']['firewallActive'], self.resource['nic']['firewall_active']) self.assertIsInstance(nic['properties']['ips'], list) self.assertEqual(nic['properties']['dhcp'], self.resource['nic']['dhcp']) self.assertEqual(nic['properties']['nat'], self.resource['nic']['nat']) self.assertEqual(nic['properties']['lan'], self.resource['nic']['lan']) def test_delete_nic(self): nic2 = self.client.delete_nic(datacenter_id=self.datacenter['id'], server_id=self.server['id'], nic_id=self.nic2['id']) self.assertTrue(nic2) def test_update_nic(self): nic = self.client.update_nic( datacenter_id=self.datacenter['id'], server_id=self.server['id'], nic_id=self.nic1['id'], name=self.resource['nic']['name'] + ' - RENAME') self.assertEqual(nic['id'], self.nic1['id']) self.assertEqual(nic['type'], 'nic') self.assertEqual(nic['properties']['name'], self.resource['nic']['name'] + ' - RENAME') def test_create_nic(self): self.assertEqual(self.nic1['type'], 'nic') self.assertEqual(self.nic1['properties']['name'], self.resource['nic']['name']) self.assertEqual(self.nic1['properties']['firewallActive'], self.resource['nic']['firewall_active']) self.assertIsInstance(self.nic1['properties']['ips'], list) self.assertEqual(self.nic1['properties']['dhcp'], self.resource['nic']['dhcp']) self.assertIsNone(self.nic1['properties']['nat']) self.assertEqual(str(self.nic1['properties']['lan']), self.lan['id']) def test_get_failure(self): try: self.client.get_nic( datacenter_id=self.datacenter['id'], server_id=self.server['id'], nic_id='00000000-0000-0000-0000-000000000000') except PBNotFoundError as e: self.assertIn(self.resource['not_found_error'], e.content[0]['message']) def test_create_failure(self): try: nic = NIC(name=self.resource['nic']['name']) self.client.create_nic( datacenter_id=self.datacenter['id'], server_id=self.server['id'], nic=nic) except PBError as e: self.assertIn(self.resource['missing_attribute_error'] % 'lan', e.content[0]['message']) if __name__ == '__main__': unittest.main() profitbricks-sdk-python-4.1.3/tests/test_request.py000066400000000000000000000044621326076220500225720ustar00rootroot00000000000000# Copyright 2015-2017 ProfitBricks GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import unittest from helpers import configuration from helpers.resources import resource from profitbricks.client import ProfitBricksService from profitbricks.errors import PBNotFoundError class TestRequest(unittest.TestCase): @classmethod def setUpClass(self): self.resource = resource() self.client = ProfitBricksService( username=configuration.USERNAME, password=configuration.PASSWORD, headers=configuration.HEADERS) self.requests = self.client.list_requests() self.request = self.requests['items'][0] def test_list_requests(self): requests = self.client.list_requests() self.assertGreater(len(requests), 0) self.assertEqual(requests['items'][0]['type'], 'request') def test_get_request(self): request = self.client.get_request(request_id=self.request['id'], status=False) self.assertEqual(request['type'], 'request') self.assertEqual(request['id'], self.request['id']) self.assertEqual(request['href'], self.request['href']) def test_get_request_status(self): request = self.client.get_request(request_id=self.request['id'], status=True) self.assertEqual(request['type'], 'request-status') self.assertEqual(request['id'], self.request['id'] + '/status') self.assertEqual(request['href'], self.request['href'] + '/status') def test_get_failure(self): try: self.client.get_request(request_id='00000000-0000-0000-0000-000000000000', status=False) except PBNotFoundError as e: self.assertIn(self.resource['not_found_error'], e.content[0]['message']) if __name__ == '__main__': unittest.main() profitbricks-sdk-python-4.1.3/tests/test_server.py000066400000000000000000000421411326076220500224040ustar00rootroot00000000000000# Copyright 2015-2017 ProfitBricks GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import unittest import time from helpers import configuration from helpers.resources import resource, check_detached_cdrom_gone from profitbricks.client import Datacenter, Server, Volume, NIC, FirewallRule from profitbricks.client import ProfitBricksService from profitbricks.errors import PBError, PBNotFoundError from six import assertRegex class TestServer(unittest.TestCase): @classmethod def setUpClass(self): self.resource = resource() self.client = ProfitBricksService( username=configuration.USERNAME, password=configuration.PASSWORD, headers=configuration.HEADERS) # Create test datacenter. self.datacenter = self.client.create_datacenter( datacenter=Datacenter(**self.resource['datacenter'])) self.client.wait_for_completion(self.datacenter) # Create test volume1. self.volume1 = self.client.create_volume( datacenter_id=self.datacenter['id'], volume=Volume(**self.resource['volume'])) self.client.wait_for_completion(self.volume1) # Create test volume2 (attach volume test). self.volume2 = self.client.create_volume( datacenter_id=self.datacenter['id'], volume=Volume(**self.resource['volume'])) self.client.wait_for_completion(self.volume2) # Create test server. server = Server(**self.resource['server']) server.attach_volumes = [self.volume1['id']] self.server = self.client.create_server( datacenter_id=self.datacenter['id'], server=server) self.client.wait_for_completion(self.server) # Create test NIC. self.nic = self.client.create_nic( datacenter_id=self.datacenter['id'], server_id=self.server['id'], nic=NIC(**self.resource['nic'])) self.client.wait_for_completion(self.nic) # Find an Ubuntu image for testing. for item in self.client.list_images()['items']: if (configuration.IMAGE_NAME in item['properties']['name'] and item['properties']['location'] == configuration.LOCATION): self.image = item # Find a cdrom image images = self.client.list_images(depth=5) usedIndex = 0 for index, image in enumerate(images['items']): if (image['metadata']['state'] == "AVAILABLE" and image['properties']['public'] is True and image['properties']['imageType'] == "CDROM" and image['properties']['location'] == configuration.LOCATION and image['properties']['licenceType'] == "LINUX"): if(usedIndex == 0): self.test_image1 = image usedIndex = index else: self.test_image2 = image break # Create test cdrom self.cdrom = self.client.attach_cdrom( datacenter_id=self.datacenter['id'], server_id=self.server['id'], cdrom_id=self.test_image1['id']) self.client.wait_for_completion(self.cdrom) @classmethod def tearDownClass(self): self.client.delete_datacenter(datacenter_id=self.datacenter['id']) def test_list_servers(self): servers = self.client.list_servers(datacenter_id=self.datacenter['id']) self.assertGreater(len(servers), 0) self.assertEqual(servers['items'][0]['type'], 'server') self.assertTrue(self, len(servers['items']) > 0) assertRegex(self, servers['items'][0]['id'], self.resource['uuid_match']) def test_get_server(self): server = self.client.get_server( datacenter_id=self.datacenter['id'], server_id=self.server['id'] ) self.assertEqual(server['type'], 'server') self.assertEqual(server['id'], self.server['id']) self.assertEqual(server['properties']['name'], self.resource['server']['name']) self.assertEqual(server['properties']['cores'], self.resource['server']['cores']) self.assertEqual(server['properties']['ram'], self.resource['server']['ram']) self.assertEqual(server['properties']['availabilityZone'], self.resource['server']['availability_zone']) self.assertEqual(server['properties']['cpuFamily'], self.resource['server']['cpu_family']) # assertRegex(self, server['properties']['bootVolume']['id'], self.resource['uuid_match']) def test_get_failure(self): try: self.client.get_server( datacenter_id=self.datacenter['id'], server_id='00000000-0000-0000-0000-000000000000') except PBNotFoundError as e: self.assertIn(self.resource['not_found_error'], e.content[0]['message']) def test_delete_server(self): server = self.client.create_server( datacenter_id=self.datacenter['id'], server=Server(**self.resource['server']) ) self.client.wait_for_completion(server) response = self.client.delete_server( datacenter_id=self.datacenter['id'], server_id=server['id'] ) self.assertTrue(response) def test_update_server(self): server = self.client.update_server( datacenter_id=self.datacenter['id'], server_id=self.server['id'], name=self.resource['server']['name'] + ' RENAME') self.client.wait_for_completion(server) server = self.client.get_server( datacenter_id=self.datacenter['id'], server_id=self.server['id'] ) self.assertEqual(server['id'], self.server['id']) self.assertEqual(server['properties']['name'], self.resource['server']['name'] + ' RENAME') self.assertEqual(server['properties']['cores'], self.resource['server']['cores']) self.assertEqual(server['properties']['ram'], self.resource['server']['ram']) def test_create_server(self): # Use server created during server test setup assertRegex(self, self.server['id'], self.resource['uuid_match']) self.assertEqual(self.server['type'], 'server') self.assertEqual(self.server['properties']['name'], self.resource['server']['name']) self.assertEqual(self.server['properties']['cores'], self.resource['server']['cores']) self.assertEqual(self.server['properties']['ram'], self.resource['server']['ram']) self.assertEqual(self.server['properties']['availabilityZone'], self.resource['server']['availability_zone']) self.assertEqual(self.server['properties']['cpuFamily'], self.resource['server']['cpu_family']) # assertRegex(self, server['properties']['bootVolume']['id'], self.resource['uuid_match']) # self.assertIsNone(self.server['properties']['availabilityZone']) self.assertIsNone(self.server['properties']['vmState']) def test_create_failure(self): try: server = Server( name=self.resource['server']['name'], ram=self.resource['server']['ram'] ) self.client.create_server(datacenter_id=self.datacenter['id'], server=server) except PBError as e: self.assertIn(self.resource['missing_attribute_error'] % 'cores', e.content[0]['message']) def test_create_composite(self): fwrule = FirewallRule(**self.resource['fwrule']) nic = NIC(firewall_rules=[fwrule], **self.resource['nic']) volume = Volume(image=self.image['id'], image_password='secretpassword123', ssh_keys=['ssh-rsa AAAAB3NzaC1'], **self.resource['volume']) volume.availability_zone = 'ZONE_3' server = Server( nics=[nic], create_volumes=[volume], **self.resource['server']) composite_server = self.client.create_server( datacenter_id=self.datacenter['id'], server=server) self.client.wait_for_completion(composite_server, timeout=600) composite_server = self.client.get_server( datacenter_id=self.datacenter['id'], server_id=composite_server['id']) assertRegex(self, composite_server['id'], self.resource['uuid_match']) self.assertEqual(composite_server['properties']['name'], self.resource['server']['name']) self.assertEqual(composite_server['properties']['cores'], self.resource['server']['cores']) self.assertEqual(composite_server['properties']['ram'], self.resource['server']['ram']) self.assertEqual(composite_server['properties']['availabilityZone'], 'ZONE_1') self.assertIn(composite_server['properties']['vmState'], self.resource['vm_states']) self.assertGreater(len(composite_server['entities']['volumes']['items']), 0) self.assertGreater(len(composite_server['entities']['nics']['items']), 0) def test_start_server(self): server = self.client.start_server( datacenter_id=self.datacenter['id'], server_id=self.server['id']) self.assertTrue(server) def test_stop_server(self): server = self.client.stop_server( datacenter_id=self.datacenter['id'], server_id=self.server['id']) self.assertTrue(server) def test_reboot_server(self): server = self.client.reboot_server( datacenter_id=self.datacenter['id'], server_id=self.server['id']) self.assertTrue(server) def test_get_attached_volumes(self): volumes = self.client.get_attached_volumes( datacenter_id=self.datacenter['id'], server_id=self.server['id']) self.assertGreater(len(volumes['items']), 0) self.assertEqual(volumes['items'][0]['type'], 'volume') self.assertEqual(volumes['items'][0]['id'], self.volume1['id']) self.assertEqual(volumes['items'][0]['properties']['name'], self.resource['volume']['name']) self.assertEqual(volumes['items'][0]['properties']['size'], self.resource['volume']['size']) self.assertEqual(volumes['items'][0]['properties']['bus'], self.resource['volume']['bus']) self.assertEqual(volumes['items'][0]['properties']['type'], self.resource['volume']['disk_type']) self.assertEqual(volumes['items'][0]['properties']['licenceType'], 'UNKNOWN') self.assertIsNone(volumes['items'][0]['properties']['image']) self.assertIsNone(volumes['items'][0]['properties']['imagePassword']) self.assertFalse(volumes['items'][0]['properties']['cpuHotPlug']) self.assertFalse(volumes['items'][0]['properties']['cpuHotUnplug']) self.assertFalse(volumes['items'][0]['properties']['ramHotPlug']) self.assertFalse(volumes['items'][0]['properties']['ramHotUnplug']) self.assertFalse(volumes['items'][0]['properties']['nicHotPlug']) self.assertFalse(volumes['items'][0]['properties']['nicHotUnplug']) self.assertFalse(volumes['items'][0]['properties']['discVirtioHotPlug']) self.assertFalse(volumes['items'][0]['properties']['discVirtioHotUnplug']) self.assertFalse(volumes['items'][0]['properties']['discScsiHotPlug']) self.assertFalse(volumes['items'][0]['properties']['discScsiHotUnplug']) def test_get_attached_volume(self): volume = self.client.get_attached_volume( datacenter_id=self.datacenter['id'], server_id=self.server['id'], volume_id=self.volume1['id']) self.assertEqual(volume['id'], self.volume1['id']) self.assertEqual(volume['properties']['name'], self.resource['volume']['name']) self.assertEqual(volume['properties']['size'], self.resource['volume']['size']) self.assertEqual(volume['properties']['bus'], self.resource['volume']['bus']) self.assertEqual(volume['properties']['type'], self.resource['volume']['disk_type']) self.assertEqual(volume['properties']['licenceType'], self.resource['volume']['licence_type']) self.assertIsNone(volume['properties']['image']) self.assertIsNone(volume['properties']['imagePassword']) self.assertFalse(volume['properties']['cpuHotPlug']) self.assertFalse(volume['properties']['cpuHotUnplug']) self.assertFalse(volume['properties']['ramHotPlug']) self.assertFalse(volume['properties']['ramHotUnplug']) self.assertFalse(volume['properties']['nicHotPlug']) self.assertFalse(volume['properties']['nicHotUnplug']) self.assertFalse(volume['properties']['discVirtioHotPlug']) self.assertFalse(volume['properties']['discVirtioHotUnplug']) self.assertFalse(volume['properties']['discScsiHotPlug']) self.assertFalse(volume['properties']['discScsiHotUnplug']) def test_attach_volume(self): volume = self.client.attach_volume( datacenter_id=self.datacenter['id'], server_id=self.server['id'], volume_id=self.volume2['id']) self.client.wait_for_completion(volume) self.assertEqual(volume['id'], self.volume2['id']) self.assertEqual(volume['properties']['name'], self.resource['volume']['name']) self.assertEqual(volume['properties']['size'], self.resource['volume']['size']) self.assertEqual(volume['properties']['type'], self.resource['volume']['disk_type']) self.assertEqual(volume['properties']['licenceType'], self.resource['volume']['licence_type']) self.assertIsNone(volume['properties']['bus']) self.assertIsNone(volume['properties']['image']) self.assertIsNone(volume['properties']['imagePassword']) self.assertFalse(volume['properties']['cpuHotPlug']) self.assertFalse(volume['properties']['cpuHotUnplug']) self.assertFalse(volume['properties']['ramHotPlug']) self.assertFalse(volume['properties']['ramHotUnplug']) self.assertFalse(volume['properties']['nicHotPlug']) self.assertFalse(volume['properties']['nicHotUnplug']) self.assertFalse(volume['properties']['discVirtioHotPlug']) self.assertFalse(volume['properties']['discVirtioHotUnplug']) self.assertFalse(volume['properties']['discScsiHotPlug']) self.assertFalse(volume['properties']['discScsiHotUnplug']) self.client.detach_volume( datacenter_id=self.datacenter['id'], server_id=self.server['id'], volume_id=self.volume2['id']) def test_detach_volume(self): volume = self.client.detach_volume( datacenter_id=self.datacenter['id'], server_id=self.server['id'], volume_id=self.volume1['id']) self.assertTrue(volume) def test_list_cdroms(self): cdroms = self.client.get_attached_cdroms( datacenter_id=self.datacenter['id'], server_id=self.server['id']) self.assertGreater(len(cdroms['items']), 0) def test_attach_cdrom(self): attached_cdrom = self.client.attach_cdrom( datacenter_id=self.datacenter['id'], server_id=self.server['id'], cdrom_id=self.test_image2['id']) self.client.wait_for_completion(attached_cdrom, timeout=600) self.assertEqual(attached_cdrom['id'], self.test_image2['id']) self.assertEqual(attached_cdrom['properties']['name'], self.test_image2['properties']['name']) def test_get_cdrom(self): attached_cdrom = self.client.attach_cdrom( datacenter_id=self.datacenter['id'], server_id=self.server['id'], cdrom_id=self.test_image1['id']) self.client.wait_for_completion(attached_cdrom, timeout=600) cdrom = self.client.get_attached_cdrom( datacenter_id=self.datacenter['id'], server_id=self.server['id'], cdrom_id=attached_cdrom['id']) self.assertEqual(cdrom['id'], attached_cdrom['id']) self.assertEqual(cdrom['properties']['name'], attached_cdrom['properties']['name']) def test_detach_cdrom(self): detached_cd = self.client.detach_cdrom( datacenter_id=self.datacenter['id'], server_id=self.server['id'], cdrom_id=self.cdrom['id']) time.sleep(15) self.assertTrue(detached_cd) try: check_detached_cdrom_gone(self) except PBNotFoundError as e: self.assertIn(self.resource['not_found_error'], e.content[0]['message']) if __name__ == '__main__': unittest.main() profitbricks-sdk-python-4.1.3/tests/test_snapshot.py000066400000000000000000000154561326076220500227460ustar00rootroot00000000000000# Copyright 2015-2017 ProfitBricks GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import unittest from helpers import configuration from helpers.resources import resource from profitbricks.client import ProfitBricksService from profitbricks.client import Datacenter, Volume, Snapshot from profitbricks.errors import PBNotFoundError class TestSnapshot(unittest.TestCase): @classmethod def setUpClass(self): self.resource = resource() self.client = ProfitBricksService( username=configuration.USERNAME, password=configuration.PASSWORD, headers=configuration.HEADERS) # Create test datacenter. self.datacenter = self.client.create_datacenter( datacenter=Datacenter(**self.resource['datacenter'])) self.client.wait_for_completion(self.datacenter) # Create test volume volume = Volume(**self.resource['volume']) self.volume = self.client.create_volume( datacenter_id=self.datacenter['id'], volume=volume ) self.client.wait_for_completion(self.volume) # Create test volume1 volume1 = Volume(**self.resource['volume']) self.volume1 = self.client.create_volume( datacenter_id=self.datacenter['id'], volume=volume1 ) self.client.wait_for_completion(self.volume1) # Create test snapshot snapshot = Snapshot(**self.resource['snapshot']) self.snapshot1 = self.client.create_snapshot( datacenter_id=self.datacenter['id'], volume_id=self.volume['id'], name=snapshot.name, description=snapshot.description) self.client.wait_for_completion(self.snapshot1) # Create test snapshot2 self.snapshot2 = self.client.create_snapshot( datacenter_id=self.datacenter['id'], volume_id=self.volume['id'], name="python sdk test snapshot", description="snapshot test description") self.client.wait_for_completion(self.snapshot2) @classmethod def tearDownClass(self): self.client.delete_snapshot(snapshot_id=self.snapshot1['id']) self.client.delete_datacenter(datacenter_id=self.datacenter['id']) def test_list_snapshots(self): snapshots = self.client.list_snapshots() self.assertGreater(len(snapshots['items']), 0) self.assertEqual(snapshots['items'][0]['type'], 'snapshot') def test_get_snapshot(self): snapshot = self.client.get_snapshot(snapshot_id=self.snapshot1['id']) self.assertEqual(snapshot['type'], 'snapshot') self.assertEqual(snapshot['id'], self.snapshot1['id']) self.assertEqual(snapshot['properties']['name'], self.resource['snapshot']['name']) self.assertTrue(snapshot['properties']['description'], self.resource['snapshot']['description']) self.assertEqual(snapshot['properties']['location'], configuration.LOCATION) self.assertEqual(snapshot['properties']['size'], self.volume['properties']['size']) self.assertEqual(snapshot['properties']['cpuHotPlug'], self.volume['properties']['cpuHotPlug']) self.assertEqual(snapshot['properties']['cpuHotUnplug'], self.volume['properties']['cpuHotUnplug']) self.assertEqual(snapshot['properties']['ramHotPlug'], self.volume['properties']['ramHotPlug']) self.assertEqual(snapshot['properties']['ramHotUnplug'], self.volume['properties']['ramHotUnplug']) self.assertEqual(snapshot['properties']['nicHotPlug'], self.volume['properties']['nicHotPlug']) self.assertEqual(snapshot['properties']['nicHotUnplug'], self.volume['properties']['nicHotUnplug']) self.assertEqual(snapshot['properties']['discVirtioHotPlug'], self.volume['properties']['discVirtioHotPlug']) self.assertEqual(snapshot['properties']['discVirtioHotUnplug'], self.volume['properties']['discVirtioHotUnplug']) self.assertEqual(snapshot['properties']['discScsiHotPlug'], self.volume['properties']['discScsiHotPlug']) self.assertEqual(snapshot['properties']['discScsiHotUnplug'], self.volume['properties']['discScsiHotUnplug']) self.assertEqual(snapshot['properties']['licenceType'], self.volume['properties']['licenceType']) def test_delete_snapshot(self): snapshot = self.client.delete_snapshot(snapshot_id=self.snapshot2['id']) self.assertTrue(snapshot) def test_update_snapshot(self): snapshot = self.client.update_snapshot( snapshot_id=self.snapshot1['id'], name=self.resource['snapshot']['name'] + ' - RENAME', description=self.resource['snapshot']['description'] + ' - RENAME') self.client.wait_for_completion(snapshot) self.assertEqual(snapshot['type'], 'snapshot') self.assertEqual(snapshot['properties']['name'], self.resource['snapshot']['name'] + ' - RENAME') self.assertEqual(snapshot['properties']['description'], self.resource['snapshot']['description'] + ' - RENAME') def test_create_snapshot(self): self.assertEqual(self.snapshot1['type'], 'snapshot') self.assertEqual(self.snapshot1['properties']['name'], self.resource['snapshot']['name']) self.assertEqual(self.snapshot1['properties']['description'], self.resource['snapshot']['description']) def test_get_failure(self): try: self.client.get_snapshot('00000000-0000-0000-0000-000000000000') except PBNotFoundError as e: self.assertIn(self.resource['not_found_error'], e.content[0]['message']) def test_create_failure(self): try: self.client.create_snapshot( datacenter_id='00000000-0000-0000-0000-000000000000', volume_id=self.volume['id'], name=self.resource['snapshot']['name']) except PBNotFoundError as e: self.assertIn(self.resource['not_found_error'], e.content[0]['message']) if __name__ == '__main__': unittest.main() profitbricks-sdk-python-4.1.3/tests/test_user_management.py000066400000000000000000000403111326076220500242450ustar00rootroot00000000000000# Copyright 2015-2017 ProfitBricks GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import unittest from random import randint from helpers import configuration from helpers.resources import resource, find_image from profitbricks.client import ProfitBricksService from profitbricks.client import Datacenter, IPBlock, User, Group, Volume from profitbricks.errors import PBError, PBNotFoundError class TestUserManagement(unittest.TestCase): @classmethod def setUpClass(self): self.resource = resource() self.client = ProfitBricksService( username=configuration.USERNAME, password=configuration.PASSWORD, headers=configuration.HEADERS) # Create datacenter resource self.datacenter = self.client.create_datacenter( datacenter=Datacenter(**self.resource['datacenter'])) self.client.wait_for_completion(self.datacenter) # Create volume resource volume = Volume(**self.resource['volume']) self.volume = self.client.create_volume( datacenter_id=self.datacenter['id'], volume=volume ) self.client.wait_for_completion(self.volume) self.image = find_image(self.client, configuration.IMAGE_NAME) # Create snapshot resource self.snapshot = self.client.create_snapshot( datacenter_id=self.datacenter['id'], volume_id=self.volume['id'], name=self.resource['snapshot']['name']) self.client.wait_for_completion(self.snapshot) # Reserve IP block resource self.ipblock = self.client.reserve_ipblock(IPBlock(**self.resource['ipblock'])) # Create User 1 self.user_dict1 = User( firstname='John', lastname='Doe', email='no-reply%s@example.com' % randint(0, 9999999999999), password='secretpassword123%s' % randint(0, 99999999), administrator=True, force_sec_auth=False) self.user1 = self.client.create_user(user=self.user_dict1) # Create User 2 self.user_dict2 = User( firstname='John', lastname='Doe', email='no-reply%s@example.com' % randint(0, 9999999999999), password='secretpassword123%s' % randint(0, 99999999)) self.user2 = self.client.create_user(user=self.user_dict2) # Create User 3 self.user_dict3 = User( firstname='John', lastname='Doe', email='no-reply%s@example.com' % randint(0, 9999999999999), password='secretpassword123%s' % randint(0, 99999999)) self.user3 = self.client.create_user(user=self.user_dict3) # Create Group 1 group = Group(**self.resource['group']) self.group1 = self.client.create_group(group) # Create Group 2 group.name = self.resource['group']['name'] + ' 2' self.group2 = self.client.create_group(group) # Create Group 3 group.name = self.resource['group']['name'] + ' 3' self.group3 = self.client.create_group(group) # Create Share 1 self.share1 = self.client.add_share( group_id=self.group3['id'], resource_id=self.datacenter['id'], edit_privilege=True, share_privilege=True) @classmethod def tearDownClass(self): self.client.delete_share(group_id=self.group3['id'], resource_id=self.datacenter['id']) self.client.delete_snapshot(snapshot_id=self.snapshot['id']) self.client.delete_user(user_id=self.user1['id']) self.client.delete_user(user_id=self.user3['id']) self.client.delete_group(group_id=self.group1['id']) self.client.delete_group(group_id=self.group3['id']) self.client.delete_ipblock(ipblock_id=self.ipblock['id']) self.client.delete_datacenter(datacenter_id=self.datacenter['id']) def test_create_user(self): self.assertEqual(self.user1['type'], 'user') self.assertEqual(self.user1['properties']['firstname'], self.user_dict1.firstname) self.assertEqual(self.user1['properties']['lastname'], self.user_dict1.lastname) self.assertEqual(self.user1['properties']['email'], self.user_dict1.email) self.assertEqual(self.user1['properties']['administrator'], self.user_dict1.administrator) self.assertEqual(self.user1['properties']['forceSecAuth'], self.user_dict1.force_sec_auth) def test_list_users(self): users = self.client.list_users() self.assertGreater(len(users['items']), 0) self.assertEqual(users['items'][0]['type'], 'user') def test_get_user(self): user = self.client.get_user(user_id=self.user1['id']) self.assertEqual(user['type'], 'user') self.assertEqual(user['id'], self.user1['id']) self.assertEqual(user['properties']['firstname'], self.user1['properties']['firstname']) self.assertEqual(user['properties']['lastname'], self.user1['properties']['lastname']) self.assertEqual(user['properties']['email'], self.user1['properties']['email']) self.assertEqual(user['properties']['administrator'], self.user1['properties']['administrator']) self.assertEqual(user['properties']['forceSecAuth'], self.user1['properties']['forceSecAuth']) self.assertFalse(user['properties']['secAuthActive']) def test_delete_user(self): user = self.client.delete_user(user_id=self.user2['id']) self.assertTrue(user) def test_update_user(self): user = self.client.update_user( user_id=self.user1['id'], firstname=self.user1['properties']['firstname'], lastname=self.user1['properties']['lastname'], email=self.user1['properties']['email'], administrator=False, force_sec_auth=self.user1['properties']['forceSecAuth'] ) self.assertEqual(user['type'], 'user') self.assertEqual(user['id'], self.user1['id']) self.assertEqual(user['properties']['firstname'], self.user1['properties']['firstname']) self.assertEqual(user['properties']['lastname'], self.user1['properties']['lastname']) self.assertEqual(user['properties']['email'], self.user1['properties']['email']) self.assertFalse(user['properties']['administrator']) self.assertEqual(user['properties']['forceSecAuth'], self.user1['properties']['forceSecAuth']) def test_create_user_failure(self): try: user = User( firstname='John', lastname='Doe', password='secretpassword123') self.client.create_user(user) except PBError as e: self.assertIn(self.resource['missing_attribute_error'] % 'email', e.content[0]['message']) def test_get_user_failure(self): try: self.client.get_user('00000000-0000-0000-0000-000000000000') except PBNotFoundError as e: self.assertIn(self.resource['not_found_error'], e.content[0]['message']) def test_create_group(self): self.assertEqual(self.group1['type'], 'group') self.assertEqual(self.group1['properties']['name'], self.resource['group']['name']) self.assertEqual(self.group1['properties']['createDataCenter'], self.resource['group']['create_datacenter']) self.assertEqual(self.group1['properties']['createSnapshot'], self.resource['group']['create_snapshot']) self.assertEqual(self.group1['properties']['reserveIp'], self.resource['group']['reserve_ip']) self.assertEqual(self.group1['properties']['accessActivityLog'], self.resource['group']['access_activity_log']) def test_list_groups(self): groups = self.client.list_groups() self.assertGreater(len(groups['items']), 0) self.assertEqual(groups['items'][0]['type'], 'group') def test_get_group(self): group = self.client.get_group(group_id=self.group1['id']) self.assertEqual(group['type'], 'group') self.assertEqual(group['id'], self.group1['id']) self.assertEqual(group['properties']['name'], self.group1['properties']['name']) self.assertEqual(group['properties']['createDataCenter'], self.group1['properties']['createDataCenter']) self.assertEqual(group['properties']['createSnapshot'], self.group1['properties']['createSnapshot']) self.assertEqual(group['properties']['reserveIp'], self.group1['properties']['reserveIp']) self.assertEqual(group['properties']['accessActivityLog'], self.group1['properties']['accessActivityLog']) def test_update_group(self): group = self.client.update_group( group_id=self.group1['id'], name=self.resource['group']['name'] + ' - RENAME', create_datacenter=False ) self.assertEqual(group['type'], 'group') self.assertEqual(group['id'], self.group1['id']) self.assertEqual(group['properties']['name'], self.resource['group']['name'] + ' - RENAME') self.assertFalse(group['properties']['createDataCenter']) def test_delete_group(self): group = self.client.delete_group(group_id=self.group2['id']) self.assertTrue(group) def test_create_group_failure(self): try: self.client.create_group(Group(create_datacenter=True)) except PBError as e: self.assertIn(self.resource['missing_attribute_error'] % 'name', e.content[0]['message']) def test_get_group_failure(self): try: self.client.get_group('00000000-0000-0000-0000-000000000000') except PBNotFoundError as e: self.assertIn(self.resource['not_found_error'], e.content[0]['message']) def test_add_share(self): self.assertEqual(self.share1['type'], 'resource') self.assertTrue(self.share1['properties']['editPrivilege']) self.assertTrue(self.share1['properties']['sharePrivilege']) def test_list_shares(self): shares = self.client.list_shares(group_id=self.group3['id']) self.assertGreater(len(shares['items']), 0) self.assertEqual(shares['items'][0]['type'], 'resource') def test_get_share(self): share = self.client.get_share(group_id=self.group3['id'], resource_id=self.datacenter['id']) self.assertEqual(share['id'], self.datacenter['id']) self.assertEqual(share['type'], 'resource') self.assertTrue(share['properties']['editPrivilege']) self.assertTrue(share['properties']['sharePrivilege']) def test_update_share(self): share = self.client.update_share(group_id=self.group3['id'], resource_id=self.datacenter['id'], share_privilege=False) self.assertEqual(share['id'], self.datacenter['id']) self.assertEqual(share['type'], 'resource') self.assertFalse(share['properties']['sharePrivilege']) def test_get_share_failure(self): try: self.client.get_share(group_id=self.group3['id'], resource_id='00000000-0000-0000-0000-000000000000') except PBNotFoundError as e: self.assertIn(self.resource['not_found_error'], e.content[0]['message']) def test_create_share_failure(self): try: self.client.add_share(group_id=self.group3['id'], resource_id='00000000-0000-0000-0000-000000000000') except PBNotFoundError as e: self.assertIn(self.resource['not_found_error'], e.content[0]['message']) def test_list_group_users(self): users = self.client.list_group_users(group_id=self.group3['id']) self.assertGreater(len(users['items']), 0) self.assertEqual(users['items'][0]['type'], 'user') def test_add_group_user(self): user = self.client.add_group_user(group_id=self.group3['id'], user_id=self.user3['id']) self.assertEqual(user['id'], self.user3['id']) self.assertEqual(user['type'], 'user') def test_remove_group_user(self): user = self.client.remove_group_user(group_id=self.group3['id'], user_id=self.user3['id']) self.assertTrue(user) def test_list_resources(self): resources = self.client.list_resources() self.assertGreater(len(resources['items']), 0) self.assertEqual(resources['id'], 'resources') self.assertEqual(resources['type'], 'collection') def test_list_datacenter_resources(self): resources = self.client.list_resources(resource_type='datacenter') self.assertGreater(len(resources['items']), 0) self.assertEqual(resources['id'], 'resources') self.assertEqual(resources['items'][0]['type'], 'datacenter') def test_list_image_resources(self): resources = self.client.list_resources(resource_type='image') self.assertGreater(len(resources['items']), 0) self.assertEqual(resources['id'], 'resources') self.assertEqual(resources['items'][0]['type'], 'image') def test_list_snapshot_resources(self): resources = self.client.list_resources(resource_type='snapshot') self.assertGreater(len(resources['items']), 0) self.assertEqual(resources['id'], 'resources') self.assertEqual(resources['items'][0]['type'], 'snapshot') def test_list_ipblock_resources(self): resources = self.client.list_resources(resource_type='ipblock') self.assertGreater(len(resources['items']), 0) self.assertEqual(resources['id'], 'resources') self.assertEqual(resources['items'][0]['type'], 'ipblock') def test_list_resources_failure(self): try: self.client.list_resources(resource_type='unknown') except PBNotFoundError as e: self.assertIn(self.resource['not_found_error'], e.content[0]['message']) def test_get_datacenter_resource(self): resource = self.client.get_resource(resource_type='datacenter', resource_id=self.datacenter['id']) self.assertEqual(resource['id'], self.datacenter['id']) self.assertEqual(resource['type'], 'datacenter') def test_get_image_resource(self): resource = self.client.get_resource(resource_type='image', resource_id=self.image['id']) self.assertEqual(resource['id'], self.image['id']) self.assertEqual(resource['type'], 'image') def test_get_snapshot_resource(self): resource = self.client.get_resource(resource_type='snapshot', resource_id=self.snapshot['id']) self.assertEqual(resource['id'], self.snapshot['id']) self.assertEqual(resource['type'], 'snapshot') def test_list_ipblock_resources2(self): resource = self.client.get_resource(resource_type='ipblock', resource_id=self.ipblock['id']) self.assertEqual(resource['id'], self.ipblock['id']) self.assertEqual(resource['type'], 'ipblock') def test_get_resource_failure(self): try: self.client.get_resource(resource_type='datacenter', resource_id='00000000-0000-0000-0000-000000000000') except PBNotFoundError as e: self.assertIn(self.resource['not_found_error'], e.content[0]['message']) if __name__ == '__main__': unittest.main() profitbricks-sdk-python-4.1.3/tests/test_volume.py000066400000000000000000000165271326076220500224160ustar00rootroot00000000000000# Copyright 2015-2017 ProfitBricks GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import unittest from helpers import configuration from helpers.resources import resource, find_image from profitbricks.client import Datacenter, Volume from profitbricks.client import ProfitBricksService from profitbricks.errors import PBError, PBNotFoundError from six import assertRegex class TestVolume(unittest.TestCase): @classmethod def setUpClass(self): self.resource = resource() self.client = ProfitBricksService( username=configuration.USERNAME, password=configuration.PASSWORD, headers=configuration.HEADERS) # Create test datacenter self.datacenter = self.client.create_datacenter( datacenter=Datacenter(**self.resource['datacenter'])) self.client.wait_for_completion(self.datacenter) self.image = find_image(self.client, configuration.IMAGE_NAME) # Create test volume vol = Volume(**self.resource['volume2']) vol.image = self.image['id'] self.volume = self.client.create_volume( datacenter_id=self.datacenter['id'], volume=vol) self.client.wait_for_completion(self.volume) # Create snapshot1 self.snapshot1 = self.client.create_snapshot( datacenter_id=self.datacenter['id'], volume_id=self.volume['id'], name=self.resource['snapshot']['name'], description=self.resource['snapshot']['description']) self.client.wait_for_completion(self.snapshot1, timeout=600) @classmethod def tearDownClass(self): self.client.delete_datacenter(datacenter_id=self.datacenter['id']) def test_list_volumes(self): volumes = self.client.list_volumes( datacenter_id=self.datacenter['id']) self.assertGreater(len(volumes), 0) assertRegex(self, volumes['items'][0]['id'], self.resource['uuid_match']) self.assertEqual(volumes['items'][0]['type'], 'volume') self.assertEqual(volumes['items'][0]['properties']['name'], self.resource['volume2']['name']) self.assertEqual(volumes['items'][0]['properties']['size'], self.resource['volume2']['size']) self.assertEqual(volumes['items'][0]['properties']['type'], self.resource['volume2']['disk_type']) self.assertIsNone(volumes['items'][0]['properties']['bus']) def test_get_volume(self): volume = self.client.get_volume( datacenter_id=self.datacenter['id'], volume_id=self.volume['id']) self.assertEqual(volume['id'], self.volume['id']) self.assertEqual(volume['type'], 'volume') self.assertEqual(volume['properties']['name'], self.resource['volume2']['name']) self.assertEqual(volume['properties']['size'], self.resource['volume2']['size']) self.assertEqual(volume['properties']['licenceType'], self.image['properties']['licenceType']) self.assertEqual(volume['properties']['type'], self.resource['volume2']['disk_type']) self.assertIsNone(volume['properties']['bus']) self.assertEqual(volume['properties']['availabilityZone'], self.resource['volume2']['availability_zone']) def test_delete_volume(self): volume = self.client.create_volume( datacenter_id=self.datacenter['id'], volume=Volume(**self.resource['volume'])) self.client.wait_for_completion(volume) volume = self.client.delete_volume( datacenter_id=self.datacenter['id'], volume_id=volume['id']) self.assertTrue(volume) def test_update_volume(self): volume = self.client.update_volume( datacenter_id=self.datacenter['id'], volume_id=self.volume['id'], size=6, name=self.resource['volume2']['name'] + ' - RENAME') self.client.wait_for_completion(volume) volume = self.client.get_volume( datacenter_id=self.datacenter['id'], volume_id=self.volume['id']) self.assertEqual(volume['id'], self.volume['id']) self.assertEqual(volume['properties']['name'], self.resource['volume2']['name'] + ' - RENAME') self.assertEqual(volume['properties']['size'], 6) def test_create_volume(self): # Use volume created during volume test setup. assertRegex(self, self.volume['id'], self.resource['uuid_match']) self.assertEqual(self.volume['properties']['name'], self.resource['volume2']['name']) self.assertEqual(self.volume['properties']['bus'], self.resource['volume2']['bus']) self.assertEqual(self.volume['properties']['type'], self.resource['volume2']['disk_type']) self.assertEqual(self.volume['properties']['size'], self.resource['volume2']['size']) self.assertEqual(self.volume['properties']['sshKeys'], self.resource['volume2']['ssh_keys']) self.assertEqual(self.volume['properties']['availabilityZone'], self.resource['volume2']['availability_zone']) def test_create_snapshot(self): # Use snapshot created during volume test setup. self.assertEqual(self.snapshot1['type'], 'snapshot') self.assertEqual(self.snapshot1['properties']['name'], self.resource['snapshot']['name']) self.assertEqual(self.snapshot1['properties']['description'], self.resource['snapshot']['description']) self.assertEqual(self.snapshot1['properties']['location'], configuration.LOCATION) self.assertIsNone(self.snapshot1['properties']['size']) self.assertIsNone(self.snapshot1['properties']['licenceType']) def test_restore_snapshot(self): response = self.client.restore_snapshot( datacenter_id=self.datacenter['id'], volume_id=self.volume['id'], snapshot_id=self.snapshot1['id']) self.assertTrue(response) def test_remove_snapshot(self): volume = self.client.remove_snapshot(snapshot_id=self.snapshot1['id']) self.assertTrue(volume) def test_get_failure(self): try: self.client.get_volume( datacenter_id=self.datacenter['id'], volume_id='00000000-0000-0000-0000-000000000000') except PBNotFoundError as e: self.assertIn(self.resource['not_found_error'], e.content[0]['message']) def test_create_failure(self): try: volume = Volume(name=self.resource['volume2']['name']) self.client.create_volume(datacenter_id=self.datacenter['id'], volume=volume) except PBError as e: self.assertIn(self.resource['missing_attribute_error'] % 'size', e.content[0]['message']) if __name__ == '__main__': unittest.main() profitbricks-sdk-python-4.1.3/tox.ini000066400000000000000000000010121326076220500176260ustar00rootroot00000000000000# Tox (http://tox.testrun.org/) is a tool for running tests # in multiple virtualenvs. This configuration file will run the # test suite on all supported python versions. To use it, "pip install tox" # and then run "tox" from this directory. # # tox.ini is also used for local default parameters for pepe8 and flake8 # [tox] envlist = py27, py34 [testenv] commands = {envpython} setup.py test deps = requests [pep8] max-line-length = 99 exclude = build,debian [flake8] max-line-length = 99 exclude = build,debian