dop_common 0.13.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +23 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +176 -0
- data/Gemfile +11 -0
- data/LICENSE.txt +177 -0
- data/README.md +48 -0
- data/Rakefile +49 -0
- data/Vagrantfile +25 -0
- data/bin/dop-puppet-autosign +56 -0
- data/doc/examples/example_deploment_plan_v0.0.1.yaml +302 -0
- data/doc/plan_format_v0.0.1.md +919 -0
- data/doc/plan_format_v0.0.2_snippets.md +56 -0
- data/dop_common.gemspec +44 -0
- data/lib/dop_common/affinity_group.rb +57 -0
- data/lib/dop_common/cli/global_options.rb +37 -0
- data/lib/dop_common/cli/log.rb +51 -0
- data/lib/dop_common/cli/node_selection.rb +62 -0
- data/lib/dop_common/command.rb +125 -0
- data/lib/dop_common/config/helper.rb +39 -0
- data/lib/dop_common/config.rb +66 -0
- data/lib/dop_common/configuration.rb +37 -0
- data/lib/dop_common/credential.rb +152 -0
- data/lib/dop_common/data_disk.rb +62 -0
- data/lib/dop_common/dns.rb +55 -0
- data/lib/dop_common/hash_parser.rb +241 -0
- data/lib/dop_common/hooks.rb +81 -0
- data/lib/dop_common/infrastructure.rb +160 -0
- data/lib/dop_common/infrastructure_properties.rb +185 -0
- data/lib/dop_common/interface.rb +113 -0
- data/lib/dop_common/log.rb +78 -0
- data/lib/dop_common/network.rb +85 -0
- data/lib/dop_common/node/config.rb +159 -0
- data/lib/dop_common/node.rb +442 -0
- data/lib/dop_common/node_filter.rb +74 -0
- data/lib/dop_common/plan.rb +188 -0
- data/lib/dop_common/plan_cache.rb +83 -0
- data/lib/dop_common/plan_store.rb +263 -0
- data/lib/dop_common/pre_processor.rb +73 -0
- data/lib/dop_common/run_options.rb +56 -0
- data/lib/dop_common/signal_handler.rb +58 -0
- data/lib/dop_common/state_store.rb +95 -0
- data/lib/dop_common/step.rb +200 -0
- data/lib/dop_common/step_set.rb +41 -0
- data/lib/dop_common/thread_context_logger.rb +77 -0
- data/lib/dop_common/utils.rb +106 -0
- data/lib/dop_common/validator.rb +53 -0
- data/lib/dop_common/version.rb +3 -0
- data/lib/dop_common.rb +32 -0
- data/lib/hiera/backend/dop_backend.rb +94 -0
- data/lib/hiera/dop_logger.rb +20 -0
- data/spec/data/fake_hook_file_invalid +1 -0
- data/spec/data/fake_hook_file_valid +5 -0
- data/spec/data/fake_keyfile +1 -0
- data/spec/dop-puppet-autosign_spec_disable.rb +33 -0
- data/spec/dop_common/affinity_group_spec.rb +41 -0
- data/spec/dop_common/command_spec.rb +83 -0
- data/spec/dop_common/credential_spec.rb +73 -0
- data/spec/dop_common/data_disk_spec.rb +165 -0
- data/spec/dop_common/dns_spec.rb +33 -0
- data/spec/dop_common/hash_parser_spec.rb +181 -0
- data/spec/dop_common/hooks_spec.rb +33 -0
- data/spec/dop_common/infrastructure_properties_spec.rb +224 -0
- data/spec/dop_common/infrastructure_spec.rb +77 -0
- data/spec/dop_common/interface_spec.rb +192 -0
- data/spec/dop_common/network_spec.rb +92 -0
- data/spec/dop_common/node_filter_spec.rb +70 -0
- data/spec/dop_common/node_spec.rb +623 -0
- data/spec/dop_common/plan_cache_spec.rb +46 -0
- data/spec/dop_common/plan_spec.rb +136 -0
- data/spec/dop_common/plan_store_spec.rb +194 -0
- data/spec/dop_common/pre_processor_spec.rb +27 -0
- data/spec/dop_common/run_options_spec.rb +65 -0
- data/spec/dop_common/signal_handler_spec.rb +31 -0
- data/spec/dop_common/step_set_spec.rb +21 -0
- data/spec/dop_common/step_spec.rb +175 -0
- data/spec/dop_common/utils_spec.rb +27 -0
- data/spec/dop_common/validator_spec.rb +47 -0
- data/spec/example_plans_spec.rb +16 -0
- data/spec/fixtures/example_ssh_key +27 -0
- data/spec/fixtures/example_ssh_key.pub +1 -0
- data/spec/fixtures/incl/root_part.yaml +1 -0
- data/spec/fixtures/incl/some_list.yaml +2 -0
- data/spec/fixtures/other_plan_same_nodes.yaml +19 -0
- data/spec/fixtures/simple_include.yaml +6 -0
- data/spec/fixtures/simple_include_with_errors.yaml +4 -0
- data/spec/fixtures/simple_plan.yaml +19 -0
- data/spec/fixtures/simple_plan_invalid.yaml +18 -0
- data/spec/fixtures/simple_plan_modified.yaml +21 -0
- data/spec/spec_helper.rb +106 -0
- metadata +381 -0
@@ -0,0 +1,160 @@
|
|
1
|
+
#
|
2
|
+
# DOP Common infrastructure hash parser
|
3
|
+
#
|
4
|
+
|
5
|
+
require 'uri'
|
6
|
+
|
7
|
+
module DopCommon
|
8
|
+
class Infrastructure
|
9
|
+
include Validator
|
10
|
+
include HashParser
|
11
|
+
include RunOptions
|
12
|
+
|
13
|
+
attr_reader :name
|
14
|
+
|
15
|
+
VALID_PROVIDER_TYPES = [:baremetal, :openstack, :ovirt, :vsphere]
|
16
|
+
|
17
|
+
VALID_PROVIDER_ALIASES = {
|
18
|
+
:rhev => :ovirt,
|
19
|
+
:rhevm => :ovirt,
|
20
|
+
:rhos => :openstack,
|
21
|
+
:vmware => :vsphere
|
22
|
+
}
|
23
|
+
|
24
|
+
def initialize(name, hash, parent={})
|
25
|
+
@name = name
|
26
|
+
@hash = symbolize_keys(hash)
|
27
|
+
@parsed_credentials = parent[:parsed_credentials]
|
28
|
+
end
|
29
|
+
|
30
|
+
def validate
|
31
|
+
valitdate_shared_options
|
32
|
+
log_validation_method(:provider_valid?)
|
33
|
+
log_validation_method(:endpoint_valid?)
|
34
|
+
log_validation_method(:networks_valid?)
|
35
|
+
log_validation_method(:affinity_groups_valid?)
|
36
|
+
log_validation_method(:credentials_valid?)
|
37
|
+
try_validate_obj("Infrastructure #{name}: Can't validate the networks part because of a previous error") { networks }
|
38
|
+
try_validate_obj("Infrastructure #{name}: Can't validate the affinity groups part because of a previous error") { affinity_groups }
|
39
|
+
end
|
40
|
+
|
41
|
+
def provider
|
42
|
+
@provider ||= provider_valid? ? @hash[:type].downcase.to_sym : nil
|
43
|
+
end
|
44
|
+
alias_method :type, :provider
|
45
|
+
|
46
|
+
def provides?(*t)
|
47
|
+
t.any? do |p|
|
48
|
+
p = p.downcase.to_sym if p.kind_of?(String)
|
49
|
+
p == provider || p == VALID_PROVIDER_ALIASES[provider]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
alias_method :type?, :provides?
|
53
|
+
|
54
|
+
def endpoint
|
55
|
+
@endpoint ||= endpoint_valid? ? create_endpoint : nil
|
56
|
+
end
|
57
|
+
|
58
|
+
def credentials
|
59
|
+
@credentials ||= credentials_valid? ? create_credentials : nil
|
60
|
+
end
|
61
|
+
|
62
|
+
def networks
|
63
|
+
@networks ||= networks_valid? ? create_networks : []
|
64
|
+
end
|
65
|
+
|
66
|
+
def affinity_groups
|
67
|
+
@affinity_groups ||= affinity_groups_valid? ? create_affinity_groups : []
|
68
|
+
end
|
69
|
+
|
70
|
+
def default_security_groups
|
71
|
+
@defalut_security_groups ||= default_security_groups_valid? ? create_default_security_groups : []
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def provider_valid?
|
77
|
+
case @hash[:type]
|
78
|
+
when nil
|
79
|
+
raise PlanParsingError, "Infrastructure #{@name}: provider type is a required property"
|
80
|
+
when String
|
81
|
+
p = @hash[:type].downcase.to_sym
|
82
|
+
raise PlanParsingError, "Infrastructure #{@name}: invalid provider type" unless
|
83
|
+
VALID_PROVIDER_TYPES.include?(p) || VALID_PROVIDER_ALIASES.keys.include?(p)
|
84
|
+
else
|
85
|
+
raise PlanParsingError, "Infrastructure #{@name}: provider type must be a string"
|
86
|
+
end
|
87
|
+
true
|
88
|
+
end
|
89
|
+
alias_method :type_valid?, :provider_valid?
|
90
|
+
|
91
|
+
def endpoint_valid?
|
92
|
+
return false if provides?(:baremetal) && @hash[:endpoint].nil?
|
93
|
+
raise PlanParsingError, "Infrastructure #{@name}: endpoint is a required property" if @hash[:endpoint].nil?
|
94
|
+
::URI.parse(@hash[:endpoint])
|
95
|
+
true
|
96
|
+
rescue URI::InvalidURIError
|
97
|
+
raise PlanParsingError, "Infrastructure #{@name}: the specified endpoint URL is invalid"
|
98
|
+
end
|
99
|
+
|
100
|
+
def credentials_valid?
|
101
|
+
return false if provides?(:baremetal) && @hash[:credentials].nil?
|
102
|
+
raise PlanParsingError, "Infrastructure #{@name}: Credentials pointer must be a string" unless
|
103
|
+
@hash[:credentials].kind_of?(String)
|
104
|
+
raise PlanParsingError, "Infrastructure #{@name}: Missing definition of endpoint credentials" unless
|
105
|
+
@parsed_credentials.has_key?(@hash[:credentials])
|
106
|
+
true
|
107
|
+
end
|
108
|
+
|
109
|
+
def networks_valid?
|
110
|
+
return false if provides?(:baremetal) && @hash[:networks].nil? # Network is optional for baremetal
|
111
|
+
raise PlanParsingError, "Infrastructure #{@name}: network is a required property" if
|
112
|
+
!provides?(:baremetal) && @hash[:networks].nil?
|
113
|
+
raise PlanParsingError, "Infrastructure #{@name}: networks must be a hash" unless
|
114
|
+
@hash[:networks].kind_of?(Hash)
|
115
|
+
raise PlanParsingError, "Infrastructure #{@name}: network names have to be string" unless
|
116
|
+
@hash[:networks].keys.all? { |name| name.kind_of?(String) }
|
117
|
+
raise PlanParsingError, "Infrastructure #{@name}: each network has to be defined as hash" unless
|
118
|
+
@hash[:networks].values.all? { |network| network.kind_of?(Hash) }
|
119
|
+
true
|
120
|
+
end
|
121
|
+
|
122
|
+
def affinity_groups_valid?
|
123
|
+
return false if @hash[:affinity_groups].nil?
|
124
|
+
@hash[:affinity_groups].kind_of?(Hash) or
|
125
|
+
raise PlanParsingError, "Infrastructure #{@name}: affinity_groups must be a hash"
|
126
|
+
@hash[:affinity_groups].keys.all? { |name| name.kind_of?(String) } or
|
127
|
+
raise PlanParsingError, "Infrastructure #{@name}: affinity group names have to be string"
|
128
|
+
@hash[:affinity_groups].values.all? { |ag| ag.kind_of?(Hash) } or
|
129
|
+
raise PlanParsingError, "Infrastructure #{@name}: affinity groups have to be defined as hash"
|
130
|
+
end
|
131
|
+
|
132
|
+
def default_security_groups_valid?
|
133
|
+
return false if @hash[:default_security_groups].nil?
|
134
|
+
@hash[:default_security_groups].kind_of?(Array) or
|
135
|
+
raise PlanParsingError, "Infrastructure #{@name}: default_security_groups must be an array"
|
136
|
+
@hash[:default_security_groups].all? { |name| name.kind_of?(String) } or
|
137
|
+
raise PlanParsingError, "Infrastructure #{@name}: security group names have to be strings"
|
138
|
+
end
|
139
|
+
|
140
|
+
def create_endpoint
|
141
|
+
::URI.parse(@hash[:endpoint])
|
142
|
+
end
|
143
|
+
|
144
|
+
def create_credentials
|
145
|
+
@parsed_credentials[@hash[:credentials]]
|
146
|
+
end
|
147
|
+
|
148
|
+
def create_networks
|
149
|
+
@hash[:networks].collect { |name,hash| ::DopCommon::Network.new(name, hash) }
|
150
|
+
end
|
151
|
+
|
152
|
+
def create_affinity_groups
|
153
|
+
@hash[:affinity_groups].collect { |name,hash| ::DopCommon::AffinityGroup.new(name, hash) }
|
154
|
+
end
|
155
|
+
|
156
|
+
def create_default_security_groups
|
157
|
+
@hash[:default_security_groups]
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,185 @@
|
|
1
|
+
#
|
2
|
+
# DOP Common infrastructure properties parser
|
3
|
+
#
|
4
|
+
|
5
|
+
module DopCommon
|
6
|
+
class InfrastructureProperties
|
7
|
+
include Validator
|
8
|
+
include HashParser
|
9
|
+
|
10
|
+
def initialize(hash, parsed_infrastructure)
|
11
|
+
@hash = symbolize_keys(hash)
|
12
|
+
@parsed_infrastructure = parsed_infrastructure
|
13
|
+
end
|
14
|
+
|
15
|
+
def validate
|
16
|
+
log_validation_method(:affinity_groups_valid?)
|
17
|
+
log_validation_method(:keep_ha_valid?)
|
18
|
+
log_validation_method(:datacenter_valid?)
|
19
|
+
log_validation_method(:cluster_valid?)
|
20
|
+
log_validation_method(:default_pool_valid?)
|
21
|
+
log_validation_method(:dest_folder_valid?)
|
22
|
+
log_validation_method(:tenant_valid?)
|
23
|
+
log_validation_method(:use_config_drive_valid?)
|
24
|
+
log_validation_method(:domain_id_valid?)
|
25
|
+
log_validation_method(:endpoint_type_valid?)
|
26
|
+
end
|
27
|
+
|
28
|
+
def affinity_groups
|
29
|
+
@affinity_groups ||= affinity_groups_valid? ? @hash[:affinity_groups] : []
|
30
|
+
end
|
31
|
+
|
32
|
+
def security_groups
|
33
|
+
@security_groups ||= security_groups_valid? ? create_security_groups : @parsed_infrastructure.default_security_groups
|
34
|
+
end
|
35
|
+
|
36
|
+
def keep_ha?
|
37
|
+
@keep_ha ||= keep_ha_valid? ? @hash[:keep_ha] : true
|
38
|
+
end
|
39
|
+
alias_method :keep_ha, :keep_ha?
|
40
|
+
|
41
|
+
def datacenter
|
42
|
+
@datacenter ||= datacenter_valid? ? @hash[:datacenter] : nil
|
43
|
+
end
|
44
|
+
|
45
|
+
def cluster
|
46
|
+
@cluster ||= cluster_valid? ? @hash[:cluster] : nil
|
47
|
+
end
|
48
|
+
|
49
|
+
def default_pool
|
50
|
+
@default_pool ||= default_pool_valid? ? @hash[:default_pool] : nil
|
51
|
+
end
|
52
|
+
|
53
|
+
def dest_folder
|
54
|
+
@dest_folder ||= dest_folder_valid? ? @hash[:dest_folder] : nil
|
55
|
+
end
|
56
|
+
|
57
|
+
def tenant
|
58
|
+
@tenant ||= tenant_valid? ? @hash[:tenant] : nil
|
59
|
+
end
|
60
|
+
|
61
|
+
def use_config_drive?
|
62
|
+
@use_config_drive ||= use_config_drive_valid? ? @hash[:use_config_drive] : false
|
63
|
+
end
|
64
|
+
alias_method :use_config_drive, :use_config_drive?
|
65
|
+
|
66
|
+
def domain_id
|
67
|
+
@domain_id ||= domain_id_valid? ? create_domain_id : nil
|
68
|
+
end
|
69
|
+
|
70
|
+
def endpoint_type
|
71
|
+
@endpoint_type ||= endpoint_type_valid? ? create_endpoint_type : nil
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def affinity_groups_valid?
|
77
|
+
ags = @hash[:affinity_groups]
|
78
|
+
return false if ags.nil?
|
79
|
+
raise PlanParsingError, "Infrastructure properties: Affinity groups, if specified, must be a non-empty array" if
|
80
|
+
!ags.kind_of?(Array) || ags.empty?
|
81
|
+
raise PlanParsingError, "Infrastructure properties: Each affinity group must be a non-empty string" if
|
82
|
+
ags.any? { |ag| !ag.kind_of?(String) || ag.empty? }
|
83
|
+
true
|
84
|
+
end
|
85
|
+
|
86
|
+
def security_groups_valid?
|
87
|
+
return false unless @hash[:security_groups] or @hash[:additional_security_groups]
|
88
|
+
raise PlanParsingError, "Infrastructure properties: security_groups and additional_security_groups are mutually exclusive" if
|
89
|
+
@hash[:security_groups] and @hash[:additional_security_groups]
|
90
|
+
sgs = @hash[:security_groups] || @hash[:additional_security_groups]
|
91
|
+
raise PlanParsingError, "Infrastructure properties: (additional_)security_groups must be non-empty arrays" if
|
92
|
+
!sgs.kind_of?(Array) || sgs.empty?
|
93
|
+
raise PlanParsingError, "Infrastructure properties: (additional_)security_groups must be non-empty strings" if
|
94
|
+
sgs.any? {|sg| !sg.kind_of?(String) || sg.empty? }
|
95
|
+
true
|
96
|
+
end
|
97
|
+
|
98
|
+
def keep_ha_valid?
|
99
|
+
return false if @hash[:keep_ha].nil?
|
100
|
+
raise PlanParsingError, "Infrastructure properties: The 'keep_ha' is valid only for OVirt/RHEVm infrastructure types" unless
|
101
|
+
@parsed_infrastructure.provides?(:ovirt)
|
102
|
+
raise PlanParsingError, "Infrastructure properties: The 'keep_ha' must be boolean" unless
|
103
|
+
@hash[:keep_ha].kind_of?(TrueClass) || @hash[:keep_ha].kind_of?(FalseClass)
|
104
|
+
true
|
105
|
+
end
|
106
|
+
|
107
|
+
def datacenter_valid?
|
108
|
+
return false unless @parsed_infrastructure.provides?(:ovirt, :vsphere)
|
109
|
+
raise PlanParsingError, "Infrastructure properties: The 'datacenter' must be defined" if
|
110
|
+
@parsed_infrastructure.provides?(:ovirt, :vsphere) && @hash[:datacenter].nil?
|
111
|
+
raise PlanParsingError, "Infrastructure properties: The 'datacenter' must be a non-empty string" if
|
112
|
+
!@hash[:datacenter].kind_of?(String) || @hash[:datacenter].empty?
|
113
|
+
true
|
114
|
+
end
|
115
|
+
|
116
|
+
def cluster_valid?
|
117
|
+
return false unless @parsed_infrastructure.provides?(:ovirt, :vsphere)
|
118
|
+
raise PlanParsingError, "Infrastructure properties: The 'cluster' must be defined" if
|
119
|
+
@parsed_infrastructure.provides?(:ovirt, :vsphere) && @hash[:cluster].nil?
|
120
|
+
raise PlanParsingError, "Infrastructure properties: The 'cluster' must be a non-empty string" if
|
121
|
+
!@hash[:cluster].kind_of?(String) || @hash[:cluster].empty?
|
122
|
+
true
|
123
|
+
end
|
124
|
+
|
125
|
+
def default_pool_valid?
|
126
|
+
return false if @hash[:default_pool].nil?
|
127
|
+
raise PlanParsingError, "Infrastructure properties: The 'default_pool' must be a non-empty string" if
|
128
|
+
!@hash[:default_pool].kind_of?(String) || @hash[:default_pool].empty?
|
129
|
+
true
|
130
|
+
end
|
131
|
+
|
132
|
+
def dest_folder_valid?
|
133
|
+
return false if @hash[:dest_folder].nil?
|
134
|
+
raise PlanParsingError, "Infrastructure properties: The 'dest_folder' must be a non-empty string" if
|
135
|
+
!@hash[:dest_folder].kind_of?(String) || @hash[:dest_folder].empty?
|
136
|
+
true
|
137
|
+
end
|
138
|
+
|
139
|
+
def tenant_valid?
|
140
|
+
return false unless @parsed_infrastructure.provides?(:openstack)
|
141
|
+
raise PlanParsingError, "Infrastructure properties: The 'tenant' must be defined" if
|
142
|
+
@parsed_infrastructure.provides?(:openstack) && @hash[:tenant].nil?
|
143
|
+
raise PlanParsingError, "Infrastructure properties: The 'tenant' must be a non-empty string" if
|
144
|
+
!@hash[:tenant].kind_of?(String) || @hash[:tenant].empty?
|
145
|
+
true
|
146
|
+
end
|
147
|
+
|
148
|
+
def domain_id_valid?
|
149
|
+
return false unless @parsed_infrastructure.provides?(:openstack)
|
150
|
+
return true if @hash[:domain_id].nil?
|
151
|
+
raise PlanParsingError, "Infrastructure properties: The domain_id must be a non-empty string" if
|
152
|
+
!@hash[:domain_id].kind_of?(String) || @hash[:domain_id].empty?
|
153
|
+
true
|
154
|
+
end
|
155
|
+
|
156
|
+
def endpoint_type_valid?
|
157
|
+
return false unless @parsed_infrastructure.provides?(:openstack)
|
158
|
+
return true if @hash[:endpoint_type].nil?
|
159
|
+
raise PlanParsingError, "Infrastructure properties: The endpoint must be 'publicURL', 'internalURL' or 'adminURL'" unless
|
160
|
+
@hash[:endpoint_type].kind_of?(String) && %w(publicURL internalURL adminURL).include?(@hash[:endpoint_type])
|
161
|
+
true
|
162
|
+
end
|
163
|
+
|
164
|
+
def use_config_drive_valid?
|
165
|
+
return false if @hash[:use_config_drive].nil?
|
166
|
+
raise PlanParsingError, "Infrastructure properties: The 'use_config_drive' is valid only for OpenStack infrastructure types" unless @parsed_infrastructure.provides?(:openstack)
|
167
|
+
raise PlanParsingError, "Infrastructure properties: The 'use_config_drive' must be boolean" unless
|
168
|
+
@hash[:use_config_drive].kind_of?(TrueClass) || @hash[:use_config_drive].kind_of?(FalseClass)
|
169
|
+
true
|
170
|
+
end
|
171
|
+
|
172
|
+
def create_security_groups
|
173
|
+
sgs = @hash[:security_groups]
|
174
|
+
sgs ? sgs : (@parsed_infrastructure.default_security_groups + @hash[:additional_security_groups]).uniq
|
175
|
+
end
|
176
|
+
|
177
|
+
def create_domain_id
|
178
|
+
@hash[:domain_id].nil? ? 'default' : @hash[:domain_id]
|
179
|
+
end
|
180
|
+
|
181
|
+
def create_endpoint_type
|
182
|
+
@hash[:endpoint_type].nil? ? 'publicURL' : @hash[:endpoint_type]
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
#
|
2
|
+
# DOP Common configuration lookup
|
3
|
+
#
|
4
|
+
require 'ipaddr'
|
5
|
+
|
6
|
+
module DopCommon
|
7
|
+
class Interface
|
8
|
+
include Validator
|
9
|
+
include HashParser
|
10
|
+
|
11
|
+
attr_reader :name
|
12
|
+
|
13
|
+
def initialize(name, hash, parent={})
|
14
|
+
@name = name
|
15
|
+
@hash = symbolize_keys(hash)
|
16
|
+
@parsed_networks = parent[:parsed_networks]
|
17
|
+
end
|
18
|
+
|
19
|
+
def validate
|
20
|
+
log_validation_method('network_valid?')
|
21
|
+
log_validation_method('ip_valid?')
|
22
|
+
log_validation_method('set_gateway_valid?')
|
23
|
+
log_validation_method('virtual_switch_valid?')
|
24
|
+
log_validation_method('floating_network_valid?')
|
25
|
+
end
|
26
|
+
|
27
|
+
def network
|
28
|
+
@network ||= network_valid? ? network_obj.name : nil
|
29
|
+
end
|
30
|
+
|
31
|
+
def ip
|
32
|
+
@ip ||= ip_valid? ? @hash[:ip] : nil
|
33
|
+
end
|
34
|
+
|
35
|
+
def netmask
|
36
|
+
@netmask ||= network_obj.ip_netmask.to_s
|
37
|
+
end
|
38
|
+
|
39
|
+
def set_gateway?
|
40
|
+
@set_gateway ||= set_gateway_valid? ? (gateway == false ? false : @hash[:set_gateway]) : true
|
41
|
+
end
|
42
|
+
alias_method :set_gateway, :set_gateway?
|
43
|
+
|
44
|
+
def gateway
|
45
|
+
@gateway ||= false == network_obj.ip_defgw ? false : network_obj.ip_defgw.to_s
|
46
|
+
end
|
47
|
+
|
48
|
+
def virtual_switch
|
49
|
+
@virtual_switch ||= virtual_switch_valid? ? @hash[:virtual_switch] : nil
|
50
|
+
end
|
51
|
+
|
52
|
+
def floating_network
|
53
|
+
@floating_network ||= floating_network_valid? ? @hash[:floating_network] : nil
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
def network_valid?
|
58
|
+
raise PlanParsingError, "Interface #{@name}: 'network' must be specified" if
|
59
|
+
@hash[:network].nil?
|
60
|
+
raise PlanParsingError, "Interface #{@name}: 'network' must be a non-empty string" if
|
61
|
+
!@hash[:network].kind_of?(String) || @hash[:network].empty?
|
62
|
+
raise PlanParsingError, "Interface #{@name}: no such network definition '#{@hash[:network]}'" unless
|
63
|
+
network_obj
|
64
|
+
true
|
65
|
+
end
|
66
|
+
|
67
|
+
def ip_valid?
|
68
|
+
raise PlanParsingError, "Interface #{@name}: 'ip' must be a string or symbol" unless
|
69
|
+
[String, Symbol].include?(@hash[:ip].class)
|
70
|
+
case @hash[:ip]
|
71
|
+
when :dhcp, 'dhcp'
|
72
|
+
@hash[:ip] = :dhcp
|
73
|
+
when :none, 'none'
|
74
|
+
@hash[:ip] = :none
|
75
|
+
else
|
76
|
+
ip_addr = IPAddr.new(@hash[:ip])
|
77
|
+
raise PlanParsingError, "Interface #{name}: IP address '#{@hash[:ip]}' " \
|
78
|
+
"is outside of '#{network_obj.ip_pool[:from]} - #{network_obj.ip_pool[:to]}' range" if
|
79
|
+
ip_addr < network_obj.ip_pool[:from] || ip_addr > network_obj.ip_pool[:to]
|
80
|
+
end
|
81
|
+
true
|
82
|
+
rescue ArgumentError
|
83
|
+
raise PlanParsingError, "Interface #{@name}: the specified IP is not valid"
|
84
|
+
end
|
85
|
+
|
86
|
+
def set_gateway_valid?
|
87
|
+
return (gateway == false ? true : false) if @hash[:set_gateway].nil?
|
88
|
+
raise PlanParsingError, "Interface #{@name}: The 'set_gateway', must be true or false" unless
|
89
|
+
[TrueClass, FalseClass].include?(@hash[:set_gateway].class)
|
90
|
+
raise PlanParsingError, "Interface #{@name}: No gateway specified for network '#{network}'" if
|
91
|
+
gateway == false && @hash[:set_gateway] == true
|
92
|
+
true
|
93
|
+
end
|
94
|
+
|
95
|
+
def virtual_switch_valid?
|
96
|
+
return false if @hash[:virtual_switch].nil?
|
97
|
+
raise PlanParsingError, "Interface #{@name}: The 'virtual_switch' must be a non-empty string" if
|
98
|
+
!@hash[:virtual_switch].kind_of?(String) || @hash[:virtual_switch].empty?
|
99
|
+
true
|
100
|
+
end
|
101
|
+
|
102
|
+
def floating_network_valid?
|
103
|
+
return false if @hash[:floating_network].nil?
|
104
|
+
raise PlanParsingError, "Interface #{@name}: the floating network must be a non-empty string" if
|
105
|
+
!@hash[:floating_network].kind_of?(String) || @hash[:floating_network].empty?
|
106
|
+
true
|
107
|
+
end
|
108
|
+
|
109
|
+
def network_obj
|
110
|
+
@network_obj ||= @parsed_networks.find { |n| n.name == @hash[:network] }
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
#
|
2
|
+
# This builds a multi-destination logger and pluggable filters for
|
3
|
+
# around the standard ruby logger.
|
4
|
+
#
|
5
|
+
require 'logger'
|
6
|
+
|
7
|
+
module DopCommon
|
8
|
+
|
9
|
+
def self.log
|
10
|
+
@log ||= create_logger(STDOUT)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.logger=(logger)
|
14
|
+
logger.formatter = DopCommon.filter_formatter
|
15
|
+
@log = logger
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.formatter
|
19
|
+
@formatter ||= Logger::Formatter.new
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.formatter=(formatter_proc)
|
23
|
+
@formatter ||= formatter_proc
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.reset_logger
|
27
|
+
@log = nil
|
28
|
+
@log_filters = []
|
29
|
+
@log_junction = []
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.log_filters
|
33
|
+
@log_filters ||= []
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.add_log_filter(filter_proc)
|
37
|
+
log_filters << filter_proc
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.log_junctions
|
41
|
+
@log_junction ||= []
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.add_log_junction(logger)
|
45
|
+
log_junctions << logger
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.remove_log_junction(logger)
|
49
|
+
log_junctions.delete(logger)
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def self.create_logger(logdev = STDOUT)
|
55
|
+
logger = Logger.new(logdev)
|
56
|
+
logger.formatter = DopCommon.filter_formatter
|
57
|
+
logger
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.filter_formatter
|
61
|
+
Proc.new do |severity, datetime, progname, msg|
|
62
|
+
filtered_message = msg
|
63
|
+
log_filters.each do |filter|
|
64
|
+
filtered_message = case filtered_message
|
65
|
+
when Exception
|
66
|
+
filtered_message.exception(filter.call(filtered_message.message))
|
67
|
+
when String
|
68
|
+
filter.call(filtered_message)
|
69
|
+
else
|
70
|
+
filter.call(filtered_message.inspect)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
log_junctions.each {|logger| logger.log(::Logger.const_get(severity), filtered_message, progname)}
|
74
|
+
formatter.call(severity, datetime, progname, filtered_message)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
#
|
2
|
+
# DOP Common infrastructure hash parser
|
3
|
+
#
|
4
|
+
|
5
|
+
module DopCommon
|
6
|
+
class Network
|
7
|
+
include Validator
|
8
|
+
include HashParser
|
9
|
+
|
10
|
+
attr_reader :name
|
11
|
+
|
12
|
+
def initialize(name, hash)
|
13
|
+
@name = name
|
14
|
+
@hash = deep_symbolize_keys(hash)
|
15
|
+
end
|
16
|
+
|
17
|
+
def validate
|
18
|
+
log_validation_method(:ip_pool_valid?)
|
19
|
+
log_validation_method(:ip_defgw_valid?)
|
20
|
+
log_validation_method(:ip_netmask_valid?)
|
21
|
+
end
|
22
|
+
|
23
|
+
def ip_netmask
|
24
|
+
@ip_netmask ||= ip_netmask_valid? ? IPAddr.new(@hash[:ip_netmask]) : nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def ip_defgw
|
28
|
+
@ip_defgw ||= ip_defgw_valid? ? (@hash[:ip_defgw] == false ? false : IPAddr.new(@hash[:ip_defgw])) : nil
|
29
|
+
end
|
30
|
+
|
31
|
+
def ip_pool
|
32
|
+
@ip_pool ||= ip_pool_valid? ? create_ip_pool : {}
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def ip_netmask_valid?
|
38
|
+
return false if @hash.empty?
|
39
|
+
IPAddr.new(@hash[:ip_netmask])
|
40
|
+
true
|
41
|
+
rescue ArgumentError
|
42
|
+
raise PlanParsingError, "Network #{@name}: Invalid network mask definition"
|
43
|
+
end
|
44
|
+
|
45
|
+
def ip_defgw_valid?
|
46
|
+
return false if @hash.empty?
|
47
|
+
return true if @hash[:ip_defgw] == false
|
48
|
+
IPAddr.new(@hash[:ip_defgw])
|
49
|
+
true
|
50
|
+
rescue ArgumentError
|
51
|
+
raise PlanParsingError, "Network #{@name}: Invalid default gateway definition"
|
52
|
+
end
|
53
|
+
|
54
|
+
def ip_pool_valid?
|
55
|
+
return false if @hash.empty? # An empty network specification is valid
|
56
|
+
# It must be a hash with from and to keys if defined
|
57
|
+
raise PlanParsingError, "Network #{@name}: An IP pool must be a hash with 'from' and 'to' keys" unless
|
58
|
+
@hash[:ip_pool].kind_of?(Hash) and
|
59
|
+
@hash[:ip_pool].has_key?(:from) and
|
60
|
+
@hash[:ip_pool].has_key?(:to)
|
61
|
+
# The IP defined by from must be lower than the IP defined by to keyword
|
62
|
+
ip_from = IPAddr.new(@hash[:ip_pool][:from])
|
63
|
+
ip_to = IPAddr.new(@hash[:ip_pool][:to])
|
64
|
+
raise PlanParsingError, "Network #{@name}: The IP defined by 'from' must to be lower than the IP defined by in 'to'" unless ip_from < ip_to
|
65
|
+
if ip_defgw
|
66
|
+
# The IP of default gateway must be out of IP pool range
|
67
|
+
raise PlanParsingError, "Network #{@name}: The default gateway must be out of IP pool range" unless ip_defgw < ip_from || ip_defgw > ip_to
|
68
|
+
# IPs specified by IP pool and the default gateway must belong to the same network
|
69
|
+
net = ip_defgw.mask(ip_netmask.to_s)
|
70
|
+
raise PlanParsingError, "Network #{@name}: IPs specified by IP pool and the default gateway must belong to the same network" unless
|
71
|
+
net.include?(ip_from) && net.include?(ip_to)
|
72
|
+
elsif ip_defgw.nil?
|
73
|
+
raise PlanParsingError, "Network #{@name}: The default gateway must either be a valid IP or false if there is no default gateway"
|
74
|
+
end
|
75
|
+
true
|
76
|
+
rescue ArgumentError
|
77
|
+
# Invalide IP/Netmasl definition
|
78
|
+
raise PlanParsingError, "Network #{@name}: Invalid IP and/or netmask definition"
|
79
|
+
end
|
80
|
+
|
81
|
+
def create_ip_pool
|
82
|
+
Hash[@hash[:ip_pool].collect { |k,v| [k.to_sym, IPAddr.new(v)] }]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|