Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Synergy support option 2 #99

Merged
merged 14 commits into from
Jan 12, 2017
2 changes: 1 addition & 1 deletion .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Metrics/MethodLength:
Max: 26

Metrics/ModuleLength:
Max: 165
Max: 210

Metrics/AbcSize:
Enabled: false
Expand Down
1 change: 1 addition & 0 deletions Berksfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ metadata

group :test do
cookbook 'oneview_test', path: './spec/fixtures/cookbooks/oneview_test'
cookbook 'oneview_test_api300_synergy', path: './spec/fixtures/cookbooks/oneview_test_api300_synergy'
end
77 changes: 74 additions & 3 deletions libraries/oneview_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,25 @@
# specific language governing permissions and limitations under the License.

require 'logger'
Dir[File.dirname(__FILE__) + '/resource_providers/*.rb'].each { |file| require file }

module OneviewCookbook
# Helpers for Oneview Resources
module Helper
# All-in-one method for performing an action on a resource
# @param context Context from the resource action block (self)
# @param [String, Symbol] type Name of the resource type. e.g., :EthernetNetwork
# @param [String, Symbol] action Action method to call on the resource. e.g., :create_or_update
def self.do_resource_action(context, type, action)
klass = OneviewCookbook::Helper.get_resource_class(context, type)
res = klass.new(context)
res.send(action)
end

# Get the resource class by name, taking into consideration the resource's properties
# @param context Context from the resource action block (self)
# @param [String, Symbol] type Name of the resource type. e.g., :EthernetNetwork
# @return [Class] Resource class
def self.get_resource_class(context, resource_type)
load_sdk(context)
api_version = context.property_is_set?(:api_version) ? context.api_version : context.node['oneview']['api_version']
Expand All @@ -24,14 +39,15 @@ def self.get_resource_class(context, resource_type)

# Get the API module given an api_version
# @param [Fixnum, String] api_version
# @return [Class] Resource class or nil if not found
# @return [Module] Resource module
def self.get_api_module(api_version)
OneviewCookbook.const_get("API#{api_version}")
rescue NameError
raise NameError, "The api_version #{api_version} is not supported. Please use a supported version."
end

# Load (and install if necessary) the oneview-sdk
# @param context Context from the resource action block (self)
def self.load_sdk(context)
node = context.node
gem 'oneview-sdk', node['oneview']['ruby_sdk_version']
Expand Down Expand Up @@ -80,6 +96,61 @@ def self.build_client(client = nil)
end
end

# Utility method that converts Hash symbol to string keys
# @param [Hash] info Hash containing the dataset
# @param [Symbol] conversion_method Symbol representing the method to be called in the conversion
# @return [Hash] Hash with the keys converted. Returns nil if info is invalid.
def self.convert_keys(info, conversion_method)
return nil unless info
support = {}
info.each do |k, v|
con = convert_keys(v, conversion_method) if v && v.class == Hash
support[k.public_send(conversion_method)] = con || v
end
support
end

# Get the diff of the current resource state and the desired state
# @param [OneviewSDK::Resource] resource Resource containing current state
# @param [Hash] desired_data Desired state for the resource
# @return [String] Diff string (multi-line). Returns empty string if there is no diff or an error occurred
def self.get_diff(resource, desired_data)
data = resource.is_a?(Hash) ? resource : resource.data
recursive_diff(data, desired_data, "\n", ' ')
rescue StandardError => e
Chef::Log.error "Failed to generate resource diff for '#{resource['name']}': #{e.message}"
'' # Return empty diff
end

# Get the diff of the current resource state and the desired state
# @param [Hash] data Current state of the resource
# @param [Hash] desired_data Desired state for the resource
# @param [String] str Current diff string to append to (used for recursive calls)
# @param [String] indent String used to indent the output
# @raise [StandardError] if the comparison cannot be made due to an unexpected error
# @return [String] Diff string (multi-line). Returns empty string if there is no diff
def self.recursive_diff(data, desired_data, str = '', indent = '')
unless desired_data.class == Hash
return '' if data == desired_data
return str << "\n#{indent}#{data.nil? ? 'nil' : data} -> #{desired_data}"
end
return str << "\n#{indent}nil -> #{desired_data}" if data.nil?
return str << "\n#{indent}#{data} -> #{desired_data}" unless data && data.class == Hash
desired_data.each do |key, val|
if val.is_a?(Hash)
if data[key].class == Hash
str2 = recursive_diff(data[key], val, '', "#{indent} ")
str << "\n#{indent}#{key}:#{str2}" unless str2.empty?
else
str << "\n#{indent}#{key}: #{data[key].nil? ? 'nil' : data[key]} -> #{val}"
end
elsif val != data[key]
str << "\n#{indent}#{key}: #{data[key].nil? ? 'nil' : data[key]} -> #{val}"
end
end
str
end

# Keeping all the old helper methods until we update all the resources

