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