├── .coveralls.yml ├── .gitignore ├── .travis.yml ├── CHANGELOG.textile ├── Gemfile ├── LICENSE.md ├── README.md ├── doc_src ├── ContributionGuide.md ├── Foundation.md ├── Model Layer.md ├── Welcome.md ├── cla-corporate.md └── cla-individual.md ├── examples ├── account_info.rb ├── account_servers.rb ├── create_ticket.rb ├── open_tickets.rb ├── order_bare_metal_package.rb ├── order_server_firewall.rb ├── order_virtual_server.rb └── ticket_info.rb ├── lib ├── softlayer │ ├── APIParameterFilter.rb │ ├── Account.rb │ ├── AccountPassword.rb │ ├── BareMetalServer.rb │ ├── BareMetalServerOrder.rb │ ├── BareMetalServerOrder_Package.rb │ ├── Client.rb │ ├── Config.rb │ ├── Datacenter.rb │ ├── DynamicAttribute.rb │ ├── ImageTemplate.rb │ ├── ModelBase.rb │ ├── NetworkComponent.rb │ ├── NetworkMessageDelivery.rb │ ├── NetworkMonitor.rb │ ├── NetworkService.rb │ ├── NetworkStorage.rb │ ├── NetworkStorageAllowedHost.rb │ ├── NetworkStorageCredential.rb │ ├── NetworkStorageGroup.rb │ ├── ObjectFilter.rb │ ├── ObjectMaskParser.rb │ ├── ObjectMaskProperty.rb │ ├── ObjectMaskToken.rb │ ├── ObjectMaskTokenizer.rb │ ├── ProductItemCategory.rb │ ├── ProductPackage.rb │ ├── Server.rb │ ├── ServerFirewall.rb │ ├── ServerFirewallOrder.rb │ ├── Service.rb │ ├── Software.rb │ ├── SoftwarePassword.rb │ ├── Ticket.rb │ ├── UserCustomer.rb │ ├── UserCustomerExternalBinding.rb │ ├── VLANFirewall.rb │ ├── VLANFirewallOrder.rb │ ├── VirtualDiskImage.rb │ ├── VirtualDiskImageSoftware.rb │ ├── VirtualServer.rb │ ├── VirtualServerOrder.rb │ ├── VirtualServerOrder_Package.rb │ ├── VirtualServerUpgradeOrder.rb │ ├── base.rb │ └── object_mask_helpers.rb └── softlayer_api.rb ├── rakefile ├── softlayer_api.gemspec └── spec ├── APIParameterFilter_spec.rb ├── Account_spec.rb ├── BareMetalServerOrder_Package_spec.rb ├── BareMetalServerOrder_spec.rb ├── BareMetalServer_spec.rb ├── Client_spec.rb ├── Config_spec.rb ├── Datacenter_spec.rb ├── DynamicAttribute_spec.rb ├── ModelBase_spec.rb ├── ObjectFilter_spec.rb ├── ObjectMaskParser_spec.rb ├── ObjectMaskProperty_spec.rb ├── ProductPackage_spec.rb ├── ServerFirewall_spec.rb ├── Server_spec.rb ├── Service_spec.rb ├── Ticket_spec.rb ├── VLANFirewall_spec.rb ├── VirtualServerOrder_spec.rb ├── VirtualServerUpgradeOrder_spec.rb ├── VirtualServer_spec.rb ├── XMLRPC_Convert_spec.rb ├── fixtures ├── Hardware_createObjectOptions.json ├── Product_Package.json ├── Virtual_Guest_createObjectOptions.json ├── datacenter_locations.json ├── test_account.json ├── test_bare_metal.json ├── test_tickets.json ├── test_virtual_servers.json ├── ticket_subjects.json ├── virtual_server_upgrade_options └── virtual_server_upgrade_options.json ├── object_mask_helpers_spec.rb ├── shared_server.rb └── spec_helper.rb /.coveralls.yml: -------------------------------------------------------------------------------- 1 | service_name: travis-ci -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.*~ 3 | *.gem 4 | .bundle/ 5 | log/ 6 | internal_examples/ 7 | doc/ 8 | coverage/ 9 | Gemfile.lock 10 | vendor/ 11 | pkg/ 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - "2.3.0" 4 | - "2.2.4" 5 | - "2.1.8" 6 | - "2.0.0" 7 | - jruby-19mode 8 | -------------------------------------------------------------------------------- /CHANGELOG.textile: -------------------------------------------------------------------------------- 1 | *3.2.3* 2 | * Support for profiles. 3 | * Support for HVM. 4 | 5 | 6 | *3.2.2* 7 | * Fix NoMethodError when getting the datacenters out of an ImageTemplate and the value was nil. 8 | * Fix NoMethodError when getting public and private images with a result limit of 1. 9 | 10 | *3.2.1* 11 | * Fix a crashing issue where a Bare Metal server order tried to retrieve the hardware ordered before it has been provisioned. 12 | 13 | *3.2* 14 | * Add password-based authentication with `SoftLayer::Client.with_password(username: '...', password: '...', ...)`. 15 | 16 | *3.1.0* 17 | * Many SoftLayer Attributes are now shadowed with snake_case names. The old CamelCase versions are deprecated and will be removed in the next major release 18 | * New functionality related to network monitoring has been added 19 | 20 | *3.0* 21 | * Substantially rewrote the ObjectFilter class. ObjectFilters used to be hashes which made it easy to manipulate their content incorrectly. The new implementation has a strict interface that makes it harder to manipulate filters incorrectly. 22 | * Added a model for Virtual Server Image Templates (SoftLayer::ImageTemplate) - VirtualServerOrder now requires an instance of this class rather than allowing you to provide the global_id of an image 23 | * Added a model for data centers (SoftLayer::Datacenter). Bare Metal, Bare Metal Package, and Virtual server orders now use an instance of Datacenter to identify where their servers will be provisioned. The routines in those classes which used to provide lists of valid data center names now return data center objects. 24 | * Virtual Server Upgrades are now handled by the VirtualServerUpgradeOrder class and not the VirtualServer class. This change was made for several reasons. Firt and foremost, it allows multiple aspects of a virtual server to be upgraded at once without having to wait on separate transactions to complete between upgrades. Secondly it opens the door for additional upgrades (for example, to disk configuration) to be added in the future. 25 | * Added a method to reboot servers. 26 | * The routine to retreive the open tickets on an account has been moved from the Ticket class. The set of open tickets is now a dynamic property of an account object. 27 | * The Model Layer now includes models for Server (aka. Shared) and VLAN (aka. Dedicated) firewalls in the ServerFirewall, and VLANFireall classes respectively. There are corresponding classes for ordering firewalls (ServerFirewallOrder and VLANFirewallOrder). To facilitate the process of locating the 'id' for a firewall, the Account class includes the find_VLAN_with_number routine which lets you look up the segments of a firewall from the VLAN nubmer. 28 | 29 | *2.2.2* 30 | * Fixed a bug in BareMetalServerOrder_Package.rb where the order template did not use an array for the "hardware" key. This lead to an order template that would be accepted by verifyOrder, but rejected by placeOrder. An internal issue to review verifyOrder has also been generated. (reported by Rohit Singh) 31 | 32 | *2.2* 33 | * Added the ability to set a timout for network requests. The timeout is given when a client is created by passing the :timeout hash parameter when creating a client. The value of the parameter is an integer number of seconds. 34 | * Fixed a bug in VirtualServer#capture_image 35 | 36 | *2.1.1* 37 | * Virtual server upgrades no longer raise exceptions 38 | * Formalized the RDoc documentation process. Added overview and welcome documentation and changed the README so it directs folks to the new documentation. 39 | 40 | *2.1.0* 41 | * Began implementing a model framework that allows Ruby developers to work with elements in the SoftLayer API in a more object-oriented fashion. The first release of this framework includes the Ticket, VirtualServer, and BareMetalServer classes. 42 | 43 | *2.0.1* 44 | * Fix broken gem configparser dependency 45 | 46 | *2.0.0* 47 | * Switched the Ruby API client to use XML-RPC when calling the SoftLayer API rather than using the REST-like interface. 48 | * Result limits are now specified using @result_limit(offset,limit)@. 49 | The @result_offset@ API filter has been removed. 50 | * The @object_mask@ call modifier no longer accepts Ruby structures. It accepts strings that are Object Masks in the "Extended Object Mask":http://sldn.softlayer.com/article/Object-Masks format. 51 | * Changed the mechanism for obtaining services to include the Client class. This makes the Ruby API very similar to the API presented by the Python bindings. The old mechanism for obtaining services still works to preserve backward compatibility but you will receive deprecation warnings in debug mode. 52 | 53 | *1.0.8* 54 | * Set a default User-Agent string to be sent with all requests to SoftLayer API. Provide interface to set a custom User-Agent string. 55 | 56 | *1.0.7* 57 | * Calls to the @getObject@ method of any service should not take parameters. The gem now warns if you make this type of call and ignores the parameters. This prevents @SoftLayer_Virtual_Guest::getObject@ from accidentally creating (billable) VirtualServer instances. 58 | 59 | *1.0.6* 60 | * Make all API calls with either a @GET@ or a @POST@ as the HTTP verb. 61 | 62 | *1.0.5* 63 | * Fixed a bug where empty hashes and empty arrays would not generate meaningful object masks 64 | 65 | *1.0.4* 66 | * Fixed a bug where the @result_limit@ and @result_offset@ object filters were just not working. 67 | 68 | *1.0.3* 69 | * Added a request filter to add result limits to request. Submitted by JN. Thanks! 70 | 71 | *1.0.2* 72 | * We have some API routines that start with 'get' but expect arguments anyway. The code now uses HTTP POST to send requests for which the user has provided arguments regardless of the name of the routine. 73 | 74 | *1.0*, *1.0.1* 75 | * Initial release of the gem 76 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec 4 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright © 2010-2014 [SoftLayer Technologies, Inc.](http://www.softlayer.com/) All rights reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Gem Version](https://badge.fury.io/rb/softlayer_api.svg)](http://badge.fury.io/rb/softlayer_api) 2 | [![Build Status](https://travis-ci.org/softlayer/softlayer-ruby.svg?branch=master)](https://travis-ci.org/SLsthompson/softlayer-ruby) 3 | [![Coverage Status](https://coveralls.io/repos/softlayer/softlayer-ruby/badge.png?branch=master)](https://coveralls.io/r/SLsthompson/softlayer-ruby?branch=master) 4 | 5 | # SoftLayer API for Ruby 6 | 7 | The softlayer-ruby project creates a [Ruby Gem](http://rubygems.org/gems/softlayer_api) which provides language bindings to the [SoftLayer API](http://sldn.softlayer.com/article/The_SoftLayer_API) for the [Ruby](http://www.ruby-lang.org) programming language. 8 | 9 | The heart of the project a foundation layer (represented by the Client and Service classes) that allows SoftLayer customers to make direct calls to the SoftLayer API. The Gem also builds a object heirarchy on top of that foundation which provides an abstract model which insulates scripts from direct dependency on the low-level details of the SoftLayer API. 10 | 11 | More comprehensive documentation on using the `softlayer_api` Gem, and contributing to the project, may be found by cloning this project and generating the documentation. 12 | 13 | ## Generating Documentation 14 | 15 | Once you have cloned the project, from the main project directory you can generate documentation by installing the bundle and using `rake`: 16 | 17 | $ cd softlayer-ruby 18 | $ bundle install 19 | $ bundle exec rake rdoc 20 | 21 | This will create a new folder named `doc` inside of the project source and populate it with project documentation. You may then open the documentation file `doc/index.html` in your browser to begin exploring the documentation. 22 | 23 | ## Author 24 | 25 | This software is written by the SoftLayer Development Team [sldn@softlayer.com](mailto:sldn@softlayer.com). 26 | 27 | Please join us at [Stack Overflow](https://stackoverflow.com/). Stack Overflow Best Practices Tip: Tag your posts with “SoftLayer” so our team can easily find your post. 28 | 29 | # Contributer License Agreement 30 | 31 | Contributions to the softlayer-ruby project require the submission of a contributer license agreement. Please generate the documentation and carefully refer to the Contribution Guide to participate. 32 | 33 | # Copyright and License 34 | 35 | The `softlayer_api` Ruby Gem and it's source files are Copyright © 2010-2014 [SoftLayer Technologies, Inc](http://www.softlayer.com/). The software is provided under an MIT license. Details of that license can be found in the embedded LICENSE.md file. 36 | 37 | #Warranty 38 | 39 | This software is provided “as is” and without any express or implied warranties, including, without limitation, the implied warranties of merchantability and fitness for a particular purpose. 40 | -------------------------------------------------------------------------------- /doc_src/Model Layer.md: -------------------------------------------------------------------------------- 1 | # `softlayer_api` Model Layer 2 | 3 | The Model Layer builds upon the Foundation layer and offers abstractions of the entities in the SoftLayer environment. For example, computing assets in the SoftLayer environment are found in two separate services [SoftLayer_Hardware](http://sldn.softlayer.com/reference/services/SoftLayer_Hardware) and [SoftLayer_Virtual_Guest](http://sldn.softlayer.com/reference/services/SoftLayer_Virtual_Guest). The Model Layer, however, creates a Server abstraction on top of those services. By doing so, a Ruby script can work with an instance of the SoftLayer::Server object class, send that object the "cancel!" message, and trust the underlying object framework to route the message to the appropriate service and routine within the network API. 4 | 5 | The Model Layer is not meant to be complete. In fact, it models just a few key elements of the SoftLayer environment at this point, however we hope the Model Layer will grow and encompass more of the entities found in the API over time. In the interim, however, the current framework includes some convenient bridges that allow Ruby scripts to move from working with the abstractions of the Model Layer to the lower-level routines of the Foundation API easily. 6 | 7 | The details of the individual classes that for the object class hierarchy of the Model layer are included in the API documentation. This document will discuss some of the general features of the Model Layer and the bridges that are in place to help code move between the Foundation and Model Layers. 8 | 9 | # The ModelBase Class 10 | 11 | The ModelBase is the abstract base class of object class hierarchy that forms the Model Layer of the `softlayer_api` Gem. An instance of ModelBase represents a single entity within the SoftLayer API. 12 | 13 | In the Foundation layer, SoftLayer entities are represented as a Ruby hash whose keys and values are the are property names and property values of the entity. In the Model Layer, SoftLayer entities are represented by instances of the concrete subclasses of the Model Base class. 14 | 15 | In implementation terms, an instance of the ModelBase class (or more accurately and instance of a concrete subclass of the ModelBase class) encapsulates the hashes of the Foundation layer defines the attributes and operations that form a convenient model for working with the underlying entity. 16 | 17 | Here we will discuss the general features that all concrete subclasses of ModelBase share. 18 | 19 | ## Initializing Instances 20 | 21 | The initializer for classes in the ModelBase hierarchy are declared: 22 | 23 | def initialize(softlayer_client, network_hash) 24 | … 25 | end 26 | 27 | The first argument is the client that the object may use to make requests to the network API. The second is the `network_hash`, the hash representation of the entity as returned by the network API. 28 | 29 | The hash used to initialize an instance of ModelBase *must* contain a key, `id`, whose value is the `id` of the SoftLayer entity that the object model instance will represent. Correspondingly, the ModelBase class defines the `id` as having the same value as the `id` property in the network hash. 30 | 31 | ## Updating Instances from the Network 32 | 33 | If you wish to ask an instance of ModelBase to update itself with the latest information from the SoftLayer network API, you may call the `refresh_details` method defined in the ModelBase class. This method is likely to make one or more calls through to the network API and will, consequently, incur the overhead of network communication. (Note: subclasses should not override `refresh_details` but should, instead, override the `softlayer_properties` method. Correspondingly, the softlayer_properties method is an implementation detail of ModelBase and is not intended to be called by outside code.) 34 | 35 | ## Bridging to the Foundation layer 36 | 37 | Because the Model Layer offers limited coverage of the vast expanse of functionality found in the network API, it is often necessary to move from an object instance in the Model Layer to the implementation details from the Foundation layer. The Model Base class includes several features to help bridge the between layers gap. 38 | 39 | ### Accessing Properties 40 | 41 | The ModelBase class defines the subscript operator (`[]`) to accept a property name as a string and return that property of the underlying hash. For example, given a ticket, represented by an instance of the SoftLayer::Ticket model layer class, there is no Model Layer representation corresponding to the `serviceProvider` property of the SoftLayer ticket entity. However, the subscript operator can be used to access that property from a model layer object: 42 | 43 | ticket = SoftLayer::Ticket.ticket_with_id(123456) 44 | service_provider = ticket['serviceProvider'] 45 | 46 | In this case we ask the ticket for the value of the `serviceProvider` property. Note that the argument to the subscript operator is a string containing the property name. 47 | 48 | This technique can only return values stored in the `softlayer_hash` encapsulated in the ModelBase class. Many classes in the Model Layer limit the information retrieved from the network (using object masks) to a subset of the full set of properties available through the network API. Scripts can check whether or not a given property is included in the underlying hash by calling the `has_sl_property?` method of ModelBase. 49 | 50 | ### Calling Network API routines 51 | 52 | Where possible, each subclass of ModelBase should provide a `service` attribute. The value of that attribute should be an instance of SoftLayer::Service, with Service Helpers applied, suitable for addressing the SoftLayer entity represented by the object through the network API. For example, consider an instance of the Model Layer class SoftLayer::Ticket which represents a service ticket within the network API. The Ticket class defines the `service` attribute of that instance to be the value: 53 | 54 | self.softlayer_client['Ticket'].object_with_id(self.id) 55 | 56 | because this service object includes `object_with_id` service helper the result can be used to call through to the network API and that call will be directed at the SoftLayer_Ticket entity with the same id as the model object. For example: 57 | 58 | ticket_object.service.getAttachedFile(file_id) 59 | 60 | calls the `getAttachedFile` method of the SoftLayer_Ticket service which is not yet available in the SoftLayer::Ticket object model class. 61 | 62 | ## The rest of the model 63 | 64 | The ModelBase class is literally the tip of the iceberg for the Model Layer, the bulk of functionality found in this layer is defined by concrete classes like SoftLayer::Ticket and SoftLayer::BareMetalServer, and in other abstract base classes like SoftLayer::Server. To understand the model presented by each class, we invite you to explore the API documentation. -------------------------------------------------------------------------------- /doc_src/Welcome.md: -------------------------------------------------------------------------------- 1 | # Welcome 2 | 3 | The `softlayer_api` Ruby Gem provides a convenient way to call into the SoftLayer API from the Ruby programming language. This is accomplished using the XML-RPC interface provided by SoftLayer and the XMLRPC client built into the core Ruby language. 4 | 5 | For more information about the SoftLayer API, and the routines and data structures it offers should visit the [SoftLayer Developer Network (SLDN) website](http://sldn.softlayer.com). 6 | 7 | This document is written for Ruby developers who wish to interact with their SoftLayer accounts through Ruby scripts. SoftLayer also offers a command line tool for performing common tasks. If prefer to use that tool, we invite you to look into the [command line interface](https://softlayer-python.readthedocs.org/en/latest/cli.html) that is part of the [git](http://github.com/softlayer/softlayer-python) project. 8 | 9 | This documentation is also written for Ruby developers who wish to contribute to the `softlayer_api` Gem. The Gem is a work in progress. We welcome the support of the SoftLayer development community to improve the Gem. The project is open source and we hope that source will serve as a useful library, stand as sample code to assist exploration, and serve as an opportunity for developers to shape it for both those needs. 10 | 11 | The primary repository for the Gem's source code is the [softlayer-ruby](http://github.com/softlayer/softlayer-ruby) github project. 12 | 13 | # Overview 14 | 15 | These Ruby language bindings allow access to the SoftLayer API on two different levels. A Foundation layer for low-level interaction with the SoftLayer API, and an abstraction layer that simplifies and isolates scripts from some of the details found in the Foundation. 16 | 17 | The Foundation layer, makes use of the [XMLRPC client](http://www.ruby-doc.org/stdlib-2.1.2/libdoc/xmlrpc/rdoc/XMLRPC/Client.html) which is part of the Core library of Ruby itself. This foundation is embodied primarily in the `SoftLayer::Client` and `SoftLayer::Service` classes. Requests are made, and responses retrieved using fundamental Ruby types such as Hashes, Arrays, Strings, and Integers. 18 | 19 | The Model layer is built atop the foundation as object class hierarchy. The class hierarchy models the structures found in the SoftLayer environment using the object-oriented features of Ruby. It does this to abstract out some of the implementation detail that a developer would commonly have to work with to communicate with SoftLayer through the foundation layer. 20 | 21 | The Model layer is by no means complete; quite to the contrary it is in its infancy and we believe that much of the development effort in the Gem will focus on incorporating new models into this layer. Because it is incomplete, however, we have put some effort into bridges from the functionality of the model, down to the lower level foundation, without trouble. Also, as a result of this, developers interested in using the Model layer should also should familiarize themselves with the Foundation. 22 | 23 | All developers should continue their exploration of the `softlayer_api` gem by examining the Foundation documentation. Clients that wish to make use of the abstractions provided in the object hierarchy may continue their exploration by looking at the Model Layer documentation. Developers who wish to expand the models found in the `softlayer_api` Gem should read the [Contribution Guide](ContributionGuide.md) 24 | -------------------------------------------------------------------------------- /doc_src/cla-individual.md: -------------------------------------------------------------------------------- 1 | #### International Business Machines, Inc. (IBM) 2 | #####Individual Contributor License Agreement ("Agreement") 3 | 4 | http://www.github.com/softlayer/softlayer-ruby 5 | 6 | Thank you for your interest in the softlayer-ruby project ("the Project"). 7 | 8 | In order to clarify the intellectual property license granted with Contributions 9 | from any person or entity, IBM must have a Contributor License Agreement ("CLA") 10 | on file that has been signed by each Contributor, indicating agreement to the 11 | license terms below. This license is for your protection as a Contributor as 12 | well as the protection of IBM and its customers; it does not change your rights 13 | to use your own Contributions for any other purpose. 14 | 15 | If you have not already done so, please complete and sign, then scan and email a 16 | pdf file of this Agreement to SLDNDeveloperRelations@wwpdl.vnet.ibm.com. 17 | 18 | Please read this document carefully before signing and keep a copy for your 19 | records. 20 | 21 | Full name: ______________________________________________________ 22 | 23 | (optional) Public name: _________________________________________ 24 | 25 | Mailing Address: ________________________________________________ 26 | 27 | Country: ______________________________________________________ 28 | 29 | Telephone: ______________________________________________________ 30 | 31 | E-Mail: ______________________________________________________ 32 | 33 | 34 | You accept and agree to the following terms and conditions for Your present and 35 | future Contributions submitted to the Project. Except for the license granted 36 | herein to IBM and recipients of software distributed by IBM, You reserve all 37 | right, title, and interest in and to Your Contributions. 38 | 39 | 1. Definitions. 40 | 41 | "You" (or "Your") shall mean the copyright owner or legal entity 42 | authorized by the copyright owner that is making this Agreement 43 | with IBM. For legal entities, the entity making a Contribution and 44 | all other entities that control, are controlled by, or are under 45 | common control with that entity are considered to be a single 46 | Contributor. For the purposes of this definition, "control" means 47 | (i) the power, direct or indirect, to cause the direction or 48 | management of such entity, whether by contract or otherwise, 49 | or (ii) ownership of fifty percent (50%) or more of the outstanding 50 | shares, or (iii) beneficial ownership of such entity. 51 | 52 | "Contribution" shall mean any original work of authorship, 53 | including any modifications or additions to an existing work, that 54 | is intentionally submitted by You to the Project for inclusion 55 | in, or documentation of, the Project (”the Work”). For the purposes 56 | of this definition, "submitted" means any form of electronic, verbal, 57 | or written communication sent to the Project or its representatives, 58 | including but not limited to communication on electronic mailing lists, 59 | source code control systems, and issue tracking systems that are 60 | managed by, or on behalf of, the Project for the purpose of discussing 61 | and improving the Work, but excluding communication that is conspicuously 62 | marked or otherwise designated in writing by You as "Not a Contribution." 63 | 64 | 2. Grant of Copyright License. 65 | 66 | Subject to the terms and conditions of this Agreement, You hereby grant 67 | to IBM and to recipients of software distributed by IBM a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright 69 | license to reproduce, prepare derivative works of, publicly display, 70 | publicly perform, sublicense, and distribute Your Contributions and 71 | such derivative works. 72 | 73 | 3. Grant of Patent License. 74 | 75 | Subject to the terms and conditions of this Agreement, You hereby grant 76 | to IBM and to recipients of software distributed by IBM a perpetual, 77 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except 78 | as stated in this section) patent license to make, have made, use, offer 79 | to sell, sell, import, and otherwise transfer the Work to which Your 80 | Contribution(s) were submitted, where such license applies only to those 81 | patent claims licensable by You that are necessarily infringed by Your 82 | Contribution(s) alone or by combination of Your Contribution(s) with the 83 | Work to which such Contribution(s) was submitted. If any entity institutes 84 | patent litigation against You or any other entity (including a cross-claim 85 | or counterclaim in a lawsuit) alleging that your Contribution, or the Work 86 | to which you have contributed, constitutes direct or contributory patent 87 | infringement, then any patent licenses granted to that entity under this 88 | Agreement for that Contribution or Work shall terminate as of the date 89 | such litigation is filed. 90 | 91 | 4. You represent that you are legally entitled to grant the above 92 | license. 93 | 94 | If your employer(s) has rights to intellectual property 95 | that you create that includes your Contributions, you represent 96 | that you have received permission to make Contributions on behalf 97 | of that employer, that your employer has waived such rights for 98 | your Contributions to the Project, or that your employer has 99 | executed a separate Corporate CLA with IBM. 100 | 101 | 5. You represent that each of Your Contributions is Your original 102 | creation (see section 7 for submissions on behalf of others). You 103 | represent that Your Contribution submissions include complete 104 | details of any third-party license or other restriction (including, 105 | but not limited to, related patents and trademarks) of which you 106 | are personally aware and which are associated with any part of Your 107 | Contributions. 108 | 109 | 6. You are not expected to provide support for Your Contributions, 110 | except to the extent You desire to provide support. 111 | 112 | You may provide support for free, for a fee, or not at all. 113 | Unless required by applicable law or agreed to in writing, You provide Your 114 | Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 115 | OF ANY KIND, either express or implied, including, without 116 | limitation, any warranties or conditions of TITLE, NON- 117 | INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. 118 | 119 | 7. Should You wish to submit work that is not Your original creation, 120 | You may submit it to the Project separately from any 121 | Contribution, identifying the complete details of its source and of 122 | any license or other restriction (including, but not limited to, 123 | related patents, trademarks, and license agreements) of which you 124 | are personally aware, and conspicuously marking the work as 125 | "Submitted on behalf of a third-party: [named here]". 126 | 127 | 8. You agree to notify IBM of any facts or circumstances of 128 | which you become aware that would make these representations 129 | inaccurate in any respect. 130 | 131 | Please sign: __________________________________ Date: ________________ 132 | 133 | 134 | -------------------------------------------------------------------------------- /examples/account_info.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | # 22 | 23 | require 'rubygems' 24 | require 'softlayer_api' 25 | require 'pp' 26 | 27 | begin 28 | softlayer_client = SoftLayer::Client.new( 29 | # :username => "joecustomer", # enter your username here 30 | # :api_key => "feeddeadbeefbadf00d..." # enter your api key here 31 | ) 32 | 33 | # Demonstrates using the low-level capabilities of the softlayer_api 34 | # gem to get directly at methods in the SoftLayer API and extract 35 | # data from them. 36 | account_service = softlayer_client['Account']; 37 | account = account_service.getObject 38 | 39 | pp account 40 | rescue Exception => exception 41 | puts "Unable to retrieve account information: #{exception}" 42 | end 43 | -------------------------------------------------------------------------------- /examples/account_servers.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | # 22 | 23 | require 'rubygems' 24 | require 'softlayer_api' 25 | require 'pp' 26 | 27 | # We can set the default client to be our client and that way 28 | # we can avoid supplying it later 29 | SoftLayer::Client.default_client = SoftLayer::Client.new( 30 | # :username => "joecustomer" # enter your username here 31 | # :api_key => "feeddeadbeefbadf00d..." # enter your api key here 32 | ) 33 | 34 | account = SoftLayer::Account.account_for_client() 35 | 36 | # grab a list of all the servers on the account. 37 | servers = account.servers 38 | 39 | # measure their fully qualified domain names so we can print a pretty table 40 | max_name_len = servers.inject(0) { |max_name, server| [max_name, server.fullyQualifiedDomainName.length].max } 41 | 42 | printf "%#{-max_name_len}s\tPrimary Public IP\n", "Server FQDN" 43 | printf "%#{-max_name_len}s\t-----------------\n", "-----------" 44 | 45 | servers.each do |server| 46 | ip_field = server.primary_public_ip ? server.primary_public_ip : "No Public Interface" 47 | printf "%#{-max_name_len}s\t#{ip_field}\n", server.fullyQualifiedDomainName 48 | end -------------------------------------------------------------------------------- /examples/create_ticket.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | # 22 | 23 | require 'rubygems' 24 | require 'softlayer_api' 25 | require 'pp' 26 | 27 | softlayer_client = SoftLayer::Client.new( 28 | # :username => "joecustomer" # enter your username here 29 | # :api_key => "feeddeadbeefbadf00d..." # enter your api key here 30 | ) 31 | 32 | begin 33 | # To create a ticket we have to assign the ticket to some user so 34 | # assign our new ticket to the current user 35 | account = SoftLayer::Account.account_for_client(softlayer_client) 36 | account_user = account.service.getCurrentUser 37 | my_user_id = account_user["id"] 38 | 39 | # We also need a subject for the ticket. Subjects are specified by id 40 | # This code prints out a table of all the ticket subjects with their 41 | # ids: 42 | ticket_subjects = SoftLayer::Ticket.ticket_subjects(softlayer_client) 43 | ticket_subjects.each do |subject| 44 | puts "#{subject['id']}\t#{subject['name']}" 45 | end 46 | 47 | # For this example we'll use 'Public Network Question' as the subject. That's id 1022 48 | public_network_question_id = 1022 49 | 50 | # A title is optional, but we'll provide one and we offer the body of the ticket 51 | # remember to pass the client to create_standard_ticket 52 | new_ticket = SoftLayer::Ticket.create_standard_ticket( 53 | :client => softlayer_client, 54 | :title => "This is a test ticket, please simply close it", 55 | :body => "This test ticket was created to test the Ruby API client. Please ignore it.", 56 | :subject_id => public_network_question_id, 57 | :assigned_user_id => my_user_id 58 | ) 59 | 60 | puts "Created a new ticket : #{new_ticket.id} - #{new_ticket.title}" 61 | 62 | # we can also add an update to the ticket: 63 | new_ticket.update("This is a ticket update sent from the Ruby library") 64 | 65 | rescue Exception => exception 66 | $stderr.puts "An exception occurred while trying to complete the SoftLayer API calls #{exception}" 67 | end 68 | -------------------------------------------------------------------------------- /examples/open_tickets.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | # 22 | 23 | require 'rubygems' 24 | require 'softlayer_api' 25 | require 'pp' 26 | 27 | # One way to provide a username and api key is to provide them 28 | # as globals. 29 | # $SL_API_USERNAME = "joecustomer" # enter your username here 30 | # $SL_API_KEY = "feeddeadbeefbadf00d..." # enter your api key here 31 | 32 | # The client constructed here must get it's credentials from somewhere 33 | # In this script you might uncomment the globals above and assign your 34 | # credentials there 35 | SoftLayer::Client.default_client = SoftLayer::Client.new() 36 | 37 | # The openTickets routine will pick up the default client established above. 38 | open_tickets = SoftLayer::Ticket.open_tickets() 39 | 40 | open_tickets.sort!{ |lhs, rhs| -(lhs.lastEditDate <=> rhs.lastEditDate) } 41 | open_tickets.each do |ticket| 42 | printf "#{ticket.id} - #{ticket.title}" 43 | 44 | ticket.has_updates? ? printf("\t*\n") : printf("\n") 45 | end 46 | -------------------------------------------------------------------------------- /examples/order_server_firewall.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | # 22 | 23 | require 'rubygems' 24 | require 'softlayer_api' 25 | require 'pp' 26 | 27 | # This is the id of the server you want to protect with a firewall. 28 | # The server can be Bare Metal or Virtual. It should have a public 29 | # network interface, and it should not already have a firewall on it. 30 | server_id = 257696 # 12345 31 | 32 | # In this example, we assume this is a Bare Metal Server 33 | is_virtual_server = false 34 | 35 | # Work with the SoftLayer API begins with a client. By setting 36 | # the "default" client we avoid having to specify the client repeatedly 37 | # in calls that follow. 38 | SoftLayer::Client.default_client = SoftLayer::Client.new( 39 | # :username => "joecustomer" # enter your username here 40 | # :api_key => "feeddeadbeefbadf00d..." # enter your api key here 41 | ) 42 | 43 | # in this case we go straight to the appropriate class to find the server 44 | # an alternative might be to create the account for this client and 45 | # search the list of servers for the one with the appropriate ID. 46 | if is_virtual_server 47 | server = SoftLayer::VirtualServer.server_with_id(server_id) 48 | else 49 | server = SoftLayer::BareMetalServer.server_with_id(server_id) 50 | end 51 | 52 | # Create an instance of SoftLayer::ServerFirewallOrder 53 | order = SoftLayer::ServerFirewallOrder.new(server) 54 | 55 | begin 56 | # this example calls order.verify which will build the order, submit it 57 | # to the network API, and will throw an exception if the order is 58 | # invalid. 59 | order.verify() 60 | puts "Firewall order is good for #{server.fullyQualifiedDomainName}" 61 | rescue => exception 62 | puts "Firewall order failed for #{server.fullyQualifiedDomainName} because #{exception}" 63 | end -------------------------------------------------------------------------------- /examples/order_virtual_server.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | # 22 | 23 | require 'rubygems' 24 | require 'softlayer_api' 25 | require 'pp' 26 | 27 | begin 28 | # This sample walks through the creation of a Virtual Server. 29 | # It explores techniques for discovering what configuration options 30 | # exist, puts together an order, then sends that order to 31 | # SoftLayer for verification. 32 | 33 | client = SoftLayer::Client.new( 34 | # :username => "joecustomer" # enter your username here 35 | # :api_key => "feeddeadbeefbadf00d..." # enter your api key here 36 | ) 37 | 38 | # We begin by creating a VirtualServerOrder and filling out the hostname and domain 39 | # attributes. 40 | server_order = SoftLayer::VirtualServerOrder.new(client) 41 | server_order.hostname = "server1" 42 | server_order.domain = "ruby-api-test.org" 43 | 44 | # We must tell the system in which datacenter we want our server created 45 | # We can ask the class to give us a list of options: 46 | puts SoftLayer::VirtualServerOrder.datacenter_options(client).inspect 47 | 48 | # The list will look something like ["ams01", "dal01", "dal05",... 49 | # Let's put our server in the 'dal05' (Dallas 5) datacenter 50 | server_order.datacenter = SoftLayer::Datacenter.datacenter_named 'dal05', client 51 | 52 | # The order must know how many computing cores we want in our virtual 53 | # server. Again we can ask the class for options. The result will 54 | # be something like [1, 2, 4, 8, 12, 16] 55 | # 2 sounds like a good number of cores for our simple server 56 | puts SoftLayer::VirtualServerOrder.core_options(client).inspect 57 | server_order.cores = 2 58 | 59 | # We must indicate how much memory the virtual server should have. 60 | # Again we can query for options and select a good value 61 | puts SoftLayer::VirtualServerOrder.memory_options(client).inspect 62 | server_order.memory = 2 #GB 63 | 64 | # Similarly we can choose an operating system for the server: 65 | puts SoftLayer::VirtualServerOrder.os_reference_code_options(client).inspect 66 | server_order.os_reference_code = 'CENTOS_6_64' 67 | 68 | # Finally, in spite of the fact that our server is simple, we want it 69 | # to have a blazing fast connection speed. Let's look at the options and choose 70 | # the fastest! (it's probably 1 Gbps) 71 | server_order.max_port_speed = SoftLayer::VirtualServerOrder.max_port_speed_options(client).max 72 | 73 | # The server order is now complete. This sample will ask it to verify itself with the 74 | # SoftLayer ordering system, but a simple change from verify to place_order! would ask 75 | # the system to provision the server (and charge it to our account) 76 | begin 77 | server_order.verify 78 | puts "The server order is OK" 79 | rescue Exception => e 80 | puts "The server order failed verification :-( -- #{e}" 81 | end 82 | rescue Exception => exception 83 | $stderr.puts "An exception occurred while trying to complete the SoftLayer API calls #{exception}" 84 | end 85 | 86 | -------------------------------------------------------------------------------- /examples/ticket_info.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | # 22 | 23 | require 'rubygems' 24 | require 'softlayer_api' 25 | require 'pp' 26 | 27 | softlayer_client = SoftLayer::Client.new( 28 | # :username => "joecustomer", # enter your username here 29 | # :api_key => "feeddeadbeefbadf00d..." # enter your api key here 30 | ) 31 | 32 | begin 33 | # Demonstrates using the low-level capabilities of the gem to get 34 | # at information. In this case we are talking directly to the ticket 35 | # service 36 | ticket_service = softlayer_client.service_named("Ticket"); 37 | 38 | # Retrive a particular ticket by ID (you will have to substitute an existing ticket's ID here) 39 | ticket_ref = ticket_service.object_with_id(12345) 40 | 41 | # Retrive very specific information about the ticket 42 | ticket = ticket_ref.object_mask("mask[updates[entry,createDate],assignedUserId,attachedHardware.datacenter]").getObject 43 | 44 | pp ticket 45 | rescue Exception => exception 46 | puts "Unable to retrieve the ticket #{exception}" 47 | end -------------------------------------------------------------------------------- /lib/softlayer/Datacenter.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # For licensing information see the LICENSE.md file in the project root. 5 | #++ 6 | 7 | module SoftLayer 8 | ## 9 | # A Data Center in the SoftLayer network 10 | # 11 | # This class corresponds to the SoftLayer_Location++ data type: 12 | # 13 | # http://sldn.softlayer.com/reference/datatypes/SoftLayer_Location 14 | # 15 | # Although in this context it is used to represent a data center and 16 | # not the more general class of locations that that data type can 17 | # represent. 18 | 19 | class Datacenter < SoftLayer::ModelBase 20 | 21 | ## 22 | # :attr_reader: 23 | # A short location description 24 | sl_attr :name 25 | 26 | ## 27 | # :attr_reader: long_name 28 | # A longer location description 29 | sl_attr :long_name, "longName" 30 | 31 | ## 32 | # Return the datacenter with the given name ('sng01' or 'dal05') 33 | def self.datacenter_named(name, client = nil) 34 | datacenters(client).find{ | datacenter | datacenter.name == name.to_s.downcase } 35 | end 36 | 37 | ## 38 | # Return a list of all the datacenters 39 | # 40 | # If the client parameter is not provided, the routine 41 | # will try to use Client::default_client. If no client 42 | # can be found, the routine will raise an exception 43 | # 44 | # This routine will only retrieve the list of datacenters from 45 | # the network once and keep it in memory unless you 46 | # pass in force_reload as true. 47 | # 48 | @@data_centers = nil 49 | def self.datacenters(client = nil, force_reload = false) 50 | softlayer_client = client || Client.default_client 51 | raise "Datacenter.datacenters requires a client to call the network API" if !softlayer_client 52 | 53 | if(!@@data_centers || force_reload) 54 | datacenters_data = softlayer_client[:Location].getDatacenters 55 | @@data_centers = datacenters_data.collect { | datacenter_data | self.new(softlayer_client, datacenter_data) } 56 | end 57 | 58 | @@data_centers 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /lib/softlayer/DynamicAttribute.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # For licensing information see the LICENSE.md file in the project root. 5 | #++ 6 | 7 | module SoftLayer 8 | 9 | ## 10 | # This module is intended to be used by classes in the SoftLayer 11 | # object model. It creates a small DSL for creating attributes 12 | # that update themselves dynamically (usually by making requests 13 | # to the SoftLayer API) 14 | # 15 | # +sl_dynamic_attr+ is an implementation of a memoization scheme 16 | # The module creates a getter which is implemented in terms of a 17 | # predicate (identifying whether or not the attribute needs to be updated) and 18 | # an update routine 19 | # 20 | # When the getter is called, it checks the predicate routine to see if 21 | # the attribute needs to be updated. If it doesn't, then the getter simply 22 | # returns the cached value for the attribute. If the attribute does need 23 | # to be updated, the getter calls the update routine to get a new value 24 | # and caches that value off before returning it to the caller. 25 | # 26 | # Declaring a attribute adds three methods to a class and 27 | # a corresponding instance variable in instances of the class 28 | # All three are based on the name of the attribute: 29 | # 30 | # * The getter simply has the same name as the attribute 31 | # * The predicate routine is called +should_update_?+ 32 | # * The updating routine is called +update_!+ 33 | # 34 | # The getter can also be called with a boolean argument. If that 35 | # argument is true, the getter will force the attribute to be updated 36 | # without consulting the +should_update?+ predicate 37 | # 38 | # When a attribute is defined, the definition takes a block. 39 | # Inside the block there is a small DSL that allows you to 40 | # set the behavior of the +should_update?+ predicate and the +update_!+ 41 | # routine. 42 | # 43 | # A attribute definition might look something like this: 44 | # 45 | # sl_dynamic_attr :lollipop do |lollipop| 46 | # lollipop.should_update? do 47 | # self.lollipop_supply_is_low? 48 | # end 49 | # 50 | # lollipop.to_update do 51 | # candy_store.buy_lollipops(bakers_dozen) 52 | # end 53 | # end 54 | # 55 | module DynamicAttribute 56 | 57 | # The DynamicAttributeDefinition inner class is to collect and 58 | # store information about how and when a sl_dynamic_attr 59 | # should be updated. This class is an implementation detail 60 | # of dynamic attributes and is not intended to be useful 61 | # outside of that context. 62 | class DynamicAttributeDefinition 63 | # the name of the attribute this definition is for 64 | attr_reader :attribute_name 65 | 66 | # The block to call in order to update the attribute. The 67 | # return value of this block should be the new value of the 68 | # attribute. 69 | attr_reader :update_block 70 | 71 | # The block to call to see if the attribute needs to be updated. 72 | attr_reader :should_update_block 73 | 74 | def initialize(attribute_name) 75 | raise ArgumentError if attribute_name.nil? 76 | raise ArgumentError if attribute_name.to_s.empty? 77 | 78 | @attribute_name = attribute_name; 79 | @update_block = Proc.new { nil; }; 80 | @should_update_block = Proc.new { true; } 81 | end 82 | 83 | # This method is used to provide behavior for the 84 | # should_update_ predicate for the attribute 85 | def should_update? (&block) 86 | @should_update_block = block 87 | end 88 | 89 | # This method is used to provide the behavior for 90 | # the update_! method for the attribute. 91 | def to_update (&block) 92 | @update_block = block 93 | end 94 | end 95 | 96 | module ClassMethods 97 | # sl_dynamic_attr declares a new dynamic softlayer attribute and accepts 98 | # a block in which the should_update? and to_update methods for the 99 | # attribute are established. 100 | def sl_dynamic_attr (attribute_name, &block) 101 | attribute_definition = DynamicAttributeDefinition.new(attribute_name) 102 | 103 | # allow the block to update the attribute definition 104 | yield attribute_definition if block_given? 105 | 106 | # store off the attribute definition where we can find it later 107 | @attribute_definitions ||= {}; 108 | @attribute_definitions[attribute_name] = attribute_definition; 109 | 110 | # define a method called "update_!" which calls the update block 111 | # stored in the attribute definition 112 | update_symbol = "update_#{attribute_name}!".to_sym 113 | define_method(update_symbol, &attribute_definition.update_block) 114 | 115 | # define a method called "should_update_?" which calls the 116 | # should update block stored in the attribute definition 117 | should_update_symbol = "should_update_#{attribute_name}?".to_sym 118 | define_method(should_update_symbol, &attribute_definition.should_update_block) 119 | 120 | # define an instance method of the class this is being 121 | # called on which will get the value of the attribute. 122 | # 123 | # The getter will take one argument "force_update" which 124 | # is treated as boolean value. If true, then the getter will 125 | # force the attribute to update (by using its "to_update") block. 126 | # 127 | # If the force variable is false, or not given, then the 128 | # getter will call the "should update" block to find out if the 129 | # attribute needs to be updated. 130 | # 131 | getter_name = attribute_name.to_sym 132 | value_instance_variable = "@#{attribute_name}".to_sym 133 | 134 | define_method(getter_name) do |*args| 135 | force_update = args[0] || false 136 | 137 | if force_update || __send__(should_update_symbol) 138 | instance_variable_set(value_instance_variable, __send__(update_symbol)) 139 | end 140 | 141 | instance_variable_get(value_instance_variable) 142 | end 143 | end 144 | 145 | def sl_dynamic_attr_definition(attribute_name) 146 | @attribute_definitions[attribute_name] 147 | end 148 | end 149 | 150 | def self.included(included_in) 151 | included_in.extend(ClassMethods) 152 | end 153 | end 154 | end 155 | -------------------------------------------------------------------------------- /lib/softlayer/ModelBase.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # For licensing information see the LICENSE.md file in the project root. 5 | #++ 6 | 7 | module SoftLayer 8 | ## 9 | # The SoftLayer Gem defines an Object Hierarchy representing entities in 10 | # an account's SoftLayer environment. This class is the base object class 11 | # for objects in that hierarchy 12 | # 13 | # The SoftLayer API represents entities as a hash of properties. This class 14 | # stores that hash and allows the use of subscripting to access those properties 15 | # directly. 16 | # 17 | # The class also has a model for making network requests that will refresh 18 | # the stored hash so that it reflects the most up-to-date information about 19 | # an entity from the server. Subclasses should override softlayer_properties 20 | # to retrieve information from the server. Client code should call 21 | # refresh_details to ask an object to update itself. 22 | # 23 | class ModelBase 24 | # The client environment that this model object belongs to 25 | attr_reader :softlayer_client 26 | 27 | ## 28 | # :attr_reader: id 29 | # The unique identifier of this object within its API service 30 | 31 | # Construct a new model object in the environment of the given client and 32 | # with the given hash of network data (presumably returned by the SoftLayer API) 33 | def initialize(softlayer_client, network_hash) 34 | raise ArgumentError, "A hash is required" if nil == network_hash 35 | raise ArgumentError, "Model objects must be created in the context of a client" if nil == softlayer_client 36 | 37 | @softlayer_client = softlayer_client 38 | @softlayer_hash = network_hash 39 | 40 | raise ArgumentError, "The hash used to construct a softlayer model object must have an id" unless has_sl_property?(:id) 41 | raise ArgumentError, "id must be non-nil and non-empty" unless self[:id] 42 | end 43 | 44 | ## 45 | # The service method of a Model object should return a SoftLayer Service 46 | # that best represents the modeled object. For example, a Ticket models 47 | # a particular entity in the SoftLayer_Ticket service. The particular 48 | # entity is identified by its id so the Ticket class would return 49 | # 50 | # softlayer_client[:Ticket].object_with_id 51 | # 52 | # which is a service which would allow calls to the ticket service 53 | # through that particular object. 54 | def service 55 | raise "Abstract method service in ModelBase was called" 56 | end 57 | 58 | ## 59 | # Asks a model object to reload itself from the SoftLayer API. 60 | # 61 | # Subclasses should not override this method, rather they should 62 | # implement softlayer_properties to actually make the API request 63 | # and return the new hash. 64 | # 65 | def refresh_details(object_mask = nil) 66 | @softlayer_hash = self.softlayer_properties(object_mask) 67 | end 68 | 69 | ## 70 | # Returns the value of of the given property as stored in the 71 | # softlayer_hash. This gives you access to the low-level, raw 72 | # properties that underlie this model object. The need for this 73 | # is not uncommon, but using this method should still be done 74 | # with deliberation. 75 | def [](softlayer_property) 76 | self.softlayer_hash[softlayer_property.to_s] 77 | end 78 | 79 | ## 80 | # Returns true if the given property can be found in the softlayer hash 81 | def has_sl_property?(softlayer_property) 82 | softlayer_hash && softlayer_hash.has_key?(softlayer_property.to_s) 83 | end 84 | 85 | ## 86 | # allows subclasses to define attributes as sl_attr 87 | # sl_attr are attributes that draw their value from the 88 | # low-level hash representation of the object. 89 | def self.sl_attr(attribute_symbol, hash_key = nil) 90 | raise "The sl_attr expects a symbol for the attribute to define" unless attribute_symbol.kind_of?(Symbol) 91 | raise "The hash key used to define an attribute cannot be empty" if hash_key && hash_key.empty? 92 | 93 | define_method(attribute_symbol.to_sym) { self[hash_key ? hash_key : attribute_symbol.to_s]} 94 | end 95 | 96 | sl_attr :id 97 | 98 | # When printing to the console using puts, ruby will call the 99 | # to_ary method trying to convert an object into an array of lines 100 | # for stdio. We override to_ary to return nil for model objects 101 | # so they may be printed 102 | def to_ary() 103 | return nil; 104 | end 105 | 106 | protected 107 | 108 | ## 109 | # Subclasses should implement this method as part of enabling the 110 | # refresh_details functionality The implementation should make a request 111 | # to the SoftLayer API and retrieve an up-to-date SoftLayer hash 112 | # representation of this object. That hash should be the return value 113 | # of this routine. 114 | # 115 | def softlayer_properties(object_mask = nil) 116 | raise "Abstract method softlayer_properties in ModelBase was called" 117 | end 118 | 119 | ## 120 | # The softlayer_hash stores the low-level information about an 121 | # object as it was retrieved from the SoftLayer API. 122 | attr_reader :softlayer_hash 123 | 124 | end # class ModelBase 125 | end # module SoftLayer 126 | -------------------------------------------------------------------------------- /lib/softlayer/NetworkComponent.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # For licensing information see the LICENSE.md file in the project root. 5 | #++ 6 | 7 | module SoftLayer 8 | class NetworkComponent < SoftLayer::ModelBase 9 | ## 10 | # :attr_reader: max_speed 11 | # A network component's maximum allowed speed, 12 | sl_attr :max_speed, 'maxSpeed' 13 | 14 | ## 15 | # :attr_reader: 16 | # A network component's maximum allowed speed, 17 | # 18 | # DEPRECATION WARNING: This attribute is deprecated in favor of max_speed 19 | # and will be removed in the next major release. 20 | sl_attr :maxSpeed 21 | 22 | ## 23 | # :attr_reader: 24 | # A network component's short name. 25 | sl_attr :name 26 | 27 | # :attr_reader: 28 | # A network component's port number. 29 | sl_attr :port 30 | 31 | ## 32 | # :attr_reader: 33 | # A network component's speed, measured in Mbit per second. 34 | sl_attr :speed 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/softlayer/NetworkMessageDelivery.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # For licensing information see the LICENSE.md file in the project root. 5 | #++ 6 | 7 | module SoftLayer 8 | ## 9 | # Each SoftLayer NetworkMessageDelivery instance provides information about 10 | # the username/password combination for a specific Network Message Delivery 11 | # account. 12 | # 13 | # This class roughly corresponds to the entity SoftLayer_Network_Message_Delivery 14 | # in the API. 15 | # 16 | class NetworkMessageDelivery < ModelBase 17 | include ::SoftLayer::DynamicAttribute 18 | 19 | ## 20 | # :attr_reader: created_at 21 | # The date this username/password pair was created. 22 | sl_attr :created_at, 'createDate' 23 | 24 | ## 25 | # :attr_reader: created 26 | # The date this username/password pair was created. 27 | # DEPRECATION WARNING: This attribute is deprecated in favor of created_at 28 | # and will be removed in the next major release. 29 | sl_attr :created, 'createDate' 30 | 31 | ## 32 | # :attr_reader: modified_at 33 | # The date of the last modification to this username/password pair. 34 | sl_attr :modified_at, 'modifyDate' 35 | 36 | ## 37 | # :attr_reader: modified 38 | # The date of the last modification to this username/password pair. 39 | # DEPRECATION WARNING: This attribute is deprecated in favor of modified_at 40 | # and will be removed in the next major release. 41 | sl_attr :modified, 'modifyDate' 42 | 43 | ## 44 | # :attr_reader: 45 | # The password part of the username/password pair. 46 | sl_attr :password 47 | 48 | ## 49 | # :attr_reader: 50 | # The username part of the username/password pair. 51 | sl_attr :username 52 | 53 | ## 54 | # Retrieve the message delivery type description of a network message delivery account. 55 | # :call-seq: 56 | # description(force_update=false) 57 | sl_dynamic_attr :description do |resource| 58 | resource.should_update? do 59 | #only retrieved once per instance 60 | @description == nil 61 | end 62 | 63 | resource.to_update do 64 | type = self.service.getType 65 | type['description'] 66 | end 67 | end 68 | 69 | ## 70 | # Retrieve the message delivery type name of a network message delivery account. 71 | # :call-seq: 72 | # name(force_update=false) 73 | sl_dynamic_attr :name do |resource| 74 | resource.should_update? do 75 | #only retrieved once per instance 76 | @name == nil 77 | end 78 | 79 | resource.to_update do 80 | type = self.service.getType 81 | type['name'] 82 | end 83 | end 84 | 85 | ## 86 | # Retrieve the vendor name for a network message delivery account. 87 | # :call-seq: 88 | # vendor(force_update=false) 89 | sl_dynamic_attr :vendor do |resource| 90 | resource.should_update? do 91 | #only retrieved once per instance 92 | @vendor == nil 93 | end 94 | 95 | resource.to_update do 96 | vendor = self.service.getVendor 97 | vendor['name'] 98 | end 99 | end 100 | 101 | ## 102 | # Updates the password for the current account password. 103 | # 104 | def password=(password) 105 | raise ArgumentError, "The new password cannot be nil" unless password 106 | raise ArgumentError, "The new password cannot be empty" if password.empty? 107 | 108 | self.service.editObject({ "password" => password.to_s }) 109 | self.refresh_details() 110 | end 111 | 112 | ## 113 | # Returns the service for interacting with the network message delivery instance 114 | # through the network API 115 | # 116 | def service 117 | softlayer_client[:Network_Message_Delivery].object_with_id(self.id) 118 | end 119 | 120 | ## 121 | # Make an API request to SoftLayer and return the latest properties hash 122 | # for this object. 123 | # 124 | def softlayer_properties(object_mask = nil) 125 | my_service = self.service 126 | 127 | if(object_mask) 128 | my_service = my_service.object_mask(object_mask) 129 | else 130 | my_service = my_service.object_mask(self.class.default_object_mask) 131 | end 132 | 133 | my_service.getObject() 134 | end 135 | 136 | protected 137 | 138 | def self.default_object_mask 139 | { 140 | "mask(SoftLayer_Network_Message_Delivery)" => [ 141 | 'createDate', 142 | 'id', 143 | 'modifyDate', 144 | 'password', 145 | 'username' 146 | ] 147 | }.to_sl_object_mask 148 | end 149 | end 150 | end #SoftLayer 151 | -------------------------------------------------------------------------------- /lib/softlayer/NetworkService.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # For licensing information see the LICENSE.md file in the project root. 5 | #++ 6 | 7 | module SoftLayer 8 | ## 9 | # Each SoftLayer NetworkService instance provides connectivity 10 | # information for a specific Network Service Resource. 11 | # 12 | # This class roughly corresponds to the entity SoftLayer_Network_Service_Resource 13 | # in the API. 14 | # 15 | class NetworkService < ModelBase 16 | include ::SoftLayer::DynamicAttribute 17 | 18 | ## 19 | # :attr_reader: 20 | # The name associated with this resource 21 | sl_attr :name 22 | 23 | ## 24 | # :attr_reader: private_ip 25 | # The backend IP address for this resource 26 | sl_attr :private_ip, 'backendIpAddress' 27 | 28 | ## 29 | # :attr_reader: public_ip 30 | # The frontend IP address for this resource 31 | sl_attr :public_ip, 'frontendIpAddress' 32 | 33 | ## 34 | # :attr_reader: ssh_username 35 | # The ssh username of for this resource 36 | sl_attr :ssh_username, 'sshUsername' 37 | 38 | ## 39 | # Retrieve the datacenter that this network service resource is available in 40 | # :call-seq: 41 | # datacenter(force_update=false) 42 | sl_dynamic_attr :datacenter do |resource| 43 | resource.should_update? do 44 | #only retrieved once per instance 45 | @datacenter == nil 46 | end 47 | 48 | resource.to_update do 49 | Datacenter::datacenter_named(self['datacenter']['name'], self.softlayer_client) 50 | end 51 | end 52 | 53 | ## 54 | # Returns the api properties used to connect to the network service resource 55 | # 56 | def api 57 | { 58 | 'host' => self['apiHost'], 59 | 'password' => self['apiPassword'], 60 | 'path' => self['apiPath'], 61 | 'port' => self['apiPort'], 62 | 'protocol' => self['apiProtocol'], 63 | 'username' => self['apiUsername'] 64 | } 65 | end 66 | 67 | ## 68 | # Returns the network service resource type name 69 | # 70 | def type 71 | self['type']['type'] 72 | end 73 | 74 | protected 75 | 76 | def self.default_object_mask 77 | { 78 | "mask(SoftLayer_Network_Service_Resource)" => [ 79 | 'apiHost', 80 | 'apiPassword', 81 | 'apiPath', 82 | 'apiPort', 83 | 'apiProtocol', 84 | 'apiUsername', 85 | 'backendIpAddress', 86 | 'datacenter', 87 | 'frontendIpAddress', 88 | 'id', 89 | 'name', 90 | 'networkDevice.id', 91 | 'sshUsername', 92 | 'type.type' 93 | ] 94 | }.to_sl_object_mask 95 | end 96 | end 97 | end #SoftLayer 98 | -------------------------------------------------------------------------------- /lib/softlayer/NetworkStorageAllowedHost.rb: -------------------------------------------------------------------------------- 1 | module SoftLayer 2 | ## 3 | # Each SoftLayer NetworkStorageAllowedHost instance provides information about 4 | # a hosts allowed access to a storage product group. 5 | # 6 | # This class roughly corresponds to the entity SoftLayer_Network_Storage_Allowed_Host 7 | # in the API. 8 | # 9 | class NetworkStorageAllowedHost < ModelBase 10 | include ::SoftLayer::DynamicAttribute 11 | 12 | ## 13 | # :attr_reader: 14 | # The name of allowed host, usually an IQN or other identifier 15 | sl_attr :name 16 | 17 | ## 18 | # Retrieve the NetworkStorageGroup instances assigned to this host 19 | # :call-seq: 20 | # assigned_groups(force_update=false) 21 | sl_dynamic_attr :assigned_groups do |resource| 22 | resource.should_update? do 23 | #only retrieved once per instance 24 | @assigned_groups == nil 25 | end 26 | 27 | resource.to_update do 28 | assigned_groups = self.service.object_mask(NetworkStorageGroup.default_object_mask).getAssignedGroups 29 | assigned_groups.collect { |assigned_group| NetworkStorageGroup.new(softlayer_client, assigned_group) unless assigned_group.empty? }.compact 30 | end 31 | end 32 | 33 | ## 34 | # Retrieve the NetworkStorage instances assigned to this host 35 | # :call-seq: 36 | # assigned_volumes(force_update=false) 37 | sl_dynamic_attr :assigned_volumes do |resource| 38 | resource.should_update? do 39 | #only retrieved once per instance 40 | @assigned_volumes == nil 41 | end 42 | 43 | resource.to_update do 44 | assigned_volumes = self.service.object_mask(NetworkStorage.default_object_mask).getAssignedVolumes 45 | assigned_volumes.collect { |assigned_volume| NetworkStorage.new(softlayer_client, assigned_volume) unless assigned_volume.empty? }.compact 46 | end 47 | end 48 | 49 | ## 50 | # Retrieve the NetworkStorageCredential instance used to access NetworkStorage for this host 51 | # :call-seq: 52 | # credential(force_update=false) 53 | sl_dynamic_attr :credential do |resource| 54 | resource.should_update? do 55 | #only retrieved once per instance 56 | @credential == nil 57 | end 58 | 59 | resource.to_update do 60 | credential = self.service.object_mask(NetworkStorageCredential.default_object_mask).getCredential 61 | NetworkStorageCredential.new(softlayer_client, credential) unless credential.empty? 62 | end 63 | end 64 | 65 | ## 66 | # Returns the service for interacting with this network storage through the network API 67 | # 68 | def service 69 | softlayer_client[:Network_Storage_Allowed_Host].object_with_id(self.id) 70 | end 71 | 72 | protected 73 | 74 | def self.default_object_mask 75 | { 76 | "mask(SoftLayer_Network_Storage_Allowed_Host)" => [ 77 | 'id', 78 | 'name' 79 | ] 80 | }.to_sl_object_mask 81 | end 82 | end 83 | end #SoftLayer 84 | -------------------------------------------------------------------------------- /lib/softlayer/NetworkStorageGroup.rb: -------------------------------------------------------------------------------- 1 | module SoftLayer 2 | ## 3 | # Each SoftLayer NetworkStorageGroup instance provides information about 4 | # a storage product group and hosts allowed access. 5 | # 6 | # This class roughly corresponds to the entity SoftLayer_Network_Storage_Group 7 | # in the API. 8 | # 9 | class NetworkStorageGroup < ModelBase 10 | include ::SoftLayer::DynamicAttribute 11 | 12 | ## 13 | # :attr_reader: 14 | # The friendly name of this group 15 | sl_attr :alias 16 | 17 | ## 18 | # :attr_reader: created_at 19 | # The date this group was created. 20 | sl_attr :created_at, 'createDate' 21 | 22 | ## 23 | # :attr_reader: created 24 | # The date this group was created. 25 | # DEPRECATION WARNING: This attribute is deprecated in favor of created_at 26 | # and will be removed in the next major release. 27 | sl_attr :created, 'createDate' 28 | 29 | ## 30 | # :attr_reader: modified_at 31 | # The date this group was modified. 32 | sl_attr :modified_at, 'modifyDate' 33 | 34 | ## 35 | # :attr_reader: modified 36 | # The date this group was modified. 37 | # DEPRECATION WARNING: This attribute is deprecated in favor of modified_at 38 | # and will be removed in the next major release. 39 | sl_attr :modified, 'modifyDate' 40 | 41 | ## 42 | # Retrieve the SoftLayer_Account which owns this group. 43 | # :call-seq: 44 | # account(force_update=false) 45 | sl_dynamic_attr :account do |resource| 46 | resource.should_update? do 47 | #only retrieved once per instance 48 | @account == nil 49 | end 50 | 51 | resource.to_update do 52 | account = self.service.getAccount 53 | Account.new(softlayer_client, account) unless account.empty? 54 | end 55 | end 56 | 57 | ## 58 | # Retrieve the allowed hosts list for this group. 59 | # :call-seq: 60 | # allowed_hosts(force_update=false) 61 | sl_dynamic_attr :allowed_hosts do |resource| 62 | resource.should_update? do 63 | #only retrieved once per instance 64 | @allowed_hosts == nil 65 | end 66 | 67 | resource.to_update do 68 | allowed_hosts = self.service.object_mask(NetworkStorageAllowedHost.default_object_mask).getAllowedHosts 69 | allowed_hosts.collect { |allowed_host| NetworkStorageAllowedHost.new(softlayer_client, allowed_host) unless allowed_host.empty? }.compact 70 | end 71 | end 72 | 73 | ## 74 | # Retrieve the network storage volumes this group is attached to. 75 | # :call-seq: 76 | # attached_volumes(force_update=false) 77 | sl_dynamic_attr :attached_volumes do |resource| 78 | resource.should_update? do 79 | #only retrieved once per instance 80 | @attached_volumes == nil 81 | end 82 | 83 | resource.to_update do 84 | attached_volumes = self.service.object_mask(NetworkStorage.default_object_mask).getAttachedVolumes 85 | attached_volumes.collect { |attached_volume| NetworkStorage.new(softlayer_client, attached_volume) unless attached_volume.empty? }.compact 86 | end 87 | end 88 | 89 | ## 90 | # Retrieve the IP address for for SoftLayer_Network_Storage_Allowed_Host objects within this group. 91 | # :call-seq: 92 | # ip_address(force_update=false) 93 | sl_dynamic_attr :ip_address do |resource| 94 | resource.should_update? do 95 | #only retrieved once per instance 96 | @ip_address == nil 97 | end 98 | 99 | resource.to_update do 100 | network_connection_details = self.service.getNetworkConnectionDetails 101 | network_connection_details["ipAddress"] unless network_connection_details.empty? 102 | end 103 | end 104 | 105 | ## 106 | # Retrieve the description of the SoftLayer_Network_Storage_OS_Type 107 | # Operating System designation that this group was created for. 108 | # :call-seq: 109 | # os_description(force_update=false) 110 | sl_dynamic_attr :os_description do |resource| 111 | resource.should_update? do 112 | #only retrieved once per instance 113 | @os_description == nil 114 | end 115 | 116 | resource.to_update do 117 | os_type = self.service.getOsType 118 | os_type["description"] unless os_type.empty? 119 | end 120 | end 121 | 122 | ## 123 | # Retrieve the name of the SoftLayer_Network_Storage_OS_Type 124 | # Operating System designation that this group was created for. 125 | # :call-seq: 126 | # os_name(force_update=false) 127 | sl_dynamic_attr :os_name do |resource| 128 | resource.should_update? do 129 | #only retrieved once per instance 130 | @os_name == nil 131 | end 132 | 133 | resource.to_update do 134 | os_type = self.service.getOsType 135 | os_type["name"] unless os_type.empty? 136 | end 137 | end 138 | 139 | ## 140 | # Retrieve the network resource this group is created on. 141 | # :call-seq: 142 | # service_resource(force_update=false) 143 | sl_dynamic_attr :service_resource do |resource| 144 | resource.should_update? do 145 | #only retrieved once per instance 146 | @service_resource == nil 147 | end 148 | 149 | resource.to_update do 150 | service_resource = self.service.object_mask(NetworkService.default_object_mask).getServiceResource 151 | NetworkService.new(softlayer_client, service_resource) unless service_resource.empty? 152 | end 153 | end 154 | 155 | ## 156 | # Retrieve the name of the SoftLayer_Network_Storage_Group_Type which describes this group. 157 | # :call-seq: 158 | # type(force_update=false) 159 | sl_dynamic_attr :type do |resource| 160 | resource.should_update? do 161 | #only retrieved once per instance 162 | @type == nil 163 | end 164 | 165 | resource.to_update do 166 | group_type = self.service.getGroupType 167 | group_type["name"] 168 | end 169 | end 170 | 171 | ## 172 | # Returns the service for interacting with this network storage through the network API 173 | # 174 | def service 175 | softlayer_client[:Network_Storage_Group].object_with_id(self.id) 176 | end 177 | 178 | protected 179 | 180 | def self.default_object_mask 181 | { 182 | "mask(SoftLayer_Network_Storage_Group)" => [ 183 | 'alias', 184 | 'createDate', 185 | 'id', 186 | 'modifyDate' 187 | ] 188 | }.to_sl_object_mask 189 | end 190 | end 191 | end #SoftLayer 192 | -------------------------------------------------------------------------------- /lib/softlayer/ObjectMaskParser.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # For licensing information see the LICENSE.md file in the project root. 5 | #++ 6 | 7 | require "softlayer/ObjectMaskTokenizer" 8 | require "softlayer/ObjectMaskProperty" 9 | 10 | module SoftLayer 11 | class ObjectMaskParserError < RuntimeError 12 | end 13 | 14 | # 15 | # A parser that can examine and validate SoftLayer Object Mask strings 16 | # 17 | # The Object Mask Parser parses Object Mask Strings into ObjectMaskProperty 18 | # structures. 19 | # 20 | # The Object Mask parser allows the Gem to merge Object Mask Strings 21 | # to avoid errors from the SoftLayer API server about duplicate properties being 22 | # provided when the same property is provided in different Object Masks 23 | # 24 | class ObjectMaskParser 25 | attr_reader :stack 26 | 27 | def initialize() 28 | @stack = [] 29 | end 30 | 31 | def parse(mask_string) 32 | @tokenizer = ObjectMaskTokenizer.new(mask_string) 33 | 34 | token = @tokenizer.current_token 35 | if token.type == :identifier 36 | property = parse_property(@tokenizer) 37 | elsif token.type == :property_set_start 38 | property_set = parse_property_set(@tokenizer) 39 | else 40 | raise ObjectMaskParserError, "A valid Object mask is a 'mask' or 'filterMask' root property, or a property set containing root properties" + ObjectMaskToken.error_for_unexpected_token(token) 41 | end 42 | 43 | recognize_token(@tokenizer, :eos, "Extraneous text after object mask: ") 44 | 45 | if property && (property.name != "mask" && property.name != "filterMask") 46 | raise ObjectMaskParserError, "Object Mask must begin with a 'mask' or 'filterMask' root property" 47 | end 48 | 49 | if property_set && property_set.find { |subproperty| subproperty.name != 'mask' && subproperty.name != 'filterMask' } 50 | raise ObjectMaskParserError, "A root property set must contain only 'mask' or 'filterMask' root properties" 51 | end 52 | 53 | property || property_set 54 | end 55 | 56 | def parse_property_set(tokenizer) 57 | token = recognize_token(tokenizer, :property_set_start, "Expected '[': ") 58 | property_sequence = parse_property_sequence(tokenizer) 59 | token = recognize_token(tokenizer, :property_set_end, "Expected ']': ") 60 | property_sequence 61 | end 62 | 63 | def parse_property_sequence(tokenizer) 64 | first_property = parse_property(tokenizer) 65 | 66 | other_children = [] 67 | token = tokenizer.current_token 68 | if(token.type.equal?(:property_set_separator)) 69 | # skip the separator 70 | tokenizer.next_token 71 | 72 | # find another property 73 | other_children = parse_property_sequence(tokenizer) 74 | end 75 | 76 | return other_children.unshift(first_property) 77 | end 78 | 79 | def parse_property (tokenizer) 80 | property_name = nil 81 | property_type = nil 82 | property_children = nil 83 | 84 | property_name = parse_property_name(tokenizer) 85 | 86 | # look for a property type 87 | property_type = nil 88 | token = tokenizer.current_token 89 | if(token.type.equal?(:property_type_start)) 90 | property_type = parse_property_type(tokenizer) 91 | end 92 | 93 | token = tokenizer.current_token 94 | if(token.type.equal?(:property_child_separator)) 95 | property_children = [ parse_property_child(tokenizer) ] 96 | elsif (token.type.equal?(:property_set_start)) 97 | property_children = parse_property_set(tokenizer) 98 | end 99 | 100 | new_property = ObjectMaskProperty.new(property_name, property_type) 101 | new_property.add_children(property_children) if property_children 102 | 103 | return new_property 104 | end 105 | 106 | def parse_property_child(tokenizer) 107 | token = recognize_token(tokenizer, :property_child_separator, "Expected a '.': ") 108 | parse_property(tokenizer) 109 | end 110 | 111 | def parse_property_name(tokenizer) 112 | token = recognize_token(tokenizer, :identifier, "Expected a valid property type: ") { |token| token.valid_property_name? } 113 | return token.value 114 | end 115 | 116 | def parse_property_type(tokenizer) 117 | token = recognize_token(tokenizer, :property_type_start, "Expected '(': ") 118 | property_type = parse_property_type_name(tokenizer) 119 | token = recognize_token(tokenizer, :property_type_end, "Expected ')': ") 120 | return property_type 121 | end 122 | 123 | def parse_property_type_name(tokenizer) 124 | token = recognize_token(tokenizer, :identifier, "Expected a valid property type: ") { |token| token.valid_property_type? } 125 | return token.value 126 | end 127 | 128 | def recognize_token(tokenizer, expected_type, error_string, &predicate) 129 | token = tokenizer.current_token 130 | if token.type.equal?(expected_type) && (!predicate || predicate.call(token)) 131 | tokenizer.next_token 132 | else 133 | raise ObjectMaskParserError, error_string + ObjectMaskToken.error_for_unexpected_token(token) 134 | token = nil; 135 | end 136 | 137 | return token 138 | end 139 | 140 | end 141 | end # Module SoftLayer 142 | -------------------------------------------------------------------------------- /lib/softlayer/ObjectMaskProperty.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # For licensing information see the LICENSE.md file in the project root. 5 | #++ 6 | 7 | module SoftLayer 8 | # 9 | # A class representing a SoftLayer Object's property as represented 10 | # in an Object Mask. 11 | # 12 | # The Object Mask Parser parses Object Mask Strings into ObjectMaskProperty 13 | # structures. 14 | # 15 | # Another useful property ObjectMaskProperty structures is that they can 16 | # can merge with compatible structures to create a new structure which 17 | # incorporates the properties of both, but in a streamlined construct 18 | # 19 | class ObjectMaskProperty 20 | attr_reader :name, :type 21 | attr_reader :children 22 | 23 | def initialize(name, type = nil) 24 | @name = name 25 | @type = type 26 | @children = [] 27 | end 28 | 29 | def to_s 30 | full_name = @name 31 | 32 | if @type && !@type.empty? 33 | full_name += "(#{@type})" 34 | end 35 | 36 | if @children.count == 1 37 | full_name + ".#{@children[0].to_s}" 38 | elsif @children.count > 1 39 | full_name + "[#{@children.collect { |child| child.to_s }.join(',')}]" 40 | else 41 | full_name 42 | end 43 | end 44 | 45 | def can_merge_with? (other_property) 46 | (self.name == other_property.name) && (self.type == other_property.type) 47 | end 48 | 49 | def add_child(new_child) 50 | mergeable_child = @children.find { |existing_child| existing_child.can_merge_with? new_child } 51 | if mergeable_child 52 | mergeable_child.merge new_child 53 | else 54 | @children.push new_child 55 | end 56 | end 57 | 58 | def add_children(new_children) 59 | new_children.each { |new_child| add_child(new_child) } 60 | end 61 | 62 | # DANGER: assumes you've already checked can_merge_with? before calling this routine! 63 | def merge(other_property) 64 | add_children other_property.children 65 | end 66 | end 67 | end # module softlayer -------------------------------------------------------------------------------- /lib/softlayer/ObjectMaskToken.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # For licensing information see the LICENSE.md file in the project root. 5 | #++ 6 | 7 | module SoftLayer 8 | # 9 | # This class is an implementation detail of the Object Mask Parser 10 | # It represents a single semantic token as parsed out of an 11 | # Object Mask String 12 | # 13 | # The class also generates error messages that the parser can use 14 | # when it encounters an unexpected token 15 | # 16 | class ObjectMaskToken 17 | attr_reader :type 18 | attr_reader :value 19 | 20 | KnownTokenTypes = [ 21 | :invalid_token, 22 | :eos, # end of string 23 | :identifier, 24 | :property_set_start, 25 | :property_set_separator, 26 | :property_set_end, 27 | :property_type_start, 28 | :property_type_end, 29 | :property_child_separator, 30 | ] 31 | 32 | def initialize(token_type, token_value = nil) 33 | @type = token_type 34 | @value = token_value 35 | end 36 | 37 | def inspect 38 | "<#{@type.inspect}, #{@value.inspect}>" 39 | end 40 | 41 | def eql?(other_token) 42 | @type.eql?(other_token.type) && @value.eql?(other_token.value) 43 | end 44 | 45 | def invalid? 46 | return @type = :invalid_token 47 | end 48 | 49 | def end_of_string? 50 | return @type == :eos 51 | end 52 | 53 | def mask_root_marker? 54 | return @type == :identifier && (@value == "mask" || @value == "filterMask") 55 | end 56 | 57 | def valid_property_name? 58 | return @type == :identifier && @value.match(/\A[a-z][a-z0-9]*\z/i) 59 | end 60 | 61 | def valid_property_type? 62 | return @type == :identifier && @value.match(/\A[a-z][a-z0-9]*(_[a-z][a-z0-9]*)*\z/i) 63 | end 64 | 65 | def self.error_for_unexpected_token(token) 66 | case token.type 67 | when :invalid_token 68 | "Unrecognized token '#{token.value}'" 69 | when :eos 70 | "Unexpected end of string" 71 | when :identifier 72 | "Unexpected identifier '#{token.value}'" 73 | when :property_set_start 74 | "Unexpected '['" 75 | when :property_set_separator 76 | "Unexpected ','" 77 | when :property_set_end 78 | "Unexpected ']'" 79 | when :property_type_start 80 | "Unexpected '('" 81 | when :property_type_end 82 | "Unexpected ')'" 83 | when :property_child_separator 84 | "Unexpected '.'" 85 | else 86 | "Unexpected value (invalid token type)" 87 | end 88 | end 89 | end 90 | 91 | end # module SoftLayer -------------------------------------------------------------------------------- /lib/softlayer/ObjectMaskTokenizer.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # For licensing information see the LICENSE.md file in the project root. 5 | #++ 6 | 7 | require 'softlayer/ObjectMaskToken' 8 | require 'strscan' 9 | 10 | module SoftLayer 11 | # 12 | # This class is an implementation detail of the ObjectMaskParser 13 | # 14 | # It takes an Object Mask String and breaks it down 15 | # into ObjectMaskToken instances. 16 | # 17 | class ObjectMaskTokenizer 18 | ObjectMask_Token_Specs = [ 19 | [/\[/, :property_set_start], 20 | [/\,/, :property_set_separator], 21 | [/\]/, :property_set_end], 22 | [/\(/, :property_type_start], 23 | [/\)/, :property_type_end], 24 | [/\./, :property_child_separator], 25 | [/[a-z][a-z0-9_]*/i, :identifier] 26 | ] 27 | 28 | def initialize(mask_string) 29 | @mask_string = mask_string.clone 30 | @scanner = StringScanner.new(@mask_string) 31 | @current_token = nil 32 | end 33 | 34 | def more_tokens? 35 | return @current_token == nil || !@current_token.end_of_string? 36 | end 37 | 38 | def current_token 39 | @current_token = next_token if !@current_token 40 | @current_token 41 | end 42 | 43 | def next_token 44 | # if we're at the end of the string, we keep returning the 45 | # EOS token 46 | if more_tokens? then 47 | 48 | if !@scanner.eos? 49 | # skip whitespace 50 | @scanner.skip(/\s+/) 51 | 52 | # search through the token specs to find which (if any) matches 53 | token_spec = ObjectMask_Token_Specs.find() do |token_spec| 54 | @scanner.check(token_spec[0]) 55 | end 56 | 57 | # if a good token spec was found, set the current token to the one found 58 | if token_spec 59 | @current_token = ObjectMaskToken.new(token_spec.last, @scanner.scan(token_spec[0])) 60 | else 61 | @current_token = ObjectMaskToken.new(:invalid_token, @scanner.rest) 62 | @scanner.terminate 63 | end 64 | else 65 | @current_token = ObjectMaskToken.new(:eos) 66 | end 67 | end 68 | 69 | @current_token 70 | end 71 | end 72 | end # module SoftLayer -------------------------------------------------------------------------------- /lib/softlayer/ServerFirewallOrder.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # For licensing information see the LICENSE.md file in the project root. 5 | #++ 6 | 7 | module SoftLayer 8 | # 9 | # This class allows you to order a Firewall for a server 10 | # 11 | class ServerFirewallOrder 12 | # The server that you are ordering the firewall for. 13 | attr_reader :server 14 | 15 | ## 16 | # Create a new order for the given server 17 | def initialize (server) 18 | @server = server 19 | 20 | raise ArgumentError, "Server does not have an active Public interface" if server.firewall_port_speed == 0 21 | end 22 | 23 | ## 24 | # Calls the SoftLayer API to verify that the template provided by this order is valid 25 | # This routine will return the order template generated by the API or will throw an exception 26 | # 27 | # This routine will not actually create a Bare Metal Instance and will not affect billing. 28 | # 29 | # If you provide a block, it will receive the order template as a parameter and 30 | # the block may make changes to the template before it is submitted. 31 | def verify() 32 | order_template = firewall_order_template 33 | order_template = yield order_template if block_given? 34 | 35 | server.softlayer_client[:Product_Order].verifyOrder(order_template) 36 | end 37 | 38 | ## 39 | # Calls the SoftLayer API to place an order for a new server based on the template in this 40 | # order. If this succeeds then you will be billed for the new server. 41 | # 42 | # If you provide a block, it will receive the order template as a parameter and 43 | # the block may make changes to the template before it is submitted. 44 | def place_order!() 45 | order_template = firewall_order_template 46 | order_template = yield order_template if block_given? 47 | 48 | server.softlayer_client[:Product_Order].placeOrder(order_template) 49 | end 50 | 51 | protected 52 | 53 | ## 54 | # Returns a hash of the creation options formatted to be sent *to* 55 | # the SoftLayer API for either verification or completion 56 | def firewall_order_template 57 | client = server.softlayer_client 58 | additional_products_package = SoftLayer::ProductPackage.additional_products_package(client) 59 | 60 | template = { 61 | 'complexType' => 'SoftLayer_Container_Product_Order_Network_Protection_Firewall', 62 | 'quantity' => 1, 63 | 'packageId' => additional_products_package.id 64 | } 65 | 66 | if @server.service.service_name == "SoftLayer_Virtual_Guest" 67 | template['virtualGuests'] = [{'id' => @server.id}] 68 | else 69 | template['hardware'] = [{'id' => @server.id}] 70 | end 71 | 72 | expected_description = "#{@server.firewall_port_speed}Mbps Hardware Firewall" 73 | firewall_items = additional_products_package.items_with_description(expected_description) 74 | 75 | raise "Could not find a price item matching the description '#{expected_description}'" if firewall_items.empty? 76 | 77 | firewall_item = firewall_items[0] 78 | 79 | template['prices'] = [{ 'id' => firewall_item.price_id }] if firewall_item.respond_to?(:price_id) 80 | 81 | template 82 | end 83 | end # class ServerFirewallOrder 84 | end # module SoftLayer 85 | -------------------------------------------------------------------------------- /lib/softlayer/Ticket.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # For licensing information see the LICENSE.md file in the project root. 5 | #++ 6 | 7 | module SoftLayer 8 | class Ticket < SoftLayer::ModelBase 9 | 10 | ## 11 | # :attr_reader: 12 | # The title is an identifying string set when the ticket is created 13 | sl_attr :title 14 | 15 | ## 16 | # :attr_reader: 17 | # The ticket system maintains a fixed set of subjects for tickets that are used to ensure tickets make it to the right folks quickly 18 | sl_attr :subject 19 | 20 | ## 21 | # :attr_reader: last_edited_at 22 | # The date the ticket was last updated. 23 | sl_attr :last_edited_at, 'lastEditDate' 24 | 25 | ## 26 | # :attr_reader: 27 | # The date the ticket was last updated. 28 | # 29 | # DEPRECATION WARNING: This attribute is deprecated in favor of last_edited_at 30 | # and will be removed in the next major release. 31 | sl_attr :lastEditDate 32 | 33 | ## 34 | # Returns true if the ticket has "unread" updates 35 | def has_updates? 36 | self['newUpdatesFlag'] 37 | end 38 | 39 | ## 40 | # Returns true if the ticket is a server admin ticket 41 | def server_admin_ticket? 42 | # note that serverAdministrationFlag comes from the server as an Integer (0, or 1) 43 | self['serverAdministrationFlag'] != 0 44 | end 45 | 46 | ## 47 | # Add an update to this ticket. 48 | # 49 | def update(body = nil) 50 | self.service.edit(self.softlayer_hash, body) 51 | end 52 | 53 | ## 54 | # Override of service from ModelBase. Returns the SoftLayer_Ticket service 55 | # set up to talk to the ticket with my ID. 56 | def service 57 | return softlayer_client[:Ticket].object_with_id(self.id) 58 | end 59 | 60 | ## 61 | # Override from model base. Requests new details about the ticket 62 | # from the server. 63 | def softlayer_properties(object_mask = nil) 64 | my_service = self.service 65 | 66 | if(object_mask) 67 | my_service = service.object_mask(object_mask) 68 | else 69 | my_service = service.object_mask(self.class.default_object_mask.to_sl_object_mask) 70 | end 71 | 72 | my_service.getObject() 73 | end 74 | 75 | ## 76 | # Returns the default object mask,as a hash, that is used when 77 | # retrieving ticket information from the SoftLayer server. 78 | def self.default_object_mask 79 | { 80 | "mask" => [ 81 | 'id', # This is an internal ticket ID, not the one usually seen in the portal 82 | 'serviceProvider', 83 | 'serviceProviderResourceId', # This is the ticket ID usually seen in the portal 84 | 'title', 85 | 'subject', 86 | {'assignedUser' => ['username', 'firstName', 'lastName'] }, 87 | 'status.id', 88 | 'createDate', 89 | 'lastEditDate', 90 | 'newUpdatesFlag', # This comes in from the server as a Boolean value 91 | 'awaitingUserResponseFlag', # This comes in from the server as a Boolean value 92 | 'serverAdministrationFlag', # This comes in from the server as an integer :-( 93 | ] 94 | }.to_sl_object_mask 95 | end 96 | 97 | ## 98 | # Queries the SoftLayer API to retrieve a list of the valid 99 | # ticket subjects. 100 | def self.ticket_subjects(client = nil) 101 | @ticket_subjects ||= nil 102 | 103 | if !@ticket_subjects 104 | softlayer_client = client || Client.default_client 105 | raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client 106 | 107 | @ticket_subjects = softlayer_client[:Ticket_Subject].getAllObjects(); 108 | end 109 | 110 | @ticket_subjects 111 | end 112 | 113 | ## 114 | # Find the ticket with the given ID and return it 115 | # 116 | # Options should contain: 117 | # 118 | # +:client+ - the client in which to search for the ticket 119 | # 120 | # If a client is not provided then the routine will search Client::default_client 121 | # If Client::default_client is also nil the routine will raise an error. 122 | # 123 | # Additionally you may provide options related to the request itself: 124 | # * *:object_mask* (string) - The object mask of properties you wish to receive for the items returned. 125 | # If not provided, the result will use the default object mask 126 | def self.ticket_with_id(ticket_id, options = {}) 127 | softlayer_client = options[:client] || Client.default_client 128 | raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client 129 | 130 | ticket_service = softlayer_client[:Ticket].object_with_id(ticket_id) 131 | ticket_service = ticket_service.object_mask(default_object_mask.to_sl_object_mask) 132 | ticket_service = ticket_service.object_mask(options[:object_mask]) if options[:object_mask] 133 | 134 | ticket_data = ticket_service.getObject() 135 | 136 | return Ticket.new(softlayer_client, ticket_data) 137 | end 138 | 139 | ## 140 | # Create and submit a new support Ticket to SoftLayer. 141 | # 142 | # The options parameter should contain: 143 | # 144 | # +:client+ - The client used to connect to the API 145 | # 146 | # If no client is given, then the routine will try to use Client.default_client 147 | # If no client can be found the routine will raise an error. 148 | # 149 | # The options should also contain: 150 | # 151 | # * +:title+ (String) - The user provided title for the ticket. 152 | # * +:body+ (String) - The content of the ticket 153 | # * +:subject_id+ (Int) - The id of a subject to use for the ticket. A list of ticket subjects can be returned by SoftLayer::Ticket.ticket_subjects 154 | # * +:assigned_user_id+ (Int) - The id of a user to whom the ticket should be assigned 155 | def self.create_standard_ticket(options = {}) 156 | softlayer_client = options[:client] || SoftLayer::Client.default_client 157 | raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client 158 | 159 | title = options[:title] 160 | body = options[:body] 161 | subject_id = options[:subject_id] 162 | assigned_user_id = options[:assigned_user_id] 163 | 164 | if(nil == assigned_user_id) 165 | current_user = softlayer_client[:Account].object_mask("id").getCurrentUser() 166 | assigned_user_id = current_user['id'] 167 | end 168 | 169 | new_ticket = { 170 | 'subjectId' => subject_id, 171 | 'contents' => body, 172 | 'assignedUserId' => assigned_user_id, 173 | 'title' => title 174 | } 175 | 176 | ticket_data = softlayer_client[:Ticket].createStandardTicket(new_ticket, body) 177 | return new(softlayer_client, ticket_data) 178 | end 179 | end 180 | end 181 | -------------------------------------------------------------------------------- /lib/softlayer/UserCustomer.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # For licensing information see the LICENSE.md file in the project root. 5 | #++ 6 | 7 | module SoftLayer 8 | ## 9 | # Each SoftLayer UserCustomer instance provides information 10 | # relating to a single SoftLayer customer portal user 11 | # 12 | # This class roughly corresponds to the entity SoftLayer_User_Customer 13 | # in the API. 14 | # 15 | class UserCustomer < ModelBase 16 | include ::SoftLayer::DynamicAttribute 17 | 18 | ## 19 | # :attr_reader: alternate_phone 20 | # A portal user's secondary phone number. 21 | sl_attr :alternate_phone, 'alternatePhone' 22 | 23 | ## 24 | # :attr_reader: created_at 25 | # The date a portal user's record was created. 26 | sl_attr :created_at, 'createDate' 27 | 28 | ## 29 | # :attr_reader: created 30 | # The date a portal user's record was created. 31 | # DEPRECATION WARNING: This attribute is deprecated in favor of created_at 32 | # and will be removed in the next major release. 33 | sl_attr :created, 'createDate' 34 | 35 | ## 36 | # :attr_reader: display_name 37 | # The portal user's display name. 38 | sl_attr :display_name, 'displayName' 39 | 40 | ## 41 | # :attr_reader: 42 | # A portal user's email address. 43 | sl_attr :email 44 | 45 | ## 46 | # :attr_reader: first_name 47 | # A portal user's first name. 48 | sl_attr :first_name, 'firstName' 49 | 50 | ## 51 | # :attr_reader: last_name 52 | # A portal user's last name. 53 | sl_attr :last_name, 'lastName' 54 | 55 | ## 56 | # :attr_reader: modified_at 57 | # The date a portal user's record was last modified. 58 | sl_attr :modified_at, 'modifyDate' 59 | 60 | ## 61 | # :attr_reader: modified 62 | # The date a portal user's record was last modified. 63 | # DEPRECATION WARNING: This attribute is deprecated in favor of modified_at 64 | # and will be removed in the next major release. 65 | sl_attr :modified, 'modifyDate' 66 | 67 | ## 68 | # :attr_reader: office_phone 69 | # A portal user's office phone number. 70 | sl_attr :office_phone, 'officePhone' 71 | 72 | ## 73 | # :attr_reader: password_expires 74 | # The expiration date for the user's password. 75 | sl_attr :password_expires, 'passwordExpireDate' 76 | 77 | ## 78 | # :attr_reader: status_changed 79 | # The date a portal users record's last status change. 80 | sl_attr :status_changed, 'statusDate' 81 | 82 | ## 83 | # :attr_reader: 84 | # A portal user's username. 85 | sl_attr :username 86 | 87 | ## 88 | # Retrieve a portal user's additional email addresses. 89 | # These email addresses are contacted when updates are made to support tickets. 90 | # :call-seq: 91 | # additional_emails(force_update=false) 92 | sl_dynamic_attr :additional_emails do |resource| 93 | resource.should_update? do 94 | #only retrieved once per instance 95 | @additional_emails == nil 96 | end 97 | 98 | resource.to_update do 99 | additional_emails = self.service.getAdditionalEmails 100 | additional_emails.collect { |additional_email| additional_email['email'] } 101 | end 102 | end 103 | 104 | ## 105 | # Retrieve a portal user's API Authentication keys. 106 | # There is a max limit of two API keys per user. 107 | # :call-seq: 108 | # api_authentication_keys(force_update=false) 109 | sl_dynamic_attr :api_authentication_keys do |resource| 110 | resource.should_update? do 111 | #only retrieved once per instance 112 | @api_authentication_keys == nil 113 | end 114 | 115 | resource.to_update do 116 | self.service.object_mask("mask[authenticationKey,ipAddressRestriction]").getApiAuthenticationKeys 117 | end 118 | end 119 | 120 | ## 121 | # Retrieve the external authentication bindings that link an external identifier to a SoftLayer user. 122 | # :call-seq: 123 | # external_bindings(force_update=false) 124 | sl_dynamic_attr :external_bindings do |resource| 125 | resource.should_update? do 126 | #only retrieved once per instance 127 | @external_bindings == nil 128 | end 129 | 130 | resource.to_update do 131 | external_bindings = self.service.object_mask(UserCustomerExternalBinding.default_object_mask).getExternalBindings 132 | external_bindings.collect { |external_binding| UserCustomerExternalBinding.new(softlayer_client, external_binding) } 133 | end 134 | end 135 | 136 | ## 137 | # Retrieve the user customer associated with the specified username 138 | # 139 | def self.user_customer_with_username(username, client = nil) 140 | softlayer_client = client || Client.default_client 141 | raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client 142 | raise "#{__method__} requires a user customer username but none was given" if !username || username.empty? 143 | 144 | user_customer_object_filter = ObjectFilter.new() 145 | 146 | user_customer_object_filter.modify { |filter| filter.accept('users.username').when_it is(username) } 147 | 148 | account_service = Account.account_for_client(softlayer_client).service 149 | account_service = account_service.object_filter(user_customer_object_filter) 150 | account_service = account_service.object_mask(UserCustomer.default_object_mask) 151 | 152 | user_customer_data = account_service.getUsers 153 | 154 | if user_customer_data.length == 1 155 | UserCustomer.new(softlayer_client, user_customer_data.first) 156 | end 157 | end 158 | 159 | ## 160 | # Returns the service for interacting with this user customer through the network API 161 | # 162 | def service 163 | softlayer_client[:User_Customer].object_with_id(self.id) 164 | end 165 | 166 | protected 167 | 168 | def self.default_object_mask 169 | { 170 | "mask(SoftLayer_User_Customer)" => [ 171 | 'alternatePhone', 172 | 'createDate', 173 | 'displayName', 174 | 'email', 175 | 'firstName', 176 | 'id', 177 | 'lastName', 178 | 'modifyDate', 179 | 'officePhone', 180 | 'passwordExpireDate', 181 | 'statusDate', 182 | 'username' 183 | ] 184 | }.to_sl_object_mask 185 | end 186 | end 187 | end #SoftLayer 188 | -------------------------------------------------------------------------------- /lib/softlayer/UserCustomerExternalBinding.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # For licensing information see the LICENSE.md file in the project root. 5 | #++ 6 | 7 | module SoftLayer 8 | ## 9 | # Each SoftLayer UserCustomerExternalBinding instance provides information 10 | # for a single user customer's external binding. 11 | # 12 | # This class roughly corresponds to the entity SoftLayer_User_Customer_External_Binding 13 | # in the API. 14 | # 15 | class UserCustomerExternalBinding < ModelBase 16 | include ::SoftLayer::DynamicAttribute 17 | 18 | ## 19 | # :attr_reader: 20 | # The flag that determines whether the external binding is active will be 21 | # used for authentication or not. 22 | sl_attr :active 23 | 24 | ## 25 | # :attr_reader: created_at 26 | # The date that the external authentication binding was created. 27 | sl_attr :created_at, 'createDate' 28 | 29 | ## 30 | # :attr_reader: created 31 | # The date that the external authentication binding was created. 32 | # DEPRECATION WARNING: This attribute is deprecated in favor of created_at 33 | # and will be removed in the next major release. 34 | sl_attr :created, 'createDate' 35 | 36 | ## 37 | # :attr_reader: 38 | # The password used to authenticate the external id at an external 39 | # authentication source. 40 | sl_attr :password 41 | 42 | ## 43 | # Retrieve an optional note for identifying the external binding. 44 | # :call-seq: 45 | # note(force_update=false) 46 | sl_dynamic_attr :note do |resource| 47 | resource.should_update? do 48 | #only retrieved once per instance 49 | @note == nil 50 | end 51 | 52 | resource.to_update do 53 | self.service.getNote 54 | end 55 | end 56 | 57 | ## 58 | # Retrieve the user friendly name of a type of external authentication binding. 59 | # :call-seq: 60 | # type(force_update=false) 61 | sl_dynamic_attr :type do |resource| 62 | resource.should_update? do 63 | #only retrieved once per instance 64 | @type == nil 65 | end 66 | 67 | resource.to_update do 68 | type = self.service.getType 69 | type['name'] 70 | end 71 | end 72 | 73 | ## 74 | # Retrieve the user friendly name of an external binding vendor. 75 | # :call-seq: 76 | # vendor(force_update=false) 77 | sl_dynamic_attr :vendor do |resource| 78 | resource.should_update? do 79 | #only retrieved once per instance 80 | @vendor == nil 81 | end 82 | 83 | resource.to_update do 84 | vendor = self.service.getVendor 85 | vendor['name'] 86 | end 87 | end 88 | 89 | ## 90 | # Returns the service for interacting with this user customer external binding 91 | # through the network API 92 | # 93 | def service 94 | softlayer_client[:User_Customer_External_Binding].object_with_id(self.id) 95 | end 96 | 97 | protected 98 | 99 | def self.default_object_mask 100 | { 101 | "mask(SoftLayer_User_Customer_External_Binding)" => [ 102 | 'active', 103 | 'createDate', 104 | 'id', 105 | 'password' 106 | ] 107 | }.to_sl_object_mask 108 | end 109 | end 110 | end #SoftLayer 111 | -------------------------------------------------------------------------------- /lib/softlayer/VLANFirewallOrder.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # For licensing information see the LICENSE.md file in the project root. 5 | #++ 6 | 7 | module SoftLayer 8 | # 9 | # This class allows you to order a Firewall for a VLAN 10 | # 11 | class VLANFirewallOrder 12 | ## 13 | # The VLAN that you are ordering the firewall for. 14 | attr_reader :vlan_id 15 | 16 | ## 17 | # Set high_availability to true if you want redundant 18 | # firewall devices (defaults to false, no high_availability) 19 | attr_accessor :high_availability 20 | 21 | ## 22 | # Create a new order for the given VLAN 23 | # Note that the vlan_id is NOT the same as the vlan number. 24 | def initialize (vlan_id, client = nil) 25 | @softlayer_client = client || Client.default_client 26 | raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !@softlayer_client 27 | 28 | @vlan_id = vlan_id 29 | @high_availability = false 30 | end 31 | 32 | ## 33 | # Calls the SoftLayer API to verify that the template provided by this order is valid 34 | # This routine will return the order template generated by the API or will throw an exception 35 | # 36 | # This routine will not actually create a Bare Metal Instance and will not affect billing. 37 | # 38 | # If you provide a block, it will receive the order template as a parameter and 39 | # the block may make changes to the template before it is submitted. 40 | def verify() 41 | order_template = firewall_order_template 42 | order_template = yield order_template if block_given? 43 | 44 | @softlayer_client[:Product_Order].verifyOrder(order_template) 45 | end 46 | 47 | ## 48 | # Calls the SoftLayer API to place an order for a new server based on the template in this 49 | # order. If this succeeds then you will be billed for the new server. 50 | # 51 | # If you provide a block, it will receive the order template as a parameter and 52 | # the block may make changes to the template before it is submitted. 53 | def place_order!() 54 | order_template = firewall_order_template 55 | order_template = yield order_template if block_given? 56 | 57 | @softlayer_client[:Product_Order].placeOrder(order_template) 58 | end 59 | 60 | protected 61 | 62 | ## 63 | # Returns a hash of the creation options formatted to be sent to 64 | # the SoftLayer API for either verification or completion 65 | def firewall_order_template 66 | client = @softlayer_client 67 | additional_products_package = SoftLayer::ProductPackage.additional_products_package(client) 68 | 69 | template = { 70 | 'complexType' => 'SoftLayer_Container_Product_Order_Network_Protection_Firewall_Dedicated', 71 | 'quantity' => 1, 72 | 'packageId' => additional_products_package.id, 73 | 'vlanId' => @vlan_id 74 | } 75 | 76 | if @high_availability 77 | expected_description = "Hardware Firewall (High Availability)" 78 | else 79 | expected_description = "Hardware Firewall (Dedicated)" 80 | end 81 | 82 | firewall_items = additional_products_package.items_with_description(expected_description) 83 | 84 | raise "Could not find a price item matching the description '#{expected_description}'" if firewall_items.empty? 85 | 86 | firewall_item = firewall_items[0] 87 | 88 | template['prices'] = [{ 'id' => firewall_item.price_id }] if firewall_item.respond_to?(:price_id) 89 | 90 | template 91 | end 92 | end # class VLANFirewallOrder 93 | end # module SoftLayer 94 | -------------------------------------------------------------------------------- /lib/softlayer/VirtualDiskImageSoftware.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # For licensing information see the LICENSE.md file in the project root. 5 | #++ 6 | 7 | module SoftLayer 8 | ## 9 | # Each SoftLayer VirtualDiskImageSoftware is a record that connects 10 | # a computing instance's virtual disk images with software records. 11 | # 12 | # This class roughly corresponds to the entity SoftLayer_Virtual_Disk_Image_Software 13 | # in the API. 14 | # 15 | class VirtualDiskImageSoftware < ModelBase 16 | include ::SoftLayer::DynamicAttribute 17 | 18 | ## 19 | # The manufacturer, name and version of a piece of software. 20 | # 21 | def description 22 | self['softwareDescription']['longDescription'] 23 | end 24 | 25 | ## 26 | # The name of this specific piece of software. 27 | # 28 | def name 29 | self['softwareDescription']['name'] 30 | end 31 | 32 | ## 33 | # The password for this specific virtual disk image software instance. 34 | # 35 | def passwords 36 | self['passwords'] 37 | end 38 | 39 | protected 40 | 41 | def self.default_object_mask 42 | { 43 | "mask(SoftLayer_Virtual_Disk_Image_Software)" => [ 44 | 'id', 45 | 'passwords[password,username]', 46 | 'softwareDescription[longDescription,name]' 47 | ] 48 | }.to_sl_object_mask 49 | end 50 | end 51 | end #SoftLayer 52 | -------------------------------------------------------------------------------- /lib/softlayer/VirtualServerUpgradeOrder.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # For licensing information see the LICENSE.md file in the project root. 5 | #++ 6 | 7 | module SoftLayer 8 | # This class is used to order changes to a virtual server. Although 9 | # the class is named "upgrade" this class can also be used for "downgrades" 10 | # (i.e. changing attributes to a smaller, or slower, value) 11 | # 12 | # The class can also be used to discover what upgrades are available 13 | # for a given virtual server. 14 | # 15 | class VirtualServerUpgradeOrder 16 | # The virtual server that this order is designed to upgrade. 17 | attr_reader :virtual_server 18 | 19 | # The number of cores the server should have after the upgrade. 20 | # If this is nil, the the number of cores will not change 21 | attr_accessor :cores 22 | 23 | # The amount of RAM (in GB) that the server should have after the upgrade 24 | # If this is nil, the ram will not change 25 | attr_accessor :ram 26 | 27 | # The port speed (in Mega bits per second) that the server should have 28 | # after the upgrade. This is typically a value like 100, or 1000 29 | # If this is nil, the port speeds will not change 30 | attr_accessor :max_port_speed 31 | 32 | # The date and time when you would like the upgrade to be processed. 33 | # This should simply be a Time object. If nil then the upgrade 34 | # will be performed immediately 35 | attr_accessor :upgrade_at 36 | 37 | ## 38 | # Create an upgrade order for the virtual server provided. 39 | # 40 | def initialize(virtual_server) 41 | raise "A virtual server must be provided at the time a virtual server order is created" if !virtual_server || !virtual_server.kind_of?(SoftLayer::VirtualServer) 42 | @virtual_server = virtual_server 43 | end 44 | 45 | ## 46 | # Sends the order represented by this object to SoftLayer for validation. 47 | # 48 | # If a block is passed to verify, the code will send the order template 49 | # being constructed to the block before the order is actually sent for 50 | # validation. 51 | # 52 | def verify() 53 | if has_order_items? 54 | order_template = order_object 55 | order_template = yield order_object if block_given? 56 | @virtual_server.softlayer_client[:Product_Order].verifyOrder(order_template) 57 | end 58 | end 59 | 60 | ## 61 | # Places the order represented by this object. This is likely to 62 | # involve a change to the charges on an account. 63 | # 64 | # If a block is passed to this routine, the code will send the order template 65 | # being constructed to that block before the order is sent 66 | # 67 | def place_order!() 68 | if has_order_items? 69 | order_template = order_object 70 | order_template = yield order_object if block_given? 71 | 72 | @virtual_server.softlayer_client[:Product_Order].placeOrder(order_template) 73 | end 74 | end 75 | 76 | ## 77 | # Return a list of values that are valid for the :cores attribute 78 | def core_options() 79 | self._item_prices_in_category("guest_core").map { |item_price| item_price['item']['capacity'].to_i}.sort.uniq 80 | end 81 | 82 | ## 83 | # Return a list of values that are valid for the :memory attribute 84 | def memory_options() 85 | self._item_prices_in_category("ram").map { |item_price| item_price['item']['capacity'].to_i}.sort.uniq 86 | end 87 | 88 | ## 89 | # Returns a list of valid values for max_port_speed 90 | def max_port_speed_options(client = nil) 91 | self._item_prices_in_category("port_speed").map { |item_price| item_price['item']['capacity'].to_i}.sort.uniq 92 | end 93 | 94 | private 95 | 96 | ## 97 | # Returns true if this order object has any upgrades specified 98 | # 99 | def has_order_items? 100 | @cores != nil || @ram != nil || @max_port_speed != nil 101 | end 102 | 103 | ## 104 | # Returns a list of the update item prices, in the given category, for the server 105 | # 106 | def _item_prices_in_category(which_category) 107 | @virtual_server.upgrade_options.select { |item_price| item_price['categories'].find { |category| category['categoryCode'] == which_category } } 108 | end 109 | 110 | ## 111 | # Searches through the upgrade items prices known to this server for the one that is in a particular category 112 | # and whose capacity matches the value given. Returns the item_price or nil 113 | # 114 | def _item_price_with_capacity(which_category, capacity) 115 | _item_prices_in_category(which_category).find { |item_price| item_price['item']['capacity'].to_i == capacity} 116 | end 117 | 118 | ## 119 | # construct an order object 120 | # 121 | def order_object 122 | prices = [] 123 | 124 | cores_price_item = @cores ? _item_price_with_capacity("guest_core", @cores) : nil 125 | ram_price_item = @ram ? _item_price_with_capacity("ram", @ram) : nil 126 | max_port_speed_price_item = @max_port_speed ? _item_price_with_capacity("port_speed", @max_port_speed) : nil 127 | 128 | prices << { "id" => cores_price_item['id'] } if cores_price_item 129 | prices << { "id" => ram_price_item['id'] } if ram_price_item 130 | prices << { "id" => max_port_speed_price_item['id'] } if max_port_speed_price_item 131 | 132 | # put together an order 133 | upgrade_order = { 134 | 'complexType' => 'SoftLayer_Container_Product_Order_Virtual_Guest_Upgrade', 135 | 'virtualGuests' => [{'id' => @virtual_server.id }], 136 | 'properties' => [{'name' => 'MAINTENANCE_WINDOW', 'value' => @upgrade_at ? @upgrade_at.iso8601 : Time.now.iso8601}], 137 | 'prices' => prices 138 | } 139 | end 140 | end # VirtualServerUpgradeOrder 141 | end # SoftLayer Module 142 | -------------------------------------------------------------------------------- /lib/softlayer/base.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # For licensing information see the LICENSE.md file in the project root. 5 | #++ 6 | 7 | require 'rubygems' 8 | 9 | ## 10 | # The SoftLayer module provides a namespace for SoftLayer code. 11 | # 12 | module SoftLayer 13 | # The version number (including major, minor, and bugfix numbers) 14 | # This should change in accordance with the concept of Semantic Versioning 15 | VERSION = "3.2.3" # version history in the CHANGELOG.textile file at the root of the source 16 | 17 | # The base URL of the SoftLayer API available to the public internet. 18 | API_PUBLIC_ENDPOINT = 'https://api.softlayer.com/xmlrpc/v3/' 19 | 20 | # The base URL of the SoftLayer API available through SoftLayer's private network 21 | API_PRIVATE_ENDPOINT = 'https://api.service.softlayer.com/xmlrpc/v3/' 22 | 23 | #-- 24 | # These globals can be used to simplify client creation 25 | #++ 26 | 27 | # Set this if you want to provide a default username for each client as it is created. 28 | # usernames provided to the client initializer will override the global 29 | $SL_API_USERNAME = nil 30 | 31 | # Set this if you want to provide a default api_key for each client as it is 32 | # created. API keys provided in the constructor when a client is created will 33 | # override the values in this global 34 | $SL_API_KEY = nil 35 | 36 | # The base URL used for the SoftLayer API's 37 | $SL_API_BASE_URL = SoftLayer::API_PUBLIC_ENDPOINT 38 | end # module SoftLayer 39 | 40 | # 41 | # History: 42 | # 43 | # The history can be found in the CHANGELOG.textile file in the project root directory 44 | -------------------------------------------------------------------------------- /lib/softlayer/object_mask_helpers.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # For licensing information see the LICENSE.md file in the project root. 5 | #++ 6 | 7 | ## 8 | # This extension to the Hash class to allows object masks to be constructed 9 | # from built-in Ruby types and converted to object masks strings for presentation 10 | # to the SoftLayer API 11 | class Hash 12 | # Given a hash, generate an Object Mask string from the structure 13 | # found within the hash. This allows object masks to be constructed 14 | # as hashes, then converted to strings when they must be passed 15 | # to the API. The routine does some very rudimentary validation to 16 | # ensure that the hash represents a valid object mask, but care must 17 | # still be taken when constructing the hash. 18 | def to_sl_object_mask() 19 | raise RuntimeError, "An object mask must contain properties" if empty? 20 | raise RuntimeError, "An object mask must start with root properties" if keys().find { |key| !__valid_root_property_key?(key) } 21 | 22 | key_strings = __sl_object_mask_properties_for_keys(); 23 | key_strings.count > 1 ? "[#{key_strings.join(',')}]" : "#{key_strings[0]}" 24 | end 25 | 26 | # Returns a string representing the hash as a property within a larger 27 | # object mask. This routine is an implementation detail used in the conversion 28 | # of hashes to object mask strings. You should not have to call this method directly. 29 | def _to_sl_object_mask_property() 30 | key_strings = __sl_object_mask_properties_for_keys(); 31 | "#{key_strings.join(',')}" 32 | end 33 | 34 | private 35 | 36 | def __valid_root_property_key?(key_string) 37 | return key_string == "mask" || (0 == (key_string =~ /\Amask\([a-z][a-z0-9_]*\)\z/i)) 38 | end 39 | 40 | def __sl_object_mask_properties_for_keys 41 | key_strings = []; 42 | 43 | each do |key, value| 44 | return "" if !value 45 | 46 | string_for_key = key._to_sl_object_mask_property 47 | 48 | if value.kind_of?(String) || value.kind_of?(Symbol) then 49 | string_for_key = "#{string_for_key}.#{value._to_sl_object_mask_property}" 50 | end 51 | 52 | if value.kind_of?(Array) || value.kind_of?(Hash) then 53 | value_string = value._to_sl_object_mask_property 54 | if value_string && !value_string.empty? 55 | string_for_key = "#{string_for_key}[#{value_string}]" 56 | end 57 | end 58 | 59 | key_strings.push(string_for_key) 60 | end 61 | 62 | key_strings 63 | end 64 | end 65 | 66 | ## 67 | # SoftLayer Extensions to the Array class to support using arrays to create 68 | # object masks 69 | class Array 70 | # Returns a string representing the object mask content represented by the 71 | # Array. Each value in the array is converted to its object mask equivalent 72 | # This routine is an implementation detail used in the conversion of hashes 73 | # to object mask strings. You should not have to call this method directly. 74 | def _to_sl_object_mask_property() 75 | return "" if self.empty? 76 | property_content = map { |item| item ? item._to_sl_object_mask_property() : nil }.compact.flatten.join(",") 77 | "#{property_content}" 78 | end 79 | end 80 | 81 | ## 82 | # SoftLayer Extensions to the String class to support using strings to create 83 | # object masks 84 | class String 85 | # Returns a string representing the object mask content represented by the 86 | # String. Strings are simply represented as copies of themselves. We make 87 | # a copy in case the original String is modified somewhere along the way 88 | # This routine is an implementation detail used in the conversion of hashes 89 | # to object mask strings. You should not have to call this method directly. 90 | def _to_sl_object_mask_property() 91 | return self.strip 92 | end 93 | end 94 | 95 | ## 96 | # SoftLayer Extensions to the Symbol class to support using symbols to create 97 | # object masks 98 | class Symbol 99 | # Converts the Symbol to a string, then converts the string to an 100 | # object mask property. This routine is an implementation detail used in 101 | # the conversion of hashes to object mask strings. You should not have to 102 | # call this method directly. 103 | def _to_sl_object_mask_property() 104 | self.to_s._to_sl_object_mask_property() 105 | end 106 | end 107 | -------------------------------------------------------------------------------- /lib/softlayer_api.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # For licensing information see the LICENSE.md file in the project root. 5 | #++ 6 | 7 | # requirements from the core libraries 8 | require 'date' 9 | require 'time' 10 | 11 | # Requirements for the Foundation Layer 12 | require 'softlayer/base' 13 | require 'softlayer/object_mask_helpers' 14 | require 'softlayer/APIParameterFilter' 15 | require 'softlayer/ObjectFilter' 16 | require 'softlayer/ObjectMaskParser' 17 | require 'softlayer/Config' 18 | require 'softlayer/Client' 19 | require 'softlayer/Service' 20 | 21 | # Requirements for the Model Layer 22 | require 'softlayer/ModelBase' 23 | require 'softlayer/Datacenter' 24 | require 'softlayer/DynamicAttribute' 25 | require 'softlayer/Account' 26 | require 'softlayer/AccountPassword' 27 | require 'softlayer/NetworkMonitor' 28 | require 'softlayer/Server' 29 | require 'softlayer/BareMetalServer' 30 | require 'softlayer/BareMetalServerOrder' 31 | require 'softlayer/BareMetalServerOrder_Package' 32 | require 'softlayer/ImageTemplate' 33 | require 'softlayer/NetworkComponent' 34 | require 'softlayer/NetworkMessageDelivery' 35 | require 'softlayer/NetworkService' 36 | require 'softlayer/NetworkStorageAllowedHost' 37 | require 'softlayer/NetworkStorageCredential' 38 | require 'softlayer/NetworkStorageGroup' 39 | require 'softlayer/NetworkStorage' 40 | require 'softlayer/ProductPackage' 41 | require 'softlayer/ProductItemCategory' 42 | require 'softlayer/ServerFirewall' 43 | require 'softlayer/ServerFirewallOrder' 44 | require 'softlayer/SoftwarePassword' 45 | require 'softlayer/Software' 46 | require 'softlayer/Ticket' 47 | require 'softlayer/UserCustomerExternalBinding' 48 | require 'softlayer/UserCustomer' 49 | require 'softlayer/VirtualDiskImage' 50 | require 'softlayer/VirtualDiskImageSoftware' 51 | require 'softlayer/VirtualServer' 52 | require 'softlayer/VirtualServerOrder' 53 | require 'softlayer/VirtualServerOrder_Package' 54 | require 'softlayer/VirtualServerUpgradeOrder' 55 | require 'softlayer/VLANFirewall' 56 | require 'softlayer/VLANFirewallOrder' 57 | -------------------------------------------------------------------------------- /rakefile: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # For licensing information see the LICENSE.md file in the project root. 5 | #++ 6 | 7 | $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__))) 8 | 9 | require 'rubygems' 10 | require 'bundler/gem_tasks' 11 | require 'rspec/core/rake_task' 12 | require 'rdoc/task' 13 | 14 | Rake::RDocTask.new do |rd| 15 | require 'lib/softlayer/base.rb' 16 | 17 | puts RDoc::Markdown::DEFAULT_EXTENSIONS 18 | 19 | rd.title = "softlayer_api gem #{SoftLayer::VERSION}" 20 | rd.main = "Welcome.md" 21 | rd.rdoc_dir = "doc" 22 | rd.rdoc_files.include('lib/**/*.rb') 23 | rd.options << '--page-dir=doc_src' 24 | end 25 | 26 | RSpec::Core::RakeTask.new(:spec) do |t| 27 | $DEBUG = 1 28 | t.rspec_opts = ["-c"] 29 | end 30 | 31 | Rake::Task[:build].enhance [:rerdoc] 32 | 33 | task :gem => [:build] 34 | task :default => :spec -------------------------------------------------------------------------------- /softlayer_api.gemspec: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # For licensing information see the LICENSE.md file in the project root. 5 | #++ 6 | 7 | $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__))) 8 | require 'lib/softlayer/base' 9 | 10 | Gem::Specification.new do |s| 11 | s.name = %q{softlayer_api} 12 | s.version = SoftLayer::VERSION 13 | s.author = "SoftLayer Development Team" 14 | s.email = %q{sldn@softlayer.com} 15 | s.description = %q{The softlayer_api gem offers a convenient mechanism for invoking the services of the SoftLayer API from Ruby.} 16 | s.summary = %q{Library for accessing the SoftLayer API} 17 | s.homepage = %q{http://sldn.softlayer.com/} 18 | s.license = %q{MIT} 19 | 20 | s.files = Dir["README.textile", "LICENSE.textile", "CHANGELOG.textile", "lib/**/*.rb", "test/**/*.rb", "examples/**/*.rb"] 21 | s.require_paths = ["lib"] 22 | 23 | s.has_rdoc = true 24 | s.required_ruby_version = '>= 1.9.2' 25 | s.add_runtime_dependency 'configparser', '~>0.1.2' 26 | s.add_development_dependency 'rake' 27 | s.add_development_dependency 'rspec' 28 | s.add_development_dependency 'rdoc', '>=2.4.2' 29 | s.add_development_dependency 'json', '~> 1.8', '>= 1.8.1' 30 | # Fixing the following gems' versions to avoid requiring 31 | # Ruby 2.0. 32 | s.add_development_dependency 'mime-types', '= 2.99.3' 33 | s.add_development_dependency 'coveralls', '= 0.7.2' 34 | end 35 | -------------------------------------------------------------------------------- /spec/APIParameterFilter_spec.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # For licensing information see the LICENSE.md file in the project root. 5 | #++ 6 | 7 | $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) 8 | 9 | require 'rubygems' 10 | require 'softlayer_api' 11 | require 'rspec' 12 | 13 | describe SoftLayer::APIParameterFilter do 14 | let(:filter) {filter = SoftLayer::APIParameterFilter.new(nil)} 15 | 16 | describe "#object_with_id" do 17 | it "initializes with empty properties" do 18 | expect(filter.server_object_id).to be_nil 19 | expect(filter.server_object_mask).to be_nil 20 | end 21 | 22 | it "rejects nil object masks" do 23 | expect { filter.object_mask(nil) }.to raise_error(ArgumentError) 24 | end 25 | 26 | it "stores its value in server_object_id when called " do 27 | result = filter.object_with_id(12345) 28 | expect(result.server_object_id).to eq 12345 29 | expect(result.parameters).to eq({:server_object_id => 12345}) 30 | end 31 | 32 | it "allows call chaining with object_mask " do 33 | result = filter.object_with_id(12345).object_mask("mask.fish", "mask.cow", "mask.duck") 34 | expect(result.server_object_id).to eq 12345 35 | expect(result.server_object_mask.to_s).to eq "mask[fish,cow,duck]" 36 | end 37 | end 38 | 39 | describe "#object_mask" do 40 | it "rejects nil object masks" do 41 | expect { filter.object_mask(nil) }.to raise_error(ArgumentError) 42 | end 43 | 44 | it "rejects calls that pass things other than strings" do 45 | expect { filter.object_mask(['anArray']) }.to raise_error(ArgumentError) 46 | expect { filter.object_mask({"a" => "hash"}) }.to raise_error(ArgumentError) 47 | expect { filter.object_mask(Object.new) }.to raise_error(ArgumentError) 48 | end 49 | 50 | it "accepts strings representing a property set" do 51 | masked_filter = nil 52 | 53 | expect { masked_filter = filter.object_mask("[mask.firstProperty, mask.secondProperty]") }.to_not raise_error 54 | expect(masked_filter.server_object_mask).to eq "mask[firstProperty,secondProperty]" 55 | end 56 | 57 | it "stores its value in server_object_mask when called" do 58 | result = filter.object_mask("mask.fish", "mask[cow]", "mask(typed).duck", "mask(typed)[chicken]") 59 | expect(result.server_object_mask).to eq '[mask[fish,cow],mask(typed)[duck,chicken]]' 60 | end 61 | 62 | it "allows call chaining with object_with_id" do 63 | result = filter.object_mask("mask.fish", "mask[cow]", "mask(typed).duck", "mask(typed)[chicken]").object_with_id(12345) 64 | expect(result.server_object_id).to eq 12345 65 | expect(result.server_object_mask).to eq "[mask[fish,cow],mask(typed)[duck,chicken]]" 66 | end 67 | 68 | it "allows call chaining with other object masks" do 69 | result = filter.object_mask("mask.fish").object_mask("mask[cow]").object_mask("mask(typed).duck").object_mask("mask(typed)[chicken]") 70 | expect(result.server_object_mask).to eq "[mask[fish,cow],mask(typed)[duck,chicken]]" 71 | end 72 | end 73 | 74 | describe "#object_filter" do 75 | it "rejects nil filters" do 76 | expect { filter.object_filter(nil) }.to raise_error(ArgumentError) 77 | end 78 | 79 | it "stores its value in server_object_filter when called" do 80 | test_filter = SoftLayer::ObjectFilter.new() 81 | test_filter.set_criteria_for_key_path("fish", "cow") 82 | 83 | result = filter.object_filter(test_filter) 84 | expect(result.server_object_filter).to eq({"fish" => "cow"}) 85 | end 86 | end 87 | 88 | describe "#method_missing" do 89 | it "invokes call_softlayer_api_with_params(method_name, self, args, &block) on its target with itself and the method_missing parameters" do 90 | target = double("method_missing_target") 91 | 92 | filter = SoftLayer::APIParameterFilter.new(target).object_mask("mask.fish", "mask[cow]", "mask(typed).duck", "mask(typed)[chicken]").object_with_id(12345) 93 | expect(target).to receive(:call_softlayer_api_with_params).with(:getObject, filter, ['marshmallow']) 94 | 95 | filter.getObject("marshmallow") 96 | end 97 | end 98 | end 99 | -------------------------------------------------------------------------------- /spec/Account_spec.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # For licensing information see the LICENSE.md file in the project root. 5 | #++ 6 | 7 | $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) 8 | 9 | require 'rubygems' 10 | require 'softlayer_api' 11 | require 'rspec' 12 | 13 | require 'spec_helper' 14 | 15 | describe SoftLayer::Account do 16 | it "should exist" do 17 | expect(SoftLayer::Account).to_not be_nil 18 | end 19 | 20 | it "knows its id" do 21 | mock_client = SoftLayer::Client.new(:username => "fake_user", :api_key => "BADKEY") 22 | test_account = SoftLayer::Account.new(mock_client, "id" => 232279, "firstName" => "kangaroo") 23 | expect(test_account.id).to eq(232279) 24 | end 25 | 26 | it "identifies itself with the Account service" do 27 | mock_client = SoftLayer::Client.new(:username => "fake_user", :api_key => "BADKEY") 28 | allow(mock_client).to receive(:[]) do |service_name| 29 | expect(service_name).to eq :Account 30 | mock_service = SoftLayer::Service.new("SoftLayer_Account", :client => mock_client) 31 | 32 | # mock out call_softlayer_api_with_params so the service doesn't actually try to 33 | # communicate with the api endpoint 34 | allow(mock_service).to receive(:call_softlayer_api_with_params) 35 | 36 | mock_service 37 | end 38 | 39 | fake_account = SoftLayer::Account.new(mock_client, "id" => 12345) 40 | expect(fake_account.service.server_object_id).to eq(12345) 41 | expect(fake_account.service.target.service_name).to eq "SoftLayer_Account" 42 | end 43 | 44 | it "should allow the user to get the default account for a service" do 45 | test_client = double("mockClient") 46 | allow(test_client).to receive(:[]) do |service_name| 47 | expect(service_name).to eq :Account 48 | 49 | test_service = double("mockService") 50 | allow(test_service).to receive(:getObject) do 51 | { "id" => "232279", "firstName" => "kangaroo" } 52 | end 53 | 54 | test_service 55 | end 56 | 57 | test_account = SoftLayer::Account.account_for_client(test_client) 58 | expect(test_account.softlayer_client).to eq(test_client) 59 | expect(test_account.id).to eq("232279") 60 | expect(test_account.firstName).to eq("kangaroo") 61 | end 62 | 63 | describe "softlayer attributes" do 64 | let (:test_account) { 65 | mock_client = SoftLayer::Client.new(:username => "fakeuser", :api_key => "fake_api_key") 66 | SoftLayer::Account.new(mock_client, fixture_from_json("test_account")) 67 | } 68 | 69 | it "exposes a great many softlayer attributes" do 70 | expect(test_account.companyName).to eq "UpAndComing Software" 71 | expect(test_account.firstName).to eq "Don " 72 | expect(test_account.lastName).to eq "Joe" 73 | expect(test_account.address1).to eq "123 Main Street" 74 | expect(test_account.address2).to eq nil 75 | expect(test_account.city).to eq "Anytown" 76 | expect(test_account.state).to eq "TX" 77 | expect(test_account.country).to eq "US" 78 | expect(test_account.postalCode).to eq "778899" 79 | expect(test_account.officePhone).to eq "555.123.4567" 80 | end 81 | end 82 | 83 | it "fetches a list of open tickets" do 84 | mock_client = SoftLayer::Client.new(:username => "fakeuser", :api_key => "fake_api_key") 85 | account_service = mock_client[:Account] 86 | 87 | expect(account_service).to receive(:call_softlayer_api_with_params).with(:getOpenTickets, instance_of(SoftLayer::APIParameterFilter),[]) do 88 | fixture_from_json("test_tickets") 89 | end 90 | 91 | test_account = SoftLayer::Account.new(mock_client, fixture_from_json("test_account")) 92 | open_tickets = nil 93 | expect { open_tickets = test_account.open_tickets }.to_not raise_error 94 | ticket_ids = open_tickets.collect { |ticket| ticket.id } 95 | expect(ticket_ids.sort).to eq [12345, 12346, 12347, 12348, 12349].sort 96 | end 97 | 98 | describe "relationship to servers" do 99 | it "should respond to a request for servers" do 100 | mock_client = SoftLayer::Client.new(:username => "fakeuser", :api_key => "fake_api_key") 101 | account_service = mock_client[:Account] 102 | allow(account_service).to receive(:getObject).and_return(fixture_from_json("test_account")) 103 | allow(account_service).to receive(:call_softlayer_api_with_params) do |api_method, api_filter, arguments| 104 | case api_method 105 | when :getHardware 106 | fixture_bare_metal_data = fixture_from_json("test_bare_metal") 107 | when :getVirtualGuests 108 | fixture_from_json("test_virtual_servers") 109 | when :getObject 110 | fixture_from_json("test_account") 111 | end 112 | end 113 | 114 | test_account = SoftLayer::Account.account_for_client(mock_client) 115 | 116 | expect(test_account).to respond_to(:servers) 117 | expect(test_account).to_not respond_to(:servers=) 118 | 119 | servers = test_account.servers 120 | expect(servers.length).to eq(6) 121 | end 122 | end 123 | 124 | describe "Account.account_for_client" do 125 | it "raises an error if there is no client available" do 126 | SoftLayer::Client.default_client = nil 127 | expect {SoftLayer::Account.account_for_client}.to raise_error(RuntimeError) 128 | end 129 | 130 | it "uses the default client if one is available" do 131 | mock_client = SoftLayer::Client.new(:username => "fakeuser", :api_key => "fake_api_key") 132 | allow(mock_client).to receive(:[]) do |service_name| 133 | mock_service = Object.new() 134 | allow(mock_service).to receive(:getObject).and_return({"id" => 12345}) 135 | mock_service 136 | end 137 | 138 | SoftLayer::Client.default_client = mock_client 139 | mock_account = SoftLayer::Account.account_for_client 140 | expect(mock_account).to be_instance_of(SoftLayer::Account) 141 | expect(mock_account.id).to be(12345) 142 | end 143 | end 144 | end 145 | -------------------------------------------------------------------------------- /spec/BareMetalServer_spec.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # For licensing information see the LICENSE.md file in the project root. 5 | #++ 6 | 7 | 8 | 9 | $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) 10 | 11 | require 'rubygems' 12 | require 'softlayer_api' 13 | require 'rspec' 14 | 15 | require 'shared_server' 16 | 17 | describe SoftLayer::BareMetalServer do 18 | let (:sample_server) do 19 | mock_client = SoftLayer::Client.new(:username => "fakeuser", :api_key => "DEADBEEFBADF00D") 20 | allow(mock_client).to receive(:[]) do |service_name| 21 | service = mock_client.service_named(service_name) 22 | allow(service).to receive(:call_softlayer_api_with_params) 23 | service 24 | end 25 | 26 | SoftLayer::BareMetalServer.new(mock_client, { "id" => 12345 }) 27 | end 28 | 29 | it "identifies itself with the SoftLayer_Hardware_Server service" do 30 | service = sample_server.service 31 | expect(service.server_object_id).to eq(12345) 32 | expect(service.target.service_name).to eq "SoftLayer_Hardware_Server" 33 | end 34 | 35 | it_behaves_like "server with port speed" do 36 | let (:server) { sample_server } 37 | end 38 | 39 | it_behaves_like "server with mutable hostname" do 40 | let (:server) { sample_server } 41 | end 42 | 43 | it "can be cancelled" do 44 | mock_client = SoftLayer::Client.new(:username => "fakeuser", :api_key => "DEADBEEFBADF00D") 45 | allow(mock_client).to receive(:[]) do |service_name| 46 | expect(service_name).to eq :Ticket 47 | 48 | service = mock_client.service_named(service_name) 49 | expect(service).to receive(:createCancelServerTicket).with(12345, 'Migrating to larger server', 'moving on up!', true, 'HARDWARE') 50 | allow(service).to receive(:call_softlayer_api_with_params) 51 | service 52 | end 53 | 54 | fake_server = SoftLayer::BareMetalServer.new(mock_client, { "id" => 12345 }) 55 | fake_server.cancel!(:migrate_larger, 'moving on up!' ) 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /spec/Client_spec.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # For licensing information see the LICENSE.md file in the project root. 5 | #++ 6 | 7 | $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), '../lib')) 8 | 9 | require 'rubygems' 10 | require 'softlayer_api' 11 | require 'rspec' 12 | 13 | describe SoftLayer::Client do 14 | before do 15 | $SL_API_USERNAME = nil 16 | $SL_API_KEY = nil 17 | $SL_API_BASE_URL = nil 18 | end 19 | 20 | it 'accepts a user name from the global variable' do 21 | $SL_API_USERNAME = 'sample' 22 | client = SoftLayer::Client.new(:api_key => 'fake_key', :endpoint_url => 'http://fakeurl.org/') 23 | expect(client.username).to eq 'sample' 24 | end 25 | 26 | it 'accepts a username in options' do 27 | $SL_API_USERNAME = 'sample' 28 | client = SoftLayer::Client.new(:username => 'fake_user', :api_key => 'fake_key', :endpoint_url => 'http://fakeurl.org/') 29 | expect(client.username).to eq 'fake_user' 30 | end 31 | 32 | it 'accepts an api key from the global variable' do 33 | $SL_API_KEY = 'sample' 34 | client = SoftLayer::Client.new(:username => 'fake_user', :endpoint_url => 'http://fakeurl.org/') 35 | expect(client.api_key).to eq 'sample' 36 | end 37 | 38 | it 'accepts an api key in options' do 39 | $SL_API_KEY = 'sample' 40 | client = SoftLayer::Client.new(:username => 'fake_user', :api_key => 'fake_key', :endpoint_url => 'http://fakeurl.org/') 41 | expect(client.api_key).to eq 'fake_key' 42 | end 43 | 44 | it 'produces empty auth headers if the username is empty' do 45 | 46 | $SL_API_USERNAME = '' 47 | client = SoftLayer::Client.new(:api_key => 'fake_key', :endpoint_url => 'http://fakeurl.org/') 48 | 49 | expect(client.authentication_headers.empty?).to be true 50 | 51 | $SL_API_USERNAME = 'good_username' 52 | $SL_API_KEY = 'sample' 53 | client = SoftLayer::Client.new(:username => '', :api_key => 'fake_key', :endpoint_url => 'http://fakeurl.org/') 54 | 55 | expect(client.authentication_headers.empty?).to be true 56 | end 57 | 58 | it 'produces empty auth headers if the username is nil' do 59 | $SL_API_USERNAME = nil 60 | client = SoftLayer::Client.new(:username => nil, :api_key => 'fake_key', :endpoint_url => 'http://fakeurl.org/') 61 | 62 | expect(client.authentication_headers.empty?).to be true 63 | end 64 | 65 | it 'produces empty auth headers if the api_key is empty' do 66 | $SL_API_KEY = '' 67 | client = SoftLayer::Client.new(:username => 'fake_user', :endpoint_url => 'http://fakeurl.org/') 68 | 69 | expect(client.authentication_headers.empty?).to be true 70 | 71 | client = SoftLayer::Client.new(:username => 'fake_user', :api_key => '', :endpoint_url => 'http://fakeurl.org/') 72 | 73 | expect(client.authentication_headers.empty?).to be true 74 | end 75 | 76 | it 'produces empty auth headers if the api_key is nil' do 77 | $SL_API_KEY = nil 78 | client = SoftLayer::Client.new(:username => 'fake_user', :endpoint_url => 'http://fakeurl.org/', :api_key => nil) 79 | 80 | expect(client.authentication_headers.empty?).to be true 81 | end 82 | 83 | it 'initializes by default with nil as the timeout' do 84 | client = SoftLayer::Client.new(:username => 'fake_user', :api_key => 'fake_key', :endpoint_url => 'http://fakeurl.org/') 85 | expect(client.network_timeout).to be_nil 86 | end 87 | 88 | it 'Accepts a timeout given as a config parameter' do 89 | client = SoftLayer::Client.new(:username => 'fake_user', :api_key => 'fake_key', :endpoint_url => 'http://fakeurl.org/', :timeout => 60) 90 | expect(client.network_timeout).to eq 60 91 | end 92 | 93 | it 'gets the default endpoint even if none is provided' do 94 | $SL_API_BASE_URL = nil 95 | client = SoftLayer::Client.new(:username => 'fake_user', :api_key => 'fake_key') 96 | expect(client.endpoint_url).to eq SoftLayer::API_PUBLIC_ENDPOINT 97 | end 98 | 99 | it 'allows the default endpoint to be overridden by globals' do 100 | $SL_API_BASE_URL = 'http://someendpoint.softlayer.com/from/globals' 101 | client = SoftLayer::Client.new(:username => 'fake_user', :api_key => 'fake_key') 102 | expect(client.endpoint_url).to eq 'http://someendpoint.softlayer.com/from/globals' 103 | end 104 | 105 | it 'allows the default endpoint to be overriden by options' do 106 | $SL_API_BASE_URL = 'http://this/wont/be/used' 107 | client = SoftLayer::Client.new(:username => 'fake_user', :api_key => 'fake_key', :endpoint_url => 'http://fakeurl.org/') 108 | expect(client.endpoint_url).to eq 'http://fakeurl.org/' 109 | end 110 | 111 | it 'has a read/write user_agent property' do 112 | client = SoftLayer::Client.new(:username => 'fake_user', :api_key => 'fake_key', :endpoint_url => 'http://fakeurl.org/') 113 | expect(client).to respond_to(:user_agent) 114 | expect(client).to respond_to(:user_agent=) 115 | end 116 | 117 | it 'has a reasonable default user agent string' do 118 | client = SoftLayer::Client.new(:username => 'fake_user', :api_key => 'fake_key', :endpoint_url => 'http://fakeurl.org/') 119 | expect(client.user_agent).to eq "softlayer_api gem/#{SoftLayer::VERSION} (Ruby #{RUBY_PLATFORM}/#{RUBY_VERSION})" 120 | end 121 | 122 | it 'should allow the user agent to change' do 123 | client = SoftLayer::Client.new(:username => 'fake_user', :api_key => 'fake_key', :endpoint_url => 'http://fakeurl.org/') 124 | client.user_agent = "Some Random User Agent" 125 | expect(client.user_agent).to eq "Some Random User Agent" 126 | end 127 | 128 | describe "obtaining services" do 129 | let(:test_client) { 130 | SoftLayer::Client.new(:username => 'fake_user', :api_key => 'fake_key', :endpoint_url => 'http://fakeurl.org/') 131 | } 132 | 133 | it "should have a service_named method" do 134 | expect(test_client).to respond_to(:service_named) 135 | end 136 | 137 | it "should reject empty or nil service names" do 138 | expect { test_client.service_named('') }.to raise_error(ArgumentError) 139 | expect { test_client.service_named(nil) }.to raise_error(ArgumentError) 140 | end 141 | 142 | it "should be able to construct a service" do 143 | test_service = test_client.service_named('Account') 144 | expect(test_service).to_not be_nil 145 | expect(test_service.service_name).to eq "SoftLayer_Account" 146 | expect(test_service.client).to be(test_client) 147 | end 148 | 149 | it "allows bracket dereferences as an alternate service syntax" do 150 | test_service = test_client[:Account] 151 | expect(test_service).to_not be_nil 152 | expect(test_service.service_name).to eq "SoftLayer_Account" 153 | expect(test_service.client).to be(test_client) 154 | end 155 | 156 | it "returns the same service repeatedly when asked more than once" do 157 | first_account_service = test_client[:Account] 158 | second_account_service = test_client.service_named('Account') 159 | 160 | expect(first_account_service).to be(second_account_service) 161 | end 162 | 163 | it "recognizes a symbol as an acceptable service name" do 164 | account_service = test_client[:Account] 165 | expect(account_service).to_not be_nil 166 | 167 | trying_again = test_client[:Account] 168 | expect(trying_again).to be(account_service) 169 | 170 | yet_again = test_client[:SoftLayer_Account] 171 | expect(yet_again).to be(account_service) 172 | 173 | once_more = test_client[:SoftLayer_Account] 174 | expect(once_more).to be(account_service) 175 | end 176 | 177 | end 178 | end 179 | -------------------------------------------------------------------------------- /spec/Config_spec.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # For licensing information see the LICENSE.md file in the project root. 5 | #++ 6 | 7 | $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) 8 | 9 | require 'rubygems' 10 | require 'softlayer_api' 11 | require 'rspec' 12 | 13 | require 'tempfile' 14 | 15 | describe SoftLayer::Config do 16 | 17 | before :each do 18 | ENV.delete("SL_USERNAME") 19 | ENV.delete("SL_API_KEY") 20 | ENV.delete("SL_API_USER_AGENT") 21 | ENV.delete("SL_PROFILE") 22 | end 23 | 24 | it "retrieves config information from environment variables" do 25 | ENV.store("SL_USERNAME", "PoohBear") 26 | ENV.store("SL_API_KEY", "DEADBEEFBADF00D") 27 | ENV.store("SL_API_USER_AGENT", "de trackerz") 28 | 29 | expect(SoftLayer::Config.environment_settings).to eq({ :user_agent => "de trackerz", :username => "PoohBear", :api_key => "DEADBEEFBADF00D" }) 30 | end 31 | 32 | it "retrieves the properties from a custom file" do 33 | file = Tempfile.new('properties_from_file') 34 | begin 35 | file.puts("[softlayer]") 36 | file.puts("username = PoohBear") 37 | file.puts("api_key = DEADBEEFBADF00D") 38 | file.puts("timeout = 40") 39 | file.close 40 | 41 | settings = SoftLayer::Config.file_settings(file.path) 42 | ensure 43 | file.close 44 | file.unlink 45 | end 46 | 47 | expect(settings[:username]).to eq("PoohBear") 48 | expect(settings[:api_key]).to eq("DEADBEEFBADF00D") 49 | expect(settings[:timeout]).to eq(40) 50 | end 51 | 52 | it "retrieves the properties from a custom file using a custom profile" do 53 | ENV.store("SL_PROFILE", "softlayer_qa") 54 | file = Tempfile.new('properties_from_file') 55 | begin 56 | file.puts("[softlayer_dev]") 57 | file.puts("username = PoohBear") 58 | file.puts("api_key = DEADBEEFBADF00D") 59 | file.puts("timeout = 40") 60 | file.puts("\n") 61 | file.puts("[softlayer_qa]") 62 | file.puts("username = Piglet") 63 | file.puts("api_key = MOOOOOOOO") 64 | file.puts("timeout = 60") 65 | file.puts("\n") 66 | file.puts("[softlayer_prod]") 67 | file.puts("username = Eeyore") 68 | file.puts("api_key = VEG_ALL_THE_WAY") 69 | file.puts("timeout = 80") 70 | file.close 71 | 72 | settings = SoftLayer::Config.file_settings(file.path) 73 | ensure 74 | file.close 75 | file.unlink 76 | end 77 | 78 | expect(settings[:username]).to eq("Piglet") 79 | expect(settings[:api_key]).to eq("MOOOOOOOO") 80 | expect(settings[:timeout]).to eq(60) 81 | end 82 | 83 | it "retrieves the timeout field as an integer when presented as a string" do 84 | file = Tempfile.new('config_test') 85 | begin 86 | file.puts("[softlayer]") 87 | file.puts("username = PoohBear") 88 | file.puts("api_key = DEADBEEFBADF00D") 89 | file.puts("timeout = 40") 90 | file.close 91 | 92 | settings = SoftLayer::Config.file_settings(file.path) 93 | ensure 94 | file.close 95 | file.unlink 96 | end 97 | 98 | expect(settings[:timeout]).to eq(40) 99 | end 100 | end 101 | -------------------------------------------------------------------------------- /spec/Datacenter_spec.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # For licensing information see the LICENSE.md file in the project root. 5 | #++ 6 | 7 | $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), '../lib')) 8 | 9 | require 'rubygems' 10 | require 'softlayer_api' 11 | require 'rspec' 12 | 13 | describe SoftLayer::Datacenter do 14 | let (:mock_client) do 15 | mock_client = SoftLayer::Client.new(:username => "fakeuser", :api_key => "DEADBEEFBADF00D") 16 | allow(mock_client[:Location]).to receive(:call_softlayer_api_with_params) do |method_name, parameters, args| 17 | fixture_from_json('datacenter_locations.json') 18 | end 19 | 20 | mock_client 21 | end 22 | 23 | it "retrieves a list of datacenters" do 24 | datacenters = SoftLayer::Datacenter.datacenters(mock_client) 25 | names = datacenters.collect { |datacenter| datacenter.name } 26 | expect(names.sort).to eq ["ams01", "dal01", "dal02", "dal04", "dal05", "dal06", "dal07", "hkg02", "hou02", "lon02", "sea01", "sjc01", "sng01", "tor01", "wdc01", "wdc03"] 27 | end 28 | 29 | it "retrieves a particular datacenter by name" do 30 | dal05 = SoftLayer::Datacenter.datacenter_named("dal05", mock_client) 31 | expect(dal05.name).to eq "dal05" 32 | expect(dal05.id).to be 138124 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /spec/DynamicAttribute_spec.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # For licensing information see the LICENSE.md file in the project root. 5 | #++ 6 | 7 | $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) 8 | 9 | require 'rubygems' 10 | require 'softlayer_api' 11 | require 'rspec' 12 | 13 | # class TestClass 14 | # include SoftLayer::DynamicAttribute 15 | # 16 | # sl_dynamic_attr(:test_attribute) do |attribute| 17 | # attribute.refresh_every(5 * 60) #refresh every 5 minutes 18 | # attribute.update do 19 | # "test_attribute value!" 20 | # end 21 | # end 22 | # end 23 | 24 | describe SoftLayer::DynamicAttribute do 25 | context "when included in a class" do 26 | before do 27 | class TestClass 28 | include SoftLayer::DynamicAttribute 29 | end 30 | end 31 | 32 | it "defines ::sl_dynamic_attr in the class" do 33 | expect(TestClass).to respond_to(:sl_dynamic_attr) 34 | end 35 | 36 | it "defines ::sl_dynamic_attr_definition in the class" do 37 | expect(TestClass).to respond_to(:sl_dynamic_attr_definition) 38 | end 39 | end 40 | 41 | describe "::sl_dynamic_attr" do 42 | before do 43 | class TestClass 44 | include SoftLayer::DynamicAttribute 45 | sl_dynamic_attr(:test_attribute) { |test_attribute| } 46 | end 47 | end 48 | 49 | it "adds a attribute definition" do 50 | expect(TestClass.sl_dynamic_attr_definition(:test_attribute)).to_not be_nil 51 | end 52 | 53 | it "adds a attribute getter to instances" do 54 | sample_instance = TestClass.new() 55 | expect(sample_instance).to respond_to(:test_attribute) 56 | 57 | expect(sample_instance.test_attribute).to be_nil 58 | end 59 | 60 | it "adds a predicate to check for updates" do 61 | sample_instance = TestClass.new() 62 | expect(sample_instance).to respond_to(:should_update_test_attribute?) 63 | end 64 | 65 | it "adds a method to perform updates" do 66 | sample_instance = TestClass.new() 67 | expect(sample_instance).to respond_to(:update_test_attribute!) 68 | end 69 | end 70 | 71 | describe "a simple attribute definition" do 72 | before do 73 | class TestClass 74 | include SoftLayer::DynamicAttribute 75 | sl_dynamic_attr(:test_attribute) do |rsrc| 76 | rsrc.to_update do 77 | method_in_test_class_instance_context 78 | "Value update accomplished!" 79 | end 80 | end 81 | def method_in_test_class_instance_context 82 | nil 83 | end 84 | end # TestClass 85 | end 86 | 87 | it "should obtain an updated value when called" do 88 | sample_instance = TestClass.new 89 | 90 | expect(sample_instance.instance_variable_defined?(:@test_attribute)).to be(false) 91 | 92 | expect(sample_instance).to receive(:method_in_test_class_instance_context) 93 | expect(sample_instance.test_attribute).to eq "Value update accomplished!" 94 | expect(sample_instance.instance_variable_defined?(:@test_attribute)).to be(true) 95 | expect(sample_instance.instance_variable_get(:@test_attribute)).to eq "Value update accomplished!" 96 | end 97 | end 98 | 99 | describe "a attribute with delayed update" do 100 | before do 101 | class TestClass 102 | include SoftLayer::DynamicAttribute 103 | sl_dynamic_attr(:test_attribute) do |rsrc| 104 | rsrc.should_update? do 105 | @last_test_attribute_update ||= Time.at(0) 106 | (Time.now - @last_test_attribute_update) > 0.5 # update once a second 107 | end 108 | 109 | rsrc.to_update do 110 | @last_test_attribute_update = Time.now 111 | Time.now 112 | end 113 | end 114 | end # TestClass 115 | end 116 | 117 | it "should obtain an updated value when called" do 118 | sample_instance = TestClass.new 119 | last_update = sample_instance.test_attribute 120 | next_update = sample_instance.test_attribute 121 | expect(next_update).to eq last_update 122 | sleep(0.75) 123 | final_update = sample_instance.test_attribute 124 | expect(final_update).to_not eq last_update 125 | end 126 | end 127 | 128 | describe SoftLayer::DynamicAttribute::DynamicAttributeDefinition do 129 | let(:test_definition) do 130 | SoftLayer::DynamicAttribute::DynamicAttributeDefinition.new(:test_attribute) 131 | end 132 | 133 | it "raises an exception if passed an invalid name" do 134 | expect { SoftLayer::DynamicAttribute::DynamicAttributeDefinition.new(nil) }.to raise_error(ArgumentError) 135 | expect { SoftLayer::DynamicAttribute::DynamicAttributeDefinition.new("") }.to raise_error(ArgumentError) 136 | end 137 | 138 | it "has valid initial values" do 139 | expect(test_definition.attribute_name).to be(:test_attribute) 140 | expect(test_definition.update_block).to_not be_nil 141 | end 142 | 143 | it "allows DSL syntax" do 144 | test_definition.should_update? { "Yea!" } 145 | test_definition.to_update do 146 | "test_attribute value!" 147 | end 148 | 149 | expect(test_definition.update_block.call).to eq "test_attribute value!" 150 | expect(test_definition.should_update_block.call).to eq "Yea!" 151 | end 152 | end 153 | end 154 | -------------------------------------------------------------------------------- /spec/ModelBase_spec.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # For licensing information see the LICENSE.md file in the project root. 5 | #++ 6 | 7 | $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) 8 | 9 | require 'rubygems' 10 | require 'softlayer_api' 11 | require 'rspec' 12 | 13 | describe SoftLayer::ModelBase do 14 | describe "#initialize" do 15 | it "rejects hashes without an id" do 16 | mock_client = double("Mock SoftLayer Client") 17 | expect { SoftLayer::ModelBase.new(mock_client, {}) }.to raise_error(ArgumentError) 18 | expect { SoftLayer::ModelBase.new(mock_client, {"id" => "someID"}) }.not_to raise_error 19 | end 20 | 21 | it "rejects models created with no client" do 22 | expect { SoftLayer::ModelBase.new(nil, nil) }.to raise_error(ArgumentError) 23 | end 24 | 25 | it "rejects nil hashes" do 26 | mock_client = double("Mock SoftLayer Client") 27 | expect { SoftLayer::ModelBase.new(mock_client, nil) }.to raise_error(ArgumentError) 28 | end 29 | 30 | it "remembers its first argument as the client" do 31 | mock_client = double("Mock SoftLayer Client") 32 | test_model = SoftLayer::ModelBase.new(mock_client, { "id" => "12345"}); 33 | expect(test_model.softlayer_client).to be(mock_client) 34 | end 35 | end 36 | 37 | it "allows access to raw softlayer properties" do 38 | mock_client = double("Mock SoftLayer Client") 39 | test_model = SoftLayer::ModelBase.new(mock_client, { "id" => "12345"}); 40 | expect(test_model[:id]).to eq("12345") 41 | expect(test_model['id']).to eq("12345") 42 | end 43 | 44 | it "allows access to exposed softlayer properties" do 45 | mock_client = double("Mock SoftLayer Client") 46 | test_model = SoftLayer::ModelBase.new(mock_client, { "id" => "12345"}); 47 | expect(test_model.id).to eq("12345") 48 | end 49 | 50 | it "returns nil from to_ary" do 51 | mock_client = double("Mock SoftLayer Client") 52 | test_model = SoftLayer::ModelBase.new(mock_client, { "id" => "12345" }) 53 | expect(test_model).to respond_to(:to_ary) 54 | expect(test_model.to_ary).to be_nil 55 | end 56 | 57 | it "realizes when low-level hash keys are added" do 58 | mock_client = double("Mock SoftLayer Client") 59 | test_model = SoftLayer::ModelBase.new(mock_client, { "id" => "12345" }) 60 | allow(test_model).to receive(:softlayer_properties) { { "id" => "12345", "newInfo" => "fun" } } 61 | expect(test_model.has_sl_property? :newInfo).to be(false) 62 | test_model.refresh_details() 63 | expect(test_model.has_sl_property? :newInfo).to be(true) 64 | end 65 | 66 | it "realizes when low-level hash keys are removed" do 67 | mock_client = double("Mock SoftLayer Client") 68 | test_model = SoftLayer::ModelBase.new(mock_client, { "id" => "12345", "newInfo" => "fun" }) 69 | allow(test_model).to receive(:softlayer_properties) { { "id" => "12345" } } 70 | expect(test_model.has_sl_property? :newInfo).to be(true) 71 | test_model.refresh_details() 72 | expect(test_model.has_sl_property? :newInfo).to be(false) 73 | end 74 | end -------------------------------------------------------------------------------- /spec/ObjectMaskProperty_spec.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # For licensing information see the LICENSE.md file in the project root. 5 | #++ 6 | 7 | $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) 8 | 9 | require 'rubygems' 10 | require 'rspec' 11 | require 'softlayer/ObjectMaskProperty' 12 | 13 | describe SoftLayer::ObjectMaskProperty do 14 | it "obtains a name when created" do 15 | property = SoftLayer::ObjectMaskProperty.new("propertyName") 16 | expect(property.name).to eq "propertyName" 17 | expect(property.type).to be_nil 18 | expect(property.children).to eq [] 19 | end 20 | 21 | it "may obtain a type when created" do 22 | property = SoftLayer::ObjectMaskProperty.new("propertyName", "SomeType") 23 | expect(property.name).to eq "propertyName" 24 | expect(property.type).to eq "SomeType" 25 | expect(property.children).to eq [] 26 | end 27 | 28 | it "knows it can merge with properties that have the same name" do 29 | property1 = SoftLayer::ObjectMaskProperty.new("propertyName", nil) 30 | property2 = SoftLayer::ObjectMaskProperty.new("propertyName", nil) 31 | 32 | expect(property1.can_merge_with?(property2)).to be(true) 33 | end 34 | 35 | it "knows it can merge with properties that have the same name and type" do 36 | property1 = SoftLayer::ObjectMaskProperty.new("propertyName", "SomeType") 37 | property2 = SoftLayer::ObjectMaskProperty.new("propertyName", "SomeType") 38 | 39 | expect(property1.can_merge_with?(property2)).to be(true) 40 | end 41 | 42 | it "knows it cannot merge if the names don't match" do 43 | property1 = SoftLayer::ObjectMaskProperty.new("propertyName", nil) 44 | property2 = SoftLayer::ObjectMaskProperty.new("someOtherName", nil) 45 | 46 | expect(property1.can_merge_with?(property2)).to be(false) 47 | end 48 | 49 | it "knows it cannot merge if the types don't match" do 50 | property1 = SoftLayer::ObjectMaskProperty.new("propertyName", "SomeType") 51 | property2 = SoftLayer::ObjectMaskProperty.new("propertyName", "AnotherType") 52 | 53 | expect(property1.can_merge_with?(property2)).to be(false) 54 | end 55 | 56 | it "collects children" do 57 | property1 = SoftLayer::ObjectMaskProperty.new("propertyName", "SomeType") 58 | property2 = SoftLayer::ObjectMaskProperty.new("propertyName", "AnotherType") 59 | 60 | property1.add_children([property2]) 61 | expect(property1.children.count).to eq 1 62 | expect(property1.children[0]).to eq property2 63 | end 64 | 65 | it "collects children" do 66 | property1 = SoftLayer::ObjectMaskProperty.new("propertyName", "SomeType") 67 | property2 = SoftLayer::ObjectMaskProperty.new("propertyName", "AnotherType") 68 | 69 | property1.add_children([property2]) 70 | property1.add_children([property2]) 71 | 72 | expect(property1.children.count).to eq 1 73 | expect(property1.children[0]).to eq property2 74 | end 75 | 76 | it "merges children" do 77 | property1 = SoftLayer::ObjectMaskProperty.new("parent") 78 | first_child = SoftLayer::ObjectMaskProperty.new("child") 79 | first_subchild = SoftLayer::ObjectMaskProperty.new("first_subchild") 80 | 81 | first_child.add_child(first_subchild) 82 | property1.add_child(first_child) 83 | 84 | property2 = SoftLayer::ObjectMaskProperty.new("parent") 85 | second_child = SoftLayer::ObjectMaskProperty.new("child") 86 | second_subchild = SoftLayer::ObjectMaskProperty.new("second_subchild") 87 | 88 | second_child.add_child(second_subchild) 89 | property2.add_child(second_child) 90 | 91 | expect(property1.can_merge_with?(property2)).to be(true) 92 | property1.add_children(property2.children) 93 | 94 | expect(property1.children.count).to eq 1 95 | child = property1.children[0] 96 | expect(child.name).to eq "child" 97 | expect(child.children.count).to eq 2 98 | expect(child.children).to include(first_subchild) 99 | expect(child.children).to include(second_subchild) 100 | end 101 | end 102 | -------------------------------------------------------------------------------- /spec/ProductPackage_spec.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # For licensing information see the LICENSE.md file in the project root. 5 | #++ 6 | 7 | $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) 8 | 9 | require 'rubygems' 10 | require 'softlayer_api' 11 | require 'rspec' 12 | 13 | describe SoftLayer::ProductPackage do 14 | it "requests packages by key name" do 15 | client = SoftLayer::Client.new(:username => "fake_user", :api_key => "BADKEY") 16 | product_package_service = client[:Product_Package] 17 | 18 | expect(product_package_service).to receive(:call_softlayer_api_with_params) do |method_name, parameters, args| 19 | expect(method_name).to be(:getAllObjects) 20 | expect(parameters.server_object_filter).to_not be_nil 21 | expect(args).to be_empty 22 | 23 | [] 24 | end 25 | 26 | SoftLayer::ProductPackage.packages_with_key_name('FAKE_KEY_NAME', client) 27 | end 28 | 29 | it "identifies itself with the Product_Package service" do 30 | mock_client = SoftLayer::Client.new(:username => "fake_user", :api_key => "BADKEY") 31 | allow(mock_client).to receive(:[]) do |service_name| 32 | expect(service_name).to eq :Product_Package 33 | mock_service = SoftLayer::Service.new("SoftLayer_Product_Package", :client => mock_client) 34 | 35 | # mock out call_softlayer_api_with_params so the service doesn't actually try to 36 | # communicate with the api endpoint 37 | allow(mock_service).to receive(:call_softlayer_api_with_params) 38 | 39 | mock_service 40 | end 41 | 42 | fake_package = SoftLayer::ProductPackage.new(mock_client, {"id" => 12345}) 43 | expect(fake_package.service.server_object_id).to eq(12345) 44 | expect(fake_package.service.target.service_name).to eq "SoftLayer_Product_Package" 45 | end 46 | 47 | describe "class methods for getting to packages" do 48 | let(:mock_client) do 49 | client = SoftLayer::Client.new(:username => "fake_user", :api_key => "BADKEY") 50 | product_package_service = client[:Product_Package] 51 | 52 | allow(product_package_service).to receive(:call_softlayer_api_with_params).with(:getAllObjects, instance_of(SoftLayer::APIParameterFilter), []).and_return([fixture_from_json("Product_Package")]) 53 | client 54 | end 55 | 56 | it "calling with a client should work fine" do 57 | expect { SoftLayer::ProductPackage.packages_with_key_name('BARE_METAL_CORE', mock_client) }.to_not raise_error 58 | expect { SoftLayer::ProductPackage.virtual_server_package(mock_client) }.to_not raise_error 59 | expect { SoftLayer::ProductPackage.bare_metal_instance_package(mock_client) }.to_not raise_error 60 | expect { SoftLayer::ProductPackage.bare_metal_server_packages(mock_client) }.to_not raise_error 61 | end 62 | 63 | it "calling with default client should work" do 64 | SoftLayer::Client.default_client = mock_client 65 | expect { SoftLayer::ProductPackage.packages_with_key_name('BARE_METAL_CORE') }.to_not raise_error 66 | expect { SoftLayer::ProductPackage.virtual_server_package() }.to_not raise_error 67 | expect { SoftLayer::ProductPackage.bare_metal_instance_package() }.to_not raise_error 68 | expect { SoftLayer::ProductPackage.bare_metal_server_packages() }.to_not raise_error 69 | SoftLayer::Client.default_client = nil 70 | end 71 | 72 | it "calling with no client should raise" do 73 | SoftLayer::Client.default_client = nil 74 | expect { SoftLayer::ProductPackage.packages_with_key_name('BARE_METAL_CORE') }.to raise_error(RuntimeError) 75 | expect { SoftLayer::ProductPackage.virtual_server_package() }.to raise_error(RuntimeError) 76 | expect { SoftLayer::ProductPackage.bare_metal_instance_package() }.to raise_error(RuntimeError) 77 | expect { SoftLayer::ProductPackage.bare_metal_server_packages() }.to raise_error(RuntimeError) 78 | end 79 | end 80 | end 81 | -------------------------------------------------------------------------------- /spec/ServerFirewall_spec.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # For licensing information see the LICENSE.md file in the project root. 5 | #++ 6 | 7 | $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) 8 | 9 | require 'rubygems' 10 | require 'softlayer_api' 11 | require 'rspec' 12 | 13 | describe SoftLayer::ServerFirewall do 14 | describe "firewall rules bypass" do 15 | let(:mock_client) { 16 | mock_client = SoftLayer::Client.new(:username => "fake_user", :api_key => "BADKEY") 17 | } 18 | 19 | it "responds to the method change_routing_bypass!" do 20 | mock_firewall = SoftLayer::ServerFirewall.new("not really a client", { "id" => 12345 }) 21 | expect(mock_firewall).to respond_to(:change_rules_bypass!) 22 | end 23 | 24 | it "accepts :apply_firewall_rules" do 25 | mock_firewall = SoftLayer::ServerFirewall.new(mock_client, {"id" => 12345}) 26 | allow(mock_firewall).to receive(:rules) { {} } 27 | 28 | firewall_update_service = mock_client[:Network_Firewall_Update_Request] 29 | 30 | expect(firewall_update_service).to receive(:call_softlayer_api_with_params) do |method, parameters, arguments| 31 | expect(arguments[0]['bypassFlag']).to be(false) 32 | end 33 | 34 | mock_firewall.change_rules_bypass!(:apply_firewall_rules) 35 | end 36 | 37 | it "accepts :bypass_firewall_rules!" do 38 | mock_firewall = SoftLayer::ServerFirewall.new(mock_client, {"id" => 12345}) 39 | allow(mock_firewall).to receive(:rules) { {} } 40 | 41 | firewall_update_service = mock_client[:Network_Firewall_Update_Request] 42 | expect(firewall_update_service).to receive(:call_softlayer_api_with_params) do |method, parameters, arguments| 43 | expect(arguments[0]['bypassFlag']).to be(true) 44 | end 45 | 46 | mock_firewall.change_rules_bypass!(:bypass_firewall_rules) 47 | end 48 | 49 | it "rejects other parameters (particularly true and false)" do 50 | mock_firewall = SoftLayer::ServerFirewall.new("not really a client", { "id" => 12345 }) 51 | allow(mock_firewall).to receive(:rules) { {} } 52 | 53 | firewall_update_service = mock_client[:Network_Firewall_Update_Request] 54 | 55 | allow(firewall_update_service).to receive(:call_softlayer_api_with_params) 56 | 57 | expect{ mock_firewall.change_rules_bypass!(true) }.to raise_error(ArgumentError) 58 | expect{ mock_firewall.change_rules_bypass!(false) }.to raise_error(ArgumentError) 59 | expect{ mock_firewall.change_rules_bypass!(:route_around_firewall) }.to raise_error(ArgumentError) 60 | expect{ mock_firewall.change_rules_bypass!(:route_through_firewall) }.to raise_error(ArgumentError) 61 | expect{ mock_firewall.change_rules_bypass!("apply_firewall_rules") }.to raise_error(ArgumentError) 62 | expect{ mock_firewall.change_rules_bypass!("bypass_firewall_rules") }.to raise_error(ArgumentError) 63 | expect{ mock_firewall.change_rules_bypass!(nil) }.to raise_error(ArgumentError) 64 | expect{ mock_firewall.change_rules_bypass!(1) }.to raise_error(ArgumentError) 65 | expect{ mock_firewall.change_rules_bypass!(0) }.to raise_error(ArgumentError) 66 | end 67 | end 68 | end 69 | -------------------------------------------------------------------------------- /spec/Server_spec.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # For licensing information see the LICENSE.md file in the project root. 5 | #++ 6 | 7 | $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) 8 | 9 | require 'rubygems' 10 | require 'softlayer_api' 11 | require 'rspec' 12 | 13 | describe SoftLayer::Server do 14 | it "is an abstract base class" do 15 | mock_client = SoftLayer::Client.new(:username => "fakeuser", :api_key => "DEADBEEFBADF00D") 16 | expect { SoftLayer::Server.new(mock_client, { "id" => 12345 }) }.to raise_error(RuntimeError) 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/Ticket_spec.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # For licensing information see the LICENSE.md file in the project root. 5 | #++ 6 | 7 | $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) 8 | 9 | require 'rubygems' 10 | require 'softlayer_api' 11 | require 'rspec' 12 | 13 | require 'spec_helper' 14 | 15 | describe SoftLayer::Ticket do 16 | before (:each) do 17 | SoftLayer::Ticket.instance_eval { @ticket_subjects = nil } 18 | end 19 | 20 | it "retrieves ticket subjects from API once" do 21 | fakeTicketSubjects = fixture_from_json("ticket_subjects") 22 | 23 | mock_client = SoftLayer::Client.new(:username => "fakeuser", :api_key=> 'fakekey') 24 | allow(mock_client).to receive(:[]) do |service_name| 25 | expect(service_name).to eq :Ticket_Subject 26 | 27 | mock_service = SoftLayer::Service.new("SoftLayer_Ticket_Subject", :client => mock_client) 28 | expect(mock_service).to receive(:getAllObjects).once.and_return(fakeTicketSubjects) 29 | expect(mock_service).to_not receive(:call_softlayer_api_with_params) 30 | 31 | mock_service 32 | end 33 | 34 | expect(SoftLayer::Ticket.ticket_subjects(mock_client)).to be(fakeTicketSubjects) 35 | 36 | # call for the subjects again which should NOT re-request them from the client 37 | # (so :getAllObjects on the service should not be called again) 38 | expect(SoftLayer::Ticket.ticket_subjects(mock_client)).to be(fakeTicketSubjects) 39 | end 40 | 41 | it "raises an error if you try to get ticket subjects with no client" do 42 | SoftLayer::Client.default_client = nil 43 | expect {SoftLayer::Ticket.ticket_subjects() }.to raise_error(RuntimeError) 44 | end 45 | 46 | it "identifies itself with the ticket service" do 47 | fake_ticket_data = fixture_from_json("test_tickets").first 48 | 49 | mock_client = SoftLayer::Client.new(:username => "fakeuser", :api_key=> 'fakekey') 50 | allow(mock_client).to receive(:[]) do |service_name| 51 | expect(service_name).to eq :Ticket 52 | mock_service = SoftLayer::Service.new("SoftLayer_Ticket", :client => mock_client) 53 | 54 | # mock out call_softlayer_api_with_params so the service doesn't actually try to 55 | # communicate with the api endpoint 56 | allow(mock_service).to receive(:call_softlayer_api_with_params) 57 | 58 | mock_service 59 | end 60 | 61 | fake_ticket = SoftLayer::Ticket.new(mock_client, fake_ticket_data) 62 | ticket_service = fake_ticket.service 63 | expect(ticket_service.server_object_id).to eq(12345) 64 | expect(ticket_service.target.service_name).to eq "SoftLayer_Ticket" 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /spec/VLANFirewall_spec.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # For licensing information see the LICENSE.md file in the project root. 5 | #++ 6 | 7 | $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) 8 | 9 | require 'rubygems' 10 | require 'softlayer_api' 11 | require 'rspec' 12 | 13 | describe SoftLayer::VLANFirewall do 14 | it "should have a class representing a firewall" do 15 | expect{ SoftLayer::VLANFirewall.new("not really a client", { "id" => 12345 }) }.to_not raise_error 16 | end 17 | 18 | describe "firewall rules bypass" do 19 | let(:mock_client) { 20 | mock_client = SoftLayer::Client.new(:username => "fake_user", :api_key => "BADKEY") 21 | } 22 | 23 | it "responds to the method change_routing_bypass!" do 24 | mock_firewall = SoftLayer::VLANFirewall.new("not really a client", { "id" => 12345 }) 25 | expect(mock_firewall).to respond_to(:change_rules_bypass!) 26 | end 27 | 28 | it "accepts :apply_firewall_rules" do 29 | mock_firewall = SoftLayer::VLANFirewall.new(mock_client, { "id" => 12345, 'networkVlanFirewall' => {'id' => 67890}}) 30 | allow(mock_firewall).to receive(:rules_ACL_id) { 0 } 31 | allow(mock_firewall).to receive(:rules) { {} } 32 | 33 | firewall_update_service = mock_client[:Network_Firewall_Update_Request] 34 | 35 | expect(firewall_update_service).to receive(:call_softlayer_api_with_params) do |method, parameters, arguments| 36 | expect(arguments[0]['bypassFlag']).to be(false) 37 | end 38 | 39 | mock_firewall.change_rules_bypass!(:apply_firewall_rules) 40 | end 41 | 42 | it "accepts :bypass_firewall_rules!" do 43 | mock_firewall = SoftLayer::VLANFirewall.new(mock_client, { "id" => 12345, 'networkVlanFirewall' => {'id' => 67890}}) 44 | allow(mock_firewall).to receive(:rules_ACL_id) { 0 } 45 | allow(mock_firewall).to receive(:rules) { {} } 46 | 47 | firewall_update_service = mock_client[:Network_Firewall_Update_Request] 48 | expect(firewall_update_service).to receive(:call_softlayer_api_with_params) do |method, parameters, arguments| 49 | expect(arguments[0]['bypassFlag']).to be(true) 50 | end 51 | 52 | mock_firewall.change_rules_bypass!(:bypass_firewall_rules) 53 | end 54 | 55 | it "rejects other parameters (particularly true and false)" do 56 | mock_firewall = SoftLayer::VLANFirewall.new("not really a client", { "id" => 12345 }) 57 | allow(mock_firewall).to receive(:rules) { {} } 58 | 59 | firewall_update_service = mock_client[:Network_Firewall_Update_Request] 60 | 61 | allow(firewall_update_service).to receive(:call_softlayer_api_with_params) 62 | 63 | expect{ mock_firewall.change_rules_bypass!(true) }.to raise_error(NoMethodError) 64 | expect{ mock_firewall.change_rules_bypass!(false) }.to raise_error(NoMethodError) 65 | expect{ mock_firewall.change_rules_bypass!(:route_around_firewall) }.to raise_error(NoMethodError) 66 | expect{ mock_firewall.change_rules_bypass!(:route_through_firewall) }.to raise_error(NoMethodError) 67 | expect{ mock_firewall.change_rules_bypass!("apply_firewall_rules") }.to raise_error(NoMethodError) 68 | expect{ mock_firewall.change_rules_bypass!("bypass_firewall_rules") }.to raise_error(NoMethodError) 69 | expect{ mock_firewall.change_rules_bypass!(nil) }.to raise_error(NoMethodError) 70 | expect{ mock_firewall.change_rules_bypass!(1) }.to raise_error(NoMethodError) 71 | expect{ mock_firewall.change_rules_bypass!(0) }.to raise_error(NoMethodError) 72 | end 73 | end 74 | 75 | describe "firewall routing changes" do 76 | let(:mock_client) { 77 | mock_client = SoftLayer::Client.new(:username => "fake_user", :api_key => "BADKEY") 78 | } 79 | 80 | it "responds to the method change_routing_bypass!" do 81 | mock_firewall = SoftLayer::VLANFirewall.new("not really a client", { "id" => 12345 }) 82 | expect(mock_firewall).to respond_to(:change_routing_bypass!) 83 | end 84 | 85 | it "accepts :route_through_firewall" do 86 | mock_firewall = SoftLayer::VLANFirewall.new(mock_client, { "id" => 12345, 'networkVlanFirewall' => {'id' => 67890}}) 87 | allow(mock_firewall).to receive(:rules_ACL_id) { 0 } 88 | vlan_firewall_service = mock_client[:Network_Vlan_Firewall] 89 | 90 | expect(vlan_firewall_service).to receive(:call_softlayer_api_with_params).with(:updateRouteBypass,an_instance_of(SoftLayer::APIParameterFilter),[false]) 91 | mock_firewall.change_routing_bypass!(:route_through_firewall) 92 | end 93 | 94 | it "accepts :route_around_firewall" do 95 | mock_firewall = SoftLayer::VLANFirewall.new(mock_client, { "id" => 12345, 'networkVlanFirewall' => {'id' => 67890}}) 96 | allow(mock_firewall).to receive(:rules_ACL_id) { 0 } 97 | vlan_firewall_service = mock_client[:Network_Vlan_Firewall] 98 | 99 | expect(vlan_firewall_service).to receive(:call_softlayer_api_with_params).with(:updateRouteBypass,an_instance_of(SoftLayer::APIParameterFilter),[true]) 100 | mock_firewall.change_routing_bypass!(:route_around_firewall) 101 | end 102 | 103 | it "rejects other parameters (particularly true and false)" do 104 | mock_firewall = SoftLayer::VLANFirewall.new("not really a client", { "id" => 12345 }) 105 | allow(mock_firewall).to receive(:rules_ACL_id) { 0 } 106 | 107 | vlan_firewall_service = mock_client[:Network_Vlan_Firewall] 108 | 109 | allow(vlan_firewall_service).to receive(:call_softlayer_api_with_params) 110 | 111 | expect{ mock_firewall.change_routing_bypass!(true) }.to raise_error(NoMethodError) 112 | expect{ mock_firewall.change_routing_bypass!(false) }.to raise_error(NoMethodError) 113 | expect{ mock_firewall.change_routing_bypass!(:apply_firewall_rules) }.to raise_error(NoMethodError) 114 | expect{ mock_firewall.change_routing_bypass!(:bypass_firewall_rules) }.to raise_error(NoMethodError) 115 | expect{ mock_firewall.change_routing_bypass!("route_around_firewall") }.to raise_error(NoMethodError) 116 | expect{ mock_firewall.change_routing_bypass!("route_through_firewall") }.to raise_error(NoMethodError) 117 | expect{ mock_firewall.change_routing_bypass!(nil) }.to raise_error(NoMethodError) 118 | expect{ mock_firewall.change_routing_bypass!(1) }.to raise_error(NoMethodError) 119 | expect{ mock_firewall.change_routing_bypass!(0) }.to raise_error(NoMethodError) 120 | end 121 | end 122 | end 123 | -------------------------------------------------------------------------------- /spec/VirtualServerUpgradeOrder_spec.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # For licensing information see the LICENSE.md file in the project root. 5 | #++ 6 | 7 | $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) 8 | 9 | require 'rubygems' 10 | require 'softlayer_api' 11 | require 'rspec' 12 | 13 | describe SoftLayer::VirtualServerUpgradeOrder do 14 | before(:each) do 15 | SoftLayer::VirtualServerUpgradeOrder.send(:public, *SoftLayer::VirtualServerUpgradeOrder.private_instance_methods) 16 | end 17 | 18 | let(:test_virtual_server) do 19 | mock_client = SoftLayer::Client.new(:username => "fakeuser", :api_key => "DEADBEEFBADF00D") 20 | virtual_guest_service = mock_client[:Virtual_Guest] 21 | allow(virtual_guest_service).to receive(:call_softlayer_api_with_params) do |api_method, parameters, api_arguments| 22 | api_return = nil 23 | 24 | case api_method 25 | when :getUpgradeItemPrices 26 | api_return = fixture_from_json('virtual_server_upgrade_options') 27 | else 28 | fail "Unexpected call to the SoftLayer_Virtual_Guest service" 29 | end 30 | 31 | api_return 32 | end 33 | 34 | test_servers = fixture_from_json('test_virtual_servers') 35 | SoftLayer::VirtualServer.new(mock_client, test_servers.first) 36 | end 37 | 38 | it "requires a virtual server when initialized" do 39 | expect { SoftLayer::VirtualServerUpgradeOrder.new(nil) }.to raise_error(RuntimeError) 40 | expect { SoftLayer::VirtualServerUpgradeOrder.new("foo") }.to raise_error(RuntimeError) 41 | expect { SoftLayer::VirtualServerUpgradeOrder.new(test_virtual_server) }.to_not raise_error 42 | end 43 | 44 | it "initializes with none of the upgrades specified" do 45 | upgrade_order = SoftLayer::VirtualServerUpgradeOrder.new(test_virtual_server) 46 | expect(upgrade_order.cores == nil) 47 | expect(upgrade_order.ram == nil) 48 | expect(upgrade_order.max_port_speed == nil) 49 | expect(upgrade_order.upgrade_at == nil) 50 | end 51 | 52 | it "identifies what options are available for upgrading the number of cores" do 53 | sample_order = SoftLayer::VirtualServerUpgradeOrder.new(test_virtual_server) 54 | expect(sample_order.core_options).to eq [1, 2, 4, 8, 12, 16] 55 | end 56 | 57 | it "identifies what options are available for upgrading ram" do 58 | sample_order = SoftLayer::VirtualServerUpgradeOrder.new(test_virtual_server) 59 | expect(sample_order.memory_options).to eq [1, 2, 4, 6, 8, 12, 16, 32, 48, 64] 60 | end 61 | 62 | it "identifies what options are available for upgrading max port speed" do 63 | sample_order = SoftLayer::VirtualServerUpgradeOrder.new(test_virtual_server) 64 | expect(sample_order.max_port_speed_options).to eq [10, 100, 1000] 65 | end 66 | 67 | it "places the number of cores asked for into the order template" do 68 | sample_order = SoftLayer::VirtualServerUpgradeOrder.new(test_virtual_server) 69 | sample_order.core_options.each do |num_cores| 70 | sample_order.cores = num_cores 71 | test_template = sample_order.order_object 72 | expect(sample_order.order_object['prices'].length).to be(1) 73 | 74 | item = sample_order._item_price_with_capacity("guest_core", num_cores) 75 | expect(test_template['prices'].first['id']).to eq item['id'] 76 | end 77 | end 78 | 79 | it "places the amount of RAM asked for into the order template" do 80 | sample_order = SoftLayer::VirtualServerUpgradeOrder.new(test_virtual_server) 81 | sample_order.memory_options.each do |ram_in_GB| 82 | sample_order.ram = ram_in_GB 83 | test_template = sample_order.order_object 84 | expect(sample_order.order_object['prices'].length).to be(1) 85 | 86 | item = sample_order._item_price_with_capacity("ram", ram_in_GB) 87 | expect(test_template['prices'].first['id']).to eq item['id'] 88 | end 89 | end 90 | 91 | it "places the port speed asked for into the order template" do 92 | sample_order = SoftLayer::VirtualServerUpgradeOrder.new(test_virtual_server) 93 | sample_order.max_port_speed_options.each do |port_speed| 94 | sample_order.max_port_speed = port_speed 95 | test_template = sample_order.order_object 96 | expect(sample_order.order_object['prices'].length).to be(1) 97 | 98 | item = sample_order._item_price_with_capacity("port_speed", port_speed) 99 | expect(test_template['prices'].first['id']).to eq item['id'] 100 | end 101 | end 102 | 103 | it "adds the default maintenance window of 'now' if none is given" do 104 | sample_order = SoftLayer::VirtualServerUpgradeOrder.new(test_virtual_server) 105 | sample_order.cores = 2 106 | test_template = sample_order.order_object 107 | 108 | expect(sample_order.order_object['properties'].first['name']).to eq('MAINTENANCE_WINDOW') 109 | 110 | time_string = sample_order.order_object['properties'].first['value'] 111 | maintenance_time = Time.iso8601(time_string) 112 | 113 | expect((Time.now - maintenance_time) <= 1.0).to be(true) 114 | end 115 | 116 | it "adds the appointed maintenance window one is given" do 117 | sample_order = SoftLayer::VirtualServerUpgradeOrder.new(test_virtual_server) 118 | sample_order.cores = 2 119 | 120 | upgrade_time = Time.now + 3600 # in an hour 121 | sample_order.upgrade_at = upgrade_time 122 | 123 | test_template = sample_order.order_object 124 | 125 | expect(sample_order.order_object['properties'].first['name']).to eq('MAINTENANCE_WINDOW') 126 | 127 | time_string = sample_order.order_object['properties'].first['value'] 128 | expect(time_string).to eq upgrade_time.iso8601 129 | end 130 | 131 | it "verifies product orders" do 132 | product_order_service = test_virtual_server.softlayer_client[:Product_Order] 133 | 134 | sample_order = SoftLayer::VirtualServerUpgradeOrder.new(test_virtual_server) 135 | sample_order.cores = 2 136 | 137 | order_object = sample_order.order_object 138 | expect(product_order_service).to receive(:call_softlayer_api_with_params).with(:verifyOrder, anything, [order_object]) 139 | 140 | sample_order.verify() 141 | end 142 | 143 | it "places product orders" do 144 | product_order_service = test_virtual_server.softlayer_client[:Product_Order] 145 | 146 | sample_order = SoftLayer::VirtualServerUpgradeOrder.new(test_virtual_server) 147 | sample_order.cores = 2 148 | 149 | order_object = sample_order.order_object 150 | expect(product_order_service).to receive(:call_softlayer_api_with_params).with(:placeOrder, anything, [order_object]) 151 | 152 | sample_order.place_order!() 153 | end 154 | end 155 | -------------------------------------------------------------------------------- /spec/VirtualServer_spec.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # For licensing information see the LICENSE.md file in the project root. 5 | #++ 6 | 7 | $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) 8 | 9 | require 'rubygems' 10 | require 'softlayer_api' 11 | require 'rspec' 12 | 13 | require 'shared_server' 14 | 15 | describe SoftLayer::VirtualServer do 16 | let(:sample_server) { 17 | mock_client = SoftLayer::Client.new(:username => "fakeuser", :api_key => "DEADBEEFBADF00D") 18 | allow(mock_client).to receive(:[]) do |service_name| 19 | service = mock_client.service_named(service_name) 20 | allow(service).to receive(:call_softlayer_api_with_params) 21 | service 22 | end 23 | 24 | SoftLayer::VirtualServer.new(mock_client, { "id" => 12345 }) 25 | } 26 | 27 | it "identifies itself with the SoftLayer_Virtual_Guest service" do 28 | service = sample_server.service 29 | expect(service.server_object_id).to eq(12345) 30 | expect(service.target.service_name).to eq "SoftLayer_Virtual_Guest" 31 | end 32 | 33 | it "implements softlayer properties inherited from Server" do 34 | mock_client = SoftLayer::Client.new(:username => "fakeuser", :api_key => "DEADBEEFBADF00D") 35 | 36 | test_servers = fixture_from_json('test_virtual_servers') 37 | test_server = SoftLayer::VirtualServer.new(mock_client,test_servers.first) 38 | 39 | expect(test_server.hostname).to eq("test-server-1") 40 | expect(test_server.domain).to eq("softlayer-api-test.rb") 41 | expect(test_server.fullyQualifiedDomainName).to eq("test-server-1.softlayer-api-test.rb") 42 | expect(test_server.datacenter).to eq({"id"=>17936, "longName"=>"Dallas 6", "name"=>"dal06"}) 43 | expect(test_server.primary_public_ip).to eq("198.51.100.121") 44 | expect(test_server.primary_private_ip).to eq("203.0.113.82") 45 | expect(test_server.notes).to eq("These are test notes") 46 | end 47 | 48 | it_behaves_like "server with port speed" do 49 | let (:server) { sample_server } 50 | end 51 | 52 | it_behaves_like "server with mutable hostname" do 53 | let (:server) { sample_server } 54 | end 55 | 56 | describe "component upgrades" do 57 | let(:mock_client) do 58 | mock_client = SoftLayer::Client.new(:username => "fakeuser", :api_key => "DEADBEEFBADF00D") 59 | virtual_guest_service = mock_client[:Virtual_Guest] 60 | 61 | allow(virtual_guest_service).to receive(:call_softlayer_api_with_params) do |api_method, parameters, api_arguments| 62 | api_return = nil 63 | 64 | case api_method 65 | when :getUpgradeItemPrices 66 | api_return = fixture_from_json('virtual_server_upgrade_options') 67 | else 68 | fail "Unexpected call to the SoftLayer_Virtual_Guest service" 69 | end 70 | 71 | api_return 72 | end 73 | 74 | mock_client 75 | end 76 | 77 | it "retrieves the item upgrades for a server from the API once" do 78 | fake_virtual_server = SoftLayer::VirtualServer.new(mock_client, {"id" => 12345}) 79 | expect(fake_virtual_server.upgrade_options).to eq fixture_from_json('virtual_server_upgrade_options') 80 | 81 | # once we've retrieve the options once, we shouldn't be calling back into the service to get them again 82 | expect(mock_client[:Virtual_Guest]).to_not receive(:call_softlayer_api_with_params) 83 | fake_virtual_server.upgrade_options 84 | end 85 | end 86 | end -------------------------------------------------------------------------------- /spec/XMLRPC_Convert_spec.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # For licensing information see the LICENSE.md file in the project root. 5 | #++ 6 | 7 | $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) 8 | 9 | require 'rubygems' 10 | require 'softlayer_api' 11 | 12 | describe XMLRPC::Convert,"::fault" do 13 | it "converts faults with faultCodes that are strings to FaultException objects" do 14 | fault_hash = { "faultCode" => "The SLAPI returns strings where it shouldn't", "faultString" => "This is the actual fault"} 15 | 16 | expect { XMLRPC::Convert.fault(fault_hash) }.not_to raise_error 17 | exception = XMLRPC::Convert.fault(fault_hash) 18 | expect(exception).to be_kind_of(XMLRPC::FaultException) 19 | expect(exception.faultCode).to eq(fault_hash['faultCode']) 20 | expect(exception.faultString).to eq(fault_hash['faultString']) 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /spec/fixtures/Product_Package.json: -------------------------------------------------------------------------------- 1 | {"description":"
Quad Processor Multi-core Servers
","id":32,"name":"Quad Processor, Quad Core Intel","availableLocations":[{"isAvailable":0,"locationId":2,"packageId":32,"location":{"id":2,"longName":"Corporate HQ Lab","name":"dal00"}},{"deliveryTimeInformation":"Typical Installation time is 2 to 4 hours.","isAvailable":1,"locationId":3,"packageId":32,"location":{"id":3,"longName":"Dallas 1","name":"dal01"}},{"deliveryTimeInformation":"Typical Installation time is 2 to 4 hours.","isAvailable":1,"locationId":18171,"packageId":32,"location":{"id":18171,"longName":"Seattle","name":"sea01"}},{"deliveryTimeInformation":"Typical Installation time is 2 to 4 hours.","isAvailable":1,"locationId":37473,"packageId":32,"location":{"id":37473,"longName":"Washington, DC 1","name":"wdc01"}},{"deliveryTimeInformation":"Typical Installation time is 2 to 4 hours.","isAvailable":1,"locationId":138124,"packageId":32,"location":{"id":138124,"longName":"Dallas 5","name":"dal05"}},{"deliveryTimeInformation":"Typical Installation time is 2 to 4 hours.","isAvailable":1,"locationId":142776,"packageId":32,"location":{"id":142776,"longName":"Dallas 7","name":"dal07"}},{"isAvailable":1,"locationId":154820,"packageId":32,"location":{"id":154820,"longName":"Dallas 6","name":"dal06"}},{"deliveryTimeInformation":"Typical Installation time is 2 to 4 hours.","isAvailable":1,"locationId":168642,"packageId":32,"location":{"id":168642,"longName":"San Jose 1","name":"sjc01"}},{"isAvailable":1,"locationId":224092,"packageId":32,"location":{"id":224092,"longName":"Singapore 1","name":"sng01"}},{"isAvailable":1,"locationId":265592,"packageId":32,"location":{"id":265592,"longName":"Amsterdam 1","name":"ams01"}},{"isAvailable":1,"locationId":352494,"packageId":32,"location":{"id":352494,"longName":"Hong Kong 2","name":"hkg02"}}]} -------------------------------------------------------------------------------- /spec/fixtures/datacenter_locations.json: -------------------------------------------------------------------------------- 1 | [{"id":265592,"longName":"Amsterdam 1","name":"ams01"},{"id":358698,"longName":"Ashburn 3","name":"wdc03"},{"id":3,"longName":"Dallas 1","name":"dal01"},{"id":154770,"longName":"Dallas 2","name":"dal02"},{"id":167092,"longName":"Dallas 4","name":"dal04"},{"id":138124,"longName":"Dallas 5","name":"dal05"},{"id":154820,"longName":"Dallas 6","name":"dal06"},{"id":142776,"longName":"Dallas 7","name":"dal07"},{"id":352494,"longName":"Hong Kong 2","name":"hkg02"},{"id":142775,"longName":"Houston 2","name":"hou02"},{"id":358694,"longName":"London 2","name":"lon02"},{"id":168642,"longName":"San Jose 1","name":"sjc01"},{"id":18171,"longName":"Seattle","name":"sea01"},{"id":224092,"longName":"Singapore 1","name":"sng01"},{"id":448994,"longName":"Toronto 1","name":"tor01"},{"id":37473,"longName":"Washington 1","name":"wdc01"}] -------------------------------------------------------------------------------- /spec/fixtures/test_account.json: -------------------------------------------------------------------------------- 1 | { 2 | "accountStatusId": 1001, 3 | "address1": "123 Main Street", 4 | "allowedPptpVpnQuantity": 5, 5 | "alternatePhone": "555.456.7891", 6 | "brandId": 2, 7 | "city": "Anytown", 8 | "claimedTaxExemptTxFlag": false, 9 | "companyName": "UpAndComing Software", 10 | "country": "US", 11 | "createDate": "2012-03-30T14:41:16-07:00", 12 | "email": "just@a@test.com", 13 | "firstName": "Don ", 14 | "id": 99999, 15 | "isReseller": 0, 16 | "lastName": "Joe", 17 | "lateFeeProtectionFlag": true, 18 | "modifyDate": "2012-03-30T14:41:50-07:00", 19 | "officePhone": "555.123.4567", 20 | "postalCode": "778899", 21 | "state": "TX", 22 | "statusDate": null 23 | } 24 | -------------------------------------------------------------------------------- /spec/fixtures/test_bare_metal.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "accountId": "99999", 3 | "bareMetalInstanceFlag": 1, 4 | "domain": "example.pqz", 5 | "fullyQualifiedDomainName": "test_server_1.example.pqz", 6 | "hardwareStatusId": 5, 7 | "hostname": "test_server_1", 8 | "id": "124437", 9 | "manufacturerSerialNumber": "c4a2554a50567f0c13e951eaf56a06", 10 | "notes": "Test notes.", 11 | "serialNumber": "SL1696570", 12 | "serviceProviderId": 1, 13 | "serviceProviderResourceId": 186908, 14 | "hardwareStatus": { 15 | "id": 5, 16 | "status": "ACTIVE" 17 | }, 18 | "networkManagementIpAddress": "0.88.0.24", 19 | "primaryBackendIpAddress": "0.88.0.23", 20 | "primaryIpAddress": "0.23.0.198", 21 | "privateIpAddress": "0.88.0.23" 22 | }, { 23 | "accountId": "99999", 24 | "bareMetalInstanceFlag": 0, 25 | "domain": "example.pqz", 26 | "fullyQualifiedDomainName": "test_server_2.example.pqz", 27 | "hardwareStatusId": 5, 28 | "hostname": "test_server_2", 29 | "id": "161282", 30 | "manufacturerSerialNumber": "1d41493be0db259bb59e4ba31b3f74", 31 | "serialNumber": "SL1861192", 32 | "serviceProviderId": 1, 33 | "serviceProviderResourceId": 125195, 34 | "globalIdentifier": "85450b0a-d86d-4144-8b4e-1918ef5061bb", 35 | "hardwareStatus": { 36 | "id": 5, 37 | "status": "ACTIVE" 38 | }, 39 | "networkManagementIpAddress": "0.54.0.67", 40 | "primaryBackendIpAddress": "0.54.0.66", 41 | "primaryIpAddress": "0.23.0.2", 42 | "privateIpAddress": "0.54.0.66" 43 | }, { 44 | "accountId": "99999", 45 | "bareMetalInstanceFlag": 0, 46 | "domain": "example.pqz", 47 | "fullyQualifiedDomainName": "test_server_3.example.pqz", 48 | "hardwareStatusId": 5, 49 | "hostname": "test_server_3", 50 | "id": "116771", 51 | "manufacturerSerialNumber": "2f4ea9bdb51c378b5019efc6e03740", 52 | "serialNumber": "SL1110013", 53 | "serviceProviderId": 1, 54 | "serviceProviderResourceId": 84576, 55 | "globalIdentifier": "04192495-5228-439d-a014-d2a15a059023", 56 | "hardwareStatus": { 57 | "id": 5, 58 | "status": "ACTIVE" 59 | }, 60 | "networkManagementIpAddress": "0.54.0.73", 61 | "primaryBackendIpAddress": "0.54.0.72", 62 | "primaryIpAddress": "0.23.0.6", 63 | "privateIpAddress": "0.54.0.72" 64 | }] 65 | -------------------------------------------------------------------------------- /spec/fixtures/test_tickets.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "assignedUser": { 4 | "firstName": "Jonathan", 5 | "lastName": "Doe", 6 | "username": "slfakeuser" 7 | }, 8 | "awaitingUserResponseFlag": false, 9 | "createDate": "2013-07-11T12:52:57-06:00", 10 | "id": 12345, 11 | "lastEditDate": "2013-07-11T12:53:59-06:00", 12 | "newUpdatesFlag": false, 13 | "serverAdministrationFlag": 0, 14 | "serviceProvider": { 15 | "description": "Infrastructure as a Service including Dedicated, Virtualized, and Cloud Hosting", 16 | "id": 1, 17 | "keyName": "SOFTLAYER", 18 | "name": "SoftLayer Technologies, Inc." 19 | }, 20 | "serviceProviderResourceId": 7129684, 21 | "status": { 22 | "id": 1001 23 | }, 24 | "title": "What's in a title anyway? Tickets are exciting!" 25 | }, 26 | { 27 | "assignedUser": { 28 | "firstName": "Stephen", 29 | "lastName": "Everyman", 30 | "username": "SLEveryman" 31 | }, 32 | "awaitingUserResponseFlag": false, 33 | "createDate": "2013-07-12T16:48:35-06:00", 34 | "id": 12346, 35 | "lastEditDate": "2013-07-12T17:16:05-06:00", 36 | "newUpdatesFlag": false, 37 | "serverAdministrationFlag": 0, 38 | "serviceProvider": { 39 | "description": "Infrastructure as a Service including Dedicated, Virtualized, and Cloud Hosting", 40 | "id": 1, 41 | "keyName": "SOFTLAYER", 42 | "name": "SoftLayer Technologies, Inc." 43 | }, 44 | "serviceProviderResourceId": 7135016, 45 | "status": { 46 | "id": 1001 47 | }, 48 | "title": "A fake title used in the place of a real title" 49 | }, 50 | { 51 | "assignedUser": { 52 | "firstName": "Joe", 53 | "lastName": "Kool", 54 | "username": "sljcool" 55 | }, 56 | "awaitingUserResponseFlag": false, 57 | "createDate": "2013-07-25T07:38:07-06:00", 58 | "id": 12347, 59 | "lastEditDate": "2013-07-25T08:25:25-06:00", 60 | "newUpdatesFlag": false, 61 | "serverAdministrationFlag": 0, 62 | "serviceProvider": { 63 | "description": "Infrastructure as a Service including Dedicated, Virtualized, and Cloud Hosting", 64 | "id": 1, 65 | "keyName": "SOFTLAYER", 66 | "name": "SoftLayer Technologies, Inc." 67 | }, 68 | "serviceProviderResourceId": 7194676, 69 | "status": { 70 | "id": 1001 71 | }, 72 | "title": "A riveting ticket that you can't put down" 73 | }, 74 | { 75 | "assignedUser": { 76 | "firstName": "Joe", 77 | "lastName": "Kool", 78 | "username": "sljcool" 79 | }, 80 | "awaitingUserResponseFlag": false, 81 | "createDate": "2013-07-30T09:51:44-06:00", 82 | "id": 12348, 83 | "lastEditDate": "2013-07-30T09:56:04-06:00", 84 | "newUpdatesFlag": false, 85 | "serverAdministrationFlag": 0, 86 | "serviceProvider": { 87 | "description": "Infrastructure as a Service including Dedicated, Virtualized, and Cloud Hosting", 88 | "id": 1, 89 | "keyName": "SOFTLAYER", 90 | "name": "SoftLayer Technologies, Inc." 91 | }, 92 | "serviceProviderResourceId": 7236607, 93 | "status": { 94 | "id": 1001 95 | }, 96 | "title": "A moderately interesting ticket" 97 | }, 98 | { 99 | "assignedUser": { 100 | "firstName": "Ralf", 101 | "lastName": "Cramden", 102 | "username": "slsomeguy" 103 | }, 104 | "awaitingUserResponseFlag": false, 105 | "createDate": "2013-07-31T14:34:50-06:00", 106 | "id": 12349, 107 | "lastEditDate": "2013-07-31T14:42:34-06:00", 108 | "newUpdatesFlag": false, 109 | "serverAdministrationFlag": 1 110 | , 111 | "serviceProvider": { 112 | "description": "Infrastructure as a Service including Dedicated, Virtualized, and Cloud Hosting", 113 | "id": 1, 114 | "keyName": "SOFTLAYER", 115 | "name": "SoftLayer Technologies, Inc." 116 | }, 117 | "serviceProviderResourceId": 7243384, 118 | "status": { 119 | "id": 1001 120 | }, 121 | "title": "A really boring ticket that you don't care about" 122 | } 123 | ] 124 | -------------------------------------------------------------------------------- /spec/fixtures/test_virtual_servers.json: -------------------------------------------------------------------------------- 1 | [{"notes":"These are test notes","createDate":"2014-06-10T09:14:15-06:00","dedicatedAccountHostOnlyFlag":false,"domain":"softlayer-api-test.rb","fullyQualifiedDomainName":"test-server-1.softlayer-api-test.rb","hostname":"test-server-1","id":62674,"maxCpu":1,"maxMemory":1024,"modifyDate":"2014-06-10T09:15:22-06:00","globalIdentifier":"feee0ce0-d3d8-0131-ec22-002500f2ef54","hourlyBillingFlag":true,"primaryBackendIpAddress":"203.0.113.82","primaryIpAddress":"198.51.100.121","privateNetworkOnlyFlag":false,"provisionDate":"2014-06-10T09:18:50-06:00","billingItem":{"id":26241,"recurringFee":"0"},"blockDevices":[{"bootableFlag":1,"createDate":"2014-06-10T09:14:53-06:00","device":"0","diskImageId":79652,"guestId":55344,"hotPlugFlag":0,"id":29549,"modifyDate":"","mountMode":"RW","mountType":"Disk","statusId":1,"uuid":"feee0e40-d3d8-0131-ec22-002500f2ef54"},{"bootableFlag":0,"createDate":"2014-06-10T09:14:46-06:00","device":"1","diskImageId":97104,"guestId":97146,"hotPlugFlag":0,"id":91153,"modifyDate":"","mountMode":"RW","mountType":"Disk","statusId":1,"uuid":"feee0ec0-d3d8-0131-ec22-002500f2ef54"}],"datacenter":{"id":17936,"longName":"Dallas 6","name":"dal06"},"networkComponents":[{"id":25665,"macAddress":"d1:78:1b:ec:76:35","maxSpeed":100,"name":"eth","port":0,"speed":100,"status":"ACTIVE","primaryIpAddress":"198.51.100.183","primarySubnet":{"broadcastAddress":"203.0.113.196","gateway":"192.0.2.230","id":96221,"netmask":"255.255.255.192","networkIdentifier":"203.0.113.168"},"uuid":"feee0f90-d3d8-0131-ec22-002500f2ef54"},{"id":84691,"macAddress":"61:42:24:63:1e:10","maxSpeed":100,"name":"eth","port":1,"speed":100,"status":"ACTIVE","primaryIpAddress":"203.0.113.92","primarySubnet":{"broadcastAddress":"203.0.113.245","gateway":"198.51.100.69","id":2621,"netmask":"255.255.255.224","networkIdentifier":"203.0.113.239"},"uuid":"feee10b0-d3d8-0131-ec22-002500f2ef54"}],"networkVlans":[{"id":83133,"vlanNumber":1318,"networkSpace":"PRIVATE"},{"id":49221,"vlanNumber":1854,"networkSpace":"PUBLIC"}],"operatingSystem":{"hardwareId":"","id":12055,"manufacturerLicenseInstance":"","passwords":["top_secret"],"softwareLicense":{"id":9547,"softwareDescriptionId":887,"softwareDescription":{"manufacturer":"CentOS","name":"CentOS","referenceCode":"CENTOS_6_64","version":"6.0-64 Minimal for VSI"}}},"powerState":{"name":"Running"},"status":{"keyName":"ACTIVE","name":"Active"},"tagReferences":[],"userData":[]},{"createDate":"2014-05-27T16:06:38-06:00","dedicatedAccountHostOnlyFlag":false,"domain":"softlayer-api-test.rb","fullyQualifiedDomainName":"test-server-2.softlayer-api-test.rb","hostname":"test-server-2","id":57152,"maxCpu":1,"maxMemory":1024,"modifyDate":"2014-05-27T16:08:01-06:00","globalIdentifier":"feee1680-d3d8-0131-ec22-002500f2ef54","hourlyBillingFlag":true,"primaryBackendIpAddress":"192.0.2.203","primaryIpAddress":"192.0.2.101","privateNetworkOnlyFlag":false,"provisionDate":"2014-05-27T16:10:37-06:00","billingItem":{"id":95857,"recurringFee":"0"},"blockDevices":[{"bootableFlag":1,"createDate":"2014-05-27T16:07:34-06:00","device":"0","diskImageId":33787,"guestId":1303,"hotPlugFlag":0,"id":71563,"modifyDate":"","mountMode":"RW","mountType":"Disk","statusId":1,"uuid":"feee1700-d3d8-0131-ec22-002500f2ef54"},{"bootableFlag":0,"createDate":"2014-05-27T16:07:13-06:00","device":"1","diskImageId":92918,"guestId":84492,"hotPlugFlag":0,"id":37371,"modifyDate":"","mountMode":"RW","mountType":"Disk","statusId":1,"uuid":"feee1760-d3d8-0131-ec22-002500f2ef54"}],"datacenter":{"id":94135,"longName":"Dallas 6","name":"dal06"},"networkComponents":[{"id":18080,"macAddress":"77:19:82:b4:88:55","maxSpeed":100,"name":"eth","port":0,"speed":100,"status":"ACTIVE","primaryIpAddress":"192.0.2.91","primarySubnet":{"broadcastAddress":"192.0.2.91","gateway":"203.0.113.230","id":45526,"netmask":"255.255.255.192","networkIdentifier":"192.0.2.22"},"uuid":"feee1810-d3d8-0131-ec22-002500f2ef54"},{"id":4025,"macAddress":"16:a0:b0:40:3a:95","maxSpeed":100,"name":"eth","port":1,"speed":100,"status":"ACTIVE","primaryIpAddress":"203.0.113.17","primarySubnet":{"broadcastAddress":"203.0.113.231","gateway":"203.0.113.184","id":44482,"netmask":"255.255.255.224","networkIdentifier":"198.51.100.240"},"uuid":"feee1900-d3d8-0131-ec22-002500f2ef54"}],"networkVlans":[{"id":41407,"vlanNumber":1854,"networkSpace":"PUBLIC"},{"id":13505,"vlanNumber":1318,"networkSpace":"PRIVATE"}],"operatingSystem":{"hardwareId":"","id":95684,"manufacturerLicenseInstance":"","passwords":["top_secret"],"softwareLicense":{"id":75930,"softwareDescriptionId":888,"softwareDescription":{"manufacturer":"CentOS","name":"CentOS","referenceCode":"CENTOS_6_64","version":"6.0-64 LAMP for VSI"}}},"powerState":{"name":"Running"},"status":{"keyName":"ACTIVE","name":"Active"},"tagReferences":[],"userData":[]},{"createDate":"2013-05-31T07:58:45-06:00","dedicatedAccountHostOnlyFlag":false,"domain":"softlayer-api-test.rb","fullyQualifiedDomainName":"test-server-3.softlayer-api-test.rb","hostname":"test-server-3","id":78144,"maxCpu":1,"maxMemory":1024,"modifyDate":"2014-01-20T20:46:18-06:00","globalIdentifier":"feee1ee0-d3d8-0131-ec22-002500f2ef54","hourlyBillingFlag":false,"primaryBackendIpAddress":"192.0.2.153","primaryIpAddress":"192.0.2.160","privateNetworkOnlyFlag":false,"provisionDate":"2013-05-31T08:04:36-06:00","billingItem":{"id":54285,"recurringFee":"0"},"blockDevices":[{"bootableFlag":1,"createDate":"2013-05-31T07:59:16-06:00","device":"0","diskImageId":34967,"guestId":81749,"hotPlugFlag":0,"id":8730,"modifyDate":"2014-01-20T20:45:32-06:00","mountMode":"RW","mountType":"Disk","statusId":1,"uuid":"feee1f60-d3d8-0131-ec22-002500f2ef54"},{"bootableFlag":0,"createDate":"2014-01-20T20:45:37-06:00","device":"1","diskImageId":34880,"guestId":88106,"hotPlugFlag":0,"id":52390,"modifyDate":"","mountMode":"RW","mountType":"Disk","statusId":1,"uuid":"feee1fc0-d3d8-0131-ec22-002500f2ef54"}],"datacenter":{"id":60948,"longName":"San Jose 1","name":"sjc01"},"networkComponents":[{"id":60582,"macAddress":"ca:19:22:ef:15:7d","maxSpeed":100,"name":"eth","port":0,"speed":100,"status":"ACTIVE","primaryIpAddress":"198.51.100.83","primarySubnet":{"broadcastAddress":"198.51.100.65","gateway":"192.0.2.63","id":41408,"netmask":"255.255.255.192","networkIdentifier":"198.51.100.67"},"uuid":"feee2070-d3d8-0131-ec22-002500f2ef54"},{"id":31860,"macAddress":"15:3a:17:4b:5d:80","maxSpeed":100,"name":"eth","port":1,"speed":100,"status":"ACTIVE","primaryIpAddress":"203.0.113.170","primarySubnet":{"broadcastAddress":"203.0.113.118","gateway":"192.0.2.209","id":38290,"netmask":"255.255.255.224","networkIdentifier":"203.0.113.195"},"uuid":"feee2160-d3d8-0131-ec22-002500f2ef54"}],"networkVlans":[{"id":71784,"vlanNumber":928,"networkSpace":"PRIVATE"},{"id":42267,"vlanNumber":932,"networkSpace":"PUBLIC"}],"operatingSystem":{"hardwareId":"","id":48341,"manufacturerLicenseInstance":"","passwords":["top_secret"],"softwareLicense":{"id":82561,"softwareDescriptionId":885,"softwareDescription":{"manufacturer":"CentOS","name":"CentOS","referenceCode":"CENTOS_6_32","version":"6.0-32 Minimal for VSI"}}},"powerState":{"name":"Running"},"status":{"keyName":"ACTIVE","name":"Active"},"tagReferences":[],"userData":[]}] -------------------------------------------------------------------------------- /spec/fixtures/ticket_subjects.json: -------------------------------------------------------------------------------- 1 | [{"id" : 1001, "name" : "Accounting Request"}, 2 | {"id" : 1002, "name" : "Sales Request"}, 3 | {"id" : 1003, "name" : "Reboots and Console Access"}, 4 | {"id" : 1041, "name" : "DNS Request"}, 5 | {"id" : 1021, "name" : "Hardware Issue"}, 6 | {"id" : 1022, "name" : "Public Network Question"}, 7 | {"id" : 1061, "name" : "Private Network Question"}, 8 | {"id" : 1201, "name" : "DOS/Abuse Issue"}, 9 | {"id" : 1101, "name" : "Security Issue"}, 10 | {"id" : 1121, "name" : "Hardware Firewall Question"}, 11 | {"id" : 1122, "name" : "Hardware Load Balancer Question"}, 12 | {"id" : 1004, "name" : "OS Reload Question"}, 13 | {"id" : 1005, "name" : "Portal Information Question"}, 14 | {"id" : 1081, "name" : "Licensing Question"}, 15 | {"id" : 1141, "name" : "Mail Server Issue"}, 16 | {"id" : 1161, "name" : "StorageLayer Question"}, 17 | {"id" : 1181, "name" : "CDNLayer Question"}, 18 | {"id" : 1221, "name" : "Transcoding Question"}, 19 | {"id" : 1261, "name" : "Colocation Service Request"}] -------------------------------------------------------------------------------- /spec/object_mask_helpers_spec.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # For licensing information see the LICENSE.md file in the project root. 5 | #++ 6 | 7 | $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), "../lib")) 8 | 9 | require 'rubygems' 10 | require 'softlayer_api' 11 | require 'rspec' 12 | 13 | describe String, "#_to_sl_object_mask_property" do 14 | it "converts to the string itself" do 15 | expect("blah"._to_sl_object_mask_property).to eql("blah") 16 | expect(" blah"._to_sl_object_mask_property).to eql("blah") 17 | expect(" blah "._to_sl_object_mask_property).to eql("blah") 18 | expect(" blah \t\n"._to_sl_object_mask_property).to eql("blah") 19 | end 20 | 21 | it "echos the empty string" do 22 | expect(""._to_sl_object_mask_property).to eql("") 23 | end 24 | end 25 | 26 | describe Array,"#_to_sl_object_mask_property" do 27 | it "converts an empty array to the empty string" do 28 | expect([]._to_sl_object_mask_property).to eql("") 29 | end 30 | 31 | it "handles simple arrays" do 32 | expect(["foo", "bar", "baz"]._to_sl_object_mask_property).to eql("foo,bar,baz") 33 | end 34 | 35 | it "flattens inner arrays to simple lists" do 36 | expect(["foo", ["bar", "baz"]]._to_sl_object_mask_property).to eql("foo,bar,baz") 37 | end 38 | 39 | it "handles nils in the array" do 40 | expect(["foo", nil, "bar"]._to_sl_object_mask_property()).to eql("foo,bar") 41 | end 42 | end 43 | 44 | describe Hash, "#_to_sl_object_mask_property" do 45 | it "returns the empty string for an empty hash" do 46 | expect({}._to_sl_object_mask_property).to eql("") 47 | end 48 | 49 | it "constructs a dot expression for a simple string value" do 50 | expect({"foo" => "foobar"}._to_sl_object_mask_property).to eql("foo.foobar") 51 | end 52 | 53 | it "builds a bracket expression with array values" do 54 | expect({"foo" => ["one", "two", "three"]}._to_sl_object_mask_property).to eql("foo[one,two,three]") 55 | end 56 | 57 | it "builds bracket expressions for nested hashes" do 58 | expect({"foo" => {"sub" => "resub"}}._to_sl_object_mask_property).to eql("foo[sub.resub]") 59 | end 60 | 61 | it "resolves simple inner values to simple dot expressions" do 62 | expect({"top" => [ "middle1", {"middle2" => "end"}]}._to_sl_object_mask_property).to eql("top[middle1,middle2.end]") 63 | end 64 | 65 | it "accepts an inner empty hash and returns a mask" do 66 | expect({ "ipAddress" => { "ipAddress" => {}}}._to_sl_object_mask_property).to eql("ipAddress[ipAddress]") 67 | end 68 | end 69 | 70 | describe Hash, "#to_sl_object_mask" do 71 | it "rejects the empty hash" do 72 | expect { {}.to_sl_object_mask }.to raise_error(RuntimeError) 73 | end 74 | 75 | it "constructs a dot expression for a simple string value" do 76 | expect({"mask" => "foobar"}.to_sl_object_mask).to eql("mask.foobar") 77 | end 78 | 79 | it "builds a bracket expression with array values" do 80 | expect({"mask" => ["one", "two", "three"]}.to_sl_object_mask).to eql("mask[one,two,three]") 81 | end 82 | 83 | it "builds bracket expressions for nested hashes" do 84 | expect({"mask(some_type)" => {"sub" => "resub"}}.to_sl_object_mask).to eql("mask(some_type)[sub.resub]") 85 | end 86 | 87 | it "resolves simple inner values to simple dot expressions" do 88 | expect({"mask" => [ "middle1", {"middle2" => "end"}]}.to_sl_object_mask).to eql("mask[middle1,middle2.end]") 89 | end 90 | 91 | it "accepts an inner empty hash and returns a mask" do 92 | expect({ "mask" => { "ipAddress" => {}}}.to_sl_object_mask).to eql("mask[ipAddress]") 93 | end 94 | 95 | it "converts masks with different roots" do 96 | object_mask = { "mask" => { "ipAddress" => {}}, 97 | "mask(duck_type)" => {"webbed" => "feet"}}.to_sl_object_mask 98 | 99 | expect(["[mask[ipAddress],mask(duck_type)[webbed.feet]]", "mask(duck_type)[webbed.feet],[mask[ipAddress]]"].find(object_mask)).to_not be_nil 100 | end 101 | end 102 | -------------------------------------------------------------------------------- /spec/shared_server.rb: -------------------------------------------------------------------------------- 1 | shared_examples_for "server with mutable hostname" do 2 | it "has a method to change the host name" do 3 | expect(server).to respond_to(:set_hostname!) 4 | end 5 | 6 | it "rejects nil hostnames" do 7 | expect { server.set_hostname!(nil) }.to raise_error(ArgumentError) 8 | end 9 | 10 | it "rejects empty hostnames" do 11 | expect { server.set_hostname!("") }.to raise_error(ArgumentError) 12 | end 13 | end 14 | 15 | shared_examples_for "server with port speed" do 16 | it "has a method to change port speed" do 17 | expect(server).to respond_to(:change_port_speed) 18 | end 19 | 20 | it "changes public port speed if no interface is specified" do 21 | expect(server.service.target).to receive(:call_softlayer_api_with_params).with(:setPublicNetworkInterfaceSpeed, instance_of(SoftLayer::APIParameterFilter),[10]) 22 | server.change_port_speed(10) 23 | end 24 | 25 | it "changes public port speed if told to do so" do 26 | expect(server.service.target).to receive(:call_softlayer_api_with_params).with(:setPublicNetworkInterfaceSpeed, instance_of(SoftLayer::APIParameterFilter),[10]) 27 | server.change_port_speed(10, true) 28 | end 29 | 30 | it "changes private port speed if told to change private" do 31 | expect(server.service.target).to receive(:call_softlayer_api_with_params).with(:setPrivateNetworkInterfaceSpeed, instance_of(SoftLayer::APIParameterFilter),[10]) 32 | server.change_port_speed(10, false) 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved. 3 | # 4 | # For licensing information see the LICENSE.md file in the project root. 5 | #++ 6 | 7 | require 'coveralls' 8 | Coveralls.wear! 9 | 10 | require 'json' 11 | 12 | def fixture_from_json(json_file_name) 13 | full_name = File.basename(json_file_name, ".json") + ".json" 14 | JSON.parse(File.read(File.join(File.dirname(__FILE__), "fixtures/#{full_name}"))) 15 | end --------------------------------------------------------------------------------