# Load (and install if necessary) the oneview-sdk
Expand Down Expand Up @@ -118,7 +189,7 @@ def load_resource
klass = get_resource_named(klass_name)
end
new_data = JSON.parse(data.to_json) rescue data
item = property_is_set?(:api_version) ? klass.new(c, new_data, api_version) : klass.new(c, new_data)
item = klass.new(c, new_data)
item['name'] ||= name
item
end
Expand All @@ -127,7 +198,7 @@ def load_resource
# @param [String] type OneviewSDK resource name
# @return [Class] OneviewSDK resource class
def get_resource_named(type)
klass = OneviewSDK.resource_named(type, api_version, api_variant)
klass = OneviewSDK.resource_named(type)
raise "Invalid OneView Resource type '#{type}'" unless klass
klass
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ module OneviewCookbook
# Base class for resource providers
class ResourceProvider
attr_accessor \
:context, # Ususally a Chef resource's context
:context, # Ususally a Chef resource's context (self)
:resource_name, # The Chef resource's type. e.g., oneview_ethernet_network
:name, # Name of the Chef resource. e.g., EthNet1
:item, # The OneviewSDK resource instance
Expand All @@ -27,6 +27,11 @@ def initialize(context)
@name = context.name
name_arr = self.class.to_s.split('::')
case name_arr.size
when 2 # No variant or api version specified. (OneviewCookbook::ResourceProvider)
# This case should really only be used for testing
# Uses the SDK's default api version and variant
@sdk_resource_type = name_arr.pop.gsub(/Provider$/i, '') # e.g., EthernetNetwork
klass = OneviewSDK.resource_named(@sdk_resource_type)
when 3 # No variant specified. e.g., OneviewCookbook::API200::EthernetNetworkProvider
@sdk_resource_type = name_arr.pop.gsub(/Provider$/i, '') # e.g., EthernetNetwork
@sdk_api_version = name_arr.pop.gsub(/API/i, '').to_i # e.g., 200
Expand All @@ -38,7 +43,7 @@ def initialize(context)
else # Something is wrong
raise "Can't build a resource object from the class #{self.class}"
end
klass = OneviewSDK.resource_named(@sdk_resource_type, @sdk_api_version, @sdk_variant)
klass ||= OneviewSDK.resource_named(@sdk_resource_type, @sdk_api_version, @sdk_variant)
c = OneviewCookbook::Helper.build_client(context.client)
new_data = JSON.parse(context.data.to_json) rescue context.data
@item = context.property_is_set?(:api_header_version) ? klass.new(c, new_data, context.api_header_version) : klass.new(c, new_data)
Expand Down Expand Up @@ -139,58 +144,24 @@ def save_res_info
end

# Utility method that converts Hash symbol to string keys
# @param [Hash] info Hash containing the dataset
# @param [Symbol] conversion_method Symbol representing the method to be called in the conversion
# @return [Hash] Hash with the keys converted. Returns nil if info is invalid.
# See the OneviewCookbook::Helper.convert_keys method for param details
def convert_keys(info, conversion_method)
return nil unless info
support = {}
info.each do |k, v|
con = convert_keys(v, conversion_method) if v && v.class == Hash
support[k.public_send(conversion_method)] = con || v
end
support
OneviewCookbook::Helper.convert_keys(info, conversion_method)
end

# Get the diff of the current resource state and the desired state
# @param [OneviewSDK::Resource] resource Resource containing current state
# @param [Hash] desired_data Desired state for the resource
# @return [String] Diff string (multi-line). Returns empty string if there is no diff or an error occurred
# See the OneviewCookbook::Helper.get_diff method for param details
def get_diff(resource, desired_data)
data = resource.is_a?(Hash) ? resource : resource.data
recursive_diff(data, desired_data, "\n", ' ')
rescue StandardError => e
Chef::Log.error "Failed to generate resource diff for #{@resource_name} '#{@name}': #{e.message}"
'' # Return empty diff
OneviewCookbook::Helper.get_diff(resource, desired_data)
end

# Get the diff of the current resource state and the desired state
# @param [Hash] data Current state of the resource
# @param [Hash] desired_data Desired state for the resource
# @param [String] str Current diff string to append to (used for recursive calls)
# @param [String] indent String used to indent the output
# @raise [StandardError] if the comparison cannot be made due to an unexpected error
# @return [String] Diff string (multi-line). Returns empty string if there is no diff
# See the OneviewCookbook::Helper.recursive_diff method for param details
def recursive_diff(data, desired_data, str = '', indent = '')
unless desired_data.class == Hash
return '' if data == desired_data
return str << "\n#{indent}#{data.nil? ? 'nil' : data} -> #{desired_data}"
end
return str << "\n#{indent}nil -> #{desired_data}" if data.nil?
return str << "\n#{indent}#{data} -> #{desired_data}" unless data && data.class == Hash
desired_data.each do |key, val|
if val.is_a?(Hash)
if data[key].class == Hash
str2 = recursive_diff(data[key], val, '', "#{indent} ")
str << "\n#{indent}#{key}:#{str2}" unless str2.empty?
else
str << "\n#{indent}#{key}: #{data[key].nil? ? 'nil' : data[key]} -> #{val}"
end
elsif val != data[key]
str << "\n#{indent}#{key}: #{data[key].nil? ? 'nil' : data[key]} -> #{val}"
end
end
str
OneviewCookbook::Helper.recursive_diff(data, desired_data, str, indent)
end
end
end

