softlayer_api 3.0.b1 → 3.0.b2
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 +4 -4
- data/CHANGELOG.textile +7 -2
- data/examples/order_bare_metal_package.rb +2 -2
- data/examples/order_server_firewall.rb +63 -0
- data/lib/softlayer/APIParameterFilter.rb +8 -1
- data/lib/softlayer/Account.rb +20 -2
- data/lib/softlayer/BareMetalServer.rb +39 -8
- data/lib/softlayer/BareMetalServerOrder.rb +12 -12
- data/lib/softlayer/BareMetalServerOrder_Package.rb +5 -5
- data/lib/softlayer/Client.rb +4 -2
- data/lib/softlayer/Config.rb +3 -3
- data/lib/softlayer/ImageTemplate.rb +11 -11
- data/lib/softlayer/ModelBase.rb +1 -1
- data/lib/softlayer/NetworkComponent.rb +14 -0
- data/lib/softlayer/ProductItemCategory.rb +17 -13
- data/lib/softlayer/ProductPackage.rb +60 -10
- data/lib/softlayer/Server.rb +30 -9
- data/lib/softlayer/ServerFirewall.rb +263 -0
- data/lib/softlayer/ServerFirewallOrder.rb +84 -0
- data/lib/softlayer/Service.rb +17 -13
- data/lib/softlayer/Ticket.rb +8 -8
- data/lib/softlayer/VLANFirewall.rb +280 -0
- data/lib/softlayer/VLANFirewallOrder.rb +93 -0
- data/lib/softlayer/VirtualServer.rb +8 -5
- data/lib/softlayer/VirtualServerOrder.rb +16 -16
- data/lib/softlayer/VirtualServerUpgradeOrder.rb +21 -21
- data/lib/softlayer/base.rb +1 -1
- data/lib/softlayer_api.rb +5 -0
- metadata +8 -2
data/lib/softlayer/ModelBase.rb
CHANGED
@@ -47,7 +47,7 @@ module SoftLayer
|
|
47
47
|
# a particular entity in the SoftLayer_Ticket service. The particular
|
48
48
|
# entity is identified by its id so the Ticket class would return
|
49
49
|
#
|
50
|
-
# softlayer_client[
|
50
|
+
# softlayer_client[:Ticket].object_with_id
|
51
51
|
#
|
52
52
|
# which is a service which would allow calls to the ticket service
|
53
53
|
# through that particular object.
|
@@ -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
|
@@ -10,9 +10,23 @@ module SoftLayer
|
|
10
10
|
# the product order is the price_id, the rest of the information is provided
|
11
11
|
# to make the object friendly to humans who may be searching for the
|
12
12
|
# meaning of a given price_id.
|
13
|
-
class ProductConfigurationOption < Struct.new(:price_id, :description, :capacity, :units, :setupFee, :laborFee,
|
13
|
+
class ProductConfigurationOption < Struct.new(:price_id, :description, :capacity, :units, :setupFee, :laborFee,
|
14
|
+
:oneTimeFee, :recurringFee, :hourlyRecurringFee)
|
14
15
|
# Is it evil, or just incongruous to give methods to a struct?
|
15
16
|
|
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
|
+
|
16
30
|
# returns true if the configurtion option has no fees associated with it.
|
17
31
|
def free?
|
18
32
|
self.setupFee == 0 && self.laborFee == 0 && self.oneTimeFee == 0 && self.recurringFee == 0 && self.hourlyRecurringFee == 0
|
@@ -63,24 +77,14 @@ module SoftLayer
|
|
63
77
|
# web UI), but this code collapses the groups.
|
64
78
|
self['groups'].collect do |group|
|
65
79
|
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)
|
77
81
|
end
|
78
82
|
end.flatten # flatten out the individual group arrays.
|
79
83
|
end
|
80
84
|
end
|
81
85
|
|
82
86
|
def service
|
83
|
-
softlayer_client[
|
87
|
+
softlayer_client[:SoftLayer_Product_Item_Category].object_with_id(self.id)
|
84
88
|
end
|
85
89
|
|
86
90
|
##
|
@@ -65,7 +65,7 @@ module SoftLayer
|
|
65
65
|
# filtering mechanism on the server side to give us a list of the categories, groups, and prices that are valid for the current
|
66
66
|
# account at the current time. We construct the ProductItemCategory objects from the results we get back.
|
67
67
|
#
|
68
|
-
configuration_data = softlayer_client[
|
68
|
+
configuration_data = softlayer_client[:Product_Package].object_with_id(self.id).object_mask("mask[isRequired,itemCategory.categoryCode]").getConfiguration()
|
69
69
|
|
70
70
|
# We sort of invert the information and create a map from category codes to a boolean representing
|
71
71
|
# whether or not they are required.
|
@@ -75,39 +75,82 @@ module SoftLayer
|
|
75
75
|
end
|
76
76
|
|
77
77
|
# This call to getCategories is the one that does lots of fancy back-end filtering for us
|
78
|
-
categories_data = softlayer_client[
|
78
|
+
categories_data = softlayer_client[:Product_Package].object_with_id(self.id).getCategories()
|
79
79
|
|
80
80
|
# Run though the categories and for each one that's in our config, create a SoftLayer::ProductItemCategory object.
|
81
81
|
# Conveniently the +keys+ of the required_by_category_code gives us a list of the category codes in the configuration
|
82
82
|
config_categories = required_by_category_code.keys
|
83
|
-
|
83
|
+
|
84
|
+
# collect all the categories into an array
|
85
|
+
@categories = categories_data.collect do |category_data|
|
84
86
|
if config_categories.include? category_data['categoryCode']
|
85
87
|
SoftLayer::ProductItemCategory.new(softlayer_client, category_data, required_by_category_code[category_data['categoryCode']])
|
86
88
|
else
|
87
|
-
|
89
|
+
SoftLayer::ProductItemCategory.new(softlayer_client, category_data, false)
|
88
90
|
end
|
89
91
|
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
|
90
114
|
end
|
91
115
|
end
|
92
116
|
|
93
117
|
##
|
94
118
|
# Returns an array of the required categories in this package
|
95
119
|
def required_categories
|
96
|
-
configuration
|
120
|
+
configuration
|
97
121
|
end
|
98
122
|
|
99
123
|
##
|
100
124
|
# Returns the product category with the given category code (or nil if one cannot be found)
|
101
125
|
def category(category_code)
|
102
|
-
|
126
|
+
categories.find { |category| category.categoryCode == category_code }
|
103
127
|
end
|
104
128
|
|
129
|
+
##
|
130
|
+
# Returns a list of the datacenters that this package is available in
|
105
131
|
def datacenter_options
|
106
|
-
available_locations.collect { |location_data| Datacenter::datacenter_named(location_data[
|
132
|
+
available_locations.collect { |location_data| Datacenter::datacenter_named(location_data['location']['name'], self.softlayer_client) }.compact
|
107
133
|
end
|
108
134
|
|
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
|
+
#
|
109
152
|
def service
|
110
|
-
softlayer_client[
|
153
|
+
softlayer_client[:Product_Package].object_with_id(self.id)
|
111
154
|
end
|
112
155
|
|
113
156
|
##
|
@@ -122,7 +165,7 @@ module SoftLayer
|
|
122
165
|
filter.accept('type.keyName').when_it is(key_name)
|
123
166
|
end
|
124
167
|
|
125
|
-
filtered_service = softlayer_client[
|
168
|
+
filtered_service = softlayer_client[:Product_Package].object_filter(filter).object_mask(self.default_object_mask('mask'))
|
126
169
|
packages_data = filtered_service.getAllObjects
|
127
170
|
packages_data.collect { |package_data| ProductPackage.new(softlayer_client, package_data) }
|
128
171
|
end
|
@@ -135,7 +178,7 @@ module SoftLayer
|
|
135
178
|
softlayer_client = client || Client.default_client
|
136
179
|
raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client
|
137
180
|
|
138
|
-
package_data = softlayer_client[
|
181
|
+
package_data = softlayer_client[:Product_Package].object_with_id(package_id).object_mask(self.default_object_mask('mask')).getObject
|
139
182
|
ProductPackage.new(softlayer_client, package_data)
|
140
183
|
end
|
141
184
|
|
@@ -168,6 +211,13 @@ module SoftLayer
|
|
168
211
|
packages_with_key_name('BARE_METAL_CPU', client)
|
169
212
|
end
|
170
213
|
|
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
|
+
|
171
221
|
protected
|
172
222
|
|
173
223
|
def self.default_object_mask(root)
|
data/lib/softlayer/Server.rb
CHANGED
@@ -16,6 +16,7 @@ module SoftLayer
|
|
16
16
|
# ancestry. As a result there is no SoftLayer API analog
|
17
17
|
# to this class.
|
18
18
|
class Server < SoftLayer::ModelBase
|
19
|
+
include ::SoftLayer::DynamicAttribute
|
19
20
|
|
20
21
|
##
|
21
22
|
# :attr_reader:
|
@@ -52,6 +53,17 @@ module SoftLayer
|
|
52
53
|
# Notes about these server (for use by the customer)
|
53
54
|
sl_attr :notes
|
54
55
|
|
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
|
+
|
55
67
|
##
|
56
68
|
# Construct a server from the given client using the network data found in +network_hash+
|
57
69
|
#
|
@@ -82,7 +94,7 @@ module SoftLayer
|
|
82
94
|
when :power_cycle
|
83
95
|
self.service.rebootHard
|
84
96
|
else
|
85
|
-
raise
|
97
|
+
raise ArgumentError, "Unrecognized reboot technique in SoftLayer::Server#reboot!}"
|
86
98
|
end
|
87
99
|
end
|
88
100
|
|
@@ -105,7 +117,7 @@ module SoftLayer
|
|
105
117
|
# Change the notes of the server
|
106
118
|
# raises ArgumentError if you pass nil as the notes
|
107
119
|
def notes=(new_notes)
|
108
|
-
raise ArgumentError
|
120
|
+
raise ArgumentError, "The new notes cannot be nil" unless new_notes
|
109
121
|
|
110
122
|
edit_template = {
|
111
123
|
"notes" => new_notes
|
@@ -119,7 +131,7 @@ module SoftLayer
|
|
119
131
|
# Change the user metadata for the server.
|
120
132
|
#
|
121
133
|
def user_metadata=(new_metadata)
|
122
|
-
raise ArgumentError
|
134
|
+
raise ArgumentError, "Cannot set user metadata to nil" unless new_metadata
|
123
135
|
self.service.setUserMetadata([new_metadata])
|
124
136
|
self.refresh_details()
|
125
137
|
end
|
@@ -129,8 +141,8 @@ module SoftLayer
|
|
129
141
|
# Raises an ArgumentError if the new hostname is nil or empty
|
130
142
|
#
|
131
143
|
def set_hostname!(new_hostname)
|
132
|
-
raise ArgumentError
|
133
|
-
raise ArgumentError
|
144
|
+
raise ArgumentError, "The new hostname cannot be nil" unless new_hostname
|
145
|
+
raise ArgumentError, "The new hostname cannot be empty" if new_hostname.empty?
|
134
146
|
|
135
147
|
edit_template = {
|
136
148
|
"hostname" => new_hostname
|
@@ -147,8 +159,8 @@ module SoftLayer
|
|
147
159
|
# no further validation is done on the domain name
|
148
160
|
#
|
149
161
|
def set_domain!(new_domain)
|
150
|
-
raise ArgumentError
|
151
|
-
raise ArgumentError
|
162
|
+
raise ArgumentError, "The new hostname cannot be nil" unless new_domain
|
163
|
+
raise ArgumentError, "The new hostname cannot be empty" if new_domain.empty?
|
152
164
|
|
153
165
|
edit_template = {
|
154
166
|
"domain" => new_domain
|
@@ -158,6 +170,16 @@ module SoftLayer
|
|
158
170
|
self.refresh_details()
|
159
171
|
end
|
160
172
|
|
173
|
+
##
|
174
|
+
# Returns the max port speed of the public network interfaces of the server taking into account
|
175
|
+
# bound interface pairs (redundant network cards).
|
176
|
+
def firewall_port_speed
|
177
|
+
network_components = self.service.object_mask("mask[id,maxSpeed]").getFrontendNetworkComponents()
|
178
|
+
max_speeds = network_components.collect { |component| component['maxSpeed'] }
|
179
|
+
|
180
|
+
max_speeds.empty? ? 0 : max_speeds.max
|
181
|
+
end
|
182
|
+
|
161
183
|
##
|
162
184
|
# Change the current port speed of the server
|
163
185
|
#
|
@@ -166,8 +188,7 @@ module SoftLayer
|
|
166
188
|
# on the port.
|
167
189
|
#
|
168
190
|
# Set +public+ to +false+ in order to change the speed of the
|
169
|
-
#
|
170
|
-
#
|
191
|
+
# private network interface.
|
171
192
|
def change_port_speed(new_speed, public = true)
|
172
193
|
if public
|
173
194
|
self.service.setPublicNetworkInterfaceSpeed(new_speed)
|
@@ -0,0 +1,263 @@
|
|
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
|
+
##
|
9
|
+
# The ServerFirewall class represents a firewall in the
|
10
|
+
# SoftLayer environment that exists in a 1 to 1 relationship
|
11
|
+
# with a particular server (either Bare Metal or Virtual).
|
12
|
+
#
|
13
|
+
# This is also called a "Shared Firewall" in some documentation.
|
14
|
+
#
|
15
|
+
# Instances of this class rougly correspond to instances of the
|
16
|
+
# SoftLayer_Network_Component_Firewall service entity.
|
17
|
+
#
|
18
|
+
class ServerFirewall < SoftLayer::ModelBase
|
19
|
+
include ::SoftLayer::DynamicAttribute
|
20
|
+
|
21
|
+
##
|
22
|
+
# :attr_reader:
|
23
|
+
# The state of the firewall, includes whether or not the rules are
|
24
|
+
# editable and whether or not the firewall rules are applied or bypassed
|
25
|
+
# Can at least be 'allow_edit', 'bypass' or 'no_edit'.
|
26
|
+
# This list may not be exhaustive
|
27
|
+
sl_attr :status
|
28
|
+
|
29
|
+
##
|
30
|
+
# :attr_reader:
|
31
|
+
# The firewall rules assigned to this firewall. These rules will
|
32
|
+
# be read from the network API every time you ask for the value
|
33
|
+
# of this property. To change the rules on the server use the
|
34
|
+
# asymmetric method change_rules!
|
35
|
+
sl_dynamic_attr :rules do |firewall_rules|
|
36
|
+
firewall_rules.should_update? do
|
37
|
+
# firewall rules update every time you ask for them.
|
38
|
+
return true
|
39
|
+
end
|
40
|
+
|
41
|
+
firewall_rules.to_update do
|
42
|
+
rules_data = self.service.object_mask(self.class.default_rules_mask).getRules()
|
43
|
+
|
44
|
+
# At the time of this writing, the object mask sent to getRules is not
|
45
|
+
# applied properly. This has been reported as a bug to the proper
|
46
|
+
# development team. In the mean time, this extra step does filtering
|
47
|
+
# that should have been done by the object mask.
|
48
|
+
rules_keys = self.class.default_rules_mask_keys
|
49
|
+
new_rules = rules_data.inject([]) do |new_rules, current_rule|
|
50
|
+
new_rule = current_rule.delete_if { |key, value| !(rules_keys.include? key) }
|
51
|
+
new_rules << new_rule
|
52
|
+
end
|
53
|
+
|
54
|
+
new_rules.sort { |lhs, rhs| lhs['orderValue'] <=> rhs['orderValue'] }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
##
|
59
|
+
# :attr_reader:
|
60
|
+
# The server that this firewall is attached to. The result may be
|
61
|
+
# either a bare metal or virtual server.
|
62
|
+
#
|
63
|
+
sl_dynamic_attr :protected_server do |protected_server|
|
64
|
+
protected_server.should_update? do
|
65
|
+
@protected_server == nil
|
66
|
+
end
|
67
|
+
|
68
|
+
protected_server.to_update do
|
69
|
+
if has_sl_property?('networkComponent')
|
70
|
+
@protected_server = SoftLayer::BareMetalServer.server_with_id(self['networkComponent']['downlinkComponent']['hardwareId'], :client => softlayer_client)
|
71
|
+
end
|
72
|
+
|
73
|
+
if has_sl_property?('guestNetworkComponent')
|
74
|
+
@protected_server = SoftLayer::VirtualServer.server_with_id(self['guestNetworkComponent']['guest']['id'], :client => softlayer_client)
|
75
|
+
end
|
76
|
+
|
77
|
+
@protected_server
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
##
|
82
|
+
# Calls super to initialize the object then initializes some
|
83
|
+
# properties
|
84
|
+
def initialize(client, network_hash)
|
85
|
+
super(client, network_hash)
|
86
|
+
@protected_server = nil
|
87
|
+
end
|
88
|
+
|
89
|
+
##
|
90
|
+
# Cancel the firewall
|
91
|
+
#
|
92
|
+
# This method cancels the firewall and releases its
|
93
|
+
# resources. The cancellation is processed immediately!
|
94
|
+
# Call this method with careful deliberation!
|
95
|
+
#
|
96
|
+
# Notes is a string that describes the reason for the
|
97
|
+
# cancellation. If empty or nil, a default string will
|
98
|
+
# be added
|
99
|
+
#
|
100
|
+
def cancel!(notes = nil)
|
101
|
+
user = self.softlayer_client[:Account].object_mask("mask[id,account]").getCurrentUser
|
102
|
+
notes = "Cancelled by a call to #{__method__} in the softlayer_api gem" if notes == nil || notes == ""
|
103
|
+
|
104
|
+
cancellation_request = {
|
105
|
+
'accountId' => user['account']['id'],
|
106
|
+
'userId' => user['id'],
|
107
|
+
'items' => [ {
|
108
|
+
'billingItemId' => self['billingItem']['id'],
|
109
|
+
'immediateCancellationFlag' => true
|
110
|
+
} ],
|
111
|
+
'notes' => notes
|
112
|
+
}
|
113
|
+
|
114
|
+
self.softlayer_client[:Billing_Item_Cancellation_Request].createObject(cancellation_request)
|
115
|
+
end
|
116
|
+
|
117
|
+
##
|
118
|
+
# Change the set of rules for the firewall.
|
119
|
+
# The rules_data parameter should be an array of hashes where
|
120
|
+
# each hash gives the conditions of the rule. The keys of the
|
121
|
+
# hashes should be entries from the array returned by
|
122
|
+
# SoftLayer::ServerFirewall.default_rules_mask_keys
|
123
|
+
#
|
124
|
+
# *NOTE!* When changing the rules on the firewall, you must
|
125
|
+
# pass in a complete set of rules each time. The rules you
|
126
|
+
# submit will replace the entire ruleset on the destination
|
127
|
+
# firewall.
|
128
|
+
#
|
129
|
+
# *NOTE!* The rules themselves have an "orderValue" property.
|
130
|
+
# It is this property, and *not* the order that the rules are
|
131
|
+
# found in the rules_data array, which will determine in which
|
132
|
+
# order the firewall applies it's rules to incomming traffic.
|
133
|
+
#
|
134
|
+
# *NOTE!* Changes to the rules are not applied immediately
|
135
|
+
# on the server side. Instead, they are enqueued by the
|
136
|
+
# firewall update service and updated periodically. A typical
|
137
|
+
# update will take about one minute to apply, but times may vary
|
138
|
+
# depending on the system load and other circumstances.
|
139
|
+
def change_rules!(rules_data)
|
140
|
+
change_object = {
|
141
|
+
"networkComponentFirewallId" => self.id,
|
142
|
+
"rules" => rules_data
|
143
|
+
}
|
144
|
+
|
145
|
+
self.softlayer_client[:Network_Firewall_Update_Request].createObject(change_object)
|
146
|
+
end
|
147
|
+
|
148
|
+
##
|
149
|
+
# This method asks the firewall to ignore its rule set and pass all traffic
|
150
|
+
# through the firewall. Compare the behavior of this routine with
|
151
|
+
# change_routing_bypass!
|
152
|
+
#
|
153
|
+
# It is important to note that changing the bypass to :bypass_firewall_rules
|
154
|
+
# removes ALL the protection offered by the firewall. This routine should be
|
155
|
+
# used with careful deliberation.
|
156
|
+
#
|
157
|
+
# Note that this routine queues a rule change and rule changes may take
|
158
|
+
# time to process. The change will probably not take effect immediately.
|
159
|
+
#
|
160
|
+
# The two symbols accepted as arguments by this routine are:
|
161
|
+
# :apply_firewall_rules - The rules of the firewall are applied to traffic. This is the default operating mode of the firewall
|
162
|
+
# :bypass_firewall_rules - The rules of the firewall are ignored. In this configuration the firewall provides no protection.
|
163
|
+
#
|
164
|
+
def change_rules_bypass!(bypass_symbol)
|
165
|
+
change_object = {
|
166
|
+
"networkComponentFirewallId" => self.id,
|
167
|
+
"rules" => self.rules
|
168
|
+
}
|
169
|
+
|
170
|
+
case bypass_symbol
|
171
|
+
when :apply_firewall_rules
|
172
|
+
change_object['bypassFlag'] = false
|
173
|
+
self.softlayer_client[:Network_Firewall_Update_Request].createObject(change_object)
|
174
|
+
when :bypass_firewall_rules
|
175
|
+
change_object['bypassFlag'] = true
|
176
|
+
self.softlayer_client[:Network_Firewall_Update_Request].createObject(change_object)
|
177
|
+
else
|
178
|
+
raise ArgumentError, "An invalid parameter was sent to #{__method__}. It accepts :apply_firewall_rules and :bypass_firewall_rules"
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
##
|
183
|
+
# Locate and return all the server firewalls in the environment.
|
184
|
+
#
|
185
|
+
# These are a bit tricky to track down. The strategy we take here is
|
186
|
+
# to look at the account and find all the VLANs that do NOT have their
|
187
|
+
# "dedicatedFirewallFlag" set.
|
188
|
+
#
|
189
|
+
# With the list of VLANs in hand we check each to see if it has an
|
190
|
+
# firewallNetworkComponents (corresponding to bare metal servers) or
|
191
|
+
# firewallGuestNetworkComponents (corresponding to virtual servers) that
|
192
|
+
# have a status of "allow_edit". Each such component is a firewall
|
193
|
+
# interface on the VLAN with rules that the customer can edit.
|
194
|
+
#
|
195
|
+
# The collection of all those VLANs becomes the set of firewalls
|
196
|
+
# for the account.
|
197
|
+
#
|
198
|
+
def self.find_firewalls(client = nil)
|
199
|
+
softlayer_client = client || Client.default_client
|
200
|
+
raise "#{__method__} requires a client but none was given and Client::default_client is not set" if !softlayer_client
|
201
|
+
|
202
|
+
# Note that the dedicatedFirewallFlag is actually an integer and not a boolean
|
203
|
+
# so we compare it against 0
|
204
|
+
shared_vlans_filter = SoftLayer::ObjectFilter.new() { |filter|
|
205
|
+
filter.accept("networkVlans.dedicatedFirewallFlag").when_it is(0)
|
206
|
+
}
|
207
|
+
|
208
|
+
bare_metal_firewalls_data = []
|
209
|
+
virtual_firewalls_data = []
|
210
|
+
|
211
|
+
shared_vlans = softlayer_client[:Account].object_mask(network_vlan_mask).object_filter(shared_vlans_filter).getNetworkVlans
|
212
|
+
shared_vlans.each do |vlan_data|
|
213
|
+
bare_metal_firewalls_data.concat vlan_data['firewallNetworkComponents'].select { |network_component| network_component['status'] != 'no_edit'}
|
214
|
+
virtual_firewalls_data.concat vlan_data['firewallGuestNetworkComponents'].select { |network_component| network_component['status'] != 'no_edit'}
|
215
|
+
end
|
216
|
+
|
217
|
+
bare_metal_firewalls = bare_metal_firewalls_data.collect { |bare_metal_firewall_data|
|
218
|
+
self.new(softlayer_client, bare_metal_firewall_data)
|
219
|
+
}
|
220
|
+
|
221
|
+
virtual_server_firewalls = virtual_firewalls_data.collect { |virtual_firewall_data|
|
222
|
+
self.new(softlayer_client, virtual_firewall_data)
|
223
|
+
}
|
224
|
+
|
225
|
+
return bare_metal_firewalls + virtual_server_firewalls
|
226
|
+
end
|
227
|
+
|
228
|
+
#--
|
229
|
+
# Methods for the SoftLayer model
|
230
|
+
#++
|
231
|
+
|
232
|
+
def service
|
233
|
+
self.softlayer_client[:Network_Component_Firewall].object_with_id(self.id)
|
234
|
+
end
|
235
|
+
|
236
|
+
def softlayer_properties(object_mask = nil)
|
237
|
+
service = self.service
|
238
|
+
service = service.object_mask(object_mask) if object_mask
|
239
|
+
|
240
|
+
if self.has_sl_property?('networkComponent')
|
241
|
+
service.object_mask("mask[id,status,billingItem.id,networkComponent.downlinkComponent.hardwareId]").getObject
|
242
|
+
else
|
243
|
+
service.object_mask("mask[id,status,billingItem.id,guestNetworkComponent.guest.id]").getObject
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
#--
|
248
|
+
#++
|
249
|
+
private
|
250
|
+
|
251
|
+
def self.network_vlan_mask
|
252
|
+
"mask[firewallNetworkComponents[id,status,billingItem.id,networkComponent.downlinkComponent.hardwareId],firewallGuestNetworkComponents[id,status,billingItem.id,guestNetworkComponent.guest.id]]"
|
253
|
+
end
|
254
|
+
|
255
|
+
def self.default_rules_mask
|
256
|
+
return { "mask" => default_rules_mask_keys }.to_sl_object_mask
|
257
|
+
end
|
258
|
+
|
259
|
+
def self.default_rules_mask_keys
|
260
|
+
['orderValue','action','destinationIpAddress','destinationIpSubnetMask',"protocol","destinationPortRangeStart","destinationPortRangeEnd",'sourceIpAddress',"sourceIpSubnetMask","version"]
|
261
|
+
end
|
262
|
+
end # ServerFirewall class
|
263
|
+
end # SoftLayer module
|