├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── CONTRIBUTORS.md ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── docs ├── cla-corporate.md └── cla-individual.md ├── examples ├── datacenter.md ├── flavor.md ├── global_ip.md ├── image.md ├── key_pair.md ├── server.md └── vlan.md ├── knife-softlayer.gemspec ├── lib ├── chef │ └── knife │ │ ├── flavor │ │ └── base.rb │ │ ├── softlayer.rb │ │ ├── softlayer_base.rb │ │ ├── softlayer_datacenter_list.rb │ │ ├── softlayer_datacenter_show.rb │ │ ├── softlayer_delete.rb │ │ ├── softlayer_flavor_list.rb │ │ ├── softlayer_global_ip_list.rb │ │ ├── softlayer_image_list.rb │ │ ├── softlayer_key_pair_create.rb │ │ ├── softlayer_key_pair_list.rb │ │ ├── softlayer_list.rb │ │ ├── softlayer_server_create.rb │ │ ├── softlayer_server_destroy.rb │ │ ├── softlayer_server_list.rb │ │ ├── softlayer_server_relaunch.rb │ │ ├── softlayer_vlan_create.rb │ │ ├── softlayer_vlan_list.rb │ │ └── softlayer_vlan_show.rb └── knife-softlayer │ └── version.rb ├── release.md └── spec ├── spec_helper.rb └── unit ├── softlayer_base_spec.rb ├── softlayer_server_create_spec.rb └── softlayer_server_destroy_spec.rb /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | .yardoc 6 | Gemfile.lock 7 | InstalledFiles 8 | _yardoc 9 | coverage 10 | doc/ 11 | lib/bundler/man 12 | pkg 13 | rdoc 14 | spec/reports 15 | test/tmp 16 | test/version_tmp 17 | tmp 18 | .idea* 19 | .ruby-version 20 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | 3 | script: bundle exec rspec spec 4 | 5 | matrix: 6 | fast_finish: true 7 | include: 8 | - rvm: 2.0.0 9 | gemfile: Gemfile 10 | - rvm: 2.1.0 11 | gemfile: Gemfile 12 | - rvm: 2.1.1 13 | gemfile: Gemfile 14 | env: COVERAGE=true 15 | 16 | notifications: 17 | email: matt.eldridge@us.ibm.com 18 | 19 | before_install: 20 | - gem update --system # todo: workaround for https://github.com/rubygems/rubygems/pull/763 21 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # knife-softlayer change log 2 | 3 | ## Unreleased changes 4 | 5 | None. 6 | 7 | ## Last release: 0.0.3 (2014-04-10) 8 | 9 | * 0.1.1 10 | * Reversed default functionality of SAN vs local storage when creating a VM. 11 | * Changed textual references from "CCI" to "VM". 12 | 13 | * 0.0.3 14 | * Update `softlayer_api` dependency to ~> v1.0.8. 15 | * Declare specific user agent string. 16 | 17 | * 0.0.2 Alpha release. 18 | * Can create and bootstrap a SoftLayer VM, destroy the VM, chef client and chef node. 19 | * Support for global IP address creation and assignment. 20 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to knife-softlayer 2 | 3 | We are happy to accept contributions to knife-softlayer. Please follow the guidelines below. 4 | 5 | * Sign our contributor agreement (CLA) You can find the [CLA here](./docs/cla-individual.md). 6 | 7 | * If you're contributing on behalf of your employer we'll need a signed copy of our corporate contributor agreement (CCLA) as well. You can find the [CCLA here](./docs/cla-corporate.md). 8 | 9 | * Fork the repo, make your changes, and open a pull request. 10 | 11 | ## Issue Tracking 12 | 13 | You can file tickets to describe the bug you'd like to fix or feature you'd 14 | like to add on the [knife-softlayer project](https://github.com/softlayer/knife-softlayer/issues) on github. 15 | 16 | ## Testing Instructions 17 | 18 | To run tests, run the following Ruby tool commands from the root of your local copy of 19 | knife-softlayer: 20 | 21 | bundle install 22 | bundle exec rspec spec 23 | 24 | **All tests must pass** before your contribution can be merged. Thus it's a good idea 25 | to execute the tests without your change to be sure you understand how to run 26 | them, as well as after to validate that you've avoided regressions. 27 | 28 | All but the most trivial changes should include **at least one unit test case** to exercise the 29 | new / changed code; please add tests to your pull request in this common case. 30 | 31 | 32 | -------------------------------------------------------------------------------- /CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | * Dave Henderson 2 | * HIGUCHI Daisuke 3 | * Matt Eldridge -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in knife-softlayer.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | © Copyright IBM Corporation 2014. 2 | 3 | LICENSE: 4 | 5 | Apache License, Version 2.0 6 | Apache License 7 | Version 2.0, January 2004 8 | http://www.apache.org/licenses/ 9 | 10 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 11 | 12 | 1. Definitions. 13 | 14 | "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. 15 | 16 | "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. 17 | 18 | "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. 19 | 20 | "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. 21 | 22 | "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. 23 | 24 | "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. 25 | 26 | "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). 27 | 28 | "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. 29 | 30 | "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." 31 | 32 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 33 | 34 | 2. Grant of Copyright License. 35 | 36 | Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 37 | 38 | 3. Grant of Patent License. 39 | 40 | Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 41 | 42 | 4. Redistribution. 43 | 44 | You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: 45 | 46 | You must give any other recipients of the Work or Derivative Works a copy of this License; and 47 | You must cause any modified files to carry prominent notices stating that You changed the files; and 48 | You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and 49 | If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. 50 | You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 51 | 52 | 5. Submission of Contributions. 53 | 54 | Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 55 | 56 | 6. Trademarks. 57 | 58 | This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 59 | 60 | 7. Disclaimer of Warranty. 61 | 62 | Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 63 | 64 | 8. Limitation of Liability. 65 | 66 | In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 67 | 68 | 9. Accepting Warranty or Additional Liability. 69 | 70 | While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. 71 | 72 | END OF TERMS AND CONDITIONS 73 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Knife::Softlayer 2 | [![Gem Version](https://badge.fury.io/rb/knife-softlayer.png)](http://badge.fury.io/rb/knife-softlayer) 3 | [![Build Status](https://api.travis-ci.org/softlayer/knife-softlayer.svg)](https://travis-ci.org/softlayer/knife-softlayer) 4 | [![Dependency Status](https://gemnasium.com/softlayer/knife-softlayer.svg)](https://gemnasium.com/softlayer/knife-softlayer) 5 | 6 | A Chef Knife plugin for launching, bootstrapping, and managing compute instances in the IBM SoftLayer cloud. 7 | 8 | ## Installation 9 | 10 | Add this line to your application's Gemfile: 11 | 12 | gem 'knife-softlayer' 13 | 14 | And then execute: 15 | 16 | $ bundle 17 | 18 | Or install it yourself as: 19 | 20 | $ gem install knife-softlayer 21 | 22 | ## Configuration 23 | Add your SoftLayer username and API key to your `knife.rb` file. 24 | 25 | ```ruby 26 | log_level :info 27 | log_location STDOUT 28 | node_name 'node' 29 | client_key '/path/to/key.pem' 30 | validation_client_name 'some-validator' 31 | validation_key '/path/to/validator.pem' 32 | chef_server_url 'https://example.com/organizations/org' 33 | syntax_check_cache_path '/path/to/syntax_check_cache' 34 | knife[:softlayer_username] = "" 35 | knife[:softlayer_api_key] = "" 36 | ``` 37 | 38 | ## Usage 39 | 40 | See `knife softlayer --help` for more information. 41 | 42 | EXAMPLES: 43 | 44 | 45 | ```bash 46 | # look at some options 47 | user@local> knife softlayer flavor list [--all] 48 | ``` 49 | 50 | ```bash 51 | # the minimum number of pieces of flare 52 | user@local> knife softlayer server create --hostname test --domain example.com --flavor tiny 53 | ``` 54 | 55 | ```bash 56 | # being sort of specific about things 57 | user@local> knife softlayer server create -H test -D example.com \ 58 | --block-storage 0:25,2:100,5:1000 \ # device:GB, device:GB, ... 59 | --network-interface-speed 1000 \ 60 | --cores 8 \ 61 | --ram 49152 \ 62 | --os-code REDHAT_6_64 \ 63 | --datacenter ams01 \ 64 | --node-name random-node-name \ 65 | --assign-global-ip \ 66 | --run-list 'recipe[apt],recipe[git],recipe[rbenv],recipe[memcached],recipe[redis]' 67 | ``` 68 | 69 | #### Legal stuff 70 | Use of this software requires runtime dependencies. Those dependencies and their respective software licenses are listed below. 71 | 72 | * [net-ssh](https://github.com/net-ssh/net-ssh/) - LICENSE: [MIT](https://github.com/net-ssh/net-ssh/blob/master/LICENSE.txt) 73 | * [softlayer_api](https://github.com/softlayer/softlayer-api-ruby-client) - LICENSE: [MIT](https://github.com/softlayer/softlayer-api-ruby-client/blob/master/LICENSE.textile) 74 | * [knife-windows](https://github.com/opscode/knife-windows) - LICENSE: [Apache v2](https://github.com/opscode/knife-windows/blob/master/LICENSE) 75 | 76 | 77 | -- 78 | > Author:: Matt Eldridge () 79 | > 80 | > © Copyright IBM Corporation 2014. 81 | > 82 | > LICENSE: Apache 2.0 (http://www.apache.org/licenses/) 83 | 84 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | 3 | require 'bundler' 4 | Bundler::GemHelper.install_tasks 5 | 6 | require 'rdoc/task' 7 | 8 | begin 9 | require 'sdoc' 10 | require 'rdoc/task' 11 | 12 | RDoc::Task.new do |rdoc| 13 | rdoc.title = "Chef Ruby API Documentation" 14 | rdoc.main = "README.rdoc" 15 | rdoc.options << '--fmt' << 'shtml' # explictly set shtml generator 16 | rdoc.template = 'direct' # lighter template 17 | rdoc.rdoc_files.include("README.rdoc", "LICENSE", "spec/tiny_server.rb", "lib/**/*.rb") 18 | rdoc.rdoc_dir = "rdoc" 19 | end 20 | rescue LoadError 21 | puts "sdoc is not available. (sudo) gem install sdoc to generate rdoc documentation." 22 | end 23 | 24 | begin 25 | require 'rspec/core/rake_task' 26 | 27 | task :default => :spec 28 | 29 | desc "Run all specs in spec directory" 30 | RSpec::Core::RakeTask.new(:spec) do |t| 31 | t.pattern = 'spec/unit/**/*_spec.rb' 32 | end 33 | 34 | rescue LoadError 35 | STDERR.puts "\n*** RSpec not available. (sudo) gem install rspec to run unit tests. ***\n\n" 36 | end 37 | 38 | -------------------------------------------------------------------------------- /docs/cla-corporate.md: -------------------------------------------------------------------------------- 1 | 2 | #### International Business machines, Inc. 3 | #####Software Grant and Corporate Contributor License Agreement ("Agreement") 4 | 5 | http://github.com/softlayer/knife-softlayer 6 | 7 | 8 | Thank you for your interest in IBM’s knife-softlayer project (“the 9 | Project"). In order to clarify the intellectual property license granted with Contributions from any person or entity, IBM must have a Contributor License Agreement (CLA) on file that has been signed by each Contributor, indicating agreement to the license terms 10 | below. This license is for your protection as a Contributor as well 11 | as the protection of IBM and its users; it does not change your rights to use your own Contributions for any other purpose. 12 | 13 | This version of the Agreement allows an entity (the "Corporation") to submit Contributions to the Project, to authorize Contributions 14 | submitted by its designated employees to the Project, and to grant 15 | copyright and patent licenses thereto 16 | 17 | If you have not already done so, please complete and sign, then scan and email a pdf file of this Agreement to pjackson@softlayer.com. 18 | 19 | 20 | 21 | Please read this document carefully before signing and keep a copy for your records. 22 | 23 | Corporation name: ________________________________________________ 24 | 25 | Corporation address: ________________________________________________ 26 | 27 | Point of Contact: ________________________________________________ 28 | 29 | E-Mail: ________________________________________________ 30 | 31 | Telephone: _____________________ 32 | 33 | 34 | You accept and agree to the following terms and conditions for Your 35 | present and future Contributions submitted to the Project. Except 36 | for the license granted herein to IBM and recipients of software distributed by IBM, You reserve all right, title, and interest in and to Your Contributions. 37 | 38 | 1. Definitions. 39 | 40 | "You" (or "Your") shall mean the copyright owner or legal entity 41 | authorized by the copyright owner that is making this Agreement 42 | with IBM. For legal entities, the entity making a Contribution and 43 | all other entities that control, are controlled by, or are under 44 | common control with that entity are considered to be a single 45 | Contributor. For the purposes of this definition, "control" means 46 | (i) the power, direct or indirect, to cause the direction or 47 | management of such entity, whether by contract or otherwise, or 48 | (ii) ownership of fifty percent (50%) or more of the outstanding 49 | shares, or (iii) beneficial ownership of such entity. 50 | 51 | "Contribution" shall mean the code, documentation or other original 52 | works of authorship expressly identified in Schedule B, as well as 53 | any original work of authorship, including any modifications or 54 | additions to an existing work, that is intentionally submitted by You to IBM for inclusion in, or documentation of, the Project managed by IBM (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to IBM or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, IBM for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution." 55 | 56 | 2. Grant of Copyright License. Subject to the terms and conditions 57 | of this Agreement, You hereby grant to IBM and to 58 | recipients of software distributed by IBM a perpetual, 59 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 60 | copyright license to reproduce, prepare derivative works of, 61 | publicly display, publicly perform, sublicense, and distribute 62 | Your Contributions and such derivative works. 63 | 64 | 3. Grant of Patent License. Subject to the terms and conditions of 65 | this Agreement, You hereby grant to IBM and to recipients 66 | of software distributed by IBM a perpetual, worldwide, 67 | non-exclusive, no-charge, royalty-free, irrevocable (except as 68 | stated in this section) patent license to make, have made, use, 69 | offer to sell, sell, import, and otherwise transfer the Work, 70 | where such license applies only to those patent claims licensable 71 | by You that are necessarily infringed by Your Contribution(s) 72 | alone or by combination of Your Contribution(s) with the Work to 73 | which such Contribution(s) were submitted. If any entity institutes 74 | patent litigation against You or any other entity (including a 75 | cross-claim or counterclaim in a lawsuit) alleging that your 76 | Contribution, or the Work to which you have contributed, constitutes 77 | direct or contributory patent infringement, then any patent licenses 78 | granted to that entity under this Agreement for that Contribution or 79 | Work shall terminate as of the date such litigation is filed. 80 | 81 | 4. You represent that You are legally entitled to grant the above 82 | license. You represent further that each employee of the 83 | Corporation designated on Schedule A below (or in a subsequent 84 | written modification to that Schedule) is authorized to submit 85 | Contributions on behalf of the Corporation. 86 | 87 | 5. You represent that each of Your Contributions is Your original 88 | creation (see section 7 for submissions on behalf of others). 89 | 90 | 6. You are not expected to provide support for Your Contributions, 91 | except to the extent You desire to provide support. You may provide 92 | support for free, for a fee, or not at all. Unless required by 93 | applicable law or agreed to in writing, You provide Your 94 | Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 95 | OF ANY KIND, either express or implied, including, without 96 | limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, 97 | MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. 98 | 99 | 7. Should You wish to submit work that is not Your original creation, 100 | You may submit it to IBM separately from any 101 | Contribution, identifying the complete details of its source and 102 | of any license or other restriction (including, but not limited 103 | to, related patents, trademarks, and license agreements) of which 104 | you are personally aware, and conspicuously marking the work as 105 | "Submitted on behalf of a third-party: [named here]". 106 | 107 | 8. It is your responsibility to notify IBM when any change 108 | is required to the list of designated employees authorized to submit 109 | Contributions on behalf of the Corporation, or to the Corporation's 110 | Point of Contact with IBM. 111 | 112 | 113 | 114 | Please sign: __________________________________ Date: _______________ 115 | 116 | Title: __________________________________ 117 | 118 | Corporation: __________________________________ 119 | 120 | 121 | Schedule A 122 | 123 | [Initial list of designated employees. NB: authorization is not 124 | tied to particular Contributions.] 125 | 126 | 127 | 128 | 129 | Schedule B 130 | 131 | [Identification of optional concurrent software grant. Would be 132 | left blank or omitted if there is no concurrent software grant.] 133 | 134 | -------------------------------------------------------------------------------- /docs/cla-individual.md: -------------------------------------------------------------------------------- 1 | #### International Business Machines, Inc. (IBM) 2 | #####Individual Contributor License Agreement ("Agreement") 3 | 4 | http://www.github.com/softlayer/knife-softlayer 5 | 6 | Thank you for your interest in the knife-softlayer project ("the Project"). 7 | 8 | In order to clarify the intellectual property license granted with Contributions from any person or entity, IBM must have a Contributor License Agreement ("CLA") on file that has been signed by each Contributor, indicating agreement to the license terms below. This license is for your protection as a Contributor as well as the protection of IBM and its customers; it does not change your rights to use your own Contributions for any other purpose. 9 | 10 | If you have not already done so, please complete and sign, then scan and email a pdf file of this Agreement to pjackson@softlayer.com 11 | 12 | Please read this document carefully before signing and keep a copy for your records. 13 | 14 | Full name: ______________________________________________________ 15 | 16 | (optional) Public name: _________________________________________ 17 | 18 | Mailing Address: ________________________________________________ 19 | 20 | Country: ______________________________________________________ 21 | 22 | Telephone: ______________________________________________________ 23 | 24 | E-Mail: ______________________________________________________ 25 | 26 | 27 | You accept and agree to the following terms and conditions for Your present and future Contributions submitted to the Project. Except for the license granted herein to IBM and recipients of software distributed by IBM, You reserve all right, title, and interest in and to Your Contributions. 28 | 29 | 1. Definitions. 30 | 31 | "You" (or "Your") shall mean the copyright owner or legal entity 32 | authorized by the copyright owner that is making this Agreement 33 | with IBM. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. 34 | 35 | "Contribution" shall mean any original work of authorship, 36 | including any modifications or additions to an existing work, that 37 | is intentionally submitted by You to the Project for inclusion 38 | in, or documentation of, the Project (”the Work”). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Project or its representatives,including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Project for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution." 39 | 40 | 2. Grant of Copyright License. Subject to the terms and conditions of 41 | this Agreement, You hereby grant to IBM and to recipients of software distributed by IBM a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works. 42 | 43 | 3. Grant of Patent License. Subject to the terms and conditions of 44 | this Agreement, You hereby grant to IBM and to recipients of software distributed by IBM a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work to which Your Contribution(s) were submitted, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed. 45 | 46 | 4. You represent that you are legally entitled to grant the above 47 | license. If your employer(s) has rights to intellectual property 48 | that you create that includes your Contributions, you represent 49 | that you have received permission to make Contributions on behalf 50 | of that employer, that your employer has waived such rights for 51 | your Contributions to the Project, or that your employer has 52 | executed a separate Corporate CLA with IBM. 53 | 54 | 5. You represent that each of Your Contributions is Your original 55 | creation (see section 7 for submissions on behalf of others). You 56 | represent that Your Contribution submissions include complete 57 | details of any third-party license or other restriction (including, 58 | but not limited to, related patents and trademarks) of which you 59 | are personally aware and which are associated with any part of Your 60 | Contributions. 61 | 62 | 6. You are not expected to provide support for Your Contributions, 63 | except to the extent You desire to provide support. You may provide 64 | support for free, for a fee, or not at all. Unless required by 65 | applicable law or agreed to in writing, You provide Your 66 | Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 67 | OF ANY KIND, either express or implied, including, without 68 | limitation, any warranties or conditions of TITLE, NON- 69 | INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. 70 | 71 | 7. Should You wish to submit work that is not Your original creation, 72 | You may submit it to the Project separately from any 73 | Contribution, identifying the complete details of its source and of 74 | any license or other restriction (including, but not limited to, 75 | related patents, trademarks, and license agreements) of which you 76 | are personally aware, and conspicuously marking the work as 77 | "Submitted on behalf of a third-party: [named here]". 78 | 79 | 8. You agree to notify IBM of any facts or circumstances of 80 | which you become aware that would make these representations 81 | inaccurate in any respect. 82 | 83 | Please sign: __________________________________ Date: ________________ 84 | 85 | -------------------------------------------------------------------------------- /examples/datacenter.md: -------------------------------------------------------------------------------- 1 | ### Datacenter Examples 2 | 3 | These examples all assume you have set your SoftLayer username and api key in `~/.chef/knife.rb` like this: 4 | 5 | ```ruby 6 | knife[:softlayer_username] = 'example_user' 7 | knife[:softlayer_api_key] = '1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a' 8 | ``` 9 | 10 | #### List all available datacenters 11 | 12 | ```sh 13 | user@localhost> knife softlayer datacenter list 14 | +-------+--------------+ 15 | | name | long_name | 16 | +-------+--------------+ 17 | | ams01 | Amsterdam 1 | 18 | +-------+--------------+ 19 | | wdc03 | Ashburn 3 | 20 | +-------+--------------+ 21 | | dal01 | Dallas 1 | 22 | +-------+--------------+ 23 | | dal02 | Dallas 2 | 24 | +-------+--------------+ 25 | | dal04 | Dallas 4 | 26 | +-------+--------------+ 27 | | dal05 | Dallas 5 | 28 | +-------+--------------+ 29 | | dal06 | Dallas 6 | 30 | +-------+--------------+ 31 | | dal07 | Dallas 7 | 32 | +-------+--------------+ 33 | | hkg02 | Hong Kong 2 | 34 | +-------+--------------+ 35 | | hou02 | Houston 2 | 36 | +-------+--------------+ 37 | | lon02 | London 2 | 38 | +-------+--------------+ 39 | | sjc01 | San Jose 1 | 40 | +-------+--------------+ 41 | | sea01 | Seattle | 42 | +-------+--------------+ 43 | | sng01 | Singapore 1 | 44 | +-------+--------------+ 45 | | tor01 | Toronto 1 | 46 | +-------+--------------+ 47 | | wdc01 | Washington 1 | 48 | +-------+--------------+ 49 | 50 | ``` 51 | 52 | #### Show a particular datacenter 53 | ```sh 54 | user@localhost> knife softlayer datacenter show tor01 55 | Long Name: Toronto 1 56 | Name: tor01 57 | Routers: 58 | +--------------+--------+ 59 | | hostname | id | 60 | +--------------+--------+ 61 | | bcr01a.tor01 | 266212 | 62 | +--------------+--------+ 63 | | fcr01a.tor01 | 266412 | 64 | +--------------+--------+ 65 | 66 | user@localhost> knife softlayer datacenter show wdc01 67 | Long Name: Washington 1 68 | Name: wdc01 69 | Routers: 70 | +--------------+--------+ 71 | | hostname | id | 72 | +--------------+--------+ 73 | | bcr01.wdc01 | 16358 | 74 | +--------------+--------+ 75 | | bcr02.wdc01 | 40379 | 76 | +--------------+--------+ 77 | | bcr03a.wdc01 | 85816 | 78 | +--------------+--------+ 79 | | bcr04a.wdc01 | 180611 | 80 | +--------------+--------+ 81 | | bcr05a.wdc01 | 235754 | 82 | +--------------+--------+ 83 | | fcr01.wdc01 | 16357 | 84 | +--------------+--------+ 85 | | fcr02.wdc01 | 40378 | 86 | +--------------+--------+ 87 | | fcr03a.wdc01 | 85814 | 88 | +--------------+--------+ 89 | | fcr04a.wdc01 | 180610 | 90 | +--------------+--------+ 91 | | fcr05a.wdc01 | 235748 | 92 | +--------------+--------+ 93 | 94 | ``` 95 | -------------------------------------------------------------------------------- /examples/flavor.md: -------------------------------------------------------------------------------- 1 | ### Flavor examples 2 | 3 | These examples all assume you have set your SoftLayer username and api key in `~/.chef/knife.rb` like this: 4 | 5 | ```ruby 6 | knife[:softlayer_username] = 'example_user' 7 | knife[:softlayer_api_key] = '1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a' 8 | ``` 9 | 10 | #### List available flavors 11 | 12 | ```sh 13 | user@localhost> knife softlayer flavor list 14 | +-----------+-----+-------+--------------------------------------------------+ 15 | | id | cpu | ram | disk | 16 | +-----------+-----+-------+--------------------------------------------------+ 17 | | m1.tiny | 2 | 1024 | [{"device"=>0, "diskImage"=>{"capacity"=>25}}] | 18 | +-----------+-----+-------+--------------------------------------------------+ 19 | | m1.small | 2 | 2048 | [{"device"=>0, "diskImage"=>{"capacity"=>100}}] | 20 | +-----------+-----+-------+--------------------------------------------------+ 21 | | m1.medium | 4 | 4096 | [{"device"=>0, "diskImage"=>{"capacity"=>500}}] | 22 | +-----------+-----+-------+--------------------------------------------------+ 23 | | m1.large | 8 | 8192 | [{"device"=>0, "diskImage"=>{"capacity"=>750}}] | 24 | +-----------+-----+-------+--------------------------------------------------+ 25 | | m1.xlarge | 16 | 16384 | [{"device"=>0, "diskImage"=>{"capacity"=>1000}}] | 26 | +-----------+-----+-------+--------------------------------------------------+ 27 | NOTICE: 28 | 'flavors' provided here for convenience; SoftLayer allows you to choose a configuration a la carte. 29 | For a full list of available instance options use --all with the `knife softlayer flavor list` subcommand. 30 | ``` 31 | 32 | #### List available *a la carte* options 33 | 34 | ```sh 35 | user@localhost> knife softlayer flavor list --all 36 | | CORES | RAM | DISK | OS | NETWORK [MBS] | DATACENTER 37 | | ========== | ========== | ========== | ========== | ========== | ========== 38 | | 1 x 2.0 GHz Core | 1024 [1 GB] | 1,000 GB (SAN) | CENTOS_LATEST | 10 | ams01 39 | | 2 x 2.0 GHz Cores | 2048 [2 GB] | 1,000 GB (SAN) | CENTOS_LATEST_64 | 100 | dal01 40 | | 4 x 2.0 GHz Cores | 4096 [4 GB] | 1,000 GB (SAN) | CENTOS_LATEST_32 | 1000 | dal05 41 | | 8 x 2.0 GHz Cores | 6144 [6 GB] | 1,000 GB (SAN) | CENTOS_6_64 | | dal06 42 | | 12 x 2.0 GHz Cores | 8192 [8 GB] | 1,500 GB (SAN) | CENTOS_6_32 | | hkg02 43 | | 16 x 2.0 GHz Cores | 12288 [12 GB] | 1,500 GB (SAN) | CENTOS_5_64 | | lon02 44 | | Private 1 x 2.0 GHz Core | 16384 [16 GB] | 1,500 GB (SAN) | CENTOS_5_32 | | sea01 45 | | Private 2 x 2.0 GHz Cores | 32768 [32 GB] | 1,500 GB (SAN) | CLOUDLINUX_LATEST | | sjc01 46 | | Private 4 x 2.0 GHz Cores | 49152 [48 GB] | 10 GB (SAN) | CLOUDLINUX_LATEST_64 | | sng01 47 | | Private 8 x 2.0 GHz Cores | 65536 [64 GB] | 10 GB (SAN) | CLOUDLINUX_LATEST_32 | | tor01 48 | | | | 10 GB (SAN) | CLOUDLINUX_6_64 | | wdc01 49 | | | | 10 GB (SAN) | CLOUDLINUX_6_32 | | 50 | | | | 100 GB (LOCAL) | CLOUDLINUX_5_64 | | 51 | | | | 100 GB (LOCAL) | CLOUDLINUX_5_32 | | 52 | | | | 100 GB (SAN) | DEBIAN_LATEST | | 53 | | | | 100 GB (SAN) | DEBIAN_LATEST_64 | | 54 | | | | 100 GB (SAN) | DEBIAN_LATEST_32 | | 55 | | | | 100 GB (SAN) | DEBIAN_7_64 | | 56 | | | | 100 GB (SAN) | DEBIAN_7_32 | | 57 | | | | 125 GB (SAN) | DEBIAN_6_64 | | 58 | # LIST HAS BEEN TRUNCATED FOR THIS EXAMPLE 59 | ``` -------------------------------------------------------------------------------- /examples/global_ip.md: -------------------------------------------------------------------------------- 1 | ### Global IP examples 2 | 3 | These examples all assume you have set your SoftLayer username and api key in `~/.chef/knife.rb` like this: 4 | 5 | ```ruby 6 | knife[:softlayer_username] = 'example_user' 7 | knife[:softlayer_api_key] = '1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a' 8 | ``` 9 | 10 | #### List global ip addresses 11 | 12 | ```sh 13 | user@localhost> knife softlayer global ip list 14 | +-----------------+-----------------+ 15 | | address | destination | 16 | +-----------------+-----------------+ 17 | | 108.168.254.93 | 173.192.218.117 | 18 | +-----------------+-----------------+ 19 | | 108.168.254.96 | NOT ROUTED | 20 | +-----------------+-----------------+ 21 | | 108.168.254.168 | NOT ROUTED | 22 | +-----------------+-----------------+ 23 | ``` -------------------------------------------------------------------------------- /examples/image.md: -------------------------------------------------------------------------------- 1 | ### Server Image examples 2 | 3 | These examples all assume you have set your SoftLayer username and api key in `~/.chef/knife.rb` like this: 4 | 5 | ```ruby 6 | knife[:softlayer_username] = 'example_user' 7 | knife[:softlayer_api_key] = '1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a' 8 | ``` 9 | 10 | #### List server images 11 | 12 | ```sh 13 | user@localhost> knife softlayer image list 14 | +--------------------------------------+--------+--------------------------------------------------------------------------------+ 15 | | id | access | name | 16 | +--------------------------------------+--------+--------------------------------------------------------------------------------+ 17 | | 29073a7a-2fac-405c-b59f-4de8ad6e4945 | PRIVATE | My Golden Image | 18 | +--------------------------------------+--------+--------------------------------------------------------------------------------+ 19 | | ebb5664c-0471-41cb-bbdf-3de557fb8ed1 | PRIVATE | My Other Golden Image | 20 | +--------------------------------------+--------+--------------------------------------------------------------------------------+ 21 | | 1176d22b-176a-499a-8d94-f9aaf29155a3 | PUBLIC | 100G CentOS 5 64-bit | 22 | +--------------------------------------+--------+--------------------------------------------------------------------------------+ 23 | | 3d9be3bc-267a-4441-bb62-25a73fe62642 | PUBLIC | 100G CentOS 5 32-bit | 24 | +--------------------------------------+--------+--------------------------------------------------------------------------------+ 25 | | 54f4ed9f-f7e6-4fe6-8b54-3a7faacd82b3 | PUBLIC | 25G CentOS 6 64-bit | 26 | +--------------------------------------+--------+--------------------------------------------------------------------------------+ 27 | | ca668dc5-290d-4fdb-9427-deb9da797962 | PUBLIC | 25G CentOS 6 32-bit | 28 | +--------------------------------------+--------+--------------------------------------------------------------------------------+ 29 | | f0254ca6-14b5-44f3-84f8-ae5d3bedb77a | PUBLIC | 100G CentOS 6 32-bit | 30 | +--------------------------------------+--------+--------------------------------------------------------------------------------+ 31 | | c27eb0ad-bddd-44c7-a37a-e3ddbbfed277 | PUBLIC | 100G CentOS 6 64-bit | 32 | +--------------------------------------+--------+--------------------------------------------------------------------------------+ 33 | | cc30a0a6-b0f1-4db1-82d4-215977aba61d | PUBLIC | 25G RedHat 6 64-bit | 34 | +--------------------------------------+--------+--------------------------------------------------------------------------------+ 35 | ``` -------------------------------------------------------------------------------- /examples/key_pair.md: -------------------------------------------------------------------------------- 1 | ### Key Pair Examples 2 | 3 | These examples all assume you have set your SoftLayer username and api key in `~/.chef/knife.rb` like this: 4 | 5 | ```ruby 6 | knife[:softlayer_username] = 'example_user' 7 | knife[:softlayer_api_key] = '1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a' 8 | ``` 9 | 10 | #### Create a Key Pair 11 | 12 | ```sh 13 | user@localhost> knife softlayer key pair create 14 | Enter the label for this key pair: 15 | ``` 16 | 17 | ```sh 18 | user@localhost> knife softlayer key pair create 19 | Enter the label for this key pair: my-new-key-pair 20 | Enter path to the public key: 21 | ``` 22 | 23 | ```sh 24 | user@localhost> knife softlayer key pair create 25 | Enter the label for this key pair: my-new-key-pair 26 | Enter path to the public key: /Users/user/.ssh/my_key_rsa.pub 27 | ``` 28 | 29 | ```sh 30 | user@localhost> knife softlayer key pair create 31 | Enter the label for this key pair: my-new-key-pair 32 | Enter path to the public key: /Users/user/.ssh/my_key_rsa.pub 33 | Key pair successfully created. Provisioning may take a few minutes to complete. 34 | Key pair ID is: 31246 35 | ``` 36 | 37 | 38 | 39 | 40 | #### List Key Pairs 41 | 42 | ```sh 43 | user@localhost> knife softlayer key pair list 44 | +-------+------------------+---------------------------+-------------+ 45 | | id | label | create_date | modify_date | 46 | +-------+------------------+---------------------------+-------------+ 47 | | 30846 | Key1 | 2014-01-01T17:05:33-05:00 | | 48 | +-------+------------------+---------------------------+-------------+ 49 | | 30946 | Key2 | 2014-02-24T11:36:43-05:00 | | 50 | +-------+------------------+---------------------------+-------------+ 51 | | 31046 | Key3 | 2014-03-24T11:35:59-05:00 | | 52 | +-------+------------------+---------------------------+-------------+ 53 | | 31146 | Key4 | 2014-04-10T14:48:35-05:00 | | 54 | +-------+------------------+---------------------------+-------------+ 55 | | 31246 | my-new-key-pair | 2014-05-29T11:53:17-05:00 | | 56 | +-------+------------------+---------------------------+-------------+ 57 | ``` -------------------------------------------------------------------------------- /examples/server.md: -------------------------------------------------------------------------------- 1 | ### Server Examples 2 | 3 | These examples all assume you have set your SoftLayer username and api key in `~/.chef/knife.rb` like this: 4 | 5 | ```ruby 6 | knife[:softlayer_username] = 'example_user' 7 | knife[:softlayer_api_key] = '1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a' 8 | ``` 9 | 10 | #### Create server and bootstrap as a chef node 11 | 12 | Create a server using an image, a flavor, assign an existing global ip address, and inject two ssh keys. 13 | 14 | ```sh 15 | user@localhost> knife softlayer server create --image-id 23f7f05f-3657-4330-8772-329ed2e816bc --assign-global-ip 108.168.254.41 --ssh-keys 12345 23456 --hostname web --domain ibm.com --flavor m1.tiny --run-list 'recipe[redis],recipe[rbenv],recipe[apt]' -i /Users/user/.ssh/my_private_key_rsa 16 | ``` 17 | 18 | Create a server, provision a new global ip address and assign it to the server, tag both the chef node and the SoftLayer server with the passed tags. 19 | 20 | ```sh 21 | user@localhost> knife softlayer server create --new-global-ip v4 --tags job=task,private=true --hostname web --domain ibm.com --flavor m1.tiny --run-list 'recipe[redis],recipe[rbenv],recipe[apt]' --ssh-keys 12345 -i /Users/user/.ssh/my_private_key_rsa 22 | ``` 23 | 24 | Create a server located in specific public and private VLANS. 25 | 26 | ```sh 27 | user@localhost> knife softlayer server create --vlan 12345 --private-vlan 23456 --hostname web --domain ibm.com --flavor m1.tiny --run-list 'recipe[redis],recipe[rbenv],recipe[apt]' --ssh-keys 12345 -i /Users/user/.ssh/my_private_key_rsa --new-global-ip v4 --tags job=task,private=true 28 | ``` 29 | 30 | Create a server specifying options from a JSON file. 31 | 32 | ```sh 33 | user@localhost> cat /Users/user/bootstrap.json 34 | { 35 | "hostname": "web", 36 | "domain": "ibm.com", 37 | "flavor": "m1.xlarge", 38 | "run-list": "recipe[redis],recipe[rbenv],recipe[apt]", 39 | "ssh-keys": "73148", 40 | "identity-file": "/Users/user/.ssh/my_softlayer_key_rsa", 41 | "image-id": "23f7f05f-3657-4330-8772-329ed2e816bc", 42 | "user_data": "first_boot=disabled" 43 | } 44 | 45 | user@localhost> knife softlayer server create --from-file /Users/user/bootstrap.json 46 | 47 | # options file + additional arguments 48 | user@localhost> knife softlayer server create --from-file /Users/user/bootstrap.json --tags job=task,private=true 49 | ``` 50 | 51 | Other options. 52 | 53 | ```sh 54 | user@localhost> knife softlayer server create --help 55 | # There are a lot... 56 | ``` 57 | 58 | #### Destroy a server and its chef node and client 59 | 60 | Use the node name as an identifier. 61 | 62 | ```sh 63 | user@localhost> knife softlayer server destroy --node-name some-node-name 64 | Decommissioning SoftLayer VM, this may take a few minutes. 65 | WARNING: Deleted node some-node-name 66 | Chef node successfully deleted. 67 | WARNING: Deleted client some-node-name 68 | Chef client successfully deleted. 69 | SoftLayer VM successfully deleted. You are no longer being billed for this instance. 70 | ``` 71 | 72 | Use the public ip address as an identifier 73 | 74 | ```sh 75 | user@localhost> knife softlayer server destroy --ip-address 33.33.33.33 76 | Decommissioning SoftLayer VM, this may take a few minutes. 77 | WARNING: Deleted node 5849421 78 | Chef node successfully deleted. 79 | WARNING: Deleted client 5849421 80 | Chef client successfully deleted. 81 | SoftLayer VM successfully deleted. You are no longer being billed for this instance. 82 | ``` 83 | -------------------------------------------------------------------------------- /examples/vlan.md: -------------------------------------------------------------------------------- 1 | ### VLAN examples 2 | 3 | These examples all assume you have set your SoftLayer username and api key in `~/.chef/knife.rb` like this: 4 | 5 | ```ruby 6 | knife[:softlayer_username] = 'example_user' 7 | knife[:softlayer_api_key] = '1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a' 8 | ``` 9 | 10 | #### Create a new VLAN 11 | 12 | ```sh 13 | user@localhost> knife softlayer vlan create 14 | Enter a vlan name: 15 | ``` 16 | 17 | ```sh 18 | user@localhost> knife softlayer vlan create 19 | Enter a vlan name: my-new-vlan 20 | Enter a datacenter name: 21 | ``` 22 | 23 | ```sh 24 | user@localhost> knife softlayer vlan create 25 | Enter a vlan name: my-new-vlan 26 | Enter a datacenter name: wdc01 27 | Enter a router hostname: 28 | ``` 29 | 30 | ```sh 31 | user@localhost> knife softlayer vlan create 32 | Enter a vlan name: my-new-vlan 33 | Enter a datacenter name: wdc01 34 | Enter a router hostname: bcr05a.wdc01 35 | Enter a network space:[PUBLIC] 36 | ``` 37 | 38 | ```sh 39 | user@localhost> knife softlayer vlan create 40 | Enter a vlan name: my-new-vlan 41 | Enter a datacenter name: wdc01 42 | Enter a router hostname: bcr05a.wdc01 43 | Enter a network space:[PUBLIC] PRIVATE # routers that start with bcr are for private VLANS; fcr are for public VLANS 44 | VLAN successfully created. Provisioning may take a few minutes to complete. 45 | ``` 46 | 47 | #### List VLANS 48 | 49 | ```sh 50 | user@localhost> knife softlayer vlan list 51 | +--------+----------------------+--------------+---------------+--------------+ 52 | | id | name | datacenter | network_space | router | 53 | +--------+----------------------+--------------+---------------+--------------+ 54 | | 379384 | [none] | Washington 1 | PUBLIC | fcr02.wdc01 | 55 | +--------+----------------------+--------------+---------------+--------------+ 56 | | 379386 | [none] | Washington 1 | PRIVATE | bcr02.wdc01 | 57 | +--------+----------------------+--------------+---------------+--------------+ 58 | | 563298 | my-new-vlan | Washington 1 | PRIVATE | bcr02.wdc01 | 59 | +--------+----------------------+--------------+---------------+--------------+ 60 | | 339652 | [none] | San Jose 1 | PUBLIC | fcr01a.sjc01 | 61 | +--------+----------------------+--------------+---------------+--------------+ 62 | | 339654 | [none] | San Jose 1 | PRIVATE | bcr01a.sjc01 | 63 | +--------+----------------------+--------------+---------------+--------------+ 64 | | 557984 | [none] | Dallas 5 | PUBLIC | fcr02a.dal05 | 65 | +--------+----------------------+--------------+---------------+--------------+ 66 | | 557986 | [none] | Dallas 5 | PRIVATE | bcr02a.dal05 | 67 | +--------+----------------------+--------------+---------------+--------------+ 68 | ``` 69 | 70 | #### Show a particular VLAN 71 | 72 | ```sh 73 | user@localhost> knife softlayer vlan show 563298 74 | ID: 563298 75 | Name: my-new-vlan 76 | Datacenter: wdc01 77 | Network Space: PRIVATE 78 | Router: bcr02.wdc01 79 | Subnets: 80 | +--------+------+---------------+---------------+---------------+---------+------------+------------+ 81 | | id | cidr | gateway_ip | network_id | broadcast | type | datacenter | ip_version | 82 | +--------+------+---------------+---------------+---------------+---------+------------+------------+ 83 | | 572522 | 30 | 10.32.181.241 | 10.32.181.240 | 10.32.181.243 | PRIMARY | wdc01 | 4 | 84 | +--------+------+---------------+---------------+---------------+---------+------------+------------+ 85 | ``` 86 | -------------------------------------------------------------------------------- /knife-softlayer.gemspec: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Matt Eldridge () 3 | # © Copyright IBM Corporation 2014. 4 | # 5 | # LICENSE: Apache 2.0 (http://www.apache.org/licenses/) 6 | # 7 | 8 | # coding: utf-8 9 | lib = File.expand_path('../lib', __FILE__) 10 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 11 | require 'knife-softlayer/version' 12 | 13 | Gem::Specification.new do |spec| 14 | spec.name = "knife-softlayer" 15 | spec.version = Knife::Softlayer::VERSION 16 | spec.authors = ["Matt Eldridge"] 17 | spec.email = ["matt.eldridge@us.ibm.com"] 18 | spec.summary = %q{SoftLayer VM support for Chef's knife utility.} 19 | spec.description = %q{A knife plugin for launching and bootstrapping instances in the IBM SoftLayer cloud.} 20 | spec.homepage = "https://github.com/SoftLayer/knife-softlayer" 21 | spec.license = "Apache 2.0" 22 | 23 | spec.files = `git ls-files -z`.split("\x0") 24 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } 25 | spec.test_files = spec.files.grep(%r{^(spec|spec|features)/}) 26 | spec.require_paths = ["lib"] 27 | 28 | spec.add_dependency "fog-softlayer", "~> 1.1.4" 29 | spec.add_dependency "knife-windows", "> 0.5.12" 30 | spec.add_dependency "net-ssh", "> 2.8.0" 31 | 32 | spec.add_development_dependency "mixlib-config", "~>2.0" 33 | spec.add_development_dependency "chef", ">=0.10.10" 34 | spec.add_development_dependency "rspec", "~>2.14" 35 | spec.add_development_dependency "rake", "~>10.1" 36 | spec.add_development_dependency "sdoc", "~>0.3" 37 | spec.add_development_dependency "bundler", "~>1.5" 38 | spec.add_development_dependency "osrcry" 39 | end 40 | -------------------------------------------------------------------------------- /lib/chef/knife/flavor/base.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Matt Eldridge () 3 | # © Copyright IBM Corporation 2014. 4 | # 5 | # LICENSE: Apache 2.0 (http://www.apache.org/licenses/) 6 | # 7 | 8 | require 'chef/knife' 9 | 10 | class Chef 11 | class Knife 12 | module SoftlayerFlavorBase 13 | 14 | ## 15 | # Build table of all VM configuration options. 16 | # @return [Hash] 17 | def options_table 18 | columns = [ 19 | "| CORES", 20 | "| RAM", 21 | "| DISK", 22 | "| OS", 23 | "| NETWORK [MBS]", 24 | "| DATACENTER", 25 | ] 26 | 27 | 6.times { columns << '| ========== ' } 28 | 29 | opts = connection.request(:virtual_guest, :get_create_object_options).body 30 | cpu = opts['processors'] 31 | ram = opts['memory'] 32 | disk = opts['blockDevices'].sort_by{|d| d['itemPrice']['item']['description'] unless d['itemPrice'].nil? } 33 | os = opts['operatingSystems'] 34 | net = opts['networkComponents'] 35 | datacenter = opts['datacenters'] 36 | 37 | i = 0 38 | until i >= opts.keys.map{|key| opts[key].count }.sort.last do 39 | columns << (cpu[i].nil? ? '| ' : '| ' + cpu[i]['itemPrice']['item']['description']) 40 | columns << (ram[i].nil? ? '| ' : '| ' + ram[i]['template']['maxMemory'].to_s + " [#{ram[i]['itemPrice']['item']['description']}]") 41 | columns << (disk[i].nil? ? '| ' : '| ' + disk[i]['itemPrice']['item']['description']) 42 | columns << (os[i].nil? ? '| ' : '| ' + os[i]['template']['operatingSystemReferenceCode']) 43 | columns << (net[i].nil? ? '| ' : '| ' + net[i]['template']['networkComponents'].first['maxSpeed'].to_s) 44 | columns << (datacenter[i].nil? ? '| ' : '| ' + datacenter[i]['template']['datacenter']['name']) 45 | i+=1 46 | end 47 | columns 48 | end 49 | 50 | end 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /lib/chef/knife/softlayer.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Matt Eldridge () 3 | # © Copyright IBM Corporation 2014. 4 | # 5 | # LICENSE: Apache 2.0 (http://www.apache.org/licenses/) 6 | # 7 | 8 | require "knife-softlayer/version" 9 | 10 | module Knife 11 | module Softlayer 12 | # Your code goes here... 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/chef/knife/softlayer_base.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Matt Eldridge () 3 | # © Copyright IBM Corporation 2014. 4 | # 5 | # LICENSE: Apache 2.0 (http://www.apache.org/licenses/) 6 | # 7 | 8 | require 'chef/knife/bootstrap' 9 | require 'knife-softlayer/version' 10 | 11 | class Chef 12 | class Knife 13 | module SoftlayerBase 14 | 15 | USER_AGENT = "Chef Knife Softlayer Plugin #{::Knife::Softlayer::VERSION}" unless defined? USER_AGENT 16 | 17 | # :nodoc: 18 | def self.included(includer) 19 | includer.class_eval do 20 | 21 | deps do 22 | require 'fog/softlayer' 23 | require 'net/ssh' 24 | require 'net/ssh/multi' 25 | require 'chef/monkey_patches/net-ssh-multi' 26 | require 'readline' 27 | require 'chef/exceptions' 28 | require 'chef/search/query' 29 | require 'chef/mixin/command' 30 | require 'chef/mixin/shell_out' 31 | require 'mixlib/shellout' 32 | require 'chef/json_compat' 33 | end 34 | 35 | # Make the base bootstrap options available on topo bootstrap 36 | self.options = (Chef::Knife::Bootstrap.options).merge(self.options) 37 | 38 | option :softlayer_credential_file, 39 | :long => "--softlayer-credential-file FILE", 40 | :description => "File containing SoftLayer credentials as used by `softlayer_api` Ruby gem.", 41 | :proc => Proc.new { |key| Chef::Config[:knife][:softlayer_credential_file] = key } 42 | 43 | option :softlayer_username, 44 | :short => "-U USERNAME", 45 | :long => "--softlayer-username KEY", 46 | :description => "Your SoftLayer Username", 47 | :proc => Proc.new { |key| Chef::Config[:knife][:softlayer_access_key_id] = key } 48 | 49 | option :softlayer_api_key, 50 | :short => "-K SECRET", 51 | :long => "--softlayer-api-key SECRET", 52 | :description => "Your SoftLayer API Key", 53 | :proc => Proc.new { |key| Chef::Config[:knife][:softlayer_secret_access_key] = key } 54 | end 55 | end 56 | 57 | ## 58 | # Returns a connection to a SoftLayer API Service Endpoint. 59 | # @param [Symbol] service 60 | # @return [SoftLayer::Service] 61 | def connection(service=:compute) 62 | self.send(service) 63 | end 64 | 65 | def compute 66 | @compute_connection ||= Fog::Compute.new( 67 | :provider => :softlayer, 68 | :softlayer_username => Chef::Config[:knife][:softlayer_username], 69 | :softlayer_api_key => Chef::Config[:knife][:softlayer_api_key], 70 | :softlayer_default_datacenter => Chef::Config[:knife][:softlayer_default_datacenter], 71 | :softlayer_default_domain => Chef::Config[:knife][:softlayer_default_domain], 72 | ) 73 | end 74 | 75 | def network 76 | @network_connection ||= Fog::Network.new( 77 | :provider => :softlayer, 78 | :softlayer_username => Chef::Config[:knife][:softlayer_username], 79 | :softlayer_api_key => Chef::Config[:knife][:softlayer_api_key], 80 | ) 81 | end 82 | 83 | ## 84 | # Locates a config value. 85 | # @param [String] key 86 | # @return [String] 87 | def locate_config_value(key) 88 | key = key.to_sym 89 | config[key] || Chef::Config[:knife][key] 90 | end 91 | 92 | ## 93 | # A CLI output formatting wrapper. 94 | # @param [String] label 95 | # @param [String] value 96 | # @param [Symbol] color 97 | # @return [String] 98 | def msg_pair(label, value, color=:cyan) 99 | if value && !value.to_s.empty? 100 | puts "#{ui.color(label, color)}: #{value}" 101 | end 102 | end 103 | 104 | end 105 | end 106 | end 107 | 108 | module OS 109 | def OS.windows? 110 | (/cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM) != nil 111 | end 112 | end 113 | -------------------------------------------------------------------------------- /lib/chef/knife/softlayer_datacenter_list.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Matt Eldridge () 3 | # © Copyright IBM Corporation 2014. 4 | # 5 | # LICENSE: Apache 2.0 (http://www.apache.org/licenses/) 6 | # 7 | 8 | require 'chef/knife/softlayer_base' 9 | 10 | class Chef 11 | class Knife 12 | class SoftlayerDatacenterList < Knife 13 | 14 | include Knife::SoftlayerBase 15 | 16 | banner 'knife softlayer datacenter list (options)' 17 | 18 | def run 19 | $stdout.sync = true 20 | table_data = connection(:network).datacenters.map do |dc| 21 | {:name => dc.name, :long_name => dc.long_name } 22 | end 23 | puts Formatador.display_table(table_data, [:name, :long_name]) 24 | end 25 | 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/chef/knife/softlayer_datacenter_show.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Matt Eldridge () 3 | # © Copyright IBM Corporation 2014. 4 | # 5 | # LICENSE: Apache 2.0 (http://www.apache.org/licenses/) 6 | # 7 | 8 | require 'chef/knife/softlayer_base' 9 | 10 | class Chef 11 | class Knife 12 | class SoftlayerDatacenterShow < Knife 13 | 14 | include Knife::SoftlayerBase 15 | 16 | banner 'knife softlayer datacenter show DATACENTER' 17 | 18 | option :dc, 19 | :short => "-a", 20 | :long => "--all", 21 | :description => "Display all available configuration options for launching an instance.", 22 | :default => false 23 | 24 | 25 | def run 26 | unless name_args.size == 1 27 | puts ui.color("Specify exactly one datacenter to show.", :red) 28 | show_usage 29 | exit 1 30 | end 31 | 32 | $stdout.sync = true 33 | dc = connection(:network).datacenters.by_name(name_args[0]) 34 | 35 | puts "#{ui.color("Long Name:", :green)} #{dc.long_name}" 36 | puts "#{ui.color("Name:", :green)} #{dc.name}" 37 | 38 | puts "#{ui.color("Routers:", :green)}" 39 | puts Formatador.display_table(dc.routers) 40 | 41 | end 42 | 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /lib/chef/knife/softlayer_delete.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Matt Eldridge () 3 | # © Copyright IBM Corporation 2014. 4 | # 5 | # LICENSE: Apache 2.0 (http://www.apache.org/licenses/) 6 | # 7 | -------------------------------------------------------------------------------- /lib/chef/knife/softlayer_flavor_list.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Matt Eldridge () 3 | # © Copyright IBM Corporation 2014. 4 | # 5 | # LICENSE: Apache 2.0 (http://www.apache.org/licenses/) 6 | # 7 | 8 | require 'chef/knife/softlayer_base' 9 | require 'chef/knife/flavor/base' 10 | 11 | class Chef 12 | class Knife 13 | class SoftlayerFlavorList < Knife 14 | 15 | include Knife::SoftlayerBase 16 | include Knife::SoftlayerFlavorBase 17 | 18 | banner 'knife softlayer flavor list (options)' 19 | 20 | option :all, 21 | :short => "-a", 22 | :long => "--all", 23 | :description => "Display all available configuration options for launching an instance.", 24 | :default => false 25 | 26 | ## 27 | # Run the procedure to list softlayer VM flavors or display all available options. 28 | # @return [nil] 29 | def run 30 | $stdout.sync = true 31 | if config[:all] 32 | 33 | if OS.windows? 34 | puts ui.list(options_table, :uneven_columns_across, 6) 35 | else 36 | IO.popen('less', 'w') do |pipe| 37 | pipe.puts ui.list(options_table, :uneven_columns_across, 6) 38 | end 39 | end 40 | 41 | msg = "These options can be used in place of 'flavors'; See `knife softlayer server create --help` for details.\n" 42 | else 43 | puts connection.flavors.table([:id, :cpu, :ram, :disk,]) 44 | msg = "'flavors' provided here for convenience; SoftLayer allows you to choose a configuration a la carte.\nFor a full list of available instance options use --all with the `knife softlayer flavor list` subcommand." 45 | end 46 | puts ui.color("\nNOTICE: ", :yellow) 47 | puts ui.color(msg) 48 | end 49 | 50 | end 51 | end 52 | end 53 | 54 | -------------------------------------------------------------------------------- /lib/chef/knife/softlayer_global_ip_list.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Matt Eldridge () 3 | # © Copyright IBM Corporation 2014. 4 | # 5 | # LICENSE: Apache 2.0 (http://www.apache.org/licenses/) 6 | # 7 | 8 | require 'chef/knife/softlayer_base' 9 | 10 | class Chef 11 | class Knife 12 | class SoftlayerGlobalIpList < Knife 13 | 14 | include Knife::SoftlayerBase 15 | 16 | banner 'knife softlayer global ip list (options)' 17 | 18 | def run 19 | $stdout.sync = true 20 | 21 | if connection(:network).get_global_ip_records.body.empty? 22 | puts ui.color("No global ip addresses found.", :green) 23 | else 24 | puts ui.color("This operation can take several minutes. ", :yellow) 25 | table_data = connection(:network).ips.map do |ip| 26 | {:address => ip.address, :destination => ip.destination_ip.respond_to?(:address) ? ip.destination_ip.address : 'NOT ROUTED'} if ip.global? 27 | end.compact 28 | puts Formatador.display_table(table_data, [:address, :destination]) 29 | end 30 | end 31 | 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/chef/knife/softlayer_image_list.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Matt Eldridge () 3 | # © Copyright IBM Corporation 2014. 4 | # 5 | # LICENSE: Apache 2.0 (http://www.apache.org/licenses/) 6 | # 7 | 8 | require 'chef/knife/softlayer_base' 9 | 10 | class Chef 11 | class Knife 12 | class SoftlayerImageList < Knife 13 | 14 | include Knife::SoftlayerBase 15 | 16 | banner 'knife softlayer image list' 17 | 18 | def run 19 | $stdout.sync = true 20 | table_data = connection(:compute).images.map { |i| {:id => i.id, :name => i.name, :access => i.public? ? 'PUBLIC' : 'PRIVATE', :account => i.account_id } } 21 | puts Formatador.display_table(table_data, [:id, :access, :name, :account]) 22 | end 23 | 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/chef/knife/softlayer_key_pair_create.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Matt Eldridge () 3 | # © Copyright IBM Corporation 2014. 4 | # 5 | # LICENSE: Apache 2.0 (http://www.apache.org/licenses/) 6 | # 7 | 8 | require 'chef/knife/softlayer_base' 9 | 10 | class Chef 11 | class Knife 12 | class SoftlayerKeyPairCreate < Knife 13 | 14 | include Knife::SoftlayerBase 15 | 16 | banner 'knife softlayer key pair create' 17 | 18 | def run 19 | $stdout.sync = true 20 | opts = { 21 | :label => ui.ask_question("Enter the label for this key pair: "), 22 | :key => ui.ask("Enter path to the public key: ", lambda{ |answer| IO.read(answer) }) 23 | } 24 | 25 | key_pair = connection(:compute).key_pairs.create(opts) 26 | 27 | if !!key_pair 28 | puts "#{ui.color("Key pair successfully created. Provisioning may take a few minutes to complete.", :green)}" 29 | puts "#{ui.color("Key pair ID is: ", :green)} #{key_pair.id}" 30 | else 31 | puts "#{ui.color("Encountered a problem verifying key pair creation. Please try again.", :yellow)}" 32 | end 33 | end 34 | 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/chef/knife/softlayer_key_pair_list.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Matt Eldridge () 3 | # © Copyright IBM Corporation 2014. 4 | # 5 | # LICENSE: Apache 2.0 (http://www.apache.org/licenses/) 6 | # 7 | 8 | require 'chef/knife/softlayer_base' 9 | 10 | class Chef 11 | class Knife 12 | class SoftlayerKeyPairList < Knife 13 | 14 | include Knife::SoftlayerBase 15 | 16 | banner 'knife softlayer key pair list' 17 | 18 | def run 19 | $stdout.sync = true 20 | table_data = connection(:compute).key_pairs.map do |kp| 21 | {:id => kp.id, :label => kp.label, :create_date => kp.create_date, :modify_date => kp.modify_date } 22 | end 23 | puts Formatador.display_table(table_data, [:id, :label, :create_date, :modify_date]) 24 | end 25 | 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/chef/knife/softlayer_list.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Matt Eldridge () 3 | # © Copyright IBM Corporation 2014. 4 | # 5 | # LICENSE: Apache 2.0 (http://www.apache.org/licenses/) 6 | # 7 | -------------------------------------------------------------------------------- /lib/chef/knife/softlayer_server_create.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Matt Eldridge () 3 | # © Copyright IBM Corporation 2014. 4 | # 5 | # LICENSE: Apache 2.0 (http://www.apache.org/licenses/) 6 | # 7 | 8 | require 'chef/knife/softlayer_base' 9 | 10 | class Chef 11 | class Knife 12 | class SoftlayerServerCreateError < StandardError; end 13 | class SoftlayerServerCreate < Knife 14 | 15 | attr_reader :cci 16 | 17 | include Knife::SoftlayerBase 18 | 19 | banner 'knife softlayer server create (options)' 20 | 21 | option :flavor, 22 | :long => '--flavor FLAVOR', 23 | :short => '-f FLAVOR', 24 | :description => 'Pre-configured packages of computing resources. See `knife softlayer flavor list` for details.' 25 | 26 | option :hostname, 27 | :long => '--hostname VALUE', 28 | :short => '-H VALUE', 29 | :description => 'The hostname SoftLayer will assign to the VM instance.' 30 | 31 | option :domain, 32 | :long => '--domain VALUE', 33 | :short => '-D VALUE', 34 | :description => 'The FQDN SoftLayer will assign to the VM instance.', 35 | :default => Chef::Config[:knife][:softlayer_default_domain] 36 | 37 | option :cores, 38 | :long => '--cores VALUE', 39 | :short => '-C VALUE', 40 | :description => 'The number of virtual cores SoftLayer will assign to the VM instance.' 41 | 42 | option :os_code, 43 | :long => '--os-code VALUE', 44 | :short => '-O VALUE', 45 | :description => 'A valid SoftLayer operating system code. See `knife softlayer flavor list --all` for a list of valid codes.' 46 | 47 | option :ram, 48 | :long => '--ram VALUE', 49 | :short => '-R VALUE', 50 | :description => 'The number of virtual cores SoftLayer will assign to the VM instance.' 51 | 52 | 53 | option :block_storage, 54 | :long => '--block-storage VALUE', 55 | :short => '-B VALUE', 56 | :description => 'The size in GB of the block storage devices (disks) for this instance. Specify 1 - 5 entries in a comma separated list following the format "dev:size". Example: "0:25,2:500" would be a 25GB volume on device 0 (the root partition) and a 100GB volume on on device 2. [NOTE: SoftLayer VMs always reserve device 1 for a swap device.] ', 57 | :proc => Proc.new { |devs| devs.split(',').map{ |dev| device, capacity = dev.split(':'); {"device"=>device, "diskImage"=>{"capacity"=>capacity}} } } 58 | 59 | option :nic, 60 | :long => '--network-interface-speed VALUE', 61 | :short => '-n VALUE', 62 | :description => 'The maximum speed of the public NIC available to the instance.', 63 | :default => nil 64 | 65 | option :bill_monthly, 66 | :long => '--bill-monthly', 67 | :description => 'Flag to bill monthly instead of hourly, minimum charge of one month.', 68 | :boolean => true, 69 | :default => false 70 | 71 | option :vlan, 72 | :long => '--vlan VLAN-ID', 73 | :description => 'Internal SoftLayer ID of the public VLAN into which the compute instance should be placed.' 74 | 75 | option :private_vlan, 76 | :long => '--private-vlan VLAN-ID', 77 | :description => 'Internal SoftLayer ID of the private VLAN into which the compute instance should be placed.' 78 | 79 | option :image_id, 80 | :long => '--image-id IMAGE-ID', 81 | :description => 'Internal SoftLayer uuid specifying the image template from which the compute instance should be booted.' 82 | 83 | option :private_network_only, 84 | :long => '--private-network-only', 85 | :description => 'Flag to be passed when the compute instance should have no public facing network interface.', 86 | :boolean => true 87 | 88 | option :use_private_network, 89 | :long => '--use-private-network', 90 | :description => 'Flag to be passwed when bootstrap is preferred over the private network.', 91 | :boolean => true 92 | 93 | option :from_file, 94 | :long => '--from-file PATH', 95 | :description => 'Path to JSON file containing arguments for provisoning and bootstrap.' 96 | 97 | #option :single_tenant, 98 | # :long => '--single-tenant', 99 | # :description => 'Create a VM with a dedicated physical host.', 100 | # :boolean => true, 101 | # :default => false 102 | 103 | option :local_storage, 104 | :long => '--local-storage', 105 | :description => 'Force local block storage instead of SAN storage.', 106 | :boolean => true, 107 | :default => false 108 | 109 | option :datacenter, 110 | :long => '--datacenter VALUE', 111 | :description => 'Create a VM in a particular datacenter.', 112 | :default => Chef::Config[:knife][:softlayer_default_datacenter] 113 | 114 | option :tags, 115 | :short => "-T T=V[,T=V,...]", 116 | :long => "--tags Tag=Value[,Tag=Value...]", 117 | :description => "The tags for this server", 118 | :proc => Proc.new { |tags| tags.split(',') } 119 | 120 | option :chef_node_name, 121 | :short => "-N NAME", 122 | :long => "--node-name NAME", 123 | :description => "The Chef node name for your new node", 124 | :proc => Proc.new { |key| Chef::Config[:knife][:chef_node_name] = key } 125 | 126 | option :ssh_user, 127 | :short => "-x USERNAME", 128 | :long => "--ssh-user USERNAME", 129 | :description => "The ssh username", 130 | :default => "root" 131 | 132 | option :ssh_password, 133 | :short => "-P PASSWORD", 134 | :long => "--ssh-password PASSWORD", 135 | :description => "The ssh password" 136 | 137 | option :ssh_keys, 138 | :short => "-S KEY", 139 | :long => "--ssh-keys KEY", 140 | :description => "The ssh keys for the SoftLayer Virtual Guest environment. Accepts a space separated list of integers.", 141 | :proc => Proc.new { |ssh_keys| ssh_keys.split(' ') } 142 | 143 | option :ssh_port, 144 | :short => "-p PORT", 145 | :long => "--ssh-port PORT", 146 | :description => "The ssh port", 147 | :default => "22", 148 | :proc => Proc.new { |key| Chef::Config[:knife][:ssh_port] = key } 149 | 150 | option :ssh_gateway, 151 | :short => "-w GATEWAY", 152 | :long => "--ssh-gateway GATEWAY", 153 | :description => "The ssh gateway server", 154 | :proc => Proc.new { |key| Chef::Config[:knife][:ssh_gateway] = key } 155 | 156 | option :identity_file, 157 | :short => "-i IDENTITY_FILE", 158 | :long => "--identity-file IDENTITY_FILE", 159 | :description => "The SSH identity file used for authentication" 160 | 161 | option :prerelease, 162 | :long => "--prerelease", 163 | :description => "Install the pre-release chef gems" 164 | 165 | option :bootstrap_version, 166 | :long => "--bootstrap-version VERSION", 167 | :description => "The version of Chef to install", 168 | :proc => Proc.new { |v| Chef::Config[:knife][:bootstrap_version] = v } 169 | 170 | option :bootstrap_proxy, 171 | :long => "--bootstrap-proxy PROXY_URL", 172 | :description => "The proxy server for the node being bootstrapped", 173 | :proc => Proc.new { |p| Chef::Config[:knife][:bootstrap_proxy] = p } 174 | 175 | option :distro, 176 | :short => "-d DISTRO", 177 | :long => "--distro DISTRO", 178 | :description => "Bootstrap a distro using a template; default is 'chef-full'", 179 | :proc => Proc.new { |d| Chef::Config[:knife][:distro] = d }, 180 | :default => "chef-full" 181 | 182 | option :template_file, 183 | :long => "--template-file TEMPLATE", 184 | :description => "Full path to location of template to use", 185 | :proc => Proc.new { |t| Chef::Config[:knife][:template_file] = t }, 186 | :default => false 187 | 188 | option :run_list, 189 | :short => "-r RUN_LIST", 190 | :long => "--run-list RUN_LIST", 191 | :description => "Comma separated list of roles/recipes to apply", 192 | :proc => lambda { |o| o.split(/[\s,]+/) } 193 | 194 | option :secret, 195 | :short => "-s SECRET", 196 | :long => "--secret ", 197 | :description => "The secret key to use to encrypt data bag item values", 198 | :proc => lambda { |s| Chef::Config[:knife][:secret] = s } 199 | 200 | option :secret_file, 201 | :long => "--secret-file SECRET_FILE", 202 | :description => "A file containing the secret key to use to encrypt data bag item values", 203 | :proc => lambda { |sf| Chef::Config[:knife][:secret_file] = sf } 204 | 205 | option :json_attributes, 206 | :short => "-j JSON", 207 | :long => "--json-attributes JSON", 208 | :description => "A JSON string to be added to the first run of chef-client", 209 | :proc => lambda { |o| JSON.parse(o) } 210 | 211 | option :host_key_verify, 212 | :long => "--[no-]host-key-verify", 213 | :description => "Verify host key, enabled by default.", 214 | :boolean => true, 215 | :default => true 216 | 217 | option :bootstrap_protocol, 218 | :long => "--bootstrap-protocol protocol", 219 | :description => "protocol to bootstrap windows servers. options: winrm/ssh", 220 | :proc => Proc.new { |key| Chef::Config[:knife][:bootstrap_protocol] = key }, 221 | :default => nil 222 | 223 | option :fqdn, 224 | :long => "--fqdn FQDN", 225 | :description => "Pre-defined FQDN", 226 | :proc => Proc.new { |key| Chef::Config[:knife][:fqdn] = key }, 227 | :default => nil 228 | 229 | option :assign_global_ip, 230 | :long => "--assign-global-ip IpAdress", 231 | :description => "Assign an existing SoftLayer Global IP address.", 232 | :default => nil 233 | 234 | option :new_global_ip, 235 | :long => "--new-global-ip VERSION", 236 | :description => "Order a new SoftLayer Global IP address and assign it to the instance." 237 | 238 | option :hint, 239 | :long => "--hint HINT_NAME[=HINT_FILE]", 240 | :description => "Specify Ohai Hint to be set on the bootstrap target. Use multiple --hint options to specify multiple hints.", 241 | :proc => Proc.new { |h| 242 | Chef::Config[:knife][:hints] ||= {} 243 | name, path = h.split("=") 244 | Chef::Config[:knife][:hints][name] = path ? JSON.parse(::File.read(path)) : Hash.new 245 | } 246 | 247 | option :user_data, 248 | :short => "-u USERDATA", 249 | :long => "--user-data USERDATA", 250 | :description => "Optional user data to pass on to SoftLayer compute instance" 251 | 252 | option :wait_for_timeout, 253 | :short => "-t VALUE", 254 | :long => "--wait-for-timeout VALUE", 255 | :description => "Timeout for provisioning proccess", 256 | :default => 600 257 | 258 | require 'chef/knife/bootstrap' 259 | # Make the base bootstrap options available on topo bootstrap 260 | self.options = (Chef::Knife::Bootstrap.options).merge(self.options) 261 | 262 | ## 263 | # Run the procedure to create a SoftLayer VM and bootstrap it. 264 | # @return [nil] 265 | def run 266 | $stdout.sync = true 267 | config.merge!(slurp_from_file(config[:from_file])) if config[:from_file] 268 | 269 | # TODO: Windows support. 270 | raise SoftlayerServerCreateError, "#{ui.color("Windows VMs not currently supported.", :red)}" if config[:os_code] =~ /^WIN_/ 271 | raise SoftlayerServerCreateError, "#{ui.color("identity file (-i) option is incompatible with password (-P) option required.", :red)}" if !!config[:identity_file] and !!config[:ssh_password] 272 | raise SoftlayerServerCreateError, "#{ui.color("--new-global-ip value must be 'v4' or 'v6'.", :red)}" if config[:new_global_ip] and !config[:new_global_ip].to_s.match(/^v[4,6]$/i) 273 | 274 | # TODO: create a pre-launch method for clean up tasks. 275 | # TODO: create a pre-launch method for clean up tasks. 276 | config[:vlan] = config[:vlan].to_i if config[:vlan] 277 | config[:private_vlan] = config[:private_vlan].to_i if config[:private_vlan] 278 | Fog.credentials[:private_key_path] = config[:identity_file] if config[:identity_file] 279 | # TODO: create a pre-launch method for clean up tasks. 280 | # TODO: create a pre-launch method for clean up tasks. 281 | 282 | opts = { 283 | :flavor => :flavor_id, 284 | :hostname => :name, 285 | :domain => nil, 286 | :cores => :cpu, 287 | :os_code => nil, 288 | :ram => nil, 289 | :block_storage => :disk, 290 | :local_storage => :ephemeral_storage, 291 | :datacenter => nil, 292 | :ssh_keys => :key_pairs, 293 | :vlan => nil, 294 | :private_vlan => nil, 295 | :image_id => nil, 296 | :private_network_only => nil, 297 | #:tags => nil, 298 | :user_data => nil 299 | } 300 | 301 | 302 | opts.keys.each do |opt| 303 | if opts[opt].nil? 304 | opts[opt] = config[opt] 305 | else 306 | opts[opts.delete(opt)] = config[opt] # clever shit like this is why I like programming :-] 307 | end 308 | end 309 | 310 | # FIXME: make the above deal with nested opts and get rid of this one-off 311 | opts[:network_components] = [ {"speed" => config[:nic]} ] if !!config[:nic] 312 | 313 | opts.delete_if { |k,v| v.nil? } 314 | puts ui.color("Launching SoftLayer VM, this may take a few minutes.", :green) 315 | instance = connection.servers.create(opts) 316 | if config[:private_network_only] || config[:use_private_network] 317 | instance.ssh_ip_address = Proc.new {|server| server.private_ip_address } 318 | end 319 | progress Proc.new { instance.wait_for(timeout=config[:wait_for_timeout].to_i) { ready? and sshable? } } 320 | putc("\n") 321 | 322 | if config[:tags] 323 | puts ui.color("Applying tags to SoftLayer instance.", :green) 324 | progress Proc.new { instance.add_tags(config[:tags]) } 325 | putc("\n") 326 | end 327 | 328 | 329 | if config[:new_global_ip] || config[:assign_global_ip] 330 | if config[:new_global_ip] # <— the value of this will be v4 or v6 331 | begin 332 | puts ui.color('Provisioning new Global IP' + config[:new_global_ip].downcase + ', this may take a few minutes.', :green) 333 | create_global_ip = Proc.new do 334 | existing_records = connection(:network).get_global_ip_records.body 335 | connection(:network).send('create_new_global_ip' + config[:new_global_ip].downcase) or raise SoftlayerServerCreateError, "Unable to create new Global IP Address."; 336 | sleep 20 # if we look for the new record too quickly it won't be there yet... 337 | new_record_global_id = (connection(:network).get_global_ip_records.body - existing_records).reduce['id'] 338 | connection(:network).ips.select { |ip| ip.global_id == new_record_global_id }.reduce 339 | end 340 | global_ip = progress(create_global_ip) or raise SoftlayerServerCreateError, "Error encountered creating Global IP Address." 341 | puts ui.color('Global IP Address successfully created.', :green) 342 | rescue SoftlayerServerCreateError => e 343 | puts ui.color('We have encountered a problem ordering the requested global IP. The transaction may not have completed.', :red) 344 | puts ui.color(e.message, :yellow) 345 | end 346 | end 347 | 348 | if config[:assign_global_ip] 349 | case config[:assign_global_ip].to_s 350 | #ipv4 351 | when /^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/ 352 | global_ip = connection(:network).ips.by_address(config[:assign_global_ip]) 353 | #ipv6 354 | when /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/ 355 | global_ip = connection(:network).ips.by_address(config[:assign_global_ip]) 356 | else 357 | raise SoftlayerServerCreateError, "--assign-global-ip value must be valid IPv4 or IPv6 address" 358 | end 359 | global_ip or raise SoftlayerServerCreateError, "Global IP address not found. Please check the address or id supplied and try again." 360 | global_ip.reload 361 | end 362 | 363 | route_global_ip = Proc.new do 364 | puts ui.color('Routing Global IP Address to Instance.', :green) 365 | global_ip.route(connection(:network).ips.by_address(instance.public_ip_address)) or raise SoftlayerServerCreateError, "Global IP address failed to route." 366 | puts ui.color('Global IP Address has been assigned.', :green) 367 | puts ui.color('Global IP Address will not function without networking rules on the endpoint operating system. See http://knowledgelayer.softlayer.com/learning/global-ip-addresses for details.', :yellow) 368 | end 369 | progress(route_global_ip) 370 | 371 | end 372 | 373 | puts ui.color('Bootstrapping Chef node, this may take a few minutes.', :green) 374 | linux_bootstrap(instance).run 375 | 376 | puts ui.color("Applying tags to Chef node.", :green) 377 | progress apply_tags(instance) 378 | 379 | end 380 | 381 | # @param [Hash] instance 382 | # @return [Chef::Knife::Bootstrap] 383 | def linux_bootstrap(instance) 384 | bootstrap = Chef::Knife::Bootstrap.new 385 | instance.ssh_ip_address = instance.private_ip_address if config[:private_network_only] 386 | bootstrap.name_args = [instance.ssh_ip_address] 387 | bootstrap.config[:ssh_user] = config[:ssh_user] 388 | bootstrap.config[:ssh_password] = config[:ssh_password] if config[:ssh_password] 389 | bootstrap.config[:identity_file] = config[:identity_file] if config[:identity_file] 390 | bootstrap.config[:ssh_port] = config[:ssh_port] 391 | bootstrap.config[:ssh_gateway] = config[:ssh_gateway] 392 | bootstrap.config[:chef_node_name] = locate_config_value(:chef_node_name) || instance.id 393 | bootstrap.config[:use_sudo] = true unless config[:ssh_user] == 'root' 394 | bootstrap.config[:host_key_verify] = config[:host_key_verify] 395 | shared_bootstrap(bootstrap) 396 | end 397 | 398 | # @param [Chef::Knife::Bootstrap] bootstrap 399 | # @return [Chef::Knife::Bootstrap] 400 | def shared_bootstrap(bootstrap) 401 | bootstrap.config[:run_list] = config[:run_list] 402 | bootstrap.config[:bootstrap_version] = locate_config_value(:bootstrap_version) 403 | bootstrap.config[:distro] = locate_config_value(:distro) 404 | bootstrap.config[:template_file] = locate_config_value(:template_file) 405 | bootstrap.config[:environment] = locate_config_value(:environment) 406 | bootstrap.config[:prerelease] = config[:prerelease] 407 | bootstrap.config[:first_boot_attributes] = locate_config_value(:json_attributes) || {} 408 | bootstrap.config[:encrypted_data_bag_secret] = locate_config_value(:encrypted_data_bag_secret) 409 | bootstrap.config[:encrypted_data_bag_secret_file] = locate_config_value(:encrypted_data_bag_secret_file) 410 | bootstrap.config[:secret] = locate_config_value(:secret) 411 | bootstrap.config[:secret_file] = locate_config_value(:secret_file) 412 | bootstrap.config[:tags] = locate_config_value(:tags) 413 | bootstrap.config[:fqdn] = locate_config_value(:fqdn) 414 | Chef::Config[:knife][:hints] ||= {} 415 | Chef::Config[:knife][:hints]['softlayer'] ||= {} 416 | bootstrap 417 | end 418 | 419 | def windows_bootstrap(server, fqdn) 420 | # TODO: Windows support.... 421 | end 422 | 423 | def progress(proc) 424 | t = Thread.new { Thread.current[:output] = proc.call } 425 | i = 0 426 | while t.alive? 427 | sleep 0.5 428 | putc('.') 429 | i += 1 430 | putc("\n") if i == 76 431 | i = 0 if i == 76 432 | end 433 | putc("\n") 434 | t.join 435 | t[:output] 436 | end 437 | 438 | def slurp_from_file(path) 439 | args = JSON.parse(IO.read(path)) 440 | args.keys.each { |key| args[key.gsub('-', '_').to_sym] = args.delete(key) } 441 | # TODO: Something less ugly than the empty rescue block below. The :proc Procs/Lambdas aren't idempotent... 442 | args.keys.each { |key| begin; args[key] = options[key][:proc] ? options[key][:proc].call(args[key]) : args[key]; rescue; end } 443 | args 444 | end 445 | 446 | def apply_tags(instance) 447 | Proc.new do 448 | chef = Chef::Search::Query.new 449 | chef.search('node', "name:#{locate_config_value(:chef_node_name) || instance.id}") do |n| 450 | config[:tags] = [] if config[:tags].nil? # we're going to tag every Chef node with the SL id no matter what 451 | config[:tags] << "slid=#{instance.id}" 452 | config[:tags].each do |tag| 453 | n.tag(tag) 454 | end 455 | n.save 456 | end 457 | end 458 | end 459 | 460 | end 461 | end 462 | end 463 | -------------------------------------------------------------------------------- /lib/chef/knife/softlayer_server_destroy.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Matt Eldridge () 3 | # © Copyright IBM Corporation 2014. 4 | # 5 | # LICENSE: Apache 2.0 (http://www.apache.org/licenses/) 6 | # 7 | 8 | require 'chef/knife/softlayer_base' 9 | require 'chef/search/query' 10 | require 'chef/api_client' 11 | 12 | class Chef 13 | class Knife 14 | class SoftlayerServerDestroy < Knife 15 | 16 | attr_accessor :node 17 | attr_accessor :cci 18 | 19 | include Knife::SoftlayerBase 20 | 21 | banner 'knife softlayer server destroy (options)' 22 | 23 | option :chef_node_name, 24 | :short => "-N NAME", 25 | :long => "--node-name NAME", 26 | :description => "The name of the node to be destroyed." 27 | 28 | option :ip_address, 29 | :long => "--ip-address ADDRESS", 30 | :short => "-I", 31 | :description => "Find the VM and node to destroy by its public IP address." 32 | 33 | ## 34 | # Run the procedure to destroy a SoftLayer VM and clean up its Chef node and client. 35 | # @return [nil] 36 | def run 37 | 38 | $stdout.sync = true 39 | 40 | puts ui.color("Decommissioning SoftLayer VM, this may take a few minutes.", :green) 41 | connection.servers.each do |server| 42 | if config[:ip_address] 43 | if server.public_ip_address == config[:ip_address] 44 | @instance = server 45 | break 46 | end 47 | elsif config[:chef_node_name] 48 | if server.name == config[:chef_node_name] 49 | config[:ip_address] = server.public_ip_address 50 | @instance = server 51 | break 52 | end 53 | elsif arg = name_args[0] 54 | if arg =~ /^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/ # ipv4 55 | if server.public_ip_address == arg 56 | @instance = server 57 | break 58 | end 59 | elsif arg =~ /^(?:[A-F0-9]{1,4}:){7}[A-F0-9]{1,4}$/ # ipv6 60 | if server.public_ip_address == arg 61 | @instance = server 62 | break 63 | end 64 | else 65 | if server.name == arg 66 | config[:ip_address] = server.public_ip_address 67 | @instance = server 68 | break 69 | end 70 | end 71 | end 72 | end 73 | @instance.nil? and raise "#{ui.color('VM instance with IP: ' + (config[:ip_address].to_s) +' not found!', :red)}" 74 | @chef = Chef::Search::Query.new 75 | @chef.search('node', "name:#{@instance.name}") do |node| 76 | begin 77 | @node = node 78 | rescue 79 | end 80 | end 81 | 82 | begin 83 | if @node 84 | begin 85 | destroy_item(Chef::Node, @node.name, "node") 86 | puts ui.color("Chef node successfully deleted.", :green) 87 | rescue Exception => e 88 | err_msg ui.color("ERROR DELETING CHEF NODE", :red) 89 | err_msg ui.color(e.message, :yellow) 90 | err_msg ui.color(e.backtrace.join("\n"), :yellow) 91 | end 92 | 93 | begin 94 | destroy_item(Chef::ApiClient, @node.name, "client") 95 | puts ui.color("Chef client successfully deleted.", :green) 96 | rescue Exception => e 97 | err_msg ui.color("ERROR DELETING CHEF CLIENT", :red) 98 | err_msg ui.color(e.message, :yellow) 99 | err_msg ui.color(e.backtrace.join("\n"), :yellow) 100 | end 101 | else 102 | "#{ui.color('Chef node: ' + config[:chef_node_name] +' not found! will destroy instance.', :red)}" 103 | end 104 | 105 | begin 106 | @instance.destroy 107 | puts ui.color("SoftLayer VM successfully deleted. You are no longer being billed for this instance.", :green) 108 | rescue Exception => e 109 | err_msg ui.color("ERROR DELETING SOFTLAYER VM. IT'S POSSIBLE YOU ARE STILL BEING BILLED FOR THIS INSTANCE. PLEASE CONTACT SUPPORT FOR FURTHER ASSISTANCE", :red) 110 | err_msg ui.color(e.message, :yellow) 111 | err_msg ui.color(e.backtrace.join("\n"), :yellow) 112 | end 113 | ensure 114 | unless err_msg.empty? 115 | err_msg.each do |msg| 116 | puts msg 117 | end 118 | end 119 | end 120 | 121 | end 122 | 123 | # @param [Chef::*] klass 124 | # @param [String] name 125 | # @param [String] type_name 126 | # @return [nil] 127 | def destroy_item(klass, name, type_name) 128 | begin 129 | object = klass.load(name) 130 | object.destroy 131 | ui.warn("Deleted #{type_name} #{name}") 132 | rescue Net::HTTPServerException 133 | ui.warn("Could not find a #{type_name} named #{name} to delete!") 134 | end 135 | end 136 | 137 | def err_msg(msg=nil) 138 | @msgs ||= [] 139 | @msgs.push(msg).compact 140 | end 141 | 142 | end 143 | end 144 | end 145 | 146 | 147 | -------------------------------------------------------------------------------- /lib/chef/knife/softlayer_server_list.rb: -------------------------------------------------------------------------------- 1 | require 'chef/knife/softlayer_base' 2 | require 'chef/search/query' 3 | require 'chef/api_client' 4 | 5 | class Chef 6 | class Knife 7 | class SoftlayerServerList < Knife 8 | 9 | include Knife::SoftlayerBase 10 | 11 | banner 'knife softlayer server list (options)' 12 | 13 | ## 14 | # Run the procedure to list all of the Softlayer VM's 15 | # @return [nil] 16 | def run 17 | 18 | $stdout.sync = true 19 | fmt = "%-20s %-8s %-15s %-15s %-10s" 20 | puts ui.color(sprintf(fmt, "Name", "Location", "Public IP", "Private IP", "Status"), :green) 21 | connection.servers.each do |server| 22 | puts sprintf fmt, server.name, server.datacenter, server.public_ip_address, server.private_ip_address, server.created_at ? 'Running' : 'Starting' 23 | end 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/chef/knife/softlayer_server_relaunch.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Matt Eldridge () 3 | # © Copyright IBM Corporation 2014. 4 | # 5 | # LICENSE: Apache 2.0 (http://www.apache.org/licenses/) 6 | # 7 | 8 | require 'chef/knife/softlayer_base' 9 | 10 | class Chef 11 | class Knife 12 | class SoftlayerServerRelaunch < Knife 13 | 14 | include Knife::SoftlayerBase 15 | 16 | banner 'knife softlayer server relaunch []' 17 | 18 | option :all, 19 | :short => "-a", 20 | :long => "--all", 21 | :description => "Display all available configuration options for launching an instance.", 22 | :default => false 23 | 24 | ## 25 | # Run the procedure to list softlayer VM flavors or display all available options. 26 | # @return [nil] 27 | def run 28 | $stdout.sync = true 29 | if name_args.count < 1 30 | ui.fatal("Server relaunch requires AT LEAST ONE node name.") 31 | exit 1; 32 | end 33 | 34 | ident_file = Chef::Config[:knife][:identity_file] || config[:identity_file] 35 | Fog.credentials[:private_key_path] = ident_file if ident_file 36 | 37 | Chef::Search::Query.new.search(:node, "name:#{name_args[0]}") do |object| 38 | @vm = connection.servers.select { |s| s.public_ip == object.ipaddress }.first 39 | end 40 | ui.fatal("Server not found on SoftLayer account.") and exit 1 unless @vm 41 | 42 | unless @vm.sshable? 43 | ui.fatal("Node with name #{name_args[0]} not sshable, relaunch canceled.") 44 | exit 1 45 | end 46 | 47 | # grab the contents of /etc/chef from the target node and stash a local copy 48 | begin 49 | puts ui.color("Capturing existing node configuration files.", :green) 50 | @vm.scp_download("/etc/chef", "/tmp/#{@vm.id}/", :recursive => true) 51 | rescue Exception => e 52 | puts ui.color(e.message, :red) 53 | ui.fatal('Relaunch canceled.') 54 | exit 1 55 | end 56 | 57 | begin 58 | puts ui.color("Relaunching SoftLayer server, this may take a few minutes.", :green) 59 | @vm.relaunch! 60 | @vm.wait_for { putc '.'; ready? && sshable? } 61 | puts '' 62 | rescue Exception => e 63 | puts ui.color(e.message, :red) 64 | ui.fatal('Relaunch FAILED. You may be missing a server.') 65 | exit 1 66 | end 67 | 68 | # push the locally stashed config items up to new machine 69 | begin 70 | puts ui.color("Installing node configuration on relaunched server.", :green) 71 | @vm.scp("/tmp/#{@vm.id}/chef/", "/etc/", :recursive => true) 72 | rescue Exception => e 73 | puts ui.color(e.message, :red) 74 | ui.fatal('Relaunch FAILED. You may be missing a chef node.') 75 | exit 1 76 | end 77 | 78 | begin 79 | puts ui.color("Installing chef-client executable on relaunched server.", :green) 80 | puts @vm.ssh('wget https://www.chef.io/chef/install.sh && sudo bash ./install.sh && rm install.sh').first.stdout 81 | rescue Exception => e 82 | puts ui.color(e.message, :red) 83 | ui.fatal('Relaunch FAILED. You may be missing a chef node.') 84 | exit 1 85 | end 86 | 87 | # run chef-client on the new machine with the existing config and attributes 88 | begin 89 | puts ui.color("Initial run of chef-client on relaunched server, this may take a few minutes.", :green) 90 | puts @vm.ssh('sudo chef-client').first.stdout 91 | rescue Exception => e 92 | puts ui.color(e.message, :red) 93 | ui.fatal('Relaunch FAILED on chef run. Your chef node may be misconfigured.') 94 | exit 1 95 | end 96 | 97 | end 98 | 99 | end 100 | end 101 | end 102 | 103 | # 104 | # Author:: Matt Eldridge () 105 | # © Copyright IBM Corporation 2014. 106 | # 107 | # LICENSE: Apache 2.0 (http://www.apache.org/licenses/) 108 | # 109 | 110 | require 'chef/knife/softlayer_base' 111 | 112 | class Chef 113 | class Knife 114 | class SoftlayerServerRelaunch < Knife 115 | 116 | include Knife::SoftlayerBase 117 | 118 | banner 'knife softlayer server relaunch []' 119 | 120 | option :all, 121 | :short => "-a", 122 | :long => "--all", 123 | :description => "Display all available configuration options for launching an instance.", 124 | :default => false 125 | 126 | require 'chef/knife/bootstrap' 127 | # Make the base bootstrap options available on topo bootstrap 128 | self.options = (Chef::Knife::Bootstrap.options).merge(self.options) 129 | 130 | ## 131 | # Run the procedure to list softlayer VM flavors or display all available options. 132 | # @return [nil] 133 | def run 134 | $stdout.sync = true 135 | if name_args.count < 1 136 | ui.fatal("Server relaunch requires AT LEAST ONE node name.") 137 | exit 1; 138 | end 139 | 140 | ident_file = Chef::Config[:knife][:identity_file] || config[:identity_file] 141 | Fog.credentials[:private_key_path] = ident_file if ident_file 142 | 143 | Chef::Search::Query.new.search(:node, "name:#{name_args[0]}") do |object| 144 | @vm = connection.servers.select { |s| s.public_ip == object.ipaddress }.first 145 | end 146 | ui.fatal("Server not found on SoftLayer account.") and exit 1 unless @vm 147 | 148 | unless @vm.sshable? 149 | ui.fatal("Node with name #{name_args[0]} not sshable, relaunch canceled.") 150 | exit 1 151 | end 152 | 153 | # grab the contents of /etc/chef from the target node and stash a local copy 154 | begin 155 | puts ui.color("Capturing existing node configuration files.", :green) 156 | @vm.scp_download("/etc/chef", "/tmp/#{@vm.id}/", :recursive => true) 157 | rescue Exception => e 158 | puts ui.color(e.message, :red) 159 | ui.fatal('Relaunch canceled.') 160 | exit 1 161 | end 162 | 163 | begin 164 | puts ui.color("Relaunching SoftLayer server, this may take a few minutes.", :green) 165 | @vm.relaunch! 166 | @vm.wait_for { putc '.'; ready? && sshable? } 167 | puts '' 168 | rescue Exception => e 169 | puts ui.color(e.message, :red) 170 | ui.fatal('Relaunch FAILED. You may be missing a server.') 171 | exit 1 172 | end 173 | 174 | # push the locally stashed config items up to new machine 175 | begin 176 | puts ui.color("Installing node configuration on relaunched server.", :green) 177 | @vm.scp("/tmp/#{@vm.id}/chef/", "/etc/", :recursive => true) 178 | rescue Exception => e 179 | puts ui.color(e.message, :red) 180 | ui.fatal('Relaunch FAILED. You may be missing a chef node.') 181 | exit 1 182 | end 183 | 184 | begin 185 | puts ui.color("Installing chef-client executable on relaunched server.", :green) 186 | puts @vm.ssh('wget https://www.chef.io/chef/install.sh && sudo bash ./install.sh && rm install.sh').first.stdout 187 | rescue Exception => e 188 | puts ui.color(e.message, :red) 189 | ui.fatal('Relaunch FAILED. You may be missing a chef node.') 190 | exit 1 191 | end 192 | 193 | # run chef-client on the new machine with the existing config and attributes 194 | begin 195 | puts ui.color("Initial run of chef-client on relaunched server, this may take a few minutes.", :green) 196 | puts @vm.ssh('sudo chef-client -j /etc/chef/first-boot.json').first.stdout 197 | rescue Exception => e 198 | puts ui.color(e.message, :red) 199 | ui.fatal('Relaunch FAILED on chef run. Your chef node may be misconfigured.') 200 | exit 1 201 | end 202 | 203 | end 204 | 205 | end 206 | end 207 | end 208 | 209 | -------------------------------------------------------------------------------- /lib/chef/knife/softlayer_vlan_create.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Matt Eldridge () 3 | # © Copyright IBM Corporation 2014. 4 | # 5 | # LICENSE: Apache 2.0 (http://www.apache.org/licenses/) 6 | # 7 | 8 | require 'chef/knife/softlayer_base' 9 | 10 | class Chef 11 | class Knife 12 | class SoftlayerVlanCreate < Knife 13 | 14 | include Knife::SoftlayerBase 15 | 16 | banner 'knife softlayer vlan create' 17 | 18 | def run 19 | 20 | $stdout.sync = true 21 | 22 | opts = { 23 | :name => ui.ask_question("Enter a vlan name: "), 24 | :datacenter => connection(:network).datacenters.by_name(ui.ask_question("Enter a datacenter name: ")), 25 | :router => {'hostname' => ui.ask_question("Enter a router hostname: ")}, 26 | :network_space => ui.ask_question("Enter a network space: ", :default => 'PUBLIC'), 27 | } 28 | 29 | vlan = connection(:network).networks.create(opts) 30 | 31 | !!vlan and puts "#{ui.color("VLAN successfully created. Provisioning may take a few minutes to complete.", :green)}" 32 | 33 | end 34 | 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/chef/knife/softlayer_vlan_list.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Matt Eldridge () 3 | # © Copyright IBM Corporation 2014. 4 | # 5 | # LICENSE: Apache 2.0 (http://www.apache.org/licenses/) 6 | # 7 | 8 | require 'chef/knife/softlayer_base' 9 | 10 | class Chef 11 | class Knife 12 | class SoftlayerVlanList < Knife 13 | 14 | include Knife::SoftlayerBase 15 | 16 | banner 'knife softlayer vlan list (options)' 17 | 18 | def run 19 | $stdout.sync = true 20 | table_data = connection(:network).networks.map do |net| 21 | {:id => net.id, :name => net.name ? net.name : '[none]', :datacenter => net.datacenter.long_name, :network_space => net.network_space, :router => net.router['hostname'] } 22 | end 23 | puts Formatador.display_table(table_data, [:id, :name, :datacenter, :network_space, :router]) 24 | end 25 | 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/chef/knife/softlayer_vlan_show.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Matt Eldridge () 3 | # © Copyright IBM Corporation 2014. 4 | # 5 | # LICENSE: Apache 2.0 (http://www.apache.org/licenses/) 6 | # 7 | 8 | require 'chef/knife/softlayer_base' 9 | 10 | class Chef 11 | class Knife 12 | class SoftlayerVlanShow < Knife 13 | 14 | include Knife::SoftlayerBase 15 | 16 | banner 'knife softlayer vlan show ID (options)' 17 | 18 | def run 19 | unless name_args.size == 1 20 | puts ui.color("Specify exactly one vlan to show.", :red) 21 | show_usage 22 | exit 1 23 | end 24 | 25 | $stdout.sync = true 26 | vlan = connection(:network).networks.get(name_args[0]) 27 | 28 | puts "#{ui.color("ID:", :green)} #{vlan.id}" 29 | puts "#{ui.color("Name:", :green)} #{vlan.name ? vlan.name : '[none]'}" 30 | puts "#{ui.color("Datacenter:", :green)} #{vlan.datacenter.name}" 31 | puts "#{ui.color("Network Space:", :green)} #{vlan.network_space}" 32 | puts "#{ui.color("Router:", :green)} #{vlan.router['hostname']}" 33 | puts "#{ui.color("Subnets:", :green)}" 34 | puts Formatador.display_table(vlan.subnets.map { |s| s.attributes.reject { |k,v| k.is_a?(String) } }, [:id, :cidr, :gateway_ip, :network_id, :broadcast, :type, :datacenter, :ip_version]) 35 | 36 | end 37 | 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /lib/knife-softlayer/version.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Matt Eldridge () 3 | # © Copyright IBM Corporation 2014. 4 | # 5 | # LICENSE: Apache 2.0 (http://www.apache.org/licenses/) 6 | # 7 | 8 | module Knife 9 | module Softlayer 10 | VERSION = "0.4.7" 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /release.md: -------------------------------------------------------------------------------- 1 | 1. Build the gem 2 | ```bash 3 | $ gem build knife-softlayer.gemspec 4 | Successfully built RubyGem 5 | Name: knife-softlayer 6 | Version: 0.4.6 7 | File: knife-softlayer-0.4.6.gem 8 | ``` 9 | 2. Push the gem to RubyGem 10 | ```bash 11 | $ gem push knife-softlayer-0.4.6.gem 12 | Enter your RubyGems.org credentials. 13 | Don't have an account yet? Create one at https://rubygems.org/sign_up 14 | Email: USERNAME 15 | Password: 16 | Signed in. 17 | Pushing gem to https://rubygems.org... 18 | Successfully registered gem: knife-softlayer (0.4.6) 19 | ``` 20 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Matt Eldridge () 3 | # © Copyright IBM Corporation 2014. 4 | # 5 | # LICENSE: Apache 2.0 (http://www.apache.org/licenses/) 6 | # 7 | 8 | $:.unshift File.expand_path('../../lib', __FILE__) 9 | require 'chef' 10 | require 'chef/knife/winrm_base' 11 | require 'chef/knife/softlayer_server_create' 12 | require 'chef/knife/softlayer_server_destroy' 13 | 14 | # Clear config between each example 15 | # to avoid dependencies between examples 16 | RSpec.configure do |c| 17 | c.before(:each) do 18 | Chef::Config.reset 19 | Chef::Config[:knife] ={} 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /spec/unit/softlayer_base_spec.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Matt Eldridge () 3 | # © Copyright IBM Corporation 2014. 4 | # 5 | # LICENSE: Apache 2.0 (http://www.apache.org/licenses/) 6 | # 7 | 8 | require File.expand_path('../../spec_helper', __FILE__) 9 | require 'chef/knife/bootstrap' 10 | require 'fog/softlayer' 11 | Fog.mock! 12 | 13 | 14 | describe Chef::Knife::SoftlayerBase do 15 | 16 | describe "connection" do 17 | it "should successfully create a connection using fog" do 18 | Chef::Config[:knife][:softlayer_username] = 'username' 19 | Chef::Config[:knife][:softlayer_api_key] = 'key' 20 | Chef::Knife::SoftlayerServerCreate.new.connection 21 | Chef::Knife::SoftlayerServerCreate.new.connection.should be_a(Fog::Compute::Softlayer::Mock) 22 | end 23 | end 24 | 25 | 26 | end 27 | -------------------------------------------------------------------------------- /spec/unit/softlayer_server_create_spec.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Matt Eldridge () 3 | # © Copyright IBM Corporation 2014. 4 | # 5 | # LICENSE: Apache 2.0 (http://www.apache.org/licenses/) 6 | # 7 | 8 | require File.expand_path('../../spec_helper', __FILE__) 9 | require 'chef/knife/bootstrap' 10 | require 'fog/softlayer' 11 | Fog.mock! 12 | 13 | 14 | describe Chef::Knife::SoftlayerServerCreate do 15 | before(:each) do 16 | Chef::Config[:knife][:softlayer_username] = 'username' 17 | Chef::Config[:knife][:softlayer_api_key] = 'key' 18 | 19 | @server_create = Chef::Knife::SoftlayerServerCreate.new 20 | @server_create.stub(:tcp_test_ssh).and_return(true) 21 | 22 | @server_create.config[:ram] = 4096 23 | @server_create.config[:cores] = 4 24 | @server_create.config[:hostname] = 'test' 25 | @server_create.config[:domain] = 'ibm.com' 26 | @server_create.config[:datacenter] = 'hkg02' 27 | @server_create.config[:os_code] = 'UBUNTU_LATEST' 28 | @server_create.config[:block_storage] = '0:100' 29 | end 30 | 31 | describe "go-wrong cases for .run" do 32 | it "should raise an exception if we try to create/bootstrap a windows instance" do 33 | @server_create.config[:os_code] = 'WIN_2012-STD_64' 34 | expect { @server_create.run }.to raise_exception(Chef::Knife::SoftlayerServerCreateError) 35 | end 36 | 37 | 38 | [':ram', ':cores', ':hostname', ':domain', ':datacenter', ':os_code', ':block_storage'].each do |opt| 39 | class_eval < 42, :ssh_ip_address => '3.3.3.3', :private_ip_address => '3.3.3.3')) 59 | bootstrap.config[:distro].should == 'chef-full' 60 | end 61 | 62 | it "creates an VM instance and bootstraps it" do 63 | @server_create.run 64 | @server_create.connection.virtual_guests.count.should == 1 65 | end 66 | 67 | it "sets ssh_user value by using -x option" do 68 | #default value of config[:ssh_user] is root 69 | @server_create.config[:ssh_user] = "tim-eah!" 70 | 71 | @server_create.run 72 | @server_create.config[:ssh_user].should == "tim-eah!" 73 | @server_create.connection.virtual_guests.count.should == 1 74 | end 75 | 76 | it "sets ssh_password value by using -P option" do 77 | # default value of config[:ssh_password] is nil 78 | @server_create.config[:ssh_password] = "passw0rd" 79 | 80 | @server_create.run 81 | @server_create.config[:ssh_password].should == "passw0rd" 82 | @server_create.connection.virtual_guests.count.should == 1 83 | end 84 | 85 | it "sets ssh_port value by using -p option" do 86 | # default value of config[:ssh_port] is 22 87 | @server_create.config[:ssh_port] = "86" 88 | 89 | @server_create.run 90 | @server_create.config[:ssh_port].should == "86" 91 | @server_create.connection.virtual_guests.count.should == 1 92 | end 93 | 94 | it "sets identity_file value by using -i option for ssh bootstrap protocol or linux image" do 95 | # default value of config[:identity_file] is nil 96 | @server_create.config[:identity_file] = "~/.ssh/mah_key_file.pem" 97 | 98 | @server_create.run 99 | @server_create.config[:identity_file].should == "~/.ssh/mah_key_file.pem" 100 | @server_create.connection.virtual_guests.count.should == 1 101 | end 102 | 103 | end 104 | 105 | end 106 | -------------------------------------------------------------------------------- /spec/unit/softlayer_server_destroy_spec.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Author:: Matt Eldridge () 3 | # © Copyright IBM Corporation 2014. 4 | # 5 | # LICENSE: Apache 2.0 (http://www.apache.org/licenses/) 6 | # 7 | 8 | require File.expand_path('../../spec_helper', __FILE__) 9 | require 'chef/knife/bootstrap' 10 | require 'fog/softlayer' 11 | Fog.mock! 12 | 13 | 14 | describe Chef::Knife::SoftlayerServerDestroy do 15 | before(:each) do 16 | Chef::Config[:knife][:softlayer_username] = 'username' 17 | Chef::Config[:knife][:softlayer_api_key] = 'key' 18 | 19 | 20 | @attributes = { 21 | :id => '1000001', 22 | :name => '1000001', 23 | :flavor_id => 'medium', 24 | :fqdn => 'test.example.com', 25 | :public_ip_address => '33.33.33.33', 26 | :private_ip_address => '10.10.10.10', 27 | :tags => ['slid=1000001'] 28 | } 29 | 30 | @knife_softlayer_destroy = Chef::Knife::SoftlayerServerDestroy.new 31 | @knife_softlayer_destroy.connection.virtual_guests = @vm_attributes 32 | @knife_softlayer_destroy.node = double("node", @attributes) 33 | @knife_softlayer_destroy.stub(:destroy_item) 34 | instance = double(@attributes) 35 | @knife_softlayer_destroy.stub_chain(:connection, :servers, :get).and_return(instance) 36 | 37 | 38 | end 39 | 40 | it "should be talking to the softlayer api and the chef server" do 41 | # Chef::Search::Query.stub(:new).and_return(double("query", :search => {})) 42 | # @knife_softlayer_destroy.config[:ip_address] = '33.33.33.33' 43 | # @knife_softlayer_destroy.connection.servers.get.should_receive(:destroy) 44 | # @knife_softlayer_destroy.run 45 | end 46 | 47 | end 48 | --------------------------------------------------------------------------------