# Load all resource providers:
Dir[File.dirname(__FILE__) + '/resource_providers/*.rb'].each { |file| require file }
5 changes: 2 additions & 3 deletions libraries/resource_providers/api200/resource_provider.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,12 @@
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

require_relative '../../resource_provider'

module OneviewCookbook
module API200
# Base class for API200 resource providers
class ResourceProvider < OneviewCookbook::ResourceProvider
end
end
end

# Load all resource API modules:
Dir[File.dirname(__FILE__) + '/resources/*.rb'].each { |file| require file }
2 changes: 1 addition & 1 deletion libraries/resource_providers/api300.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ module API300
# @return [Class] Resource class or nil if not found
def self.resource_named(type, variant)
raise "API300 variant #{variant} is not supported!" unless SUPPORTED_VARIANTS.include?(variant.to_s)
new_type = type.to_s.downcase.gsub(/[ -_]/, '')
new_type = type.to_s.downcase.gsub(/[ -_]/, '') + 'provider'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we still be calling it resource_named?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you suggest instead? resource_provider_named ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is an option, or maybe just provider_named.

api_module = OneviewCookbook::API300.const_get(variant.to_s)
api_module.constants.each do |c|
klass = api_module.const_get(c)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

require_relative '../../api200/ethernet_network_provider'

module OneviewCookbook
module API300
module C7000
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

require_relative '../../api200/ethernet_network_provider'

module OneviewCookbook
module API300
module Synergy
Expand Down
16 changes: 4 additions & 12 deletions resources/ethernet_network.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,17 @@
default_action :create

action :create do
klass = OneviewCookbook::Helper.get_resource_class(self, :EthernetNetwork)
res = klass.new(self)
res.create_or_update
OneviewCookbook::Helper.do_resource_action(self, :EthernetNetwork, :create_or_update)
end

action :create_if_missing do
klass = OneviewCookbook::Helper.get_resource_class(self, :EthernetNetwork)
res = klass.new(self)
res.create_if_missing
OneviewCookbook::Helper.do_resource_action(self, :EthernetNetwork, :create_if_missing)
end

action :reset_connection_template do
klass = OneviewCookbook::Helper.get_resource_class(self, :EthernetNetwork)
res = klass.new(self)
res.reset_connection_template
OneviewCookbook::Helper.do_resource_action(self, :EthernetNetwork, :reset_connection_template)
end

action :delete do
klass = OneviewCookbook::Helper.get_resource_class(self, :EthernetNetwork)
res = klass.new(self)
res.delete
OneviewCookbook::Helper.do_resource_action(self, :EthernetNetwork, :delete)
end
5 changes: 2 additions & 3 deletions spec/fixtures/cookbooks/oneview_test/attributes/default.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@
# specific language governing permissions and limitations under the License.
#

# default['oneview']['ruby_sdk_version'] = '~> 3.0'
# default['oneview']['api_version'] = 300
default['oneview']['api_version'] = 200
# default['oneview']['api_variant'] = 'C7000'

default['oneview_test']['client'] = { url: 'https://oneview.example.com', user: 'Administrator', password: 'secret123' }
default['oneview_test']['client'] = { url: 'https://oneview.example.com', user: 'Administrator', password: 'secret123', api_version: 200 }
4 changes: 2 additions & 2 deletions spec/fixtures/cookbooks/oneview_test/metadata.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name 'oneview_test'
maintainer 'test cookbook'
maintainer 'none'
license 'All rights reserved'
description 'A test cookbook for the oneview cookbook'
description 'A test cookbook for the oneview cookbook API200 module'
version '0.1.1'

depends 'oneview'
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#
# Cookbook Name:: oneview_test_api300_synergy
# Attributes:: default
#
# (c) Copyright 2016 Hewlett Packard Enterprise Development LP
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
#

default['oneview']['api_version'] = 300
default['oneview']['api_variant'] = 'Synergy'

default['oneview_test']['client'] = { url: 'https://oneview.example.com', user: 'Administrator', password: 'secret123', api_version: 300 }
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
name 'oneview_test_api300_synergy'
maintainer 'none'
license 'All rights reserved'
description 'A test cookbook for the oneview cookbook API300::Synergy module'
version '0.1.0'

depends 'oneview'
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#
# Cookbook Name:: oneview_test_api300_synergy
# Recipe:: ethernet_network_create
#
# (c) Copyright 2016 Hewlett Packard Enterprise Development LP
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
#

oneview_ethernet_network 'EthNet1' do
client node['oneview_test']['client']
data(
vlanId: '1001',
purpose: 'General',
smartLink: false,
privateNetwork: false,
connectionTemplateUri: nil
)
end
Loading