oneview-sdk 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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