Skip to content

Commit 6578276

Browse files
committed
Added VLANFirewalls and some related models
1 parent aa72f9f commit 6578276

18 files changed

Lines changed: 288 additions & 67 deletions

examples/order_bare_metal_package.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,11 +129,11 @@ def tl_dr_version
129129
# We have a configuration for the server, we also need a location for the new server.
130130
# The package can give us a list of locations. Let's print out that list
131131
puts "\nData Centers for '#{quad_intel_package.name}':"
132-
quad_intel_package.datacenter_options.each { |location| puts "\t#{location}"}
132+
quad_intel_package.datacenter_options.each { |datacenter| puts "\t#{datacenter.name}"}
133133

134134
# With all the config options in place we can now construct the product order.
135135
server_order = SoftLayer::BareMetalServerOrder_Package.new(quad_intel_package, client)
136-
server_order.datacenter = 'sng01'
136+
server_order.datacenter = SoftLayer::Datacenter.datacenter_named 'sng01', client
137137
server_order.hostname = 'sample'
138138
server_order.domain = 'softlayerapi.org'
139139
server_order.configuration_options = config_options

lib/softlayer/APIParameterFilter.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ def server_result_offset
173173
##
174174
# A utility method that returns the object filter (if any) stored with this filter.
175175
def server_object_filter
176-
self.parameters[:object_filter].to_h
176+
self.parameters[:object_filter].to_h if self.parameters.has_key?(:object_filter)
177177
end
178178

179179
##

lib/softlayer/Client.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class Client
3232

3333
# A string passsed as the value for the User-Agent header when requests are sent to SoftLayer API.
3434
attr_accessor :user_agent
35-
35+
3636
# An integer value (in seconds). The number of seconds to wait for HTTP requests to the network API
3737
# until they timeout. This value can be nil in which case the timeout will be the default value for
3838
# the library handling network communication (often 30 seconds)
@@ -84,8 +84,10 @@ def initialize(options = {})
8484
# and the endpoint url
8585
@endpoint_url = settings[:endpoint_url] || API_PUBLIC_ENDPOINT
8686

87+
# set the user agent to the one provided, or set it to a default one
8788
@user_agent = settings[:user_agent] || "softlayer_api gem/#{SoftLayer::VERSION} (Ruby #{RUBY_PLATFORM}/#{RUBY_VERSION})"
88-
89+
90+
# and assign a time out if the settings offer one
8991
@network_timeout = settings[:timeout] if settings.has_key?(:timeout)
9092

9193
raise "A SoftLayer Client requires a username" if !@username || @username.empty?

lib/softlayer/NetworkComponent.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#--
2+
# Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved.
3+
#
4+
# For licensing information see the LICENSE.md file in the project root.
5+
#++
6+
7+
module SoftLayer
8+
class NetworkComponent < SoftLayer::ModelBase
9+
sl_attr :name
10+
sl_attr :port
11+
sl_attr :speed
12+
sl_attr :maxSpeed
13+
end
14+
end

lib/softlayer/ProductItemCategory.rb

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,23 @@ module SoftLayer
1010
# the product order is the price_id, the rest of the information is provided
1111
# to make the object friendly to humans who may be searching for the
1212
# meaning of a given price_id.
13-
class ProductConfigurationOption < Struct.new(:price_id, :description, :capacity, :units, :setupFee, :laborFee, :oneTimeFee, :recurringFee, :hourlyRecurringFee)
13+
class ProductConfigurationOption < Struct.new(:price_id, :description, :capacity, :units, :setupFee, :laborFee,
14+
:oneTimeFee, :recurringFee, :hourlyRecurringFee)
1415
# Is it evil, or just incongruous to give methods to a struct?
1516

