oneview-sdk 1.0.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.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/.gitattributes +2 -0
  3. data/.gitignore +29 -0
  4. data/.rubocop.yml +73 -0
  5. data/.travis.yml +8 -0
  6. data/CHANGELOG.md +39 -0
  7. data/Gemfile +2 -0
  8. data/LICENSE +201 -0
  9. data/README.md +317 -0
  10. data/Rakefile +90 -0
  11. data/bin/oneview-sdk-ruby +4 -0
  12. data/lib/oneview-sdk.rb +9 -0
  13. data/lib/oneview-sdk/cli.rb +407 -0
  14. data/lib/oneview-sdk/client.rb +163 -0
  15. data/lib/oneview-sdk/config_loader.rb +20 -0
  16. data/lib/oneview-sdk/resource.rb +313 -0
  17. data/lib/oneview-sdk/resource/enclosure.rb +169 -0
  18. data/lib/oneview-sdk/resource/enclosure_group.rb +98 -0
  19. data/lib/oneview-sdk/resource/ethernet_network.rb +60 -0
  20. data/lib/oneview-sdk/resource/fc_network.rb +31 -0
  21. data/lib/oneview-sdk/resource/fcoe_network.rb +25 -0
  22. data/lib/oneview-sdk/resource/firmware_bundle.rb +37 -0
  23. data/lib/oneview-sdk/resource/firmware_driver.rb +21 -0
  24. data/lib/oneview-sdk/resource/interconnect.rb +87 -0
  25. data/lib/oneview-sdk/resource/lig_uplink_set.rb +86 -0
  26. data/lib/oneview-sdk/resource/logical_enclosure.rb +84 -0
  27. data/lib/oneview-sdk/resource/logical_interconnect.rb +283 -0
  28. data/lib/oneview-sdk/resource/logical_interconnect_group.rb +92 -0
  29. data/lib/oneview-sdk/resource/server_hardware.rb +88 -0
  30. data/lib/oneview-sdk/resource/server_hardware_type.rb +27 -0
  31. data/lib/oneview-sdk/resource/server_profile.rb +37 -0
  32. data/lib/oneview-sdk/resource/server_profile_template.rb +24 -0
  33. data/lib/oneview-sdk/resource/storage_pool.rb +41 -0
  34. data/lib/oneview-sdk/resource/storage_system.rb +63 -0
  35. data/lib/oneview-sdk/resource/uplink_set.rb +119 -0
  36. data/lib/oneview-sdk/resource/volume.rb +188 -0
  37. data/lib/oneview-sdk/resource/volume_snapshot.rb +27 -0
  38. data/lib/oneview-sdk/resource/volume_template.rb +106 -0
  39. data/lib/oneview-sdk/rest.rb +163 -0
  40. data/lib/oneview-sdk/ssl_helper.rb +75 -0
  41. data/lib/oneview-sdk/version.rb +4 -0
  42. data/oneview-sdk.gemspec +31 -0
  43. metadata +204 -0
