Permalink
Browse files

Merge storage_api branch.

git-svn-id: https://svn.apache.org/repos/asf/incubator/libcloud/trunk@1079029 13f79535-47bb-0310-9956-ffa450edef68
  • Loading branch information...
1 parent 0a39465 commit 80ccbfe5a5fbc85062d96efacebcf8609f6fc9d7 @Kami Kami committed Mar 7, 2011
Showing with 21,232 additions and 0 deletions.
  1. +185 −0 CHANGES
  2. +8 −0 DISCLAIMER
  3. +76 −0 HACKING
  4. +202 −0 LICENSE
  5. +13 −0 MANIFEST.in
  6. +8 −0 NOTICE
  7. +60 −0 README
  8. +128 −0 demos/ec2_demo.py
  9. +21 −0 demos/secrets.py.dist
  10. +147 −0 dist/hash-sign.sh
  11. +39 −0 dist/release.sh
  12. +36 −0 example_compute.py
  13. +29 −0 example_storage.py
  14. +56 −0 libcloud/__init__.py
  15. +25 −0 libcloud/base.py
  16. 0 libcloud/common/__init__.py
  17. +420 −0 libcloud/common/base.py
  18. +45 −0 libcloud/common/types.py
  19. 0 libcloud/compute/__init__.py
  20. +573 −0 libcloud/compute/base.py
  21. +130 −0 libcloud/compute/deployment.py
  22. +38 −0 libcloud/compute/drivers/__init__.py
  23. +221 −0 libcloud/compute/drivers/brightbox.py
  24. +654 −0 libcloud/compute/drivers/cloudsigma.py
  25. +243 −0 libcloud/compute/drivers/dreamhost.py
  26. +297 −0 libcloud/compute/drivers/dummy.py
  27. +937 −0 libcloud/compute/drivers/ec2.py
  28. +360 −0 libcloud/compute/drivers/ecp.py
  29. +568 −0 libcloud/compute/drivers/elastichosts.py
  30. +470 −0 libcloud/compute/drivers/gogrid.py
  31. +191 −0 libcloud/compute/drivers/ibm_sbc.py
  32. +618 −0 libcloud/compute/drivers/linode.py
  33. +219 −0 libcloud/compute/drivers/opennebula.py
  34. +601 −0 libcloud/compute/drivers/rackspace.py
  35. +313 −0 libcloud/compute/drivers/rimuhosting.py
  36. +243 −0 libcloud/compute/drivers/slicehost.py
  37. +442 −0 libcloud/compute/drivers/softlayer.py
  38. +624 −0 libcloud/compute/drivers/vcloud.py
  39. +308 −0 libcloud/compute/drivers/voxel.py
  40. +199 −0 libcloud/compute/drivers/vpsnet.py
  41. +76 −0 libcloud/compute/providers.py
  42. +186 −0 libcloud/compute/ssh.py
  43. +103 −0 libcloud/compute/types.py
  44. +19 −0 libcloud/deployment.py
  45. +38 −0 libcloud/drivers/__init__.py
  46. +19 −0 libcloud/drivers/brightbox.py
  47. +20 −0 libcloud/drivers/cloudsigma.py
  48. +19 −0 libcloud/drivers/dreamhost.py
  49. +19 −0 libcloud/drivers/dummy.py
  50. +19 −0 libcloud/drivers/ec2.py
  51. +19 −0 libcloud/drivers/ecp.py
  52. +19 −0 libcloud/drivers/elastichosts.py
  53. +19 −0 libcloud/drivers/gogrid.py
  54. +19 −0 libcloud/drivers/ibm_sbc.py
  55. +19 −0 libcloud/drivers/linode.py
  56. +22 −0 libcloud/drivers/opennebula.py
  57. +19 −0 libcloud/drivers/rackspace.py
  58. +19 −0 libcloud/drivers/rimuhosting.py
  59. +19 −0 libcloud/drivers/slicehost.py
  60. +19 −0 libcloud/drivers/softlayer.py
  61. +19 −0 libcloud/drivers/vcloud.py
  62. +19 −0 libcloud/drivers/voxel.py
  63. +19 −0 libcloud/drivers/vpsnet.py
  64. +157 −0 libcloud/httplib_ssl.py
  65. +19 −0 libcloud/providers.py
  66. +54 −0 libcloud/security.py
  67. +19 −0 libcloud/ssh.py
  68. 0 libcloud/storage/__init__.py
  69. +503 −0 libcloud/storage/base.py
  70. +23 −0 libcloud/storage/drivers/__init__.py
  71. +523 −0 libcloud/storage/drivers/cloudfiles.py
  72. +337 −0 libcloud/storage/drivers/dummy.py
  73. +29 −0 libcloud/storage/providers.py
  74. +68 −0 libcloud/storage/types.py
  75. +23 −0 libcloud/types.py
  76. +77 −0 libcloud/utils.py
  77. +136 −0 setup.py
  78. +201 −0 test/__init__.py
  79. +77 −0 test/compute/__init__.py
  80. +62 −0 test/compute/fixtures/brightbox/create_server.json
  81. +21 −0 test/compute/fixtures/brightbox/list_images.json
  82. +8 −0 test/compute/fixtures/brightbox/list_server_types.json
  83. +62 −0 test/compute/fixtures/brightbox/list_servers.json
  84. +3 −0 test/compute/fixtures/brightbox/list_zones.json
  85. +1 −0 test/compute/fixtures/brightbox/token.json
  86. +19 −0 test/compute/fixtures/cloudsigma/drives_clone.txt
  87. +39 −0 test/compute/fixtures/cloudsigma/drives_info.txt
  88. +19 −0 test/compute/fixtures/cloudsigma/drives_single_info.txt
  89. +1,735 −0 test/compute/fixtures/cloudsigma/drives_standard_info.txt
  90. +13 −0 test/compute/fixtures/cloudsigma/resources_ip_create.txt
  91. +3 −0 test/compute/fixtures/cloudsigma/resources_ip_list.txt
  92. +26 −0 test/compute/fixtures/cloudsigma/servers_create.txt
  93. +26 −0 test/compute/fixtures/cloudsigma/servers_info.txt
  94. +26 −0 test/compute/fixtures/cloudsigma/servers_set.txt
  95. +4 −0 test/compute/fixtures/ec2/create_tags.xml
  96. +4 −0 test/compute/fixtures/ec2/delete_tags.xml
  97. +9 −0 test/compute/fixtures/ec2/describe_addresses.xml
  98. +17 −0 test/compute/fixtures/ec2/describe_addresses_multi.xml
  99. +9 −0 test/compute/fixtures/ec2/describe_addresses_single.xml
  100. +17 −0 test/compute/fixtures/ec2/describe_availability_zones.xml
  101. +16 −0 test/compute/fixtures/ec2/describe_images.xml
  102. +39 −0 test/compute/fixtures/ec2/describe_instances.xml
  103. +23 −0 test/compute/fixtures/ec2/describe_tags.xml
  104. +4 −0 test/compute/fixtures/ec2/reboot_instances.xml
  105. +31 −0 test/compute/fixtures/ec2/run_instances.xml
  106. +32 −0 test/compute/fixtures/ec2/run_instances_idem.xml
  107. +12 −0 test/compute/fixtures/ec2/run_instances_idem_mismatch.xml
  108. +16 −0 test/compute/fixtures/ec2/terminate_instances.xml
  109. +9 −0 test/compute/fixtures/ecp/htemplate_list.json
  110. +1 −0 test/compute/fixtures/ecp/network_list.json
  111. +6 −0 test/compute/fixtures/ecp/ptemplate_list.json
  112. +1 −0 test/compute/fixtures/ecp/vm_1_action_delete.json
  113. +3 −0 test/compute/fixtures/ecp/vm_1_action_start.json
  114. +3 −0 test/compute/fixtures/ecp/vm_1_action_stop.json
  115. +3 −0 test/compute/fixtures/ecp/vm_1_get.json
  116. +10 −0 test/compute/fixtures/ecp/vm_list.json
  117. +1 −0 test/compute/fixtures/ecp/vm_put.json
  118. +12 −0 test/compute/fixtures/elastichosts/drives_create.json
  119. +12 −0 test/compute/fixtures/elastichosts/drives_info.json
  120. +25 −0 test/compute/fixtures/elastichosts/servers_create.json
  121. +27 −0 test/compute/fixtures/elastichosts/servers_info.json
  122. +180 −0 test/compute/fixtures/gogrid/image_list.json
  123. +62 −0 test/compute/fixtures/gogrid/image_save.json
  124. +69 −0 test/compute/fixtures/gogrid/ip_list.json
  125. +12 −0 test/compute/fixtures/gogrid/ip_list_empty.json
  126. +24 −0 test/compute/fixtures/gogrid/lookup_list_ip_datacenter.json
  127. +102 −0 test/compute/fixtures/gogrid/password_list.json
  128. +96 −0 test/compute/fixtures/gogrid/server_add.json
  129. +97 −0 test/compute/fixtures/gogrid/server_delete.json
  130. +97 −0 test/compute/fixtures/gogrid/server_edit.json
  131. +97 −0 test/compute/fixtures/gogrid/server_list.json
  132. +97 −0 test/compute/fixtures/gogrid/server_power.json
  133. +1 −0 test/compute/fixtures/ibm_sbc/create.xml
  134. +1 −0 test/compute/fixtures/ibm_sbc/delete.xml
  135. +2 −0 test/compute/fixtures/ibm_sbc/images.xml
  136. +1 −0 test/compute/fixtures/ibm_sbc/instances.xml
  137. +1 −0 test/compute/fixtures/ibm_sbc/instances_deleted.xml
  138. +1 −0 test/compute/fixtures/ibm_sbc/locations.xml
  139. +1 −0 test/compute/fixtures/ibm_sbc/reboot_active.xml
  140. +1 −0 test/compute/fixtures/ibm_sbc/sizes.xml
  141. +1 −0 test/compute/fixtures/meta/helloworld.txt
  142. +15 −0 test/compute/fixtures/opennebula/compute.xml
  143. +5 −0 test/compute/fixtures/opennebula/computes.xml
  144. +7 −0 test/compute/fixtures/opennebula/disk.xml
  145. +5 −0 test/compute/fixtures/opennebula/storage.xml
  146. +10 −0 test/compute/fixtures/rackspace/v1_slug_flavors_detail.xml
  147. +15 −0 test/compute/fixtures/rackspace/v1_slug_images_detail.xml
  148. +3 −0 test/compute/fixtures/rackspace/v1_slug_images_post.xml
  149. +15 −0 test/compute/fixtures/rackspace/v1_slug_limits.xml
  150. +12 −0 test/compute/fixtures/rackspace/v1_slug_servers.xml
  151. +14 −0 test/compute/fixtures/rackspace/v1_slug_servers_detail.xml
  152. +2 −0 test/compute/fixtures/rackspace/v1_slug_servers_detail_empty.xml
  153. +16 −0 test/compute/fixtures/rackspace/v1_slug_servers_detail_metadata.xml
  154. +10 −0 test/compute/fixtures/rackspace/v1_slug_servers_ips.xml
  155. +15 −0 test/compute/fixtures/rackspace/v1_slug_servers_metadata.xml
  156. +6 −0 test/compute/fixtures/rackspace/v1_slug_shared_ip_group.xml
  157. +5 −0 test/compute/fixtures/rackspace/v1_slug_shared_ip_groups.xml
  158. +16 −0 test/compute/fixtures/rackspace/v1_slug_shared_ip_groups_detail.xml
  159. +22 −0 test/compute/fixtures/rimuhosting/r_distributions.json
  160. +27 −0 test/compute/fixtures/rimuhosting/r_orders.json
  161. +62 −0 test/compute/fixtures/rimuhosting/r_orders_new_vps.json
  162. +13 −0 test/compute/fixtures/rimuhosting/r_orders_order_88833465_api_ivan_net_nz_vps.json
  163. +40 −0 test/compute/fixtures/rimuhosting/r_orders_order_88833465_api_ivan_net_nz_vps_running_state.json
  164. +26 −0 test/compute/fixtures/rimuhosting/r_pricing_plans.json
  165. +45 −0 test/compute/fixtures/slicehost/flavors.xml
  166. +47 −0 test/compute/fixtures/slicehost/images.xml
  167. +15 −0 test/compute/fixtures/slicehost/slices_1_reboot.xml
  168. +3 −0 test/compute/fixtures/slicehost/slices_1_reboot_forbidden.xml
  169. +4 −0 test/compute/fixtures/slicehost/slices_errors.xml
  170. +17 −0 test/compute/fixtures/slicehost/slices_get.xml
  171. +16 −0 test/compute/fixtures/slicehost/slices_post.xml
  172. +1,066 −0 test/compute/fixtures/softlayer/v3_SoftLayer_Account_getVirtualGuests.xml
  173. +99 −0 test/compute/fixtures/softlayer/v3_SoftLayer_Location_Datacenter_getDatacenters.xml
  174. +6 −0 test/compute/fixtures/terremark/api_v0_8_catalogItem_5.xml
  175. +3 −0 test/compute/fixtures/terremark/api_v0_8_login.xml
  176. +5 −0 test/compute/fixtures/terremark/api_v0_8_org_240.xml
  177. +4 −0 test/compute/fixtures/terremark/api_v0_8_task_10496.xml
  178. +4 −0 test/compute/fixtures/terremark/api_v0_8_task_11001.xml
  179. +132 −0 test/compute/fixtures/terremark/api_v0_8_vapp_14031.xml
  180. +4 −0 test/compute/fixtures/terremark/api_v0_8_vapp_14031_action_deploy.xml
  181. +132 −0 test/compute/fixtures/terremark/api_v0_8_vapp_14031_get.xml
  182. +4 −0 test/compute/fixtures/terremark/api_v0_8_vapp_14031_power_action_powerOn.xml
  183. +4 −0 test/compute/fixtures/terremark/api_v0_8_vapp_14031_power_action_poweroff.xml
  184. +4 −0 test/compute/fixtures/terremark/api_v0_8_vapp_14031_power_action_reset.xml
  185. +12 −0 test/compute/fixtures/terremark/api_v0_8_vdc_224.xml
  186. +3 −0 test/compute/fixtures/terremark/api_v0_8_vdc_224_action_instantiateVAppTemplate.xml
  187. +5 −0 test/compute/fixtures/terremark/api_v0_8_vdc_224_catalog.xml
  188. +11 −0 test/compute/fixtures/voxel/unauthorized.xml
  189. +83 −0 test/compute/test_base.py
  190. +132 −0 test/compute/test_brightbox.py
  191. +186 −0 test/compute/test_cloudsigma.py
  192. +279 −0 test/compute/test_dreamhost.py
  193. +273 −0 test/compute/test_ec2.py
  194. +128 −0 test/compute/test_ecp.py
  195. +106 −0 test/compute/test_elastichosts.py
  196. +242 −0 test/compute/test_gogrid.py
  197. +206 −0 test/compute/test_ibm_sbc.py
  198. +148 −0 test/compute/test_linode.py
  199. +122 −0 test/compute/test_opennebula.py
  200. +267 −0 test/compute/test_rackspace.py
  201. +107 −0 test/compute/test_rimuhosting.py
  202. +155 −0 test/compute/test_slicehost.py
  203. +83 −0 test/compute/test_softlayer.py
  204. +142 −0 test/compute/test_vcloud.py
  205. +53 −0 test/compute/test_voxel.py
  206. +209 −0 test/compute/test_vpsnet.py
  207. +46 −0 test/file_fixtures.py
  208. +64 −0 test/secrets.py
  209. +60 −0 test/secrets.py-dist
  210. 0 test/storage/__init__.py
  211. +14 −0 test/storage/fixtures/cloudfiles/list_container_objects.json
  212. +1 −0 test/storage/fixtures/cloudfiles/list_container_objects_empty.json
  213. +5 −0 test/storage/fixtures/cloudfiles/list_containers.json
  214. +1 −0 test/storage/fixtures/cloudfiles/list_containers_empty.json
  215. +1 −0 test/storage/fixtures/cloudfiles/meta_data.json