17+
def initialize(package_item_data, price_item_data)
18+
self.description = package_item_data['description']
19+
self.capacity = package_item_data['capacity']
20+
self.units = package_item_data['units']
21+
22+
self.price_id = price_item_data['id']
23+
self.setupFee = price_item_data['setupFee'] ? price_item_data['setupFee'].to_f : 0.0
24+
self.laborFee = price_item_data['laborFee'] ? price_item_data['laborFee'].to_f : 0.0
25+
self.oneTimeFee = price_item_data['oneTimeFee'] ? price_item_data['oneTimeFee'].to_f : 0.0
26+
self.recurringFee = price_item_data['recurringFee'] ? price_item_data['recurringFee'].to_f : 0.0
27+
self.hourlyRecurringFee = price_item_data['hourlyRecurringFee'] ? price_item_data['hourlyRecurringFee'].to_f : 0.0
28+
end
29+
1630
# returns true if the configurtion option has no fees associated with it.
1731
def free?
1832
self.setupFee == 0 && self.laborFee == 0 && self.oneTimeFee == 0 && self.recurringFee == 0 && self.hourlyRecurringFee == 0
@@ -63,17 +77,7 @@ class ProductItemCategory < ModelBase
6377
# web UI), but this code collapses the groups.
6478
self['groups'].collect do |group|
6579
group['prices'].sort{|lhs,rhs| lhs['sort'] <=> rhs['sort']}.collect do |price_item|
66-
ProductConfigurationOption.new(
67-
price_item['id'],
68-
price_item['item']['description'],
69-
price_item['item']['capacity'],
70-
price_item['item']['units'],
71-
price_item['setupFee'] ? price_item['setupFee'].to_f : 0.0,
72-
price_item['laborFee'] ? price_item['laborFee'].to_f : 0.0,
73-
price_item['oneTimeFee'] ? price_item['oneTimeFee'].to_f : 0.0,
74-
price_item['recurringFee'] ? price_item['recurringFee'].to_f : 0.0,
75-
price_item['hourlyRecurringFee'] ? price_item['hourlyRecurringFee'].to_f : 0.0
76-
)
80+
ProductConfigurationOption.new(price_item['item'], price_item)
7781
end
7882
end.flatten # flatten out the individual group arrays.
7983
end

lib/softlayer/ProductPackage.rb

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,32 +80,75 @@ class ProductPackage < ModelBase
8080
# Run though the categories and for each one that's in our config, create a SoftLayer::ProductItemCategory object.
8181
# Conveniently the +keys+ of the required_by_category_code gives us a list of the category codes in the configuration
8282
config_categories = required_by_category_code.keys
83-
categories_data.collect do |category_data|
83+
84+
# collect all the categories into an array
85+
@categories = categories_data.collect do |category_data|
8486
if config_categories.include? category_data['categoryCode']
8587
SoftLayer::ProductItemCategory.new(softlayer_client, category_data, required_by_category_code[category_data['categoryCode']])
8688
else
87-
nil
89+
SoftLayer::ProductItemCategory.new(softlayer_client, category_data, false)
8890
end
8991
end.compact
92+
93+
# The configuration consists of only those categories that are required.
94+
@categories.select { |category| category.required? }
95+
end # to_update
96+
end # configuration
97+
98+
##
99+
# The full set of product categories contained in the package
100+
#
101+
sl_dynamic_attr :categories do |resource|
102+
resource.should_update? do
103+
@categories == nil
104+
end
105+
106+
resource.to_update do
107+
# This is a bit ugly, but what we do is ask for the configuration
108+
# which updates all the categories for the package (and marks those
109+
# that are required)
110+
self.configuration
111+
112+
# return the value constructed by the configuraiton
113+
@categories
90114
end
91115
end
92116

93117
##
94118
# Returns an array of the required categories in this package
95119
def required_categories
96-
configuration.select { |category| category.required? }
120+
configuration
97121
end
98122

99123
##
100124
# Returns the product category with the given category code (or nil if one cannot be found)
101125
def category(category_code)
102-
configuration.find { |category| category.categoryCode == category_code }
126+
categories.find { |category| category.categoryCode == category_code }
103127
end
104128

