azure-0.7.9/0000755000004100000410000000000013112322225012676 5ustar www-datawww-dataazure-0.7.9/Rakefile0000644000004100000410000001152613112322225014350 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'rake/testtask' require 'rubygems/package_task' require 'dotenv/tasks' gem_spec = eval(File.read('./azure.gemspec')) Gem::PackageTask.new(gem_spec) do |pkg| pkg.need_zip = false pkg.need_tar = false end namespace :test do task :require_environment => :dotenv do unset_environment = [ ENV.fetch('AZURE_STORAGE_ACCOUNT', nil), ENV.fetch('AZURE_STORAGE_ACCESS_KEY', nil), ENV.fetch('AZURE_SERVICEBUS_NAMESPACE', nil), ENV.fetch('AZURE_SERVICEBUS_ACCESS_KEY', nil), ENV.fetch('AZURE_MANAGEMENT_CERTIFICATE', nil), ENV.fetch('AZURE_SUBSCRIPTION_ID', nil) ].include?(nil) abort '[ABORTING] Configure your environment to run the integration tests' if unset_environment end Rake::TestTask.new :unit do |t| t.pattern = 'test/unit/**/*_test.rb' t.verbose = true t.libs = %w(lib test) end namespace :unit do def component_task(component) Rake::TestTask.new component do |t| t.pattern = "test/unit/#{component}/**/*_test.rb" t.verbose = true t.libs = %w(lib test) end end component_task :affinity_group component_task :base_management component_task :blob component_task :cloud_service_management component_task :core component_task :database component_task :service component_task :storage_management component_task :table component_task :virtual_machine_image_management component_task :virtual_machine_management component_task :vnet end Rake::TestTask.new :integration do |t| t.test_files = Dir['test/integration/**/*_test.rb'].reject do |path| path.include?('database') end t.verbose = true t.libs = %w(lib test) end task :integration => :require_environment namespace :integration do def component_task(component) Rake::TestTask.new component do |t| t.pattern = "test/integration/#{component}/**/*_test.rb" t.verbose = true t.libs = %w(lib test) end task component => 'test:require_environment' end component_task :affinity_group component_task :blob component_task :cloud_service component_task :database component_task :location component_task :queue component_task :service_bus component_task :storage_management component_task :table component_task :vm component_task :vm_image component_task :vnet end Rake::TestTask.new :recorded do |t| t.test_files = Dir['test/integration/**/*_test.rb'].reject do |path| # Reject the test paths those are not yet VCR recorded # Following three are Azure-Storage gem features and need to be removed when we take dependency on azure-storage gem path.include?('/blob/') || path.include?('/queue/') || path.include?('/table/') end t.verbose = true t.libs = %w(lib test) end namespace :recorded do def component_task(component) Rake::TestTask.new component do |t| t.pattern = "test/integration/#{component}/**/*_test.rb" t.verbose = true t.libs = %w(lib test) end end component_task :affinity_group component_task :cloud_service component_task :database component_task :location component_task :service_bus component_task :storage_management component_task :vm component_task :vm_image component_task :vnet end task :cleanup => :require_environment do $:.unshift 'lib' require 'azure' Azure.configure do |config| config.access_key = ENV.fetch('AZURE_STORAGE_ACCESS_KEY') config.account_name = ENV.fetch('AZURE_STORAGE_ACCOUNT') config.acs_namespace = ENV.fetch('AZURE_SERVICEBUS_NAMESPACE') config.sb_access_key = ENV.fetch('AZURE_SERVICEBUS_ACCESS_KEY') config.management_certificate = ENV.fetch('AZURE_MANAGEMENT_CERTIFICATE') config.management_endpoint = ENV.fetch('AZURE_MANAGEMENT_ENDPOINT') config.sql_database_management_endpoint = ENV.fetch('AZURE_SQL_DATABASE_MANAGEMENT_ENDPOINT') config.subscription_id = ENV.fetch('AZURE_SUBSCRIPTION_ID') end end end task :test => %w(test:unit test:integration) task :default => :test azure-0.7.9/bin/0000755000004100000410000000000013112322225013446 5ustar www-datawww-dataazure-0.7.9/bin/pfxer0000755000004100000410000000223613112322225014523 0ustar www-datawww-data#!/usr/bin/env ruby -U require 'azure' require 'thor' require 'fileutils' module Azure class Pfxer < Thor desc 'transform', 'Transforms a publish settings file from Azure into a .pfx' long_desc <<-TRANSFORM `transform` will create a .pfx file from an Azure publish settings file. The publish settings file contains a pfx, but the pfx is base64 encode within the publish settings xml structure. This is here to make that a little easier. TRANSFORM option :in, required: true, desc: 'Path to publish settings file' option :out, desc: 'Path to where you want your .pfx' def transform path = File.expand_path(options[:in]) if File.exists?(path) pub_settings_xml = Nokogiri::XML(File.read(path)) b64_pfx = pub_settings_xml.css('PublishData PublishProfile Subscription')[0]['ManagementCertificate'] pfx = Base64.decode64(b64_pfx) file_name = File.basename(path, '.publishsettings').downcase.gsub(' ', '_') out_path = options[:out] || File.join(File.dirname(path), file_name + '.pfx') File.binwrite(File.expand_path(out_path), pfx) end end end end Azure::Pfxer.start(ARGV)azure-0.7.9/Gemfile0000644000004100000410000000143513112322225014174 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- source 'https://rubygems.org' gemspecazure-0.7.9/ChangeLog.md0000644000004100000410000001760613112322225015061 0ustar www-datawww-data## 2017.04.19 - Azure Gems @version 0.7.9 * No new changes in this release. This version is released to fix the checksum issue in the rubygems. [#755](https://github.com/Azure/azure-sdk-for-ruby/issues/755) ## 2017.04.17 - Azure Gems @version 0.7.8 * Code changes to install nokogiri based on the ruby version. [#749](https://github.com/Azure/azure-sdk-for-ruby/pull/749) ## 2016.11.14 - Azure Gems @version 0.7.7 * Fixing Relax mime-types gem pinning Issue [#482](https://github.com/Azure/azure-sdk-for-ruby/issues/482) PR [#518](https://github.com/Azure/azure-sdk-for-ruby/pull/518) * Fixing Azure gem has hard dependency on JSON 1.8 and fails with newer ruby versions [#482](https://github.com/Azure/azure-sdk-for-ruby/issues/490) PR [#518](https://github.com/Azure/azure-sdk-for-ruby/pull/518) ## 2016.9.1 - Azure Gems @version 0.7.6 * Fixing Service Bus authorization with SAS [#425](https://github.com/Azure/azure-sdk-for-ruby/commit/09ab24e511889d41a1fef4755217baa595a279f5) * Adding require for azure/core and autoload Auth::SharedAccessSigner [#443](https://github.com/Azure/azure-sdk-for-ruby/pull/443) * Blacklist strict-transport-security, it crashes the JSON parser when receiving Service Bus messages [#432](https://github.com/Azure/azure-sdk-for-ruby/pull/432) * Replace uuid gem with SecureRandom [#394](https://github.com/Azure/azure-sdk-for-ruby/commit/1d25e563c6c21aaa42cb77ce08bba1303656700b) ## 2016.5.2 - Azure Gems @version 0.7.5 * Require azure/core before extending serialization class with it [#383](https://github.com/Azure/azure-sdk-for-ruby/pull/383) ## 2016.4.18 - Azure Gems @version 0.7.4 * Signing key must be provided (ArgumentError) [#367](https://github.com/Azure/azure-sdk-for-ruby/issues/367) * Can not upload file with spaces [#360](https://github.com/Azure/azure-sdk-for-ruby/issues/360) * Updating gem to consume 'azure-core' [gem](https://rubygems.org/gems/azure-core), [Git repo](https://github.com/Azure/azure-ruby-asm-core) ## 2016.4.12 - Azure Gems @version 0.7.3 * Support for VCR recorded test enabled * Following Issues were addressed [April Release Closed Issues](https://github.com/Azure/azure-sdk-for-ruby/issues?q=milestone%3A%22April+Release%22+is%3Aclosed) ## 2016.03.11 Azure::ARM Gems @version 0.2.1 * fix the require for module_definition via [#327](https://github.com/Azure/azure-sdk-for-ruby/issues/327) ## 2016.03.11 Azure::ARM Gems @version 0.2.0 * azure_mgmt_compute version 0.2.0 * http://www.rubydoc.info/gems/azure_mgmt_compute * added scale sets * warn on vm_size unknown * azure_mgmt_network version 0.2.0 * added express route * added route tables * breaking change: public_ip_addresses became public_ipaddresses * azure_mgmt_resource version 0.2.0 * added policy_assignments and policy_definitions * updated to the latest api_version (2015-11-01) * azure_mgmt_storage version 0.2.0 * update to the latest api_version (2015-06-15) * azure_mgmt_cdn version 0.2.0 * [Azure CDN management SDK](https://azure.microsoft.com/en-us/services/cdn/) * initial release: http://www.rubydoc.info/gems/azure_mgmt_cdn * azure_mgmt_authorization version 0.2.0 * [Azure Role Based Authorization management SDK](https://azure.microsoft.com/en-us/documentation/articles/role-based-access-control-configure/) * initial release: http://www.rubydoc.info/gems/azure_mgmt_authorization * azure_mgmt_features version 0.2.0 * [Azure Feature Exposure Control management SDK](https://msdn.microsoft.com/en-us/library/azure/mt592690.aspx) * initial release: http://www.rubydoc.info/gems/azure_mgmt_features * azure_mgmt_graph version 0.2.0 * [Azure Active Directory Graph SDK](https://msdn.microsoft.com/en-us/library/azure/hh974476.aspx) * initial release: http://www.rubydoc.info/gems/azure_mgmt_graph * azure_mgmt_locks version 0.2.0 * [Azure Resource Management Locks SDK](https://msdn.microsoft.com/en-us/library/azure/mt204563.aspx) * initial release: http://www.rubydoc.info/gems/azure_mgmt_locks * azure_mgmt_notification_hubs * [Azure Notification Hubs Management SDK](https://azure.microsoft.com/en-us/documentation/services/notification-hubs/) * initial release: http://www.rubydoc.info/gems/azure_mgmt_notification_hubs * azure_mgmt_redis version 0.2.0 * [Azure Redis Management SDK](https://azure.microsoft.com/en-us/services/cache/) * initial release: http://www.rubydoc.info/gems/azure_mgmt_redis * azure_mgmt_scheduler version 0.2.0 * [Azure Scheduler Management SDK](https://azure.microsoft.com/en-us/services/scheduler/) * initial release: http://www.rubydoc.info/gems/azure_mgmt_scheduler * azure_mgmt_search version 0.2.0 * [Azure Search Management SDK](https://azure.microsoft.com/en-us/services/search/) * initial release: http://www.rubydoc.info/gems/azure_mgmt_search * azure_mgmt_sql version 0.2.0 * [Azure SQL Management SDK](https://azure.microsoft.com/en-us/services/sql-database/) * initial release: http://www.rubydoc.info/gems/azure_mgmt_sql * azure_mgmt_subscriptions version 0.2.0 * [Azure Subscriptions Management SDK](https://azure.microsoft.com/en-us/services/sql-database/) * initial release: http://www.rubydoc.info/gems/azure_mgmt_subscriptions * azure_mgmt_web version 0.2.0 * [Azure WebApp Managment SDK](https://azure.microsoft.com/en-us/services/app-service/web/) * initial release: http://www.rubydoc.info/gems/azure_mgmt_web ## 2015.8.28 - Azure version 0.7.1 * Documentation updates to the readme * Content encoding adapted for Blobs in the case of IO or String * Make sure that auto_generated certificate is uploaded with add_role for Cloud Services * Fix issue of autoloading VirtualMachineDiskManagementService ## 2015.05.06 - Azure version 0.7.0.pre * Upgraded ASM versioning to 2014-06-01 * Added the ability to use personal vm images not just gallery images * Namespaced all of the things so there is no leakage * Added proper support for .pfx * Added bin to transform .publishsettings files to .pfx files * Added support for Azure.config.management_certificate as string * Added ms-blob-content-disposition support, bump blob `x-ms-version` header to `2013-08-15` * Fixed KeepAlive and timeout for blobs * VM sizes are available -- we just warn if they are not in our list * Support for nokogiri on windows * Ability to delete a staging deployment of a cloud service * Peek_lock fixed for service bus * Support for creating shared access signatures ## 2014.05.06 - Azure version 0.6.4 * Upgraded Service Management Versioning to 2014-04-01 * Created separate API for add role * Logical Unit Number(lun) is optional argument in API add_data_disk * Cloud service should delete only if there are no other VMs/Deployments in the cloud service * Added more sizes(Basic_A0, Basic_A1, Basic_A2, Basic_A3, Basic_A4) options for Virtual Machine and Cloud Service. ## 2014.03.28 - Azure version 0.6.3 * Added get_cloud_service_properties method, which returns all cloud service properties (embed-detail=true), including info about all VMs * Added winrm_http_port and winrm_https_port to get_virtual_machine method to allow the users to configure custom ports for winrm-http and winrm-https * Checks if any ports are in use before adding a role in the existing cloud service * Auto generate public port for add role. * Fix issue https://github.com/Azure/azure-sdk-for-ruby/issues/130 ## 2014.03.15 - Azure version 0.6.2 * Restart Virtual Machine * Add disk to Virtual Machine * Add/Update Virtual Machine endpoints * Delete Virtual Machine endpoint ## 2014.02.18 - Azure version 0.6.1 * Fixed http redirection error * Add a new role to existing deployment * Add support for including VMs in availability sets ## 2013.12.02 - Azure version 0.6.0 * Add the following service management API * Virtual Machine * Virtual Machine Image * Virtual Network * Cloud Service * Storage * Sql Database * Location and Affinity Group ## 2013.04.25 - Azure version 0.5.0 * First release * Ruby 1.9.3 and 2.0 support * Storage support: Blob, Table and Queue * Service Bus support: Queue, Topic/Subscription azure-0.7.9/LICENSE.txt0000644000004100000410000002613013112322225014523 0ustar www-datawww-data 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 2015 Microsoft Corporation 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. azure-0.7.9/.travis.yml0000644000004100000410000000103413112322225015005 0ustar www-datawww-datasudo: false language: ruby rvm: - 2.0.0 - 2.1.5 - 2.2.0 - 2.3.0 script: - bundle install && bundle exec rake test:unit - if [ "$INTEG_RECORDED" == "true" ] ; then bundle exec rake test:recorded ; fi deploy: provider: rubygems api_key: secure: VXi28DFUnQ7tYuvOwwfs4Og0/Wkfq4GWMtU9Wjgn6VuYMuRMzMD34acIpgqCDW4dVLlsCtvx1MIZLvaHe0F5nOJP1LZ88IW8bDEa2dSRMhXXKGf/CF9sOhMxK9uoThMp7FHMSttfasudooIsrQn9FExdCs5s7ndDQ+gOD/QHKjk= gem: azure on: tags: true condition: "$TRAVIS_RUBY_VERSION == 2.3.0" repo: Azure/azure-sdk-for-ruby azure-0.7.9/lib/0000755000004100000410000000000013112322225013444 5ustar www-datawww-dataazure-0.7.9/lib/azure/0000755000004100000410000000000013112322225014572 5ustar www-datawww-dataazure-0.7.9/lib/azure/default.rb0000644000004100000410000000677413112322225016561 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- module Azure # Default configuration options for {Client} module Default # Default API endpoint SERVICE_MANAGEMENT_ENDPOINT = 'https://management.core.windows.net'.freeze # Default User Agent header string USER_AGENT = "Azure-SDK-For-Ruby/#{Azure::Version}".freeze class << self # Configuration options # @return [Hash] def options Hash[Azure::Configurable.keys.map{|key| [key, send(key)]}] end # Default storage access key # @return [String] def storage_access_key ENV['AZURE_STORAGE_ACCESS_KEY'] end # Default storage account name # @return [String] def storage_account_name ENV['AZURE_STORAGE_ACCOUNT'] end # Default storage table host # @return [String] def storage_table_host ENV['AZURE_STORAGE_TABLE_HOST'] end # Default storage blob host # @return [String] def storage_blob_host ENV['AZURE_STORAGE_BLOB_HOST'] end # Default storage queue host # @return [String] def storage_queue_host ENV['AZURE_STORAGE_QUEUE_HOST'] end # Default service bus namespace # @return [String] def sb_namespace ENV['AZURE_SERVICEBUS_NAMESPACE'] end # Default service bus access key # @return [String] def sb_access_key ENV['AZURE_SERVICEBUS_ACCESS_KEY'] end # Default service bus Shared Access Policy key # @return [String] def sb_sas_key ENV['AZURE_SERVICEBUS_SAS_KEY'] end # Default service bus Shared Access Policy key name # @return [String] def sb_sas_key_name ENV['AZURE_SERVICEBUS_SAS_KEY_NAME'] end # Default service bus issuer # @return [String] def sb_issuer ENV['AZURE_SERVICEBUS_ISSUER'] || 'owner' end # Default management certificate to use for management activeities # @return [String] def management_certificate ENV['AZURE_MANAGEMENT_CERTIFICATE'] end # Default subscription to use for management activities # @return [String] def subscription_id ENV['AZURE_SUBSCRIPTION_ID'] end # Default Azure management endpoint # @return [String] def management_endpoint ENV['AZURE_MANAGEMENT_ENDPOINT'] || (SERVICE_MANAGEMENT_ENDPOINT + '/') end # Default SQL database management endpoint # @return [String] def sql_database_management_endpoint ENV['AZURE_SQL_DATABASE_MANAGEMENT_ENDPOINT'] || "#{SERVICE_MANAGEMENT_ENDPOINT}:8443/" end # Default Certificate Authority bundle to be used for https # @return [String] def ca_file ENV['SSL_CERT_FILE'] end end end end azure-0.7.9/lib/azure/cloud_service_management/0000755000004100000410000000000013112322225021614 5ustar www-datawww-dataazure-0.7.9/lib/azure/cloud_service_management/serialization.rb0000644000004100000410000001134113112322225025016 0ustar www-datawww-data#------------------------------------------------------------------------- # Copyright 2013 Microsoft Open Technologies, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'base64' require 'azure/cloud_service_management/cloud_service' require 'azure/virtual_machine_management/serialization' module Azure module CloudServiceManagement module Serialization extend Azure::Core::Utility def self.cloud_services_to_xml(name, options = {}) options[:label] = options[:label] || name builder = Nokogiri::XML::Builder.new do |xml| xml.CreateHostedService( 'xmlns' => 'http://schemas.microsoft.com/windowsazure' ) do xml.ServiceName(name) xml.Label(Base64.encode64(options[:label])) xml.Description(options[:description]) unless\ options[:description].nil? || options[:description].empty? unless options[:affinity_group_name].nil? xml.AffinityGroup(options[:affinity_group_name]) else xml.Location(options[:location]) end xml.ExtendedProperties do options[:extended_properties].each do |prop_name, prop_value| xml.ExtendedProperty do xml.Name(prop_name) xml.Value(prop_value) end unless (prop_name.nil? || prop_name.empty?)\ || (prop_value.nil? || prop_value.empty?) end end unless options[:extended_properties].nil?\ || options[:extended_properties].empty? end end builder.doc.to_xml end def self.cloud_services_from_xml(cloud_xml) clouds = [] cloud_services_xml = cloud_xml.css('HostedServices HostedService') cloud_services_xml = cloud_xml.css('HostedService') if \ cloud_services_xml.length == 0 cloud_services_xml.each do |cloud_service_xml| cloud = CloudService.new cloud.url = xml_content(cloud_service_xml, 'Url') cloud.name = xml_content(cloud_service_xml, 'ServiceName') props_xml = cloud_service_xml.css('HostedServiceProperties') cloud.label = Base64.decode64(xml_content(props_xml, 'Label')) cloud.description = xml_content(props_xml, 'Description') location = xml_content(props_xml, 'Location') cloud.location = location unless location.empty? affinity_group = xml_content(props_xml, 'AffinityGroup') cloud.affinity_group = affinity_group unless affinity_group cloud.status = xml_content(props_xml, 'Status') cloud.date_created = xml_content(props_xml, 'DateCreated') cloud.date_modified = xml_content(props_xml, 'DateLastModified') cloud.extended_properties = {} props_xml.css('ExtendedProperties ExtendedProperty').map do |prop| p_name = xml_content(prop, 'Name') p_value = xml_content(prop, 'Value') cloud.extended_properties[p_name] = p_value end cloud.default_winrm_certificate_thumbprint = xml_content( cloud_service_xml, 'DefaultWinRMCertificateThumbprint' ) deployment_xml = cloud_services_xml.css('Deployments Deployment') cloud.deployment_name = xml_content(deployment_xml, 'Name') vms_in_deployment = {} cloud_service_xml.css('Deployments').each do |deployxml| deployment_name = xml_content(deployxml, 'Deployment Name') vms = Azure::VirtualMachineManagement::Serialization.virtual_machines_from_xml( deployxml, cloud.name ) vms_in_deployment[deployment_name.to_sym] = vms if vms end cloud.virtual_machines = vms_in_deployment clouds << cloud end clouds.compact end def self.add_certificate_to_xml(data) builder = Nokogiri::XML::Builder.new do |xml| xml.CertificateFile('xmlns' => 'http://schemas.microsoft.com/windowsazure') do xml.Data data xml.CertificateFormat 'pfx' xml.Password nil end end builder.doc.to_xml end end end end azure-0.7.9/lib/azure/cloud_service_management/cloud_service_management_service.rb0000644000004100000410000001321613112322225030706 0ustar www-datawww-data#------------------------------------------------------------------------- # Copyright 2013 Microsoft Open Technologies, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/cloud_service_management/serialization' module Azure module CloudServiceManagement class CloudServiceManagementService < BaseManagement::BaseManagementService include Azure::Core::Utility # Public: Creates a new cloud service in Microsoft Azure. # # ==== Attributes # # * +name+ - String. The name of the cloud service. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:label+ -String. The label for this cloud service. # * +:description+ - String. A description for the hosted service. (optional) # * +:location+ - String. The regional data center location where the # cloud service will be created. Required if affinity group not # specified (optional) # * +:affinity_group_name - String. Name of the affinity group with # which to assocate the cloud service. Required if location not # specified (optional) # * +:extended_properties+ - Hash. Key/Value pairs of extended # properties to add to the cloud service. The key is used as the # property name and the value as its value. (optional) # # See http://msdn.microsoft.com/en-us/library/azure/gg441304.aspx # # Returns None def create_cloud_service(name, options = {}) Azure::Loggerx.error_with_exit 'Cloud service name is not valid ' unless name if get_cloud_service(name) Azure::Loggerx.warn "Cloud service #{name} already exists. Skipped..." else Azure::Loggerx.info "Creating cloud service #{name}." request_path = '/services/hostedservices' body = Serialization.cloud_services_to_xml(name, options) request = client.management_request(:post, request_path, body) request.call end end # Public: Gets a list of hosted services available under the current subscription. # # Returns an array of Azure::CloudServiceManagement::CloudService objects def list_cloud_services request_path = '/services/hostedservices' request = client.management_request(:get, request_path) response = request.call Serialization.cloud_services_from_xml(response) end # Public: Checks to see if the specified hosted service is available # # ==== Attributes # # * +name+ - String. Cloud service name. # # Returns: A boolean value indicating whether the cloud service exists. # If true, the cloud service is available. If false, the cloud service # does not exist. def get_cloud_service(name) list_cloud_services.select { |x| x.name.casecmp(name) == 0 }.first end def get_cloud_service_properties(name) request_path = "/services/hostedservices/#{name}?embed-detail=true" request = client.management_request(:get, request_path) response = request.call Serialization.cloud_services_from_xml(response).first end # Public: Deletes the specified cloud service of given subscription id from Microsoft Azure. # # ==== Attributes # # * +name+ - String. Cloud service name. # # Returns: None def delete_cloud_service(cloud_service_name) request_path= "/services/hostedservices/#{cloud_service_name}" request = client.management_request(:delete, request_path) Azure::Loggerx.info "Deleting cloud service #{cloud_service_name}. \n" request.call end # Public: Deletes the specified deployment. # # ==== Attributes # # * +cloud_service_name+ - String. Cloud service name. # * +slot+ - String. 'production' or 'staging'. Optional parameters. # Default if not specified is 'production' # # See http://msdn.microsoft.com/en-us/library/azure/ee460815.aspx # # Returns NONE def delete_cloud_service_deployment(cloud_service_name, slot='production') slot = 'production' unless slot request_path= "/services/hostedservices/#{cloud_service_name}/deploymentslots/#{slot}" request = client.management_request(:delete, request_path) Azure::Loggerx.info "Deleting deployment of cloud service \"#{cloud_service_name}\" ..." request.call end def upload_certificate(cloud_service_name, ssh) data = export_der(ssh[:cert], ssh[:key]) request_path= "/services/hostedservices/#{cloud_service_name}/certificates" body = Serialization.add_certificate_to_xml(data) Azure::Loggerx.info "Uploading certificate to cloud service #{cloud_service_name}..." request = client.management_request(:post, request_path, body) request.call end end end end Azure::CloudServiceManagementService = Azure::CloudServiceManagement::CloudServiceManagementService azure-0.7.9/lib/azure/cloud_service_management/cloud_service.rb0000644000004100000410000000250213112322225024766 0ustar www-datawww-data#------------------------------------------------------------------------- # Copyright 2013 Microsoft Open Technologies, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- module Azure module CloudServiceManagement class CloudService def initialize yield self if block_given? end attr_accessor :url attr_accessor :name attr_accessor :label attr_accessor :description attr_accessor :location attr_accessor :affinity_group attr_accessor :status attr_accessor :date_created attr_accessor :date_modified attr_accessor :extended_properties attr_accessor :default_winrm_certificate_thumbprint attr_accessor :virtual_machines attr_accessor :deployment_name end end end azure-0.7.9/lib/azure/base_management/0000755000004100000410000000000013112322225017700 5ustar www-datawww-dataazure-0.7.9/lib/azure/base_management/serialization.rb0000644000004100000410000001152313112322225023104 0ustar www-datawww-data#------------------------------------------------------------------------- # Copyright 2013 Microsoft Open Technologies, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/core' module Azure module BaseManagement module Serialization extend Azure::Core::Utility def self.locations_from_xml(locationXML) location_objs = [] xml = locationXML.css('Locations Location') xml.each do |meta_node| loc = Location.new loc.name = xml_content(meta_node, 'Name') available_services = meta_node.css('AvailableServices').children loc.available_services = available_services.to_ary.join(', ') role_sizes = [] meta_node.css('VirtualMachinesRoleSizes RoleSize').each do | role_size_node | role_sizes << role_size_node.text end loc.role_sizes = role_sizes location_objs << loc end location_objs end def self.affinity_group_to_xml(name, location, label, options = {}) builder = Nokogiri::XML::Builder.new do |xml| xml.CreateAffinityGroup( 'xmlns' => 'http://schemas.microsoft.com/windowsazure' ) do xml.Name name xml.Label Base64.encode64(label).strip xml.Description options[:description] xml.Location location end end builder.doc.to_xml end def self.affinity_groups_from_xml(affinity_xml) affinity_groups = [] affinity_group_services_xml = affinity_xml.css( 'AffinityGroups AffinityGroup' ) affinity_group_services_xml.each do |ag_xml| affinity_group = AffinityGroup.new affinity_group.name = xml_content(ag_xml, 'Name') affinity_group.label = Base64.decode64(xml_content(ag_xml, 'Label')) affinity_group.description = xml_content(ag_xml, 'Description') affinity_group.location = xml_content(ag_xml, 'Location') capabilities = ag_xml.css('Capabilities Capability') affinity_group.capability = capabilities.map { |x| x.content } affinity_groups << affinity_group end affinity_groups.compact end def self.affinity_group_from_xml(affinity_xml) hosted_services_xml = affinity_xml.css( 'AffinityGroup HostedServices HostedService' ) storage_services_xml = affinity_xml.css( 'AffinityGroup StorageServices StorageService' ) capability_xml = affinity_xml.css( 'AffinityGroup Capabilities Capability' ) AffinityGroup.new do |affinity_group| affinity_group.name = xml_content( affinity_xml, 'AffinityGroup Name' ) affinity_group.label = Base64.decode64( xml_content( affinity_xml, 'AffinityGroup Label' ) ) affinity_group.description = xml_content( affinity_xml, 'AffinityGroup Description' ) affinity_group.location = xml_content( affinity_xml, 'AffinityGroup Location' ) affinity_group.hosted_services = [] hosted_services_xml.each do |hosted_service_xml| affinity_group.hosted_services << { url: xml_content(hosted_service_xml, 'Url'), service_name: xml_content(hosted_service_xml, 'ServiceName') } end affinity_group.storage_services = [] storage_services_xml.each do |storage_service_xml| affinity_group.storage_services << { url: xml_content(storage_service_xml, 'Url'), service_name: xml_content(storage_service_xml, 'ServiceName') } end affinity_group.capability = capability_xml.map { |x| x.content } end end def self.resource_to_xml(label, options = {}) builder = Nokogiri::XML::Builder.new do |xml| xml.UpdateAffinityGroup( 'xmlns' => 'http://schemas.microsoft.com/windowsazure' ) do xml.Label Base64.encode64(label).strip xml.Description options[:description] if options[:description] end end builder.doc.to_xml end end end end azure-0.7.9/lib/azure/base_management/affinity_group.rb0000644000004100000410000000217313112322225023255 0ustar www-datawww-data#------------------------------------------------------------------------- # Copyright 2013 Microsoft Open Technologies, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- module Azure module BaseManagement # Represents an AffinityGroup class AffinityGroup def initialize yield self if block_given? end attr_accessor :name attr_accessor :label attr_accessor :description attr_accessor :location attr_accessor :hosted_services attr_accessor :storage_services attr_accessor :capability end end end azure-0.7.9/lib/azure/base_management/location.rb0000644000004100000410000000174013112322225022037 0ustar www-datawww-data#------------------------------------------------------------------------- # Copyright 2013 Microsoft Open Technologies, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require "azure/base_management/serialization" module Azure module BaseManagement class Location attr_accessor :name, :available_services, :role_sizes def initialize yield self if block_given? end end end end azure-0.7.9/lib/azure/base_management/management_http_request.rb0000644000004100000410000001470113112322225025153 0ustar www-datawww-data#------------------------------------------------------------------------- # Copyright 2013 Microsoft Open Technologies, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/core/http/http_response' require 'azure/core/http/http_request' # Represents an HTTP request that can perform synchronous queries to # an HTTP server, returning a HttpResponse module Azure module BaseManagement class ManagementHttpRequest < Azure::Core::Http::HttpRequest include Azure::Core::Utility attr_accessor :warn # Creates the ManagementHttpRequest # # @param method [Symbol] The HTTP method to use (:get, :post, :put, :del, etc...) # @param path [URI] The URI of the HTTP endpoint to query # @param options_or_body [Hash|IO|String] The request options including {:client, :body} or raw body only def initialize(method, path, options_or_body = {}) options ||= unless options_or_body.is_a?(Hash) {body: options_or_body} end || options_or_body || {} options[:client] ||= Azure super(method, self.class.request_uri(path, options[:client]), options) @warn = options.fetch(:warn, false) content_length = body ? body.bytesize.to_s : '0' headers.update({ 'x-ms-version' => '2015-04-01', 'Content-Type' => 'application/xml', 'Content-Length' => content_length }) end # Public: Sends a request to HTTP server and returns a HttpResponse # # Returns a Nokogiri::XML instance of HttpResponse body def call conn = http_setup res = set_up_response(method.to_sym, uri, conn, headers ,body) response = wait_for_completion(Azure::Core::Http::HttpResponse.new(res)) Nokogiri::XML response.body unless response.nil? end def apply_body_headers super end # Public: Wait for HTTP request completion. # # ==== Attributes # # * +response+ - Azure::Core::Http::HttpResponse. HttpResponse Response # # Print Error or Success of HttpRequest def wait_for_completion(response) ret_val = Nokogiri::XML response.body if ret_val.at_css('Error Code') && ret_val.at_css('Error Code').content == 'AuthenticationFailed' Azure::Loggerx.error_with_exit ret_val.at_css('Error Code').content + ' : ' + ret_val.at_css('Error Message').content end if response.status_code.to_i == 200 || response.status_code.to_i == 201 response elsif redirected? response rebuild_request response elsif response.status_code.to_i > 201 && response.status_code.to_i <= 299 check_completion(response.headers['x-ms-request-id']) elsif warn && !response.success? response elsif response.body if ret_val.at_css('Error Code') && ret_val.at_css('Error Message') Azure::Loggerx.error_with_exit ret_val.at_css('Error Code').content + ' : ' + ret_val.at_css('Error Message').content else Azure::Loggerx.exception_message "http error: #{response.status_code}" end else Azure::Loggerx.exception_message "http error: #{response.status_code}" end end def http_setup http = super http.ssl[:client_cert] = @client.http_certificate_key if @client.http_certificate_key http.ssl[:client_key] = @client.http_private_key if @client.http_private_key http end # Public: Gets the status of the specified operation and determines whether # the operation has succeeded, failed, or is still in progress. # # ==== Attributes # # * +request_id+ - String. x-ms-request-id response header of request # # See: http://msdn.microsoft.com/en-us/library/azure/ee460783.aspx # # Print Error or Success of Operation. def check_completion(request_id) request_path = "/#{client.subscription_id}/operations/#{request_id}" conn = http_setup headers['Content-Length'] = '0' @method = :get done = false until done Azure::Loggerx.info('# ') res = set_up_response(method.to_sym, URI(request_path), conn, headers ,body) response = Azure::Core::Http::HttpResponse.new(res) ret_val = Nokogiri::XML response.body status = xml_content(ret_val, 'Operation Status') status_code = response.status_code.to_i if status != 'InProgress' done = true end if redirected? response @uri = self.class.request_uri(response.headers['location'], client) conn = http_setup done = false end if done if status.downcase != 'succeeded' error_code = xml_content(ret_val, 'Operation Error Code') error_msg = xml_content(ret_val, 'Operation Error Message') Azure::Loggerx.exception_message "#{error_code}: #{error_msg}" else Azure::Loggerx.success "#{status.downcase} (#{status_code})" end return else sleep(5) end end end def rebuild_request(response) host_uri = URI.parse(response.headers['location']) conn = http_setup res = set_up_response(method.to_sym, host_uri, conn, headers ,body) wait_for_completion(HttpResponse.new(res)) end def redirected?(response) (response.status_code.to_i == 307) end private def self.request_uri(path, client) relative_path = path =~ /^\// ? path[1..-1] : path # remove the starting slash in the relative path if exists URI.join(client.management_endpoint, '/' + client.subscription_id + '/', relative_path) end end end end azure-0.7.9/lib/azure/base_management/base_management_service.rb0000644000004100000410000001744513112322225025066 0ustar www-datawww-data#------------------------------------------------------------------------- # Copyright 2013 Microsoft Open Technologies, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/base_management/serialization' require 'azure/base_management/location' require 'azure/base_management/affinity_group' module Azure module BaseManagement # The base for all Azure management services # @!attribute [rw] client # The client contains the configuration scope and the ability to produce http agents. # defaults to global client. # @see Azure.client # @return [Azure::Client] class BaseManagementService attr_accessor :client # @param options [Hash] options including :client def initialize(options = {}) @client = options[:client] || Azure.client validate_configuration! end # Validate the configuration of the service. # @return [void] def validate_configuration! subs_id = @client.subscription_id error_message = 'Subscription ID not valid.' raise ArgumentError.new(error_message) if subs_id.nil? || subs_id.empty? m_ep = @client.management_endpoint error_message = 'Management endpoint not valid.' raise ArgumentError.new(error_message) if m_ep.nil? || m_ep.empty? end # Gets a list of regional data center locations from the server # @return [Array] def list_locations request = @client.management_request(:get, '/locations') response = request.call Serialization.locations_from_xml(response) end # Gets a list of role sizes associated with the # specified subscription. # @return [Array] def list_role_sizes role_sizes = [] locations = list_locations locations.each do | location | role_sizes << location.role_sizes end role_sizes.flatten.uniq.compact.sort end # Gets a lists the affinity groups associated with # the specified subscription. # @see http://msdn.microsoft.com/en-us/library/azure/ee460797.aspx # @return [Array] def list_affinity_groups request_path = '/affinitygroups' request = @client.management_request(:get, request_path) response = request.call Serialization.affinity_groups_from_xml(response) end # Creates a new affinity group for the specified subscription. # # ==== Attributes # # * +name+ - String. Affinity Group name. # * +location+ - String. The location where the affinity group will # be created. # * +label+ - String. Name for the affinity specified as a # base-64 encoded string. # # ==== Options # # Accepted key/value pairs are: # * +:description+ - String. A description for the affinity group. # (optional) # # @see http://msdn.microsoft.com/en-us/library/azure/gg715317.aspx # # @return [void] def create_affinity_group(name, location, label, options = {}) if name.nil? || name.strip.empty? raise 'Affinity Group name cannot be empty' elsif list_affinity_groups.map(&:name).include?(name) raise Azure::Error::Error.new( 'ConflictError', 409, "An affinity group #{name} already exists in the current subscription." ) else validate_location(location) body = Serialization.affinity_group_to_xml(name, location, label, options) request_path = '/affinitygroups' request = @client.management_request(:post, request_path, body) request.call Azure::Loggerx.info "Affinity Group #{name} is created." end end # Updates the label and/or the description for an affinity group # for the specified subscription. # # ==== Attributes # # * +name+ - String. Affinity Group name. # * +label+ - String. Name for the affinity specified as a # base-64 encoded string. # # ==== Options # # Accepted key/value pairs are: # * +:description+ - String. A description for the affinity group. # (optional) # # @see http://msdn.microsoft.com/en-us/library/azure/gg715316.aspx # # @return [void] def update_affinity_group(name, label, options = {}) raise 'Label name cannot be empty' if label.nil? || label.empty? if affinity_group(name) body = Serialization.resource_to_xml(label, options) request_path = "/affinitygroups/#{name}" request = @client.management_request(:put, request_path, body) request.call Azure::Loggerx.info "Affinity Group #{name} is updated." end end # Deletes an affinity group in the specified subscription # # ==== Attributes # # * +name+ - String. Affinity Group name. # # @see http://msdn.microsoft.com/en-us/library/azure/gg715314.aspx # # @return [void] def delete_affinity_group(name) if affinity_group(name) request_path = "/affinitygroups/#{name}" request = @client.management_request(:delete, request_path) request.call Azure::Loggerx.info "Deleted affinity group #{name}." end end # Returns the system properties associated with the specified # affinity group. # # ==== Attributes # # * +name+ - String. Affinity Group name. # # @see http://msdn.microsoft.com/en-us/library/azure/ee460789.aspx # # @return [Azure::BaseManagement::AffinityGroup] def get_affinity_group(name) if affinity_group(name) request_path = "/affinitygroups/#{name}" request = @client.management_request(:get, request_path) response = request.call Serialization.affinity_group_from_xml(response) end end private def affinity_group(affinity_group_name) if affinity_group_name.nil? ||\ affinity_group_name.empty? ||\ !list_affinity_groups.map { |x| x.name.downcase }.include?( affinity_group_name.downcase ) error = Azure::Error::Error.new('AffinityGroupNotFound', 404, 'The affinity group does not exist.') raise error else true end end def validate_location(location_name) base_mgmt_service = Azure::BaseManagementService.new locations = base_mgmt_service.list_locations.map(&:name) unless locations.map(&:downcase).include?(location_name.downcase) error = "Value '#{location_name}' specified for parameter"\ " 'location' is invalid."\ " Allowed values are #{locations.join(',')}" raise error end end end end end Azure::BaseManagementService = Azure::BaseManagement::BaseManagementService azure-0.7.9/lib/azure/base_management/sql_management_http_request.rb0000644000004100000410000000335313112322225026033 0ustar www-datawww-data#------------------------------------------------------------------------- # Copyright 2013 Microsoft Open Technologies, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- # Represents an HTTP request that can perform synchronous queries to # an HTTP server, returning a HttpResponse module Azure module BaseManagement # This class is used for communicating with the Management certificate authentication API endpoint class SqlManagementHttpRequest < ManagementHttpRequest # Creates the SqlManagementHttpRequest # # @param method [Symbol] The HTTP method to use (:get, :post, :put, :del, etc...) # @param path [URI] The URI of the HTTP endpoint to query # @param options_or_body [Hash|IO|String] The request options including {:client, :body} or raw body only def initialize(method, path, options_or_body = {}) path = '/services/sqlservers' + (path.start_with?('/') ? path : '/' + path) super(method, path, options_or_body) headers['x-ms-version'] = '2012-03-01' self.uri = URI.parse(Azure.config.sql_database_management_endpoint + Azure.config.subscription_id + path) end end end end azure-0.7.9/lib/azure/client_services.rb0000644000004100000410000001056213112322225020304 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- module Azure # The Azure::Configurable module provides quick access to the Azure services and initializes the service to use # the configuration of the class it's mixed into. module ClientServices # Azure Blob service configured for this Azure client instance # @return [Azure::Blob::BlobService] def blobs(options = {}) Azure::Blob::BlobService.new(default_client(options)) end # Azure Queue service configured for this Azure client instance # @return [Azure::Queue::QueueService] def queues(options = {}) Azure::Queue::QueueService.new(default_client(options)) end # Azure Table service configured for this Azure client instance # @return [Azure::Table::TableService] def tables(options = {}) Azure::Table::TableService.new(default_client(options)) end # Azure Service Bus service configured for this Azure client instance # @return [Azure::ServiceBus::ServiceBusService] def service_bus(options = {}) Azure::ServiceBus::ServiceBusService.new(service_bus_host, default_client(options)) end # Azure Virtual Image management service configured for this Azure client instance # @return [Azure::VirtualMachineImageManagement::VirtualMachineImageManagementService] def vm_image_management(options = {}) Azure::VirtualMachineImageManagement::VirtualMachineImageManagementService.new(default_client(options)) end # Azure Virtual Machine disk management service configured for this Azure client instance # @return [Azure::VirtualMachineImageManagement::VirtualMachineDiskManagementService] def vm_disk_management(options = {}) Azure::VirtualMachineImageManagement::VirtualMachineDiskManagementService.new(default_client(options)) end # Azure Virtual Machine management service configured for this Azure client instance # @return [Azure::VirtualMachineManagement::VirtualMachineManagementService] def vm_management(options = {}) Azure::VirtualMachineManagement::VirtualMachineManagementService.new(default_client(options)) end # Azure SQL Database management service configured for this Azure client instance # @return [Azure::SqlDatabaseManagement::SqlDatabaseManagementService] def sql_database_management(options = {}) Azure::SqlDatabaseManagement::SqlDatabaseManagementService.new(default_client(options)) end # Azure Network management service configured for this Azure client instance # @return [Azure::VirtualNetworkManagement::VirtualNetworkManagementService] def network_management(options = {}) Azure::VirtualNetworkManagement::VirtualNetworkManagementService.new(default_client(options)) end # Azure Cloud Service management service configured for this Azure client instance # @return [Azure::CloudServiceManagement::CloudServiceManagementService] def cloud_service_management(options = {}) Azure::CloudServiceManagement::CloudServiceManagementService.new(default_client(options)) end # Azure Storage management service configured for this Azure client instance # @return [Azure::StorageManagement::StorageManagementService] def storage_management(options = {}) Azure::StorageManagement::StorageManagementService.new(default_client(options)) end # Azure Generic management service configured for this Azure client instance # @return [Azure::BaseManagement::BaseManagementService] def base_management(options = {}) Azure::BaseManagement::BaseManagementService.new(default_client(options)) end private def default_client(opts) !opts.empty? ? {client: Azure.client(opts)} : {client: self} end end endazure-0.7.9/lib/azure/client.rb0000644000004100000410000000210213112322225016370 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- module Azure class Client include Azure::Configurable include Azure::HttpClient include Azure::ClientServices def initialize(options = {}) reset!(options) end # Check if this client is configured with the same options def same_options?(opts) opts.hash == options.hash end end endazure-0.7.9/lib/azure/service_bus/0000755000004100000410000000000013112322225017103 5ustar www-datawww-dataazure-0.7.9/lib/azure/service_bus/serialization.rb0000644000004100000410000001335413112322225022313 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'nokogiri' require "azure/service_bus/queue" require "azure/service_bus/topic" require "azure/service_bus/subscription" require "azure/service_bus/rule" require "azure/service_bus/relay" require "azure/service/enumeration_results" module Azure module ServiceBus module Serialization module ClassMethods def rule_to_xml(xml, rule) rule_aspect_to_xml xml, 'Filter', rule if rule.filter rule_aspect_to_xml xml, 'Action', rule if rule.action end def rule_aspect_to_xml(xml, aspect_name, rule) aspect = rule.description[aspect_name].dup xml.send(aspect_name, "i:type" => aspect.delete(:type)) { aspect.each { |k,v| if k == :sql_expression k = "SqlExpression" elsif k == :compatibility_level k = "CompatibilityLevel" elsif k == :correlation_id k = "CorrelationId" end xml.send(k, v) } } end def resource_to_xml(resource, entry) doc = Nokogiri::XML::Builder.new(:encoding => "UTF-8") do |xml| xml.entry(:xmlns => 'http://www.w3.org/2005/Atom') { xml.content(:type => 'application/xml') { xml.send("#{resource.to_s.capitalize}Description", 'xmlns' => 'http://schemas.microsoft.com/netservices/2010/10/servicebus/connect', 'xmlns:i' => 'http://www.w3.org/2001/XMLSchema-instance') { if resource == :rule rule_to_xml xml, entry else entry.get_props.each do |p| xml.send(p[0], p[1].to_s) end end } } } end doc.to_xml end def resource_from_xml(resource, node) resource = resource.to_s.capitalize name = (node % "title").text Azure::ServiceBus.const_get(resource).new(name) do |r| r.id = URI((node % "id").text) if (node % "id") r.published = Time.parse((node % "published").text) if (node % "published") r.updated = Time.parse((node % "updated").text) if (node % "updated") r.author_name = (node % "author/name").text if (node % "author/name") r.description = (node / "content/#{resource}Description *").each_with_object({}) do |element, description| if resource == "Rule" handle_rule_description_element element, description else description[element.name] = element.text end end end end def handle_rule_description_element(element, description) if element.name == "Filter" or element.name == "Action" value = {} value[:type] = element["type"] element.children.each do |child| if child.name == "SqlExpression" value[:sql_expression] = child.content elsif child.name == "CompatibilityLevel" value[:compatibility_level] = child.content elsif child.name == "CorrelationId" value[:correlation_id] = child.content end end description[element.name] = value end end def resources_from_xml(resource, xml) feed = Nokogiri::XML(xml).remove_namespaces! values = (feed / 'entry').map {|node| resource_from_xml(resource, node) } values.class.module_eval { attr_accessor :next_link} values.next_link = feed.xpath("//link[@rel='next']/@href") values end def resources_from_xml_with_next_link(resource, xml) feed = Nokogiri::XML(xml).remove_namespaces! values = Azure::Service::EnumerationResults.new((feed / 'entry').map {|node| resource_from_xml(resource, node) }) next_token = nil next_uri = feed.xpath("//link[@rel='next']/@href") if next_uri != nil && next_uri.length > 0 u = URI.parse(next_uri.to_s) p = CGI.parse(u.query) if p['skip'] || p['top'] next_token = { } next_token[:top] = p['top'] if p['top'] next_token[:skip] = p['skip'] if p['skip'] end end values.continuation_token = next_token values end def to_bool(s) (s || "").downcase == 'true' end def slopify(xml) node = (xml.is_a? String) ? Nokogiri.Slop(xml).root : xml node.slop! if node.is_a? Nokogiri::XML::Document unless node.respond_to? :method_missing node = node.root if node.is_a? Nokogiri::XML::Document node end def expect_node(node_name, xml) raise "Xml is not a #{node_name} node." unless xml.name == node_name end end extend ClassMethods def self.included( other ) other.extend( ClassMethods ) end end end end azure-0.7.9/lib/azure/service_bus/rule.rb0000644000004100000410000000675113112322225020410 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/service_bus/resource' require 'azure/service_bus/rule_aspect' require 'azure/service_bus/sql_filter' require 'azure/service_bus/true_filter' require 'azure/service_bus/false_filter' require 'azure/service_bus/correlation_filter' require 'azure/service_bus/sql_rule_action' require 'azure/service_bus/empty_rule_action' module Azure module ServiceBus class Rule < Resource attr_accessor :topic attr_accessor :subscription # Public: Initialize the rule. # # ==== Attributes # # * +name+ - A String with the name of the rule. # * +options+ - The resource options Hash # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:filter+ - String. The rule filter. # * +:action+ - String. The rule action. # def initialize(name, options = {}) normalized_options = {} normalized_options["Filter"] = options[:filter] if options.has_key?(:filter) normalized_options["Action"] = options[:action] if options.has_key?(:action) super(name, normalized_options) end # Filter: SqlFilter, TrueFilter /FalseFiilter, CorrelationFilter # # The Filter property references an instance of Filter that is evaluated against a message. The following types of # filters are provided: # # SqlFilter - A type of Filter that is represented by SQL expression. For detailed information about SqlFilter # syntax, see SqlFilter.SqlExpression Property. # # TrueFilter/FalseFiilter - A handy shortcut for always returning true or false. They are a type of SqlFilter. # # CorrelationFilter: A type of Filter that matches CorrelationId property of BrokeredMessage. def filter RuleAspect.from_hash(description['Filter']) end def filter=(val) description['Filter'] = val.to_hash end # Action: String # # The Action property references an instance of FilterAction. The following type of FilterAction are provided. # Default is an instance of EmptyRuleAction. # # SqlFilterAction - A type of FilterAction that is represented by SQL expression. For detailed information about # SqlFilterAction syntax, see SqlRuleAction.SqlExpression Property. # # EmptyRuleAction - A type of FilterAction that represents an empty action. def action RuleAspect.from_hash(description['Action']) end def action=(val) description['Action'] = val.to_hash end def ordered_props [ 'Filter', 'Action' ] end end end endazure-0.7.9/lib/azure/service_bus/topic.rb0000644000004100000410000002125313112322225020551 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/service_bus/resource' module Azure module ServiceBus class Topic < Resource # Public: Initialize the topic. # # ==== Attributes # # * +name+ - A String with the name of the topic. # * +options+ - The resource options Hash # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:default_message_time_to_tive+ - XML datetime. Determines how long a message lives in the associated subscriptions. # * +:maximum_number_of_subscriptions+ - Number. Specifies the maximum number of subscriptions that can be associated with the topic. # * +:max_size_in_megabytes+ - Number. Specifies the maximum topic size in megabytes # * +:requires_duplicate_detection+ - Boolean. If enabled, the topic will detect duplicate messages within the time span specified by the DuplicateDetectionHistoryTimeWindow property # * +:dead_lettering_on_filter_evaluation_exceptions+ - Boolean. Determines how the Service Bus handles a message that causes an exception during a subscription's filter evaluation. # * +:duplicate_detection_history_time_window+ - XML datetime. Specifies the time span during which the Service Bus will detect message duplication. # * +:enable_batched_operations+ - Boolean. Enables or disables service side batching behavior when performing operations for the specific queue. # def initialize(name, options = {}) normalized_options = {} normalized_options["DefaultMessageTimeToLive"] = options[:default_message_time_to_live].to_s if options.has_key?(:default_message_time_to_live) normalized_options["MaximumNumberOfSubscriptions"] = options[:maximum_number_of_subscriptions].to_s if options.has_key?(:maximum_number_of_subscriptions) normalized_options["MaxSizeInMegabytes"] = options[:max_size_in_megabytes].to_s if options.has_key?(:max_size_in_megabytes) normalized_options["RequiresDuplicateDetection"] = options[:requires_duplicate_detection].to_s if options.has_key?(:requires_duplicate_detection) normalized_options["DeadLetteringOnFilterEvaluationExceptions"] = options[:dead_lettering_on_filter_evaluation_exceptions].to_s if options.has_key?(:dead_lettering_on_filter_evaluation_exceptions) normalized_options["DuplicateDetectionHistoryTimeWindow"] = options[:duplicate_detection_history_time_window].to_s if options.has_key?(:duplicate_detection_history_time_window) normalized_options["EnableBatchedOperations"] = options[:enable_batched_operations].to_s if options.has_key?(:enable_batched_operations) super(name, normalized_options) end # MaxSizeInMegabytes: Number # # Specifies the maximum topic size in megabytes. Any attempt to enqueue a message that will cause the topic to # exceed this value will fail. All messages that are stored in the topic or any of its subscriptions count # towards this value. Multiple copies of a message that reside in one or multiple subscriptions count as a single # messages. For example, if message m exists once in subscription s1 and twice in subscription s2, m is counted # as a single message. You can only set this parameter at topic creation time using the following values: # # Range: 1 - 5*1024 MB # Default: 1*1024 def max_size_in_megabytes to_i description['MaxSizeInMegabytes'] end def max_size_in_megabytes=(val) _set 'MaxSizeInMegabytes', val end # SizeInBytes: Number # # Reflects the actual bytes toward the topic quota that messages in the topic currently occupy. (read-only) # # Range: 0 - MaxTopicSizeinMegaBytes def size_in_bytes to_i description['SizeInBytes'] end def size_in_bytes=(val) _set 'SizeInBytes', val end # DefaultMessageTimeToLive: XML datetime # # Determines how long a message lives in the associated subscriptions. Subscriptions inherit the TTL from the # topic unless they are created explicitly with a smaller TTL. Based on whether dead-lettering is enabled, a # message whose TTL has expired will either be moved to the subscription's associated DeadLtterQueue or will # be permanently deleted. The following values are settable at topic creation time: # # Range: 1 second - TimeSpan.MaxValue # Default: TimeSpan.MaxValue def default_message_time_to_live to_interval description['DefaultMessageTimeToLive'] end def default_message_time_to_live=(val) _set 'DefaultMessageTimeToLive', val end # RequiresDuplicateDetection: True, False # # If enabled, the topic will detect duplicate messages within the time span specified by the # DuplicateDetectionHistoryTimeWindow property. Settable only at topic creation time. # # Default: false def requires_duplicate_detection to_bool description['RequiresDuplicateDetection'] end def requires_duplicate_detection=(val) _set 'RequiresDuplicateDetection', val end # DuplicateDetectionHistoryTimeWindow # # Specifies the time span during which the Service Bus will detect message duplication. # # Range: 1 second - 7 days # Default: 10 minutes def duplicate_detection_history_time_window to_interval description['DuplicateDetectionHistoryTimeWindow'] end def duplicate_detection_history_time_window=(val) _set 'DuplicateDetectionHistoryTimeWindow', val end # MaximumNumberOfSubscriptions # # Specifies the maximum number of subscriptions that can be associated with the topic. # # Range: 1 - 2000 # Default: 2000 subscriptions def maximum_number_of_subscriptions to_i description['MaximumNumberOfSubscriptions'] end def maximum_number_of_subscriptions=(val) _set 'MaximumNumberOfSubscriptions', val end # EnableBatchedOperations # # Enables or disables service side batching behavior when performing operations for the specific queue. When # enabled, service bus will collect/batch multiple operations to the backend to be more connection efficient. # # If user wants lower operation latency then they can disable this feature. def enable_batched_operations to_bool description['EnableBatchedOperations'] end def enable_batched_operations=(val) _set 'EnableBatchedOperations', val end # DeadLetteringOnFilterEvaluationExceptions: True, False # # Determines how the Service Bus handles a message that causes an exception during a subscription's filter # evaluation. If the value is set to true, the message that caused the exception will be moved to the # subscription's dead-letter queue. Otherwise, it will be discarded. By default this parameter is set to true, # allowing the user a chance to investigate the cause of the exception. It can occur from a malformed message or # some incorrect assumptions being made in the filter about the form of the message. Settable only at topic # creation time. # # Default: true def dead_lettering_on_filter_evaluation_exceptions to_bool description['DeadLetteringOnFilterEvaluationExceptions'] end def dead_lettering_on_filter_evaluation_exceptions=(val) _set 'DeadLetteringOnFilterEvaluationExceptions', val end def ordered_props [ 'DefaultMessageTimeToLive', 'MaximumNumberOfSubscriptions', 'MaxSizeInMegabytes', 'RequiresDuplicateDetection', 'DeadLetteringOnFilterEvaluationExceptions', 'DuplicateDetectionHistoryTimeWindow', 'EnableBatchedOperations', 'SizeInBytes' ] end end end endazure-0.7.9/lib/azure/service_bus/true_filter.rb0000644000004100000410000000252613112322225021761 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/service_bus/sql_filter' module Azure module ServiceBus class TrueFilter < SqlFilter # Public: Initialize the SQL True Filter. # # ==== Attributes # # * +hash+ - The resource options Hash # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:sql_expression+ - The SQL expression. # def initialize(hash=nil) hash = {} unless hash hash[:sql_expression] = "1 = 1" unless hash[:sql_expression] super(hash) end end end end azure-0.7.9/lib/azure/service_bus/auth/0000755000004100000410000000000013112322225020044 5ustar www-datawww-dataazure-0.7.9/lib/azure/service_bus/auth/wrap_signer.rb0000644000004100000410000000447313112322225022721 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'uri' require 'azure/core/auth/signer' require 'azure/service_bus/auth/wrap_service' module Azure module ServiceBus module Auth class WrapSigner < Azure::Core::Auth::Signer def initialize(host = nil, options={}) client = (options[:client] || Azure) host = host || client.acs_host @tokens = {} @wrap_service = Azure::ServiceBus::Auth::WrapService.new(host, nil, nil, options) end attr_accessor :tokens def name 'WRAP' end public def sign_request(req) signature = sign(req.method, req.uri, req.headers) req.headers['Authorization'] = "#{name} #{signature}" end def sign(method, uri, headers) access_token = get_access_token(create_scope_uri(uri)) 'access_token="%s"' % access_token end private def get_access_token(uri) token = tokens[uri.to_s] token = tokens[uri.to_s] = @wrap_service.get_access_token(uri) unless valid_token?(token) token[:token] end private def create_scope_uri(target_uri) targetUriComponents = URI.parse(target_uri.to_s) # ACS body and caching should be HTTP targetUriComponents.scheme = 'http' # ACS body and caching should not include query targetUriComponents.query = nil targetUriComponents end private def valid_token?(token) token and token[:expiration] > Time.now.to_i end end end end endazure-0.7.9/lib/azure/service_bus/auth/wrap_service.rb0000644000004100000410000000607313112322225023070 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/core/filtered_service' require 'uri' module Azure module ServiceBus module Auth class WrapService < Azure::Core::FilteredService def initialize(host=nil, issuer=nil, access_key=nil, options={}) host = host || (options[:client] || Azure).acs_host super(host, options) @issuer = issuer || client.sb_issuer @access_key = access_key || client.sb_access_key end # Gets a WRAP access token with specified parameters. # # Returns access token (String) def get_access_token(resource_uri) uri = wrap_uri body = get_wrap_acs_body(resource_uri) headers = { "Content-Type" => "application/x-www-form-urlencoded", "Content-Length" => "0" } response = call(:post, uri, body, headers) parse_token(response.body) end # Generate the wrap ACS body for the given uri as a String # # resource_uri - The resource URI # # Returns a url-encoded String def get_wrap_acs_body(resource_uri) non_ssl_uri = resource_uri.dup non_ssl_uri.scheme = 'http' params = { :wrap_scope => non_ssl_uri.to_s, :wrap_name => @issuer, :wrap_password => @access_key } ::URI.encode_www_form(params) end # Generate the URI for the ACS Wrap service # # path - String. Path for the uri (optional, Default="WRAPv0.9") # query - Hash. Query parameters for the uri (optional) # # Returns a URI. def wrap_uri(path="WRAPv0.9", query={}) query = query || {} uri = URI.parse(File.join(host, path)) uri.query = URI.encode_www_form(query) unless query.empty? uri end def parse_token(body) begin decoded = URI.decode_www_form(body.strip) token = decoded.assoc("wrap_access_token").last expires_in = decoded.assoc("wrap_access_token_expires_in").last.to_i return { :token => token, :expiration => Time.now.to_i + expires_in / 2 } rescue => e raise "Cannot get the access token from returned string: %s" % body end end end end end endazure-0.7.9/lib/azure/service_bus/auth/shared_access_signer.rb0000644000004100000410000000305313112322225024530 0ustar www-datawww-datarequire 'cgi' module Azure module ServiceBus module Auth class SharedAccessSigner < Azure::Core::Auth::Signer # The number of seconds from the time of signature that the SAS token will expire attr_accessor :expiry_offset, :key_name # Public: Initialize the Signer. # # @param key_name [String] The service bus SAS key name. Defaults to the one in the global configuration. # @param key [String] The service bus SAS key encoded in Base64. Defaults to the one in the global configuration. # @param expiry_offset [Integer] The number of seconds from the time of signature that the SAS token will expire. Defaults to 30 minutes. def initialize(key_name=Azure.sb_sas_key_name, key=Azure.sb_sas_key, expiry_offset = 60*5) @access_key = key @key_name, @expiry_offset = key_name, expiry_offset end def name 'SharedAccessSignature' end def token(uri) url_encoded_resource = CGI.escape(uri.to_s.downcase).gsub('+', '%20').downcase expiry = Time.now.to_i + expiry_offset sig = CGI.escape(signature(url_encoded_resource, expiry)).gsub('+', '%20') "#{name} sig=#{sig}&se=#{expiry}&skn=#{key_name}&sr=#{url_encoded_resource}" end def sign_request(req) req.headers['Authorization'] = token(req.uri) end private def signature(url_encoded_resource, expiry) sign([url_encoded_resource, expiry].join("\n")) end end end end end azure-0.7.9/lib/azure/service_bus/brokered_message.rb0000644000004100000410000000703313112322225022734 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- module Azure module ServiceBus class BrokeredMessage # Please read http://msdn.microsoft.com/en-us/library/azure/hh780742 # for more information on brokered message properties # Public: Get/Set the ContentType of the message # # Returns a String attr_accessor :content_type # Public: Get/Set the CorrelationID of the message # # Returns a String attr_accessor :correlation_id # Public: Get/Set the SessionID of the message # # Returns a String attr_accessor :session_id # Public: Get/Set the DeliveryCount of the message # # Returns an Integer attr_accessor :delivery_count # Public: Get/Set the LockedUntilUtc for the message # # Returns a DateTime attr_accessor :locked_until_utc # Public: Get/Set the LockToken of the message # # Returns a String (GUID) attr_accessor :lock_token # Public: Get/Set the MessageID of the message # # Returns a String attr_accessor :message_id alias_method :id, :message_id # Public: Get/Set the Label for the message # # Returns a String attr_accessor :label # Public: Get/Set the ReplyTo for the message # # Returns a String attr_accessor :reply_to # Public: Get/Set the EnqueuedTimeUtc for the message # # Returns a DateTime attr_accessor :enqueued_time_utc # Public: Get/Set the SequenceNumber for the message # # Returns an Integer attr_accessor :sequence_number # Public: Get/Set the TimeToLive for the message # # Returns an Integer attr_accessor :time_to_live # Public: Get/Set the To field for the message # # Returns a String attr_accessor :to # Public: Get/Set the ScheduledEnqueueTimeUtc for the message # # Returns a DateTime attr_accessor :scheduled_enqueue_time_utc # Public: Get/Set the ReplyToSessionId for the message # # Returns a String attr_accessor :reply_to_session_id # Public: Get/Set custom key-value properties of the message # # Returns a Hash attr_accessor :properties # Public: Get/Set the body of the message # # Returns a String attr_accessor :body # Public: Get/Set the URI of the locked message. This URI is needed to unlock or delete the message # # Returns an URI attr_accessor :location # Public: Constructor. # # body - String. The body of the message # properties - Hash. The properties of the message (optional) def initialize(body, properties={}) @body = body @properties = properties yield self if block_given? end end end endazure-0.7.9/lib/azure/service_bus/sql_rule_action.rb0000644000004100000410000000336113112322225022616 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/service_bus/action' module Azure module ServiceBus class SqlRuleAction < Action # Public: Initialize the SQL Rule Action. # # ==== Attributes # # * +hash+ - The resource options Hash # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:sql_expression+ - The SQL expression. # * +:compatibility_level+ - The compatibility level. # def initialize(hash=nil) hash = {} unless hash @sql_expression = hash[:sql_expression] @compatibility_level = (hash[:compatibility_level] || 20).to_i super() end attr_accessor :sql_expression attr_accessor :compatibility_level def to_hash(hash={}) hash[:sql_expression]=sql_expression if sql_expression hash[:compatibility_level]=compatibility_level.to_s if compatibility_level super(hash) end end end end azure-0.7.9/lib/azure/service_bus/filter.rb0000644000004100000410000000155713112322225020725 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/service_bus/rule_aspect' module Azure module ServiceBus class Filter < RuleAspect; end end end azure-0.7.9/lib/azure/service_bus/empty_rule_action.rb0000644000004100000410000000206313112322225023153 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/service_bus/action' module Azure module ServiceBus class EmptyRuleAction < Action # Public: Initialize the empty rule action. # # ==== Attributes # # * +hash+ - The resource options Hash def initialize(hash=nil) super() end end end endazure-0.7.9/lib/azure/service_bus/false_filter.rb0000644000004100000410000000253013112322225022067 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/service_bus/sql_filter' module Azure module ServiceBus class FalseFilter < SqlFilter # Public: Initialize the SQL false Filter. # # ==== Attributes # # * +hash+ - The resource options Hash # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:sql_expression+ - The SQL expression. # def initialize(hash=nil) hash = {} unless hash hash[:sql_expression] = "1 = 0" unless hash[:sql_expression] super(hash) end end end end azure-0.7.9/lib/azure/service_bus/resource.rb0000644000004100000410000000524213112322225021262 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/service_bus/resource' require 'azure/service_bus/interval' module Azure module ServiceBus class Resource # Public: Initialize the resource (either queue, topic, rule or subscription). # # name - A String with the name of the resource. # description - The resource description Hash def initialize(name, description = {}) @name = name @description = description yield self if block_given? end # Get the Resource's ID. # # Returns a URI. attr_accessor :id alias_method :url, :id # Get the Resource's name. # # Returns a String. attr_accessor :name alias_method :title, :name # Public: Get the published time # # Returns a Time attr_accessor :published # Public: Get the updated time # # Returns a Time attr_accessor :updated # Public: Get the author name # # Returns a String attr_accessor :author_name # Public: Resource description # # Returns a Hash attr_accessor :description def get_props desc = description.dup props = [] ordered_props.each { |prop_name| if desc[prop_name] props.push [prop_name, desc[prop_name]] desc.delete prop_name end } desc.each { |k,v| props.push [k, v] } props end private def to_interval(s) s ? Interval.parse(s) : s end def to_time(s) s ? Time.parse(s) : s end def to_i(s) s ? s.to_i : s end def to_bool(s) s ? (s || "").downcase == 'true' : s end def _set(name, value) if value description[name] = value.to_s else description.delete name end end def ordered_props [] end end end endazure-0.7.9/lib/azure/service_bus/correlation_filter.rb0000644000004100000410000000272713112322225023326 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/service_bus/filter' module Azure module ServiceBus class CorrelationFilter < Filter # Public: Initialize the Correlation Id Filter. # # ==== Attributes # # * +hash+ - The resource options Hash # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:correlation_id+ - The correlation identifier. # def initialize(hash=nil) hash = {} unless hash @correlation_id = hash[:correlation_id] super() end attr_accessor :correlation_id #CorrelationId def to_hash(hash={}) hash[:correlation_id]=correlation_id if correlation_id super(hash) end end end end azure-0.7.9/lib/azure/service_bus/subscription.rb0000644000004100000410000002050413112322225022155 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/service_bus/resource' module Azure module ServiceBus class Subscription < Resource attr_accessor :topic # Public: Initialize the subscription. # # ==== Attributes # # * +name+ - A String with the name of the subscription. # * +options+ - The resource options Hash # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:lock_duration+ - XML datetime. Determines the amount of time in seconds in which a message should be locked for processing by a receiver. # * +:requires_session+ - Boolean. If set to true, the queue will be session-aware and only SessionReceiver will be supported. # * +:default_message_time_to_live+ - XML datetime. Determines how long a message lives in the associated subscriptions. # * +:dead_lettering_on_message_expiration:+ - Boolean. This field controls how the Service Bus handles a message whose TTL has expired. # * +:dead_lettering_on_filter_evaluation_exceptions+ - Boolean. Determines how the Service Bus handles a message that causes an exception during a subscription's filter evaluation. # * +:enable_batched_operations+ - Boolean. Enables or disables service side batching behavior when performing operations for the specific queue. # * +:max_delivery_count+ - Number. A message is automatically deadlettered after this number of deliveries. # * +:message_count+ - Number. Displays the number of messages currently in the queue. # def initialize(name, options = {}) normalized_options = {} normalized_options["LockDuration"] = options[:lock_duration].to_s if options.has_key?(:lock_duration) normalized_options["RequiresSession"] = options[:requires_session].to_s if options.has_key?(:requires_session) normalized_options["DefaultMessageTimeToLive"] = options[:default_message_time_to_live].to_s if options.has_key?(:default_message_time_to_live) normalized_options["DeadLetteringOnMessageExpiration"] = options[:dead_lettering_on_message_expiration].to_s if options.has_key?(:dead_lettering_on_message_expiration) normalized_options["DeadLetteringOnFilterEvaluationExceptions"] = options[:dead_lettering_on_filter_evaluation_exceptions].to_s if options.has_key?(:dead_lettering_on_filter_evaluation_exceptions) normalized_options["EnableBatchedOperations"] = options[:enable_batched_operations].to_s if options.has_key?(:enable_batched_operations) normalized_options["MaxDeliveryCount"] = options[:max_delivery_count].to_s if options.has_key?(:max_delivery_count) normalized_options["MessageCount"] = options[:message_count].to_s if options.has_key?(:message_count) super(name, normalized_options) end # LockDuration: XML datetime # # The default lock duration is applied to subscriptions that do not define a lock duration. Settable only at # subscription creation time: # # Range: 0 - 5 minutes. 0 means that the message is not locked # Default: 30 seconds def lock_duration to_interval description['LockDuration'] end def lock_duration=(val) _set 'LockDuration', val end # RequiresSession: True, False # # Settable only at subscription creation time. If set to true, the subscription will be session-aware and only # SessionReceiver will be supported. Session-aware subscription are not supported through REST. # # Default: false def requires_session to_bool description['RequiresSession'] end def requires_session=(val) _set 'RequiresSession', val end # DefaultMessageTimeToLive: PTnHnMnS # # Determines how long a message lives in the subscription. Based on whether dead-lettering is enabled, a message # whose TTL has expired will either be moved to the subscription's associated DeadLtterQueue or permanently # deleted. # # Range: 1 second - TimeSpan.MaxValue # Default: TimeSpan.MaxValue if the topic also does not specify a TTL. Otherwise the setting from topic is inherited. def default_message_time_to_live to_interval description['DefaultMessageTimeToLive'] end def default_message_time_to_live=(val) _set 'DefaultMessageTimeToLive', val end # DeadLetteringOnMessageExpiration: True, False # # This field controls how the Service Bus handles a message whose TTL has expired. If it is enabled and a message # expires, the Service Bus moves the message from the queue into the subscription's dead-letter sub-queue. If # disabled, message will be permanently deleted from the subscription's main queue. Settable only at subscription # creation time. # # Default: false def dead_lettering_on_message_expiration to_bool description['DeadLetteringOnMessageExpiration'] end def dead_lettering_on_message_expiration=(val) _set 'DeadLetteringOnMessageExpiration', val end # DeadLetteringOnFilterEvaluationExceptions: True, False # # Determines how the Service Bus handles a message that causes an exception during a subscription's filter # evaluation. If the value is set to true, the message that caused the exception will be moved to the # subscription's dead-letter queue. Otherwise, it will be discarded. By default this parameter is set to true, # allowing the user a chance to investigate the cause of the exception. It can occur from a malformed message or # some incorrect assumptions being made in the filter about the form of the message. Settable only at topic # creation time. # # Default: true def dead_lettering_on_filter_evaluation_exceptions to_bool description['DeadLetteringOnFilterEvaluationExceptions'] end def dead_lettering_on_filter_evaluation_exceptions=(val) _set 'DeadLetteringOnFilterEvaluationExceptions', val end # EnableBatchedOperations # # Enables or disables service side batching behavior when performing operations for the specific queue. When # enabled, service bus will collect/batch multiple operations to the backend to be more connection efficient. If # user wants lower operation latency then they can disable this feature. def enable_batched_operations to_bool description['EnableBatchedOperations'] end def enable_batched_operations=(val) _set 'EnableBatchedOperations', val end # MaxDeliveryCount # # The maximum number of times a message SB will try to deliver before being dead lettered or discarded. def max_delivery_count to_i description['MaxDeliveryCount'] end def max_delivery_count=(val) _set 'MaxDeliveryCount', val end # MessageCount # # Reports the number of messages in the queue as reported by the monitoring system. def message_count to_i description['MessageCount'] end def message_count=(val) _set 'MessageCount', val end def ordered_props [ "LockDuration", "RequiresSession", "DefaultMessageTimeToLive", "DeadLetteringOnMessageExpiration", "DeadLetteringOnFilterEvaluationExceptions", "MessageCount", "MaxDeliveryCount", "EnableBatchedOperations" ] end end end endazure-0.7.9/lib/azure/service_bus/interval.rb0000644000004100000410000000657113112322225021265 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require "delegate" module Azure module ServiceBus # Public: Helper class to decorate a numeric duration so it can be output # as an ISO8601-compliant Duration. (This class only implements the subset # of the ISO8601 standard that the Azure REST API uses.) # # Examples # # # Initialize an Interval from a Number, or use .try_convert to be # # intelligent: # # Interval.new(10) #=> PT10S # Interval.try_convert(10) #=> PT10S # Interval.try_convert("PT10S") #=> PT10S # Interval.try_convert(nil) #=> nil class Interval < SimpleDelegator # Public: Attempt to convert an object into an Interval. # # object - An object that might be converted into an Interval. # # Returns an Interval or nil. def self.try_convert(object) if object.respond_to?(:to_interval) object.to_interval elsif object.respond_to?(:to_int) new(object) elsif object.respond_to?(:to_str) parse(object) else nil end end # Public: Parse a String into an Interval. # # string - A String in the Interval format. # # Returns an Interval. def self.parse(string) re = /P([\d\.\,]+Y)?([\d\.\,]+M)?([\d\.\,]+D)?(?:T([\d\.\,]+H)?([\d\.\,]+M)?([\d\.\,]+S)?)?/ match = re.match(string) return nil if match.nil? #years = match[1].to_f #months = match[2].to_f days = match[3].to_f hours = match[4].to_f minutes = match[5].to_f seconds = match[6].to_f new(seconds + minutes * 60 + hours * 3600 + days * 86400) end # Public: Return this amount of seconds formatted as an interval. # # Returns a String. def to_s days = to_i / 86400 hours = (to_i % 86400) / 3600 minutes = (to_i % 3600) / 60 seconds = (self % 60) days = "%s" % { :d => days.zero? ? nil : "#{days}D" } time = "%s%s%s" % { :h => hours.zero? ? nil : "#{hours}H", :m => minutes.zero? ? nil : "#{minutes}M", :s => nonzero? && seconds.zero? ? nil : "#{seconds}S" } "P#{days}" + (time.empty? ? "" : "T#{time}") end alias_method :inspect, :to_s # Public: Convert this object into an interval. # # Returns self. def to_interval self end end end endazure-0.7.9/lib/azure/service_bus/brokered_message_serializer.rb0000644000004100000410000001246113112322225025166 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'rubygems' require 'json' require 'time' require 'uri' require 'azure/service_bus/brokered_message' module Azure module ServiceBus # BrokeredMessageSerializer class BrokeredMessageSerializer PROPERTIES = { 'ContentType' => 'content_type', 'CorrelationId' => 'correlation_id', 'SessionID' => 'session_id', 'DeliveryCount' => 'delivery_count', 'LockedUntilUtc' => 'locked_until_utc', 'LockToken' => 'lock_token', 'MessageId' => 'message_id', 'Label' => 'label', 'ReplyTo' => 'reply_to', 'EnqueuedTimeUtc' => 'enqueued_time_utc', 'SequenceNumber' => 'sequence_number', 'TimeToLive' => 'time_to_live', 'To' => 'to', 'ScheduledEnqueueTimeUtc' => 'scheduled_enqueue_time_utc', 'ReplyToSessionId' => 'reply_to_session_id' }.freeze attr_reader :message def initialize(msg) @message = msg end def self.get_from_http_response(response) props = JSON.parse(response.headers['brokerproperties']) BrokeredMessage.new(response.body) do |m| loc_header = response.headers['location'] m.location = URI(loc_header) unless loc_header.nil? m.content_type = response.headers['content-type'] # String based properties m.lock_token = props['LockToken'] m.message_id = props['MessageId'] m.label = props['Label'] m.to = props['To'] m.session_id = props['SessionID'] m.correlation_id = props['CorrelationId'] m.reply_to = props['ReplyTo'] m.reply_to = props['ReplyTo'] m.reply_to_session_id = props['ReplyToSessionId'] # Time based properties utc_lock = props['LockedUntilUtc'] m.locked_until_utc = Time.parse(utc_lock) unless utc_lock.nil? enqueued_time_utc = self.parse_dot_net_serialized_datetime( props['EnqueuedTimeUtc'] ) unless props['EnqueuedTimeUtc'].nil? m.enqueued_time_utc = enqueued_time_utc unless enqueued_time_utc.nil? m.scheduled_enqueue_time_utc = Time.parse( props['ScheduledEnqueueTimeUtc'] ) unless props['ScheduledEnqueueTimeUtc'].nil? # Numeric based properties m.delivery_count = props['DeliveryCount'].to_i m.sequence_number = props['SequenceNumber'].to_i m.time_to_live = props['TimeToLive'].to_f # Custom Properties header_names_black_list = %w( brokerproperties date transfer-encoding location server connection content-type content-length strict-transport-security ) props = response.headers.reject do |k, _| header_names_black_list.include?(k.downcase) end props.each do |prop_name, value| parsed = JSON.parse('{ "' + prop_name + '" : ' + value + '}') m.properties[prop_name] = parsed[prop_name] end end end # Serialize the message's attributes to JSON # # Returns a JSON String def to_json hash = {} PROPERTIES.each do |p, u| attr_name = u.encode('UTF-8') value = @message.send(attr_name) hash[p] = value.to_s.encode('UTF-8') unless value.nil? end hash.to_json end # Build a hash based on message properties and ensure # the values are in a valid format for HTTP headers # # Returns a Hash def get_property_headers hash = {} @message.properties.each do |name, value| value = value.httpdate if !value.nil? && value.class == Time tmp = JSON.generate [value] hash[name] = tmp[1..(tmp.length - 2)] end hash end private # Take the .net json serialization of a DateTime (i.e. /Date(...)/) # and return a time object # # Returns a Time instance def self.parse_dot_net_serialized_datetime(datetime) begin Time.parse(datetime) rescue milliseconds_in_second = 1000 match = /\/Date\((\d+)\)\//.match(datetime) if !match.nil? ticks = match[1].to_i Time.at(ticks / milliseconds_in_second) else nil end end end end end end azure-0.7.9/lib/azure/service_bus/rule_aspect.rb0000644000004100000410000000221313112322225021734 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- module Azure module ServiceBus class RuleAspect def initialize() yield self if block_given? end def to_hash(hash={}) hash[:type]=self.class.name.split('::').last hash end def self.from_hash(hash) hash = {} unless hash type = hash[:type] Azure::ServiceBus.const_get(type).new(hash) if type end end end end azure-0.7.9/lib/azure/service_bus/action.rb0000644000004100000410000000155713112322225020715 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/service_bus/rule_aspect' module Azure module ServiceBus class Action < RuleAspect; end end end azure-0.7.9/lib/azure/service_bus/service_bus_service.rb0000644000004100000410000010657313112322225023475 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/core/signed_service' require 'azure/service_bus/auth/wrap_signer' require 'azure/service_bus/auth/shared_access_signer' require 'azure/service_bus/serialization' require 'azure/service_bus/brokered_message_serializer' require 'azure/core/http/http_response' module Azure module ServiceBus class ServiceBusService < Azure::Core::SignedService DEFAULT_TIMEOUT = 60 def initialize(host=nil, options = {}) client_config = options[:client] || Azure signer = options[:signer] || Auth::WrapSigner.new(client_config.acs_host, client: client_config) super(signer, nil, options) @host = host || @client.config.service_bus_host with_filter do |req, res| req.headers.delete 'x-ms-date' req.headers.delete 'x-ms-version' req.headers.delete 'DataServiceVersion' req.headers.delete 'MaxDataServiceVersion' req.headers['X-Process-At'] = 'servicebus' res.call end end # Creates a new relay endpoint. Once created, this relay endpoint resource manifest is immutable. # # ==== Attributes # # * +relay+ - Azure::ServiceBus::Relay instance to create on server, or a string of the relay endpoint name # * +options+ - Hash. The relay endpoint properties. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:relay_type+ - String. Determines the type of the relay endpoint. # * +:requires_client_authorization+ - Boolean. Determines whether or not clients need to authenticate when making calls. # * +:requires_transport_security+ - Boolean. Determines whether or not the endpoint uses transport security. # def create_relay(relay, options={}) relay = _new_or_existing(Azure::ServiceBus::Relay, relay, options ? options : {}) create_resource_entry(:relay, relay, relay.name) end # Deletes an existing relay endpoint. # # ==== Attributes # # * +relay+ - Azure::ServiceBus::Relay instance to delete or a string of the relay endpoint name def delete_relay(relay) delete_resource_entry(:relay, _name_for(relay)) end # Retrieves the description for the specified relay endpoint. # # ==== Attributes # # * +relay+ - Azure::ServiceBus::Relay instance to retrieve or a string of the relay endpoint name def get_relay(relay) resource_entry(:relay, _name_for(relay)) end # Enumerates the relay endpoints in the service namespace. # # ==== Attributes # # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:skip+ - Integer. Number of queues to skip. # * +:top+ - Integer. Number of queues to list. def list_relays(options={}) query = {} query["$skip"] = options[:skip].to_i.to_s if options[:skip] query["$top"] = options[:top].to_i.to_s if options[:top] resource_list(:relay, query) end # Creates a new queue. Once created, this queue's resource manifest is immutable. # # ==== Attributes # # * +queue+ - Azure::ServiceBus::Queue instance to create on server, or a string of the queue name # * +options+ - Hash. The queue properties. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:default_message_time_to_live+ - XML datetime. Determines how long a message lives in the associated subscriptions. # * +:duplicate_detection_history_time_window+ - XML datetime. Specifies the time span during which the Service Bus will detect message duplication. # * +:enable_batched_operations+ - Boolean. Enables or disables service side batching behavior when performing operations for the specific queue. # * +:dead_lettering_on_message_expiration:+ - Boolean. This field controls how the Service Bus handles a message whose TTL has expired. # * +:lock_duration+ - XML datetime. Determines the amount of time in seconds in which a message should be locked for processing by a receiver. # * +:max_delivery_count+ - Number. A message is automatically deadlettered after this number of deliveries. # * +:max_size_in_megabytes+ - Number. Specifies the maximum topic size in megabytes # * +:message_count+ - Number. Displays the number of messages currently in the queue. # * +:requires_duplicate_detection+ - Boolean. If enabled, the topic will detect duplicate messages within the time span specified by the DuplicateDetectionHistoryTimeWindow property # * +:requires_session+ - Boolean. If set to true, the queue will be session-aware and only SessionReceiver will be supported. # * +:size_in_bytes+ - Number. Reflects the actual bytes toward the topic quota that messages in the topic currently occupy. # def create_queue(queue, options={}) queue = _new_or_existing(Azure::ServiceBus::Queue, queue, options ? options : {}) create_resource_entry(:queue, queue, queue.name) end # Deletes an existing queue. This operation will also remove all associated state # including messages in the queue. # # ==== Attributes # # * +queue+ - Azure::ServiceBus::Queue instance to delete or a string of the queue name def delete_queue(queue) delete_resource_entry(:queue, _name_for(queue)) end # Retrieves an existing queue. # # ==== Attributes # # * +queue+ - Azure::ServiceBus::Queue instance to retrieve or a string of the queue name def get_queue(queue) resource_entry(:queue, _name_for(queue)) end # Enumerates the queues in the service namespace. # # ==== Attributes # # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:skip+ - Integer. Number of queues to skip. # * +:top+ - Integer. Number of queues to list. def list_queues(options={}) query = {} query["$skip"] = options[:skip].to_i.to_s if options[:skip] query["$top"] = options[:top].to_i.to_s if options[:top] resource_list(:queue, query) end # Creates a new topic. Once created, this topic resource manifest is immutable. # # ==== Attributes # # * +topic+ - Azure::ServiceBus::Topic instance to create on server, or a string of the topic name # * +options+ - Hash. The topic properties. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:default_message_time_to_tive+ - XML datetime. Determines how long a message lives in the associated subscriptions. # * +:maximum_number_of_subscriptions+ - Number. Specifies the maximum number of subscriptions that can be associated with the topic. # * +:max_size_in_megabytes+ - Number. Specifies the maximum topic size in megabytes # * +:requires_duplicate_detection+ - Boolean. If enabled, the topic will detect duplicate messages within the time span specified by the DuplicateDetectionHistoryTimeWindow property # * +:dead_lettering_on_filter_evaluation_exceptions+ - Boolean. Determines how the Service Bus handles a message that causes an exception during a subscription's filter evaluation. # * +:duplicate_detection_history_time_window+ - XML datetime. Specifies the time span during which the Service Bus will detect message duplication. # * +:enable_batched_operations+ - Boolean. Enables or disables service side batching behavior when performing operations for the specific queue. # def create_topic(topic, options={}) topic = _new_or_existing(Azure::ServiceBus::Topic, topic, options ? options : {}) create_resource_entry(:topic, topic, topic.name) end # Deletes an existing topic. This operation will also remove all associated state # including associated subscriptions. # # ==== Attributes # # * +topic+ - Azure::ServiceBus::Topic instance to delete or a string of the topic name def delete_topic(topic) delete_resource_entry(:topic, _name_for(topic)) end # Retrieves the description for the specified topic. # # ==== Attributes # # * +topic+ - Azure::ServiceBus::Topic instance to retrieve or a string of the topic name def get_topic(topic) resource_entry(:topic, _name_for(topic)) end # Retrieves the topics in the service namespace. # # ==== Attributes # # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:skip+ - Integer. Number of topics to skip. # * +:top+ - Integer. Number of topics to list. def list_topics(options={}) query = {} query["$skip"] = options[:skip].to_i.to_s if options[:skip] query["$top"] = options[:top].to_i.to_s if options[:top] resource_list(:topic, query) end # Creates a new rule. Once created, this rule's resource manifest is immutable. # # ==== Attributes # # Pass either (topic_name, subscription_name, rule_name) as strings, or (rule) a rule object. # When using (topic_name, subscription_name, rule_name, options) overload, you may also pass the properties for the rule. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:filter+ - String. The rule filter. # * +:action+ - String. The rule action. # def create_rule(*p) rule = _rule_from(*p) result = create_resource_entry(:rule, rule, rule.topic, rule.subscription, rule.name) result.topic = rule.topic result.subscription = rule.subscription result end # Deletes an existing rule. # # ==== Attributes # # Pass either (topic_name, subscription_name, rule_name) as strings, or (rule) a object with .name, .topic, and # .subscription methods such as Azure::ServiceBus::Rule instance. # # Note: The default rule name is '$Default'. Use this name to delete the default rule for the subscription. def delete_rule(*p) topic_name, subscription_name, rule_name = _rule_args(*p) delete_resource_entry(:rule, topic_name, subscription_name, rule_name) end # Retrieves the description for the specified rule. # # ==== Attributes # # Pass either (topic_name, subscription_name, rule_name) as strings, or (rule) a object with .name, .topic, and # .subscription methods such as Azure::ServiceBus::Rule instance. # # Note: The default rule name is '$Default'. Use this name to retrieve the default rule for the subscription. def get_rule(*p) topic_name, subscription_name, rule_name = _rule_args(*p) result = resource_entry(:rule, topic_name, subscription_name, rule_name) result.topic = topic_name result.subscription = subscription_name result end # Retrieves the rules that exist under the specified subscription. # # ==== Attributes # # Pass either (topic_name, subscription_name) as strings, or (subscription) a object with .name and .topic methods # such as Azure::ServiceBus::Subscription instance. # # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:skip+ - Integer. Number of topics to skip. # * +:top+ - Integer. Number of topics to list. def list_rules(*p) topic_name, subscription_name, options = _subscription_args(*p) query = {} query["$skip"] = options[:skip].to_i.to_s if options[:skip] query["$top"] = options[:top].to_i.to_s if options[:top] results = resource_list(:rule, topic_name, subscription_name, query) results.each { |r| r.topic = topic_name; r.subscription=subscription_name } return results end # Creates a new subscription. Once created, this subscription resource manifest is # immutable. # # ==== Attributes # # Pass either (topic_name, subscription_name) as strings, or (subscription) a object. # When using (topic_name, subscription_name) overload, you may also pass optional properties for the subscription. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:lock_duration+ - XML datetime. Determines the amount of time in seconds in which a message should be locked for processing by a receiver. # * +:requires_session+ - Boolean. If set to true, the queue will be session-aware and only SessionReceiver will be supported. # * +:default_message_time_to_live+ - XML datetime. Determines how long a message lives in the associated subscriptions. # * +:dead_lettering_on_message_expiration:+ - Boolean. This field controls how the Service Bus handles a message whose TTL has expired. # * +:dead_lettering_on_filter_evaluation_exceptions+ - Boolean. Determines how the Service Bus handles a message that causes an exception during a subscription's filter evaluation. # * +:enable_batched_operations+ - Boolean. Enables or disables service side batching behavior when performing operations for the specific queue. # * +:max_delivery_count+ - Number. A message is automatically deadlettered after this number of deliveries. # * +:message_count+ - Number. Displays the number of messages currently in the queue. # def create_subscription(*p) subscription = _subscription_from(*p) result = create_resource_entry(:subscription, subscription, subscription.topic, subscription.name) result.topic = subscription.topic result end # # Deletes an existing subscription. # # ==== Attributes # # Pass either (topic_name, subscription_name) as strings, or (subscription) a object with .name and .topic methods # such as Azure::ServiceBus::Subscription instance. def delete_subscription(*p) topic_name, subscription_name = _subscription_args(*p) delete_resource_entry(:subscription, topic_name, subscription_name) end # Gets an existing subscription. # # ==== Attributes # # Pass either (topic_name, subscription_name) as strings, or (subscription) a object with .name and .topic methods # such as Azure::ServiceBus::Subscription instance. def get_subscription(*p) topic_name, subscription_name = _subscription_args(*p) result = resource_entry(:subscription, topic_name, subscription_name) result.topic = topic_name result end # Retrieves the subscriptions in the specified topic. # # ==== Attributes # # * +topic+ - Either a Azure::ServiceBus::Topic instance or a string of the topic name # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:skip+ - Integer. Number of subscriptions to skip. # * +:top+ - Integer. Number of subscriptions to list. def list_subscriptions(topic, options={}) topic = _name_for(topic) query = {} query["$skip"] = options[:skip].to_i.to_s if options[:skip] query["$top"] = options[:top].to_i.to_s if options[:top] results = resource_list(:subscription, topic, query) results.each { |s| s.topic = topic } return results end # Enqueues a message into the specified topic. The limit to the number of messages # which may be present in the topic is governed by the message size in MaxTopicSizeInBytes. # If this message causes the topic to exceed its quota, a quota exceeded error is # returned and the message will be rejected. # # ==== Attributes # # * +topic+ - Either a Azure::ServiceBus::Topic instance or a string of the topic name # * +message+ - An Azure::ServiceBus::BrokeredMessage object containing message body and properties, # or a string of the message body (a default BrokeredMessage will be created from the string). def send_topic_message(topic, message) _send_message(_name_for(topic), message) end # This operation is used to atomically retrieve and lock a message for processing. # The message is guaranteed not to be delivered to other receivers during the lock # duration period specified in buffer description. Once the lock expires, the # message will be available to other receivers (on the same subscription only) # during the lock duration period specified in the topic description. Once the lock # expires, the message will be available to other receivers. In order to complete # processing of the message, the receiver should issue a delete command with the # lock ID received from this operation. To abandon processing of the message and # unlock it for other receivers, an Unlock Message command should be issued, or # the lock duration period can expire. # # ==== Attributes # # * +topic+ - String. The name of the topic or a Topic instance # * +subscription+ - String. The name of the subscription or a Subscription instance # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:timeout+ - Integer. Timeout for the REST call. def peek_lock_subscription_message(topic, subscription, options={}) topic = _name_for(topic) subscription = _name_for(subscription) _peek_lock_message(subscriptions_path(topic, subscription), options[:timeout] ? options[:timeout] : DEFAULT_TIMEOUT) end # # Unlock a message for processing by other receivers on a given subscription. # This operation deletes the lock object, causing the message to be unlocked. # A message must have first been locked by a receiver before this operation # is called. # # ==== Attributes # # * +message+ - String. Either the message location URL or a message object. # def unlock_subscription_message(message) _unlock_message(message) end # Read and delete a message from a subscription as an atomic operation. This # operation should be used when a best-effort guarantee is sufficient for an # application; that is, using this operation it is possible for messages to # be lost if processing fails. # # ==== Attributes # # * +topic+ - The name of the topic or a Topic instance # * +subscription+ - The name of the subscription or a Subscription instance # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:timeout+ - Integer. Timeout for the REST call. # def read_delete_subscription_message(topic, subscription, options={}) topic = _name_for(topic) subscription = _name_for(subscription) _read_delete_message(subscriptions_path(topic, subscription), options[:timeout] ? options[:timeout] : DEFAULT_TIMEOUT) end # Completes processing on a locked message and delete it from the subscription. # This operation should only be called after processing a previously locked # message is successful to maintain At-Least-Once delivery assurances. # # ==== Attributes # # * +message+ - String. Either the message location URL or a message object. # def delete_subscription_message(message) _delete_message(message) end # Sends a message into the specified queue. The limit to the number of messages # which may be present in the topic is governed by the message size the # MaxTopicSizeInMegaBytes. If this message will cause the queue to exceed its # quota, a quota exceeded error is returned and the message will be rejected. # # ==== Attributes # # * +queue+ - Either a Azure::ServiceBus::Queue instance or a string of the queue name # * +message+ - An Azure::ServiceBus::BrokeredMessage object containing message body and properties, # or a string of the message body (a default BrokeredMessage will be created from the string). def send_queue_message(queue, message) _send_message(_name_for(queue), message) end # # Automatically retrieves and locks a message from a queue for processing. The # message is guaranteed not to be delivered to other receivers (on the same # subscription only) during the lock duration period specified in the queue # description. Once the lock expires, the message will be available to other # receivers. In order to complete processing of the message, the receiver # should issue a delete command with the lock ID received from this operation. # To abandon processing of the message and unlock it for other receivers, # an Unlock Message command should be issued, or the lock duration period # can expire. # # ==== Attributes # # * +queue+ - String. Either a Azure::ServiceBus::Queue instance or a string of the queue name # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:timeout+ - Integer. Timeout for the REST call. # def peek_lock_queue_message(queue, options={}) _peek_lock_message(_name_for(queue), options[:timeout] ? options[:timeout] : DEFAULT_TIMEOUT) end # Unlocks a message for processing by other receivers on a given subscription. # This operation deletes the lock object, causing the message to be unlocked. # A message must have first been locked by a receiver before this operation is # called. # # ==== Attributes # # * +message+ - String. Either the message location URL or a message object. # def unlock_queue_message(message) _unlock_message(message) end # Reads and deletes a message from a queue as an atomic operation. This operation # should be used when a best-effort guarantee is sufficient for an application; # that is, using this operation it is possible for messages to be lost if # processing fails. # # ==== Attributes # # * +queue+ - Either a Azure::ServiceBus::Queue instance or a string of the queue name # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:timeout+ - Integer. Timeout for the REST call. # def read_delete_queue_message(queue, options={}) _read_delete_message(_name_for(queue), options[:timeout] ? options[:timeout] : DEFAULT_TIMEOUT) end # Completes processing on a locked message and delete it from the queue. This # operation should only be called after processing a previously locked message # is successful to maintain At-Least-Once delivery assurances. # # ==== Attributes # # * +message+ - String. Either the message location URL or a message object. # def delete_queue_message(message) _delete_message(message) end # Public: Receives a queue message. # # ==== Attributes # # * +queue+ - String. The queue name. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:peek_lock+ - Boolean. Lock when peeking. # * +:timeout+ - Integer. Timeout for the REST call. # def receive_queue_message(queue, options={}) peek_lock = options.fetch(:peek_lock, true) options[:timeout] = options[:timeout] ? options[:timeout] : DEFAULT_TIMEOUT if peek_lock peek_lock_queue_message(queue, options) else read_delete_queue_message(queue, options) end end # Public: Receives a subscription message. # # ==== Attributes # # * +topic+ - String. The topic name. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:peek_lock+ - Boolean. Lock when peeking. # * +:timeout+ - Integer. Timeout for the REST call. # def receive_subscription_message(topic, subscription, options={}) peek_lock = options.fetch(:peek_lock, true) options[:timeout] = options[:timeout] ? options[:timeout] : DEFAULT_TIMEOUT if peek_lock peek_lock_subscription_message(topic, subscription, options) else read_delete_subscription_message(topic, subscription, options) end end private def _unlock_message(message) _modify_message(:put, message) end def _delete_message(message) _modify_message(:delete, message) end def _modify_message(method, message) uri = if (message.respond_to? :location) message.location else message end call(method, uri) nil end def _send_message(path, message) message = Azure::ServiceBus::BrokeredMessage.new(message.to_s) unless message.kind_of?(Azure::ServiceBus::BrokeredMessage) serializer = BrokeredMessageSerializer.new(message) broker_properties = serializer.to_json message_properties = serializer.get_property_headers content_type = message.content_type || 'text/plain' headers = { 'BrokerProperties' => broker_properties } message_properties.each do |k, v| headers[k.to_s.encode("UTF-8")] = v.encode("UTF-8") end headers["Content-Type"] = content_type call(:post, messages_uri(path), message.body, headers) nil end def _read_delete_message(path, timeout=DEFAULT_TIMEOUT) _retrieve_message(:delete, path, timeout) end def _peek_lock_message(path, timeout=DEFAULT_TIMEOUT) _retrieve_message(:post, path, timeout) end def _retrieve_message(method, path, timeout=DEFAULT_TIMEOUT) uri = messages_head_uri(path, {"timeout" => timeout.to_s}) response = call(method, uri) (response.status_code == 204) ? nil : BrokeredMessageSerializer.get_from_http_response(response) end def _rule_from(*p) rule = nil if p.length == 3 or p.length == 4 rule = Azure::ServiceBus::Rule.new(p[2]) do |r| r.topic = p[0] r.subscription = p[1] r.description = p[3] if p.length == 4 end elsif p.length == 1 and p[0].respond_to? :name and p[0].respond_to? :topic and p[0].respond_to? :subscription and p[0].respond_to? :description rule = p[0] else raise ArgumentError, "Must provide either (topic_name, subscription_name) as strings, or (subscription) a object with .name and .topic methods such as Azure::ServiceBus::Subscription instance." end rule end def _rule_args(*p) if p.length == 3 topic_name = p[0] subscription_name = p[1] rule_name = p[2] elsif p.length == 1 and p[0].respond_to? :name and p[0].respond_to? :topic topic_name = p[0].topic subscription_name = p[0].subscription rule_name = p[0].name else raise ArgumentError, "Must provide either (topic_name, subscription_name, rule_name) as strings, or (rule) a object with .name, .topic, and .subscription methods such as Azure::ServiceBus::Rule instance." end return topic_name, subscription_name, rule_name end def _subscription_from(*p) subscription = nil if p.length == 3 subscription = Azure::ServiceBus::Subscription.new(p[1], p[2]) do |sub| sub.topic = p[0] end elsif p.length == 2 subscription = Azure::ServiceBus::Subscription.new(p[1]) do |sub| sub.topic = p[0] end elsif p.length == 1 and p[0].respond_to? :name and p[0].respond_to? :topic and p[0].respond_to? :description subscription = p[0] else raise ArgumentError, "Must provide either (topic_name, subscription_name) as strings, or (subscription) a object with .name and .topic methods such as Azure::ServiceBus::Subscription instance." end subscription end def _subscription_args(*p) raise ArgumentError, "Not enough args" if p.length < 1 topic_name = nil subscription_name = nil options = {} if p.length == 3 # topic/sub/options topic_name = _name_for(p[0]) subscription_name = _name_for(p[1]) options =p[2] elsif p.length == 2 # either subscription/options or topic/sub if p[0].respond_to? :name and p[0].respond_to? :topic topic_name = p[0].topic subscription_name = p[0].name options =p[1] else topic_name = _name_for(p[0]) subscription_name = _name_for(p[1]) end elsif p.length == 1 and p[0].respond_to? :name and p[0].respond_to? :topic topic_name = p[0].topic subscription_name = p[0].name else raise ArgumentError, "Must provide either (topic_name, subscription_name) as strings, or (subscription) a object with .name and .topic methods such as Azure::ServiceBus::Subscription instance." end return topic_name, subscription_name, options end def _name_for(val) val.respond_to?(:name) ? val.name : val end def _new_or_existing(type, *p) p[0].kind_of?(type) ? p[0] : type.new(*p) end def create_resource_entry(resource, entry, *p) body = Serialization.resource_to_xml resource, entry response = call(:put, self.send("#{resource.to_s}_uri", *p), body) results = Serialization.resources_from_xml(resource, response.body) results ? results.first : results end def delete_resource_entry(resource, *p) call(:delete, self.send("#{resource.to_s}_uri", *p)) nil end def resource_entry(resource, *p) uri = self.send("#{resource.to_s}_uri", *p) response = call(:get, uri) results = Serialization.resources_from_xml(resource, response.body) result = results ? results.first : results raise Azure::Core::Http::HTTPError.new(Azure::Core::Http::HttpResponse.new(Azure::Core::Http::HttpResponse::MockResponse.new(404, 'ResourceNotFoundThe specified resource does not exist.', {}), uri)) unless result result end def resource_list(resource, *p) response = call(:get, self.send("#{resource.to_s}_list_uri", *p)) Serialization.resources_from_xml_with_next_link(resource, response.body) end # paths protected def message_path(path, sequence_number, lock_token) "#{messages_path(path)}/#{sequence_number}/#{lock_token}" end protected def messages_head_path(path) "#{messages_path(path)}/head" end protected def messages_path(path) "#{path}/messages" end protected def rule_path(topic, subscription, rule) "#{subscriptions_path(topic, subscription)}/rules/#{rule}" end protected def subscriptions_path(topic, subscription) "#{topic}/subscriptions/#{subscription}" end # messages uris protected def message_uri(path, sequence_number, lock_token, query={}) generate_uri(message_path(path, sequence_number, lock_token), query) end protected def messages_head_uri(path, query={}) generate_uri(messages_head_path(path), query) end protected def messages_uri(path, query={}) generate_uri(messages_path(path), query) end # entry uris protected def rule_uri(topic, subscription, rule, query={}) generate_uri(rule_path(topic, subscription, rule), query) end protected def subscription_uri(topic, subscription, query={}) generate_uri(subscriptions_path(topic, subscription), query) end protected def relay_uri(relay, query={}) query["api-version"] = "2013-10" generate_uri(relay, query) end protected def queue_uri(topic, query={}) generate_uri(topic, query) end protected def topic_uri(topic, query={}) generate_uri(topic, query) end # list uris protected def rule_list_uri(topic, subscription, query={}) resource_list_uri(:rule, query, subscriptions_path(topic, subscription)) end protected def subscription_list_uri(topic, query={}) resource_list_uri(:subscription, query, topic) end protected def relay_list_uri(query={}) resource_list_uri(:relay, query) end protected def queue_list_uri(query={}) resource_list_uri(:queue, query) end protected def topic_list_uri(query={}) resource_list_uri(:topic, query) end protected def resource_list_uri(resource, query={}, subpath='$Resources') skip = query.delete ["$skip"] top = query.delete ["$top"] uri = generate_uri("#{subpath}/#{resource.to_s.capitalize}s", query) uri.query = [uri.query, "$skip=" + skip].join('&') if skip uri.query = [uri.query, "$top=" + top].join('&') if top uri end end end end Azure::ServiceBusService = Azure::ServiceBus::ServiceBusService azure-0.7.9/lib/azure/service_bus/sql_filter.rb0000644000004100000410000000344413112322225021601 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/service_bus/filter' module Azure module ServiceBus class SqlFilter < Filter # Public: Initialize the SQL Rule Filter. # # ==== Attributes # # * +hash+ - The resource options Hash # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:sql_expression+ - The SQL expression. # * +:compatibility_level+ - The compatibility level. # def initialize(hash=nil) hash = {} unless hash @sql_expression = hash[:sql_expression] if hash[:sql_expression] @compatibility_level = (hash[:compatibility_level] || 20).to_i if hash[:compatibility_level] super() end attr_accessor :sql_expression attr_accessor :compatibility_level def to_hash(hash={}) hash[:sql_expression]=sql_expression if sql_expression hash[:compatibility_level]=compatibility_level.to_s if compatibility_level super(hash) end end end end azure-0.7.9/lib/azure/service_bus/relay.rb0000644000004100000410000000620013112322225020542 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/service_bus/resource' module Azure module ServiceBus class Relay < Resource # Public: Initialize the relay endpoint. # # ==== Attributes # # * +name+ - A String with the name of the relay endpoint. # * +options+ - The resource options Hash # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:relay_type+ - String. Determines the type of the relay endpoint. This is required. # * +:requires_client_authorization+ - Boolean. Determines whether or not clients need to authenticate when making calls. # * +:requires_transport_security+ - Boolean. Determines whether or not the endpoint uses transport security. # def initialize(name, options = {}) normalized_options = {} normalized_options["RelayType"] = options[:relay_type].to_s normalized_options["RequiresClientAuthorization"] = options[:requires_client_authorization].to_s if options.has_key?(:requires_client_authorization) normalized_options["RequiresTransportSecurity"] = options[:requires_transport_security].to_s if options.has_key?(:requires_transport_security) super(name, normalized_options) end # RelayType: String # # Displays the relay type of the endpoint. def relay_type description['RelayType'] end def relay_type=(val) _set 'RelayType', val end # RequiresClientAuthorization: Boolean # # Determines whether or not clients need to authenticate when making calls. # # Default: true def requires_client_authorization to_bool description['RequiresClientAuthorization'] end def requires_client_authorization=(val) _set 'RequiresClientAuthorization', val end # RequiresTransportSecurity: Boolean # # Determines whether or not the endpoint uses transport security. # # Default: true def requires_transport_security to_bool description['RequiresTransportSecurity'] end def requires_transport_security=(val) _set 'RequiresTransportSecurity', val end def ordered_props [ 'RelayType', 'RequiresClientAuthorization', 'RequiresTransportSecurity' ] end end end endazure-0.7.9/lib/azure/service_bus/queue.rb0000644000004100000410000002406013112322225020556 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/service_bus/resource' module Azure module ServiceBus class Queue < Resource # Public: Initialize the queue. # # ==== Attributes # # * +name+ - A String with the name of the queue. # * +options+ - The resource options Hash # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:default_message_time_to_live+ - XML datetime. Determines how long a message lives in the associated subscriptions. # * +:duplicate_detection_history_time_window+ - XML datetime. Specifies the time span during which the Service Bus will detect message duplication. # * +:enable_batched_operations+ - Boolean. Enables or disables service side batching behavior when performing operations for the specific queue. # * +:dead_lettering_on_message_expiration:+ - Boolean. This field controls how the Service Bus handles a message whose TTL has expired. # * +:lock_duration+ - XML datetime. Determines the amount of time in seconds in which a message should be locked for processing by a receiver. # * +:max_delivery_count+ - Number. A message is automatically deadlettered after this number of deliveries. # * +:max_size_in_megabytes+ - Number. Specifies the maximum topic size in megabytes # * +:message_count+ - Number. Displays the number of messages currently in the queue. # * +:requires_duplicate_detection+ - Boolean. If enabled, the topic will detect duplicate messages within the time span specified by the DuplicateDetectionHistoryTimeWindow property # * +:requires_session+ - Boolean. If set to true, the queue will be session-aware and only SessionReceiver will be supported. # * +:size_in_bytes+ - Number. Reflects the actual bytes toward the topic quota that messages in the topic currently occupy. # def initialize(name, options = {}) normalized_options = {} normalized_options["DefaultMessageTimeToLive"] = options[:default_message_time_to_live].to_s if options.has_key?(:default_message_time_to_live) normalized_options["DuplicateDetectionHistoryTimeWindow"] = options[:duplicate_detection_history_time_window].to_s if options.has_key?(:duplicate_detection_history_time_window) normalized_options["EnableBatchedOperations"] = options[:enable_batched_operations].to_s if options.has_key?(:enable_batched_operations) normalized_options["DeadLetteringOnMessageExpiration"] = options[:dead_lettering_on_message_expiration].to_s if options.has_key?(:dead_lettering_on_message_expiration) normalized_options["LockDuration"] = options[:lock_duration].to_s if options.has_key?(:lock_duration) normalized_options["MaxDeliveryCount"] = options[:max_delivery_count].to_s if options.has_key?(:max_delivery_count) normalized_options["MaxSizeInMegabytes"] = options[:max_size_in_megabytes].to_s if options.has_key?(:max_size_in_megabytes) normalized_options["MessageCount"] = options[:message_count].to_s if options.has_key?(:message_count) normalized_options["RequiresDuplicateDetection"] = options[:requires_duplicate_detection].to_s if options.has_key?(:requires_duplicate_detection) normalized_options["RequiresSession"] = options[:requires_session].to_s if options.has_key?(:requires_session) normalized_options["SizeInBytes"] = options[:size_in_bytes].to_s if options.has_key?(:size_in_bytes) super(name, normalized_options) end # MessageCount: Number # # Displays the number of messages currently in the queue. def message_count to_i description['MessageCount'] end def message_count=(val) _set 'MessageCount', val end # LockDuration: XML datetime # # Determines the amount of time in seconds in which a message should be locked for processing by a receiver. # After this period, the message is unlocked and available for consumption by the next receiver. Settable # only at queue creation time: # # Range: 0 - 5 minutes. 0 means that the message is not locked # Default: 30 seconds def lock_duration to_interval description['LockDuration'] end def lock_duration=(val) _set 'LockDuration', val end # RequiresSession: True, False # # Settable only at queue creation time. If set to true, the queue will be session-aware and only SessionReceiver # will be supported. Session-aware queues are not supported through REST. # # Default for durable queue: false def requires_session to_bool description['RequiresSession'] end def requires_session=(val) _set 'RequiresSession', val end # DeadLetteringOnMessageExpiration: True, False # # This field controls how the Service Bus handles a message whose TTL has expired. If it is enabled and a message # expires, the Service Bus moves the message from the queue into the queue's dead-letter sub-queue. If disabled, # message will be permanently deleted from the queue. Settable only at queue creation time. # # Default: false def dead_lettering_on_message_expiration to_bool description['DeadLetteringOnMessageExpiration'] end def enable_dead_lettering_on_message_expiration=(val) _set 'DeadLetteringOnMessageExpiration', val end # MaxDeliveryCount: Number # # A message is automatically deadlettered after this number of deliveries. def max_delivery_count to_i description['MaxDeliveryCount'] end def max_delivery_count=(val) _set 'MaxDeliveryCount', val end # MaxSizeInMegaBytes: Number # # Specifies the maximum queue size in megabytes. Any attempt to enqueue a message that will cause the queue to # exceed this value will fail. You can only set this parameter at queue creation time using the following values: # # Range: 1 - 1024 (valid values are 1024, 2048, 3072, 4096, 5120) # Default: 1*1024 (valid values are 1024, 2048, 3072, 4096, 5120) def max_size_in_megabytes to_i description['MaxSizeInMegabytes'] end def max_size_in_megabytes=(val) _set 'MaxSizeInMegabytes', val end # SizeinBytes: Number # # Reflects the actual bytes that messages in the queue currently occupy toward the queue's quota. # # Range: 0 - MaxTopicSizeinMegaBytes def size_in_bytes to_i description['SizeInBytes'] end def size_in_bytes=(val) _set 'SizeInBytes', val end # DefaultMessageTimeToLive: XML datetime # # Depending on whether DeadLettering is enabled, a message is automatically moved to the DeadLetterQueue or # deleted if it has been stored in the queue for longer than the specified time. This value is overwritten # by a TTL specified on the message if and only if the message TTL is smaller than the TTL set on the queue. # This value is immutable after the Queue has been created: # # Range: 1 second - TimeSpan.MaxValue # Default: TimeSpan.MaxValue def default_message_time_to_live to_interval description['DefaultMessageTimeToLive'] end def default_message_time_to_live=(val) _set 'DefaultMessageTimeToLive', val end # RequiresDuplicateDetection: True, False # # Settable only at queue creation time. # # Default for durable queue: false def requires_duplicate_detection to_bool description['RequiresDuplicateDetection'] end def requires_duplicate_detection=(val) _set 'RequiresDuplicateDetection', val end # DuplicateDetectionHistoryTimeWindow # # Specifies the time span during which the Service Bus will detect message duplication. # # Range: 1 second - 7 days # Default: 10 minutes def duplicate_detection_history_time_window to_interval description['DuplicateDetectionHistoryTimeWindow'] end def duplicate_detection_history_time_window=(val) _set 'DuplicateDetectionHistoryTimeWindow', val end # EnableBatchedOperations # # Enables or disables service side batching behavior when performing operations for the specific queue. When # enabled, service bus will collect/batch multiple operations to the backend to be more connection efficient. # # If user wants lower operation latency then they can disable this feature. def enable_batched_operations to_bool description['EnableBatchedOperations'] end def enable_batched_operations=(val) _set 'EnableBatchedOperations', val end def ordered_props [ 'LockDuration', 'MaxSizeInMegabytes', 'RequiresDuplicateDetection', 'RequiresSession', 'DefaultMessageTimeToLive', 'DeadLetteringOnMessageExpiration', 'DuplicateDetectionHistoryTimeWindow', 'MaxDeliveryCount', 'EnableBatchedOperations', 'SizeInBytes', 'MessageCount' ] end end end endazure-0.7.9/lib/azure/version.rb0000644000004100000410000000206413112322225016606 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- module Azure class Version MAJOR = 0 unless defined? MAJOR MINOR = 7 unless defined? MINOR UPDATE = 9 unless defined? UPDATE PRE = nil unless defined? PRE class << self # @return [String] def to_s [MAJOR, MINOR, UPDATE, PRE].compact.join('.') end end end end azure-0.7.9/lib/azure/blob/0000755000004100000410000000000013112322225015510 5ustar www-datawww-dataazure-0.7.9/lib/azure/blob/serialization.rb0000644000004100000410000002476513112322225020730 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/service/serialization' require 'azure/service/enumeration_results' require 'azure/blob/container' require 'azure/blob/blob' require 'azure/blob/block' require 'base64' module Azure module Blob module Serialization include Azure::Service::Serialization def self.container_enumeration_results_from_xml(xml) xml = slopify(xml) expect_node("EnumerationResults", xml) results = enumeration_results_from_xml(xml, Azure::Service::EnumerationResults.new) return results unless (xml > "Containers").any? && ((xml > "Containers") > "Container").any? if xml.Containers.Container.count == 0 results.push(container_from_xml(xml.Containers.Container)) else xml.Containers.Container.each { |container_node| results.push(container_from_xml(container_node)) } end results end def self.container_from_xml(xml) xml = slopify(xml) expect_node("Container", xml) Container.new do |container| container.name = xml.Name.text if (xml > "Name").any? container.properties = container_properties_from_xml(xml.Properties) if (xml > "Properties").any? container.metadata = metadata_from_xml(xml.Metadata) if (xml > "Metadata").any? end end def self.container_from_headers(headers) Container.new do |container| container.properties = container_properties_from_headers(headers) container.public_access_level = public_access_level_from_headers(headers) container.metadata = metadata_from_headers(headers) end end def self.container_properties_from_xml(xml) xml = slopify(xml) expect_node("Properties", xml) props = {} props[:last_modified] = (xml > "Last-Modified").text if (xml > "Last-Modified").any? props[:etag] = xml.Etag.text if (xml > "Etag").any? props[:lease_status] = xml.LeaseStatus.text if (xml > "LeaseStatus").any? props[:lease_state] = xml.LeaseState.text if (xml > "LeaseState").any? props[:lease_duration] = xml.LeaseDuration.text if (xml > "LeaseDuration").any? props end def self.container_properties_from_headers(headers) props = {} props[:last_modified] = headers["Last-Modified"] props[:etag] = headers["Etag"] props[:lease_status] = headers["x-ms-lease-status"] props[:lease_state] = headers["x-ms-lease-state"] props[:lease_duration] = headers["x-ms-lease-duration"] props end def self.public_access_level_from_headers(headers) headers["x-ms-blob-public-access"] end def self.blob_enumeration_results_from_xml(xml) xml = slopify(xml) expect_node("EnumerationResults", xml) results = enumeration_results_from_xml(xml, Azure::Service::EnumerationResults.new) return results unless (xml > "Blobs").any? if ((xml > "Blobs") > "Blob").any? if xml.Blobs.Blob.count == 0 results.push(blob_from_xml(xml.Blobs.Blob)) else xml.Blobs.Blob.each { |blob_node| results.push(blob_from_xml(blob_node)) } end end results end def self.blob_from_xml(xml) xml = slopify(xml) expect_node("Blob", xml) Blob.new do |blob| blob.name = xml.Name.text if (xml > "Name").any? blob.snapshot = xml.Snapshot.text if (xml > "Snapshot").any? blob.properties = blob_properties_from_xml(xml.Properties) if (xml > "Properties").any? blob.metadata = metadata_from_xml(xml.Metadata) if (xml > "Metadata").any? end end def self.blob_from_headers(headers) Blob.new do |blob| blob.properties = blob_properties_from_headers(headers) blob.metadata = metadata_from_headers(headers) end end def self.blob_properties_from_xml(xml) xml = slopify(xml) expect_node("Properties", xml) props = {} props[:last_modified] = (xml > "Last-Modified").text if (xml > "Last-Modified").any? props[:etag] = xml.Etag.text if (xml > "Etag").any? props[:lease_status] = xml.LeaseStatus.text if (xml > "LeaseStatus").any? props[:lease_state] = xml.LeaseState.text if (xml > "LeaseState").any? props[:lease_duration] = xml.LeaseDuration.text if (xml > "LeaseDuration").any? props[:content_length] = (xml > "Content-Length").text.to_i if (xml > "Content-Length").any? props[:content_type] = (xml > "Content-Type").text if (xml > "Content-Type").any? props[:content_encoding] = (xml > "Content-Encoding").text if (xml > "Content-Encoding").any? props[:content_language] = (xml > "Content-Language").text if (xml > "Content-Language").any? props[:content_md5] = (xml > "Content-MD5").text if (xml > "Content-MD5").any? props[:cache_control] = (xml > "Cache-Control").text if (xml > "Cache-Control").any? props[:sequence_number] = (xml > "x-ms-blob-sequence-number").text.to_i if (xml > "x-ms-blob-sequence-number").any? props[:blob_type] = xml.BlobType.text if (xml > "BlobType").any? props[:copy_id] = xml.CopyId.text if (xml > "CopyId").any? props[:copy_status] = xml.CopyStatus.text if (xml > "CopyStatus").any? props[:copy_source] = xml.CopySource.text if (xml > "CopySource").any? props[:copy_progress] = xml.CopyProgress.text if (xml > "CopyProgress").any? props[:copy_completion_time] = xml.CopyCompletionTime.text if (xml > "CopyCompletionTime").any? props[:copy_status_description] = xml.CopyStatusDescription.text if (xml > "CopyStatusDescription").any? props end def self.blob_properties_from_headers(headers) props = {} props[:last_modified] = headers["Last-Modified"] props[:etag] = headers["Etag"] props[:lease_status] = headers["x-ms-lease-status"] props[:lease_state] = headers["x-ms-lease-state"] props[:lease_duration] = headers["x-ms-lease-duration"] props[:content_length] = headers["x-ms-blob-content-length"] || headers["Content-Length"] props[:content_length] = props[:content_length].to_i if props[:content_length] props[:content_type] = headers["x-ms-blob-content-type"] || headers["Content-Type"] props[:content_encoding] = headers["x-ms-blob-content-encoding"] || headers["Content-Encoding"] props[:content_language] = headers["x-ms-blob-content-language"] || headers["Content-Language"] props[:content_md5] = headers["x-ms-blob-content-md5"] || headers["Content-MD5"] props[:cache_control] = headers["x-ms-blob-cache-control"] || headers["Cache-Control"] props[:sequence_number] = headers["x-ms-blob-sequence-number"].to_i if headers["x-ms-blob-sequence-number"] props[:blob_type] = headers["x-ms-blob-type"] props[:copy_id] = headers["x-ms-copy-id"] props[:copy_status] = headers["x-ms-copy-status"] props[:copy_source] = headers["x-ms-copy-source"] props[:copy_progress] = headers["x-ms-copy-progress"] props[:copy_completion_time] = headers["x-ms-copy-completion-time"] props[:copy_status_description] = headers["x-ms-copy-status-description"] props[:accept_ranges] = headers["Accept-Ranges"].to_i if headers["Accept-Ranges"] props end def self.block_list_to_xml(block_list) builder = Nokogiri::XML::Builder.new(:encoding => "UTF-8") do |xml| xml.BlockList { block_list.each { |block| encoded_id = Base64.strict_encode64(block[0]) case block[1] when :uncommitted xml.Uncommitted encoded_id when :committed xml.Committed encoded_id else xml.Latest encoded_id end } } end builder.to_xml end def self.block_list_from_xml(xml) xml = slopify(xml) expect_node("BlockList", xml) block_list = { :committed => [], :uncommitted => [] } if ((xml > "CommittedBlocks") > "Block").any? if xml.CommittedBlocks.Block.count == 0 add_block(:committed, xml.CommittedBlocks.Block, block_list) else xml.CommittedBlocks.Block.each { |block_node| add_block(:committed, block_node, block_list) } end end return block_list unless (xml > "UncommittedBlocks") if ((xml > "UncommittedBlocks") > "Block").any? if xml.UncommittedBlocks.Block.count == 0 add_block(:uncommitted, xml.UncommittedBlocks.Block, block_list) else xml.UncommittedBlocks.Block.each { |block_node| add_block(:uncommitted, block_node, block_list) } end end block_list end def self.add_block(type, block_node, block_list) block = Block.new do |b| b.name = Base64.strict_decode64(block_node.Name.text) if (block_node > "Name").any? b.size = block_node.Size.text.to_i if (block_node > "Size").any? b.type = type end block_list[type].push block end def self.page_list_from_xml(xml) xml = slopify(xml) expect_node("PageList", xml) page_list = [] return page_list unless (xml > "PageRange").any? if xml.PageRange.count == 0 page_list.push [xml.PageRange.Start.text.to_i, xml.PageRange.End.text.to_i] else xml.PageRange.each { |page_range| page_list.push [page_range.Start.text.to_i, page_range.End.text.to_i] } end page_list end end end endazure-0.7.9/lib/azure/blob/blob.rb0000644000004100000410000000203613112322225016754 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- module Azure module Blob class Blob def initialize @properties = {} @metadata = {} yield self if block_given? end attr_accessor :name attr_accessor :snapshot attr_accessor :properties attr_accessor :metadata end end endazure-0.7.9/lib/azure/blob/blob_service.rb0000644000004100000410000021103213112322225020472 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/service/storage_service' require 'azure/blob/serialization' require 'azure/blob/auth/shared_access_signature' require 'base64' module Azure module Blob class BlobService < Service::StorageService def initialize(options = {}) client_config = options[:client] || Azure signer = options[:signer] || Azure::Core::Auth::SharedKey.new(client_config.storage_account_name, client_config.storage_access_key) super(signer, client_config.storage_account_name, options) @host = client.storage_blob_host end # Public: Get a list of Containers from the server # # ==== Attributes # # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:prefix+ - String. Filters the results to return only containers # whose name begins with the specified prefix. (optional) # # * +:marker+ - String. An identifier the specifies the portion of the # list to be returned. This value comes from the property # Azure::Service::EnumerationResults.continuation_token when there # are more containers available than were returned. The # marker value may then be used here to request the next set # of list items. (optional) # # * +:max_results+ - Integer. Specifies the maximum number of containers to return. # If max_results is not specified, or is a value greater than # 5,000, the server will return up to 5,000 items. If it is set # to a value less than or equal to zero, the server will return # status code 400 (Bad Request). (optional) # # * +:metadata+ - Boolean. Specifies whether or not to return the container metadata. # (optional, Default=false) # # * +:timeout+ - Integer. A timeout in seconds. # # NOTE: Metadata requested with the :metadata parameter must have been stored in # accordance with the naming restrictions imposed by the 2009-09-19 version of the Blob # service. Beginning with that version, all metadata names must adhere to the naming # conventions for C# identifiers. # # See: http://msdn.microsoft.com/en-us/library/aa664670(VS.71).aspx # # Any metadata with invalid names which were previously stored, will be returned with the # key "x-ms-invalid-name" in the metadata hash. This may contain multiple values and be an # Array (vs a String if it only contains a single value). # # Returns an Azure::Service::EnumerationResults def list_containers(options={}) query = { } if options query['prefix'] = options[:prefix] if options[:prefix] query['marker'] = options[:marker] if options[:marker] query['maxresults'] = options[:max_results].to_s if options[:max_results] query['include'] = 'metadata' if options[:metadata] == true query['timeout'] = options[:timeout].to_s if options[:timeout] end uri = containers_uri(query) response = call(:get, uri) Serialization.container_enumeration_results_from_xml(response.body) end # Public: Create a new container # # ==== Attributes # # * +name+ - String. The name of the container # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:metadata+ - Hash. User defined metadata for the container (optional) # * +:public_access_level+ - String. One of "container" or "blob" (optional) # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/dd179468.aspx # # Returns a Container def create_container(name, options={}) query = { } query['timeout'] = options[:timeout].to_s if options[:timeout] uri = container_uri(name, query) headers = service_properties_headers add_metadata_to_headers(options[:metadata], headers) if options[:metadata] headers['x-ms-blob-public-access'] = options[:public_access_level].to_s if options[:public_access_level] response = call(:put, uri, nil, headers) container = Serialization.container_from_headers(response.headers) container.name = name container.metadata = options[:metadata] container end # Public: Deletes a container # # ==== Attributes # # * +name+ - String. The name of the container # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/dd179408.aspx # # Returns nil on success def delete_container(name, options={}) query = { } query['timeout'] = options[:timeout].to_s if options[:timeout] call(:delete, container_uri(name, query)) nil end # Public: Returns all properties and metadata on the container. # # ==== Attributes # # * +name+ - String. The name of the container # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/dd179370.aspx # # Returns a Container def get_container_properties(name, options={}) query = { } query['timeout'] = options[:timeout].to_s if options[:timeout] response = call(:get, container_uri(name, query)) container = Serialization.container_from_headers(response.headers) container.name = name container end # Public: Returns only user-defined metadata for the specified container. # # ==== Attributes # # * +name+ - String. The name of the container # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/ee691976.aspx # # Returns a Container def get_container_metadata(name, options={}) query = { 'comp' => 'metadata'} query['timeout'] = options[:timeout].to_s if options[:timeout] response = call(:get, container_uri(name, query)) container = Serialization.container_from_headers(response.headers) container.name = name container end # Public: Gets the access control list (ACL) and any container-level access policies # for the container. # # ==== Attributes # # * +name+ - String. The name of the container # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/dd179469.aspx # # Returns a tuple of (container, signed_identifiers) # container - A Azure::Entity::Blob::Container instance # signed_identifiers - A list of Azure::Entity::SignedIdentifier instances # def get_container_acl(name, options={}) query = { 'comp' => 'acl'} query['timeout'] = options[:timeout].to_s if options[:timeout] response = call(:get, container_uri(name, query)) container = Serialization.container_from_headers(response.headers) container.name = name signed_identifiers = nil signed_identifiers = Serialization.signed_identifiers_from_xml(response.body) if response.body != nil && response.body.length > 0 return container, signed_identifiers end # Public: Sets the ACL and any container-level access policies for the container. # # ==== Attributes # # * +name+ - String. The name of the container # * +public_access_level+ - String. The container public access level # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:signed_identifiers+ - Array. A list of Azure::Entity::SignedIdentifier instances (optional) # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/dd179391.aspx # # Returns a tuple of (container, signed_identifiers) # * +container+ - A Azure::Entity::Blob::Container instance # * +signed_identifiers+ - A list of Azure::Entity::SignedIdentifier instances # def set_container_acl(name, public_access_level, options={}) query = { 'comp' => 'acl'} query['timeout'] = options[:timeout].to_s if options[:timeout] uri =container_uri(name, query) headers = service_properties_headers headers['x-ms-blob-public-access'] = public_access_level if public_access_level && public_access_level.to_s.length > 0 signed_identifiers = nil signed_identifiers = options[:signed_identifiers] if options[:signed_identifiers] body = nil body = Serialization.signed_identifiers_to_xml(signed_identifiers) if signed_identifiers response = call(:put, uri, body, headers) container = Serialization.container_from_headers(response.headers) container.name = name container.public_access_level = public_access_level return container, signed_identifiers || [] end # Public: Sets custom metadata for the container. # # ==== Attributes # # * +name+ - String. The name of the container # * +metadata+ - Hash. A Hash of the metadata values # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/dd179362.aspx # # Returns nil on success def set_container_metadata(name, metadata, options={}) query = { 'comp' => 'metadata'} query['timeout'] = options[:timeout].to_s if options[:timeout] headers = service_properties_headers add_metadata_to_headers(metadata, headers) if metadata call(:put, container_uri(name, query), nil, headers) nil end # Public: Get a list of Blobs from the server # # ==== Attributes # # * +name+ - String. The name of the container to list blobs for. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:prefix+ - String. Filters the results to return only blobs # whose name begins with the specified prefix. (optional) # * +:delimiter+ - String. When the request includes this parameter, the operation # returns a BlobPrefix element in the response body that acts as a # placeholder for all blobs whose names begin with the same substring # up to the appearance of the delimiter character. The delimiter may # be a single character or a string. # * +:marker+ - String. An identifier that specifies the portion of the # list to be returned. This value comes from the property # Azure::Service::EnumerationResults.continuation_token when # there are more blobs available than were returned. The # marker value may then be used here to request the next set # of list items. (optional) # * +:max_results+ - Integer. Specifies the maximum number of blobs to return. # If max_results is not specified, or is a value greater than # 5,000, the server will return up to 5,000 items. If it is set # to a value less than or equal to zero, the server will return # status code 400 (Bad Request). (optional) # * +:metadata+ - Boolean. Specifies whether or not to return the blob metadata. # (optional, Default=false) # * +:snapshots+ - Boolean. Specifies that snapshots should be included in the # enumeration. Snapshots are listed from oldest to newest in the # response. (optional, Default=false) # * +:uncomittedblobs+ - Boolean. Specifies that blobs for which blocks have been uploaded, # but which have not been committed using put_block_list, be included # in the response. (optional, Default=false) # * +:copy+ - Boolean. Specifies that metadata related to any current or previous # copy_blob operation should be included in the response. # (optional, Default=false) # * +:timeout+ - Integer. A timeout in seconds. # # NOTE: Metadata requested with the :metadata parameter must have been stored in # accordance with the naming restrictions imposed by the 2009-09-19 version of the Blob # service. Beginning with that version, all metadata names must adhere to the naming # conventions for C# identifiers. # # See: http://msdn.microsoft.com/en-us/library/azure/dd135734.aspx # # Any metadata with invalid names which were previously stored, will be returned with the # key "x-ms-invalid-name" in the metadata hash. This may contain multiple values and be an # Array (vs a String if it only contains a single value). # # Returns an Azure::Service::EnumerationResults def list_blobs(name, options={}) query = { 'comp' => 'list'} query['prefix'] = options[:prefix].gsub(/\\/, '/') if options[:prefix] query['delimiter'] = options[:delimiter] if options[:delimiter] query['marker'] = options[:marker] if options[:marker] query['maxresults'] = options[:max_results].to_s if options[:max_results] query['timeout'] = options[:timeout].to_s if options[:timeout] included_datasets = [] included_datasets.push('metadata') if options[:metadata] == true included_datasets.push('snapshots') if options[:snapshots] == true included_datasets.push('uncommittedblobs') if options[:uncommittedblobs] == true included_datasets.push('copy') if options[:copy] == true query['include'] = included_datasets.join ',' if included_datasets.length > 0 uri = container_uri(name, query) response = call(:get, uri) Serialization.blob_enumeration_results_from_xml(response.body) end # Public: Creates a new page blob. Note that calling create_page_blob to create a page # blob only initializes the blob. To add content to a page blob, call create_blob_pages method. # # ==== Attributes # # * +container+ - String. The container name. # * +blob+ - String. The blob name. # * +length+ - Integer. Specifies the maximum size for the page blob, up to 1 TB. The page blob size must be aligned to a 512-byte boundary. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:content_type+ - String. Content type for the request. Will be saved with blob unless alternate value is provided in blob_content_type. # * +:content_encoding+ - String. Content encoding for the request. Will be saved with blob unless alternate value is provided in blob_content_encoding. # * +:content_language+ - String. Content language for the request. Will be saved with blob unless alternate value is provided in blob_content_language. # * +:content_md5+ - String. Content MD5 for the request. Will be saved with blob unless alternate value is provided in blob_content_md5. # * +:cache_control+ - String. Cache control for the request. Will be saved with blob unless alternate value is provided in blob_cache_control. # * +:blob_content_type+ - String. Content type for the blob. Will be saved with blob. # * +:blob_content_encoding+ - String. Content encoding for the blob. Will be saved with blob. # * +:blob_content_language+ - String. Content language for the blob. Will be saved with blob. # * +:blob_content_md5+ - String. Content MD5 for the blob. Will be saved with blob. # * +:blob_cache_control+ - String. Cache control for the blob. Will be saved with blob. # * +:metadata+ - Hash. Custom metadata values to store with the blob. # * +:sequence_number+ - Integer. The sequence number is a user-controlled value that you can use to track requests. The value of the sequence number must be between 0 and 2^63 - 1.The default value is 0. # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/dd179451.aspx # # Returns a Blob def create_page_blob(container, blob, length, options={}) query = { } query['timeout'] = options[:timeout].to_s if options[:timeout] uri = blob_uri(container, blob, query) headers = service_properties_headers # set x-ms-blob-type to PageBlob headers['x-ms-blob-type'] = 'PageBlob' # ensure content-length is 0 and x-ms-blob-content-length is the blob length headers['Content-Length'] = 0.to_s headers['x-ms-blob-content-length'] = length.to_s # set x-ms-sequence-number from options (or default to 0) headers['x-ms-sequence-number'] = (options[:sequence_number] || 0).to_s # set the rest of the optional headers headers['Content-Type'] = options[:content_type] if options[:content_type] headers['Content-Encoding'] = options[:content_encoding] if options[:content_encoding] headers['Content-Language'] = options[:content_language] if options[:content_language] headers['Content-MD5'] = options[:content_md5] if options[:content_md5] headers['Cache-Control'] = options[:cache_control] if options[:cache_control] headers['x-ms-blob-content-type'] = options[:blob_content_type] if options[:blob_content_type] headers['x-ms-blob-content-encoding'] = options[:blob_content_encoding] if options[:blob_content_encoding] headers['x-ms-blob-content-language'] = options[:blob_content_language] if options[:blob_content_language] headers['x-ms-blob-content-md5'] = options[:blob_content_md5] if options[:blob_content_md5] headers['x-ms-blob-cache-control'] = options[:blob_cache_control] if options[:blob_cache_control] add_metadata_to_headers(options[:metadata], headers) if options[:metadata] # call PutBlob with empty body response = call(:put, uri, nil, headers) result = Serialization.blob_from_headers(response.headers) result.name = blob result end # Public: Creates a range of pages in a page blob. # # ==== Attributes # # * +container+ - String. Name of container # * +blob+ - String. Name of blob # * +start_range+ - Integer. Position of first byte of first page # * +end_range+ - Integer. Position of last byte of of last page # * +content+ - IO or String. Content to write. Length in bytes should equal end_range - start_range # * +options+ - Hash. A collection of options. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:if_sequence_number_le+ - If the blob's sequence number is less than or equal to the specified value, the request proceeds; otherwise it fails with the SequenceNumberConditionNotMet error (HTTP status code 412 - Precondition Failed). # * +:if_sequence_number_lt+ - If the blob's sequence number is less than the specified value, the request proceeds; otherwise it fails with SequenceNumberConditionNotMet error (HTTP status code 412 - Precondition Failed). # * +:if_sequence_number_eq+ - If the blob's sequence number is equal to the specified value, the request proceeds; otherwise it fails with SequenceNumberConditionNotMet error (HTTP status code 412 - Precondition Failed). # * +:if_modified_since+ - A DateTime value. Specify this conditional header to write the page only if the blob has been modified since the specified date/time. If the blob has not been modified, the Blob service returns status code 412 (Precondition Failed). # * +:if_unmodified_since+ - A DateTime value. Specify this conditional header to write the page only if the blob has not been modified since the specified date/time. If the blob has been modified, the Blob service returns status code 412 (Precondition Failed). # * +:if_match+ - An ETag value. Specify an ETag value for this conditional header to write the page only if the blob's ETag value matches the value specified. If the values do not match, the Blob service returns status code 412 (Precondition Failed). # * +:if_none_match+ - An ETag value. Specify an ETag value for this conditional header to write the page only if the blob's ETag value does not match the value specified. If the values are identical, the Blob service returns status code 412 (Precondition Failed). # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/ee691975.aspx # # Returns Blob def create_blob_pages(container, blob, start_range, end_range, content, options={}) query = { 'comp' => 'page'} query['timeout'] = options[:timeout].to_s if options[:timeout] uri = blob_uri(container, blob, query) headers = service_properties_headers headers['x-ms-range'] = "bytes=#{start_range}-#{end_range}" headers['x-ms-page-write'] = 'update' # clear default content type headers['Content-Type'] = '' # set optional headers unless options.empty? headers['x-ms-if-sequence-number-le'] = options[:if_sequence_number_le] if options[:if_sequence_number_le] headers['x-ms-if-sequence-number-lt'] = options[:if_sequence_number_lt] if options[:if_sequence_number_lt] headers['x-ms-if-sequence-number-eq'] = options[:if_sequence_number_eq] if options[:if_sequence_number_eq] headers['If-Modified-Since'] = options[:if_modified_since] if options[:if_modified_since] headers['If-Unmodified-Since'] = options[:if_unmodified_since] if options[:if_unmodified_since] headers['If-Match'] = options[:if_match] if options[:if_match] headers['If-None-Match'] = options[:if_none_match] if options[:if_none_match] end response = call(:put, uri, content, headers) result = Serialization.blob_from_headers(response.headers) result.name = blob result end # Public: Clears a range of pages from the blob. # # ==== Attributes # # * +container+ - String. Name of container. # * +blob+ - String. Name of blob. # * +start_range+ - Integer. Position of first byte of first page. # * +end_range+ - Integer. Position of last byte of of last page. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/ee691975.aspx # # Returns Blob def clear_blob_pages(container, blob, start_range, end_range, options={}) query = { 'comp' => 'page'} query['timeout'] = options[:timeout].to_s if options[:timeout] uri = blob_uri(container, blob, query) headers = service_properties_headers headers['x-ms-range'] = "bytes=#{start_range}-#{end_range}" headers['x-ms-page-write'] = 'clear' # clear default content type headers['Content-Type'] = '' response = call(:put, uri, nil, headers) result = Serialization.blob_from_headers(response.headers) result.name = blob result end # Public: Creates a new block blob or updates the content of an existing block blob. # # Updating an existing block blob overwrites any existing metadata on the blob # Partial updates are not supported with create_block_blob the content of the # existing blob is overwritten with the content of the new blob. To perform a # partial update of the content of a block blob, use the create_block_list # method. # # Note that the default content type is application/octet-stream. # # ==== Attributes # # * +container+ - String. The container name. # * +blob+ - String. The blob name. # * +content+ - IO or String. The content of the blob. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:content_type+ - String. Content type for the request. Will be saved with blob unless alternate value is provided in blob_content_type. # * +:content_encoding+ - String. Content encoding for the request. Will be saved with blob unless alternate value is provided in blob_content_encoding. # * +:content_language+ - String. Content language for the request. Will be saved with blob unless alternate value is provided in blob_content_language. # * +:content_md5+ - String. Content MD5 for the request. Will be saved with blob unless alternate value is provided in blob_content_md5. # * +:cache_control+ - String. Cache control for the request. Will be saved with blob unless alternate value is provided in blob_cache_control. # * +:blob_content_type+ - String. Content type for the blob. Will be saved with blob. # * +:blob_content_encoding+ - String. Content encoding for the blob. Will be saved with blob. # * +:blob_content_language+ - String. Content language for the blob. Will be saved with blob. # * +:blob_content_md5+ - String. Content MD5 for the blob. Will be saved with blob. # * +:blob_cache_control+ - String. Cache control for the blob. Will be saved with blob. # * +:metadata+ - Hash. Custom metadata values to store with the blob. # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/dd179451.aspx # # Returns a Blob def create_block_blob(container, blob, content, options={}) query = { } query['timeout'] = options[:timeout].to_s if options[:timeout] uri = blob_uri(container, blob, query) headers = service_properties_headers # set x-ms-blob-type to BlockBlob headers['x-ms-blob-type'] = 'BlockBlob' # set the rest of the optional headers headers['Content-Type'] = options[:content_type] || 'application/octet-stream' headers['Content-Encoding'] = options[:content_encoding] if options[:content_encoding] headers['Content-Language'] = options[:content_language] if options[:content_language] headers['Content-MD5'] = options[:content_md5] if options[:content_md5] headers['Cache-Control'] = options[:cache_control] if options[:cache_control] headers['x-ms-blob-content-type'] = options[:blob_content_type] if options[:blob_content_type] headers['x-ms-blob-content-encoding'] = options[:blob_content_encoding] if options[:blob_content_encoding] headers['x-ms-blob-content-language'] = options[:blob_content_language] if options[:blob_content_language] headers['x-ms-blob-content-md5'] = options[:blob_content_md5] if options[:blob_content_md5] headers['x-ms-blob-cache-control'] = options[:blob_cache_control] if options[:blob_cache_control] headers['x-ms-blob-content-disposition'] = options[:blob_content_disposition] if options[:blob_content_disposition] add_metadata_to_headers(options[:metadata], headers) if options[:metadata] # call PutBlob with empty body response = call(:put, uri, content, headers) result = Serialization.blob_from_headers(response.headers) result.name = blob result end # Public: Creates a new block to be committed as part of a block blob. # # ==== Attributes # # * +container+ - String. The container name. # * +blob+ - String. The blob name. # * +block_id+ - String. The block id. Note: this should be the raw block id, not Base64 encoded. # * +content+ - IO or String. The content of the blob. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:content_md5+ - String. Content MD5 for the request contents. # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/dd135726.aspx # # Returns the MD5 of the uploaded block (as calculated by the server) def create_blob_block(container, blob, block_id, content, options={}) query = { 'comp' => 'block'} query['blockid'] = Base64.strict_encode64(block_id) query['timeout'] = options[:timeout].to_s if options[:timeout] uri = blob_uri(container, blob, query) headers = service_properties_headers headers['Content-MD5'] = options[:content_md5] if options[:content_md5] response = call(:put, uri, content, headers) response.headers['Content-MD5'] end # Public: Commits existing blob blocks to a blob. # # This method writes a blob by specifying the list of block IDs that make up the # blob. In order to be written as part of a blob, a block must have been # successfully written to the server in a prior create_blob_block method. # # You can call Put Block List to update a blob by uploading only those blocks # that have changed, then committing the new and existing blocks together. # You can do this by specifying whether to commit a block from the committed # block list or from the uncommitted block list, or to commit the most recently # uploaded version of the block, whichever list it may belong to. # # ==== Attributes # # * +container+ - String. The container name. # * +blob+ - String. The blob name. # * +block_list+ - Array. A ordered list of lists in the following format: # [ ["block_id1", :committed], ["block_id2", :uncommitted], ["block_id3"], ["block_id4", :committed]... ] # The first element of the inner list is the block_id, the second is optional # and can be either :committed or :uncommitted to indicate in which group of blocks # the id should be looked for. If it is omitted, the latest of either group will be used. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:content_md5+ - String. Content MD5 for the request contents (not the blob contents!) # * +:blob_content_type+ - String. Content type for the blob. Will be saved with blob. # * +:blob_content_encoding+ - String. Content encoding for the blob. Will be saved with blob. # * +:blob_content_language+ - String. Content language for the blob. Will be saved with blob. # * +:blob_content_md5+ - String. Content MD5 for the blob. Will be saved with blob. # * +:blob_cache_control+ - String. Cache control for the blob. Will be saved with blob. # * +:metadata+ - Hash. Custom metadata values to store with the blob. # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/dd179467.aspx # # Returns nil on success def commit_blob_blocks(container, blob, block_list, options={}) query = { 'comp' => 'blocklist'} query['timeout'] = options[:timeout].to_s if options[:timeout] uri = blob_uri(container, blob, query) headers = service_properties_headers unless options.empty? headers['Content-MD5'] = options[:content_md5] if options[:content_md5] headers['x-ms-blob-content-type'] = options[:blob_content_type] if options[:blob_content_type] headers['x-ms-blob-content-encoding'] = options[:blob_content_encoding] if options[:blob_content_encoding] headers['x-ms-blob-content-language'] = options[:blob_content_language] if options[:blob_content_language] headers['x-ms-blob-content-md5'] = options[:blob_content_md5] if options[:blob_content_md5] headers['x-ms-blob-cache-control'] = options[:blob_cache_control] if options[:blob_cache_control] headers['x-ms-blob-content-disposition'] = options[:blob_content_disposition] if options[:blob_content_disposition] add_metadata_to_headers(options[:metadata], headers) if options[:metadata] end body = Serialization.block_list_to_xml(block_list) call(:put, uri, body, headers) nil end # Public: Retrieves the list of blocks that have been uploaded as part of a block blob. # # There are two block lists maintained for a blob: # 1) Committed Block List: The list of blocks that have been successfully # committed to a given blob with commitBlobBlocks. # 2) Uncommitted Block List: The list of blocks that have been uploaded for a # blob using Put Block (REST API), but that have not yet been committed. # These blocks are stored in Microsoft Azure in association with a blob, but do # not yet form part of the blob. # # ==== Attributes # # * +container+ - String. The container name. # * +blob+ - String. The blob name. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:blocklist_type+ - Symbol. One of :all, :committed, :uncommitted. Defaults to :all (optional) # * +:snapshot+ - String. An opaque DateTime value that specifies the blob snapshot to # retrieve information from. (optional) # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/dd179400.aspx # # Returns a list of Azure::Entity::Blob::Block instances def list_blob_blocks(container, blob, options={}) options[:blocklist_type] = options[:blocklist_type] || :all query = { 'comp' => 'blocklist'} query['snapshot'] = options[:snapshot] if options[:snapshot] query['blocklisttype'] = options[:blocklist_type].to_s if options[:blocklist_type] query['timeout'] = options[:timeout].to_s if options[:timeout] uri = blob_uri(container, blob, query) response = call(:get, uri) Serialization.block_list_from_xml(response.body) end # Public: Returns all properties and metadata on the blob. # # ==== Attributes # # * +container+ - String. The container name. # * +blob+ - String. The blob name. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:snapshot+ - String. An opaque DateTime value that specifies the blob snapshot to # retrieve information from. # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/dd179394.aspx # # Returns a Blob def get_blob_properties(container, blob, options={}) query = { } query['snapshot'] = options[:snapshot] if options[:snapshot] query['timeout'] = options[:timeout].to_s if options[:timeout] uri = blob_uri(container, blob, query) response = call(:head, uri) result = Serialization.blob_from_headers(response.headers) result.name = blob result.snapshot = options[:snapshot] result end # Public: Returns metadata on the blob. # # ==== Attributes # # * +container+ - String. The container name. # * +blob+ - String. The blob name. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:snapshot+ - String. An opaque DateTime value that specifies the blob snapshot to # retrieve information from. # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/dd179350.aspx # # Returns a Blob def get_blob_metadata(container, blob, options={}) query = {'comp' => 'metadata'} query['snapshot'] = options[:snapshot] if options[:snapshot] query['timeout'] = options[:timeout].to_s if options[:timeout] uri = blob_uri(container, blob, query) response = call(:get, uri) result = Serialization.blob_from_headers(response.headers) result.name = blob result.snapshot = options[:snapshot] result end # Public: Returns a list of active page ranges for a page blob. Active page ranges are # those that have been populated with data. # # ==== Attributes # # * +container+ - String. The container name. # * +blob+ - String. The blob name. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:start_range+ - Integer. Position of first byte of first page. (optional) # * +:end_range+ - Integer. Position of last byte of of last page. (optional) # * +:snapshot+ - String. An opaque DateTime value that specifies the blob snapshot to # retrieve information from. (optional) # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/ee691973.aspx # # Returns a list of page ranges in the format [ [start, end], [start, end], ... ] # # eg. [ [0, 511], [512, 1024], ... ] # def list_page_blob_ranges(container, blob, options={}) query = {'comp' => 'pagelist'} query.update({'snapshot' => options[:snapshot]}) if options[:snapshot] query['timeout'] = options[:timeout].to_s if options[:timeout] uri = blob_uri(container, blob, query) options[:start_range] = 0 if options[:end_range] and not options[:start_range] headers = service_properties_headers headers = { 'x-ms-range' => "bytes=#{options[:start_range]}-#{options[:end_range]}" } if options[:start_range] response = call(:get, uri, nil, headers) pagelist = Serialization.page_list_from_xml(response.body) pagelist end # Public: Sets system properties defined for a blob. # # ==== Attributes # # * +container+ - String. The container name. # * +blob+ - String. The blob name. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:content_type+ - String. Content type for the blob. Will be saved with blob. # * +:content_encoding+ - String. Content encoding for the blob. Will be saved with blob. # * +:content_language+ - String. Content language for the blob. Will be saved with blob. # * +:content_md5+ - String. Content MD5 for the blob. Will be saved with blob. # * +:cache_control+ - String. Cache control for the blob. Will be saved with blob. # * +:content_length+ - Integer. Resizes a page blob to the specified size. If the specified # value is less than the current size of the blob, then all pages above # the specified value are cleared. This property cannot be used to change # the size of a block blob. Setting this property for a block blob returns # status code 400 (Bad Request). # * +:sequence_number_action+ - Symbol. This property indicates how the service should modify the sequence # number for the blob. Required if :sequence_number is used. This property # applies to page blobs only. # # Specify one of the following options for this property: # # * +:max+ - Sets the sequence number to be the higher of the value included with # the request and the value currently stored for the blob. # * +:update+ - Sets the sequence number to the value included with the request. # * +:increment+ - Increments the value of the sequence number by 1. If specifying this # option, do not include the sequence_number option; doing so will return # status code 400 (Bad Request). # * +:sequence_number+ - Integer. This property sets the blob's sequence number. The sequence number is a # user-controlled property that you can use to track requests and manage concurrency # issues. Required if the :sequence_number_action option is set to :max or :update. # This property applies to page blobs only. # # Use this together with the :sequence_number_action to update the blob's sequence # number, either to the specified value or to the higher of the values specified with # the request or currently stored with the blob. # # This header should not be specified if :sequence_number_action is set to :increment; # in this case the service automatically increments the sequence number by one. # # To set the sequence number to a value of your choosing, this property must be specified # together with :sequence_number_action # * +:timeout+ - Integer. A timeout in seconds. # # Remarks: # # The semantics for updating a blob's properties are as follows: # # * A page blob's sequence number is updated only if the request meets either of the following conditions: # # * The :sequence_number_action property is set to :max or :update, and a value for :sequence_number is also set. # * The :sequence_number_action property is set to :increment, indicating that the service should increment # the sequence number by one. # # * The size of the page blob is modified only if a value for :content_length is specified. # # * If :sequence_number and/or :content_length are the only properties specified, then the other properties of the blob # will NOT be modified. # # * If any one or more of the following properties are set, then all of these properties are set together. If a value is # not provided for a given property when at least one of the properties listed below is set, then that property will be # cleared for the blob. # # * :cache_control # * :content_type # * :content_md5 # * :content_encoding # * :content_language # # See http://msdn.microsoft.com/en-us/library/azure/ee691966.aspx # # Returns nil on success. def set_blob_properties(container, blob, options={}) query = { 'comp' => 'properties'} query['timeout'] = options[:timeout].to_s if options[:timeout] uri = blob_uri(container, blob, query) headers = service_properties_headers unless options.empty? headers['x-ms-blob-content-type'] = options[:blob_content_type] if options[:blob_content_type] headers['x-ms-blob-content-encoding'] = options[:blob_content_encoding] if options[:blob_content_encoding] headers['x-ms-blob-content-language'] = options[:blob_content_language] if options[:blob_content_language] headers['x-ms-blob-content-md5'] = options[:blob_content_md5] if options[:blob_content_md5] headers['x-ms-blob-cache-control'] = options[:blob_cache_control] if options[:blob_cache_control] headers['x-ms-blob-content-length'] = options[:blob_content_length].to_s if options[:blob_content_length] headers['x-ms-blob-sequence-number-action'] = options[:sequence_number_action].to_s if options[:sequence_number_action] headers['x-ms-blob-sequence-number'] = options[:sequence_number].to_s if options[:sequence_number] headers['x-ms-blob-content-disposition'] = options[:blob_content_disposition] if options[:blob_content_disposition] end call(:put, uri, nil, headers) nil end # Public: Sets metadata headers on the blob. # # ==== Attributes # # * +container+ - String. The container name. # * +blob+ - String. The blob name. # * +metadata+ - Hash. The custom metadata. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/dd179414.aspx # # Returns nil on success. def set_blob_metadata(container, blob, metadata, options={}) query = { 'comp' => 'metadata'} query['timeout'] = options[:timeout].to_s if options[:timeout] uri = blob_uri(container, blob, query) headers = service_properties_headers add_metadata_to_headers(metadata, headers) if metadata call(:put, uri, nil, headers) nil end # Public: Reads or downloads a blob from the system, including its metadata and properties. # # ==== Attributes # # * +container+ - String. The container name. # * +blob+ - String. The blob name. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:start_range+ - Integer. Position of first byte of first page. (optional) # * +:end_range+ - Integer. Position of last byte of of last page. (optional) # * +:snapshot+ - String. An opaque DateTime value that specifies the blob snapshot to # retrieve information from. (optional) # * +:get_content_md5+ - Boolean. Return the MD5 hash for the range. This option only valid if # start_range and end_range are specified. (optional) # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/dd179440.aspx # # Returns a blob and the blob body def get_blob(container, blob, options={}) query = { } query['snapshot'] = options[:snapshot] if options[:snapshot] query['timeout'] = options[:timeout].to_s if options[:timeout] uri = blob_uri(container, blob, query) headers = service_properties_headers options[:start_range] = 0 if options[:end_range] and not options[:start_range] if options[:start_range] headers['x-ms-range'] = "bytes=#{options[:start_range]}-#{options[:end_range]}" headers['x-ms-range-get-content-md5'] = true if options[:get_content_md5] end response = call(:get, uri, nil, headers) result = Serialization.blob_from_headers(response.headers) result.name = blob unless result.name return result, response.body end # Public: Deletes a blob or blob snapshot. # # ==== Attributes # # * +container+ - String. The container name. # * +blob+ - String. The blob name. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:snapshot+ - String. An opaque DateTime value that specifies the blob snapshot to # retrieve information from. (optional) # * +:delete_snapshots+ - Symbol. Used to specify the scope of the delete operation for snapshots. # This parameter is ignored if a blob does not have snapshots, or if a # snapshot is specified in the snapshot parameter. (optional) # # Possible values include: # * +:only+ - Deletes only the snapshots for the blob, but leaves the blob # * +:include+ - Deletes the blob and all of the snapshots for the blob # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/dd179440.aspx # # Returns nil on success def delete_blob(container, blob, options={}) query = { } query['snapshot'] = options[:snapshot] if options[:snapshot] query['timeout'] = options[:timeout].to_s if options[:timeout] uri = blob_uri(container, blob, query) options[:delete_snapshots] = :include unless options[:delete_snapshots] headers = service_properties_headers headers['x-ms-delete-snapshots'] = options[:delete_snapshots].to_s if options[:delete_snapshots] && options[:snapshot] == nil call(:delete, uri, nil, headers) nil end # Public: Creates a snapshot of a blob. # # ==== Attributes # # * +container+ - String. The container name. # * +blob+ - String. The blob name. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:metadata+ - Hash. Custom metadata values to store with the blob snapshot. # * +:if_modified_since+ - A DateTime value. Specify this option to write the page only if the blob has been # modified since the specified date/time. If the blob has not been modified, the # Blob service returns status code 412 (Precondition Failed). # * +:if_unmodified_since+ - A DateTime value. Specify this option to write the page only if the blob has not # been modified since the specified date/time. If the blob has been modified, the # Blob service returns status code 412 (Precondition Failed). # * +:if_match+ - An ETag value. Specify an ETag value to write the page only if the blob's ETag # value matches the value specified. If the values do not match, the Blob service # returns status code 412 (Precondition Failed). # * +:if_none_match+ - An ETag value. Specify an ETag value to write the page only if the blob's ETag # value does not match the value specified. If the values are identical, the Blob # service returns status code 412 (Precondition Failed). # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/ee691971.aspx # # Returns the snapshot DateTime value def create_blob_snapshot(container, blob, options={}) query = { 'comp' => 'snapshot'} query['timeout'] = options[:timeout].to_s if options[:timeout] uri = blob_uri(container, blob, query) headers = service_properties_headers unless options.empty? add_metadata_to_headers(options[:metadata], headers) if options[:metadata] headers['If-Modified-Since'] = options[:if_modified_since] if options[:if_modified_since] headers['If-Unmodified-Since'] = options[:if_unmodified_since] if options[:if_unmodified_since] headers['If-Match'] = options[:if_match] if options[:if_match] headers['If-None-Match'] = options[:if_none_match] if options[:if_none_match] end response = call(:put, uri, nil, headers) response.headers['x-ms-snapshot'] end # Public: Copies a source blob to a destination blob within the same storage account. # # ==== Attributes # # * +source_container+ - String. The destination container name to copy to. # * +source_blob+ - String. The destination blob name to copy to. # * +destination_container+ - String. The source container name to copy from. # * +destination_blob+ - String. The source blob name to copy from. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:source_snapshot+ - String. A snapshot id for the source blob # * +:metadata+ - Hash. Custom metadata values to store with the copy. If this parameter is not # specified, the operation will copy the source blob metadata to the destination # blob. If this parameter is specified, the destination blob is created with the # specified metadata, and metadata is not copied from the source blob. # * +:source_if_modified_since+ - A DateTime value. Specify this option to write the page only if the source blob # has been modified since the specified date/time. If the blob has not been # modified, the Blob service returns status code 412 (Precondition Failed). # * +:source_if_unmodified_since+ - A DateTime value. Specify this option to write the page only if the source blob # has not been modified since the specified date/time. If the blob has been # modified, the Blob service returns status code 412 (Precondition Failed). # * +:source_if_match+ - An ETag value. Specify an ETag value to write the page only if the source blob's # ETag value matches the value specified. If the values do not match, the Blob # service returns status code 412 (Precondition Failed). # * +:source_if_none_match+ - An ETag value. Specify an ETag value to write the page only if the source blob's # ETag value does not match the value specified. If the values are identical, the # Blob service returns status code 412 (Precondition Failed). # * +:dest_if_modified_since+ - A DateTime value. Specify this option to write the page only if the destination # blob has been modified since the specified date/time. If the blob has not been # modified, the Blob service returns status code 412 (Precondition Failed). # * +:dest_if_unmodified_since+ - A DateTime value. Specify this option to write the page only if the destination # blob has not been modified since the specified date/time. If the blob has been # modified, the Blob service returns status code 412 (Precondition Failed). # * +:dest_if_match+ - An ETag value. Specify an ETag value to write the page only if the destination # blob's ETag value matches the value specified. If the values do not match, the # Blob service returns status code 412 (Precondition Failed). # * +:dest_if_none_match+ - An ETag value. Specify an ETag value to write the page only if the destination # blob's ETag value does not match the value specified. If the values are # identical, the Blob service returns status code 412 (Precondition Failed). # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/dd894037.aspx # # Returns a tuple of (copy_id, copy_status). # # * +copy_id+ - String identifier for this copy operation. Use with get_blob or get_blob_properties to check # the status of this copy operation, or pass to abort_copy_blob to abort a pending copy. # * +copy_status+ - String. The state of the copy operation, with these values: # "success" - The copy completed successfully. # "pending" - The copy is in progress. # def copy_blob(destination_container, destination_blob, source_container, source_blob, options={}) query = { } query['timeout'] = options[:timeout].to_s if options[:timeout] uri = blob_uri(destination_container, destination_blob, query) headers = service_properties_headers headers['x-ms-copy-source'] = blob_uri(source_container, source_blob, options[:source_snapshot] ? { 'snapshot' => options[:source_snapshot] } : {}).to_s unless options.empty? headers['If-Modified-Since'] = options[:dest_if_modified_since] if options[:dest_if_modified_since] headers['If-Unmodified-Since'] = options[:dest_if_unmodified_since] if options[:dest_if_unmodified_since] headers['If-Match'] = options[:dest_if_match] if options[:dest_if_match] headers['If-None-Match'] = options[:dest_if_none_match] if options[:dest_if_none_match] headers['x-ms-source-if-modified-since'] = options[:source_if_modified_since] if options[:source_if_modified_since] headers['x-ms-source-if-unmodified-since'] = options[:source_if_unmodified_since] if options[:source_if_unmodified_since] headers['x-ms-source-if-match'] = options[:source_if_match] if options[:source_if_match] headers['x-ms-source-if-none-match'] = options[:source_if_none_match] if options[:source_if_none_match] add_metadata_to_headers(options[:metadata], headers) if options[:metadata] end response = call(:put, uri, nil, headers) return response.headers['x-ms-copy-id'], response.headers['x-ms-copy-status'] end # Public: Establishes an exclusive one-minute write lock on a blob. To write to a locked # blob, a client must provide a lease ID. # # ==== Attributes # # * +container+ - String. The container name. # * +blob+ - String. The blob name. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:duration+ - Integer. Default -1. Specifies the duration of the lease, in seconds, or negative one (-1) # for a lease that never expires. A non-infinite lease can be between 15 and 60 seconds. (optional) # * +:proposed_lease_id+ - String. Proposed lease ID, in a GUID string format. The Blob service returns 400 (Invalid request) # if the proposed lease ID is not in the correct format. (optional) # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/ee691972.aspx # # Returns a String of the new unique lease id. While the lease is active, you must include the lease ID with any request # to write to the blob, or to renew, change, or release the lease. A successful renew operation also returns the lease id # for the active lease. # def acquire_lease(container, blob, options={}) query = { 'comp' => 'lease'} query['timeout'] = options[:timeout].to_s if options[:timeout] uri = blob_uri(container, blob, query) duration = -1 duration = options[:duration] if options[:duration] headers = service_properties_headers headers['x-ms-lease-action'] = 'acquire' headers['x-ms-lease-duration'] = duration.to_s if duration headers['x-ms-proposed-lease-id'] = options[:proposed_lease_id] if options[:proposed_lease_id] response = call(:put, uri, nil, headers) response.headers['x-ms-lease-id'] end # Public: Renews the lease. The lease can be renewed if the lease ID specified on the request matches that # associated with the blob. Note that the lease may be renewed even if it has expired as long as the blob # has not been modified or leased again since the expiration of that lease. When you renew a lease, the # lease duration clock resets. # # ==== Attributes # # * +container+ - String. The container name. # * +blob+ - String. The blob name. # * +lease+ - String. The lease id # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/ee691972.aspx # # Returns the renewed lease id def renew_lease(container, blob, lease, options={}) query = { 'comp' => 'lease'} query['timeout'] = options[:timeout].to_s if options[:timeout] uri = blob_uri(container, blob, query) headers = service_properties_headers headers['x-ms-lease-action'] = 'renew' headers['x-ms-lease-id'] = lease response = call(:put, uri, nil, headers) response.headers['x-ms-lease-id'] end # Public: Releases the lease. The lease may be released if the lease ID specified on the request matches that # associated with the blob. Releasing the lease allows another client to immediately acquire the lease for # the blob as soon as the release is complete. # # ==== Attributes # # * +container+ - String. The container name. # * +blob+ - String. The blob name. # * +lease+ - String. The lease id. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/ee691972.aspx # # Returns nil on success def release_lease(container, blob, lease, options={}) query = { 'comp' => 'lease'} query['timeout'] = options[:timeout].to_s if options[:timeout] uri = blob_uri(container, blob, query) headers = service_properties_headers headers['x-ms-lease-action'] = 'release' headers['x-ms-lease-id'] = lease call(:put, uri, nil, headers) nil end # Public: Breaks the lease, if the blob has an active lease. Once a lease is broken, it cannot be renewed. Any # authorized request can break the lease; the request is not required to specify a matching lease ID. When a # lease is broken, the lease break period is allowed to elapse, during which time no lease operation except # break and release can be performed on the blob. When a lease is successfully broken, the response indicates # the interval in seconds until a new lease can be acquired. # # A lease that has been broken can also be released, in which case another client may immediately acquire the # lease on the blob. # # ==== Attributes # # * +container+ - String. The container name. # * +blob+ - String. The blob name. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:break_period+ - Integer. The proposed duration of seconds that the lease should continue before it is # broken, between 0 and 60 seconds. This break period is only used if it is shorter than # the time remaining on the lease. If longer, the time remaining on the lease is used. A # new lease will not be available before the break period has expired, but the lease may # be held for longer than the break period. # # If this option is not used, a fixed-duration lease breaks after the remaining lease # period elapses, and an infinite lease breaks immediately. # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/ee691972.aspx # # Returns an Integer of the remaining lease time. This value is the approximate time remaining in the lease # period, in seconds. This header is returned only for a successful request to break the lease. If the break # is immediate, 0 is returned. def break_lease(container, blob, options={}) query = { 'comp' => 'lease'} query['timeout'] = options[:timeout].to_s if options[:timeout] uri = blob_uri(container, blob, query) headers = service_properties_headers headers['x-ms-lease-action'] = 'break' headers['x-ms-lease-break-period'] = options[:break_period].to_s if options[:break_period] response = call(:put, uri, nil, headers) response.headers['x-ms-lease-time'].to_i end def call(method, uri, body=nil, headers=nil) # Synchronize body and header encoding; header['Content-Encoding'] takes precedence. if headers && !body.nil? if headers['Content-Encoding'].nil? headers['Content-Encoding'] = body.encoding.to_s if body.respond_to? :encoding # String headers['Content-Encoding'] = body.external_encoding.to_s if body.respond_to? :external_encoding # IO else body.force_encoding(headers['Content-Encoding']) if body.respond_to? :force_encoding # String body.set_encoding(headers['Content-Encoding']) if body.respond_to? :set_encoding # IO end # Azure Storage Service expects content-encoding to be lowercase. # Authentication will fail otherwise. headers['Content-Encoding'] = headers['Content-Encoding'].downcase end response = super # Force the response.body to the content encoding of specified in the header. # content-encoding is echo'd back for the blob and is used to store the encoding of the octet stream if !response.nil? && !response.body.nil? && response.headers['content-encoding'] response.body.force_encoding(response.headers['content-encoding']) end response end # Protected: Generate the URI for the collection of containers. # # ==== Attributes # # * +query+ - A Hash of key => value query parameters. # * +host+ - The host of the API. # # Returns a URI. protected def containers_uri(query={}) query = { 'comp' => 'list'}.merge(query) generate_uri('/', query) end # Protected: Generate the URI for a specific container. # # ==== Attributes # # * +name+ - The container name. If this is a URI, we just return this. # * +query+ - A Hash of key => value query parameters. # * +host+ - The host of the API. # # Returns a URI. def container_uri(name, query={}) return name if name.kind_of? ::URI query = { 'restype' => 'container'}.merge(query) generate_uri(name, query) end # Protected: Generate the URI for a specific Blob. # # ==== Attributes # # * +container_name+ - String representing the name of the container. # * +blob_name+ - String representing the name of the blob. # * +query+ - A Hash of key => value query parameters. # * +host+ - The host of the API. # # Returns a URI. def blob_uri(container_name, blob_name, query={}) if container_name.nil? || container_name.empty? path = blob_name else path = File.join(container_name, blob_name) end path = CGI.escape(path.encode('UTF-8')) # Unencode the forward slashes to match what the server expects. path = path.gsub(/%2F/, '/') # Unencode the backward slashes to match what the server expects. path = path.gsub(/%5C/, '/') # Re-encode the spaces (encoded as space) to the % encoding. path = path.gsub(/\+/, '%20') generate_uri(path, query) end end end end Azure::BlobService = Azure::Blob::BlobServiceazure-0.7.9/lib/azure/blob/container.rb0000644000004100000410000000205613112322225020022 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- module Azure module Blob class Container def initialize @properties = {} @metadata = {} yield self if block_given? end attr_accessor :name attr_accessor :properties attr_accessor :metadata attr_accessor :public_access_level end end endazure-0.7.9/lib/azure/blob/auth/0000755000004100000410000000000013112322225016451 5ustar www-datawww-dataazure-0.7.9/lib/azure/blob/auth/shared_access_signature.rb0000644000004100000410000001370713112322225023656 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- # TODO: extract common SAS logic, add support for the other SAS types require 'azure/core/auth/signer' # This will break if used against API version prior to 2013-08-15 as the format # changed # @see https://msdn.microsoft.com/library/azure/dn140255.aspx for more information on construction module Azure module Blob module Auth class SharedAccessSignature < Azure::Core::Auth::Signer DEFAULTS = { resource: 'b', permissions: 'r', version: '2014-02-14' } KEY_MAPPINGS = { permissions: :sp, start: :st, expiry: :se, resource: :sr, identifier: :si, version: :sv, cache_control: :rscc, content_disposition: :rscd, content_encoding: :rsce, content_language: :rscl, content_type: :rsct } OPTIONAL_QUERY_PARAMS = [:sp, :si, :rscc, :rscd, :rsce, :rscl, :rsct] attr :account_name # Public: Initialize the Signer. # # @param account_name [String] The account name. Defaults to the one in the global configuration. # @param access_key [String] The access_key encoded in Base64. Defaults to the one in the global configuration. def initialize(account_name=Azure.storage_account_name, access_key=Azure.storage_access_key) @account_name = account_name super(access_key) end # Construct the plaintext to the spec required for signatures # @return [String] def signable_string(path, options) # Order is significant # The newlines from empty strings here are required options[:start] = Time.parse(options[:start]).utc.iso8601 if options[:start] options[:expiry] = Time.parse(options[:expiry]).utc.iso8601 if options[:expiry] [ options[:permissions], options[:start], options[:expiry], canonicalized_resource(path), options[:identifier], options[:version], options[:cache_control], options[:content_disposition], options[:content_encoding], options[:content_language], options[:content_type] ].join("\n") end # Return the cononicalized resource representation of the blob resource # @return [String] def canonicalized_resource(path) "/#{account_name}#{path.start_with?('/') ? '' : '/'}#{path}" end # A customised URI reflecting options for the resource signed with the Shared Access Signature # @param uri [URI] uri to resource including query options # @param options [Hash] # # ==== Options # # * +:resource+ - String. Resource type, either 'b' (blob) or 'c' (container). Default 'b' # * +:permissions+ - String. Combination of 'r','w','d','l' (container only) in this order. Default 'r' # * +:start+ - String. UTC Date/Time in ISO8601 format. Optional. # * +:expiry+ - String. UTC Date/Time in ISO8601 format. Optional. Default now + 30 minutes. # * +:identifier+ - String. Identifier for stored access policy. Optional # * +:version+ - String. API version. Default 2014-02-14 # # * +:cache_control+ - String. Response header override. Optional. # * +:content_disposition+ - String. Response header override. Optional. # * +:content_encoding+ - String. Response header override. Optional. # * +:content_language+ - String. Response header override. Optional. # * +:content_type+ - String. Response header override. Optional. def signed_uri(uri, options) parsed_query = CGI::parse(uri.query || '').inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo} options[:start] = Time.parse(options[:start]).utc.iso8601 if options[:start] options[:expiry] = Time.parse(options[:expiry]).utc.iso8601 if options[:expiry] options[:expiry] ||= (Time.now + 60*30).utc.iso8601 if parsed_query.has_key?(:restype) options[:resource] = parsed_query[:restype].first == 'container' ? 'c' : 'b' end options = DEFAULTS.merge(options) sas_params = URI.encode_www_form(query_hash(uri.path, options)) URI.parse(uri.to_s + (uri.query.nil? ? '?' : '&') + sas_params) end def sign_request(req) header_options = {}.tap do |opts| opts[:version] = req.headers['x-ms-version'] if req.headers.has_key?('x-ms-version') end req.uri = signed_uri(req.uri, header_options) end private def signature(path, options) sign(signable_string(path, options)) end def query_hash(path, options) Hash[options.map { |k, v| [KEY_MAPPINGS[k], v] }].reject { |k, v| OPTIONAL_QUERY_PARAMS.include?(k) && v.to_s == '' }.merge( sig: signature(path, options) ) end end end end endazure-0.7.9/lib/azure/blob/block.rb0000644000004100000410000000212213112322225017124 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- module Azure module Blob # Represents a Block as part of a BlockList # The type should be one of :uncommitted, :committed or :latest class Block def initialize @type = :latest yield self if block_given? end attr_accessor :name attr_accessor :size attr_accessor :type end end endazure-0.7.9/lib/azure/queue/0000755000004100000410000000000013112322225015716 5ustar www-datawww-dataazure-0.7.9/lib/azure/queue/serialization.rb0000644000004100000410000000710313112322225021121 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/service/serialization' require 'azure/service/enumeration_results' require 'azure/queue/queue' require 'azure/queue/message' module Azure module Queue module Serialization include Service::Serialization def self.queue_messages_from_xml(xml, decode) xml = slopify(xml) expect_node("QueueMessagesList", xml) results = [] return results unless (xml > "QueueMessage").any? if xml.QueueMessage.count == 0 results.push(queue_message_from_xml(xml.QueueMessage, decode)) else xml.QueueMessage.each { |message_node| results.push(queue_message_from_xml(message_node, decode)) } end results end def self.queue_message_from_xml(xml, decode) xml = slopify(xml) expect_node("QueueMessage", xml) Message.new do |msg| msg.id = xml.MessageId.text if (xml > "MessageId").any? msg.insertion_time = xml.InsertionTime.text if (xml > "InsertionTime").any? msg.expiration_time = xml.ExpirationTime.text if (xml > "ExpirationTime").any? msg.dequeue_count = xml.DequeueCount.text.to_i if (xml > "DequeueCount").any? msg.message_text = xml.MessageText.text if (xml > "MessageText").any? msg.time_next_visible = xml.TimeNextVisible.text if (xml > "TimeNextVisible").any? msg.pop_receipt = xml.PopReceipt.text if (xml > "PopReceipt").any? msg.message_text = Base64.decode64(msg.message_text) if decode end end def self.message_to_xml(message_text, encode) if encode builder = Nokogiri::XML::Builder.new do |xml| xml.QueueMessage { xml.MessageText Base64.encode64(message_text) } end else builder = Nokogiri::XML::Builder.new(:encoding => 'utf-8') do |xml| xml.QueueMessage { xml.MessageText message_text.encode("utf-8") } end end builder.to_xml end def self.queue_enumeration_results_from_xml(xml) xml = slopify(xml) expect_node("EnumerationResults", xml) results = enumeration_results_from_xml(xml, Azure::Service::EnumerationResults.new) return results unless (xml > "Queues").any? && ((xml > "Queues") > "Queue").any? if xml.Queues.Queue.count == 0 results.push(queue_from_xml(xml.Queues.Queue)) else xml.Queues.Queue.each { |queue_node| results.push(queue_from_xml(queue_node)) } end results end def self.queue_from_xml(xml) xml = slopify(xml) expect_node("Queue", xml) queue = Queue.new queue.name = xml.Name.text if (xml > "Name").any? queue.metadata = metadata_from_xml(xml.Metadata) if (xml > "Metadata").any? queue end end end end azure-0.7.9/lib/azure/queue/queue_service.rb0000644000004100000410000005641613112322225021123 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/service/storage_service' require 'azure/queue/serialization' module Azure module Queue class QueueService < Service::StorageService def initialize(options = {}) super(nil, nil, options) @host = @client.storage_queue_host end # Public: Get a list of Queues from the server # # ==== Attributes # # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:prefix+ - String. Filters the results to return only containers # whose name begins with the specified prefix. (optional) # * +:marker+ - String. An identifier the specifies the portion of the # list to be returned. This value comes from the property # Azure::Service::EnumerationResults.continuation_token when there # are more containers available than were returned. The # marker value may then be used here to request the next set # of list items. (optional) # * +:max_results+ - Integer. Specifies the maximum number of containers to return. # If max_results is not specified, or is a value greater than # 5,000, the server will return up to 5,000 items. If it is set # to a value less than or equal to zero, the server will return # status code 400 (Bad Request). (optional) # * +:metadata+ - Boolean. Specifies whether or not to return the container metadata. # (optional, Default=false) # * +:timeout+ - Integer. A timeout in seconds. # # NOTE: Metadata requested with the :metadata parameter must have been stored in # accordance with the naming restrictions imposed by the 2009-09-19 version of the queue # service. Beginning with that version, all metadata names must adhere to the naming # conventions for C# identifiers. # # See http://msdn.microsoft.com/en-us/library/azure/dd179466 # # Any metadata with invalid names which were previously stored, will be returned with the # key "x-ms-invalid-name" in the metadata hash. This may contain multiple values and be an # Array (vs a String if it only contains a single value). # # Returns an Azure::Service::EnumerationResults def list_queues(options={}) query = { } query["prefix"] = options[:prefix] if options[:prefix] query["marker"] = options[:marker] if options[:marker] query["maxresults"] = options[:max_results].to_s if options[:max_results] query["include"] = "metadata" if options[:metadata] == true query["timeout"] = options[:timeout].to_s if options[:timeout] uri = collection_uri(query) response = call(:get, uri) Serialization.queue_enumeration_results_from_xml(response.body) end # Public: Clears all messages from the queue. # # If a queue contains a large number of messages, Clear Messages may time out # before all messages have been deleted. In this case the Queue service will # return status code 500 (Internal Server Error), with the additional error # code OperationTimedOut. If the operation times out, the client should # continue to retry Clear Messages until it succeeds, to ensure that all # messages have been deleted. # # ==== Attributes # # * +queue_name+ - String. The name of the queue. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/dd179454 # # Returns nil on success def clear_messages(queue_name, options={}) query = { } query["timeout"] = options[:timeout].to_s if options[:timeout] uri = messages_uri(queue_name, query) call(:delete, uri) nil end # Public: Creates a new queue under the storage account. # # ==== Attributes # # * +queue_name+ - String. The queue name. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:metadata+ - Hash. A hash of user defined metadata. # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/dd179342 # # Returns nil on success def create_queue(queue_name, options={}) query = { } query["timeout"] = options[:timeout].to_s if options[:timeout] uri = queue_uri(queue_name, query) headers = { } add_metadata_to_headers(options[:metadata] || {}, headers) if options[:metadata] call(:put, uri, nil, headers) nil end # Public: Deletes a queue. # # ==== Attributes # # * +queue_name+ - String. The queue name. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/dd179436 # # Returns nil on success def delete_queue(queue_name, options={}) query = { } query["timeout"] = options[:timeout].to_s if options[:timeout] uri = queue_uri(queue_name, query) call(:delete, uri) nil end # Public: Returns queue properties, including user-defined metadata. # # ==== Attributes # # * +queue_name+ - String. The queue name. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/dd179384 # # Returns a tuple of (approximate_message_count, metadata) # * approximate_messages_count - Integer. The approximate number of messages in the queue. This number is not # lower than the actual number of messages in the queue, but could be higher. # * metadata - Hash. The queue metadata (Default: {}) # def get_queue_metadata(queue_name, options={}) query = { "comp" => "metadata" } query["timeout"] = options[:timeout].to_s if options[:timeout] uri = queue_uri(queue_name, query) response = call(:get, uri) approximate_messages_count = response.headers["x-ms-approximate-messages-count"] metadata = Serialization.metadata_from_headers(response.headers) return approximate_messages_count.to_i, metadata end # Public: Sets user-defined metadata on the queue. To delete queue metadata, call # this API with an empty hash in the metadata parameter. # # ==== Attributes # # * +queue_name+ - String. The queue name. # * +metadata+ - Hash. A hash of user defined metadata # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/dd179348 # # Returns nil on success def set_queue_metadata(queue_name, metadata, options={}) query = { "comp" => "metadata" } query["timeout"] = options[:timeout].to_s if options[:timeout] uri = queue_uri(queue_name, query) headers ={} add_metadata_to_headers(metadata || {}, headers) call(:put, uri, nil, headers) nil end # Public: Gets the access control list (ACL) for the queue. # # ==== Attributes # # * +queue_name+ - String. The queue name. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/jj159101 # # Returns a list of Azure::Entity::SignedIdentifier instances def get_queue_acl(queue_name, options={}) query = { "comp" => "acl" } query["timeout"] = options[:timeout].to_s if options[:timeout] response = call(:get, queue_uri(queue_name, query)) signed_identifiers = [] signed_identifiers = Serialization.signed_identifiers_from_xml(response.body) unless response.body == nil or response.body.length < 1 signed_identifiers end # Public: Sets the access control list (ACL) for the queue. # # ==== Attributes # # * +queue_name+ - String. The queue name. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:signed_identifiers+ - Array. A list of Azure::Entity::SignedIdentifier instances # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/jj159099 # # Returns nil on success def set_queue_acl(queue_name, options={}) query = { "comp" => "acl" } query["timeout"] = options[:timeout].to_s if options[:timeout] uri =queue_uri(queue_name, query) body = nil body = Serialization.signed_identifiers_to_xml(options[:signed_identifiers]) if options[:signed_identifiers] && options[:signed_identifiers].length > 0 call(:put, uri, body, {}) nil end # Public: Adds a message to the queue and optionally sets a visibility timeout for the message. # # ==== Attributes # # * +queue_name+ - String. The queue name. # * +message_text+ - String. The message contents. Note that the message content must be in a format that may be encoded with UTF-8. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:visibility_timeout+ - Integer. Specifies the new visibility timeout value, in seconds, relative to server # time. The new value must be larger than or equal to 0, and cannot be larger than 7 # days. The visibility timeout of a message cannot be set to a value later than the # expiry time. :visibility_timeout should be set to a value smaller than the # time-to-live value. If not specified, the default value is 0. # * +:message_ttl+ - Integer. Specifies the time-to-live interval for the message, in seconds. The maximum # time-to-live allowed is 7 days. If not specified, the default time-to-live is 7 days. # * +:encode+ - Boolean. If set to true, the message will be base64 encoded. # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/dd179346 # # Returns nil on success def create_message(queue_name, message_text, options={}) query = { } unless options.empty? query["visibilitytimeout"] = options[:visibility_timeout] if options[:visibility_timeout] query["messagettl"] = options[:message_ttl] if options[:message_ttl] query["timeout"] = options[:timeout].to_s if options[:timeout] end uri = messages_uri(queue_name, query) body = Serialization.message_to_xml(message_text, options[:encode]) call(:post, uri, body, {}) nil end # Public: Deletes a specified message from the queue. # # ==== Attributes # # * +queue_name+ - String. The queue name. # * +message_id+ - String. The id of the message. # * +pop_receipt+ - String. The valid pop receipt value returned from an earlier call to the Get Messages or # Update Message operation. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/dd179347 # # Returns nil on success # # Remarks: # # When a message is successfully deleted, it is immediately marked for deletion and is no longer accessible to # clients. The message is later removed from the queue during garbage collection. # # After a client retrieves a message with the Get Messages operation, the client is expected to process and # delete the message. To delete the message, you must have two items of data returned in the response body of # the Get Messages operation: # # * The message ID, an opaque GUID value that identifies the message in the queue. # # * A valid pop receipt, an opaque value that indicates that the message has been retrieved. # # The message ID is returned from the previous Get Messages operation. The pop receipt is returned from the most # recent Get Messages or Update Message operation. In order for the Delete Message operation to succeed, the pop # receipt specified on the request must match the pop receipt returned from the Get Messages or Update Message # operation. # # Pop receipts remain valid until one of the following events occurs: # # * The message has expired. # # * The message has been deleted using the last pop receipt received either from Get Messages or Update Message. # # * The invisibility time has elapsed and the message has been dequeued by a Get Messages request. When the # invisibility time elapses, the message becomes visible again. If it is retrieved by another Get Messages # request, the returned pop receipt can be used to delete or update the message. # # * The message has been updated with a new visibility timeout. When the message is updated, a new pop receipt # will be returned. # # If the message has already been deleted when Delete Message is called, the Queue service returns status code # 404 (Not Found). # # If a message with a matching pop receipt is not found, the service returns status code 400 (Bad Request), with # additional error information indicating that the cause of the failure was a mismatched pop receipt. # def delete_message(queue_name, message_id, pop_receipt, options={}) query = { "popreceipt" => pop_receipt } query["timeout"] = options[:timeout].to_s if options[:timeout] uri = message_uri(queue_name, message_id, query) call(:delete, uri) nil end # Public: Retrieves one or more messages from the front of the queue, without changing the message visibility. # # ==== Attributes # # * +queue_name+ - String. The queue name. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:number_of_messages+ - Integer. How many messages to return. (optional, Default: 1) # * +:decode+ - Boolean. Boolean value indicating if the message should be base64 decoded. # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/dd179472 # # Returns a list of Azure::Entity::Queue::Message instances def peek_messages(queue_name, options={}) number_of_messages=1 number_of_messages = options[:number_of_messages] if options[:number_of_messages] query = { "peekonly" => "true", "numofmessages"=> number_of_messages.to_s } query["timeout"] = options[:timeout].to_s if options[:timeout] uri = messages_uri(queue_name, query) response = call(:get, uri) messages = Serialization.queue_messages_from_xml(response.body, options[:decode]) messages end # Public: Retrieves one or more messages from the front of the queue. # # ==== Attributes # # * +queue_name+ - String. The queue name. # * +visibility_timeout+ - Integer. The new visibility timeout value, in seconds, relative to server time. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:number_of_messages+ - Integer. How many messages to return. (optional, Default: 1) # * +:timeout+ - Integer. A timeout in seconds. # * +:decode+ - Boolean. Boolean value indicating if the message should be base64 decoded. # # See http://msdn.microsoft.com/en-us/library/azure/dd179474 # # Returns a list of Azure::Entity::Queue::Message instances def list_messages(queue_name, visibility_timeout, options={}) number_of_messages=1 number_of_messages = options[:number_of_messages] if options[:number_of_messages] query = { "visibilitytimeout" => visibility_timeout.to_s, "numofmessages"=> number_of_messages.to_s } query["timeout"] = options[:timeout].to_s if options[:timeout] uri = messages_uri(queue_name, query) response = call(:get, uri) messages = Serialization.queue_messages_from_xml(response.body, options[:decode]) messages end # Public: Adds a message to the queue and optionally sets a visibility timeout for the message. # # ==== Attributes # # * +queue_name+ - String. The queue name. # * +message_id+ - String. The id of the message. # * +pop_receipt+ - String. The valid pop receipt value returned from an earlier call to the Get Messages or # update Message operation. # * +message_text+ - String. The message contents. Note that the message content must be in a format that may # be encoded with UTF-8. # * +visibility_timeout+ - Integer. The new visibility timeout value, in seconds, relative to server time. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:encode+ - Boolean. If set to true, the message will be base64 encoded. # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/hh452234 # # Returns a tuple of (pop_receipt, time_next_visible) # * pop_receipt - String. The pop receipt of the queue message. # * time_next_visible - String. A UTC date/time value that represents when the message will be visible on the queue. # # Remarks: # # An Update Message operation will fail if the specified message does not exist in the queue, or if the # specified pop receipt does not match the message. # # A pop receipt is returned by the Get Messages operation or the Update Message operation. Pop receipts # remain valid until one of the following events occurs: # # * The message has expired. # # * The message has been deleted using the last pop receipt received either from Get Messages or # Update Message. # # * The invisibility time has elapsed and the message has been dequeued by a Get Messages request. When # the invisibility time elapses, the message becomes visible again. If it is retrieved by another # Get Messages request, the returned pop receipt can be used to delete or update the message. # # * The message has been updated with a new visibility timeout. When the message is updated, a new pop # receipt will be returned. # # The Update Message operation can be used to continually extend the invisibility of a queue message. This # functionality can be useful if you want a worker role to "lease" a queue message. For example, if a worker # role calls Get Messages and recognizes that it needs more time to process a message, it can continually # extend the message's invisibility until it is processed. If the worker role were to fail during processing, # eventually the message would become visible again and another worker role could process it. # def update_message(queue_name, message_id, pop_receipt, message_text, visibility_timeout, options={}) query = { "visibilitytimeout" => visibility_timeout.to_s, "popreceipt" => pop_receipt } query["timeout"] = options[:timeout].to_s if options[:timeout] uri = message_uri(queue_name, message_id, query) body = Serialization.message_to_xml(message_text, options[:encode]) response = call(:put, uri, body, {}) new_pop_receipt = response.headers["x-ms-popreceipt"] time_next_visible = response.headers["x-ms-time-next-visible"] return new_pop_receipt, time_next_visible end # Protected: Generate the URI for the collection of queues. # # ==== Attributes # # * +query+ - A Hash of query parameters (default: {}). # # Returns a URI. protected def collection_uri(query={}) query.update({:comp => 'list', :include => 'metadata'}) generate_uri("", query) end # Protected: Generate the URI for a given queue. # # ==== Attributes # # * +queue_name+ - The name of the queue. # * +query+ - A Hash of query parameters (default: {}). # # Returns a URI. protected def queue_uri(queue_name, query={}) return queue_name if queue_name.kind_of? ::URI generate_uri(queue_name, query) end # Protected: Generate the messages URI for the given queue. # # ==== Attributes # # * +queue_name+ - The name of the queue. # * +query+ - A Hash of query parameters (default: {}). # # Returns a URI. protected def messages_uri(queue_name, query={}) generate_uri("#{queue_name}/messages", query) end # Protected: Generate the URI for a given message # # ==== Attributes # # * +queue_name+ - The name of the queue. # * +message_id+ - The id of the message. # * +query+ - A Hash of query parameters (default: {}). # # Returns a URI. protected def message_uri(queue_name, message_id, query={}) generate_uri("#{queue_name}/messages/#{message_id}", query) end end end end Azure::QueueService = Azure::Queue::QueueService azure-0.7.9/lib/azure/queue/message.rb0000644000004100000410000000214713112322225017673 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- module Azure module Queue class Message def initialize yield self if block_given? end attr_accessor :id attr_accessor :insertion_time attr_accessor :expiration_time attr_accessor :dequeue_count attr_accessor :message_text attr_accessor :time_next_visible attr_accessor :pop_receipt end end endazure-0.7.9/lib/azure/queue/queue.rb0000644000004100000410000000171013112322225017366 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- module Azure module Queue class Queue def initialize @metadata = {} yield self if block_given? end attr_accessor :name attr_accessor :metadata end end endazure-0.7.9/lib/azure/table/0000755000004100000410000000000013112322225015661 5ustar www-datawww-dataazure-0.7.9/lib/azure/table/serialization.rb0000644000004100000410000000717513112322225021075 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/service/serialization' require 'azure/table/guid' require 'azure/table/edmtype' require 'time' require 'date' module Azure module Table module Serialization include Azure::Service::Serialization def self.hash_to_entry_xml(hash, id=nil, xml=Nokogiri::XML::Builder.new(:encoding => 'UTF-8')) entry_namespaces = { 'xmlns' => 'http://www.w3.org/2005/Atom', 'xmlns:m' => 'http://schemas.microsoft.com/ado/2007/08/dataservices/metadata', 'xmlns:d' => 'http://schemas.microsoft.com/ado/2007/08/dataservices' } xml.entry entry_namespaces do |entry| id ? entry.id(id): entry.id entry.updated Time.now.xmlschema entry.title entry.author do |author| author.name end hash_to_content_xml(hash, entry) end xml end def self.hash_to_content_xml(hash, xml=Nokogiri::XML::Builder.new(:encoding => 'UTF-8')) xml.send('content', :type => 'application/xml') do |content| content.send('m:properties') do |properties| hash.each do |key, val| key = key.encode('UTF-8') if key.is_a? String and !key.encoding.names.include?('BINARY') val = val.encode('UTF-8') if val.is_a? String and !val.encoding.names.include?('BINARY') type = Azure::Table::EdmType.property_type(val) attributes = {} attributes['m:type'] = type unless type.nil? || type.empty? if val.nil? attributes['m:null'] = 'true' properties.send("d:#{key}", attributes) else properties.send("d:#{key}", Azure::Table::EdmType.serialize_value(type, val), attributes) end end end end xml end def self.entries_from_feed_xml(xml) xml = slopify(xml) expect_node('feed', xml) return nil unless (xml > 'entry').any? results = [] if (xml > 'entry').count == 0 results.push hash_from_entry_xml((xml > 'entry')) else (xml > 'entry').each do |entry| results.push hash_from_entry_xml(entry) end end results end def self.hash_from_entry_xml(xml) xml = slopify(xml) expect_node('entry', xml) result = {} result[:etag] = xml['etag'] result[:updated] = Time.parse((xml > 'updated').text) if (xml > 'updated').any? properties = {} if (xml > 'content').any? (xml > 'content').first.first_element_child.element_children.each do |prop| properties[prop.name] = prop.text != '' ? Azure::Table::EdmType.unserialize_query_value(prop.text, prop['m:type']) : prop['null'] ? nil : '' end end result[:properties] = properties result end end end endazure-0.7.9/lib/azure/table/entity.rb0000644000004100000410000000200613112322225017520 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- module Azure module Table class Entity def initialize @properties = {} yield self if block_given? end attr_accessor :table attr_accessor :updated attr_accessor :etag attr_accessor :properties end end endazure-0.7.9/lib/azure/table/edmtype.rb0000644000004100000410000000733013112322225017660 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/table/guid' require "time" require "date" module Azure module Table module EdmType # Public: Get the Edm type of an object # # value - Object. An typed instance # # Returns the Edm type as a String def self.property_type(value) case value when Float "Edm.Double" when Date, Time, DateTime "Edm.DateTime" when Integer value.abs < 2**31 ? "Edm.Int32" : "Edm.Int64" when TrueClass, FalseClass "Edm.Boolean" when GUID "Edm.Guid" when IO, File "Edm.Binary" when String value.encoding.names.include?("BINARY") ? "Edm.Binary" : "" else value.kind_of?(IO) ? "Edm.Binary" : "" end end # Public: Get the value of a property in a serialized way # # value - Object. An typed instance # # Returns the Edm type as a String def self.serialize_value(type, value) case type when "Edm.Double", "Edm.Int32", "Edm.Int64", "Edm.Guid", "Edm.String", nil value.to_s when "Edm.Binary" Base64.encode64(value.to_s).chomp("\n") when "Edm.DateTime" value.xmlschema(7) when "Edm.Boolean" if value.nil? '' else value == true ? '1' : '0' end else value.to_s end end # Public: Serializes EDM value into proper value to be used in query. # # value - String. The value to serialize. # # Returns the serialized value def self.serialize_query_value(value) case value when Date, Time, DateTime "datetime'#{value.iso8601}'" when TrueClass, FalseClass value ? "true" : "false" when Float, Integer value.abs < 2**31 ? value.to_s : value.to_s + "L" when GUID "guid'#{value.to_s}'" when IO, File "X'" + value.to_s.unpack("H*").join("") + "'" else if value != nil && value.encoding.names.include?("BINARY") "X'" + value.to_s.unpack("H*").join("") + "'" else # NULL also is treated as EdmType::STRING value.to_s.gsub("'","''"); end end end # Public: Convert a serialized value into an typed object # # value - String. The Edm value # type - String. The Edm datatype # # Returns an typed object def self.unserialize_query_value(value, type) case type when "Edm.DateTime" Time.parse(value) when "Edm.Double" Float(value) when "Edm.Int32", "Edm.Int64" Integer(value) when "Edm.Boolean" /true/i === value when "Edm.Guid" GUID.new(value.to_s) when "Edm.Binary" Base64.decode64(value.to_s).force_encoding("BINARY") else value.to_s end end end end endazure-0.7.9/lib/azure/table/auth/0000755000004100000410000000000013112322225016622 5ustar www-datawww-dataazure-0.7.9/lib/azure/table/auth/shared_key.rb0000644000004100000410000000403213112322225021264 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'cgi' require 'azure/core/auth/signer' module Azure module Table module Auth class SharedKey < Azure::Core::Auth::SharedKey # The account name attr :account_name # Generate the string to sign. # # @param method [Symbol] The HTTP request method. # @param uri [URI] The URI of the request we're signing. # @param headers [Hash] The HTTP request headers. # # Returns a plain text string. def signable_string(method, uri, headers) [ method.to_s.upcase, headers.fetch('Content-MD5', ''), headers.fetch('Content-Type', ''), headers.fetch('Date') { headers.fetch('x-ms-date') }, canonicalized_resource(uri) ].join("\n") end # Calculate the Canonicalized Resource string for a request. # # @param uri [URI] The request's URI. # # @return [String] with the canonicalized resource. def canonicalized_resource(uri) resource = "/#{account_name}#{uri.path}" comp = CGI.parse(uri.query.to_s).fetch('comp', nil) resource = [resource, 'comp=' + comp[0]].join('?') if comp resource end end end end end azure-0.7.9/lib/azure/table/auth/shared_key_lite.rb0000644000004100000410000000275013112322225022306 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/table/auth/shared_key' module Azure module Table module Auth class SharedKeyLite < SharedKey # Public: The name of the strategy. # # Returns a String. def name 'SharedKeyLite' end # Generate the string to sign. # # verb - The HTTP request method. # uri - The URI of the request we're signing. # headers - A Hash of HTTP request headers. # # Returns a plain text string. def signable_string(method, uri, headers) [ headers.fetch('Date') { headers.fetch('x-ms-date') }, canonicalized_resource(uri) ].join("\n") end end end end end azure-0.7.9/lib/azure/table/table_service.rb0000644000004100000410000005112713112322225021023 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/service/storage_service' require 'azure/table/auth/shared_key' require 'azure/table/serialization' require 'azure/table/entity' module Azure module Table class TableService < Azure::Service::StorageService def initialize(options = {}) client_config = options[:client] || Azure signer = options[:signer] || Auth::SharedKey.new(client_config.storage_account_name, client_config.storage_access_key) super(signer, client_config.storage_account_name, options) @host = client.storage_table_host end # Public: Creates new table in the storage account # # ==== Attributes # # * +table_name+ - String. The table name # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/dd135729 # # @return [nil] on success def create_table(table_name, options={}) query = { } query['timeout'] = options[:timeout].to_s if options[:timeout] body = Azure::Table::Serialization.hash_to_entry_xml({"TableName" => table_name}).to_xml call(:post, collection_uri(query), body) nil end # Public: Deletes the specified table and any data it contains. # # ==== Attributes # # * +table_name+ - String. The table name # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/dd179387 # # Returns nil on success def delete_table(table_name, options={}) query = { } query["timeout"] = options[:timeout].to_s if options[:timeout] call(:delete, table_uri(table_name, query)) nil end # Public: Gets the table. # # ==== Attributes # # * +table_name+ - String. The table name # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:timeout+ - Integer. A timeout in seconds. # # Returns the last updated time for the table def get_table(table_name, options={}) query = { } query["timeout"] = options[:timeout].to_s if options[:timeout] response = call(:get, table_uri(table_name, query)) results = Azure::Table::Serialization.hash_from_entry_xml(response.body) results[:updated] end # Public: Gets a list of all tables on the account. # # ==== Attributes # # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:next_table_token+ - String. A token used to enumerate the next page of results, when the list of tables is # larger than a single operation can return at once. (optional) # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/dd179405 # # Returns an array with an extra continuation_token property on success def query_tables(options={}) query = { } query["NextTable"] = options[:next_table_token] if options[:next_table_token] query["timeout"] = options[:timeout].to_s if options[:timeout] uri = collection_uri(query) response = call(:get, uri) entries = Azure::Table::Serialization.entries_from_feed_xml(response.body) || [] values = Azure::Service::EnumerationResults.new(entries) values.continuation_token = response.headers["x-ms-continuation-NextTableName"] values end # Public: Gets the access control list (ACL) for the table. # # ==== Attributes # # * +table_name+ - String. The table name # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/jj159100 # # Returns a list of Azure::Entity::SignedIdentifier instances def get_table_acl(table_name, options={}) query = { 'comp' => 'acl'} query['timeout'] = options[:timeout].to_s if options[:timeout] response = call(:get, generate_uri(table_name, query), nil, {'x-ms-version' => '2012-02-12'}) signed_identifiers = [] signed_identifiers = Azure::Table::Serialization.signed_identifiers_from_xml response.body unless response.body == nil or response.body.length < 1 signed_identifiers end # Public: Sets the access control list (ACL) for the table. # # ==== Attributes # # * +table_name+ - String. The table name # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:signed_identifiers+ - Array. A list of Azure::Entity::SignedIdentifier instances # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/jj159102 # # Returns nil on success def set_table_acl(table_name, options={}) query = { 'comp' => 'acl'} query['timeout'] = options[:timeout].to_s if options[:timeout] uri = generate_uri(table_name, query) body = nil body = Azure::Table::Serialization.signed_identifiers_to_xml options[:signed_identifiers] if options[:signed_identifiers] && options[:signed_identifiers].length > 0 call(:put, uri, body, {'x-ms-version' => '2012-02-12'}) nil end # Public: Inserts new entity to the table. # # # ==== Attributes # # * +table_name+ - String. The table name # * +entity_values+ - Hash. A hash of the name/value pairs for the entity. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/dd179433 # # Returns a Azure::Entity::Table::Entity def insert_entity(table_name, entity_values, options={}) body = Azure::Table::Serialization.hash_to_entry_xml(entity_values).to_xml query = { } query['timeout'] = options[:timeout].to_s if options[:timeout] response = call(:post, entities_uri(table_name, nil, nil, query), body) result = Azure::Table::Serialization.hash_from_entry_xml(response.body) Entity.new do |entity| entity.table = table_name entity.updated = result[:updated] entity.etag = response.headers['etag'] || result[:etag] entity.properties = result[:properties] end end # Public: Queries entities for the given table name # # ==== Attributes # # * +table_name+ - String. The table name # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:partition_key+ - String. The partition key (optional) # * +:row_key+ - String. The row key (optional) # * +:select+ - Array. An array of property names to return (optional) # * +:filter+ - String. A filter expression (optional) # * +:top+ - Integer. A limit for the number of results returned (optional) # * +:continuation_token+ - Hash. The continuation token. # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/dd179421 # # Returns an array with an extra continuation_token property on success def query_entities(table_name, options={}) query ={} query["$select"] = options[:select].join ',' if options[:select] query["$filter"] = options[:filter] if options[:filter] query["$top"] = options[:top].to_s if options[:top] unless options[:partition_key] and options[:row_key] query["NextPartitionKey"] = options[:continuation_token][:next_partition_key] if options[:continuation_token] and options[:continuation_token][:next_partition_key] query["NextRowKey"] = options[:continuation_token][:next_row_key] if options[:continuation_token] and options[:continuation_token][:next_row_key] query["timeout"] = options[:timeout].to_s if options[:timeout] uri = entities_uri(table_name, options[:partition_key], options[:row_key], query) response = call(:get, uri, nil, { "DataServiceVersion" => "2.0;NetFx"}) entities = Azure::Service::EnumerationResults.new results = (options[:partition_key] and options[:row_key]) ? [Azure::Table::Serialization.hash_from_entry_xml(response.body)] : Azure::Table::Serialization.entries_from_feed_xml(response.body) results.each do |result| entity = Entity.new do |e| e.table = table_name e.updated = result[:updated] e.etag = response.headers["etag"] || result[:etag] e.properties = result[:properties] end entities.push entity end if results entities.continuation_token = nil entities.continuation_token = { :next_partition_key=> response.headers["x-ms-continuation-NextPartitionKey"], :next_row_key => response.headers["x-ms-continuation-NextRowKey"] } if response.headers["x-ms-continuation-NextPartitionKey"] entities end # Public: Updates an existing entity in a table. The Update Entity operation replaces # the entire entity and can be used to remove properties. # # ==== Attributes # # * +table_name+ - String. The table name # * +entity_values+ - Hash. A hash of the name/value pairs for the entity. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:if_match+ - String. A matching condition which is required for update (optional, Default="*") # * +:create_if_not_exists+ - Boolean. If true, and partition_key and row_key do not reference and existing entity, # that entity will be inserted. If false, the operation will fail. (optional, Default=false) # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/dd179427 # # Returns the ETag for the entity on success def update_entity(table_name, entity_values, options={}) if_match = "*" if_match = options[:if_match] if options[:if_match] query = { } query["timeout"] = options[:timeout].to_s if options[:timeout] uri = entities_uri(table_name, entity_values["PartitionKey"], entity_values["RowKey"], query) headers = {} headers["If-Match"] = if_match || "*" unless options[:create_if_not_exists] body = Azure::Table::Serialization.hash_to_entry_xml(entity_values).to_xml response = call(:put, uri, body, headers) response.headers["etag"] end # Public: Updates an existing entity by updating the entity's properties. This operation # does not replace the existing entity, as the update_entity operation does. # # ==== Attributes # # * +table_name+ - String. The table name # * +entity_values+ - Hash. A hash of the name/value pairs for the entity. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:if_match+ - String. A matching condition which is required for update (optional, Default="*") # * +:create_if_not_exists+ - Boolean. If true, and partition_key and row_key do not reference and existing entity, # that entity will be inserted. If false, the operation will fail. (optional, Default=false) # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/dd179392 # # Returns the ETag for the entity on success def merge_entity(table_name, entity_values, options={}) if_match = "*" if_match = options[:if_match] if options[:if_match] query = { } query["timeout"] = options[:timeout].to_s if options[:timeout] uri = entities_uri(table_name, entity_values["PartitionKey"], entity_values["RowKey"], query) headers = { "X-HTTP-Method"=> "MERGE" } headers["If-Match"] = if_match || "*" unless options[:create_if_not_exists] body = Azure::Table::Serialization.hash_to_entry_xml(entity_values).to_xml response = call(:post, uri, body, headers) response.headers["etag"] end # Public: Inserts or updates an existing entity within a table by merging new property values into the entity. # # ==== Attributes # # * +table_name+ - String. The table name # * +entity_values+ - Hash. A hash of the name/value pairs for the entity. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/hh452241 # # Returns the ETag for the entity on success def insert_or_merge_entity(table_name, entity_values, options={}) options[:create_if_not_exists] = true merge_entity(table_name, entity_values, options) end # Public: Inserts or updates a new entity into a table. # # ==== Attributes # # * +table_name+ - String. The table name # * +entity_values+ - Hash. A hash of the name/value pairs for the entity. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/hh452242 # # Returns the ETag for the entity on success def insert_or_replace_entity(table_name, entity_values, options={}) options[:create_if_not_exists] = true update_entity(table_name, entity_values, options) end # Public: Deletes an existing entity in the table. # # ==== Attributes # # * +table_name+ - String. The table name # * +partition_key+ - String. The partition key # * +row_key+ - String. The row key # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:if_match+ - String. A matching condition which is required for update (optional, Default="*") # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/dd135727 # # Returns nil on success def delete_entity(table_name, partition_key, row_key, options={}) if_match = "*" if_match = options[:if_match] if options[:if_match] query = { } query["timeout"] = options[:timeout].to_s if options[:timeout] call(:delete, entities_uri(table_name, partition_key, row_key, query), nil, { "If-Match"=> if_match }) nil end # Public: Executes a batch of operations. # # ==== Attributes # # * +batch+ - The Azure::Table::Batch instance to execute. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:timeout+ - Integer. A timeout in seconds. # # See http://msdn.microsoft.com/en-us/library/azure/dd894038 # # Returns an array of results, one for each operation in the batch def execute_batch(batch, options={}) headers = { 'Content-Type' => "multipart/mixed; boundary=#{batch.batch_id}", 'Accept' => 'application/atom+xml,application/xml', 'Accept-Charset'=> 'UTF-8' } query = { } query["timeout"] = options[:timeout].to_s if options[:timeout] body = batch.to_body response = call(:post, generate_uri('/$batch', query), body, headers) batch.parse_response(response) end # Public: Gets an existing entity in the table. # # ==== Attributes # # * +table_name+ - String. The table name # * +partition_key+ - String. The partition key # * +row_key+ - String. The row key # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:timeout+ - Integer. A timeout in seconds. # # Returns an Azure::Table::Entity instance on success def get_entity(table_name, partition_key, row_key, options={}) options[:partition_key] = partition_key options[:row_key] = row_key results = query_entities(table_name, options) results.length > 0 ? results[0] : nil end # Protected: Generate the URI for the collection of tables. # # Returns a URI protected def collection_uri(query={}) generate_uri("Tables", query) end # Public: Generate the URI for a specific table. # # ==== Attributes # # * +name+ - The table name. If this is a URI, we just return this # # Returns a URI public def table_uri(name, query={}) return name if name.kind_of? ::URI generate_uri("Tables('#{name}')", query) end # Public: Generate the URI for an entity or group of entities in a table. # If both the 'partition_key' and 'row_key' are specified, then the URI # will match the entity under those specific keys. # # ==== Attributes # # * +table_name+ - The table name # * +partition_key+ - The desired partition key (optional) # * +row_key+ - The desired row key (optional) # # Returns a URI public def entities_uri(table_name, partition_key=nil, row_key=nil, query={}) return table_name if table_name.kind_of? ::URI path = if partition_key && row_key "%s(PartitionKey='%s',RowKey='%s')" % [ table_name.encode("UTF-8"), encodeODataUriValue(partition_key.encode("UTF-8")), encodeODataUriValue(row_key.encode("UTF-8")) ] else "%s()" % table_name.encode("UTF-8") end uri = generate_uri(path) qs = [] if query query.each do | key, val | key = key.encode("UTF-8") val = val.encode("UTF-8") if key[0] == "$" qs.push "#{key}#{::URI.encode_www_form(""=>val)}" else qs.push ::URI.encode_www_form(key=>val) end end end uri.query = qs.join '&' if qs.length > 0 uri end protected def encodeODataUriValues(values) new_values = [] values.each do |value| new_values.push encodeODataUriValue(value) end new_values end protected def encodeODataUriValue(value) # Replace each single quote (') with double single quotes ('') not double # quotes (") value = value.gsub("'", "''") # Encode the special URL characters value = URI.escape(value) value end end end end Azure::TableService = Azure::Table::TableService azure-0.7.9/lib/azure/table/query.rb0000644000004100000410000000555013112322225017360 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/table/table_service' require 'azure/table/edmtype' module Azure module Table class Query def initialize(table="", partition=nil, row=nil, &block) @table = table @partition_key = partition @row_key = row @fields = [] @filters = [] @top_n = nil @table_service = Azure::Table::TableService.new self.instance_eval(&block) if block_given? end attr_reader :table attr_reader :partition_key attr_reader :row_key attr_reader :fields attr_reader :filters attr_reader :top_n attr_reader :next_partition_key attr_reader :next_row_key attr_reader :table_service def from(table_name) @table = table_name self end def partition(partition_key) @partition_key = partition_key self end def row(row_key) @row_key = row_key self end def select(*p) @fields.concat(p) self end def where(*p) @filters.push(p) self end def top(n) @top_n = n self end def next_partition(next_partition_key) @next_partition_key = next_partition_key self end def next_row(next_row_key) @next_row_key = next_row_key self end def execute @table_service.query_entities(@table, { :partition_key => @partition_key, :row_key => @row_key, :select => @fields.map{ |f| f.to_s }, :filter => _build_filter_string, :top => (@top_n ? @top_n.to_i : @top_n), :continuation_token => { :next_partition_key => @next_partition_key, :next_row_key => @next_row_key } }) end def _build_filter_string result = "" clauses = [] filters.each { |f| clauses.push "#{f[0].to_s} #{f[1].to_s} #{Azure::Table::EdmType.serialize_query_value(f[2])}" } return nil if clauses.length == 0 result << clauses.join(" and ") result end end end endazure-0.7.9/lib/azure/table/batch_response.rb0000644000004100000410000000754513112322225021220 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- module Azure module Table module BatchResponse def self.parse(data) context = { :lines => data.lines.to_a, :index=> 0, :responses => [] } find(context) { |c| batch_boundary c } find(context) { |c| batch_headers c } while(find(context){ |c| changeset_boundary_or_end c } == :boundary) find(context) { |c| changeset_headers c } find(context) { |c| response c } find(context) { |c| response_headers c } find(context) { |c| response_body c } end context[:responses] end def self.find(context, &block) while(context[:index] < context[:lines].length) result = block.call(context) return result if result context[:index] +=1 end end def self.response_body(context) end_of_body = nil end_of_body = changeset_boundary_or_end(context.dup.merge!({:index=>context[:index] + 1})) if context[:index] < (context[:lines].length - 1) if end_of_body context[:responses].last[:body] ||= "" context[:responses].last[:body] << current_line(context) return context[:responses].last[:body] else context[:responses].last[:body] ||= "" context[:responses].last[:body] << current_line(context) return nil end end def self.response_headers(context) match = /(.*): (.*)/.match(current_line(context)) if context[:responses].last[:headers] and not match context[:index] += 1 return context[:responses].last[:headers] elsif match context[:responses].last[:headers] ||= {} context[:responses].last[:headers][match[1].downcase] = match[2].strip return nil else return nil end end def self.response(context) match = /HTTP\/1.1 (\d*) (.*)/.match(current_line(context)) return nil unless match response = {:status_code => match[1], :message => match[2] } context[:responses].push response end def self.changeset_headers(context) current_line(context).strip == '' end def self.changeset_boundary_or_end(context) match_boundary = /--changesetresponse_(.*)/.match(current_line(context)) match_end = /--changesetresponse_(.*)--/.match(current_line(context)) (match_boundary and not match_end) ? :boundary : (match_end ? :end : nil) end def self.batch_headers(context) match = /(.*): (.*)/.match(current_line(context)) if context[:batch_headers] and not match return context[:batch_headers] elsif match context[:batch_headers] ||= {} context[:batch_headers][match[1].downcase] = match[2] return nil else return nil end end def self.batch_boundary(context) match = /--batchresponse_(.*)/.match(current_line(context)) match ? match[1] : nil end def self.current_line(context) context[:lines][context[:index]] end end end end azure-0.7.9/lib/azure/table/guid.rb0000644000004100000410000000163213112322225017140 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require "delegate" module Azure module Table # Public: Wrapper around a string to represent a GUID # class GUID < SimpleDelegator end end endazure-0.7.9/lib/azure/table/batch.rb0000644000004100000410000002522513112322225017275 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'securerandom' require 'azure/table/serialization' require 'azure/table/table_service' require 'azure/table/batch_response' require 'azure/core/http/http_error' module Azure module Table # Represents a batch of table operations. # # Example usage (block syntax): # # results = Batch.new "table", "partition" do # insert "row1", {"meta"=>"data"} # insert "row2", {"meta"=>"data"} # end.execute # # which is equivalent to (fluent syntax): # # results = Batch.new("table", "partition") # .insert("row1", {"meta"=>"data"}) # .insert("row2", {"meta"=>"data"}) # .execute # # which is equivalent to (as class): # # svc = TableSerice.new # # batch = Batch.new "table", "partition" # batch.insert "row1", {"meta"=>"data"} # batch.insert "row2", {"meta"=>"data"} # # results = svc.execute_batch batch # class Batch def initialize(table, partition, &block) @table = table @partition = partition @operations = [] @entity_keys = [] @table_service = Azure::Table::TableService.new @batch_id = "batch_" + SecureRandom.uuid @changeset_id = "changeset_" + SecureRandom.uuid self.instance_eval(&block) if block_given? end private attr_reader :table attr_reader :partition attr_reader :table_service attr_accessor :operations attr_accessor :entity_keys attr_accessor :changeset_id public attr_accessor :batch_id protected def execute @table_service.execute_batch(self) end protected class ResponseWrapper def initialize(hash) @hash = hash end def uri @hash[:uri] end def status_code @hash[:status_code].to_i end def body @hash[:body] end end protected def add_operation(method, uri, body=nil, headers=nil) op = { :method => method, :uri => uri, :body => body, :headers => headers } operations.push op end protected def check_entity_key(key) raise ArgumentError, "Only allowed to perform a single operation per entity, and there is already a operation registered in this batch for the key: #{key}." if entity_keys.include? key entity_keys.push key end public def parse_response(response) responses = BatchResponse.parse response.body new_responses = [] (0..responses.length-1).each { |index| operation = operations[index] response = responses[index] if response[:status_code].to_i > 299 # failed error = Azure::Core::Http::HTTPError.new(ResponseWrapper.new(response.merge({:uri=>operation[:uri]}))) error.description = response[:message] if (error.description || '').strip == '' raise error else # success case operation[:method] when :post # entity from body result = Azure::Table::Serialization.hash_from_entry_xml(response[:body]) entity = Azure::Table::Entity.new entity.table = table entity.updated = result[:updated] entity.etag = response[:headers]["etag"] || result[:etag] entity.properties = result[:properties] new_responses.push entity when :put, :merge # etag from headers new_responses.push response[:headers]["etag"] when :delete # true new_responses.push nil end end } new_responses end public def to_body body = "" body.define_singleton_method(:add_line) do |a| self << (a||nil) + "\n" end body.add_line "--#{batch_id}" body.add_line "Content-Type: multipart/mixed; boundary=#{changeset_id}" body.add_line "" content_id = 1 operations.each { |op| body.add_line "--#{changeset_id}" body.add_line "Content-Type: application/http" body.add_line "Content-Transfer-Encoding: binary" body.add_line "" body.add_line "#{op[:method].to_s.upcase} #{op[:uri]} HTTP/1.1" body.add_line "Content-ID: #{content_id}" if op[:headers] op[:headers].each { |k,v| body.add_line "#{k}: #{v}" } end if op[:body] body.add_line "Content-Type: application/atom+xml;type=entry" body.add_line "Content-Length: #{op[:body].bytesize.to_s}" body.add_line "" body.add_line op[:body] else body.add_line "" end content_id += 1 } body.add_line "--#{changeset_id}--" body.add_line "--#{batch_id}--" end # Public: Inserts new entity to the table. # # ==== Attributes # # * +row_key+ - String. The row key # * +entity_values+ - Hash. A hash of the name/value pairs for the entity. # # See http://msdn.microsoft.com/en-us/library/azure/dd179433 public def insert(row_key, entity_values) check_entity_key(row_key) body = Azure::Table::Serialization.hash_to_entry_xml({ "PartitionKey" => partition, "RowKey" => row_key }.merge(entity_values) ).to_xml add_operation(:post, @table_service.entities_uri(table), body) self end # Public: Updates an existing entity in a table. The Update Entity operation replaces # the entire entity and can be used to remove properties. # # ==== Attributes # # * +row_key+ - String. The row key # * +entity_values+ - Hash. A hash of the name/value pairs for the entity. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * :if_match - String. A matching condition which is required for update (optional, Default="*") # * :create_if_not_exists - Boolean. If true, and partition_key and row_key do not reference and existing entity, # that entity will be inserted. If false, the operation will fail. (optional, Default=false) # # See http://msdn.microsoft.com/en-us/library/azure/dd179427 public def update(row_key, entity_values, options={}) check_entity_key(row_key) uri = @table_service.entities_uri(table, partition, row_key) headers = {} headers["If-Match"] = options[:if_match] || "*" unless options[:create_if_not_exists] body = Azure::Table::Serialization.hash_to_entry_xml(entity_values).to_xml add_operation(:put, uri, body, headers) self end # Public: Updates an existing entity by updating the entity's properties. This operation # does not replace the existing entity, as the update_entity operation does. # # ==== Attributes # # * +row_key+ - String. The row key # * +entity_values+ - Hash. A hash of the name/value pairs for the entity. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +if_match+ - String. A matching condition which is required for update (optional, Default="*") # * +create_if_not_exists+ - Boolean. If true, and partition_key and row_key do not reference and existing entity, # that entity will be inserted. If false, the operation will fail. (optional, Default=false) # # See http://msdn.microsoft.com/en-us/library/azure/dd179392 public def merge(row_key, entity_values, options={}) check_entity_key(row_key) uri = @table_service.entities_uri(table, partition, row_key) headers = {} headers["If-Match"] = options[:if_match] || "*" unless options[:create_if_not_exists] body = Azure::Table::Serialization.hash_to_entry_xml(entity_values).to_xml add_operation(:merge, uri, body, headers) self end # Public: Inserts or updates an existing entity within a table by merging new property values into the entity. # # ==== Attributes # # * +row_key+ - String. The row key # * +entity_values+ - Hash. A hash of the name/value pairs for the entity. # # See http://msdn.microsoft.com/en-us/library/azure/hh452241 public def insert_or_merge(row_key, entity_values) merge(row_key, entity_values, { :create_if_not_exists => true }) self end # Public: Inserts or updates a new entity into a table. # # ==== Attributes # # * +row_key+ - String. The row key # * +entity_values+ - Hash. A hash of the name/value pairs for the entity. # # See http://msdn.microsoft.com/en-us/library/azure/hh452242 public def insert_or_replace(row_key, entity_values) update(row_key, entity_values, { :create_if_not_exists => true }) self end # Public: Deletes an existing entity in the table. # # ==== Attributes # # * +row_key+ - String. The row key # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +if_match+ - String. A matching condition which is required for update (optional, Default="*") # # See http://msdn.microsoft.com/en-us/library/azure/dd135727 public def delete(row_key, options={}) add_operation(:delete, @table_service.entities_uri(table, partition, row_key), nil, {"If-Match"=> options[:if_match] || "*"}) self end end end endazure-0.7.9/lib/azure/virtual_network_management/0000755000004100000410000000000013112322225022225 5ustar www-datawww-dataazure-0.7.9/lib/azure/virtual_network_management/serialization.rb0000644000004100000410000001536313112322225025437 0ustar www-datawww-data#------------------------------------------------------------------------- # Copyright 2013 Microsoft Open Technologies, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/virtual_network_management/virtual_network' module Azure module VirtualNetworkManagement module Serialization extend Azure::Core::Utility def self.virtual_network_from_xml(network_xml) virtual_networks = [] virtual_network_services_xml = network_xml.css( 'VirtualNetworkSites VirtualNetworkSite' ) virtual_network_services_xml.each do |virtual_network_service_xml| virtual_network = VirtualNetwork.new virtual_network.name = xml_content( virtual_network_service_xml, 'Name' ) virtual_network.id = xml_content( virtual_network_service_xml, 'Id' ) virtual_network.affinity_group = xml_content( virtual_network_service_xml, 'AffinityGroup' ) virtual_network.location = xml_content( virtual_network_service_xml, 'Location' ) virtual_network.state = xml_content( virtual_network_service_xml, 'State' ) address_prefixes = virtual_network_service_xml.css( 'AddressSpace AddressPrefixes AddressPrefix' ) virtual_network.address_space = address_prefixes.map do |ap| ap.content end subnets_xml = virtual_network_service_xml.css('Subnets Subnet') virtual_network.subnets = subnets_xml.map do |sn| { name: xml_content(sn, 'Name'), address_prefix: xml_content(sn, 'AddressPrefix') } end dns_servers_xml = virtual_network_service_xml.css( 'Dns DnsServers DnsServer' ) virtual_network.dns_servers = dns_servers_xml.map do |dns| { name: xml_content(dns, 'Name'), ip_address: xml_content(dns, 'Address') } end virtual_networks << virtual_network end virtual_networks.compact end def self.virtual_network_to_xml(vnet_name, location, address_spaces, options = {}) options[:dns] ||= {} options[:subnet] ||= {} options[:subnet].each do |subnet| IPAddr.validate_ip_and_prefix(subnet[:ip_address], subnet[:cidr]) end address_spaces = address_spaces.compact.uniq.reject(&:empty?) address_spaces.each do |address_space| IPAddr.validate_address_space(address_space) end vnet_service = Azure::VirtualNetworkManagementService.new vnets = vnet_service.list_virtual_networks builder = Nokogiri::XML::Builder.new do |xml| xml.NetworkConfiguration( 'xmlns' => 'http://schemas.microsoft.com/ServiceHosting'\ '/2011/07/NetworkConfiguration') do xml.VirtualNetworkConfiguration do xml.Dns do xml.DnsServers do dns = vnets.map(&:dns_servers) dns.flatten! unless dns.empty? dns_server_to_xml(xml, dns, options[:dns]) end end xml.VirtualNetworkSites do vnets.each do |vnet| if vnet.name != vnet_name others = { subnet: vnet.subnets, dns: vnet.dns_servers } virtual_network_site(xml, vnet.name, vnet.location, vnet.affinity_group, vnet.address_space, others) end end virtual_network_site(xml, vnet_name, location, nil, address_spaces, options) end end end end builder.doc.to_xml end private def self.virtual_network_site(xml, vnet_name, location, affinity_group, address_spaces, options) xml.VirtualNetworkSite( 'name' => vnet_name, 'AffinityGroup' => affinity_group, 'Location' => location ) do xml.DnsServersRef do options[:dns].each do |dns| xml.DnsServerRef('name' => dns[:name]) end end xml.Subnets do options[:subnet].each do |subnet| xml.Subnet('name' => subnet[:name]) do if subnet.key?(:cidr) address_prefix = IPAddr.address_prefix(subnet[:ip_address], subnet[:cidr]) else address_prefix = subnet[:address_prefix] end xml.AddressPrefix address_prefix end end end xml.AddressSpace do address_spaces.each do |address_space| xml.AddressPrefix address_space end end end end def self.dns_server_to_xml(xml, dns_servers, new_dns_servers) dns_list = {} dns_list.merge!(merge_dns(new_dns_servers)) dns_list.merge!(merge_dns(dns_servers)) dns_list.each do |name, ip_address| xml.DnsServer('name' => name, 'IPAddress' => ip_address) end end def self.merge_dns(dns_list) new_dns_list = {} dns_list.each do |dns| unless dns_list.include?(dns[:name]) new_dns_list.merge!(dns[:name] => dns[:ip_address]) end end new_dns_list end end end end azure-0.7.9/lib/azure/virtual_network_management/virtual_network.rb0000644000004100000410000000234513112322225026015 0ustar www-datawww-data#------------------------------------------------------------------------- # Copyright 2013 Microsoft Open Technologies, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- module Azure module VirtualNetworkManagement # Represents a Virtual Network class VirtualNetwork def initialize yield self if block_given? @address_space = [] @subnets = [] @dns_servers = [] end attr_accessor :name attr_accessor :state attr_accessor :id attr_accessor :address_space attr_accessor :subnets attr_accessor :dns_servers attr_accessor :affinity_group attr_accessor :location end end end azure-0.7.9/lib/azure/virtual_network_management/virtual_network_management_service.rb0000644000004100000410000001045713112322225031734 0ustar www-datawww-data#------------------------------------------------------------------------- # Copyright 2013 Microsoft Open Technologies, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/virtual_network_management/serialization' module Azure module VirtualNetworkManagement # VirtualNetworkManagementService class VirtualNetworkManagementService < BaseManagement::BaseManagementService # Public: Gets a list of virtual network services available under the # current subscription. # # See http://msdn.microsoft.com/en-us/library/azure/jj157185.aspx # # Returns an array of # Azure::VirtualNetworkServiceManagement::VirtualNetwork objects def list_virtual_networks request_path = '/services/networking/virtualnetwork' request = client.management_request(:get, request_path, nil) response = request.call Serialization.virtual_network_from_xml(response) end def set_network_configuration(*args) if args.length == 1 set_virtual_network_using_xml(args[0]) elsif args.length == 4 || args.length == 3 args[3] ||= { subnet: [], dns: [] } set_virtual_network(args[0], args[1], args[2], args[3]) else raise 'Wrong number of arguments' end end private # Private: Configures virtual network. # # ==== Attributes # # * +vnet+ - String. The name of the virtual network. # * +location+ - String. The location where the virtual network will be created. # * +address_space+ - Array. Contains a collection of Classless # Inter-Domain Routing (CIDR) identifiers that specify the address # space that you will use for your local network site # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:subnets+ - Array. A hash of the name/value pairs. # Contains the name, IPv4 address and Cidr of the DNS server. # (optional) # * +dns_servers+ - Array. A hash of the name/value pairs. # Contains the name and IPv4 address of the DNS server. (optional) # # See http://msdn.microsoft.com/en-us/library/azure/jj157181.aspx # # Returns None def set_virtual_network(vnet, location, address_space, options = { subnet: [], dns: [] }) request_path = '/services/networking/media' body = Serialization.virtual_network_to_xml(vnet, location, address_space, options) request = client.management_request(:put, request_path, body) request.headers['Content-Type'] = 'text/plain' Azure::Loggerx.info "Creating virtual network #{vnet}." request.call end def set_virtual_network_using_xml(file) request_path = '/services/networking/media' if file !~ /(xml)$/ raise 'File expects a .xml extension.' elsif !File.exist?(file) raise "Could not read from file '#{file}'." else body = File.read(file) end request = client.management_request(:put, request_path, body) request.headers['Content-Type'] = 'text/plain' Azure::Loggerx.info 'Creating virtual network.' request.call end end end end Azure::VirtualNetworkManagementService = Azure::VirtualNetworkManagement::VirtualNetworkManagementServiceazure-0.7.9/lib/azure/service/0000755000004100000410000000000013112322225016232 5ustar www-datawww-dataazure-0.7.9/lib/azure/service/serialization.rb0000644000004100000410000002514413112322225021442 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'nokogiri' require 'azure/service/enumeration_results' require 'azure/service/signed_identifier' require 'azure/service/access_policy' require 'azure/service/storage_service_properties' require 'azure/service/logging' require 'azure/service/metrics' require 'azure/service/retention_policy' require 'azure/service/cors' require 'azure/service/cors_rule' module Azure module Service module Serialization module ClassMethods def signed_identifiers_from_xml(xml) xml = slopify(xml) expect_node("SignedIdentifiers", xml) identifiers = [] return identifiers unless (xml > "SignedIdentifier").any? if xml.SignedIdentifier.count == 0 identifiers.push(signed_identifier_from_xml(xml.SignedIdentifier)) else xml.SignedIdentifier.each { |identifier_node| identifiers.push(signed_identifier_from_xml(identifier_node)) } end identifiers end def signed_identifiers_to_xml(signed_identifiers) builder = Nokogiri::XML::Builder.new(:encoding=>"utf-8") do |xml| xml.SignedIdentifiers { signed_identifiers.each do |identifier| xml.SignedIdentifier { xml.Id identifier.id xml.AccessPolicy { xml.Start identifier.access_policy.start xml.Expiry identifier.access_policy.expiry xml.Permission identifier.access_policy.permission } } end } end builder.to_xml end def signed_identifier_from_xml(xml) xml = slopify(xml) expect_node("SignedIdentifier", xml) SignedIdentifier.new do |identifier| identifier.id = xml.Id.text if (xml > "Id").any? identifier.access_policy = access_policy_from_xml(xml.AccessPolicy) if (xml > "AccessPolicy").any? end end def access_policy_from_xml(xml) xml = slopify(xml) expect_node("AccessPolicy", xml) AccessPolicy.new do |policy| policy.start = xml.Start.text if (xml > "Start").any? policy.expiry = xml.Expiry.text if (xml > "Expiry").any? policy.permission = xml.Permission.text if (xml > "Permission").any? end end def enumeration_results_from_xml(xml, results) xml = slopify(xml) expect_node("EnumerationResults", xml) results = results || EnumerationResults.new; results.continuation_token = xml.NextMarker.text if (xml > "NextMarker").any? results end def metadata_from_xml(xml) xml = slopify(xml) expect_node("Metadata", xml) metadata = {} xml.children.each { |meta_node| key = meta_node.name.downcase if metadata.has_key? key metadata[key] = [metadata[key]] unless metadata[key].respond_to? :push metadata[key].push(meta_node.text) else metadata[key] = meta_node.text end } metadata end def metadata_from_headers(headers) metadata = {} headers.each { |k, v| if key = k[/^x-ms-meta-(.*)/, 1] if metadata.has_key? key metadata[key] = [metadata[key]] unless metadata[key].respond_to? :push metadata[key].push(v) else metadata[key] = v end end } metadata end def retention_policy_to_xml(retention_policy, xml) xml.RetentionPolicy { xml.Enabled retention_policy.enabled unless retention_policy.enabled == nil xml.Days retention_policy.days if retention_policy.days } end def retention_policy_from_xml(xml) xml = slopify(xml) expect_node("RetentionPolicy", xml) RetentionPolicy.new do |policy| policy.enabled = to_bool(xml.Enabled.text) if (xml > "Enabled").any? policy.days = xml.Days.text.to_i if (xml > "Days").any? end end def metrics_to_xml_children(metrics, xml) xml.Version metrics.version if metrics.version xml.Enabled metrics.enabled unless metrics.enabled == nil xml.IncludeAPIs metrics.include_apis unless metrics.include_apis == nil retention_policy_to_xml(metrics.retention_policy, xml) if metrics.retention_policy end def hour_metrics_to_xml(metrics, xml) xml.HourMetrics { metrics_to_xml_children(metrics, xml) } end def minute_metrics_to_xml(metrics, xml) xml.MinuteMetrics { metrics_to_xml_children(metrics, xml) } end def metrics_from_xml(xml) xml = slopify(xml) Metrics.new do |metrics| metrics.version = xml.Version.text if (xml > "Version").any? metrics.enabled = to_bool(xml.Enabled.text) if (xml > "Enabled").any? metrics.include_apis = to_bool(xml.IncludeAPIs.text) if (xml > "IncludeAPIs").any? metrics.retention_policy = retention_policy_from_xml(xml.RetentionPolicy) end end def logging_to_xml(logging, xml) xml.Logging { xml.Version logging.version if logging.version xml.Delete logging.delete unless logging.delete == nil xml.Read logging.read unless logging.read == nil xml.Write logging.write unless logging.write == nil retention_policy_to_xml(logging.retention_policy, xml) if logging.retention_policy } end def logging_from_xml(xml) xml = slopify(xml) expect_node("Logging", xml) Logging.new do |logging| logging.version = xml.Version.text if (xml > "Version").any? logging.delete = to_bool(xml.Delete.text) if (xml > "Delete").any? logging.read = to_bool(xml.Read.text) if (xml > "Read").any? logging.write = to_bool(xml.Write.text) if (xml > "Write").any? logging.retention_policy = retention_policy_from_xml(xml.RetentionPolicy) end end def cors_to_xml(cors, xml) xml.Cors { cors.cors_rules.to_a.each do |cors_rule| cors_rule_to_xml(cors_rule, xml) end } end def cors_rule_to_xml(cors_rule, xml) xml.CorsRule { xml.AllowedOrigins cors_rule.allowed_origins.join(",") if cors_rule.allowed_origins xml.AllowedMethods cors_rule.allowed_methods.join(",") if cors_rule.allowed_methods xml.MaxAgeInSeconds cors_rule.max_age_in_seconds if cors_rule.max_age_in_seconds xml.ExposedHeaders cors_rule.exposed_headers.join(",") if cors_rule.exposed_headers xml.AllowedHeaders cors_rule.allowed_headers.join(",") if cors_rule.allowed_headers } end def cors_from_xml(xml) xml = slopify(xml) expect_node("Cors", xml) Cors.new do |cors| cors.cors_rules = xml.children.to_a.map {|child| cors_rule_from_xml(child)} end end def cors_rule_from_xml(xml) xml = slopify(xml) expect_node("CorsRule", xml) CorsRule.new do |cors_rule| cors_rule.allowed_origins = ary_from_node(xml.AllowedOrigins) if (xml > "AllowedOrigins").any? cors_rule.allowed_methods = ary_from_node(xml.AllowedMethods) if (xml > "AllowedMethods").any? cors_rule.max_age_in_seconds = xml.MaxAgeInSeconds.text.to_i if (xml > "MaxAgeInSeconds").any? cors_rule.exposed_headers = ary_from_node(xml.ExposedHeaders) if (xml > "ExposedHeaders").any? cors_rule.allowed_headers = ary_from_node(xml.AllowedHeaders) if (xml > "AllowedHeaders").any? end end def ary_from_node(node) node.text.split(",").map {|s| s.strip} end def service_properties_to_xml(properties) builder = Nokogiri::XML::Builder.new(:encoding => 'utf-8') do |xml| xml.StorageServiceProperties { xml.DefaultServiceVersion(properties.default_service_version) if properties.default_service_version logging_to_xml(properties.logging, xml) if properties.logging hour_metrics_to_xml(properties.hour_metrics, xml) if properties.hour_metrics minute_metrics_to_xml(properties.minute_metrics, xml) if properties.minute_metrics cors_to_xml(properties.cors, xml) if properties.cors } end builder.to_xml end def service_properties_from_xml(xml) xml = slopify(xml) expect_node("StorageServiceProperties", xml) StorageServiceProperties.new do |props| props.default_service_version = xml.DefaultServiceVersion.text if (xml > "DefaultServiceVersion").any? props.logging = logging_from_xml(xml.Logging) props.hour_metrics = metrics_from_xml(xml.HourMetrics) props.minute_metrics = metrics_from_xml(xml.MinuteMetrics) props.cors = cors_from_xml(xml.Cors) end end def to_bool(s) (s || "").downcase == 'true' end def slopify(xml) node = (xml.is_a? String) ? Nokogiri.Slop(xml).root : xml node.slop! if node.is_a? Nokogiri::XML::Document unless node.respond_to? :method_missing node = node.root if node.is_a? Nokogiri::XML::Document node end def expect_node(node_name, xml) raise "Xml is not a #{node_name} node." unless xml.name == node_name end end extend ClassMethods def self.included( other ) other.extend( ClassMethods ) end end end endazure-0.7.9/lib/azure/service/access_policy.rb0000644000004100000410000000172713112322225021406 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- module Azure module Service class AccessPolicy def initialize yield self if block_given? end attr_accessor :start attr_accessor :expiry attr_accessor :permission end end endazure-0.7.9/lib/azure/service/logging.rb0000644000004100000410000000215213112322225020205 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/service/retention_policy' module Azure module Service class Logging def initialize @retention_policy = RetentionPolicy.new yield self if block_given? end attr_accessor :version attr_accessor :delete attr_accessor :read attr_accessor :write attr_accessor :retention_policy end end endazure-0.7.9/lib/azure/service/signed_identifier.rb0000644000004100000410000000202213112322225022226 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/service/access_policy' module Azure module Service class SignedIdentifier def initialize @access_policy = AccessPolicy.new yield self if block_given? end attr_accessor :id attr_accessor :access_policy end end endazure-0.7.9/lib/azure/service/storage_service_properties.rb0000644000004100000410000000243013112322225024216 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/service/logging' require 'azure/service/metrics' require 'azure/service/cors' module Azure module Service class StorageServiceProperties def initialize @logging = Logging.new @hour_metrics = Metrics.new @minute_metrics = Metrics.new @cors = Cors.new yield self if block_given? end attr_accessor :logging attr_accessor :hour_metrics attr_accessor :minute_metrics attr_accessor :cors attr_accessor :default_service_version end end end azure-0.7.9/lib/azure/service/metrics.rb0000644000004100000410000000213013112322225020221 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/service/retention_policy' module Azure module Service class Metrics def initialize @retention_policy = RetentionPolicy.new yield self if block_given? end attr_accessor :version attr_accessor :enabled attr_accessor :include_apis attr_accessor :retention_policy end end endazure-0.7.9/lib/azure/service/enumeration_results.rb0000644000004100000410000000156413112322225022674 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- module Azure module Service class EnumerationResults < Array attr_accessor :continuation_token end end endazure-0.7.9/lib/azure/service/cors.rb0000644000004100000410000000024213112322225017523 0ustar www-datawww-datamodule Azure module Service class Cors def initialize yield self if block_given? end attr_accessor :cors_rules end end end azure-0.7.9/lib/azure/service/retention_policy.rb0000644000004100000410000000167213112322225022153 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- module Azure module Service class RetentionPolicy def initialize yield self if block_given? end attr_accessor :enabled attr_accessor :days end end endazure-0.7.9/lib/azure/service/cors_rule.rb0000644000004100000410000000050213112322225020551 0ustar www-datawww-datamodule Azure module Service class CorsRule def initialize yield self if block_given? end attr_accessor :allowed_origins attr_accessor :allowed_methods attr_accessor :max_age_in_seconds attr_accessor :exposed_headers attr_accessor :allowed_headers end end end azure-0.7.9/lib/azure/service/storage_service.rb0000644000004100000410000000643213112322225021750 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/core/signed_service' module Azure module Service # A base class for StorageService implementations class StorageService < Azure::Core::SignedService # Create a new instance of the StorageService # # @param signer [Azure::Core::Auth::Signer] An implementation of Signer used for signing requests. # (optional, Default=Azure::Core::Auth::SharedKey.new) # @param account_name [String] The account name (optional, Default=Azure.config.storage_account_name) # @param options [Azure::Configurable] the client configuration context def initialize(signer=Core::Auth::SharedKey.new, account_name=nil, options = {}) super(signer, account_name, options) end # Public: Get Storage Service properties # # See http://msdn.microsoft.com/en-us/library/azure/hh452239 # See http://msdn.microsoft.com/en-us/library/azure/hh452243 # # Returns a Hash with the service properties or nil if the operation failed def get_service_properties uri = service_properties_uri response = call(:get, uri, nil, service_properties_headers) Serialization.service_properties_from_xml response.body end # Public: Set Storage Service properties # # service_properties - An instance of Azure::Entity::Service::StorageServiceProperties # # See http://msdn.microsoft.com/en-us/library/azure/hh452235 # See http://msdn.microsoft.com/en-us/library/azure/hh452232 # # Returns boolean indicating success. def set_service_properties(service_properties) body = Serialization.service_properties_to_xml service_properties uri = service_properties_uri call(:put, uri, body, service_properties_headers) nil end # Public: Generate the URI for the service properties # # query - see Azure::Services::GetServiceProperties#call documentation. # # Returns a URI. def service_properties_uri(query={}) query.update(restype: 'service', comp: 'properties') generate_uri('', query) end # Adds metadata properties to header hash with required prefix # # metadata - A Hash of metadata name/value pairs # headers - A Hash of HTTP headers def add_metadata_to_headers(metadata, headers) metadata.each do |key, value| headers["x-ms-meta-#{key}"] = value end end def service_properties_headers {'x-ms-version' => '2014-02-14'} end end end endazure-0.7.9/lib/azure/http_client.rb0000644000004100000410000000554213112322225017442 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- module Azure module HttpClient # Creates a new management request for the current configuration # @param method [Symbol] the HTTP method # @param path [URI] the path to the management resource # @param options_or_body [Hash|Body] options which can include body def management_request(method, path, options_or_body = {}) options_or_body ||= {} options = if options_or_body.is_a?(Hash) options_or_body else {body: options_or_body} end BaseManagement::ManagementHttpRequest.new(method, path, {client: self}.merge(options)) end # Creates a new management request for the current configuration # @param method [Symbol] the HTTP method # @param path [URI] the path to the management resource # @param options_or_body [Hash|Body] options which can include body def sql_management_request(method, path, options_or_body = {}) options_or_body ||= {} options = if options_or_body.is_a?(Hash) options_or_body else {body: options_or_body} end puts [method, path, options] BaseManagement::SqlManagementHttpRequest.new(method, path, {client: self}.merge(options)) end # Returns the http agent based on uri # @param uri [URI|String] the base uri (scheme, host, port) of the http endpoint # @return [Net::HTTP] http agent for a given uri def agents(uri) ssl_options = {} uri = URI.parse(uri) if uri.is_a?(String) if uri.scheme.downcase == 'https' ssl_options[:ca_file] = self.ca_file if self.ca_file ssl_options[:verify] = true end proxy_options = if ENV['HTTP_PROXY'] || ENV['HTTPS_PROXY'] ENV['HTTP_PROXY'] ? URI::parse(ENV['HTTP_PROXY']) : URI::parse(ENV['HTTPS_PROXY']) end || nil Faraday.new(uri, ssl: ssl_options, proxy: proxy_options) do |conn| conn.use FaradayMiddleware::FollowRedirects conn.adapter Faraday.default_adapter end end end endazure-0.7.9/lib/azure/storage_management/0000755000004100000410000000000013112322225020432 5ustar www-datawww-dataazure-0.7.9/lib/azure/storage_management/serialization.rb0000644000004100000410000001755213112322225023646 0ustar www-datawww-data#------------------------------------------------------------------------- # Copyright 2013 Microsoft Open Technologies, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'base64' require 'azure/storage_management/storage_account' module Azure module StorageManagement # Storage management serialization module is responsible for converting # the objects to XML and vice versa. module Serialization extend Azure::Core::Utility def self.storage_services_to_xml(name, options = {}) builder = Nokogiri::XML::Builder.new do |xml| xml.CreateStorageServiceInput( 'xmlns' => 'http://schemas.microsoft.com/windowsazure' ) do xml.ServiceName(name) label = options[:label] || name xml.Label(Base64.encode64(label)) xml.Description options[:description]\ || 'Explicitly created storage service' unless options[:affinity_group_name].nil? xml.AffinityGroup options[:affinity_group_name] else xml.Location options[:location] end add_options_to_xml(xml, options) end end builder.doc.to_xml end def self.storage_services_from_xml(storage_xml) storage_accounts = [] storage_services_xml = storage_xml.css('StorageService') storage_services_xml.each do |storage_service_xml| storage_account = StorageAccount.new storage_account.url = xml_content(storage_service_xml, 'Url') storage_account.name = xml_content( storage_service_xml, 'ServiceName' ) storage_service_properties = storage_service_xml.css( 'StorageServiceProperties' ) storage_account.description = xml_content( storage_service_properties, 'Description' ) storage_account.affinity_group = xml_content( storage_service_properties, 'AffinityGroup' ) storage_account.location = xml_content( storage_service_properties, 'Location' ) storage_account.label = Base64.decode64( xml_content(storage_service_properties, 'Label') ) storage_account.status = xml_content( storage_service_properties, 'Status' ) storage_account.endpoints = storage_service_properties.css( 'Endpoints Endpoint' ).map { |endpoint| endpoint.content } storage_account.geo_replication_enabled = xml_content( storage_service_properties, 'GeoReplicationEnabled' ) storage_account.account_type = xml_content( storage_service_properties, 'AccountType' ) storage_account.geo_primary_region = xml_content( storage_service_properties, 'GeoPrimaryRegion' ) storage_account.status_of_primary = xml_content( storage_service_properties, 'StatusOfPrimary' ) storage_account.last_geo_failover_time = xml_content( storage_service_properties, 'LastGeoFailoverTime' ) storage_account.geo_secondary_region = xml_content( storage_service_properties, 'GeoSecondaryRegion' ) storage_account.status_of_secondary = xml_content( storage_service_properties, 'StatusOfSecondary' ) storage_account.creation_time = xml_content( storage_service_properties, 'CreationTime' ) storage_account.extended_properties = storage_service_xml.css( 'ExtendedProperties ExtendedProperty' ).map do |prop| { name: xml_content(prop, 'Name'), value: xml_content(prop, 'Value') } end storage_accounts << storage_account end storage_accounts.compact end def self.storage_update_to_xml(options) # Cannot update if options is nil or empty fail 'No options specified' if options.empty? # Either one of Label, or Description is required. if (options[:label].nil? || options[:label].empty?) && (options[:description].nil? || options[:description].empty?) fail 'Either one of Label or Description'\ ' has to be provided. Both cannot be empty' end # The input param may not be nil or empty, but the values inside may # be. Check if atleast one value exists before proceeding. is_empty = true options.each do |option, value| case option when :description, :label is_empty = value.nil? || value.empty? when :geo_replication_enabled is_empty = !(value.is_a?(TrueClass) || value.is_a?(FalseClass)) when :extended_properties value.each do |p, v| is_empty = ((p.nil? || p.empty?) || (v.nil? || v.empty?)) break unless is_empty end end break unless is_empty end # Raise a RuntimeError if no options were provided fail 'No Options Specified' if is_empty builder = Nokogiri::XML::Builder.new do |xml| xml.UpdateStorageServiceInput( 'xmlns' => 'http://schemas.microsoft.com/windowsazure' ) do # Check if label is nil. Use description only if label is nil if options[:label].nil? || options[:label].empty? desc = options[:description] xml.Description(desc) unless desc.nil? || desc.empty? else label = Base64.encode64(options[:label]) xml.Label(label) unless label.nil? || label.empty? end add_options_to_xml(xml, options) end end builder.to_xml end def self.add_options_to_xml(xml, options = {}) gre = options[:geo_replication_enabled] xml.GeoReplicationEnabled( gre ) unless gre.nil? || !(gre.is_a?(TrueClass) || gre.is_a?(FalseClass)) xml.ExtendedProperties do options[:extended_properties].each do |name, value| xml.ExtendedProperty do xml.Name name xml.Value value end unless (name.to_s.empty?) || (value.to_s.empty?) end end unless options[:extended_properties].to_s.empty? xml.AccountType options[:account_type] if options[:account_type] end def self.storage_account_keys_from_xml(storage_xml) storage_xml.css('StorageService') storage_service_xml = storage_xml.css('StorageService').first service_key_xml = storage_service_xml.css('StorageServiceKeys').first storage_account_keys = StorageAccountKeys.new storage_account_keys.url = xml_content(storage_service_xml, 'Url') storage_account_keys.primary_key = xml_content(service_key_xml, 'Primary') storage_account_keys.secondary_key = xml_content(service_key_xml, 'Secondary') storage_account_keys end def self.regenerate_storage_account_keys_to_xml(key_type) builder = Nokogiri::XML::Builder.new do |xml| xml.RegenerateKeys( 'xmlns' => 'http://schemas.microsoft.com/windowsazure' ) do xml.KeyType(key_type) end end builder.doc.to_xml end end end end azure-0.7.9/lib/azure/storage_management/storage_management_service.rb0000644000004100000410000002102013112322225026332 0ustar www-datawww-data#------------------------------------------------------------------------- # Copyright 2013 Microsoft Open Technologies, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/storage_management/serialization' module Azure module StorageManagement # Provides Storage Management API class StorageManagementService < BaseManagement::BaseManagementService # Public: Gets a list of storage accounts available under the # current subscription. # # Returns an array of Azure::StorageManagement::StorageAccount objects def list_storage_accounts request_path = '/services/storageservices' request = client.management_request(:get, request_path) response = request.call Serialization.storage_services_from_xml(response) end # Public: Checks to see if the specified storage account is available # # ==== Attributes # # * +name+ - String. Storage account name. # # Returns an Azure::StorageManagement::StorageAccount instance def get_storage_account(name) list_storage_accounts.select { |x| x.name.casecmp(name.to_s) == 0 }.first end # Public: Gets the properties of the storage account specified. # # ==== Attributes # # * +name+ - String. The name of the storage account. Required. # # See http://msdn.microsoft.com/en-us/library/azure/ee460802.aspx # # Returns the storage account def get_storage_account_properties(name) request_path = "/services/storageservices/#{name}" request = client.management_request(:get, request_path) response = request.call Serialization.storage_services_from_xml(response).first end # Public: Create a new storage account in Microsoft Azure. # # ==== Attributes # # * +name+ - String. The name of the storage service. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:label+ - String. The label for this storage account. The name will # be used as label if none specified. (optional) # * +:location+ - String. The location where the storage # service will be created. Reqruied if no affinity_group_name specified. # * +:description+ - String. A description for the storage # service. (optional) # * +:affinity_group_name+ - String. The name of an existing affinity group # in the specified subscription. Required if no location specified. # * +:geo_replication_enabled+ - String. A flag indicating wheter to # turn Geo replication on or off. Values other than 'true'/'false' # will result in an error from the REST API. (optional) # * +:extended_properties+ - Hash. Key/Value pairs of extended # properties to add to the storage account. The key is used as the # property name and the value as its value. (optional) # * +:account_type+ - String. Specifies the type of storage account # # See http://msdn.microsoft.com/en-us/library/azure/hh264518.aspx # # Returns None def create_storage_account(name, options = {}) raise 'Name not specified' if !name || name.class != String || name.empty? options[:account_type] ||= 'Standard_GRS' if get_storage_account(name) Azure::Loggerx.warn "Storage Account #{name} already exists. Skipped..." else Azure::Loggerx.info "Creating Storage Account #{name}." body = Serialization.storage_services_to_xml(name, options) request_path = '/services/storageservices' request = client.management_request(:post, request_path, body: body) request.call end end # Public: Updates an existing storage account in Microsoft Azure # # ==== Attributes # # * +name+ - String. The name of the storage service. # * +options+ - Hash. parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:label+ - String. A label for the storage service. Required if no # description is provided. If both label and description are # provided, only the label will get updated. # * +:description+ - String. A description for the storage service. # Required if no label is provided. If both label and description are # provided, only the label will get updated. # * +:geo_replication_enabled+ - Boolean (TrueClass/FalseClass). Boolean # flag indicating whether to turn Geo replication on or off. (optional) # * +:extended_properties+ - Hash. Key/Value pairs of extended # properties to add to the storage account. The key is used as the # property name and the value as its value. (optional) # * +:account_type+ - String. Specifies the type of storage account # # See http://msdn.microsoft.com/en-us/library/azure/hh264516.aspx # # Returns None # Fails with RuntimeError if invalid options specified def update_storage_account(name, options) if get_storage_account name Azure::Loggerx.info "Account '#{name}' exists, updating..." body = Serialization.storage_update_to_xml options request_path = "/services/storageservices/#{name}" request = client.management_request(:put, request_path, body) request.call else Azure::Loggerx.warn "Storage Account '#{name}' does not exist. Skipped..." end end # Public: Deletes the specified storage account of given subscription id # from Microsoft Azure. # # ==== Attributes # # * +name+ - String. Storage account name. # # See http://msdn.microsoft.com/en-us/library/azure/hh264517.aspx # # Returns: None def delete_storage_account(name) Azure::Loggerx.info "Deleting Storage Account #{name}." request_path = "/services/storageservices/#{name}" request = client.management_request(:delete, request_path) request.call rescue => e e.message end # Public: Regenerates the primary or secondary access key for the specified storage account # # ==== Attributes # # * +name+ - String. Storage account name. # * +key_type+ - String. Specifies which key(primary or secondary) to regenerate # # Returns an Azure::StorageManagement::StorageAccountKeys instance. # # See: # http://msdn.microsoft.com/en-us/library/azure/ee460795.aspx def regenerate_storage_account_keys(name, key_type = 'primary') if get_storage_account name path = "/services/storageservices/#{name}/keys?action=regenerate" body = Serialization.regenerate_storage_account_keys_to_xml key_type request = client.management_request(:post, path, body) response = request.call Serialization.storage_account_keys_from_xml(response) else Azure::Loggerx.warn "Storage Account '#{name}' does not exist." end end # Public: Gets the primary and secondary access keys for the specified storage account. # # ==== Attributes # # * +name+ - String. Storage account name. # # Returns an Azure::StorageManagement::StorageAccountKeys instance. # # See: # http://msdn.microsoft.com/en-us/library/azure/ee460785.aspx def get_storage_account_keys(name) if get_storage_account name path = "/services/storageservices/#{name}/keys" request = client.management_request(:get, path) response = request.call Serialization.storage_account_keys_from_xml(response) else Azure::Loggerx.warn "Storage Account '#{name}' does not exist." end end end end end Azure::StorageManagementService = Azure::StorageManagement::StorageManagementService azure-0.7.9/lib/azure/storage_management/storage_account.rb0000644000004100000410000000327413112322225024145 0ustar www-datawww-data#------------------------------------------------------------------------- # Copyright 2013 Microsoft Open Technologies, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- module Azure module StorageManagement # Represents a Microsoft Azure storage account class StorageAccount def initialize yield self if block_given? end attr_accessor :url attr_accessor :name attr_accessor :description attr_accessor :location attr_accessor :affinity_group attr_accessor :label attr_accessor :status attr_accessor :endpoints attr_accessor :geo_replication_enabled attr_accessor :geo_primary_region attr_accessor :status_of_primary attr_accessor :last_geo_failover_time attr_accessor :geo_secondary_region attr_accessor :status_of_secondary attr_accessor :creation_time attr_accessor :extended_properties attr_accessor :account_type end # Represents Windows Azure storage account keys class StorageAccountKeys attr_accessor :url attr_accessor :primary_key attr_accessor :secondary_key end end end azure-0.7.9/lib/azure/sql_database_management/0000755000004100000410000000000013112322225021411 5ustar www-datawww-dataazure-0.7.9/lib/azure/sql_database_management/serialization.rb0000644000004100000410000001022513112322225024613 0ustar www-datawww-data#------------------------------------------------------------------------- # Copyright 2013 Microsoft Open Technologies, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/sql_database_management/sql_server' require 'azure/sql_database_management/firewall_rule' module Azure module SqlDatabaseManagement module Serialization extend Azure::Core::Utility def self.server_to_xml(login, password, location, version = 12.0) builder = Nokogiri::XML::Builder.new do |xml| xml.Server('xmlns' => 'http://schemas.microsoft.com/sqlazure/2010/12/') { xml.AdministratorLogin login xml.AdministratorLoginPassword password xml.Location location xml.Version version } end builder.doc.to_xml end def self.servers_from_xml(wrapper_XML) servers_XML = wrapper_XML.css('Servers Server') servers_XML.map do |server_xml| server = SqlServer.new server.name = xml_content(server_xml, 'Name') server.administrator_login = xml_content(server_xml, 'AdministratorLogin') server.location = xml_content(server_xml, 'Location') server.version = xml_content(server_xml, 'Version') server.state = xml_content(server_xml, 'State') server.fully_qualified_domain_name = xml_content(server_xml, 'FullyQualifiedDomainName') server end end def self.server_name_from_xml(response_xml, login, location, version) if response_xml.css('Error').css('Message').to_s != '' raise Azure::SqlDatabaseManagement::Error.new(response_xml.css('Error').css('Message').to_s) end server_name = xml_content(response_xml, 'ServerName') SqlServer.new do |db| db.name = server_name db.location = location db.administrator_login = login db.version = version db.fully_qualified_domain_name = response_xml.css('ServerName').first['FullyQualifiedDomainName'] end end def self.reset_password_to_xml(password) builder = Nokogiri::XML::Builder.new do |xml| xml.AdministratorLoginPassword(password, {'xmlns' => 'http://schemas.microsoft.com/sqlazure/2010/12/'}) end builder.doc.to_xml end # Serialize a firewall rule to xml # @param rule [Azure::SqlDatabaseManagement::FirewallRule] The firewall rule to serialize # @return [String] xml document contain the firewall rule def self.firewall_rule_to_xml(rule) builder = Nokogiri::XML::Builder.new do |xml| xml.ServiceResource('xmlns' => 'http://schemas.microsoft.com/windowsazure') { xml.Name rule.name xml.StartIPAddress rule.start_ip_address xml.EndIPAddress rule.end_ip_address } end builder.doc.to_xml end # Create a list of firewall hashes from xml # @param response_xml The xml containing the list of ServiceResources (firewall rules) # @return [Array] def self.database_firewall_from_xml(response_xml) service_resources = response_xml.css( 'ServiceResources ServiceResource' ) service_resources.map do |resource| FirewallRule.new do |rule| rule.name = xml_content(resource, 'Name') rule.type = xml_content(resource, 'Type') rule.start_ip_address = xml_content(resource, 'StartIPAddress') rule.end_ip_address = xml_content(resource, 'EndIPAddress') end end end end end end azure-0.7.9/lib/azure/sql_database_management/errors.rb0000644000004100000410000000060213112322225023250 0ustar www-datawww-data module Azure module SqlDatabaseManagement # Generic error for SQL Management activities class Error < Azure::Core::Error;end # Server does not exist error for SQL Management activities class ServerDoesNotExist < Azure::Core::Error;end # Firewall Rule does not exist error for SQL Management activities class RuleDoesNotExist < Azure::Core::Error;end end endazure-0.7.9/lib/azure/sql_database_management/sql_server.rb0000644000004100000410000000211113112322225024116 0ustar www-datawww-data#------------------------------------------------------------------------- # Copyright 2013 Microsoft Open Technologies, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- module Azure module SqlDatabaseManagement class SqlServer def initialize yield self if block_given? end attr_accessor :name attr_accessor :administrator_login attr_accessor :location attr_accessor :fully_qualified_domain_name attr_accessor :version attr_accessor :state end end end azure-0.7.9/lib/azure/sql_database_management/sql_database_management_service.rb0000644000004100000410000002275113112322225030304 0ustar www-datawww-data#------------------------------------------------------------------------- # Copyright 2013 Microsoft Open Technologies, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'socket' require 'net/http' require 'azure/sql_database_management/sql_server' require 'azure/sql_database_management/firewall_rule' require 'azure/sql_database_management/serialization' require 'azure/sql_database_management/errors' module Azure module SqlDatabaseManagement # Service for managing SQL Azure servers and databases class SqlDatabaseManagementService < BaseManagement::BaseManagementService # Gets a list of database servers available under the current subscription. # # @see http://msdn.microsoft.com/en-us/library/azure/dn505702.aspx # # @return Array def list_servers response = client.sql_management_request(:get, servers_path).call Serialization.servers_from_xml(response) end # Create a new sql server in Microsoft Azure. # # @param login [String] The administrator login name. # @param password [String] The administrator login password. # @param location [String] The location where the database server will be created. # @param version [String|Integer] The version of the SQL server (default 12.0) # # @see http://msdn.microsoft.com/en-us/library/azure/dn505699.aspx # # @return [Azure::SqlDatabaseManagement::SqlServer] def create_server(login, password, location, version = 12.0) body = Serialization.server_to_xml(login, password, location, version) server_names = list_servers.map(&:name) created_server = nil begin response = client.sql_management_request(:post, servers_path, body: body, warn: true).call created_server = Serialization.server_name_from_xml(response, login, location, version) rescue RuntimeError => ex if ex.message =~ /Please retry the request/ created_server = list_servers.reject{|server| server_names.include?(server.name)}.first # sometimes the service returns 500, but it doesn't really mean it. # # If the operation returns success, the operation is complete and the server is created immediately. If the # operation fails because of a user error, a server is not created. If there is a communication error or # internal server error, you should check the status of the operation using List Servers. # raise unless created_server else raise end end Azure::Loggerx.info "SQL database server #{created_server.name} is created." if created_server created_server end # Deletes the specified database server of given subscription id from Microsoft Azure. # # @param name [String] SqlServer name. # # @see http://msdn.microsoft.com/en-us/library/azure/dn505695.aspx # # @return [void] def delete_server(name) ensure_server_exists!(name) client.sql_management_request(:delete, servers_path(name)).call Azure::Loggerx.info "Deleted database server #{name}." end # Sets the administrative password of a SQL Database server for a subscription. Will raise # Azure::SqlManagement::Error if server does not exist. # # @param name [String] SqlServer name. # @param password [String] SqlServer new password. # # @see http://msdn.microsoft.com/en-us/library/azure/dn505696.aspx # # @return [void] def reset_password(name, password) ensure_server_exists!(name) body = Serialization.reset_password_to_xml(password) request_path = servers_path "#{name}?op=ResetPassword" request = client.sql_management_request(:post, request_path, body) request.call Azure::Loggerx.info "Password for server #{name} changed successfully." end # Adds a new server-level firewall or updates an existing server-level firewall rule for a SQL Database server. # # @param server_name [String] SqlServer server name. # @param rule_name [String] Firewall rule name. # @param start_ip [String] The lowest IP address in the range of the server-level firewall setting. # IP addresses equal to or greater than this can attempt to connect to the server. # The lowest possible IP address is 0.0.0.0. (default: first local public ip) # @param end_ip [String] The highest IP address in the range of the server-level firewall setting. IP # addresses equal to or less than this can attempt to connect to the server. The # highest possible IP address is 255.255.255.255. (default: first local public ip) # # @see http://msdn.microsoft.com/en-us/library/azure/dn505707.aspx # # @return [void] def set_sql_server_firewall_rule(server_name, rule_name, start_ip = public_ipv4, end_ip = public_ipv4) if [server_name, rule_name, start_ip, end_ip].any? { |arg| arg.nil? || arg.empty? || !arg.kind_of?(String) } raise ArgumentError.new('Missing or empty parameter server_name, rule_name, start_ip or end_ip') end fw_rule = FirewallRule.new do |rule| rule.name = rule_name rule.start_ip_address = start_ip rule.end_ip_address = end_ip end body = Serialization.firewall_rule_to_xml(fw_rule) method = if list_sql_server_firewall_rules(server_name).any? { |rule| rule.name == rule_name } # the rule exists, so we need to update the rule :put else # the rule doesn't exist, so we need to create the rule :post end request_path = servers_path "#{server_name}/firewallrules/#{method == :put ? rule_name : ''}" client.sql_management_request(method, request_path, body).call Azure::Loggerx.info "Modified server-level firewall rule #{rule_name}." end # Gets a list of server-level firewall rules set for SQL database servers. Will raise # Azure::SqlManagement::Error if server does not exist. # # @param server_name [String] Database server name. # # @see http://msdn.microsoft.com/en-us/library/azure/dn505715.aspx # # @return [Array] def list_sql_server_firewall_rules(server_name) ensure_server_exists!(server_name) response = client.sql_management_request(:get, servers_path("#{server_name}/firewallrules")).call Serialization.database_firewall_from_xml(response) end # Deletes a server-level firewall rule from a SQL Database server. Will raise # Azure::SqlManagement::Error if server does not exist. # # @param server_name [String] SQL database server name. # @param rule_name [String] SQL database server firewall rule name. # # @see http://msdn.microsoft.com/en-us/library/azure/dn505706.aspx # # @return [void] def delete_sql_server_firewall_rule(server_name, rule_name) ensure_server_exists!(server_name) if list_sql_server_firewall_rules(server_name).any? { |rule| rule.name == rule_name } request_path = servers_path "#{server_name}/firewallrules/#{rule_name}" client.sql_management_request(:delete, request_path).call Azure::Loggerx.info "Deleted server-level firewall rule #{rule_name}." else raise RuleDoesNotExist.new("Firewall rule named #{rule_name} does not exist for server named #{server_name}") end end private def servers_path(end_path = '') '/servers' + if end_path.start_with?('/') end_path elsif end_path.empty? '' else "/#{end_path}" end end # Ensures the a database server of a given name exists and raises Azure::SqlManagement::ServerDoesNotExist if it # does not exist # # @param server_name [String] The name of the Azure SQL Server # @return [void] Returns true if the server exists def ensure_server_exists!(server_name) raise ServerDoesNotExist.new("Server named #{server_name} does not exist.") unless server_exists?(server_name) end def server_exists?(server_name) ArgumentError.new('Servername cannot be empty or null.') if server_name.empty? list_servers.any? { |server| server.name == server_name } end def public_ipv4 @public_ip ||= Net::HTTP.get('whatismyip.akamai.com', '/') end end end end Azure::SqlDatabaseManagementService = Azure::SqlDatabaseManagement::SqlDatabaseManagementService azure-0.7.9/lib/azure/sql_database_management/firewall_rule.rb0000644000004100000410000000177713112322225024606 0ustar www-datawww-data#------------------------------------------------------------------------- # Copyright 2015 Microsoft Open Technologies, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- module Azure module SqlDatabaseManagement class FirewallRule def initialize yield self if block_given? end attr_accessor :name attr_accessor :type attr_accessor :start_ip_address attr_accessor :end_ip_address end end endazure-0.7.9/lib/azure/configurable.rb0000644000004100000410000002402213112322225017557 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- module Azure # The Azure::Configurable module provides basic configuration for Azure activities. module Configurable # @!attribute [w] storage_access_key # @return [String] Azure Storage access key. # @!attribute storage_account_name # @return [String] Azure Storage account name # @!attribute sb_access_key # @return [String] Azure Service Bus access key # @!attribute sb_sas_key # @return [String] Azure Service Bus Shared Access Signature key # @!attribute sb_sas_key_name # @return [String] Azure Service Bus Shared Access Signature key name # @!attribute sb_namespace # @return [String] Azure Service Bus namespace # @!attribute sb_issuer # @return [String] Azure Service Bus issuer # @!attribute ca_file # @return [String] Location of the Certificate Authority bundle to be used for HTTPS # @!attribute management_certificate # @return [String|File] Azure Service Management certificate (pfx or pem) # @!attribute subscription_id # @return [String] Azure Subscription ID # @!attribute http_private_key # @return [String] Private key used for HTTPS certificate based authentication (derived from the management cert) # @!attribute http_certificate_key # @return [String] Public key used for HTTPS certificate based authentication (derived from the management cert) # @!attribute sql_database_management_endpoint # @return [String] Azure SQL database management endpoint. default: https://management.core.windows.net:8443/ # @!attribute storage_blob_host # @return [String] Set the host for the Blob service. Only set this if you want # something custom (like, for example, to point this to a LocalStorage # emulator). This should be the complete host, including http:// at the # start. When using the emulator, make sure to include your account name at # the end. # @!attribute storage_table_host # @return [String] Get the host for this service. If you set something using #storage_table_host=, # then we use that. Else we default to Azure's default hosts, based # on your account name. # @!attribute storage_queue_host # @return [String] Set the host for the Queue service. Only set this if you want # something custom (like, for example, to point this to a LocalStorage # emulator). This should be the complete host, including http:// at the # start. When using the emulator, make sure to include your account name at # the end. # @!attribute management_endpoint # @return [String] Azure Service Management Endpoint. default: https://management.core.windows.net attr_accessor :storage_access_key, :storage_account_name, :sb_access_key, :sb_namespace, :sb_sas_key, :sb_sas_key_name, :sb_issuer, :ca_file, :subscription_id attr_reader :http_private_key, :http_certificate_key, :acs_host, :service_bus_host attr_writer :storage_table_host, :storage_blob_host, :storage_queue_host, :sql_database_management_endpoint, :management_endpoint class << self # List of configurable keys for {Azure::Client} # @return [Array] of option keys def keys @keys ||= [ :storage_access_key, :storage_account_name, :sb_access_key, :sb_sas_key, :sb_sas_key_name, :ca_file, :sb_namespace, :management_certificate, :subscription_id, :sql_database_management_endpoint, :sb_issuer, :storage_table_host, :storage_blob_host, :storage_queue_host, :management_endpoint ] end end # Set configuration options using a block def configure yield self end # Reset configuration options to default values def reset!(options = {}) Azure::Configurable.keys.each do |key| value = if self == Azure Azure::Default.options[key] else Azure.send(key) end if key == :management_certificate @certificate_key = nil @private_key = nil send(:"#{key.to_s + '='}", value) else instance_variable_set(:"@#{key}", options.fetch(key, value)) end end self.send(:reset_agents!) if self.respond_to?(:reset_agents!) self end alias setup reset! def management_certificate @management_certificate end # Set the management certificate via string or file (populates #http_private_key and #http_certificate_key) # @param [String|File] the string or file representing the .pem or .pfx def management_certificate=(cert_string_or_file) self.send(:reset_agents!) if self.respond_to?(:reset_agents!) if cert_string_or_file.nil? @certificate_key = @private_key = @management_certificate = nil else # the pfx may have null chars which will raise an exception in File.file? invalid_file_chars = cert_string_or_file.to_s =~ /\x00/ # validate only if input is file path if !invalid_file_chars && File.file?(cert_string_or_file) && File.extname(cert_string_or_file).downcase =~ /(pem|pfx)$/ error_message = "Could not read from file '#{cert_string_or_file}'." raise ArgumentError.new(error_message) unless test('r', cert_string_or_file) end # get the string representation of cert cert_file = if !invalid_file_chars && File.file?(cert_string_or_file) read_cert_from_file(cert_string_or_file) else cert_string_or_file end begin if cert_file =~ /-----BEGIN CERTIFICATE-----/ # Parse pem content @certificate_key = OpenSSL::X509::Certificate.new(cert_file) @private_key = OpenSSL::PKey::RSA.new(cert_file) else # Parse pfx content cert_content = OpenSSL::PKCS12.new(cert_file) @certificate_key = OpenSSL::X509::Certificate.new( cert_content.certificate.to_pem ) @private_key = OpenSSL::PKey::RSA.new(cert_content.key.to_pem) end @management_certificate = cert_file rescue OpenSSL::OpenSSLError => e @certificate_key = nil @private_key = nil raise ArgumentError.new("Management certificate not valid. Error: #{e.message}") end end end # Private side of the management certificate # @returns [OpenSSL::X509::Certificate] def http_private_key @private_key end # Public side of the management certificate # @returns [OpenSSL::PKey::RSA] def http_certificate_key @certificate_key end def management_endpoint normalize_endpoint do if URI(@management_endpoint).scheme.nil? "https://#{@management_endpoint}" else @management_endpoint end end end def sql_database_management_endpoint normalize_endpoint do if URI(@sql_database_management_endpoint).scheme.nil? "https://#{@sql_database_management_endpoint}:8443" else @sql_database_management_endpoint end end end # Storage queue host # @return [String] def storage_queue_host @storage_queue_host || default_host(:queue) end # Storage blob host # @return [String] def storage_blob_host @storage_blob_host || default_host(:blob) end # Storage table host # @return [String] def storage_table_host @storage_table_host || default_host(:table) end # Get the host for the ACS service. # @return [String] def acs_host if sb_namespace.empty? nil else "https://#{sb_namespace}-sb.accesscontrol.windows.net" end end # Get the host for Service Bus based on the Service Bus Namespace. # @return [String] def service_bus_host if sb_namespace.empty? nil else "https://#{sb_namespace}.servicebus.windows.net" end end def config self end private def default_host(service) "https://#{storage_account_name}.#{service}.core.windows.net" end def read_cert_from_file(cert_file_path) if File.extname(cert_file_path).downcase == '.pem' File.read(cert_file_path) elsif File.extname(cert_file_path).downcase == '.publishsettings' management_cert = parse_publishsettings(cert_file_path)['ManagementCertificate'] Base64.decode64(management_cert) else File.binread(cert_file_path) end end def parse_publishsettings(pub_file_path) pub_xml = Nokogiri::XML.parse(File.read(pub_file_path)) pub_xml.css('PublishData PublishProfile Subscription')[0] end def normalize_endpoint if block_given? File.join(yield, '') else nil end end def options Hash[Azure::Configurable.keys.map { |key| [key, instance_variable_get(:"@#{key}")] }] end end endazure-0.7.9/lib/azure/virtual_machine_image_management/0000755000004100000410000000000013112322225023302 5ustar www-datawww-dataazure-0.7.9/lib/azure/virtual_machine_image_management/serialization.rb0000644000004100000410000000553213112322225026511 0ustar www-datawww-data#------------------------------------------------------------------------- # Copyright 2013 Microsoft Open Technologies, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/virtual_machine_image_management/virtual_machine_image' require 'azure/virtual_machine_image_management/virtual_machine_disk' module Azure module VirtualMachineImageManagement module Serialization extend Azure::Core::Utility def self.virtual_machine_images_from_xml(imageXML) os_images = [] virtual_machine_images = imageXML.css('Images OSImage') virtual_machine_images.each do |image_node| image = VirtualMachineImage.new image.os_type = xml_content(image_node, 'OS') image.name = xml_content(image_node, 'Name') image.category = xml_content(image_node, 'Category') image.media_link = xml_content(image_node, 'MediaLink') image.locations = xml_content(image_node, 'Location') image.image_type = 'OS' os_images << image end os_images end def self.virtual_machine_vm_images_from_xml(imageXML) os_images = [] virtual_machine_images = imageXML.css('VMImages VMImage') virtual_machine_images.each do |image_node| image = VirtualMachineImage.new image.name = xml_content(image_node, 'Name') image.category = xml_content(image_node, 'Category') image.locations = xml_content(image_node, 'Location') image.os_type = xml_content(image_node, 'OSDiskConfiguration OS') image.media_link = xml_content(image_node, 'OSDiskConfiguration MediaLink') image.image_type = 'VM' os_images << image end os_images end def self.disks_from_xml(diskXML) os_disks = [] disks = diskXML.css('Disks Disk') disks.each do |disk_node| disk = VirtualMachineDisk.new disk.name = xml_content(disk_node, 'Name') disk.os_type = xml_content(disk_node, 'OS') disk.attached = !xml_content(disk_node, 'AttachedTo').empty? disk.image = xml_content(disk_node, 'SourceImageName') disk.size = xml_content(disk_node, 'LogicalDiskSizeInGB') os_disks << disk end os_disks end end end end azure-0.7.9/lib/azure/virtual_machine_image_management/virtual_machine_image_management_service.rb0000644000004100000410000000657613112322225034075 0ustar www-datawww-data#------------------------------------------------------------------------- # Copyright 2013 Microsoft Open Technologies, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/virtual_machine_image_management/serialization' module Azure module VirtualMachineImageManagement class VirtualMachineImageManagementService < BaseManagement::BaseManagementService # Public: Gets a list of virtual machine images from the server # # Returns an array of Azure::VirtualMachineImageManagement::VirtualMachineImage objects def list_virtual_machine_images list_os_images + list_vm_images end # Private: Gets a list of the operating system images that is associated with the specified subscription # # See http://msdn.microsoft.com/en-us/library/azure/jj157191.aspx # # Returns an array of Azure::VirtualMachineImageManagement::VirtualMachineImage objects def list_os_images request_path = '/services/images' request = client.management_request(:get, request_path, nil) response = request.call Serialization.virtual_machine_images_from_xml(response) end # Private: Gets a list of the VM Images that is associated with the specified subscription # # See http://msdn.microsoft.com/en-us/library/azure/dn499770.aspx # # Returns an array of Azure::VirtualMachineImageManagement::VirtualMachineImage objects def list_vm_images request_path = '/services/vmimages' request = client.management_request(:get, request_path) response = request.call Serialization.virtual_machine_vm_images_from_xml(response) end end class VirtualMachineDiskManagementService < BaseManagement::BaseManagementService # Public: Gets a list of Disks from the server. # # Returns an array of Azure::VirtualMachineDiskManagementService objects def list_virtual_machine_disks request_path = '/services/disks' request = client.management_request(:get, request_path) response = request.call Serialization.disks_from_xml(response) end def get_virtual_machine_disk(disk_name) disk = list_virtual_machine_disks.select { |x| x.name == disk_name } disk.first end # Public: Deletes the specified data or operating system disk from the image repository. # # Returns None def delete_virtual_machine_disk(disk_name, options={}) Azure::Loggerx.info "Deleting Disk \"#{disk_name}\". " path = "/services/disks/#{disk_name}#{ '?comp=media' if options[:delete_vhd] }" request = client.management_request(:delete, path) request.call end end end end Azure::VirtualMachineImageManagementService = Azure::VirtualMachineImageManagement::VirtualMachineImageManagementService azure-0.7.9/lib/azure/virtual_machine_image_management/virtual_machine_disk.rb0000644000004100000410000000171413112322225030016 0ustar www-datawww-data#------------------------------------------------------------------------- # Copyright 2013 Microsoft Open Technologies, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- module Azure module VirtualMachineImageManagement class VirtualMachineDisk def initialize yield self if block_given? end attr_accessor :name, :attached, :os_type, :image, :size end end end azure-0.7.9/lib/azure/virtual_machine_image_management/virtual_machine_image.rb0000644000004100000410000000174413112322225030151 0ustar www-datawww-data#------------------------------------------------------------------------- # Copyright 2013 Microsoft Open Technologies, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- module Azure module VirtualMachineImageManagement class VirtualMachineImage def initialize yield self if block_given? end attr_accessor :os_type, :name, :category, :locations, :media_link, :image_type end end end azure-0.7.9/lib/azure/http_response_helper.rb0000644000004100000410000000325113112322225021354 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- module Azure module HttpResponseHelper # Sends request to HTTP server and returns a Faraday::Response # @param method [Symbol] The HTTP method to use (:get, :post, :put, :del, etc...) # @param url [URI] The URI of the HTTP endpoint to query # @param conn [Net::HTTP] http agent for a given uri # @param headers [String] The request headers # @param body [String] The request body #returns Faraday::Response def set_up_response(method, url, conn, headers ,body) conn.run_request(method, url, nil, nil) do |req| req.body = body if body req.headers = headers if headers unless headers.nil? keep_alive = headers['Keep-Alive'] || headers['keep-alive'] req.options[:timeout] = keep_alive.split('=').last.to_i unless keep_alive.nil? end req.options[:open_timeout] ||= 60 end end end endazure-0.7.9/lib/azure/virtual_machine_management/0000755000004100000410000000000013112322225022140 5ustar www-datawww-dataazure-0.7.9/lib/azure/virtual_machine_management/serialization.rb0000644000004100000410000004527713112322225025361 0ustar www-datawww-data#------------------------------------------------------------------------- # Copyright 2013 Microsoft Open Technologies, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/core' require 'azure/virtual_machine_management/virtual_machine' require 'base64' module Azure module VirtualMachineManagement module Serialization extend Azure::Core::Utility def self.shutdown_virtual_machine_to_xml builder = Nokogiri::XML::Builder.new do |xml| xml.ShutdownRoleOperation( 'xmlns' => 'http://schemas.microsoft.com/windowsazure', 'xmlns:i' => 'http://www.w3.org/2001/XMLSchema-instance' ) do xml.OperationType 'ShutdownRoleOperation' xml.PostShutdownAction 'StoppedDeallocated' end end builder.doc.to_xml end def self.start_virtual_machine_to_xml builder = Nokogiri::XML::Builder.new do |xml| xml.StartRoleOperation( 'xmlns' => 'http://schemas.microsoft.com/windowsazure', 'xmlns:i' => 'http://www.w3.org/2001/XMLSchema-instance' ) do xml.OperationType 'StartRoleOperation' end end builder.doc.to_xml end def self.restart_virtual_machine_to_xml builder = Nokogiri::XML::Builder.new do |xml| xml.RestartRoleOperation( 'xmlns' => 'http://schemas.microsoft.com/windowsazure', 'xmlns:i' => 'http://www.w3.org/2001/XMLSchema-instance' ) do xml.OperationType 'RestartRoleOperation' end end builder.doc.to_xml end def self.deployment_to_xml(params, image, options) options[:deployment_name] ||= options[:cloud_service_name] builder = Nokogiri::XML::Builder.new do |xml| xml.Deployment( 'xmlns' => 'http://schemas.microsoft.com/windowsazure', 'xmlns:i' => 'http://www.w3.org/2001/XMLSchema-instance' ) do xml.Name options[:deployment_name] xml.DeploymentSlot 'Production' xml.Label Base64.encode64(options[:deployment_name]).strip xml.RoleList { xml.Role('i:type' => 'PersistentVMRole') } if options[:virtual_network_name] xml.VirtualNetworkName options[:virtual_network_name] end if options[:reserved_ip_name] xml.ReservedIPName options[:reserved_ip_name] end end end builder.doc.at_css('Role') << role_to_xml(params, image, options).at_css('PersistentVMRole').children.to_s builder.doc.to_xml end def self.role_to_xml(params, image, options) builder = Nokogiri::XML::Builder.new do |xml| xml.PersistentVMRole( 'xmlns' => 'http://schemas.microsoft.com/windowsazure', 'xmlns:i' => 'http://www.w3.org/2001/XMLSchema-instance' ) do xml.RoleName { xml.text params[:vm_name] } xml.OsVersion('i:nil' => 'true') xml.RoleType 'PersistentVMRole' xml.ConfigurationSets do provisioning_configuration_to_xml(xml, params, options) if image.image_type == 'OS' || image.image_type == 'VM' xml.ConfigurationSet('i:type' => 'NetworkConfigurationSet') do xml.ConfigurationSetType 'NetworkConfiguration' xml.InputEndpoints do default_endpoints_to_xml(xml, options) specified_endpoints_to_xml( xml, 'TCP', options[:tcp_endpoints], options[:existing_ports] ) if options[:tcp_endpoints] specified_endpoints_to_xml( xml, 'UDP', options[:udp_endpoints], options[:existing_ports] ) if options[:udp_endpoints] end if options[:virtual_network_name] && options[:subnet_name] xml.SubnetNames do xml.SubnetName options[:subnet_name] end xml.StaticVirtualNetworkIPAddress options[:static_virtual_network_ipaddress] if options[:static_virtual_network_ipaddress] end end end xml.VMImageName image.name if image.image_type == 'VM' xml.AvailabilitySetName options[:availability_set_name] xml.Label Base64.encode64(params[:vm_name]).strip if image.category == 'User' storage_host = URI.parse( image.media_link ).host else storage_host = options[:storage_account_name] + '.blob.core.windows.net' end if image.image_type == 'OS' xml.OSVirtualHardDisk do xml.MediaLink 'http://' + storage_host + '/vhds/' + (Time.now.strftime('disk_%Y_%m_%d_%H_%M_%S_%L')) + '.vhd' xml.SourceImageName params[:image] end end xml.RoleSize options[:vm_size] end end builder.doc end def self.provisioning_configuration_to_xml(xml, params, options) fingerprint = params[:certificate][:fingerprint] if options[:os_type] == 'Linux' xml.ConfigurationSet('i:type' => 'LinuxProvisioningConfigurationSet') do xml.ConfigurationSetType 'LinuxProvisioningConfiguration' xml.HostName params[:vm_name] xml.UserName params[:vm_user] if params[:password] xml.UserPassword params[:password] xml.DisableSshPasswordAuthentication 'false' end if fingerprint xml.SSH do xml.PublicKeys do xml.PublicKey do xml.Fingerprint fingerprint.to_s.upcase xml.Path "/home/#{params[:vm_user]}/.ssh/authorized_keys" end end xml.KeyPairs do xml.KeyPair do xml.Fingerprint fingerprint.to_s.upcase xml.Path "/home/#{params[:vm_user]}/.ssh/id_rsa" end end end end xml.CustomData params[:custom_data] if params[:custom_data] end elsif options[:os_type] == 'Windows' xml.ConfigurationSet('i:type' => 'WindowsProvisioningConfigurationSet') do xml.ConfigurationSetType 'WindowsProvisioningConfiguration' xml.ComputerName params[:vm_name] xml.AdminPassword params[:password] xml.ResetPasswordOnFirstLogon 'false' xml.EnableAutomaticUpdates 'true' if enable_winrm?(options[:winrm_transport]) xml.WinRM do xml.Listeners do if options[:winrm_transport].include?('http') xml.Listener do xml.Protocol 'Http' end end if options[:winrm_transport].include?('https') xml.Listener do xml.Protocol 'Https' xml.CertificateThumbprint fingerprint if fingerprint end end end end end xml.AdminUsername params[:vm_user] xml.CustomData params[:custom_data] if params[:custom_data] end end end def self.default_endpoints_to_xml(xml, options) os_type = options[:os_type] used_ports = options[:existing_ports] endpoints = [] if os_type == 'Linux' preferred_port = '22' port_already_opened?(used_ports, options[:ssh_port]) endpoints << { name: 'SSH', public_port: options[:ssh_port] || assign_random_port(preferred_port, used_ports), protocol: 'TCP', local_port: preferred_port } elsif os_type == 'Windows' && options[:winrm_transport] if options[:winrm_transport].include?('http') preferred_port = '5985' port_already_opened?(used_ports, options[:winrm_http_port]) endpoints << { name: 'WinRm-Http', public_port: options[:winrm_http_port] || assign_random_port(preferred_port, used_ports), protocol: 'TCP', local_port: preferred_port } end if options[:winrm_transport].include?('https') preferred_port = '5986' port_already_opened?(used_ports, options[:winrm_https_port]) endpoints << { name: 'PowerShell', public_port: options[:winrm_https_port] || assign_random_port(preferred_port, used_ports), protocol: 'TCP', local_port: preferred_port } end end endpoints_to_xml(xml, endpoints) end def self.specified_endpoints_to_xml(xml, protocol, specified_endpoints, existing_ports = []) endpoints = [] protocol_up = protocol.upcase specified_endpoints.split(',').each do |endpoint| ports = endpoint.split(':') specified_ep = {} if ports.length > 1 port_already_opened?(existing_ports, ports[1]) specified_ep[:name] = "#{protocol_up}-PORT-#{ports[1]}" specified_ep[:public_port] = ports[1] else port_already_opened?(existing_ports, ports[0]) specified_ep[:name] = "#{protocol_up}-PORT-#{ports[0]}" specified_ep[:public_port] = ports[0] end specified_ep[:local_port] = ports[0] specified_ep[:protocol] = protocol_up endpoints << specified_ep end endpoints_to_xml(xml, endpoints) end def self.virtual_machines_from_xml(deployXML, cloud_service_name) unless deployXML.nil? or deployXML.at_css('Deployment Name').nil? instances = deployXML.css('Deployment RoleInstanceList RoleInstance') roles = deployXML.css('Deployment RoleList Role') ip = deployXML.css('Deployment VirtualIPs VirtualIP') vms = [] instances.each do |instance| vm = VirtualMachine.new role_name = xml_content(instance, 'RoleName') vm.status = xml_content(instance, 'InstanceStatus') vm.vm_name = role_name vm.ipaddress = xml_content(ip, 'Address') vm.role_size = xml_content(instance, 'InstanceSize') vm.hostname = xml_content(instance, 'HostName') vm.cloud_service_name = cloud_service_name vm.deployment_name = xml_content(deployXML, 'Deployment Name') vm.deployment_status = xml_content(deployXML, 'Deployment Status') vm.virtual_network_name = xml_content( deployXML.css('Deployment'), 'VirtualNetworkName' ) roles.each do |role| if xml_content(role, 'RoleName') == role_name vm.availability_set_name = xml_content(role, 'AvailabilitySetName') endpoints_from_xml(role, vm) vm.data_disks = data_disks_from_xml(role) subnet = xml_content(role, 'ConfigurationSets ConfigurationSet SubnetNames SubnetName' ) vm.subnet = subnet unless subnet.empty? static_virtual_network_ipaddress = xml_content(role,'ConfigurationSets ConfigurationSet StaticVirtualNetworkIPAddress') vm.static_virtual_network_ipaddress = static_virtual_network_ipaddress unless static_virtual_network_ipaddress.empty? vm.os_type = xml_content(role, 'OSVirtualHardDisk OS') vm.disk_name = xml_content(role, 'OSVirtualHardDisk DiskName') vm.media_link = xml_content(role, 'OSVirtualHardDisk MediaLink') vm.image = xml_content(role, 'OSVirtualHardDisk SourceImageName') break end end vms << vm end vms end end def self.data_disks_from_xml(rolesXML) data_disks = [] virtual_hard_disks = rolesXML.css('DataVirtualHardDisks DataVirtualHardDisk') virtual_hard_disks.each do |disk| data_disk = {} data_disk[:name] = xml_content(disk, 'DiskName') data_disk[:lun] = xml_content(disk, 'Lun') data_disk[:size_in_gb] = xml_content(disk, 'LogicalDiskSizeInGB') data_disk[:media_link] = xml_content(disk, 'MediaLink') data_disks << data_disk end data_disks end def self.endpoints_from_xml(rolesXML, vm) vm.tcp_endpoints = [] vm.udp_endpoints = [] endpoints = rolesXML.css('ConfigurationSets ConfigurationSet InputEndpoints InputEndpoint') endpoints.each do |endpoint| lb_name = xml_content(endpoint, 'LoadBalancedEndpointSetName') ep = {} ep[:name] = xml_content(endpoint, 'Name') ep[:vip] = xml_content(endpoint, 'Vip') ep[:public_port] = xml_content(endpoint, 'Port') ep[:local_port] = xml_content(endpoint, 'LocalPort') ep[:protocol] = xml_content(endpoint, 'Protocol') server_return = xml_content(endpoint, 'EnableDirectServerReturn') ep[:direct_server_return] = server_return if !server_return.empty? unless lb_name.empty? ep[:protocol] = endpoint.css('Protocol').last.text ep[:load_balancer_name] = lb_name lb_port = xml_content(endpoint, 'LoadBalancerProbe Port') lb_protocol = xml_content(endpoint, 'LoadBalancerProbe Protocol') lb_path = xml_content(endpoint, 'LoadBalancerProbe Path') lb_interval = xml_content( endpoint, 'LoadBalancerProbe IntervalInSeconds' ) lb_timeout = xml_content( endpoint, 'LoadBalancerProbe TimeoutInSeconds' ) ep[:load_balancer] = { port: lb_port, path: lb_path, protocol: lb_protocol, interval: lb_interval, timeout: lb_timeout } end if ep[:protocol].downcase == 'tcp' vm.tcp_endpoints << ep else vm.udp_endpoints << ep end end end def self.update_role_to_xml(endpoints, vm) builder = Nokogiri::XML::Builder.new do |xml| xml.PersistentVMRole( 'xmlns' => 'http://schemas.microsoft.com/windowsazure', 'xmlns:i' => 'http://www.w3.org/2001/XMLSchema-instance' ) do xml.ConfigurationSets do xml.ConfigurationSet do xml.ConfigurationSetType 'NetworkConfiguration' xml.InputEndpoints do endpoints_to_xml(xml, endpoints) end xml.SubnetNames do xml.SubnetName vm.subnet if vm.subnet end xml.StaticVirtualNetworkIPAddress vm.static_virtual_network_ipaddress if vm.static_virtual_network_ipaddress end end xml.OSVirtualHardDisk do end end end builder.doc.to_xml end def self.endpoints_to_xml(xml, endpoints) endpoints.each do |endpoint| endpoint[:load_balancer] ||= {} protocol = endpoint[:protocol] port = endpoint[:public_port] interval = endpoint[:load_balancer][:interval] timeout = endpoint[:load_balancer][:timeout] path = endpoint[:load_balancer][:path] balancer_name = endpoint[:load_balancer_name] xml.InputEndpoint do xml.LoadBalancedEndpointSetName balancer_name if balancer_name xml.LocalPort endpoint[:local_port] xml.Name endpoint[:name] xml.Port endpoint[:public_port] if balancer_name xml.LoadBalancerProbe do xml.Path path if path xml.Port endpoint[:load_balancer][:port] || port xml.Protocol endpoint[:load_balancer][:protocol] || 'TCP' xml.IntervalInSeconds interval if interval xml.TimeoutInSeconds timeout if timeout end end xml.Protocol protocol xml.EnableDirectServerReturn endpoint[:direct_server_return] unless endpoint[:direct_server_return].nil? end end end def self.add_data_disk_to_xml(vm, options) if options[:import] && options[:disk_name].nil? Azure::Loggerx.error_with_exit "The data disk name is not valid." end media_link = vm.media_link builder = Nokogiri::XML::Builder.new do |xml| xml.DataVirtualHardDisk( 'xmlns' => 'http://schemas.microsoft.com/windowsazure', 'xmlns:i' => 'http://www.w3.org/2001/XMLSchema-instance' ) do xml.HostCaching options[:host_caching] || 'ReadOnly' xml.DiskLabel options[:disk_label] xml.DiskName options[:disk_name] if options[:import] xml.LogicalDiskSizeInGB options[:disk_size] || 100 unless options[:import] disk_name = media_link[/([^\/]+)$/] media_link = media_link.gsub(/#{disk_name}/, (Time.now.strftime('disk_%Y_%m_%d_%H_%M_%S')) + '.vhd') xml.MediaLink media_link end end end builder.doc.to_xml end private def self.port_already_opened?(existing_ports, port) return false if existing_ports.nil? raise "Port #{port} conflicts with a port already opened. "\ "Please select a different port." if existing_ports.include?(port) false end def self.assign_random_port(preferred_port, used_ports) random_port = nil if used_ports.nil? || !used_ports.include?(preferred_port) random_port = preferred_port else random_port = Random.new.rand(10000..65535) while(used_ports.include?(random_port.to_s)) random_port = Random.new.rand(10000..65535) end end random_port end end end end azure-0.7.9/lib/azure/virtual_machine_management/virtual_machine.rb0000644000004100000410000000302313112322225025635 0ustar www-datawww-data#------------------------------------------------------------------------- # Copyright 2013 Microsoft Open Technologies, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- module Azure module VirtualMachineManagement class VirtualMachine def initialize yield self if block_given? end attr_accessor :cloud_service_name attr_accessor :status attr_accessor :ipaddress attr_accessor :vm_name attr_accessor :udp_endpoints attr_accessor :hostname attr_accessor :deployment_name attr_accessor :deployment_status attr_accessor :tcp_endpoints attr_accessor :role_size attr_accessor :image attr_accessor :os_type attr_accessor :disk_name attr_accessor :virtual_network_name attr_accessor :availability_set_name attr_accessor :media_link attr_accessor :data_disks attr_accessor :subnet attr_accessor :static_virtual_network_ipaddress end end end azure-0.7.9/lib/azure/virtual_machine_management/virtual_machine_management_service.rb0000644000004100000410000006312713112322225031564 0ustar www-datawww-data#------------------------------------------------------------------------- # Copyright 2013 Microsoft Open Technologies, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'azure/virtual_machine_management/serialization' include Azure::VirtualMachineImageManagement module Azure module VirtualMachineManagement class VirtualMachineManagementService < BaseManagement::BaseManagementService include Azure::Core::Utility # Public: Get a lists of virtual machines available under the current subscription. # # Returns an list of Azure::VirtualMachineManagement::VirtualMachine instances. def list_virtual_machines(*cloud_service_names) roles = [] cloud_service_names.flatten! if cloud_service_names.empty? cloud_service = client.cloud_service_management cloud_service_names = cloud_service.list_cloud_services.map(&:name) end cloud_service_names.each do |cloud_service_name| request_path = "/services/hostedservices/#{cloud_service_name}/deploymentslots/production" request = client.management_request(:get, request_path) request.warn = true response = request.call roles << Serialization.virtual_machines_from_xml(response, cloud_service_name) end roles.flatten.compact end # Public: Gets a virtual machine based on the provided name and cloud service name. # # ==== Attributes # # * +name+ - String. Virtual machine name. # * +cloud_service_name+ - String. Cloud service name. # # Returns an Azure::VirtualMachineManagement::VirtualMachine instance. def get_virtual_machine(name, cloud_service_name) server = list_virtual_machines(cloud_service_name).select { |x| x.vm_name.casecmp(name) == 0 } server.first end # Public: Provisions a virtual machine based on the supplied configuration. # # ==== Attributes # # * +params+ - Hash. parameters. # * +options+ - Hash. Optional parameters. # # ==== Params # # Accepted key/value pairs are: # * +:vm_name+ - String. Name of virtual machine. # * +:vm_user+ - String. User name for the virtual machine instance. # * +:password+ - String. A description for the hosted service. # * +:image+ - String. Name of the disk image to use to create the virtual machine. # * +:location+ - String. The location where the virtual machine will be created. # * +:affinity_group_name - String. The affinity group name to be used # for the cloud service and the storage account if these do not exist. # # ==== Options # # Accepted key/value pairs are: # * +:storage_account_name+ - String. Name of storage account. # * +:cloud_service_name+ - String. Name of cloud service. # * +:deployment_name+ - String. A name for the deployment. # * +:tcp_endpoints+ - String. Specifies the internal port and external/public port separated by a colon. # You can map multiple internal and external ports by separating them with a comma. # * +:ssh_private_key_file+ - String. Path of private key file. # * +:ssh_port+ - Integer. Specifies the SSH port number. # * +:winrm_http_port+ - Integer. Specifies the WinRM HTTP port number. # * +:winrm_https_port+ - Integer. Specifies the WinRM HTTPS port number. # * +:vm_size+ - String. Specifies the size of the virtual machine instance. # * +:winrm_transport+ - Array. Specifies WINRM transport protocol. # * +:availability_set_name+ - String. Specifies the availability set name. # * +:reserved_ip_name+ - String. Specifies the reserved IP name. # # @return [Azure::VirtualMachineManagement::VirtualMachine] objects of newly created instance. # # # @see http://msdn.microsoft.com/en-us/library/azure/jj157194.aspx def create_virtual_machine(params, options = {}) image = get_image(params[:image]) options[:os_type] = image.os_type validate_deployment_params(params, options) options[:deployment_name] ||= options[:cloud_service_name] Azure::Loggerx.info 'Creating deployment...' options[:cloud_service_name] ||= generate_cloud_service_name(params[:vm_name]) options[:storage_account_name] ||= generate_storage_account_name(params[:vm_name]) optionals = {} if options[:virtual_network_name] virtual_network_service = client.network_management virtual_networks = virtual_network_service.list_virtual_networks.select { |x| x.name == options[:virtual_network_name] } if virtual_networks.empty? Azure::Loggerx.error_with_exit "Virtual network #{options[:virtual_network_name]} doesn't exists" else vnet = virtual_networks.first if !vnet.affinity_group.empty? options[:affinity_group_name] = vnet.affinity_group else optionals[:location] = vnet.location end end elsif options[:affinity_group_name] optionals[:affinity_group_name] = options[:affinity_group_name] else optionals[:location] = params[:location] end cloud_service = client.cloud_service_management cloud_service.create_cloud_service(options[:cloud_service_name], optionals) cloud_service.upload_certificate(options[:cloud_service_name], params[:certificate]) unless params[:certificate].empty? unless image.category == 'User' options[:storage_account_name] ||= generate_storage_account_name(params[:vm_name]) client.storage_management.create_storage_account(options[:storage_account_name], optionals) end body = Serialization.deployment_to_xml(params, image, options) path = "/services/hostedservices/#{options[:cloud_service_name]}/deployments" Azure::Loggerx.info 'Deployment in progress...' request = client.management_request(:post, path, body) request.call get_virtual_machine(params[:vm_name], options[:cloud_service_name]) rescue Exception => e Azure::Loggerx.error_with_exit "Failed to create virtual machine : "+e.message end # Public: Add a new role to a cloud service. Atleast one deployment should exist before you can add a role. # # ==== Attributes # # * +params+ - Hash. parameters. # * +options+ - Hash. Optional parameters. # # ==== Params # # Accepted key/value pairs are: # * +:vm_name+ - String. Name of virtual machine. # * +:vm_user+ - String. User name for the virtual machine instance. # * +:password+ - String. A description for the hosted service. # * +:image+ - String. Name of the disk image to use to create the virtual machine. # * +:cloud_service_name+ - String. Name of cloud service. # # ==== Options # # Accepted key/value pairs are: # * +:storage_account_name+ - String. Name of storage account. # * +:tcp_endpoints+ - String. Specifies the internal port and external/public port separated by a colon. # You can map multiple internal and external ports by separating them with a comma. # * +:ssh_private_key_file+ - String. Path of private key file. # * +:ssh_port+ - Integer. Specifies the SSH port number. # * +:winrm_http_port - Integer. Specifies the WinRM HTTP port number. # * +:winrm_https_port - Integer. Specifies the WinRM HTTPS port number. # * +:vm_size+ - String. Specifies the size of the virtual machine instance. # * +:winrm_transport+ - Array. Specifies WINRM transport protocol. # # Returns Azure::VirtualMachineManagement::VirtualMachine objects of newly created instance. # # See: # http://msdn.microsoft.com/en-us/library/azure/jj157186.aspx def add_role(params, options = {}) image = get_image(params[:image]) options[:os_type] = image.os_type validate_deployment_params(params, options, true) cloud_services = client.cloud_service_management cloud_service = cloud_services.get_cloud_service_properties(params[:cloud_service_name]) deployment_name = cloud_service.deployment_name Azure::Loggerx.error_with_exit "Deployment doesn't exists." if cloud_service && deployment_name.empty? others = {} if cloud_service.location others[:location] = cloud_service.location elsif cloud_service.affinity_group others[:affinity_group_name] = cloud_service.affinity_group end unless image.category == 'User' options[:storage_account_name] ||= generate_storage_account_name(params[:vm_name]) client.storage_management.create_storage_account(options[:storage_account_name], others) end Azure::Loggerx.info 'Deployment exists, adding role...' existing_ports = [] cloud_service.virtual_machines[deployment_name.to_sym].each do |vm| vm.tcp_endpoints.each do |endpoint| existing_ports << endpoint[:public_port] end end cloud_services.upload_certificate(options[:cloud_service_name], params[:certificate]) unless params[:certificate].empty? options[:existing_ports] = existing_ports body = Serialization.role_to_xml(params, image, options).to_xml path = "/services/hostedservices/#{cloud_service.name}/deployments/#{deployment_name}/roles" Azure::Loggerx.info 'Deployment in progress...' request = client.management_request(:post, path, body) request.call get_virtual_machine(params[:vm_name], cloud_service.name) end # Public: Deletes the deployment, cloud service and disk. # # ==== Attributes # # * +vm_name+ - String. Virtual machine name. # * +cloud_service_name+ - String. Cloud service name. # # ==== Options # # Accepted key/value pairs are: # * +delete_vhd+ - Boolean. To delete the associated VHD from the blob storage location. # # See http://msdn.microsoft.com/en-us/library/azure/gg441305.aspx # See http://msdn.microsoft.com/en-us/library/azure/jj157179.aspx # # Returns NONE def delete_virtual_machine(vm_name, cloud_service_name, options={}) virtual_machines = list_virtual_machines(cloud_service_name) vm = virtual_machines.select { |x| x.vm_name == vm_name }.first if vm if virtual_machines.size == 1 cloud_service = client.cloud_service_management cloud_service.delete_cloud_service_deployment(cloud_service_name) cloud_service.delete_cloud_service(cloud_service_name) else path = "/services/hostedservices/#{vm.cloud_service_name}/deployments/#{vm.deployment_name}/roles/#{vm.vm_name}" Azure::Loggerx.info "Deleting virtual machine #{vm_name}. \n" request = client.management_request(:delete, path) request.call end Azure::Loggerx.info "Waiting for disk to be released.\n" disk_name = vm.disk_name disk_management_service = client.vm_disk_management # Wait for 180s for disk to be released. disk = nil 18.times do Azure::Loggerx.info '# ' disk = disk_management_service.get_virtual_machine_disk(disk_name) unless disk.attached Azure::Loggerx.info "Disk released.\n" break end sleep 10 end if disk.attached Azure::Loggerx.error "\nCannot delete disk #{disk_name}." else disk_management_service.delete_virtual_machine_disk(disk_name, options) end else Azure::Loggerx.error "Cannot find virtual machine #{vm_name} under cloud service #{cloud_service_name}" end rescue end # Public: Shuts down the specified virtual machine. # # ==== Attributes # # * +name+ - String. Virtual machine name. # * +cloud_service_name+ - String. Cloud service name. # # See http://msdn.microsoft.com/en-us/library/azure/jj157195.aspx # # Returns NONE def shutdown_virtual_machine(vm_name, cloud_service_name) vm = get_virtual_machine(vm_name, cloud_service_name) if vm if %w(StoppedVM StoppedDeallocated).include?(vm.status) Azure::Loggerx.error 'Cannot perform the shutdown operation on a stopped virtual machine.' elsif vm.deployment_status == 'Running' path = "/services/hostedservices/#{vm.cloud_service_name}/deployments/#{vm.deployment_name}/roleinstances/#{vm.vm_name}/Operations" body = Serialization.shutdown_virtual_machine_to_xml Azure::Loggerx.info "Shutting down virtual machine \"#{vm.vm_name}\" ..." request = client.management_request(:post, path, body) request.call else Azure::Loggerx.error 'Cannot perform the shutdown operation on a stopped deployment.' end else Azure::Loggerx.error "Cannot find virtual machine \"#{vm_name}\" under cloud service \"#{cloud_service_name}\". " end end # Public: Starts the specified virtual machine. # # ==== Attributes # # * +name+ - String. Virtual machine name. # * +cloud_service_name+ - String. Cloud service name. # # See http://msdn.microsoft.com/en-us/library/azure/jj157189.aspx # # Returns NONE def start_virtual_machine(vm_name, cloud_service_name) vm = get_virtual_machine(vm_name, cloud_service_name) if vm if vm.status == 'ReadyRole' Azure::Loggerx.error 'Cannot perform the start operation on started virtual machine.' else path = "/services/hostedservices/#{vm.cloud_service_name}/deployments/#{vm.deployment_name}/roleinstances/#{vm.vm_name}/Operations" body = Serialization.start_virtual_machine_to_xml Azure::Loggerx.info "Starting virtual machine \"#{vm.vm_name}\" ..." request = client.management_request(:post, path, body: body) request.call end else Azure::Loggerx.error "Cannot find virtual machine \"#{vm_name}\" under cloud service \"#{cloud_service_name}\"." end end # Public: Restarts the specified virtual machine. # # ==== Attributes # # * +name+ - String. Virtual machine name. # * +cloud_service_name+ - String. Cloud service name. # # See http://msdn.microsoft.com/en-us/library/azure/jj157197.aspx # # Returns NONE def restart_virtual_machine(vm_name, cloud_service_name) vm = get_virtual_machine(vm_name, cloud_service_name) if vm path = "/services/hostedservices/#{vm.cloud_service_name}/deployments/#{vm.deployment_name}/roleinstances/#{vm.vm_name}/Operations" body = Serialization.restart_virtual_machine_to_xml Azure::Loggerx.info "Restarting virtual machine \"#{vm.vm_name}\" ..." request = client.management_request(:post, path, body: body) request.call else Azure::Loggerx.error "Cannot find virtual machine \"#{vm_name}\" under cloud service \"#{cloud_service_name}\"." end end # Public: Add/Update endpoints of virtual machine. # # ==== Attributes # # * +name+ - String. Virtual machine name. # * +cloud_service_name+ - String. Cloud service name. # * +input_endpoints+ - Hash. A hash of the name/value pairs for the endpoint. # # ==== Endpoint # # Accepted key/value pairs are: # * +:local_port+ - String. Specifies the internal port on which the # Virtual Machine is listening. # * +:public_port+ - String. Specifies the external port to use for # the endpoint. # * +:name+ - String. Specifies the name of the external endpoint. # * +load_balancer_name+ - String. Specifies a name for a set of # load-balanced endpoints. # * +:protocol+ - String. Specifies the transport protocol # for the endpoint. Possible values are: TCP, UDP # * +:direct_server_return+ - String. Specifies whether the endpoint # uses Direct Server Return. Possible values are: true, false (optional) # * +:load_balancer - Hash. Contains properties that define the # endpoint settings that the load balancer uses to monitor the # availability of the Virtual Machine (optional) # # === Load balancer # # Accepted key/value pairs are: # * +:port+ - String. Specifies the internal port on which the # Virtual Machine is listening. # * +:protocol+ - String. Specifies the protocol to use to inspect the # availability status of the virtual machine. # * +:interval+ - String. Specifies the interval for the load balancer # probe in seconds. (optional) # * +:timeout+ - String. Specifies the timeout for the load balancer # probe in seconds. (optional) # * +:path+ - String. Specifies the relative path to inspect to # determine the availability status of the Virtual Machine. (optional) # # See http://msdn.microsoft.com/en-us/library/azure/jj157187.aspx # # Returns NONE def update_endpoints(vm_name, cloud_service_name, *input_endpoints) input_endpoints.flatten! vm = get_virtual_machine(vm_name, cloud_service_name) if vm path = "/services/hostedservices/#{vm.cloud_service_name}/deployments/#{vm.deployment_name}/roles/#{vm_name}" endpoints = vm.tcp_endpoints + vm.udp_endpoints input_endpoints.each do |iep| endpoints.delete_if { |ep| iep[:name].downcase == ep[:name].downcase } end endpoints += input_endpoints body = Serialization.update_role_to_xml(endpoints, vm) request = client.management_request(:put, path, body: body) Azure::Loggerx.info "Updating endpoints of virtual machine #{vm.vm_name} ..." request.call else Azure::Loggerx.error "Cannot find virtual machine \"#{vm_name}\" under cloud service \"#{cloud_service_name}\"." end end # Public: Delete endpoint of virtual machine. # # ==== Attributes # # * +name+ - String. Virtual machine name. # * +cloud_service_name+ - String. Cloud service name. # * +endpoint_name+ - String. Name of endpoint. # # See http://msdn.microsoft.com/en-us/library/azure/jj157187.aspx # # Returns NONE def delete_endpoint(vm_name, cloud_service_name, endpoint_name) vm = get_virtual_machine(vm_name, cloud_service_name) if vm path = "/services/hostedservices/#{vm.cloud_service_name}/deployments/#{vm.deployment_name}/roles/#{vm_name}" endpoints = vm.tcp_endpoints + vm.udp_endpoints endpoints.delete_if { |ep| endpoint_name.downcase == ep[:name].downcase } body = Serialization.update_role_to_xml(endpoints, vm) request = client.management_request(:put, path, body: body) Azure::Loggerx.info "Deleting virtual machine endpoint #{endpoint_name} ..." request.call else Azure::Loggerx.error "Cannot find virtual machine \"#{vm_name}\" under cloud service \"#{cloud_service_name}\"." end end # Public: adds a data disk to a virtual machine. # # ==== Attributes # # * +cloud_service_name+ - String. Cloud service name. # * +vm_name+ - String. Virtual machine name. # * +options+ - Hash. Optional parameters. # # ==== Options # # Accepted key/value pairs in options parameter are: # * +:import+ - Boolean. if true, then allows to use an existing # disk by disk name. if false, then create and attach new data disk. # * +:disk_name+ - String. Specifies the name of the disk. # Reqruied if using existing disk. # * +:host_caching+ - String. Specifies the caching behavior of data disk # The default is ReadOnly. Possible values are: None, ReadOnly, ReadWrite # * +:disk_label+ - String. Specifies the description of the data disk. # * +:disk_size+ - String. Specifies the size of disk in GB # # See http://msdn.microsoft.com/en-us/library/azure/jj157199.aspx # # Returns None def add_data_disk(vm_name, cloud_service_name, options = {}) options[:import] ||= false vm = get_virtual_machine(vm_name, cloud_service_name) if vm path = "/services/hostedservices/#{cloud_service_name}/deployments/#{vm.deployment_name}/roles/#{vm_name}/DataDisks" body = Serialization.add_data_disk_to_xml(vm, options) Azure::Loggerx.info "Adding data disk to virtual machine #{vm_name} ..." request = client.management_request(:post, path, body: body) request.call else Azure::Loggerx.error "Cannot find virtual machine \"#{vm_name}\" under cloud service \"#{cloud_service_name}\"." end end private # Private: Gets the operating system type of an image. # # Returns Linux or Windows def get_image(image_name) image_service = client.vm_image_management image = image_service.list_virtual_machine_images.select { |x| x.name.casecmp(image_name.to_s) == 0 }.first Azure::Loggerx.error_with_exit 'The virtual machine image source is not valid.' unless image image end def generate_cloud_service_name(vm_name) random_string(vm_name + '-service-') end def generate_storage_account_name(vm_name) random_string(vm_name + 'storage').gsub(/[^0-9a-z ]/i, '').downcase[0..23] end def validate_deployment_params(params, options, add_role = false) errors = [] params_keys = %w(vm_name image vm_user) params_keys += ['password'] if options[:os_type] == 'Windows' options_keys = [] options_keys = %w(private_key_file) if certificate_required?(params, options) if add_role params_keys += ['cloud_service_name'] else params_keys += ['location'] end params_keys.each do |key| errors << key if params[key.to_sym].nil? end options_keys.each do |key| errors << key if options[key.to_sym].nil? end if errors.empty? validate_location(params[:location]) unless add_role validate_role_size(options[:vm_size]) params[:certificate] = {} if certificate_required?(params, options) begin params[:certificate][:key] = OpenSSL::PKey.read File.read(options[:private_key_file]) params[:certificate][:cert] = get_certificate(options[:private_key_file]) params[:certificate][:fingerprint] = export_fingerprint(params[:certificate][:cert]) rescue Exception => e Azure::Loggerx.error_with_exit e.message end end else Azure::Loggerx.error_with_exit "You did not provide a valid '#{errors.uniq.join(", ")}' value." end end def certificate_required?(params, options) if options[:os_type] == 'Linux' params[:password].nil? else winrm_with_https(options) end end def winrm_with_https(options) if options[:os_type] == 'Windows' options[:winrm_transport] && options[:winrm_transport].include?('https') && options[:private_key_file] end end def validate_role_size(vm_size) suggested_role_sizes = client.base_management.list_role_sizes if vm_size && !suggested_role_sizes.include?(vm_size) Azure::Loggerx.warn "Value '#{vm_size}' specified for parameter 'vm_size' is not in the list of valid VM role sizes. Suggested values are '#{suggested_role_sizes.join(',')}'" end end def validate_location(location_name) locations = client.base_management.list_locations location = locations.select { |loc| loc.name.downcase == location_name.downcase }.first if location.nil? Azure::Loggerx.error_with_exit "Value '#{location_name}' specified for parameter 'location' is invalid. Allowed values are #{locations.map(&:name).join(',')}" elsif !location.available_services.include?('PersistentVMRole') Azure::Loggerx.error_with_exit "Persistentvmrole not enabled for \"#{location.name}\". Try different location" end end end end end Azure::VirtualMachineManagementService = Azure::VirtualMachineManagement::VirtualMachineManagementService azure-0.7.9/lib/azure.rb0000644000004100000410000001551513112322225015126 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'rubygems' require 'nokogiri' require 'base64' require 'openssl' require 'uri' require 'rexml/document' require 'addressable/uri' require 'faraday' require 'faraday_middleware' require 'azure/core' module Azure autoload :Client, 'azure/client' autoload :ClientServices, 'azure/client_services' autoload :Configurable, 'azure/configurable' autoload :Default, 'azure/default' autoload :HttpClient, 'azure/http_client' autoload :Version, 'azure/version' # helpers because the naming is far too verbose autoload :BaseManagementService, 'azure/base_management/base_management_service' autoload :CloudServiceManagementService, 'azure/cloud_service_management/cloud_service_management_service' autoload :SqlDatabaseManagementService, 'azure/sql_database_management/sql_database_management_service' autoload :StorageManagementService, 'azure/storage_management/storage_management_service' autoload :VirtualMachineImageManagementService, 'azure/virtual_machine_image_management/virtual_machine_image_management_service' autoload :VirtualMachineManagementService, 'azure/virtual_machine_management/virtual_machine_management_service' autoload :VirtualNetworkManagementService, 'azure/virtual_network_management/virtual_network_management_service' module BaseManagement autoload :ManagementHttpRequest, 'azure/base_management/management_http_request' autoload :SqlManagementHttpRequest, 'azure/base_management/sql_management_http_request' autoload :BaseManagementService, 'azure/base_management/base_management_service' autoload :Location, 'azure/base_management/location' autoload :AffinityGroup, 'azure/base_management/affinity_group' autoload :Serialization, 'azure/base_management/serialization' end module Blob autoload :BlobService, 'azure/blob/blob_service' autoload :Blob, 'azure/blob/blob' autoload :Block, 'azure/blob/block' autoload :Container, 'azure/blob/container' autoload :Serialization, 'azure/blob/serialization' end module CloudServiceManagement autoload :CloudServiceManagementService, 'azure/cloud_service_management/cloud_service_management_service' autoload :CloudService, 'azure/cloud_service_management/cloud_service' end module Queue autoload :QueueService, 'azure/queue/queue_service' autoload :Message, 'azure/queue/message' autoload :Queue, 'azure/queue/queue' end module ServiceBus autoload :ServiceBusService, 'azure/service_bus/service_bus_service' autoload :EmptyRuleAction, 'azure/service_bus/empty_rule_action' autoload :SqlRuleAction, 'azure/service_bus/sql_rule_action' autoload :SqlFilter, 'azure/service_bus/sql_filter' autoload :TrueFilter, 'azure/service_bus/true_filter' autoload :CorrelationFilter, 'azure/service_bus/correlation_filter' module Auth autoload :SharedAccessSigner, 'azure/service_bus/auth/shared_access_signer' end end module SqlDatabaseManagement autoload :SqlDatabaseManagementService, 'azure/sql_database_management/sql_database_management_service' autoload :Serialization, 'azure/sql_database_management/serialization' autoload :SqlServer, 'azure/sql_database_management/sql_server' end module StorageManagement autoload :StorageManagementService, 'azure/storage_management/storage_management_service' autoload :Serialization, 'azure/storage_management/serialization' autoload :StorageAccount, 'azure/storage_management/storage_account' end module Table autoload :TableService, 'azure/table/table_service' autoload :Batch, 'azure/table/batch' autoload :Query, 'azure/table/query' end module VirtualMachineImageManagement autoload :VirtualMachineImageManagementService, 'azure/virtual_machine_image_management/virtual_machine_image_management_service' autoload :VirtualMachineDiskManagementService, 'azure/virtual_machine_image_management/virtual_machine_image_management_service' autoload :Serialization, 'azure/virtual_machine_image_management/serialization' autoload :VirtualMachineImage, 'azure/virtual_machine_image_management/virtual_machine_image' autoload :VirtualMachineDisk, 'azure/virtual_machine_image_management/virtual_machine_disk' end module VirtualMachineManagement autoload :VirtualMachineManagementService, 'azure/virtual_machine_management/virtual_machine_management_service' autoload :Serialization, 'azure/virtual_machine_management/serialization' autoload :VirtualMachine, 'azure/virtual_machine_management/virtual_machine' end module VirtualNetworkManagement autoload :VirtualNetworkManagementService, 'azure/virtual_network_management/virtual_network_management_service' autoload :Serialization, 'azure/virtual_network_management/serialization' autoload :VirtualNetwork, 'azure/virtual_network_management/virtual_network' end class << self include Azure::Configurable # API client based on configured options {Configurable} # # @return [Azure::Client] API wrapper def client(options = {}) @client = Azure::Client.new(options) unless defined?(@client) && @client.same_options?(options) @client end private def method_missing(method_name, *args, &block) return super unless client.respond_to?(method_name) client.send(method_name, *args, &block) end end Azure.setup end azure-0.7.9/azure.gemspec0000644000004100000410000000516413112322225015377 0ustar www-datawww-data#------------------------------------------------------------------------- # # Copyright (c) Microsoft and contributors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #-------------------------------------------------------------------------- require 'date' require File.expand_path('../lib/azure/version', __FILE__) Gem::Specification.new do |s| s.name = 'azure' s.version = Azure::Version s.authors = ['Microsoft Corporation', 'AppFog'] s.email = 'azureruby@microsoft.com' s.description = 'Microsoft Azure Client Library for Ruby' s.summary = 'Official ruby client library to consume Microsoft Azure services.' s.homepage = 'http://github.com/azure/azure-sdk-for-ruby' s.license = 'Apache License, Version 2.0' s.files = `git ls-files`.split("\n").reject { |f| f.start_with?("test") } s.bindir = 'bin' s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) } s.required_ruby_version = '>= 1.9.3' s.add_runtime_dependency('addressable', '~> 2.3') s.add_runtime_dependency('azure-core', '~> 0.1') s.add_runtime_dependency('faraday', '~> 0.9') s.add_runtime_dependency('faraday_middleware', '~> 0.10') s.add_runtime_dependency('mime-types', ['>= 1', '< 4.0']) # vagrant-share and other stuff relies on 1 if RUBY_VERSION < "2.1.0" s.add_runtime_dependency('nokogiri', '~> 1.6.0') else s.add_runtime_dependency('nokogiri', '~> 1.7') end s.add_runtime_dependency('systemu', '~> 2.6') s.add_runtime_dependency('thor', '~> 0.19') s.add_development_dependency('dotenv', '~> 2.0') s.add_development_dependency('minitest', '~> 5') s.add_development_dependency('minitest-reporters', '~> 1') s.add_development_dependency('mocha', '~> 1.0') s.add_development_dependency('rake', '~> 10.0') s.add_development_dependency('timecop', '~> 0.7') s.add_development_dependency('yard', '~> 0.8') s.add_development_dependency('vcr', '~> 3.0') end azure-0.7.9/.gitignore0000644000004100000410000000045013112322225014665 0ustar www-datawww-data.rvmrc *.gem *.rbc *.swp *.tmproj *~ .\#* tags /pkg /doc Gemfile.lock nbproject/* *.gem .idea .project .bundle .config .yardoc _yardoc coverage doc/ lib/bundler/man pkg rdoc .DS_Store .ruby-version .env *.pem !service_management/azure/test/fixtures/management_certificate.pem *.publishsettings azure-0.7.9/.env_sample0000644000004100000410000000134213112322225015030 0ustar www-datawww-data# This is a sample of what your .env file should look like if you wanted to run integration tests. # If you move this file to the .env and replace the environment variables with your own values, then the environment # will be included when the rake test tasks are executed. Otherwise, you could just export the environment vars # yourself. AZURE_STORAGE_ACCOUNT="your storage account" AZURE_STORAGE_ACCESS_KEY="your storage access key" AZURE_SERVICEBUS_NAMESPACE="your sb namespace" AZURE_SERVICEBUS_ACCESS_KEY="your sb access key" AZURE_MANAGEMENT_CERTIFICATE="your management cert path" AZURE_SUBSCRIPTION_ID="your subscription id" AZURE_SERVICEBUS_SAS_KEY="Service Bus SAS key" AZURE_SERVICEBUS_SAS_KEY_NAME="Service Bus SAS key name"azure-0.7.9/README.md0000644000004100000410000005052313112322225014162 0ustar www-datawww-data# Microsoft Azure SDK for Ruby - ASM [![Build Status](https://travis-ci.org/Azure/azure-sdk-for-ruby.png?branch=asm)](https://travis-ci.org/Azure/azure-sdk-for-ruby) [![Gem Version](https://badge.fury.io/rb/azure.svg)](https://badge.fury.io/rb/azure) This project provides a Ruby package for Azure Service Management, which makes it easy to access and manage Microsoft Azure Services like Storage, Service Bus and Virtual Machines. * **Azure Service Management (ASM) or Classic**: Supported by most existing Azure resources. If you're looking for **Azure Resource Management (ARM)**, preview version of the Ruby SDK is available [here](https://github.com/Azure/azure-sdk-for-ruby) Information on Azure deployment models: [https://azure.microsoft.com/en-us/documentation/articles/azure-classic-rm/](https://azure.microsoft.com/en-us/documentation/articles/azure-classic-rm/) ## Azure Service Management * Cloud Service Management * [SQL Database Server Management](https://github.com/Azure/azure-sdk-for-ruby/tree/asm#sql) * Storage Account Management * [Virtual Network Management](https://github.com/Azure/azure-sdk-for-ruby/tree/asm#vnets) ## Azure Services * [Storage](https://github.com/Azure/azure-sdk-for-ruby/tree/asm#storage) Azure Storage is now availabe in its own preview gem and GitHub [repo](https://github.com/Azure/azure-storage-ruby) * [Service Bus](https://github.com/Azure/azure-sdk-for-ruby/tree/asm#usage) * [Relays](https://github.com/Azure/azure-sdk-for-ruby/tree/asm#relays) * [Topics](https://github.com/Azure/azure-sdk-for-ruby/tree/asm#topics) * Queues # Supported Ruby Versions * Ruby 2+ Note: x64 Ruby for Windows is known to have some compatibility issues. # Getting Started with Azure Service Management ## Install the rubygem package You can install the azure rubygem package directly. ```bash gem install azure ``` :warning: Azure storage is moving into its own azure-storage [gem](https://rubygems.org/gems/azure-storage) and [GitHub repo](https://github.com/Azure/azure-storage-ruby). It's in preview today. ## Setup Connection You can use this SDK against the Microsoft Azure Services in the cloud, or against the local Storage Emulator if you are on Windows. Service Bus and Microsoft Azure Service Management emulation are not supported. Of course, to use the Microsoft Azure Services in the cloud, you need to first [create a Microsoft Azure account](http://www.azure.com/en-us/pricing/free-trial/). After that, you can get the information you need to configure Storage and Service Bus from the [Microsoft Azure Portal](https://manage.windowsazure.com). There are two ways you can set up the connections: 1. [via code](#via-code) 2. [via environment variables](#via-environment-variables) ### Via Code * Against Microsoft Azure Services in the cloud ```ruby require "azure" Azure.storage_account_name = "" Azure.storage_access_key = "" # Configure these 3 properties to use Service Bus Azure.sb_namespace = "" Azure.sb_access_key = "" Azure.sb_issuer = "" # Configure these 3 properties to use Service Management. We support passwordless pfx & pem cert formats. Azure.management_certificate = "" Azure.subscription_id = "" # Configure a ca_cert.pem file if you are having issues with ssl peer verification Azure.ca_file = "./ca_file.pem" # Or create a specific instance of an Azure.client, which will inherit your default configuration settings. client = Azure.client(storage_account_name: "your account name", storage_access_key: "your access key") ``` * Against local Emulator (Windows Only) ```ruby require "azure" # Configure these 2 properties to use local Storage Emulator Azure.storage_account_name = "devstoreaccount1" Azure.storage_access_key = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==" Azure.storage_blob_host = "http://127.0.0.1:10000/devstoreaccount1" Azure.storage_queue_host = "http://127.0.0.1:10001/devstoreaccount1" Azure.storage_table_host = "http://127.0.0.1:10002/devstoreaccount1" # For Azure Government make sure to provide the full URI to the endpoint # End point mapping at https://azure.microsoft.com/en-us/documentation/articles/azure-government-developer-guide/ # Local Service Bus Emulator is not supported # Local Service Management emulation is not supported ``` ### Via Environment Variables * Against Microsoft Azure Services in the cloud * Storage ```bash AZURE_STORAGE_ACCOUNT = AZURE_STORAGE_ACCESS_KEY = ``` * Service Bus ```bash AZURE_SERVICEBUS_NAMESPACE = AZURE_SERVICEBUS_ACCESS_KEY = AZURE_SERVICEBUS_ISSUER = ``` * Service Management ```bash AZURE_MANAGEMENT_CERTIFICATE = AZURE_SUBSCRIPTION_ID = AZURE_MANAGEMENT_ENDPOINT = AZURE_SQL_DATABASE_MANAGEMENT_ENDPOINT = AZURE_SQL_DATABASE_AUTHENTICATION_MODE = <:management_certificate or :sql_server> ``` * [SSL Certificate File](https://gist.github.com/fnichol/867550) ```bash SSL_CERT_FILE= ``` * Against local Emulator (Windows Only) * Storage ```bash AZURE_STORAGE_ACCOUNT = devstoreaccount1 AZURE_STORAGE_ACCESS_KEY = Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw== AZURE_STORAGE_BLOB_HOST = http://127.0.0.1:10000/devstoreaccount1 AZURE_STORAGE_QUEUE_HOST = http://127.0.0.1:10001/devstoreaccount1 AZURE_STORAGE_TABLE_HOST = http://127.0.0.1:10002/devstoreaccount1 ``` * Service Bus: not supported * Service Management: not supported # Usage ## Storage ### Setup your Storage Credentials ```ruby # Require the azure rubygem require "azure" # Add your default storage credentials Azure.storage_account_name = "your account name" Azure.storage_access_key = "your access key" # Or create a specific instance of an Azure.client client = Azure.client(storage_account_name: "your account name", storage_access_key: "your access key") default_blobs = Azure.blobs # uses the Azure.storage_account_name and Azure.storage_access_key blobs = client.blobs # uses the client.storage_account_name and client.storage_access_key ``` Azure storage is available its own azure-storage [gem](https://rubygems.org/gems/azure-storage) and [GitHub repo](https://github.com/Azure/azure-storage-ruby) ### Relay ```ruby # Require the azure rubygem require "azure" # Create an azure service bus object service_bus = Azure.service_bus # Create a relay endpoint with just the endpoint name relay1 = service_bus.create_relay("test-relay-1", { :relay_type => "Http" }) # Create a relay endpoint with a relay object relay2 = Azure::ServiceBus::Relay.new("test-relay-2") relay2.requires_client_authorization = false relay2 = service_bus.create_relay(relay2) # Delete a relay endpoint service_bus.delete_relay("test-relay2") ``` ### Topics ```ruby # Require the azure rubygem require "azure" # Create an azure service bus object service_bus = Azure.service_bus # Create a topic with just the topic name topic1 = service_bus.create_topic("test-topic-1") # Create a topic with a topic object topic2 = Azure::ServiceBus::Topic.new("test-topic-2") topic2.max_size_in_megabytes = 2048 topic2 = service_bus.create_topic(topic2) # Create a subscription subscription = Azure::ServiceBus::Subscription.new("test-subscription-1") subscription.topic = topic1.name subscription = service_bus.create_subscription(subscription) # Send a topic message with just the message body azure_service_bus.send_topic_message(topic1, "test topic message") # Send a topic message with a brokered message object message = Azure::ServiceBus::BrokeredMessage.new("another test topic message") message.correlation_id = "test-correlation-id-1" service_bus.send_topic_message(topic1, message) # Receive a subscription message message = service_bus.receive_subscription_message(topic1.name, subscription.name) # Delete a subscription message service_bus.delete_subscription_message(message) # Delete a subscription service_bus.delete_subscription(subscription) # Delete a topic service_bus.delete_topic(topic1) ``` ## Virtual Machine Management ```ruby # Require the azure rubygem require 'azure' # Configure these properties Azure.management_certificate = "path to *.pem or *.pfx file" Azure.subscription_id = "your subscription id" # Create a virtual machine service object vm_management = Azure.vm_management # Get a list of existing virtual machines in your subscription vm_management.list_virtual_machines # API to shutdown Virtual Machine vm_management.shutdown_virtual_machine('vm_name', 'cloud_service_name') # API to start Virtual Machine vm_management.start_virtual_machine('vm_name', 'cloud_service_name') # API to restart Virtual Machine vm_management.restart_virtual_machine('vm_name', 'cloud_service_name') # API for add disk to Virtual Machine options = { :disk_label => 'disk-label', :disk_size => 100, #In GB :import => false, :disk_name => 'Disk name' #Required when import is true } vm_management.add_data_disk('vm_name', 'cloud_service_name', options) # API to add/update Virtual Machine endpoints endpoint1 = { :name => 'ep-1', :public_port => 996, :local_port => 998, :protocol => 'TCP', } endpoint2 = { :name => 'ep-2', :public_port => 997, :local_port => 997, :protocol => 'TCP', :load_balancer_name => ‘lb-ep2’, :load_balancer => {:protocol => 'http', :path => 'hello'} } vm_management.update_endpoints('vm_name', 'cloud_service_name', endpoint1, endpoint2) # API to delete Virtual Machine endpoint vm_management.delete_endpoint('vm_name', 'cloud_service_name', 'endpoint_name') # API to delete Virtual Machine options = { :delete_vhd => true } vm_management.delete_virtual_machine('vm_name', 'cloud_service_name', options) # API to start deployment params = { :vm_name => 'vm_name', :vm_user => 'azureuser', :image => '5112500ae3b842c8b9c604889f8753c3__OpenLogic-CentOS-63APR20130415', :password => 'Password', :location => 'West US' } options = { :storage_account_name => 'storage_suse', :cloud_service_name => 'cloud_service_name', :deployment_name =>'vm_name', :tcp_endpoints => '80,3389:3390', :private_key_file => './private_key.key', # required for ssh :ssh_port => 2222, :vm_size => 'Small', # Use any Azure VM size :affinity_group_name => 'affinity1', :virtual_network_name => 'xplattestvnet', :subnet_name => 'subnet1', :availability_set_name => 'availabiltyset1', :reserved_ip_name => 'reservedipname' } vm_management.create_virtual_machine(params,options) # API usage to add new roles under cloud service creating VM # API add_role create multiple roles under the same cloud service. Atleast a single deployment should be created under a hosted service. params = { :vm_name => 'vm_name', :cloud_service_name => 'cloud_service_name', :vm_user => 'azureuser', :image => 'a699494373c04fc0bc8f2bb1389d6106__Win2K8R2SP1-Datacenter-201305.01-en.us-127GB.vhd', :password => 'ComplexPassword', } options = { :storage_account_name => 'storage_suse', :winrm_transport => ['https','http'], # Currently http(s) is supported. :tcp_endpoints => '80,3389:3390', :private_key_file => './private_key.key', # Required for winrm(https) certificate. :winrm_https_port => 5999, :winrm_http_port => 6999, # Used to open different powershell port :vm_size => 'Small', # Use any Azure VM size :availability_set_name => 'availabiltyset' } vm_management.add_role(params, options) ``` ## List Images ```ruby # Get a list of available virtual machine images vm_image_management = Azure.vm_image_management vm_image_management.list_virtual_machine_images ``` ## Base Management ```ruby # Get a list of available regional data center locations base_management = Azure.base_management base_management.list_locations ``` ### Affinity Group Management ```ruby # Require the azure rubygem require 'azure' # Create a affinity group service object base_management = Azure.base_management # Get a list of affinity group that are provisioned for a subscription. base_management.list_affinity_groups # API to delete affinity group base_management.delete_affinity_group('affinity-group-name') # API to add a new affinity group to a subscription options = {:description => 'Some Description'} base_management.create_affinity_group('affinity-group-name', 'West US', 'Label Name', options) # API to update affinity group options = {:description => 'Some Description'} base_management.update_affinity_group('affinity-group-name', 'Label Name', options) # API to list properties associated with the specified affinity group base_management.get_affinity_group('affinity-group-name') ``` ## SQL Database Server Management ```ruby # Require the azure rubygem require 'azure' # Configure these properties Azure.management_certificate = "path to *.pem or *.pfx file" Azure.subscription_id = "your subscription id" # Create a database server service object sql_db_service = Azure.sql_database_management # Get a list of SQL Database servers that are provisioned for a subscription. sql_db_service.list_servers # API to delete SQL Database server sql_db_service.delete_server('server_name') # API to adds a new SQL Database server to a subscription sql_db_service.create_server('admin-login', 'ComplexPassword', 'West US') # API to sets the administrative password of a SQL Database server for a subscription sql_db_service.reset_password('server-name', 'NewPassword') # Get a list of all the server-level firewall rules for a SQL Database server that belongs to a subscription sql_db_service.list_sql_server_firewall_rules("server-name") # API to adds a new server-level firewall rule or updates an existing server-level firewall rule for a SQL Database server with requester’s IP address. sql_db_service.delete_sql_server_firewall_rule("server-name", "rule-name") # API to add/updates server-level firewall rule for a SQL Database server that belongs to a subscription ip_range = {:start_ip_address => "0.0.0.1", :end_ip_address => "0.0.0.5"} sql_db_service.set_sql_server_firewall_rule("server-name", "rule-name", ip_range) # If ip_range was not specified in the above api then the IP of the machine from where the api is being called would be set as the rule. # To toggle between the option to allow Microsoft Azure services to access db server similar to azure portal just set the fire wall rule # with iprange to be 0.0.0.0 as start and end.Remove the rule to unset this option. ``` ## Virtual Network Management ```ruby # Require the azure rubygem require 'azure' # Create a virtual network service object vnet = Azure.network_management # API to get a list of virtual networks created for a subscription. vnet.list_virtual_networks # API to configure virtual network with required and optional parameters address_space = ['172.16.0.0/12', '10.0.0.0/8', '192.168.0.0/24'] subnets = [{:name => 'subnet-1', :ip_address=>'172.16.0.0', :cidr=>12}, {:name => 'subnet-2', :ip_address=>'10.0.0.0', :cidr=>8}] dns_servers = [{:name => 'dns-1', :ip_address=>'1.2.3.4'}, {:name => 'dns-2', :ip_address=>'8.7.6.5'}] options = {:subnet => subnets, :dns => dns_servers} vnet.set_network_configuration('virtual-network-name', 'location_name', address_space, options) # API to configure virtual network from xml file that can be exported from management portal and customized to add or delete vnet vnetxml = './customnetwork.xml' vnet.set_network_configuration(vnetxml) ``` # Getting Started with Certificates Currently the sdk supports *.pem or *.pfx (passwordless pfx) for service management operations. Following are the steps discussed on various cert operations. ## Get Started with Publish Settings * To create a pfx from the publishsettings, simply download the publishsettings file for your subscription [https://manage.windowsazure.com/publishsettings](https://manage.windowsazure.com/publishsettings/index?client=powershell). Make sure you have this gem installed and run `pfxer transform --in [path to your .publishsettings file]`. This will create a .pfx from your publish settings file which can be supplied as a cert parameter for Service Management Commands. ## Get Started with OpenSSL * Using the following openssl commands to create a cert and upload to Azure Management * Generate public and private `openssl req -x509 -nodes -days 365 -newkey rsa:1024 -keyout cert.pem -out cert.pem` * Generate public .cer for Azure upload `openssl x509 -inform pem -in cert.pem -outform der -out mgmt.cer` * Upload the `mgmt.cer` to Azure Management through [https://management.azure.com](https://management.azure.com) * Use cert.pem as your cert parameter for Service Management Commands. # Contribute Code or Provide Feedback 1. Fork it 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 new Pull Request ## Development Environment Setup ### Download Source Code To get the source code of the SDK via **git** just type: ```bash git clone https://github.com/Azure/azure-sdk-for-ruby.git cd ./azure-sdk-for-ruby git remote add upstream https://github.com/Azure/azure-sdk-for-ruby.git git checkout -b asm upstream/asm ``` Run bundler to install all the gem dependencies: ```bash bundle install ``` ### Setup the Environment for Integration Tests If you would like to run the integration test suite, you will need to setup environment variables which will be used during the integration tests. These tests will use these credentials to run live tests against Azure with the provided credentials (you will be charged for usage, so verify the clean up scripts did their job at the end of a test run). The root of the project contains a .env_sample file. This dot file is a sample of the actual environment vars needed to run the integration tests. Do the following to prepare your environment for integration tests: * Copy .env_sample to .env **relative to root of the project dir** * Update .env with your credentials **.env is in the .gitignore, so should only reside locally** Azure Service Management integration tests use VCR, so they are recorded and can be easily played back without consuming any Azure live resources. ### Run Tests You can use the following commands to run: * All the tests: ``rake test``. **This will run integration tests if you have .env file or env vars setup** * A specific suite of tests: ``rake test:unit``, ``rake test:integration``, ``rake test:integration:blob``, etc. * one particular test file: ``ruby -I"lib:test" ""`` To run VCR tests: * Set the following environment variable ``INTEG_RECORDED = true``, then run ``rake test:recorded`` ### Generate Documentation Running the command ``yard`` will generate the API documentation in the `./doc` directory. ## Provide Feedback If you encounter any bugs with the library please file an issue in the [Issues](https://github.com/Azure/azure-sdk-for-ruby/issues) section of the project. Please make sure to label the issues with either arm or asm to help us expedite the process. # Maintainers * [David Justice](https://github.com/devigned) # Azure CLI Tooling For documentation on [Azure PowerShell](http://github.com/azure/azure-powershell). For documentation on [Azure CLI](http://github.com/azure/azure-xplat-cli). --- This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.