@@ -0,0 +1,188 @@
1
+ module OneviewSDK
2
+ # Volume resource implementation
3
+ class Volume < Resource
4
+ BASE_URI = '/rest/storage-volumes'.freeze
5
+
6
+ # @!group Validates
7
+
8
+ VALID_PROVISION_TYPES = %w(Thin Full).freeze
9
+ # Validate the type of provisioning
10
+ # @param [String] value Must be Thin or Full
11
+ def validate_provisionType(value)
12
+ fail 'Invalid provision type' unless VALID_PROVISION_TYPES.include?(value)
13
+ end
14
+
15
+ # @!endgroup
16
+
17
+ # It's possible to create the volume in 6 different ways:
18
+ # 1) Common = Storage System + Storage Pool
19
+ # 2) Template = Storage Volume Template
20
+ # 3) Common with snapshots = Storage System + Storage Pool + Snapshot Pool
21
+ # 4) Management = Storage System + wwn
22
+ # 5) Management by name = Storage System + Storage System Volume Name
23
+ # 6) Snapshot = Snapshot Pool + Storage Pool + Snapshot
24
+
25
+ # Create the volume
26
+ # @note provisioningParameters are required for creation, but not afterwards; after creation, they will be removed.
27
+ # @raise [RuntimeError] if the client is not set
28
+ # @raise [RuntimeError] if the resource creation fails
29
+ # @return [Resource] self
30
+ def create
31
+ ensure_client
32
+ response = @client.rest_post(self.class::BASE_URI, { 'body' => @data }, @api_version)
33
+ body = @client.response_handler(response)
34
+ set_all(body)
35
+ @data.delete('provisioningParameters')
36
+ self
37
+ end
38
+
39
+ # Delete resource from OneView or from Oneview and storage system
40
+ # @param [Symbol] flag Delete storage system from Oneview only or in storage system as well
41
+ # @return [true] if resource was deleted successfully
42
+ def delete(flag = :all)
43
+ ensure_client && ensure_uri
44
+ case flag
45
+ when :oneview
46
+ response = @client.rest_api(:delete, @data['uri'], { 'exportOnly' => true }, @api_version)
47
+ @client.response_handler(response)
48
+ when :all
49
+ response = @client.rest_api(:delete, @data['uri'], {}, @api_version)
50
+ @client.response_handler(response)
51
+ else
52
+ fail 'Invalid flag value, use :oneview or :all'
53
+ end
54
+ true
55
+ end
56
+
57
+ # Sets the storage system to the volume
58
+ # @param [OneviewSDK::StorageSystem] storage_system Storage System
59
+ def set_storage_system(storage_system)
60
+ assure_uri(storage_system)
61
+ set('storageSystemUri', storage_system['uri'])
62
+ end
63
+
64
+ # Sets the storage pool to the volume
65
+ # @param [OneviewSDK::StoragePool] storage_pool Storage pool
66
+ def set_storage_pool(storage_pool)
67
+ assure_uri(storage_pool)
68
+ set('storagePoolUri', storage_pool['uri'])
69
+ end
70
+
71
+ # Adds storage volume template to the volume
72
+ # @param [OneviewSDK::VolumeTemplate] storage_volume_template Storage Volume Template
73
+ def set_storage_volume_template(storage_volume_template)
74
+ assure_uri(storage_volume_template)
75
+ set('templateUri', storage_volume_template['uri'])
76
+ end
77
+
78
+ # Sets the snapshot pool to the volume
79
+ # @param [OneviewSDK::StoragePool] storage_pool Storage Pool to use for snapshots
80
+ def set_snapshot_pool(storage_pool)
81
+ assure_uri(storage_pool)
82
+ set('snapshotPoolUri', storage_pool['uri'])
83
+ end
84
+
85
+ # Create a snapshot of the volume
86
+ # @param [String, OneviewSDK::VolumeSnapshot] snapshot String or OneviewSDK::VolumeSnapshot object
87
+ # @param [String] description Provide a description
88
+ # @return [true] if snapshot was created successfully
89
+ def create_snapshot(snapshot, description = nil)
90
+ ensure_uri && ensure_client
91
+ if snapshot.is_a?(OneviewSDK::Resource) || snapshot.is_a?(Hash)
92
+ name = snapshot[:name] || snapshot['name']
93
+ description ||= snapshot[:description] || snapshot['description']
94
+ else
95
+ name = snapshot
96
+ end
97
+ data = {
98
+ type: 'Snapshot',
99
+ description: description,
100
+ name: name
101
+ }
102
+ response = @client.rest_post("#{@data['uri']}/snapshots", { 'body' => data }, @api_version)
103
+ @client.response_handler(response)
104
+ true
105
+ end
106
+
107
+ # Delete a snapshot of the volume
108
+ # @param [String] name snapshot name
109
+ # @return [true] if snapshot was created successfully
110
+ def delete_snapshot(name)
111
+ result = get_snapshot(name)
112
+ response = @client.rest_api(:delete, result['uri'], {}, @api_version)
113
+ @client.response_handler(response)
114
+ true
115
+ end
116
+
117
+ # Retrieve snapshot by name
118
+ # @param [String] name
119
+ # @return [Hash] snapshot data
120
+ def get_snapshot(name)
121
+ results = get_snapshots
122
+ results.each do |snapshot|
123
+ return snapshot if snapshot['name'] == name
124
+ end
125
+ end
126
+
127
+ # Get snapshots of this volume
128
+ # @return [Array] Array of snapshots
129
+ def get_snapshots
130
+ ensure_uri && ensure_client
131
+ results = []
132
+ uri = "#{@data['uri']}/snapshots"
133
+ loop do
134
+ response = @client.rest_get(uri, @api_version)
135
+ body = @client.response_handler(response)
136
+ members = body['members']
137
+ members.each do |member|
138
+ results.push(member)
139
+ end
140
+ break unless body['nextPageUri']
141
+ uri = body['nextPageUri']
142
+ end
143
+ results
144
+ end
145
+
146
+ # Get all the attachable volumes managed by the appliance
147
+ # @param [Client] client The client object for the appliance
148
+ # @return [Array<OneviewSDK::Volume>] Array of volumes
149
+ def self.get_attachable_volumes(client)
150
+ results = []
151
+ uri = "#{BASE_URI}/attachable-volumes"
152
+ loop do
153
+ response = client.rest_get(uri)
154
+ body = client.response_handler(response)
155
+ members = body['members']
156
+ members.each { |member| results.push(OneviewSDK::Volume.new(client, member)) }
157
+ break unless body['nextPageUri']
158
+ uri = body['nextPageUri']
159
+ end
160
+ results
161
+ end
162
+
163
+ # Gets the list of extra managed storage volume paths
164
+ # @param [OneviewSDK::Client] client
165
+ # @return response
166
+ def self.get_extra_managed_volume_paths(client)
167
+ response = client.rest_get(BASE_URI + '/repair?alertFixType=ExtraManagedStorageVolumePaths')
168
+ client.response_handler(response)
169
+ end
170
+
171
+ # Removes extra presentation from volume
172
+ # @return response
173
+ def repair
174
+ response = client.rest_post(BASE_URI + '/repair', 'body' => { resourceUri: @data['uri'], type: 'ExtraManagedStorageVolumePaths' })
175
+ client.response_handler(response)
176
+ end
177
+
178
+ private
179
+
180
+ # Verify if the resource has a URI
181
+ # If not, first it tries to retrieve, and then verify for its existence
182
+ def assure_uri(resource)
183
+ resource.retrieve! unless resource['uri']
184
+ fail "#{resource.class}: #{resource['name']} not found" unless resource['uri']
185
+ end
186
+
187
+ end
188
+ end
@@ -0,0 +1,27 @@
1
+ module OneviewSDK
2
+ # Volume snapshot resource implementation
3
+ class VolumeSnapshot < Resource
4
+ BASE_URI = nil
5
+
6
+ def initialize(client, params = {}, api_ver = nil)
7
+ super
8
+ # Default values
9
+ @data['type'] ||= 'Snapshot'
10
+ end
11
+
12
+ def create
13
+ unavailable_method
14
+ end
15
+
16
+ def update
17
+ unavailable_method
18
+ end
19
+
20
+ # Sets the volume
21
+ # @param [OneviewSDK::Volume] volume Volume
22
+ def set_volume(volume)
23
+ fail 'Please set the volume\'s uri attribute!' unless volume['uri']
24
+ @data['storageVolumeUri'] = volume['uri']
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,106 @@
1
+ module OneviewSDK
2
+ # Volume template resource implementation
3
+ class VolumeTemplate < Resource
4
+ BASE_URI = '/rest/storage-volume-templates'.freeze
5
+
6
+ # Create client object, establish connection, and set up logging and api version.
7
+ # @param [Client] client The Client object with a connection to the OneView appliance
8
+ # @param [Hash] params The options for this resource (key-value pairs)
9
+ # @param [Integer] api_ver The api version to use when interracting with this resource.
10
+ # Defaults to client.api_version if exists, or OneviewSDK::Client::DEFAULT_API_VERSION.
11
+ # Defaults type to StorageVolumeTemplate when API version is 120
12
+ # Defaults type to StorageVolumeTemplateV3 when API version is 200
13
+ def initialize(client, params = {}, api_ver = nil)
14
+ super
15
+ # Default values:
16
+ @data['provisioning'] ||= {}
17
+ @data['type'] ||= 'StorageVolumeTemplateV3'
18
+ end
19
+
20
+ # @!group Validates
21
+
22
+ VALID_REFRESH_STATES = %w(NotRefreshing RefreshFailed RefreshPending Refreshing).freeze
23
+ # Validate refreshState
24
+ # @param [String] value NotRefreshing, RefreshFailed, RefreshPending, Refreshing
25
+ def validate_refreshState(value)
26
+ fail 'Invalid refresh state' unless VALID_REFRESH_STATES.include?(value)
27
+ end
28
+
29
+ VALID_STATUSES = %w(OK Disabled Warning Critical Unknown).freeze
30
+ # Validate status
31
+ # @param [String] value OK, Disabled, Warning, Critical, Unknown
32
+ def validate_status(value)
33
+ fail 'Invalid status' unless VALID_STATUSES.include?(value)
34
+ end
35
+
36
+ # @!endgroup
37
+
38
+ # Create the resource on OneView using the current data
39
+ # Adds Accept-Language attribute in the Header equal to "en_US"
40
+ # @note Calls refresh method to set additional data
41
+ # @raise [RuntimeError] if the client is not set
42
+ # @raise [RuntimeError] if the resource creation fails
43
+ # @return [Resource] self
44
+ def create
45
+ ensure_client
46
+ response = @client.rest_post(self.class::BASE_URI, { 'Accept-Language' => 'en_US', 'body' => @data }, @api_version)
47
+ body = @client.response_handler(response)
48
+ set_all(body)
49
+ end
50
+
51
+ # Delete volume template from OneView
52
+ # Adds Accept-Language attribute in the Header equal to "en_US"
53
+ # @return [TrueClass] if volume template was deleted successfully
54
+ def delete
55
+ ensure_client && ensure_uri
56
+ response = @client.rest_delete(@data['uri'], { 'Accept-Language' => 'en_US' }, @api_version)
57
+ @client.response_handler(response)
58
+ true
59
+ end
60
+
61
+ # Update volume template from OneView
62
+ # Adds Accept-Language attribute in the Header equal to "en_US"
63
+ # @return [Resource] self
64
+ def update(attributes = {})
65
+ set_all(attributes)
66
+ ensure_client && ensure_uri
67
+ response = @client.rest_put(@data['uri'], { 'Accept-Language' => 'en_US', 'body' => @data }, @api_version)
68
+ @client.response_handler(response)
69
+ self
70
+ end
71
+
72
+ # Set storage pool
73
+ # @param [Boolean] shareable
74
+ # @param [String] provisionType 'Thin' or 'Full'
75
+ # @param [String] capacity (in bytes)
76
+ # @param [OneviewSDK::StoragePool] storage_pool
77
+ def set_provisioning(shareable, provisionType, capacity, storage_pool)
78
+ @data['provisioning']['shareable'] = shareable
79
+ @data['provisioning']['provisionType'] = provisionType
80
+ @data['provisioning']['capacity'] = capacity
81
+ storage_pool.retrieve! unless storage_pool['uri']
82
+ @data['provisioning']['storagePoolUri'] = storage_pool['uri']
83
+ end
84
+
85
+ # Set storage system
86
+ # @param [OneviewSDK::StorageSystem] storage_system Storage System to be used to create the template
87
+ def set_storage_system(storage_system)
88
+ storage_system.retrieve! unless storage_system['uri']
89
+ @data['storageSystemUri'] = storage_system['uri']
90
+ end
91
+
92
+ # Set snapshot pool
93
+ # @param [OneviewSDK::StoragePool] storage_pool Storage Pool to generate the template
94
+ def set_snapshot_pool(storage_pool)
95
+ storage_pool.retrieve! unless storage_pool['uri']
96
+ @data['snapshotPoolUri'] = storage_pool['uri']
97
+ end
98
+
99
+ # Get connectable volume templates by its attributes
100
+ # @param [Hash] attributes Hash containing the attributes name and value
101
+ def get_connectable_volume_templates(attributes = {})
102
+ OneviewSDK::Resource.find_by(@client, attributes, BASE_URI + '/connectable-volume-templates')
103
+ end
104
+
105
+ end
106
+ end
@@ -0,0 +1,163 @@
1
+ require 'uri'
2
+ require 'net/http'
3
+ require 'openssl'
4
+ require 'json'
5
+
6
+ module OneviewSDK
7
+ # Contains all the methods for making API REST calls
8
+ module Rest
9
+ # Make a restful API request to OneView
10
+ # @param [Symbol] type The rest method/type Options: [:get, :post, :delete, :patch, :put]
11
+ # @param [String] path The path for the request. Usually starts with "/rest/"
12
+ # @param [Hash] options The options for the request
13
+ # @option options [String] :body Hash to be converted into json and set as the request body
14
+ # @option options [String] :Content-Type ('application/json') Set to nil or :none to have this option removed
15
+ # @option options [Integer] :X-API-Version (client.api_version) API version to use for this request
16
+ # @option options [Integer] :auth (client.token) Authentication token to use for this request
17
+ # @raise [RuntimeError] if SSL validation of OneView instance's certificate failed
18
+ # @return [NetHTTPResponse] Response object
19
+ def rest_api(type, path, options = {}, api_ver = @api_version)
20
+ @logger.debug "Making :#{type} rest call to #{@url}#{path}"
21
+ fail 'Must specify path' unless path
22
+
23
+ uri = URI.parse(URI.escape(@url + path))
24
+ http = Net::HTTP.new(uri.host, uri.port)
25
+ http.use_ssl = true if uri.scheme == 'https'
26
+ if @ssl_enabled
27
+ http.cert_store = @cert_store if @cert_store
28
+ else http.verify_mode = OpenSSL::SSL::VERIFY_NONE
29
+ end
30
+
31
+ request = build_request(type, uri, options, api_ver)
32
+ response = http.request(request)
33
+ @logger.debug " Response: Code=#{response.code}. Headers=#{response.to_hash}\n Body=#{response.body}"
34
+ response
35
+ rescue OpenSSL::SSL::SSLError => e
36
+ msg = 'SSL verification failed for request. Please either:'
37
+ msg += "\n 1. Install the certificate into your system's cert store"
38
+ msg += ". Using cert store: #{ENV['SSL_CERT_FILE']}" if ENV['SSL_CERT_FILE']
39
+ msg += "\n 2. Run oneview-sdk-ruby cert import #{@url}"
40
+ msg += "\n 3. Set the :ssl_enabled option to false for your client (NOT RECOMMENDED)"
41
+ @logger.error msg
42
+ raise e
43
+ end
44
+
45
+ # Make a restful GET request to OneView
46
+ # Parameters & return value align with those of the {OneviewSDK::Rest::rest_api} method above
47
+ def rest_get(path, api_ver = @api_version)
48
+ rest_api(:get, path, {}, api_ver)
49
+ end
50
+
51
+ # Make a restful POST request to OneView
52
+ # Parameters & return value align with those of the {OneviewSDK::Rest::rest_api} method above
53
+ def rest_post(path, options = {}, api_ver = @api_version)
54
+ rest_api(:post, path, options, api_ver)
55
+ end
56
+
57
+ # Make a restful PUT request to OneView
58
+ # Parameters & return value align with those of the {OneviewSDK::Rest::rest_api} method above
59
+ def rest_put(path, options = {}, api_ver = @api_version)
60
+ rest_api(:put, path, options, api_ver)
61
+ end
62
+
63
+ # Make a restful PATCH request to OneView
64
+ # Parameters & return value align with those of the {OneviewSDK::Rest::rest_api} method above
65
+ def rest_patch(path, options = {}, api_ver = @api_version)
66
+ rest_api(:patch, path, options, api_ver)
67
+ end
68
+
69
+ # Make a restful DELETE request to OneView
70
+ # Parameters & return value align with those of the {OneviewSDK::Rest::rest_api} method above
71
+ def rest_delete(path, options = {}, api_ver = @api_version)
72
+ rest_api(:delete, path, options, api_ver)
73
+ end
74
+
75
+ RESPONSE_CODE_OK = 200
76
+ RESPONSE_CODE_CREATED = 201
77
+ RESPONSE_CODE_ACCEPTED = 202
78
+ RESPONSE_CODE_NO_CONTENT = 204
79
+ RESPONSE_CODE_BAD_REQUEST = 400
80
+ RESPONSE_CODE_UNAUTHORIZED = 401
81
+ RESPONSE_CODE_NOT_FOUND = 404
82
+
83
+ # Handle the response from a rest call.
84
+ # If an asynchronous task was started, this waits for it to complete.
85
+ # @param [HTTPResponse] response HTTP response
86
+ # @param [Boolean] wait_on_task Wait on task (or just return task details)
87
+ # @raise [RuntimeError] if the request failed
88
+ # @raise [RuntimeError] if a task was returned that did not complete successfully
89
+ # @return [Hash] The parsed JSON body
90
+ def response_handler(response, wait_on_task = true)
91
+ case response.code.to_i
92
+ when RESPONSE_CODE_OK # Synchronous read/query
93
+ begin
94
+ return JSON.parse(response.body)
95
+ rescue JSON::ParserError => e
96
+ @logger.warn "Failed to parse JSON response. #{e}"
97
+ return response.body
98
+ end
99
+ when RESPONSE_CODE_CREATED # Synchronous add
100
+ return JSON.parse(response.body)
101
+ when RESPONSE_CODE_ACCEPTED # Asynchronous add, update or delete
102
+ return JSON.parse(response.body) unless wait_on_task
103
+ @logger.debug "Waiting for task: response.header['location']"
104
+ task = wait_for(response.header['location'])
105
+ return true unless task['associatedResource'] && task['associatedResource']['resourceUri']
106
+ resource_data = rest_get(task['associatedResource']['resourceUri'])
107
+ return JSON.parse(resource_data.body)
108
+ when RESPONSE_CODE_NO_CONTENT # Synchronous delete
109
+ return {}
110
+ when RESPONSE_CODE_BAD_REQUEST
111
+ fail "400 BAD REQUEST #{response.body}"
112
+ when RESPONSE_CODE_UNAUTHORIZED
113
+ fail "401 UNAUTHORIZED #{response.body}"
114
+ when RESPONSE_CODE_NOT_FOUND
115
+ fail "404 NOT FOUND #{response.body}"
116
+ else
117
+ fail "#{response.code} #{response.body}"
118
+ end
119
+ end
120
+
121
+
122
+ private
123
+
124
+ # Build a request object using the data given
125
+ def build_request(type, uri, options, api_ver)
126
+ case type.downcase.to_sym
127
+ when :get
128
+ request = Net::HTTP::Get.new(uri.request_uri)
129
+ when :post
130
+ request = Net::HTTP::Post.new(uri.request_uri)
131
+ when :put
132
+ request = Net::HTTP::Put.new(uri.request_uri)
133
+ when :patch
134
+ request = Net::HTTP::Patch.new(uri.request_uri)
135
+ when :delete
136
+ request = Net::HTTP::Delete.new(uri.request_uri)
137
+ else
138
+ fail "Invalid rest call: #{type}"
139
+ end
140
+
141
+ options['X-API-Version'] ||= api_ver
142
+ options['auth'] ||= @token
143
+ options['Content-Type'] ||= 'application/json'
144
+ options.delete('Content-Type') if [:none, 'none', nil].include?(options['Content-Type'])
145
+ options.delete('X-API-Version') if [:none, 'none', nil].include?(options['X-API-Version'])
146
+ options.delete('auth') if [:none, 'none', nil].include?(options['auth'])
147
+ options.each do |key, val|
148
+ if key.to_s.downcase == 'body'
149
+ request.body = val.to_json rescue val
150
+ else
151
+ request[key] = val
152
+ end
153
+ end
154
+
155
+ filtered_options = options.to_s
156
+ filtered_options.gsub!(@password, 'filtered') if @password
157
+ filtered_options.gsub!(@token, 'filtered') if @token
158
+ @logger.debug " Options: #{filtered_options}"
159
+
160
+ request
161
+ end
162
+ end
163
+ end