129+
##
130+
# Returns a list of the datacenters that this package is available in
105131
def datacenter_options
106132
available_locations.collect { |location_data| Datacenter::datacenter_named(location_data["location"]["name"], self.softlayer_client) }.compact
107133
end
108134

135+
##
136+
# Returns the package items with the given description
137+
# Currently this is returning the low-level hash representation directly from the Network API
138+
#
139+
def items_with_description(expected_description)
140+
filter = ObjectFilter.new { |filter| filter.accept("items.description").when_it is(expected_description) }
141+
items_data = self.service.object_filter(filter).getItems()
142+
143+
items_data.collect do |item_data|
144+
first_price = item_data['prices'][0]
145+
ProductConfigurationOption.new(item_data, first_price)
146+
end
147+
end
148+
149+
##
150+
# Returns the service for interacting with this package through the network API
151+
#
109152
def service
110153
softlayer_client['Product_Package'].object_with_id(self.id)
111154
end
@@ -168,6 +211,13 @@ def self.bare_metal_server_packages(client = nil)
168211
packages_with_key_name('BARE_METAL_CPU', client)
169212
end
170213

214+
##
215+
# The "Additional Products" package is a grab-bag of products
216+
# and services. It has a "well known" id of 0
217+
def self.additional_products_package(client = nil)
218+
return package_with_id(0, client)
219+
end
220+
171221
protected
172222

173223
def self.default_object_mask(root)

lib/softlayer/Server.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ module SoftLayer
1616
# ancestry. As a result there is no SoftLayer API analog
1717
# to this class.
1818
class Server < SoftLayer::ModelBase
19+
include ::SoftLayer::DynamicAttribute
1920

2021
##
2122
# :attr_reader:
@@ -52,6 +53,17 @@ class Server < SoftLayer::ModelBase
5253
# Notes about these server (for use by the customer)
5354
sl_attr :notes
5455

56+
sl_dynamic_attr :primary_network_component do |primary_component|
57+
primary_component.should_update? do
58+
return @primary_network_component == nil
59+
end
60+
61+
primary_component.to_update do
62+
component_data = self.service.getPrimaryNetworkComponent();
63+
SoftLayer::NetworkComponent.new(self.softlayer_client, component_data)
64+
end
65+
end
66+
5567
##
5668
# Construct a server from the given client using the network data found in +network_hash+
5769
#

lib/softlayer/Service.rb

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,19 @@
2020

2121
require 'xmlrpc/client'
2222

23+
# utility routine for swapping constants without warnings.
24+
def with_warnings(flag)
25+
old_verbose, $VERBOSE = $VERBOSE, flag
26+
yield
27+
ensure
28+
$VERBOSE = old_verbose
29+
end
30+
31+
# enable parsing of "nil" values in structures returned from the API
32+
with_warnings(nil) {
33+
XMLRPC::Config.const_set('ENABLE_NIL_PARSER', true)
34+
}
35+
2336
# The XML-RPC spec calls for the "faultCode" in faults to be an integer
2437
# but the SoftLayer XML-RPC API can return strings as the "faultCode"
2538
#
@@ -39,16 +52,6 @@ def self.fault(hash)
3952
end
4053
end
4154

42-
# The XMLRPC client uses a fixed user agent string, but we want to
43-
# supply our own, so we add a method to XMLRPC::Client that lets
44-
# us change it.
45-
class XMLRPC::Client
46-
def self.set_user_agent(new_agent)
47-
remove_const(:USER_AGENT) if const_defined?(:USER_AGENT)
48-
const_set(:USER_AGENT, new_agent)
49-
end
50-
end
51-
5255
module SoftLayer
5356
# = SoftLayer API Service
5457
#
@@ -233,7 +236,7 @@ def call_softlayer_api_with_params(method_name, parameters, args)
233236
# The client knows about authentication, so ask him for the auth headers
234237
authentication_headers = self.client.authentication_headers
235238
additional_headers.merge!(authentication_headers)
236-
239+
237240
if parameters && parameters.server_object_filter
238241
additional_headers.merge!("#{@service_name}ObjectFilter" => parameters.server_object_filter)
239242
end
@@ -315,10 +318,11 @@ def http
315318
end
316319