Sorry, we could not display the entire diff because it was too big.
View
185 CHANGES
@@ -0,0 +1,185 @@
+ -*- coding: utf-8 -*-
+
+Changes with Apache Libcloud 0.4.3
+
+ *) Implement ex_edit_image method for GoGrid driver
+ which allows changing image attributes like name,
+ description and make image public or private.
+ [Roman Bogorodskiy]
+
+Changes with Apache Libcloud 0.4.2
+
+ *) Fix EC2 create_node to become backward compatible for
+ NodeLocation.
+ [Tomaž Muraus]
+
+ *) Update code for compatibility with CPython 2.5
+ [Jerry Chen]
+
+ *) Implement ex_edit_node method for GoGrid driver which allows
+ changing node attributes like amount of RAM or description.
+ [Roman Bogorodskiy]
+
+ *) Add ex_set_password and ex_set_server_name to Rackspace driver.
+ [Peter Herndon, Paul Querna]
+
+ *) Add Hard and Soft reboot methods to Rackspace driver.
+ [Peter Herndon]
+
+ *) EC2 Driver availability zones, via ex_list_availability_zones;
+ list_locations rewrite to include availablity zones
+ [Tomaž Muraus]
+
+ *) EC2 Driver Idempotency capability in create_node; LIBCLOUD-69
+ [David LaBissoniere]
+
+ *) SSL Certificate Name Verification:
+ - libcloud.security module
+ - LibcloudHTTPSConnection, LibcloudHTTPConnection (alias)
+ - Emits warning when not verifying, or CA certs not found
+
+ *) Append ORD1 to available Rackspace location, but keep in the
+ same node as DFW1, because it's not readable or writeable from
+ the API.
+ [Per suggestion of Grig Gheorghiu]
+
+ *) ex_create_ip_group, ex_list_ip_groups, ex_delete_ip_group,
+ ex_share_ip, ex_unshare_ip, ex_list_ip_addresses additions
+ to Rackspace driver
+ [Andrew Klochkov]
+
+ *) New driver for CloudSigma
+ [Tomaž Muraus]
+
+ *) New driver for Brightbox Cloud. LIBCLOUD-63
+ [Tim Fletcher]
+
+ *) Deployment capability to ElasticHosts
+ [Tomaž Muraus]
+
+ *) Allow deploy_node to use non-standard SSH username and port
+ [Tomaž Muraus]
+
+ *) Added Rackspace UK (London) support
+ [Chmouel Boudjnah]
+
+ *) GoGrid driver: add support for locations, i.e. listing
+ of locations and creation of a node in specified
+ location
+ [Roman Bogorodskiy]
+
+ *) GoGrid and Rackspace drivers: add ex_save_image() extra
+ call to convert running node to an image
+ [Roman Bogorodskiy]
+
+ *) GoGrid driver: add support for creating 'sandbox' server
+ and populate isSandbox flag in node's extra information.
+ [Roman Bogorodskiy]
+
+ *) Add ImportKeyPair and DescribeKeyPair to EC2. LIBCLOUD-62
+ [Philip Schwartz]
+
+ *) Update EC2 driver and test fixtures for new API.
+ [Philip Schwartz]
+
+Changes with Apache Libcloud 0.4.0 [Released October 6, 2010]
+
+ *) Add create keypair functionality to EC2 Drivers. LIBCLOUD-57
+ [Grig Gheorghiu]
+
+ *) Improve handling of GoGrid accounts with limited access
+ API keys. [Paul Querna]
+
+ *) New Driver for ElasticHosts. LIBCLOUD-45
+ [Tomaz Muraus]
+
+ *) Use more consistent name for GoGrid driver and use http
+ POST method for 'unsafe' operations
+ [Russell Haering]
+
+ *) Implement password handling and add deployment support
+ for GoGrid nodes.
+ [Roman Bogorodskiy]
+
+ *) Fix behavior of GoGrid's create_node to wait for a Node ID.
+ [Roman Bogorodskiy]
+
+ *) Add ex_create_node_nowait to GoGrid driver if you don't need to
+ wait for a Node ID when creating a node.
+ [Roman Bogorodskiy]
+
+ *) Removed libcloud.interfaces module.
+ [Paul Querna]
+
+ *) Removed dependency on zope.interfaces.
+ [Paul Querna]
+
+ *) RimuHosting moved API endpoint address.
+ [Paul Querna]
+
+ *) Fix regression and error in GoGrid driver for parsing node objects.
+ [Roman Bogorodskiy]
+
+ *) Added more test cases for GoGrid driver. LIBCLOUD-34
+ [Roman Bogorodskiy, Jerry Chen]
+
+ *) Fix parsing of Slicehost nodes with multiple Public IP addresses.
+ [Paul Querna]
+
+ *) Add exit_status to ScriptDeployment. LIBCLOUD-36
+ [Paul Querna]
+
+ *) Update prices for several drivers.
+ [Brad Morgan, Paul Querna]
+
+ *) Update Linode driver to reflect new plan sizes.
+ [Jed Smith]
+
+ *) Change default of 'location' in Linode create_node. LIBCLOUD-41
+ [Jed Smith, Steve Steiner]
+
+ *) Document the Linode driver.
+ [Jed Smith]
+
+ *) Request a private, LAN IP address at Linode creation.
+ [Jed Smith]
+
+Changes with Apache Libcloud 0.3.1 [Released May 11, 2010]
+
+ *) Updates to Apache License blocks to correctly reflect status as an
+ Apache Project.
+
+ *) Fix NOTICE file to use 2010 copyright date.
+
+ *) Improve error messages for when running the test cases without
+ first setting up a secrets.py
+
+Changes with Apache Libcloud 0.3.0 [Tagged May 6, 2010, not released]
+
+ *) New Drivers for:
+ - Dreamhost
+ - Eucalyptus
+ - Enomaly ECP
+ - IBM Developer Cloud
+ - OpenNebula
+ - SoftLayer
+
+ *) Added new deployment and bootstrap API.
+
+ *) Improved Voxel driver.
+
+ *) Added support for Amazon EC2 Asia Pacific (Singapore) Region.
+
+ *) Improved test coverage for all drivers.
+
+ *) Add support for multiple security groups in EC2.
+
+ *) Fixed bug in Rackspace and RimuHosting when using multiple threads.
+
+ *) Improved debugging and logging of HTTP requests.
+
+ *) Improved documentation for all classes and methods.
+
+Changes with Apache Libcloud 0.2.0 [Tagged February 2, 2010]
+
+ *) First public release.
View
8 DISCLAIMER
@@ -0,0 +1,8 @@
+Apache Incubator is an effort undergoing incubation at The Apache Software
+Foundation (ASF), sponsored by the name of sponsor. Incubation is required of
+all newly accepted projects until a further review indicates that the
+infrastructure, communications, and decision making process have stabilized in
+a manner consistent with other successful ASF projects. While incubation
+status is not necessarily a reflection of the completeness or stability of the
+code, it does indicate that the project has yet to be fully endorsed by the
+ASF.
View
76 HACKING
@@ -0,0 +1,76 @@
+
+General Information
+===================
+ * URL: http://incubator.apache.org/libcloud/devinfo.html
+
+Git Repositories
+===================
+ * Official Git Mirror: git://git.apache.org/libcloud.git
+ * Github Mirror: git://github.com/apache/libcloud.git
+
+Using The Git-SVN Bridge (For Committers)
+=========================================
+
+ $ git clone git://git.apache.org/libcloud libcloud
+ $ cd libcloud
+
+ $ curl http://git.apache.org/authors.txt > .git/authors.txt
+ $ git config svn.authorsfile ".git/authors.txt"
+
+ # Optionally, set your Apache commiter info, if different from global
+ $ git config user.name "Your Name"
+ $ git config user.email "you@example.org"
+
+ $ git svn init \
+ --prefix=origin/ \
+ --tags=tags \
+ --trunk=trunk \
+ --branches=branches \
+ https://svn.apache.org/repos/asf/incubator/libcloud
+
+ $ git svn rebase
+
+ To push commits back to SVN:
+ $ git svn dcommit
+
+Testing
+=======
+
+ Libcloud includes an example secrets.py file at:
+ test/secrets.py-dist
+
+ To run the test cases, you most likely want to run:
+ $ cp test/secrets.py-dist test/secrets.py
+
+ This is done to prevent accidental commits of a developers provider credentials.
+
+ To run all suites:
+
+ libcloud$ python setup.py test
+ running test
+ ................................................................................................
+ ----------------------------------------------------------------------
+ Ran 96 tests in 0.182s
+
+ OK
+
+ To run specific tests:
+
+ libcloud$ PYTHONPATH=. python test/test_base.py
+ .......
+ ----------------------------------------------------------------------
+ Ran 7 tests in 0.001s
+
+ OK
+
+Making a release
+=======
+
+ We have a script that runs the required setup.py commands and then hashes
+ and signs the files. To run it:
+
+ cd dist
+ ./release.sh -u yourusername@apache.org
+
+ This should result in a set of apache-libcloud-${VERSION}.{tar.bz2,zip}{,asc,md5,sha1}
+ files that are suitable to be uploaded for a release.
View
202 LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
View
13 MANIFEST.in
@@ -0,0 +1,13 @@
+include LICENSE
+include NOTICE
+include DISCLAIMER
+include example.py
+include CONTRIBUTORS
+include CHANGES
+include HACKING
+include README
+prune test/secrets.py
+include test/*.py
+include demos/*
+include test/secrets.py-dist
+include test/fixtures/*/*
View
8 NOTICE
@@ -0,0 +1,8 @@
+Apache Libcloud
+Copyright (c) 2010 The Apache Software Foundation
+
+This product includes software developed by
+The Apache Software Foundation (http://www.apache.org/).
+
+This product includes software developed by
+Cloudkick (http://www.cloudkick.com/).
View
60 README
@@ -0,0 +1,60 @@
+
+Apache libcloud - a unified interface into the cloud
+====================================================
+
+The goal of this project is to create a basic yet functional standard library
+into various cloud providers.
+
+Apache libcloud is an incubator project at the Apache Software Foundation, see
+<http://incubator.apache.org/libcloud> for more information.
+
+For API documentation and examples, see:
+ <http://incubator.apache.org/libcloud/getting-started.html>
+
+
+Important Security Note
+=======================
+
+Python's built-in SSL module does not do certificate validation.
+
+To address this, we've introduced the libcloud.security module with tunable
+parameters.
+
+View the entire guide at: <http://wiki.apache.org/incubator/LibcloudSSL>
+
+Enabling SSL Certificate Check
+==============================
+
+ import libcloud.security
+ libcloud.security.VERIFY_SSL_CERT = True
+
+ # optionally, add to CA_CERTS_PATH
+ libcloud.security.CA_CERTS_PATH.append("/path/to/your/cacerts.txt")
+
+CA_CERTS_PATH contains common paths to CA bundle installations on the
+following platforms:
+
+ * openssl on CentOS/Fedora
+ * ca-certificates on Debian/Ubuntu/Arch/Gentoo
+ * ca_root_nss on FreeBSD
+ * curl-ca-bundle on Mac OS X
+
+Note for OS X Users
+===================
+
+OS X root certificates are stored in the Keychain format, unlike the standard
+PEM format available on other *nix platforms. For this reason, it is not
+possible to include the standard OS X root certificates with CA_CERTS_PATH.
+
+Acquiring CA Certificates
+=========================
+
+If the above packages are unavailable to you, and you don't wish to roll your
+own, the makers of cURL provides an excellent resource, generated from
+Mozilla: http://curl.haxx.se/docs/caextract.html
+
+Feedback
+========
+
+Please send feedback to the mailing list at <libcloud@incubator.apache.org>,
+or the JIRA at <https://issues.apache.org/jira/browse/LIBCLOUD>.
View
128 demos/ec2_demo.py
@@ -0,0 +1,128 @@
+#!/usr/bin/env python
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# This example provides both a running script (invoke from command line)
+# and an importable module one can play with in Interactive Mode.
+#
+# See docstrings for usage examples.
+#
+
+try:
+ import secrets
+except:
+ pass
+import sys; sys.path.append('..')
+
+from libcloud.compute.types import Provider
+from libcloud.providers import get_driver
+
+from pprint import pprint
+
+def main(argv):
+ """Main EC2 Demo
+
+ When invoked from the command line, it will connect using secrets.py
+ (see secrets.py.dist for setup instructions), and perform the following
+ tasks:
+
+ - List current nodes
+ - List available images (up to 10)
+ - List available sizes (up to 10)
+ """
+ # Load EC2 driver
+ EC2Driver = get_driver(Provider.EC2_US_EAST)
+
+ # Instantiate with Access ID and Secret Key
+ # (see secrets.py.dist)
+ try:
+ ec2 = EC2Driver(secrets.EC2_ACCESS_ID, secrets.EC2_SECRET_KEY)
+ print ">> Loading nodes..."
+ nodes = ec2.list_nodes()
+ pprint(nodes)
+ except NameError, e:
+ print ">> Fatal Error: %s" % e
+ print " (Hint: modify secrets.py.dist)"
+ return 1
+ except Exception, e:
+ print ">> Fatal error: %s" % e
+ return 1
+
+ print ">> Loading images... (showing up to 10)"
+ images = ec2.list_images()
+ pprint(images[:10])
+
+ print ">> Loading sizes... (showing up to 10)"
+ sizes = ec2.list_sizes()
+ pprint(sizes[:10])
+
+ return 0
+
+def get_ec2(**kwargs):
+ """An easy way to play with the EC2 Driver in Interactive Mode
+
+ # Load credentials from secrets.py
+ >>> from ec2demo import get_ec2
+ >>> ec2 = get_ec2()
+
+ # Or, provide credentials
+ >>> from ec2demo import get_ec2
+ >>> ec2 = get_ec2(access_id='xxx', secret_key='yyy')
+
+ # Do things
+ >>> ec2.load_nodes()
+ >>> images = ec2.load_images()
+ >>> sizes = ec2.load_sizes()
+ """
+ access_id = kwargs.get('access_id', secrets.EC2_ACCESS_ID)
+ secret_key = kwargs.get('secret_key', secrets.EC2_SECRET_KEY)
+
+ EC2Driver = get_driver(Provider.EC2_US_EAST)
+ return EC2Driver(access_id, secret_key)
+
+def create_demo(ec2):
+ """Create EC2 Node Demo
+
+ >>> from ec2demo import get_ec2, create_demo
+ >>> ec2 = get_ec2()
+ >>> node = create_demo(ec2)
+ >>> node
+ <Node: uuid=9d1..., name=i-7b1fa910, state=3, public_ip=[''], ...>
+
+ And to destroy the node:
+
+ >>> node.destroy()
+
+ If you've accidentally quit and need to destroy the node:
+
+ >>> from ec2demo import get_ec2
+ >>> nodes = ec2.list_nodes()
+ >>> nodes[0].destroy() # assuming it's the first node
+ """
+ images = ec2.list_images()
+ image = [image for image in images if 'ami' in image.id][0]
+ sizes = ec2.list_sizes()
+ size = sizes[0]
+
+ # Note, name is ignored by EC2
+ node = ec2.create_node(name='create_image_demo',
+ image=image,
+ size=size)
+ return node
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
View
21 demos/secrets.py.dist
@@ -0,0 +1,21 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Copy this file to secrets.py for use with provided examples
+#
+
+EC2_ACCESS_ID=''
+EC2_SECRET_KEY=''
View
147 dist/hash-sign.sh
@@ -0,0 +1,147 @@
+#!/bin/sh
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# hash-sign.sh : hash and sign the specified files
+#
+# USAGE: hash-sign.sh file1 file2 ...
+#
+
+user=""
+case "$1" in
+ -u)
+ shift
+ user="$1"
+ shift
+ ;;
+esac
+
+allfiles=$*
+
+
+split="---------------------------------------------------------------------"
+
+echo $split
+echo ""
+echo "Generating MD5/SHA1 checksum files ..."
+echo ""
+
+# check for executables
+gpg="`which gpg 2> /dev/null | head -1`"
+pgp="`which pgp 2> /dev/null | head -1`"
+openssl="`which openssl 2> /dev/null | head -1`"
+md5sum="`which md5sum 2> /dev/null | head -1`"
+sha1sum="`which sha1sum 2> /dev/null | head -1`"
+md5="`which md5 2> /dev/null | head -1`"
+sha1="`which sha1 2> /dev/null | head -1`"
+
+# if found we use openssl for generating the checksums
+# and convert the results into machine-readable format.
+if test -x "${openssl}"; then
+ for file in ${allfiles}; do
+ if test -f "${file}"; then
+ echo "openssl: creating md5 checksum file for ${file} ..."
+ ${openssl} md5 ${file} |\
+ sed -e 's#^MD5(\(.*\))= \([0-9a-f]*\)$#\2 *\1#' > ${file}.md5
+ echo "openssl: creating sha1 checksum file for ${file} ..."
+ ${openssl} sha1 ${file} |\
+ sed -e 's#^SHA1(\(.*\))= \([0-9a-f]*\)$#\2 *\1#' > ${file}.sha1
+ fi
+ done
+# no openssl found - check if we have gpg
+elif test -x "${gpg}"; then
+ for file in ${allfiles}; do
+ if test -f "${file}"; then
+ echo "gpg: creating md5 checksum file for ${file} ..."
+ ${gpg} --print-md md5 ${file} |\
+ sed -e '{N;s#\n##;}' |\
+ sed -e 's#\(.*\): \(.*\)#\2::\1#;s#[\r\n]##g;s# ##g' \
+ -e 'y#ABCDEF#abcdef#;s#::# *#' > ${file}.md5
+ echo "gpg: creating sha1 checksum file for ${file} ..."
+ ${gpg} --print-md sha1 ${file} |\
+ sed -e '{N;s#\n##;}' |\
+ sed -e 's#\(.*\): \(.*\)#\2::\1#;s#[\r\n]##g;s# ##g' \
+ -e 'y#ABCDEF#abcdef#;s#::# *#' > ${file}.sha1
+ fi
+ done
+else
+ # no openssl or gpg found - check for md5sum
+ if test -x "${md5sum}"; then
+ for file in ${allfiles}; do
+ if test -f "${file}"; then
+ echo "md5sum: creating md5 checksum file for ${file} ..."
+ ${md5sum} -b ${file} > ${file}.md5
+ fi
+ done
+ # no openssl or gpg found - check for md5
+ elif test -x "${md5}"; then
+ for file in ${allfiles}; do
+ if test -f "${file}"; then
+ echo "md5: creating md5 checksum file for ${file} ..."
+ ${md5} -r ${file} | sed -e 's# # *#' > ${file}.md5
+ fi
+ done
+ fi
+ # no openssl or gpg found - check for sha1sum
+ if test -x "${sha1sum}"; then
+ for file in ${allfiles}; do
+ if test -f "${file}"; then
+ echo "sha1sum: creating sha1 checksum file for ${file} ..."
+ ${sha1sum} -b ${file} > ${file}.sha1
+ fi
+ done
+ # no openssl or gpg found - check for sha1
+ elif test -x "${sha1}"; then
+ for file in ${allfiles}; do
+ if test -f "${file}"; then
+ echo "sha1: creating sha1 checksum file for ${file} ..."
+ ${sha1} -r ${file} | sed -e 's# # *#' > ${file}.sha1
+ fi
+ done
+ fi
+fi
+
+echo $split
+echo ""
+echo "Signing the files ..."
+echo ""
+
+# if found we use pgp for signing the files
+if test -x "${pgp}"; then
+ if test -n "${user}"; then
+ args="-u ${user}"
+ fi
+ for file in ${allfiles}; do
+ if test -f "${file}"; then
+ echo "pgp: creating asc signature file for ${file} ..."
+ ${pgp} -sba ${file} ${args}
+ fi
+ done
+# no pgp found - check for gpg
+elif test -x "${gpg}"; then
+ if test -z "${user}"; then
+ args="--default-key ${args}"
+ else
+ args="-u ${user} ${args}"
+ fi
+ for file in ${allfiles}; do
+ if test -f "${file}"; then
+ echo "gpg: creating asc signature file for ${file} ..."
+ ${gpg} --armor ${args} --detach-sign ${file}
+ fi
+ done
+else
+ echo "PGP or GnuPG not found! Not signing release!"
+fi
View
39 dist/release.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -e
+
+user=""
+case "$1" in
+ -u)
+ shift
+ user="$1"
+ shift
+ ;;
+esac
+
+if test -z "${user}"; then
+ echo "must pass -u with a gpg id"
+ exit 1
+fi
+
+cd ..
+
+python setup.py sdist --formats=bztar,zip
+
+cd dist
+
+./hash-sign.sh -u ${user} *.tar.bz2 *.zip
View
36 example_compute.py
@@ -0,0 +1,36 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+from libcloud.compute.types import Provider
+from libcloud.compute.providers import get_driver
+
+EC2 = get_driver(Provider.EC2_US_EAST)
+Slicehost = get_driver(Provider.SLICEHOST)
+Rackspace = get_driver(Provider.RACKSPACE)
+
+drivers = [ EC2('access key id', 'secret key'),
+ Slicehost('api key'),
+ Rackspace('username', 'api key') ]
+
+nodes = [ driver.list_nodes() for driver in drivers ]
+
+print nodes
+# [ <Node: provider=Amazon, status=RUNNING, name=bob, ip=1.2.3.4.5>,
+# <Node: provider=Slicehost, status=REBOOT, name=korine, ip=6.7.8.9.10>, ... ]
+
+# grab the node named "test"
+node = filter(lambda x: x.name == 'test', nodes)[0]
+
+# reboot "test"
+node.reboot()
View
29 example_storage.py
@@ -0,0 +1,29 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from pprint import pprint
+
+from libcloud.storage.types import Provider
+from libcloud.storage.providers import get_driver
+
+CloudFiles = get_driver(Provider.CloudFiles)
+
+driver = CloudFiles('access key id', 'secret key')
+
+containers = driver.list_containers()
+container_objects = driver.list_container_objects(containers[0])
+
+pprint(containers)
+pprint(container_objects)
View
56 libcloud/__init__.py
@@ -0,0 +1,56 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+libcloud provides a unified interface to the cloud computing resources.
+
+@var __version__: Current version of libcloud
+"""
+
+__all__ = ["__version__", "enable_debug"]
+
+__version__ = "0.4.3-dev"
+
+def enable_debug(fo):
+ """
+ Enable library wide debugging to a file-like object.
+
+ @param fo: Where to append debugging information
+ @type fo: File like object, only write operations are used.
+ """
+ from libcloud.base import ConnectionKey, LoggingHTTPConnection, LoggingHTTPSConnection
+ LoggingHTTPSConnection.log = fo
+ LoggingHTTPConnection.log = fo
+ ConnectionKey.conn_classes = (LoggingHTTPConnection, LoggingHTTPSConnection)
+
+def _init_once():
+ """
+ Utility function that is ran once on Library import.
+
+ This checks for the LIBCLOUD_DEBUG enviroment variable, which if it exists
+ is where we will log debug information about the provider transports.
+
+ If LIBCLOUD_DEBUG is not a path, C{/tmp/libcloud_debug.log} is used by
+ default.
+ """
+ import os
+ d = os.getenv("LIBCLOUD_DEBUG")
+ if d:
+ if d.isdigit():
+ d = "/tmp/libcloud_debug.log"
+ fo = open(d, "a")
+ enable_debug(fo)
+
+_init_once()
View
25 libcloud/base.py
@@ -0,0 +1,25 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from libcloud.common.base import RawResponse, Response, LoggingConnection
+from libcloud.common.base import LoggingHTTPSConnection, LoggingHTTPConnection
+from libcloud.common.base import ConnectionKey, ConnectionUserAndKey
+from libcloud.compute.base import Node, NodeSize, NodeImage
+from libcloud.compute.base import NodeLocation, NodeAuthSSHKey, NodeAuthPassword
+from libcloud.compute.base import NodeDriver, is_private_subnet
+
+from libcloud.utils import deprecated_warning
+
+deprecated_warning(__name__)
View
0 libcloud/common/__init__.py
No changes.
View
420 libcloud/common/base.py
@@ -0,0 +1,420 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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 httplib
+import urllib
+import time
+import hashlib
+import StringIO
+import ssl
+import os
+import socket
+import struct
+
+from pipes import quote as pquote
+
+import libcloud
+
+from libcloud.httplib_ssl import LibcloudHTTPSConnection
+from httplib import HTTPConnection as LibcloudHTTPConnection
+
+class RawResponse(object):
+
+ def __init__(self, response=None):
+ self._status = None
+ self._response = None
+ self._headers = {}
+ self._error = None
+ self._reason = None
+
+ @property
+ def response(self):
+ if not self._response:
+ self._response = self.connection.connection.getresponse()
+ return self._response
+
+ @property
+ def status(self):
+ if not self._status:
+ self._status = self.response.status
+ return self._status
+
+ @property
+ def headers(self):
+ if not self._headers:
+ self._headers = dict(self.response.getheaders())
+ return self._headers
+
+ @property
+ def reason(self):
+ if not self._reason:
+ self._reason = self.response.reason
+ return self._reason
+
+class Response(object):
+ """
+ A Base Response class to derive from.
+ """
+ NODE_STATE_MAP = {}
+
+ object = None
+ body = None
+ status = httplib.OK
+ headers = {}
+ error = None
+ connection = None
+
+ def __init__(self, response):
+ self.body = response.read()
+ self.status = response.status
+ self.headers = dict(response.getheaders())
+ self.error = response.reason
+
+ if not self.success():
+ raise Exception(self.parse_error())
+
+ self.object = self.parse_body()
+
+ def parse_body(self):
+ """
+ Parse response body.
+
+ Override in a provider's subclass.
+
+ @return: Parsed body.
+ """
+ return self.body
+
+ def parse_error(self):
+ """
+ Parse the error messages.
+
+ Override in a provider's subclass.
+
+ @return: Parsed error.
+ """
+ return self.body
+
+ def success(self):
+ """
+ Determine if our request was successful.
+
+ The meaning of this can be arbitrary; did we receive OK status? Did
+ the node get created? Were we authenticated?
+
+ @return: C{True} or C{False}
+ """
+ return self.status == httplib.OK or self.status == httplib.CREATED
+
+#TODO: Move this to a better location/package
+class LoggingConnection():
+ """
+ Debug class to log all HTTP(s) requests as they could be made
+ with the C{curl} command.
+
+ @cvar log: file-like object that logs entries are written to.
+ """
+ log = None
+
+ def _log_response(self, r):
+ rv = "# -------- begin %d:%d response ----------\n" % (id(self), id(r))
+ ht = ""
+ v = r.version
+ if r.version == 10:
+ v = "HTTP/1.0"
+ if r.version == 11:
+ v = "HTTP/1.1"
+ ht += "%s %s %s\r\n" % (v, r.status, r.reason)
+ body = r.read()
+ for h in r.getheaders():
+ ht += "%s: %s\r\n" % (h[0].title(), h[1])
+ ht += "\r\n"
+ # this is evil. laugh with me. ha arharhrhahahaha
+ class fakesock:
+ def __init__(self, s):
+ self.s = s
+ def makefile(self, mode, foo):
+ return StringIO.StringIO(self.s)
+ rr = r
+ if r.chunked:
+ ht += "%x\r\n" % (len(body))
+ ht += body
+ ht += "\r\n0\r\n"
+ else:
+ ht += body
+ rr = httplib.HTTPResponse(fakesock(ht),
+ method=r._method,
+ debuglevel=r.debuglevel)
+ rr.begin()
+ rv += ht
+ rv += ("\n# -------- end %d:%d response ----------\n"
+ % (id(self), id(r)))
+ return (rr, rv)
+
+ def _log_curl(self, method, url, body, headers):
+ cmd = ["curl", "-i"]
+
+ cmd.extend(["-X", pquote(method)])
+
+ for h in headers:
+ cmd.extend(["-H", pquote("%s: %s" % (h, headers[h]))])
+
+ # TODO: in python 2.6, body can be a file-like object.
+ if body is not None and len(body) > 0:
+ cmd.extend(["--data-binary", pquote(body)])
+
+ cmd.extend([pquote("https://%s:%d%s" % (self.host, self.port, url))])
+ return " ".join(cmd)
+
+class LoggingHTTPSConnection(LoggingConnection, LibcloudHTTPSConnection):
+ """
+ Utility Class for logging HTTPS connections
+ """
+
+ def getresponse(self):
+ r = LibcloudHTTPSConnection.getresponse(self)
+ if self.log is not None:
+ r, rv = self._log_response(r)
+ self.log.write(rv + "\n")
+ self.log.flush()
+ return r
+
+ def request(self, method, url, body=None, headers=None):
+ headers.update({'X-LC-Request-ID': str(id(self))})
+ if self.log is not None:
+ pre = "# -------- begin %d request ----------\n" % id(self)
+ self.log.write(pre +
+ self._log_curl(method, url, body, headers) + "\n")
+ self.log.flush()
+ return LibcloudHTTPSConnection.request(self, method, url, body, headers)
+
+class LoggingHTTPConnection(LoggingConnection, LibcloudHTTPConnection):
+ """
+ Utility Class for logging HTTP connections
+ """
+
+ def getresponse(self):
+ r = LibcloudHTTPConnection.getresponse(self)
+ if self.log is not None:
+ r, rv = self._log_response(r)
+ self.log.write(rv + "\n")
+ self.log.flush()
+ return r
+
+ def request(self, method, url, body=None, headers=None):
+ headers.update({'X-LC-Request-ID': str(id(self))})
+ if self.log is not None:
+ pre = "# -------- begin %d request ----------\n" % id(self)
+ self.log.write(pre +
+ self._log_curl(method, url, body, headers) + "\n")
+ self.log.flush()
+ return LibcloudHTTPConnection.request(self, method, url,
+ body, headers)
+
+class ConnectionKey(object):
+ """
+ A Base Connection class to derive from.
+ """
+ #conn_classes = (LoggingHTTPSConnection)
+ conn_classes = (LibcloudHTTPConnection, LibcloudHTTPSConnection)
+
+ responseCls = Response
+ rawResponseCls = RawResponse
+ connection = None
+ host = '127.0.0.1'
+ port = (80, 443)
+ secure = 1
+ driver = None
+ action = None
+
+ def __init__(self, key, secure=True, host=None, force_port=None):
+ """
+ Initialize `user_id` and `key`; set `secure` to an C{int} based on
+ passed value.
+ """
+ self.key = key
+ self.secure = secure and 1 or 0
+ self.ua = []
+ if host:
+ self.host = host
+
+ if force_port:
+ self.port = (force_port, force_port)
+
+ def connect(self, host=None, port=None):
+ """
+ Establish a connection with the API server.
+
+ @type host: C{str}
+ @param host: Optional host to override our default
+
+ @type port: C{int}
+ @param port: Optional port to override our default
+
+ @returns: A connection
+ """
+ host = host or self.host
+ port = port or self.port[self.secure]
+
+ kwargs = {'host': host, 'port': port}
+
+ connection = self.conn_classes[self.secure](**kwargs)
+ # You can uncoment this line, if you setup a reverse proxy server
+ # which proxies to your endpoint, and lets you easily capture
+ # connections in cleartext when you setup the proxy to do SSL
+ # for you
+ #connection = self.conn_classes[False]("127.0.0.1", 8080)
+
+ self.connection = connection
+
+ def _user_agent(self):
+ return 'libcloud/%s (%s)%s' % (
+ libcloud.__version__,
+ self.driver.name,
+ "".join([" (%s)" % x for x in self.ua]))
+
+ def user_agent_append(self, token):
+ """
+ Append a token to a user agent string.
+
+ Users of the library should call this to uniquely identify thier requests
+ to a provider.
+
+ @type token: C{str}
+ @param token: Token to add to the user agent.
+ """
+ self.ua.append(token)
+
+ def request(self,
+ action,
+ params=None,
+ data='',
+ headers=None,
+ method='GET',
+ raw=False):
+ """
+ Request a given `action`.
+
+ Basically a wrapper around the connection
+ object's `request` that does some helpful pre-processing.
+
+ @type action: C{str}
+ @param action: A path
+
+ @type params: C{dict}
+ @param params: Optional mapping of additional parameters to send. If
+ None, leave as an empty C{dict}.
+
+ @type data: C{unicode}
+ @param data: A body of data to send with the request.
+
+ @type headers: C{dict}
+ @param headers: Extra headers to add to the request
+ None, leave as an empty C{dict}.
+
+ @type method: C{str}
+ @param method: An HTTP method such as "GET" or "POST".
+
+ @return: An instance of type I{responseCls}
+ """
+ if params is None:
+ params = {}
+ if headers is None:
+ headers = {}
+
+ self.action = action
+ self.method = method
+ # Extend default parameters
+ params = self.add_default_params(params)
+ # Extend default headers
+ headers = self.add_default_headers(headers)
+ # We always send a content length and user-agent header
+ headers.update({'User-Agent': self._user_agent()})
+ headers.update({'Host': self.host})
+ # Encode data if necessary
+ if data != '' and data != None:
+ data = self.encode_data(data)
+
+ if data is not None:
+ headers.update({'Content-Length': str(len(data))})
+
+ if params:
+ url = '?'.join((action, urllib.urlencode(params)))
+ else:
+ url = action
+
+ # Removed terrible hack...this a less-bad hack that doesn't execute a
+ # request twice, but it's still a hack.
+ self.connect()
+ try:
+ # @TODO: Should we just pass File object as body to request method
+ # instead of dealing with splitting and sending the file ourselves?
+ if raw:
+ self.connection.putrequest(method, action)
+
+ for key, value in headers.iteritems():
+ self.connection.putheader(key, value)
+
+ self.connection.endheaders()
+ else:
+ self.connection.request(method=method, url=url, body=data,
+ headers=headers)
+ except ssl.SSLError, e:
+ raise ssl.SSLError(str(e))
+
+ if raw:
+ response = self.rawResponseCls()
+ else:
+ response = self.responseCls(self.connection.getresponse())
+
+ response.connection = self
+ return response
+
+ def add_default_params(self, params):
+ """
+ Adds default parameters (such as API key, version, etc.)
+ to the passed `params`
+
+ Should return a dictionary.
+ """
+ return params
+
+ def add_default_headers(self, headers):
+ """
+ Adds default headers (such as Authorization, X-Foo-Bar)
+ to the passed `headers`
+
+ Should return a dictionary.
+ """
+ return headers
+
+ def encode_data(self, data):
+ """
+ Encode body data.
+
+ Override in a provider's subclass.
+ """
+ return data
+
+class ConnectionUserAndKey(ConnectionKey):
+ """
+ Base connection which accepts a user_id and key
+ """
+
+ user_id = None
+
+ def __init__(self, user_id, key, secure=True, host=None, port=None):
+ super(ConnectionUserAndKey, self).__init__(key, secure, host, port)
+ self.user_id = user_id
View
45 libcloud/common/types.py
@@ -0,0 +1,45 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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 LibcloudError(Exception):
+ """The base class for other libcloud exceptions"""
+ def __init__(self, value, driver=None):
+ self.value = value
+ self.driver = driver
+
+ def __str__(self):
+ return "<LibcloudError in "+ repr(self.driver) +" "+ repr(self.value) + ">"
+
+class MalformedResponseError(LibcloudError):
+ """Exception for the cases when a provider returns a malformed
+ response, e.g. you request JSON and provider returns
+ '<h3>something</h3>' due to some error on their side."""
+ def __init__(self, value, body=None, driver=None):
+ self.value = value
+ self.driver = driver
+ self.body = body
+ def __str__(self):
+ return "<MalformedResponseException in "+ repr(self.driver) +" "+ repr(self.value) +">: "+ repr(self.body)
+
+class InvalidCredsError(LibcloudError):
+ """Exception used when invalid credentials are used on a provider."""
+ def __init__(self, value='Invalid credentials with the provider', driver=None):
+ self.value = value
+ self.driver = driver
+ def __str__(self):
+ return repr(self.value)
+
+"""Deprecated alias of L{InvalidCredsError}"""
+InvalidCredsException = InvalidCredsError
View
0 libcloud/compute/__init__.py
No changes.
View
573 libcloud/compute/base.py
@@ -0,0 +1,573 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+Provides base classes for working with drivers
+"""
+import httplib, urllib
+import time
+import hashlib
+import StringIO
+import ssl
+import os
+import socket
+import struct
+
+from libcloud.common.base import ConnectionKey, ConnectionUserAndKey
+from libcloud.compute.types import NodeState, DeploymentError
+from libcloud.compute.ssh import SSHClient
+from libcloud.httplib_ssl import LibcloudHTTPSConnection
+from httplib import HTTPConnection as LibcloudHTTPConnection
+
+class Node(object):
+ """
+ Provide a common interface for handling nodes of all types.
+
+ The Node object provides the interface in libcloud through which
+ we can manipulate nodes in different cloud providers in the same
+ way. Node objects don't actually do much directly themselves,
+ instead the node driver handles the connection to the node.
+
+ You don't normally create a node object yourself; instead you use
+ a driver and then have that create the node for you.
+
+ >>> from libcloud.compute.drivers.dummy import DummyNodeDriver
+ >>> driver = DummyNodeDriver(0)
+ >>> node = driver.create_node()
+ >>> node.public_ip[0]
+ '127.0.0.3'
+ >>> node.name
+ 'dummy-3'
+
+ You can also get nodes from the driver's list_node function.
+
+ >>> node = driver.list_nodes()[0]
+ >>> node.name
+ 'dummy-1'
+
+ the node keeps a reference to its own driver which means that we
+ can work on nodes from different providers without having to know
+ which is which.
+
+ >>> driver = DummyNodeDriver(72)
+ >>> node2 = driver.create_node()
+ >>> node.driver.creds
+ 0
+ >>> node2.driver.creds
+ 72
+
+ Althrough Node objects can be subclassed, this isn't normally
+ done. Instead, any driver specific information is stored in the
+ "extra" proproperty of the node.
+
+ >>> node.extra
+ {'foo': 'bar'}
+
+ """
+
+ def __init__(self, id, name, state, public_ip, private_ip,
+ driver, extra=None):
+ self.id = str(id) if id else None
+ self.name = name
+ self.state = state
+ self.public_ip = public_ip
+ self.private_ip = private_ip
+ self.driver = driver
+ self.uuid = self.get_uuid()
+ if not extra:
+ self.extra = {}
+ else:
+ self.extra = extra
+
+ def get_uuid(self):
+ """Unique hash for this node
+
+ @return: C{string}
+
+ The hash is a function of an SHA1 hash of the node's ID and
+ its driver which means that it should be unique between all
+ nodes. In some subclasses (e.g. GoGrid) there is no ID
+ available so the public IP address is used. This means that,
+ unlike a properly done system UUID, the same UUID may mean a
+ different system install at a different time
+
+ >>> from libcloud.compute.drivers.dummy import DummyNodeDriver
+ >>> driver = DummyNodeDriver(0)
+ >>> node = driver.create_node()
+ >>> node.get_uuid()
+ 'd3748461511d8b9b0e0bfa0d4d3383a619a2bb9f'
+
+ Note, for example, that this example will always produce the
+ same UUID!
+ """
+ return hashlib.sha1("%s:%d" % (self.id,self.driver.type)).hexdigest()
+
+ def reboot(self):
+ """Reboot this node
+
+ @return: C{bool}
+
+ This calls the node's driver and reboots the node
+
+ >>> from libcloud.compute.drivers.dummy import DummyNodeDriver
+ >>> driver = DummyNodeDriver(0)
+ >>> node = driver.create_node()
+ >>> from libcloud.compute.types import NodeState
+ >>> node.state == NodeState.RUNNING
+ True
+ >>> node.state == NodeState.REBOOTING
+ False
+ >>> node.reboot()
+ True
+ >>> node.state == NodeState.REBOOTING
+ True
+ """
+ return self.driver.reboot_node(self)
+
+ def destroy(self):
+ """Destroy this node
+
+ @return: C{bool}
+
+ This calls the node's driver and destroys the node
+
+ >>> from libcloud.compute.drivers.dummy import DummyNodeDriver
+ >>> driver = DummyNodeDriver(0)
+ >>> from libcloud.compute.types import NodeState
+ >>> node = driver.create_node()
+ >>> node.state == NodeState.RUNNING
+ True
+ >>> node.destroy()
+ True
+ >>> node.state == NodeState.RUNNING
+ False
+
+ """
+ return self.driver.destroy_node(self)
+
+ def __repr__(self):
+ return (('<Node: uuid=%s, name=%s, state=%s, public_ip=%s, '
+ 'provider=%s ...>')
+ % (self.uuid, self.name, self.state, self.public_ip,
+ self.driver.name))
+
+
+class NodeSize(object):
+ """
+ A Base NodeSize class to derive from.
+
+ NodeSizes are objects which are typically returned a driver's
+ list_sizes function. They contain a number of different
+ parameters which define how big an image is.
+
+ The exact parameters available depends on the provider.
+
+ N.B. Where a parameter is "unlimited" (for example bandwidth in
+ Amazon) this will be given as 0.
+
+ >>> from libcloud.compute.drivers.dummy import DummyNodeDriver
+ >>> driver = DummyNodeDriver(0)
+ >>> size = driver.list_sizes()[0]
+ >>> size.ram
+ 128
+ >>> size.bandwidth
+ 500
+ >>> size.price
+ 4
+ """
+
+ def __init__(self, id, name, ram, disk, bandwidth, price, driver):
+ self.id = str(id)
+ self.name = name
+ self.ram = ram
+ self.disk = disk
+ self.bandwidth = bandwidth
+ self.price = price
+ self.driver = driver
+ def __repr__(self):
+ return (('<NodeSize: id=%s, name=%s, ram=%s disk=%s bandwidth=%s '
+ 'price=%s driver=%s ...>')
+ % (self.id, self.name, self.ram, self.disk, self.bandwidth,
+ self.price, self.driver.name))
+
+
+class NodeImage(object):
+ """
+ An operating system image.
+
+ NodeImage objects are typically returned by the driver for the
+ cloud provider in response to the list_images function
+
+ >>> from libcloud.compute.drivers.dummy import DummyNodeDriver
+ >>> driver = DummyNodeDriver(0)
+ >>> image = driver.list_images()[0]
+ >>> image.name
+ 'Ubuntu 9.10'
+
+ Apart from name and id, there is no further standard information;
+ other parameters are stored in a driver specific "extra" variable
+
+ When creating a node, a node image should be given as an argument
+ to the create_node function to decide which OS image to use.
+
+ >>> node = driver.create_node(image=image)
+
+ """
+
+ def __init__(self, id, name, driver, extra=None):
+ self.id = str(id)
+ self.name = name
+ self.driver = driver
+ if not extra:
+ self.extra = {}
+ else:
+ self.extra = extra
+ def __repr__(self):
+ return (('<NodeImage: id=%s, name=%s, driver=%s ...>')
+ % (self.id, self.name, self.driver.name))
+
+class NodeLocation(object):
+ """
+ A physical location where nodes can be.
+
+ >>> from libcloud.compute.drivers.dummy import DummyNodeDriver
+ >>> driver = DummyNodeDriver(0)
+ >>> location = driver.list_locations()[0]
+ >>> location.country
+ 'US'
+ """
+
+ def __init__(self, id, name, country, driver):
+ self.id = str(id)
+ self.name = name
+ self.country = country
+ self.driver = driver
+ def __repr__(self):
+ return (('<NodeLocation: id=%s, name=%s, country=%s, driver=%s>')
+ % (self.id, self.name, self.country, self.driver.name))
+
+class NodeAuthSSHKey(object):
+ """
+ An SSH key to be installed for authentication to a node.
+
+ This is the actual contents of the users ssh public key which will
+ normally be installed as root's public key on the node.
+
+ >>> pubkey = '...' # read from file
+ >>> from libcloud.compute.base import NodeAuthSSHKey
+ >>> k = NodeAuthSSHKey(pubkey)
+ >>> k
+ <NodeAuthSSHKey>
+
+ """
+ def __init__(self, pubkey):
+ self.pubkey = pubkey
+ def __repr__(self):
+ return '<NodeAuthSSHKey>'
+
+class NodeAuthPassword(object):
+ """
+ A password to be used for authentication to a node.
+ """
+ def __init__(self, password):
+ self.password = password
+ def __repr__(self):
+ return '<NodeAuthPassword>'
+
+class NodeDriver(object):
+ """
+ A base NodeDriver class to derive from
+
+ This class is always subclassed by a specific driver. For
+ examples of base behavior of most functions (except deploy node)
+ see the dummy driver.
+
+ """
+
+ connectionCls = ConnectionKey
+ name = None
+ type = None
+ port = None
+ features = {"create_node": []}
+ """
+ List of available features for a driver.
+ - L{create_node}
+ - ssh_key: Supports L{NodeAuthSSHKey} as an authentication method
+ for nodes.
+ - password: Supports L{NodeAuthPassword} as an authentication
+ method for nodes.
+ - generates_password: Returns a password attribute on the Node
+ object returned from creation.
+ """
+
+ NODE_STATE_MAP = {}
+
+ def __init__(self, key, secret=None, secure=True, host=None, port=None):
+ """
+ @keyword key: API key or username to used
+ @type key: str
+
+ @keyword secret: Secret password to be used
+ @type secret: str
+
+ @keyword secure: Weither to use HTTPS or HTTP. Note: Some providers
+ only support HTTPS, and it is on by default.
+ @type secure: bool
+
+ @keyword host: Override hostname used for connections.
+ @type host: str
+
+ @keyword port: Override port used for connections.
+ @type port: int
+ """
+ self.key = key
+ self.secret = secret
+ self.secure = secure
+ args = [self.key]
+
+ if self.secret != None:
+ args.append(self.secret)
+
+ args.append(secure)
+
+ if host != None:
+ args.append(host)
+
+ if port != None:
+ args.append(port)
+
+ self.connection = self.connectionCls(*args)
+
+ self.connection.driver = self
+ self.connection.connect()
+
+ def create_node(self, **kwargs):
+ """Create a new node instance.
+
+ @keyword name: String with a name for this new node (required)
+ @type name: str
+
+ @keyword size: The size of resources allocated to this node.
+ (required)
+ @type size: L{NodeSize}
+
+ @keyword image: OS Image to boot on node. (required)
+ @type image: L{NodeImage}
+
+ @keyword location: Which data center to create a node in. If empty,
+ undefined behavoir will be selected. (optional)
+ @type location: L{NodeLocation}
+
+ @keyword auth: Initial authentication information for the node
+ (optional)
+ @type auth: L{NodeAuthSSHKey} or L{NodeAuthPassword}
+
+ @return: The newly created L{Node}.
+ """
+ raise NotImplementedError, \
+ 'create_node not implemented for this driver'
+
+ def destroy_node(self, node):
+ """Destroy a node.
+
+ Depending upon the provider, this may destroy all data associated with
+ the node, including backups.
+
+ @return: C{bool} True if the destroy was successful, otherwise False
+ """
+ raise NotImplementedError, \
+ 'destroy_node not implemented for this driver'
+
+ def reboot_node(self, node):
+ """
+ Reboot a node.
+ @return: C{bool} True if the reboot was successful, otherwise False
+ """
+ raise NotImplementedError, \
+ 'reboot_node not implemented for this driver'
+
+ def list_nodes(self):
+ """
+ List all nodes
+ @return: C{list} of L{Node} objects
+ """
+ raise NotImplementedError, \
+ 'list_nodes not implemented for this driver'
+
+ def list_images(self, location=None):
+ """
+ List images on a provider
+ @return: C{list} of L{NodeImage} objects
+ """
+ raise NotImplementedError, \
+ 'list_images not implemented for this driver'
+
+ def list_sizes(self, location=None):
+ """
+ List sizes on a provider
+ @return: C{list} of L{NodeSize} objects
+ """
+ raise NotImplementedError, \
+ 'list_sizes not implemented for this driver'
+
+ def list_locations(self):
+ """
+ List data centers for a provider
+ @return: C{list} of L{NodeLocation} objects
+ """
+ raise NotImplementedError, \
+ 'list_locations not implemented for this driver'
+
+ def deploy_node(self, **kwargs):
+ """
+ Create a new node, and start deployment.
+
+ Depends on a Provider Driver supporting either using a specific password
+ or returning a generated password.
+
+ This function may raise a L{DeploymentException}, if a create_node
+ call was successful, but there is a later error (like SSH failing or
+ timing out). This exception includes a Node object which you may want
+ to destroy if incomplete deployments are not desirable.
+
+ @keyword deploy: Deployment to run once machine is online and availble to SSH.
+ @type deploy: L{Deployment}
+
+ @keyword ssh_username: Optional name of the account which is used when connecting to
+ SSH server (default is root)
+ @type ssh_username: C{str}
+
+ @keyword ssh_port: Optional SSH server port (default is 22)
+ @type ssh_port: C{int}
+
+ See L{NodeDriver.create_node} for more keyword args.
+
+ >>> from libcloud.compute.drivers.dummy import DummyNodeDriver
+ >>> from libcloud.deployment import ScriptDeployment, MultiStepDeployment
+ >>> from libcloud.compute.base import NodeAuthSSHKey
+ >>> driver = DummyNodeDriver(0)
+ >>> key = NodeAuthSSHKey('...') # read from file
+ >>> script = ScriptDeployment("yum -y install emacs strace tcpdump")
+ >>> msd = MultiStepDeployment([key, script])
+ >>> def d():
+ ... try:
+ ... node = driver.deploy_node(deploy=msd)
+ ... except NotImplementedError:
+ ... print "not implemented for dummy driver"
+ >>> d()
+ not implemented for dummy driver
+
+ Deploy node is typically not overridden in subclasses. The
+ existing implementation should be able to handle most such.
</