purest 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +284 -0
- data/lib/purest.rb +46 -0
- data/lib/purest/configuration.rb +22 -0
- data/lib/purest/custom_exceptions.rb +14 -0
- data/lib/purest/host.rb +98 -0
- data/lib/purest/host_group.rb +95 -0
- data/lib/purest/physical_array.rb +110 -0
- data/lib/purest/rest.rb +120 -0
- data/lib/purest/volume.rb +117 -0
- metadata +178 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: '0061492805748e646a06d9f5077446336d985ea40ff8ed76a3f9a5d8b60ff74d'
|
4
|
+
data.tar.gz: f58236ea14ff460b5698f0f348540c8c7fe87db762200fd37e8cb7ccfea3f505
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e51d88129e1f4807334c14ee3f9787e79bbcce33254dc83fa21450d63d67f1cdf3a687b84210df13b5eb5fad51ee5d30de71a2149e7e59672243036e287f0cea
|
7
|
+
data.tar.gz: c5f9532c491299d10b1c8e7add8bcefe1905534ca6496f84a5cf7ac5450726e2175d7fa6bdf8530e6da5cf761e61447273c623b8562c09313546a5817314dcac
|
data/README.md
ADDED
@@ -0,0 +1,284 @@
|
|
1
|
+
# Purest (or Pure Rest, if you will) -- a simple gem for interacting with Pure Storage's REST API
|
2
|
+
|
3
|
+
A simple to use library for Ruby, inspired by the WeAreFarmGeek's Diplomat gem (seriously, those guys are awesome), allowing for easy interaction with Pure Storage's REST API.
|
4
|
+
|
5
|
+
## Disclaimer
|
6
|
+
This started as sort of a labor of love/learning exercise, and sort of blossomed into this. That being said, it means a few things:
|
7
|
+
|
8
|
+
1) I may have made some stupid mistakes in here, if so..so be it. Raise them in issues or submit PRs, and I'll gladly fix/merge if I feel the code submitted carries the spirit of my little project. Odds are I won't reject a PR unless you try to rewrite everything for some obtuse reason I don't agree with.
|
9
|
+
|
10
|
+
2) I am not affiliated with Pure Storage, beyond the fact that my company uses their product.
|
11
|
+
|
12
|
+
3) This isn't done. What I think of as the 'core' functionality is there- you can manipulate volumes, hosts, and host groups, along with the array itself. I still need to add in classes for ProtectionGroups, Alerts/Messages, SNMP Manager Connections, SSL, Network Interfaces, Hardware, Apps, and Users.
|
13
|
+
|
14
|
+
## Requirements
|
15
|
+
|
16
|
+
To be captain obvious, this does require you have access to a Pure Storage array.
|
17
|
+
|
18
|
+
This library requires you use Ruby 2.3 or above.
|
19
|
+
|
20
|
+
# Usage
|
21
|
+
|
22
|
+
## Configuration
|
23
|
+
|
24
|
+
```
|
25
|
+
require 'purest'
|
26
|
+
|
27
|
+
Purest.configure do |config|
|
28
|
+
config.url = "https://purehost.yourdomain.com"
|
29
|
+
config.options = {ssl: { verify: true }}
|
30
|
+
config.api_version = '1.11'
|
31
|
+
config.username = 'api-enabled-user'
|
32
|
+
config.password = 'password'
|
33
|
+
end
|
34
|
+
```
|
35
|
+
|
36
|
+
## API options
|
37
|
+
The various class methods of this gem turn the provided options into HTTP parameters, and are
|
38
|
+
named accordingly. For instance, ```Purest::Volume.get({:snap => true})``` translates
|
39
|
+
to http://purehost.yourdomain.com/api/1.11/volume?snap=true. For a full list
|
40
|
+
of options for a given class, Pure provides good documentation at:
|
41
|
+
https://purehost.yourdomain.com/static/0/help/rest/.
|
42
|
+
|
43
|
+
Below I'll provide a large group of examples, but I won't be detailing every single method call with all of its possible options, for that I will again refer you to Pure's REST API docs.
|
44
|
+
|
45
|
+
# Examples
|
46
|
+
## Volumes
|
47
|
+
Getting volumes:
|
48
|
+
```ruby
|
49
|
+
# Get the full list of volumes
|
50
|
+
Purest::Volume.get
|
51
|
+
|
52
|
+
# Get a single volume
|
53
|
+
Purest::Volume.get(:name => 'volume1')
|
54
|
+
|
55
|
+
# Get monitoring information about a volume
|
56
|
+
Purest::Volume.get(:name => 'volume1', :action => 'monitor')
|
57
|
+
|
58
|
+
# Get multiple volumes
|
59
|
+
Purest::Volume.get(:names => ['volume1', 'volume2'])
|
60
|
+
|
61
|
+
# Get a list of snapshots
|
62
|
+
Purest::Volume.get(:snap => true)
|
63
|
+
|
64
|
+
# Get a single snapshot
|
65
|
+
Purest::Volume.get(:name => 'volume1', :snap => true)
|
66
|
+
|
67
|
+
# Get multiple snapshots
|
68
|
+
Purest::Volume.get(:names => ['volume1', 'volume2'], :snap => true)
|
69
|
+
|
70
|
+
# List block differences for the specified snapshot
|
71
|
+
Purest::Volume.get(:name => 'volume1.snap', :show_diff => true, :block_size => 512, :length => '2G')
|
72
|
+
|
73
|
+
# List shared connections for a specified volume
|
74
|
+
Purest::Volume.get(:name => 'volume1', :show_hgroup => true)
|
75
|
+
|
76
|
+
# List private connections for a specified volume
|
77
|
+
Purest::Volume.get(:name => 'volume1', :show_host => true)
|
78
|
+
```
|
79
|
+
|
80
|
+
Creating volumes:
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
# Creating a new volume
|
84
|
+
Purest::Volume.create(:name => 'volume', :size => '10G')
|
85
|
+
|
86
|
+
# Creating a new volume by copying another
|
87
|
+
Purest::Volume.create(:name => 'volume, ':source => 'other_vol')
|
88
|
+
|
89
|
+
# Overwriting a volume by copying another
|
90
|
+
Purest::Volume.create(:name => 'volume', :source => 'other_vol', :overwrite => true)
|
91
|
+
|
92
|
+
# Add a volume to a protection group
|
93
|
+
Purest::Volume.create(:name => 'volume', :pgroup => 'protection-group')
|
94
|
+
```
|
95
|
+
|
96
|
+
Updating volumes
|
97
|
+
```ruby
|
98
|
+
# Growing a volume
|
99
|
+
Purest::Volume.update(:name => 'volume', :size => "15G")
|
100
|
+
|
101
|
+
# Truncate (shrink) a volume
|
102
|
+
Purest::Volume.update(:name => 'volume', :size => "10G", :truncate => true)
|
103
|
+
|
104
|
+
# Rename a volume
|
105
|
+
Purest::Volume.update(:name => 'volume', :new_name => 'volume_renamed')
|
106
|
+
```
|
107
|
+
|
108
|
+
Deleting volumes
|
109
|
+
|
110
|
+
```ruby
|
111
|
+
# Delete a volume
|
112
|
+
Purest::Volume.delete(:name => 'volume_to_delete')
|
113
|
+
|
114
|
+
# Eradicating a volume
|
115
|
+
Purest::Volume.delete(:name => 'volume_to_delete', :eradicate => true)
|
116
|
+
|
117
|
+
# Deleting a volume from a protection group
|
118
|
+
Purest::Volume.delete(:name => 'volume', :pgroup => 'pgroup1')
|
119
|
+
```
|
120
|
+
|
121
|
+
## Hosts
|
122
|
+
Getting hosts:
|
123
|
+
```ruby
|
124
|
+
# Get a list of all the hosts on an array
|
125
|
+
Purest::Host.get
|
126
|
+
|
127
|
+
# Get a list of hosts with performance data
|
128
|
+
Purest::Host.get(:action => 'monitor')
|
129
|
+
|
130
|
+
# Get a single host on an array
|
131
|
+
Purest::Host.get(:name => 'host123')
|
132
|
+
|
133
|
+
# Get a list of hosts, by name, on an array
|
134
|
+
Purest::Host.get(:names => ['host123', 'host456'])
|
135
|
+
```
|
136
|
+
|
137
|
+
Creating hosts:
|
138
|
+
```ruby
|
139
|
+
# Create a host
|
140
|
+
Purest::Host.create({:name => 'host123'})
|
141
|
+
|
142
|
+
# Create a host and set ISCSI IQNs
|
143
|
+
Purest::Host.create(:name => 'host123', :iqnlist => ['iqnstuff-1', 'iqnstuff-2'])
|
144
|
+
|
145
|
+
# Add a host to a protection group
|
146
|
+
Purest::Host.create(:name => 'host123', :protection_group => 'pgroup123')
|
147
|
+
|
148
|
+
# Connect a volume to a host
|
149
|
+
Purest::Host.create(:name => 'host123', :volume => 'volume123')
|
150
|
+
```
|
151
|
+
|
152
|
+
Updating hosts:
|
153
|
+
```ruby
|
154
|
+
# Rename a host
|
155
|
+
Purest::Host.update(:name => 'host123', :new_name => 'host456')
|
156
|
+
|
157
|
+
# Set the host username/password for CHAP
|
158
|
+
Purest::Host.update(:name => 'host123', :host_user => 'username', :host_password => 'supersecretpassword')
|
159
|
+
```
|
160
|
+
|
161
|
+
Deleting hosts:
|
162
|
+
```ruby
|
163
|
+
# Delete a host
|
164
|
+
Purest::Host.delete(:name => 'host123')
|
165
|
+
|
166
|
+
# Remove a host from a protection group
|
167
|
+
Purest::Host.delete(:name => 'host123', :protection_group => 'pgroup123')
|
168
|
+
|
169
|
+
# Remove the connection between a host and a volume
|
170
|
+
Purest::Host.delete(:name => 'host123', :volume => 'volume123')
|
171
|
+
```
|
172
|
+
|
173
|
+
# Physical Arrays
|
174
|
+
|
175
|
+
List the attributes on an array:
|
176
|
+
```ruby
|
177
|
+
# List all the attributes
|
178
|
+
Purest::PhysicalArray.get
|
179
|
+
|
180
|
+
# List connected arrays
|
181
|
+
Purest::PhysicalArray.get(:connection => true)
|
182
|
+
```
|
183
|
+
|
184
|
+
Create a connection between two arrays:
|
185
|
+
```ruby
|
186
|
+
Purest::PhysicalArray.create(:connection_key => '<key>', :management_address => 'hostname', :type => ['replication'])
|
187
|
+
```
|
188
|
+
|
189
|
+
Update the attributes on an array:
|
190
|
+
```ruby
|
191
|
+
# rename an array
|
192
|
+
Purest::PhysicalArray.update(:new_name => 'new_name')
|
193
|
+
```
|
194
|
+
|
195
|
+
Disconnect the current array from a specified array:
|
196
|
+
```ruby
|
197
|
+
# Given that your pure is purehost.yourdomain.com, as defined in the config block above
|
198
|
+
# Disconnect purehost2.yourdomain.com from yours
|
199
|
+
Purest::PhysicalArray.delete(:name => 'purehost2.yourdomain.com')
|
200
|
+
```
|
201
|
+
|
202
|
+
# Host Groups
|
203
|
+
Getting information about host groups
|
204
|
+
```ruby
|
205
|
+
# Get a list of host groups
|
206
|
+
Purest::HostGroup.get
|
207
|
+
|
208
|
+
# Get a single host group
|
209
|
+
Purest::HostGroup.get(:name => 'hgroup1')
|
210
|
+
|
211
|
+
# Get a list of host groups, with monitoring information
|
212
|
+
Purest::HostGroup.get(:names => ['hgroup1', 'hgroup2'], :action => 'monitor')
|
213
|
+
|
214
|
+
# Get a list of volumes associated with a specified host
|
215
|
+
Purest::HostGroup.get(:name => 'hgroup1', :show_volumes => true)
|
216
|
+
```
|
217
|
+
|
218
|
+
Creating host groups
|
219
|
+
```ruby
|
220
|
+
# Create a host group with a specified name
|
221
|
+
Purest::HostGroup.create(:name => 'hgroup1')
|
222
|
+
|
223
|
+
# Create a host group and supply its host members
|
224
|
+
Purest::HostGroup.create(:name => 'hgroup1', :hostlist => ['host1', 'host2'])
|
225
|
+
|
226
|
+
# Add a host group to a protection group
|
227
|
+
Purest::HostGroup.create(:name => 'hgroup1', :protection_group => 'pgroup1')
|
228
|
+
|
229
|
+
# Connect a volume to all hosts in a specified host group
|
230
|
+
Purest::HostGroup.create(:name => 'hgroup1', :volume => 'v3')
|
231
|
+
```
|
232
|
+
|
233
|
+
Updating host groups
|
234
|
+
```ruby
|
235
|
+
# Renaming a host group
|
236
|
+
Purest::HostGroup.update(:name => 'hgroup1', :new_name => 'hgroup1-renamed')
|
237
|
+
|
238
|
+
# Replace the list of member hosts
|
239
|
+
Purest::HostGroup.update(:name => 'hgroup1', :hostlist => ['host1', 'host2'])
|
240
|
+
|
241
|
+
# Add a list of hosts to existing host list
|
242
|
+
Purest::HostGroup.update(:name => 'hgroup1', :addhostlist => ['host3', 'host4'])
|
243
|
+
|
244
|
+
# Remove a list of hosts from a host list
|
245
|
+
Purest::HostGroup.update(:name => 'hgroup1', :remhostlist => ['host1'])
|
246
|
+
```
|
247
|
+
|
248
|
+
Deleting host groups
|
249
|
+
```ruby
|
250
|
+
# Delete a host group
|
251
|
+
Purest::HostGroup.delete(:name => 'hgroup1')
|
252
|
+
|
253
|
+
# Remove a host group member from a protection group
|
254
|
+
Purest::HostGroup.delete(:name => 'hgroup1', :protection_group => 'pgroup1')
|
255
|
+
|
256
|
+
# Break the connection between a host group and a volume
|
257
|
+
Purest::HostGroup.delete(:name => 'hgroup1', :volume => 'volume1')
|
258
|
+
```
|
259
|
+
|
260
|
+
# Specs
|
261
|
+
This library is tested with rspec, to execute the specs merely run
|
262
|
+
```
|
263
|
+
rspec
|
264
|
+
```
|
265
|
+
|
266
|
+
This project also supports an integration test suite. However, you must have API access to a pure array for this integration test suite to work.
|
267
|
+
|
268
|
+
Create an .integration.yaml inside the project, containing your pure array URL and credentials like so:
|
269
|
+
```
|
270
|
+
$ cat ~/ruby/purest/.integration.yaml
|
271
|
+
---
|
272
|
+
username: 'api-user'
|
273
|
+
password: 'api-password'
|
274
|
+
url: 'https://yoursuperawesomepurehost.com'
|
275
|
+
```
|
276
|
+
|
277
|
+
Execute integration tests:
|
278
|
+
```
|
279
|
+
rspec -t integration
|
280
|
+
```
|
281
|
+
|
282
|
+
By default, this will run against version 1.1 to 1.11 of the API. This is useful for ensuring functionality added/subtracted to this project is programmatically tested against all versions of the API. I mostly did this as an exercise, that being said I think it provides a lot of usefulness and if it can be improved let me know (or submit a PR).
|
283
|
+
|
284
|
+
It is worth mentioning, this generates a fair bit of work for your Pure array so...you've been warned.
|
data/lib/purest.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'time'
|
4
|
+
require 'json'
|
5
|
+
require 'faraday'
|
6
|
+
require 'faraday_middleware'
|
7
|
+
require 'faraday-cookie_jar'
|
8
|
+
require 'pry'
|
9
|
+
require 'psych'
|
10
|
+
|
11
|
+
module Purest
|
12
|
+
class << self
|
13
|
+
attr_accessor :root_path
|
14
|
+
attr_accessor :lib_path
|
15
|
+
attr_accessor :configuration
|
16
|
+
|
17
|
+
# Internal: Requires internal Faraday libraries.
|
18
|
+
# @param *libs One or more relative String names to Faraday classes.
|
19
|
+
# @return [nil]
|
20
|
+
def require_libs(*libs)
|
21
|
+
libs.each do |lib|
|
22
|
+
require "#{lib_path}/#{lib}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
alias require_lib require_libs
|
27
|
+
end
|
28
|
+
|
29
|
+
raise 'Purest only supports ruby >= 2.3.0' unless RUBY_VERSION.to_f >= 2.3
|
30
|
+
|
31
|
+
self.root_path = File.expand_path __dir__
|
32
|
+
self.lib_path = File.expand_path 'purest', __dir__
|
33
|
+
|
34
|
+
require_libs 'custom_exceptions', 'configuration', 'rest', 'host', 'host_group', 'volume', 'physical_array'
|
35
|
+
|
36
|
+
self.configuration ||= Purest::Configuration.new
|
37
|
+
|
38
|
+
class << self
|
39
|
+
# Build optional configuration by yielding a block to configure
|
40
|
+
# @yield [Purest::Configuration]
|
41
|
+
def configure
|
42
|
+
self.configuration ||= Purest::Configuration.new
|
43
|
+
yield(configuration)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Purest
|
4
|
+
# Methods for configuring Purest
|
5
|
+
class Configuration
|
6
|
+
attr_accessor :api_version, :url, :username, :password, :options
|
7
|
+
|
8
|
+
# Override defaults for configuration
|
9
|
+
# @param api_version [String] the API version to interact with
|
10
|
+
# @param url [String] pure's connection URL
|
11
|
+
# @param username [String] username to authenticate with
|
12
|
+
# @param password [String] password to authenticate with
|
13
|
+
# @param options [Hash] extra options to configure Faraday::Connection
|
14
|
+
def initialize(api_version = '1.11', url = nil, username = nil, password = nil, options = {})
|
15
|
+
@api_version = api_version
|
16
|
+
@url = url
|
17
|
+
@username = username
|
18
|
+
@password = password
|
19
|
+
@options = options
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/purest/host.rb
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Purest
|
4
|
+
class Host < Purest::Rest
|
5
|
+
@access_methods = %i[get create update delete]
|
6
|
+
|
7
|
+
GET_PARAMS = [:action, :all, :chap, :connect, :names, :personality,
|
8
|
+
:private, :protect, :shared, :space]
|
9
|
+
|
10
|
+
# Get a list of hosts, GET
|
11
|
+
# @param options [Hash] options to pass
|
12
|
+
def get(options = nil)
|
13
|
+
@options = options
|
14
|
+
create_session unless authenticated?
|
15
|
+
|
16
|
+
raw_resp = @conn.get do |req|
|
17
|
+
url = ["/api/#{Purest.configuration.api_version}/host"]
|
18
|
+
url.map!{|u| u + "/#{@options[:name]}"} if !@options.nil? && @options[:name]
|
19
|
+
url.map!{|u| u + "/volume"} if !@options.nil? && @options[:show_volumes]
|
20
|
+
|
21
|
+
# Generate array, consisting of url parts, to be built
|
22
|
+
# by concat_url method below
|
23
|
+
GET_PARAMS.each do |param|
|
24
|
+
url += self.send(:"use_#{param}",@options)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Build url from array of parts, send request
|
28
|
+
req.url concat_url url
|
29
|
+
end
|
30
|
+
|
31
|
+
JSON.parse(raw_resp.body, :symbolize_names => true)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Create a host, POST
|
35
|
+
# @param options [Hash] options to pass
|
36
|
+
def create(options = nil)
|
37
|
+
@options = options
|
38
|
+
|
39
|
+
raise RequiredArgument, ':name required when creating a host' if @options.nil? || @options[:name].nil?
|
40
|
+
create_session unless authenticated?
|
41
|
+
|
42
|
+
raw_resp = @conn.post do |req|
|
43
|
+
url = ["/api/#{Purest.configuration.api_version}/host/#{@options[:name]}"]
|
44
|
+
url.map!{|u| u + "/pgroup/#{@options[:protection_group]}"} if @options[:protection_group]
|
45
|
+
url.map!{|u| u + "/volume/#{@options[:volume]}"} if @options[:volume]
|
46
|
+
req.body = @options.to_json
|
47
|
+
|
48
|
+
req.url concat_url url
|
49
|
+
end
|
50
|
+
|
51
|
+
JSON.parse(raw_resp.body, :symbolize_names => true)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Update a host, PUT
|
55
|
+
# @param options [Hash] options to pass
|
56
|
+
def update(options = nil)
|
57
|
+
@options = options
|
58
|
+
|
59
|
+
create_session unless authenticated?
|
60
|
+
|
61
|
+
raw_resp = @conn.put do |req|
|
62
|
+
req.url "/api/#{Purest.configuration.api_version}/host/#{@options[:name]}"
|
63
|
+
|
64
|
+
# Repurpose @options[:name] so that it is now set to the new_name
|
65
|
+
# allowing us to rename a volume.
|
66
|
+
@options[:name] = @options.delete(:new_name) if @options[:new_name]
|
67
|
+
req.body = @options.to_json
|
68
|
+
end
|
69
|
+
|
70
|
+
JSON.parse(raw_resp.body, :symbolize_names => true)
|
71
|
+
end
|
72
|
+
|
73
|
+
def delete(options = nil)
|
74
|
+
@options = options
|
75
|
+
|
76
|
+
create_session unless authenticated?
|
77
|
+
|
78
|
+
raw_resp = @conn.delete do |req|
|
79
|
+
url = ["/api/#{Purest.configuration.api_version}/host/#{@options[:name]}"]
|
80
|
+
url.map!{|u| u + "/pgroup/#{@options[:protection_group]}"} if @options[:protection_group]
|
81
|
+
url.map!{|u| u + "/volume/#{@options[:volume]}"} if @options[:volume]
|
82
|
+
|
83
|
+
req.url concat_url url
|
84
|
+
end
|
85
|
+
|
86
|
+
JSON.parse(raw_resp.body, :symbolize_names => true)
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
# Dynamic method generation, e.g. use_all, use_action, etc
|
92
|
+
GET_PARAMS.each do |param|
|
93
|
+
define_method :"use_#{param}" do |options|
|
94
|
+
options ? use_named_parameter(param, options[param]) : []
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
#
|
3
|
+
module Purest
|
4
|
+
class HostGroup < Purest::Rest
|
5
|
+
@access_methods = %i[get create update delete]
|
6
|
+
|
7
|
+
GET_PARAMS = [:action, :block_size, :connect, :historical, :length, :names,
|
8
|
+
:pending, :pending_only, :pgrouplist, :private, :protect,
|
9
|
+
:shared, :snap, :space]
|
10
|
+
|
11
|
+
# Get a list of hostgroups, GET
|
12
|
+
# @param options [Hash] options to pass in
|
13
|
+
def get(options = nil)
|
14
|
+
@options = options
|
15
|
+
create_session unless authenticated?
|
16
|
+
|
17
|
+
raw_resp = @conn.get do |req|
|
18
|
+
url = ["/api/#{Purest.configuration.api_version}/hgroup"]
|
19
|
+
url.map!{|u| u + "/#{@options[:name]}"} if !@options.nil? && @options[:name]
|
20
|
+
url.map!{|u| u + "/volume"} if !@options.nil? && @options[:name] && @options[:show_volumes]
|
21
|
+
|
22
|
+
# Generate array, consisting of url parts, to be built
|
23
|
+
# by concat_url method below
|
24
|
+
GET_PARAMS.each do |param|
|
25
|
+
url += self.send(:"use_#{param}",@options)
|
26
|
+
end
|
27
|
+
|
28
|
+
req.url concat_url url
|
29
|
+
end
|
30
|
+
|
31
|
+
JSON.parse(raw_resp.body, :symbolize_names => true)
|
32
|
+
end
|
33
|
+
|
34
|
+
def create(options = nil)
|
35
|
+
@options = options
|
36
|
+
create_session unless authenticated?
|
37
|
+
|
38
|
+
raw_resp = @conn.post do |req|
|
39
|
+
url = ["/api/#{Purest.configuration.api_version}/hgroup/#{@options[:name]}"]
|
40
|
+
url.map!{|u| u + "/pgroup/#{@options[:protection_group]}"} if @options[:protection_group]
|
41
|
+
url.map!{|u| u + "/volume/#{@options[:volume]}"} if @options[:volume]
|
42
|
+
|
43
|
+
req.body = @options.to_json
|
44
|
+
req.url concat_url url
|
45
|
+
end
|
46
|
+
|
47
|
+
JSON.parse(raw_resp.body, :symbolize_names => true)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Update a host group, PUT
|
51
|
+
# @param options [Hash] options to pass
|
52
|
+
def update(options = nil)
|
53
|
+
@options = options
|
54
|
+
|
55
|
+
create_session unless authenticated?
|
56
|
+
|
57
|
+
raw_resp = @conn.put do |req|
|
58
|
+
req.url "/api/#{Purest.configuration.api_version}/hgroup/#{@options[:name]}"
|
59
|
+
|
60
|
+
# Repurpose @options[:name] so that it is now set to the new_name
|
61
|
+
# allowing us to rename a volume.
|
62
|
+
@options[:name] = @options.delete(:new_name) if @options[:new_name]
|
63
|
+
req.body = @options.to_json
|
64
|
+
end
|
65
|
+
|
66
|
+
JSON.parse(raw_resp.body, :symbolize_names => true)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Delete a host group, DELETE
|
70
|
+
# @param options[Hash] options to pass
|
71
|
+
def delete(options = nil)
|
72
|
+
@options = options
|
73
|
+
|
74
|
+
create_session unless authenticated?
|
75
|
+
|
76
|
+
raw_resp = @conn.delete do |req|
|
77
|
+
url = ["/api/#{Purest.configuration.api_version}/hgroup/#{@options[:name]}"]
|
78
|
+
url.map!{|u| u + "/pgroup/#{@options[:protection_group]}"} if @options[:protection_group]
|
79
|
+
url.map!{|u| u + "/volume/#{@options[:volume]}"} if @options[:volume]
|
80
|
+
|
81
|
+
req.url concat_url url
|
82
|
+
end
|
83
|
+
|
84
|
+
JSON.parse(raw_resp.body, :symbolize_names => true)
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
# Dynamic method generation, e.g. use_all, use_action, etc
|
89
|
+
GET_PARAMS.each do |param|
|
90
|
+
define_method :"use_#{param}" do |options|
|
91
|
+
options ? use_named_parameter(param, options[param]) : []
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Purest
|
4
|
+
class PhysicalArray < Purest::Rest
|
5
|
+
@access_methods = %i[get create update delete]
|
6
|
+
|
7
|
+
GET_PARAMS = [:action, :banner, :connection_key, :controllers, :historical,
|
8
|
+
:idle_timeout, :ntpserver, :phonehome, :proxy, :relayhost, :scsi_timeout,
|
9
|
+
:senderdomain, :space, :syslogserver, :throttle]
|
10
|
+
|
11
|
+
# Get a list of hosts, GET
|
12
|
+
# @param options [Hash] options to pass
|
13
|
+
def get(options = nil)
|
14
|
+
@options = options
|
15
|
+
create_session unless authenticated?
|
16
|
+
|
17
|
+
raw_resp = @conn.get do |req|
|
18
|
+
url = ["/api/#{Purest.configuration.api_version}/array"]
|
19
|
+
|
20
|
+
# Map /connection, /console_lock, /phonehome, /remoteassist
|
21
|
+
# depending on what was passed in
|
22
|
+
[:connection, :console_lock, :phonehome, :remoteassist].each do |path|
|
23
|
+
url.map!{|u| u + "/#{path.to_s}"} if !@options.nil? && @options[path]
|
24
|
+
end
|
25
|
+
|
26
|
+
# Generate array, consisting of url parts, to be built
|
27
|
+
# by concat_url method below
|
28
|
+
GET_PARAMS.each do |param|
|
29
|
+
url += self.send(:"use_#{param}",@options)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Build url from array of parts, send request
|
33
|
+
req.url concat_url url
|
34
|
+
end
|
35
|
+
|
36
|
+
JSON.parse(raw_resp.body, :symbolize_names => true)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Create a connection between two arrays
|
40
|
+
# @param options [Hash] options to pass
|
41
|
+
def create(options = nil)
|
42
|
+
@options = options
|
43
|
+
|
44
|
+
raw_resp = @conn.post do |req|
|
45
|
+
req.url "/api/#{Purest.configuration.api_version}/array/connection"
|
46
|
+
req.body = @options.to_json
|
47
|
+
end
|
48
|
+
|
49
|
+
JSON.parse(raw_resp.body, :symbolize_names => true)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Update attributes on an array, PUT
|
53
|
+
# @param options [Hash] options to pass in
|
54
|
+
def update(options = nil)
|
55
|
+
@options = options
|
56
|
+
|
57
|
+
raw_resp = @conn.put do |req|
|
58
|
+
url = ["/api/#{Purest.configuration.api_version}/array"]
|
59
|
+
|
60
|
+
if !@options.nil? && @options[:connected_array]
|
61
|
+
url.map!{|u| u + "/connection/#{@options[:connected_array]}"}
|
62
|
+
end
|
63
|
+
|
64
|
+
# Small conditional to ease remote assistance connecting/disconnecting
|
65
|
+
if !@options.nil? && @options[:remote_assist]
|
66
|
+
url.map!{|u| u + "/remoteassist"}
|
67
|
+
@options[:action] = @options.delete(:remote_assist)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Small loop to ease console locking and home phoning
|
71
|
+
[:console_lock, :phonehome].each do |path|
|
72
|
+
if !@options.nil? && @options[path]
|
73
|
+
url.map!{|u| u + "/#{path.to_s}"}
|
74
|
+
@options[:enabled] = @options.delete(path)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Keeping things consistent
|
79
|
+
@options[:name] = @options.delete(:new_name) if @options[:new_name]
|
80
|
+
|
81
|
+
# Don't send this as part of the JSON body if it's present
|
82
|
+
# in the options hash, as it's not a valid param
|
83
|
+
@options.delete(:connected_array)
|
84
|
+
req.body = @options.to_json
|
85
|
+
|
86
|
+
req.url concat_url url
|
87
|
+
end
|
88
|
+
|
89
|
+
JSON.parse(raw_resp.body, :symbolize_names => true)
|
90
|
+
end
|
91
|
+
|
92
|
+
# Disconnect one array from another
|
93
|
+
# @param options [Hash] options to pass in
|
94
|
+
def delete(options = nil)
|
95
|
+
@options = options
|
96
|
+
|
97
|
+
raw_resp = @conn.delete do |req|
|
98
|
+
req.url "/api/#{Purest.configuration.api_version}/array/connection/#{@options[:connected_array]}"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
GET_PARAMS.each do |attribute|
|
105
|
+
define_method :"use_#{attribute}" do |options|
|
106
|
+
options ? use_named_parameter(attribute, options[attribute]) : []
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
data/lib/purest/rest.rb
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Purest
|
4
|
+
# Base class for interacting with PURE storage REST API
|
5
|
+
class Rest < Purest::CustomExceptions
|
6
|
+
@access_methods = []
|
7
|
+
@get_params = []
|
8
|
+
|
9
|
+
# Initialize the fadaray connection, create session unless one exists
|
10
|
+
def initialize
|
11
|
+
establish_connection
|
12
|
+
create_session unless authenticated?
|
13
|
+
end
|
14
|
+
|
15
|
+
# Check if session exists, and whether or not it's expired
|
16
|
+
def authenticated?
|
17
|
+
if defined? @session_expire
|
18
|
+
if Time.now.utc < @session_expire
|
19
|
+
true
|
20
|
+
else
|
21
|
+
false
|
22
|
+
end
|
23
|
+
else
|
24
|
+
false
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Assemble a url from an array of parts.
|
29
|
+
# @param parts [Array] the url chunks to be assembled
|
30
|
+
# @return [String] the resultant url string
|
31
|
+
def concat_url(parts)
|
32
|
+
if parts.length > 1
|
33
|
+
parts.first + '?' + parts.drop(1).join('&')
|
34
|
+
else
|
35
|
+
parts.first
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Format url parameters into strings correctly
|
40
|
+
# @param name [String] the name of the parameter
|
41
|
+
# @param value [String] the value of the parameter
|
42
|
+
# @return [Array] the resultant parameter string inside an array.
|
43
|
+
def use_named_parameter(name, value)
|
44
|
+
if value.is_a? Array
|
45
|
+
["#{name}=#{value.join(',')}"]
|
46
|
+
else
|
47
|
+
value ? ["#{name}=#{value}"] : []
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Logout current session
|
52
|
+
def logout
|
53
|
+
raw_resp = @conn.delete do |req|
|
54
|
+
req.url "/api/#{Purest.configuration.api_version}/auth/session"
|
55
|
+
end
|
56
|
+
remove_instance_variable(:@session_expire)
|
57
|
+
end
|
58
|
+
|
59
|
+
class << self
|
60
|
+
def access_method?(meth_id)
|
61
|
+
@access_methods.include? meth_id
|
62
|
+
end
|
63
|
+
|
64
|
+
def method_missing(meth_id, *args)
|
65
|
+
if access_method?(meth_id)
|
66
|
+
new.send(meth_id, *args)
|
67
|
+
else
|
68
|
+
|
69
|
+
# See https://bugs.ruby-lang.org/issues/10969
|
70
|
+
begin
|
71
|
+
super
|
72
|
+
rescue NameError => err
|
73
|
+
raise NoMethodError, err
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def get_token
|
82
|
+
raw_resp = @conn.post do |req|
|
83
|
+
req.url "/api/#{Purest.configuration.api_version}/auth/apitoken"
|
84
|
+
req.params = {
|
85
|
+
"username": Purest.configuration.username.to_s,
|
86
|
+
"password": Purest.configuration.password.to_s
|
87
|
+
}
|
88
|
+
end
|
89
|
+
|
90
|
+
JSON.parse(raw_resp.body)['api_token']
|
91
|
+
end
|
92
|
+
|
93
|
+
def create_session
|
94
|
+
raw_resp = @conn.post do |req|
|
95
|
+
req.url "/api/#{Purest.configuration.api_version}/auth/session"
|
96
|
+
req.params = {
|
97
|
+
"api_token": get_token
|
98
|
+
}
|
99
|
+
end
|
100
|
+
|
101
|
+
# Man they really tuck that away, don't they?
|
102
|
+
@session_expire = Time.parse(raw_resp.env.response_headers['set-cookie'].split(';')[1].split(',')[1].strip).utc
|
103
|
+
|
104
|
+
raw_resp
|
105
|
+
end
|
106
|
+
|
107
|
+
# Build the API Client
|
108
|
+
def establish_connection
|
109
|
+
@conn = build_connection
|
110
|
+
end
|
111
|
+
|
112
|
+
def build_connection
|
113
|
+
Faraday.new(Purest.configuration.url, Purest.configuration.options) do |faraday|
|
114
|
+
faraday.request :json
|
115
|
+
faraday.use :cookie_jar
|
116
|
+
faraday.adapter Faraday.default_adapter
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Purest
|
4
|
+
class Volume < Purest::Rest
|
5
|
+
@access_methods = %i[get create update delete]
|
6
|
+
|
7
|
+
GET_PARAMS = [:action, :block_size, :connect, :historical, :length, :names,
|
8
|
+
:pending, :pending_only, :pgrouplist, :private, :protect,
|
9
|
+
:shared, :snap, :space]
|
10
|
+
|
11
|
+
# Get a list of volumes, GET
|
12
|
+
# @param options [Hash] options to pass
|
13
|
+
def get(options = nil)
|
14
|
+
@options = options
|
15
|
+
create_session unless authenticated?
|
16
|
+
|
17
|
+
# Build up a url from parts, allowing for dynamic http calls
|
18
|
+
# by simply passing in params accepted by Pure's API
|
19
|
+
raw_resp = @conn.get do |req|
|
20
|
+
url = ["/api/#{Purest.configuration.api_version}/volume"]
|
21
|
+
|
22
|
+
url.map!{|u| u + "/#{@options[:name]}"} if !@options.nil? && @options[:name]
|
23
|
+
|
24
|
+
# Map /diff, /hgroup, or /host depending on option passed
|
25
|
+
[:show_diff, :show_hgroup, :show_host].each do |path|
|
26
|
+
new_path = path.to_s.gsub('show_', '')
|
27
|
+
url.map!{|u| u + "/#{new_path}"} if !@options.nil? && @options[path]
|
28
|
+
end
|
29
|
+
|
30
|
+
# Generate array, consisting of url parts, to be built
|
31
|
+
# by concat_url method below
|
32
|
+
GET_PARAMS.each do |param|
|
33
|
+
url += self.send(:"use_#{param}",@options)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Build url from array of parts, send request
|
37
|
+
req.url concat_url url
|
38
|
+
end
|
39
|
+
|
40
|
+
JSON.parse(raw_resp.body, :symbolize_names => true)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Create a volume, POST
|
44
|
+
# @param options [Hash] options to pass
|
45
|
+
def create(options = nil)
|
46
|
+
@options = options
|
47
|
+
|
48
|
+
raw_resp = @conn.post do |req|
|
49
|
+
url = "/api/#{Purest.configuration.api_version}/volume"
|
50
|
+
url += "/#{@options[:name]}" if @options[:name]
|
51
|
+
url += "/pgroup/#{@options[:pgroup]}" if @options[:pgroup]
|
52
|
+
req.body = @options.to_json
|
53
|
+
|
54
|
+
req.url url
|
55
|
+
end
|
56
|
+
|
57
|
+
JSON.parse(raw_resp.body, :symbolize_names => true)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Update a volume, PUT
|
61
|
+
# @param options [Hash] options to pass
|
62
|
+
def update(options = nil)
|
63
|
+
@options = options
|
64
|
+
|
65
|
+
raw_resp = @conn.put do |req|
|
66
|
+
# Here we construct the url first, because the options hash
|
67
|
+
# may have to be manipulated below
|
68
|
+
req.url "/api/#{Purest.configuration.api_version}/volume/#{@options[:name]}"
|
69
|
+
|
70
|
+
# Repurpose @options[:name] so that it is now set to the new_name
|
71
|
+
# allowing us to rename a volume.
|
72
|
+
@options[:name] = @options.delete(:new_name) if @options[:new_name]
|
73
|
+
|
74
|
+
# Remove name from @options which is sent in the body,
|
75
|
+
# if size is passed in, because those two cannot be sent together.
|
76
|
+
@options.delete(:name) if @options[:size]
|
77
|
+
|
78
|
+
req.body = @options.to_json
|
79
|
+
end
|
80
|
+
|
81
|
+
JSON.parse(raw_resp.body, :symbolize_names => true)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Delete a volume, DELETE
|
85
|
+
# @param options [Hash] options to pass
|
86
|
+
def delete(options = nil)
|
87
|
+
@options = options
|
88
|
+
|
89
|
+
raw_resp = @conn.delete do |req|
|
90
|
+
url = "/api/#{Purest.configuration.api_version}/volume/#{@options[:name]}"
|
91
|
+
url += "/pgroup/#{@options[:pgroup]}" if @options[:pgroup]
|
92
|
+
|
93
|
+
req.url url
|
94
|
+
end
|
95
|
+
|
96
|
+
if @options[:eradicate]
|
97
|
+
raw_resp = @conn.delete do |req|
|
98
|
+
url = ["/api/#{Purest.configuration.api_version}/volume/#{@options[:name]}"]
|
99
|
+
req.body = @options.to_json
|
100
|
+
|
101
|
+
req.url concat_url url
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
JSON.parse(raw_resp.body, :symbolize_names => true)
|
106
|
+
end
|
107
|
+
|
108
|
+
private
|
109
|
+
|
110
|
+
# Dynamic method generation, e.g. use_connect, use_eradicate, etc
|
111
|
+
GET_PARAMS.each do |param|
|
112
|
+
define_method :"use_#{param}" do |options|
|
113
|
+
options ? use_named_parameter(param, options[param]) : []
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
metadata
ADDED
@@ -0,0 +1,178 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: purest
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sean McKinley
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-06-29 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: cucumber
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: pry
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.11.3
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.11.3
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 3.7.0
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 3.7.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: fakes-rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.1'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '2.1'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rubocop
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.57.2
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.57.2
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: webmock
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 3.4.0
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 3.4.0
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: faraday
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 0.15.2
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 0.15.2
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: faraday_middleware
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 0.12.2
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 0.12.2
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: faraday-cookie_jar
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: 0.0.6
|
132
|
+
type: :runtime
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: 0.0.6
|
139
|
+
description:
|
140
|
+
email: sean.mckinley@outlook.com
|
141
|
+
executables: []
|
142
|
+
extensions: []
|
143
|
+
extra_rdoc_files: []
|
144
|
+
files:
|
145
|
+
- README.md
|
146
|
+
- lib/purest.rb
|
147
|
+
- lib/purest/configuration.rb
|
148
|
+
- lib/purest/custom_exceptions.rb
|
149
|
+
- lib/purest/host.rb
|
150
|
+
- lib/purest/host_group.rb
|
151
|
+
- lib/purest/physical_array.rb
|
152
|
+
- lib/purest/rest.rb
|
153
|
+
- lib/purest/volume.rb
|
154
|
+
homepage:
|
155
|
+
licenses:
|
156
|
+
- MIT
|
157
|
+
metadata: {}
|
158
|
+
post_install_message:
|
159
|
+
rdoc_options: []
|
160
|
+
require_paths:
|
161
|
+
- lib
|
162
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
168
|
+
requirements:
|
169
|
+
- - ">="
|
170
|
+
- !ruby/object:Gem::Version
|
171
|
+
version: '0'
|
172
|
+
requirements: []
|
173
|
+
rubyforge_project:
|
174
|
+
rubygems_version: 2.7.6
|
175
|
+
signing_key:
|
176
|
+
specification_version: 4
|
177
|
+
summary: A ruby gem for interacting with PURE storage's REST API
|
178
|
+
test_files: []
|