317320
@xmlrpc_client.http.set_debug_output($stderr)
321+
@xmlrpc_client.http.instance_variable_set(:@verify_mode, OpenSSL::SSL::VERIFY_NONE)
318322
end # $DEBUG
319323
end
320324

321325
@xmlrpc_client
322326
end
323-
end # Service class
327+
end # Service class
324328
end # module SoftLayer

lib/softlayer/VLANFirewall.rb

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#--
2+
# Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved.
3+
#
4+
# For licensing information see the LICENSE.md file in the project root.
5+
#++
6+
7+
module SoftLayer
8+
class VLANFirewall < SoftLayer::ModelBase
9+
sl_attr :VLAN_number, 'vlanNumber'
10+
11+
##
12+
# return the name of the router the firewall is attached to
13+
def primaryRouter
14+
return self['primaryRouter']['hostname']
15+
end
16+
17+
##
18+
# The fully qualified domain name of the firewall
19+
def fullyQualifiedDomainName
20+
if self.has_sl_property?('networkVlanFirewall')
21+
return self['networkVlanFirewall']['fullyQualifiedDomainName']
22+
else
23+
return @softlayer_hash
24+
end
25+
end
26+
27+
##
28+
# returns true if the firewall has the high availability flag set
29+
#
30+
def high_availability?
31+
# note that highAvailabilityFirewallFlag is a boolean in the softlayer hash
32+
return self.has_sl_property?('highAvailabilityFirewallFlag') && self['highAvailabilityFirewallFlag']
33+
end
34+
35+
def rule_set
36+
rule_set = nil
37+
38+
# Search down through the firewall's data to find the AccessControlList (ACL) for the
39+
# "outside" interface which handles "in"-wardly directed traffic. This is the list that
40+
# has the rules we're interested in.
41+
outside_interface_data = self["firewallInterfaces"].find { |firewall_interface_data| firewall_interface_data['name'] == 'outside' }
42+
if outside_interface_data
43+
incoming_ACL = outside_interface_data['firewallContextAccessControlLists'].find { |firewallACL_data| firewallACL_data['direction'] == 'in' }
44+
45+
firewall_ACL = self.softlayer_client[:Network_Firewall_AccessControlList].object_with_id(incoming_ACL['id']).getObject
46+
rule_set = VLANFirewallRuleset.new(self.softlayer_client, firewall_ACL)
47+
end
48+
49+
return rule_set
50+
end
51+
52+
##
53+
# collect a list of the firewalls on the account
54+
#
55+
def self.find_firewalls(client)
56+
softlayer_client = client || Client.default_client
57+
raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client
58+
59+
vlan_firewall_filter = SoftLayer::ObjectFilter.new() { |filter|
60+
filter.accept("networkVlans.networkVlanFirewall").when_it is_not_null
61+
}
62+
63+
vlan_firewalls = client[:Account].object_mask(vlan_firewall_mask).object_filter(vlan_firewall_filter).getNetworkVlans
64+
vlan_firewalls.collect { |firewall_data| SoftLayer::VLANFirewall.new(client, firewall_data)}
65+
end
66+
67+
private
68+
69+
def self.vlan_firewall_mask
70+
return "mask[primaryRouter,dedicatedFirewallFlag,highAvailabilityFirewallFlag,"+
71+
"firewallInterfaces.firewallContextAccessControlLists," +
72+
"networkVlanFirewall[id, datacenter, primaryIpAddress, firewallType, fullyQualifiedDomainName]]"
73+
end
74+
end # class Firewall
75+
end # module SoftLayer

0 commit comments

Comments
 (0)