forj 1.0.3 → 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +59 -0
- data/Gemfile +14 -15
- data/Rakefile +11 -3
- data/bin/forj +231 -338
- data/forj.gemspec +4 -7
- data/forj/defaults.yaml +3 -3
- data/lib/appinit.rb +21 -32
- data/lib/boot.rb +156 -0
- data/lib/cloud_connection.rb +42 -0
- data/lib/cloud_test.rb +86 -68
- data/lib/destroy.rb +97 -0
- data/lib/forj-settings.rb +350 -167
- data/lib/forj/ForjCli.rb +12 -14
- data/lib/forj/ForjCore.rb +78 -85
- data/lib/forj/process/ForjProcess.rb +1157 -771
- data/lib/get.rb +51 -0
- data/lib/ssh.rb +86 -78
- data/spec/boot_spec.rb +0 -1
- data/spec/spec_helper.rb +65 -65
- metadata +14 -22
data/lib/forj/ForjCli.rb
CHANGED
@@ -20,20 +20,18 @@
|
|
20
20
|
|
21
21
|
# Define framework object on BaseDefinition
|
22
22
|
# See lib/core/definition.rb for function details usage.
|
23
|
-
|
24
|
-
|
25
23
|
class Lorj::BaseDefinition
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
24
|
+
# ************************************ SSH Object
|
25
|
+
define_obj(:ssh,
|
26
|
+
|
27
|
+
:create_e => :ssh_connection
|
28
|
+
)
|
29
|
+
obj_needs :CloudObject, :forge
|
30
|
+
obj_needs :data, :instance_name
|
31
|
+
obj_needs :data, :keypair_name
|
32
|
+
obj_needs :data, :keypair_path
|
35
33
|
|
36
|
-
|
37
|
-
|
38
|
-
|
34
|
+
obj_needs_optional
|
35
|
+
obj_needs :data, :forge_server
|
36
|
+
obj_needs :data, :ssh_user
|
39
37
|
end
|
data/lib/forj/ForjCore.rb
CHANGED
@@ -15,99 +15,92 @@
|
|
15
15
|
# See the License for the specific language governing permissions and
|
16
16
|
# limitations under the License.
|
17
17
|
|
18
|
+
FORJCORE_PATH = File.expand_path(File.dirname(__FILE__))
|
19
|
+
|
20
|
+
require File.join(FORJCORE_PATH, 'process', 'ForjProcess.rb')
|
21
|
+
|
18
22
|
# Defines how to manage Maestro and forges
|
19
23
|
# create a maestro box. Identify a forge instance, delete it,...
|
20
24
|
|
21
25
|
# Define framework object on BaseDefinition
|
22
26
|
# See lib/core/definition.rb for function details usage.
|
27
|
+
class Lorj::BaseDefinition
|
28
|
+
process_default :use_controller => false
|
23
29
|
|
24
|
-
|
30
|
+
# ******************* Maestro Repository object
|
31
|
+
define_obj :maestro_repository,
|
25
32
|
|
26
|
-
|
33
|
+
:create_e => :clone_or_use_maestro_repo
|
27
34
|
|
28
|
-
|
35
|
+
obj_needs :data, :maestro_url
|
36
|
+
|
37
|
+
obj_needs_optional
|
38
|
+
obj_needs :data, :maestro_repo
|
39
|
+
|
40
|
+
# ******************* Infra Repository object
|
41
|
+
define_obj :infra_repository,
|
42
|
+
|
43
|
+
:create_e => :create_or_use_infra
|
44
|
+
|
45
|
+
obj_needs :CloudObject, :maestro_repository
|
46
|
+
obj_needs :data, :infra_repo
|
47
|
+
obj_needs :data, :branch
|
48
|
+
|
49
|
+
# ******************* metadata object
|
50
|
+
define_obj :metadata,
|
51
|
+
|
52
|
+
:create_e => :build_metadata
|
53
|
+
|
54
|
+
obj_needs :data, :instance_name
|
55
|
+
obj_needs :data, :network_name
|
56
|
+
obj_needs :data, :security_group
|
57
|
+
obj_needs :data, :keypair_name
|
58
|
+
obj_needs :data, :image_name
|
59
|
+
obj_needs :data, :bp_flavor
|
60
|
+
obj_needs :data, :compute
|
61
|
+
obj_needs :data, :branch
|
62
|
+
obj_needs :data, :domain_name
|
63
|
+
obj_needs :data, :tenant_name
|
64
|
+
# sent in base64
|
65
|
+
obj_needs :data, :os_user
|
66
|
+
obj_needs :data, :os_enckey
|
67
|
+
obj_needs :data, :account_id
|
68
|
+
obj_needs :data, :account_key
|
69
|
+
obj_needs_optional
|
70
|
+
|
71
|
+
# If requested by user, ask Maestro to manage the DNS.
|
72
|
+
obj_needs :data, :dns_service
|
73
|
+
obj_needs :data, :dns_tenant_id
|
74
|
+
|
75
|
+
# If requested by user, ask Maestro to instantiate a blueprint.
|
76
|
+
obj_needs :data, :blueprint
|
77
|
+
# Add init bootstrap additional steps
|
78
|
+
obj_needs :data, :bootstrap
|
79
|
+
# Add init additional git clone steps.
|
80
|
+
obj_needs :data, :repos
|
81
|
+
|
82
|
+
# ******************* userdata object
|
83
|
+
define_obj :userdata,
|
84
|
+
|
85
|
+
:create_e => :build_userdata
|
86
|
+
|
87
|
+
obj_needs :CloudObject, :maestro_repository
|
88
|
+
obj_needs :CloudObject, :metadata
|
89
|
+
obj_needs :CloudObject, :infra_repository
|
90
|
+
|
91
|
+
# ******************* forge object
|
92
|
+
define_obj :forge,
|
93
|
+
|
94
|
+
:create_e => :build_forge,
|
95
|
+
:delete_e => :delete_forge,
|
96
|
+
:get_e => :get_forge
|
29
97
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
{
|
35
|
-
:create_e => :clone_or_use_maestro_repo
|
36
|
-
}
|
37
|
-
|
38
|
-
obj_needs :data, :maestro_url
|
39
|
-
|
40
|
-
obj_needs_optional
|
41
|
-
obj_needs :data, :maestro_repo
|
42
|
-
|
43
|
-
# ******************* Infra Repository object
|
44
|
-
define_obj :infra_repository,
|
45
|
-
{
|
46
|
-
:create_e => :create_or_use_infra
|
47
|
-
}
|
48
|
-
|
49
|
-
obj_needs :CloudObject, :maestro_repository
|
50
|
-
obj_needs :data, :infra_repo
|
51
|
-
obj_needs :data, :branch
|
52
|
-
|
53
|
-
# ******************* metadata object
|
54
|
-
define_obj :metadata,
|
55
|
-
{
|
56
|
-
:create_e => :build_metadata
|
57
|
-
}
|
58
|
-
|
59
|
-
obj_needs :data, :instance_name
|
60
|
-
obj_needs :data, :network_name
|
61
|
-
obj_needs :data, :security_group
|
62
|
-
obj_needs :data, :keypair_name
|
63
|
-
obj_needs :data, :image_name
|
64
|
-
obj_needs :data, :bp_flavor
|
65
|
-
obj_needs :data, :compute
|
66
|
-
obj_needs :data, :branch
|
67
|
-
obj_needs :data, :domain_name
|
68
|
-
obj_needs :data, :tenant_name
|
69
|
-
# sent in base64
|
70
|
-
obj_needs :data, :os_user
|
71
|
-
obj_needs :data, :os_enckey
|
72
|
-
obj_needs :data, :account_id
|
73
|
-
obj_needs :data, :account_key
|
74
|
-
obj_needs_optional
|
75
|
-
|
76
|
-
# If requested by user, ask Maestro to manage the DNS.
|
77
|
-
obj_needs :data, :dns_service
|
78
|
-
obj_needs :data, :dns_tenant_id
|
79
|
-
|
80
|
-
# If requested by user, ask Maestro to instantiate a blueprint.
|
81
|
-
obj_needs :data, :blueprint
|
82
|
-
# Add init bootstrap additional steps
|
83
|
-
obj_needs :data, :bootstrap
|
84
|
-
# Add init additional git clone steps.
|
85
|
-
obj_needs :data, :repos
|
86
|
-
|
87
|
-
# ******************* userdata object
|
88
|
-
define_obj :userdata,
|
89
|
-
{
|
90
|
-
:create_e => :build_userdata
|
91
|
-
}
|
92
|
-
|
93
|
-
obj_needs :CloudObject, :maestro_repository
|
94
|
-
obj_needs :CloudObject, :metadata
|
95
|
-
obj_needs :CloudObject, :infra_repository
|
96
|
-
|
97
|
-
# ******************* forge object
|
98
|
-
define_obj :forge,
|
99
|
-
{
|
100
|
-
:create_e => :build_forge,
|
101
|
-
:delete_e => :delete_forge,
|
102
|
-
:get_e => :get_forge
|
103
|
-
}
|
104
|
-
obj_needs :CloudObject, :compute_connection
|
105
|
-
obj_needs :CloudObject, :metadata, { :for => [:create_e] }
|
106
|
-
obj_needs :CloudObject, :userdata, { :for => [:create_e] }
|
107
|
-
obj_needs :data, :instance_name, { :for => [:create_e] }
|
108
|
-
|
109
|
-
obj_needs_optional
|
110
|
-
obj_needs :CloudObject, :server
|
111
|
-
obj_needs :data, :blueprint
|
98
|
+
obj_needs :CloudObject, :compute_connection
|
99
|
+
obj_needs :CloudObject, :metadata, :for => [:create_e]
|
100
|
+
obj_needs :CloudObject, :userdata, :for => [:create_e]
|
101
|
+
obj_needs :data, :instance_name, :for => [:create_e]
|
112
102
|
|
103
|
+
obj_needs_optional
|
104
|
+
obj_needs :CloudObject, :server
|
105
|
+
obj_needs :data, :blueprint
|
113
106
|
end
|
@@ -26,882 +26,1268 @@ require 'encryptor' # gem install encryptor
|
|
26
26
|
require 'base64'
|
27
27
|
require 'net/ssh'
|
28
28
|
|
29
|
-
|
29
|
+
INFRA_VERSION = '0.0.37'
|
30
30
|
|
31
|
-
# Functions for boot
|
31
|
+
# Functions for boot - build_forge
|
32
32
|
class ForjCoreProcess
|
33
|
+
def build_forge(sObjectType, hParams)
|
34
|
+
forge_exist?(sObjectType)
|
33
35
|
|
34
|
-
|
35
|
-
key_file = File.join($FORJ_CREDS_PATH, '.key')
|
36
|
-
|
37
|
-
if not File.exists?(key_file)
|
38
|
-
# Need to create a random key.
|
39
|
-
entr = {
|
40
|
-
:key => rand(36**10).to_s(36),
|
41
|
-
:salt => Time.now.to_i.to_s,
|
42
|
-
:iv => Base64::strict_encode64(OpenSSL::Cipher::Cipher.new('aes-256-cbc').random_iv)
|
43
|
-
}
|
44
|
-
|
45
|
-
PrcLib.debug("Writing '%s' key file" % key_file)
|
46
|
-
File.open(key_file, 'w') do |out|
|
47
|
-
out.write(Base64::encode64(entr.to_yaml))
|
48
|
-
end
|
49
|
-
else
|
50
|
-
PrcLib.debug("Loading '%s' key file" % key_file)
|
51
|
-
encoded_key = IO.read(key_file)
|
52
|
-
entr = YAML.load(Base64::decode64(encoded_key))
|
53
|
-
end
|
54
|
-
os_enckey = hParams[:os_enckey]
|
36
|
+
o_server = data_objects(:server, :ObjectData)
|
55
37
|
|
56
|
-
|
57
|
-
os_key = Encryptor.decrypt(
|
58
|
-
:value => Base64::strict_decode64(os_enckey),
|
59
|
-
:key => entr[:key],
|
60
|
-
:iv => Base64::strict_decode64(entr[:iv]),
|
61
|
-
:salt => entr[:salt]
|
62
|
-
)
|
63
|
-
rescue => e
|
64
|
-
raise "Unable to decript your password. You need to re-execute setup."
|
65
|
-
end
|
38
|
+
boot_options = boot_keypairs(o_server)
|
66
39
|
|
67
|
-
|
68
|
-
|
69
|
-
pipe.puts('HPCLOUD_OS_USER=%s' % [hParams[:os_user]] )
|
70
|
-
pipe.puts('HPCLOUD_OS_KEY=%s' % [os_key] )
|
71
|
-
pipe.puts('DNS_KEY=%s' % [hParams[:account_id]] )
|
72
|
-
pipe.puts('DNS_SECRET=%s' % [hParams[:account_key]])
|
73
|
-
pipe.close_write
|
74
|
-
hpcloud_priv = pipe.read
|
75
|
-
}
|
40
|
+
# Define the log lines to get and test.
|
41
|
+
config.set(:log_lines, 5)
|
76
42
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
'cdksite' => config.get(:server_name),
|
81
|
-
'cdkdomain' => hParams[:domain_name],
|
82
|
-
'eroip' => '127.0.0.1',
|
83
|
-
'erosite' => config.get(:server_name),
|
84
|
-
'erodomain' => hParams[:domain_name],
|
85
|
-
'gitbranch' => hParams[:branch],
|
86
|
-
'security_groups' => hParams[:security_group],
|
87
|
-
'tenant_name' => hParams[:tenant_name],
|
88
|
-
'network_name' => hParams[:network_name],
|
89
|
-
'hpcloud_os_region' => hParams[:compute],
|
90
|
-
'PUPPET_DEBUG' => 'True',
|
91
|
-
'image_name' => hParams[:image_name],
|
92
|
-
'key_name' => hParams[:keypair_name],
|
93
|
-
'hpcloud_priv' => Base64.strict_encode64(hpcloud_priv).gsub('=', '') # Remove pad
|
94
|
-
}
|
43
|
+
PrcLib.info("Maestro server '%s' id is '%s'.",
|
44
|
+
o_server[:name], o_server[:id])
|
45
|
+
# Waiting for server to come online before assigning a public IP.
|
95
46
|
|
96
|
-
|
97
|
-
|
98
|
-
hMeta['dns_tenantid'] = hParams[:dns_tenant_id]
|
99
|
-
end
|
100
|
-
# If requested by user, ask Maestro to instantiate a blueprint.
|
101
|
-
hMeta['blueprint'] = hParams[:blueprint] if hParams[:blueprint]
|
47
|
+
s_status = :checking
|
48
|
+
maestro_create_status(s_status)
|
102
49
|
|
103
|
-
|
104
|
-
hMeta['repos'] = hParams[:repos] if hParams[:repos]
|
105
|
-
# Add init bootstrap additionnal steps
|
106
|
-
hMeta['bootstrap'] = hParams[:bootstrap] if hParams[:bootstrap]
|
50
|
+
o_address = data_objects(:public_ip, :ObjectData)
|
107
51
|
|
108
|
-
|
52
|
+
s_status = active_server?(o_server, o_address, boot_options[:keys],
|
53
|
+
boot_options[:coherent], s_status
|
54
|
+
)
|
109
55
|
|
110
|
-
|
111
|
-
hMetaPrintable['hpcloud_priv'] = "XXX - data hidden - XXX"
|
112
|
-
PrcLib.info("Metadata set:\n%s" % hMetaPrintable)
|
56
|
+
till_server_active(s_status, o_server, o_address, boot_options)
|
113
57
|
|
114
|
-
|
115
|
-
oMetaData[:meta_data] = hMeta
|
58
|
+
o_forge = get_forge(sObjectType, config[:instance_name], hParams)
|
116
59
|
|
117
|
-
|
118
|
-
|
60
|
+
read_blueprint_implemented(o_forge, o_address)
|
61
|
+
o_forge
|
62
|
+
end
|
119
63
|
|
120
|
-
|
64
|
+
def forge_exist?(sObjectType)
|
65
|
+
o_forge = process_get(sObjectType, config[:instance_name])
|
66
|
+
if o_forge.empty? || o_forge[:servers].length == 0
|
67
|
+
PrcLib.high_level_msg("\nBuilding your forge...\n")
|
68
|
+
process_create(:internet_server)
|
69
|
+
else
|
70
|
+
load_existing_forge(o_forge)
|
71
|
+
end
|
72
|
+
end
|
121
73
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
else
|
127
|
-
oForge[:servers].each { | oServerToFind |
|
128
|
-
Get(:server, oServerToFind[:id]) if /^maestro\./ =~ oServerToFind[:name]
|
129
|
-
}
|
130
|
-
PrcLib.high_level_msg ("\nChecking your forge...\n")
|
131
|
-
oServer = DataObjects(:server, :ObjectData)
|
132
|
-
if oServer
|
133
|
-
oIP = Query(:public_ip, :server_id => oServer[:id])
|
134
|
-
if oIP.length > 0
|
135
|
-
register oIP[0]
|
136
|
-
end
|
137
|
-
Create(:keypairs)
|
138
|
-
else
|
139
|
-
PrcLib.high_level_msg ("\nYour forge exist, without maestro. Building Maestro...\n")
|
140
|
-
Create(:internet_server)
|
141
|
-
|
142
|
-
PrcLib.high_level_msg ("\nBuilding your forge...\n")
|
143
|
-
end
|
74
|
+
def load_existing_forge(o_forge)
|
75
|
+
o_forge[:servers].each do |oServerToFind|
|
76
|
+
if /^maestro\./ =~ oServerToFind[:name]
|
77
|
+
process_get(:server, oServerToFind[:id])
|
144
78
|
end
|
79
|
+
end
|
80
|
+
PrcLib.high_level_msg("\nChecking your forge...\n")
|
81
|
+
|
82
|
+
o_server = data_objects(:server, :ObjectData)
|
83
|
+
if o_server
|
84
|
+
o_ip = process_query(:public_ip, :server_id => o_server[:id])
|
85
|
+
register o_ip[0] if o_ip.length > 0
|
86
|
+
process_create(:keypairs)
|
87
|
+
else
|
88
|
+
PrcLib.high_level_msg("\nYour forge exist, without maestro." \
|
89
|
+
" Building Maestro...\n")
|
90
|
+
process_create(:internet_server)
|
91
|
+
|
92
|
+
PrcLib.high_level_msg("\nBuilding your forge...\n")
|
93
|
+
end
|
94
|
+
end
|
145
95
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
96
|
+
def boot_keypairs(o_server)
|
97
|
+
# Get keypairs
|
98
|
+
h_keys = keypair_detect(
|
99
|
+
o_server[:key_name],
|
100
|
+
File.join(Forj.keypairs_path, o_server[:key_name])
|
101
|
+
)
|
102
|
+
|
103
|
+
private_key_file = File.join(
|
104
|
+
h_keys[:keypair_path],
|
105
|
+
h_keys[:private_key_name]
|
106
|
+
)
|
107
|
+
# public_key_file = File.join(
|
108
|
+
# h_keys[:keypair_path],
|
109
|
+
# h_keys[:public_key_name]
|
110
|
+
# )
|
111
|
+
|
112
|
+
o_server_key = process_get(:keypairs, o_server[:key_name])
|
113
|
+
|
114
|
+
keypair_coherent = coherent_keypair?(h_keys, o_server_key)
|
115
|
+
boot_options = { :keys => private_key_file, :coherent => keypair_coherent }
|
116
|
+
boot_options
|
117
|
+
end
|
157
118
|
|
158
|
-
|
159
|
-
|
119
|
+
def active_server?(o_server, o_address, private_key_file,
|
120
|
+
keypair_coherent, s_status
|
121
|
+
)
|
122
|
+
if o_server[:attrs][:status] == :active
|
123
|
+
s_msg = <<-END
|
124
|
+
Your forj Maestro server is up and running and is publically accessible
|
125
|
+
through IP '%s'.
|
126
|
+
|
127
|
+
You can connect to '%s' with:
|
128
|
+
ssh ubuntu@%s -o StrictHostKeyChecking=no -i %s
|
129
|
+
END
|
130
|
+
s_msg = format(s_msg, o_address[:public_ip], o_server[:name],
|
131
|
+
o_address[:public_ip], private_key_file
|
132
|
+
)
|
133
|
+
|
134
|
+
unless keypair_coherent
|
135
|
+
s_msg += "\n" + ANSI.bold(
|
136
|
+
'Unfortunatelly'
|
137
|
+
) + ' your current keypair is not usable to connect to your server.' \
|
138
|
+
"\n" + 'You need to fix this issue to gain access to your server.'
|
139
|
+
end
|
140
|
+
PrcLib.info(s_msg)
|
160
141
|
|
161
|
-
|
162
|
-
|
142
|
+
o_log = process_get(:server_log, 25)[:attrs][:output]
|
143
|
+
if /cloud-init boot finished/ =~ o_log
|
144
|
+
s_status = :active
|
145
|
+
PrcLib.high_level_msg("\n%s\nThe forge is ready...\n", s_msg)
|
146
|
+
else
|
147
|
+
PrcLib.high_level_msg("\n%s\nThe forge is still building...\n", s_msg)
|
148
|
+
s_status = :cloud_init
|
149
|
+
end
|
150
|
+
else
|
151
|
+
sleep 5
|
152
|
+
s_status = :starting
|
153
|
+
end
|
154
|
+
s_status
|
155
|
+
end
|
156
|
+
end
|
163
157
|
|
164
|
-
|
165
|
-
|
166
|
-
|
158
|
+
# Functions for boot - build_forge
|
159
|
+
class ForjCoreProcess
|
160
|
+
# rubocop:disable CyclomaticComplexity
|
161
|
+
def maestro_create_status(sStatus, iCurAct = 4)
|
162
|
+
s_activity = '/-\\|?'
|
163
|
+
if iCurAct < 4
|
164
|
+
s_cur_act = 'ACTIVE'
|
165
|
+
else
|
166
|
+
s_cur_act = ANSI.bold('PENDING')
|
167
|
+
end
|
168
|
+
|
169
|
+
case sStatus
|
170
|
+
when :checking
|
171
|
+
PrcLib.state('Checking server status')
|
172
|
+
when :starting
|
173
|
+
PrcLib.state('STARTING')
|
174
|
+
when :assign_ip
|
175
|
+
PrcLib.state('%s - %s - Assigning Public IP',
|
176
|
+
s_activity[iCurAct], s_cur_act)
|
177
|
+
when :cloud_init
|
178
|
+
PrcLib.state('%s - %s - Currently running cloud-init. Be patient.',
|
179
|
+
s_activity[iCurAct], s_cur_act)
|
180
|
+
when :nonet
|
181
|
+
PrcLib.state('%s - %s - Currently running cloud-init. Be patient.',
|
182
|
+
s_activity[iCurAct], s_cur_act)
|
183
|
+
when :restart
|
184
|
+
PrcLib.state('RESTARTING - Currently restarting maestro box. Be patient.')
|
185
|
+
when :active
|
186
|
+
PrcLib.info('Server is active')
|
187
|
+
end
|
188
|
+
end
|
189
|
+
# rubocop:enable CyclomaticComplexity
|
190
|
+
|
191
|
+
def till_server_active(s_status, o_server, o_address, boot_options)
|
192
|
+
m_cloud_init_error = []
|
193
|
+
i_cur_act = 0
|
194
|
+
o_old_log = ''
|
195
|
+
|
196
|
+
while s_status != :active
|
197
|
+
maestro_create_status(s_status, i_cur_act)
|
198
|
+
i_cur_act += 1
|
199
|
+
i_cur_act = i_cur_act % 4
|
200
|
+
o_server = load_server(o_server)
|
201
|
+
# s_status = o_server[:attrs][:status]
|
202
|
+
if s_status == :starting
|
203
|
+
s_status = :assign_ip if o_server[:attrs][:status] == :active
|
204
|
+
elsif s_status == :assign_ip
|
205
|
+
s_status = assign_ip_boot(o_address, boot_options, s_status, o_server)
|
206
|
+
else # analyze the log output
|
207
|
+
output_options = { :status => s_status, :error => m_cloud_init_error,
|
208
|
+
:old_log => o_old_log, :cur_act => i_cur_act
|
209
|
+
}
|
210
|
+
output_options = analyze_log_output(output_options, s_status)
|
211
|
+
s_status = output_options[:status]
|
212
|
+
m_cloud_init_error = output_options[:error]
|
213
|
+
o_old_log = output_options[:old_log]
|
214
|
+
i_cur_act = output_options[:cur_act]
|
215
|
+
end
|
216
|
+
sleep(5) if s_status != :active
|
217
|
+
end
|
218
|
+
end
|
167
219
|
|
168
|
-
|
169
|
-
|
170
|
-
|
220
|
+
# Function to get the server, tracking errors
|
221
|
+
#
|
222
|
+
# *return*
|
223
|
+
# - Server found.
|
224
|
+
#
|
225
|
+
def load_server(server)
|
226
|
+
begin
|
227
|
+
found_server = process_get(:server, server[:attrs][:id])
|
228
|
+
rescue => e
|
229
|
+
PrcLib.error(e.message)
|
230
|
+
end
|
231
|
+
(found_server.nil? ? server : found_server)
|
232
|
+
end
|
233
|
+
end
|
171
234
|
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
sStatus = :active
|
183
|
-
PrcLib.high_level_msg ("\n%s\nThe forge is ready...\n" % sMsg)
|
184
|
-
else
|
185
|
-
PrcLib.high_level_msg ("\n%s\nThe forge is still building...\n" % sMsg)
|
186
|
-
sStatus = :cloud_init
|
187
|
-
end
|
235
|
+
# Functions for boot - build_forge
|
236
|
+
class ForjCoreProcess
|
237
|
+
def assign_ip_boot(o_address, boot_options, s_status, o_server)
|
238
|
+
if o_address.empty?
|
239
|
+
# To be able to ask for server IP assigned
|
240
|
+
query_cache_cleanup(:public_ip)
|
241
|
+
o_addresses = process_query(:public_ip, :server_id => o_server[:id])
|
242
|
+
if o_addresses.length == 0
|
243
|
+
# Assigning Public IP.
|
244
|
+
o_address = process_create(:public_ip)
|
188
245
|
else
|
189
|
-
|
190
|
-
sStatus = :starting
|
246
|
+
o_address = o_addresses[0]
|
191
247
|
end
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
while sStatus != :active
|
198
|
-
maestro_create_status(sStatus, iCurAct)
|
199
|
-
iCurAct += 1
|
200
|
-
iCurAct = iCurAct % 4
|
201
|
-
begin
|
202
|
-
oServer = Get(:server, oServer[:attrs][:id])
|
203
|
-
rescue => e
|
204
|
-
PrcLib.error(e.message)
|
205
|
-
end
|
206
|
-
if sStatus == :starting
|
207
|
-
if oServer[:attrs][:status] == :active
|
208
|
-
sStatus = :assign_ip
|
209
|
-
end
|
210
|
-
elsif sStatus == :assign_ip
|
211
|
-
if oAddress.empty?
|
212
|
-
query_cache_cleanup(:public_ip) # To be able to ask for server IP assigned
|
213
|
-
oAddresses = Query(:public_ip, :server_id => oServer[:id])
|
214
|
-
if oAddresses.length == 0
|
215
|
-
# Assigning Public IP.
|
216
|
-
oAddress = Create(:public_ip)
|
217
|
-
else
|
218
|
-
oAddress = oAddresses[0]
|
219
|
-
end
|
220
|
-
end
|
221
|
-
sMsg = <<-END
|
222
|
-
Public IP for server '#{oServer[:name]}' is assigned.
|
223
|
-
Now, as soon as the server respond to the ssh port, you will be able to get a tail of the build with:
|
248
|
+
end
|
249
|
+
s_msg = <<-END
|
250
|
+
Public IP for server '%s' is assigned.
|
251
|
+
Now, as soon as the server respond to the ssh port,
|
252
|
+
you will be able to get a tail of the build with:
|
224
253
|
while [ 1 = 1 ]
|
225
254
|
do
|
226
|
-
|
227
|
-
|
255
|
+
ssh ubuntu@%s -o StrictHostKeyChecking=no -i %s tail -f /var/log/cloud-init.log
|
256
|
+
sleep 5
|
228
257
|
done
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
PrcLib.high_level_msg ("Critical error cleared. Cloud-init seems moving...")
|
244
|
-
PrcLib.info ("Critical error cleared. Cloud-init seems moving...")
|
245
|
-
mCloudInitError = []
|
246
|
-
end
|
247
|
-
elsif /\[CRITICAL\]/ =~ oLog
|
248
|
-
mCritical = oLog.scan(/.*\[CRITICAL\].*\n/)
|
249
|
-
if not (mCloudInitError == mCritical)
|
250
|
-
sReported = oLog.clone
|
251
|
-
sReported['CRITICAL'] = ANSI.bold('CRITICAL')
|
252
|
-
PrcLib.error("cloud-init error detected:\n-----\n%s\n-----\nPlease connect to the box to decide what you need to do." % [sReported])
|
253
|
-
mCloudInitError = mCritical
|
254
|
-
end
|
255
|
-
elsif sStatus == :cloud_init and /cloud-init-nonet gave up waiting for a network device/ =~ oLog
|
256
|
-
# Valid for ubuntu image 12.04
|
257
|
-
PrcLib.warning("Cloud-init has gave up to configure the network. waiting...")
|
258
|
-
sStatus = :nonet
|
259
|
-
elsif sStatus == :nonet and /Booting system without full network configuration/ =~ oLog
|
260
|
-
# Valid for ubuntu image 12.04
|
261
|
-
PrcLib.warning("forj has detected an issue to bring up your maestro server. Removing it and re-creating a new one. please be patient...")
|
262
|
-
sStatus = :restart
|
263
|
-
elsif sStatus == :restart
|
264
|
-
Delete(:server)
|
265
|
-
Create(:internet_server)
|
266
|
-
sStatus = :starting
|
267
|
-
end
|
268
|
-
end
|
269
|
-
sleep(5) if sStatus != :active
|
270
|
-
end
|
258
|
+
END
|
259
|
+
s_msg = format(s_msg, o_server[:name],
|
260
|
+
o_address[:public_ip], boot_options[:keys]
|
261
|
+
)
|
262
|
+
unless boot_options[:coherent]
|
263
|
+
s_msg += ANSI.bold("\nUnfortunatelly") + " your current keypair' \
|
264
|
+
' is not usable to connect to your server.\nYou need to fix' \
|
265
|
+
' this issue to gain access to your server."
|
266
|
+
end
|
267
|
+
PrcLib.info(s_msg)
|
268
|
+
PrcLib.high_level_msg("\n%s\nThe forge is still building...\n", s_msg)
|
269
|
+
s_status = :cloud_init
|
270
|
+
s_status
|
271
|
+
end
|
271
272
|
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
273
|
+
def analyze_log_output(output_options, s_status)
|
274
|
+
# m_cloud_init_error = []
|
275
|
+
# o_old_log = ''
|
276
|
+
o_log = process_get(:server_log, 25)[:attrs][:output]
|
277
|
+
# i_cur_act = 4 if o_log == o_old_log
|
278
|
+
output_options[:cur_act] = 4 if o_log == output_options[:old_log]
|
279
|
+
# o_old_log = o_log
|
280
|
+
output_options[:old_log] = o_log
|
281
|
+
if /cloud-init boot finished/ =~ o_log
|
282
|
+
# s_status = :active
|
283
|
+
output_options[:status] = :active
|
284
|
+
output_options[:error] = display_boot_moving_error(
|
285
|
+
output_options[:error]
|
286
|
+
)
|
287
|
+
elsif /\[CRITICAL\]/ =~ o_log
|
288
|
+
m_critical = o_log.scan(/.*\[CRITICAL\].*\n/)
|
289
|
+
output_options[:error] = display_boot_critical_error(
|
290
|
+
output_options[:error],
|
291
|
+
m_critical
|
292
|
+
)
|
293
|
+
else
|
294
|
+
# validate server status
|
295
|
+
output_options = analyze_server_status(s_status, o_log, output_options)
|
296
|
+
end
|
297
|
+
output_options
|
298
|
+
end
|
299
|
+
|
300
|
+
def display_boot_critical_error(m_cloud_init_error, m_critical)
|
301
|
+
# unless (m_cloud_init_error == m_critical)
|
302
|
+
return if m_cloud_init_error == m_critical
|
303
|
+
s_reported = o_log.clone
|
304
|
+
s_reported['CRITICAL'] = ANSI.bold('CRITICAL')
|
305
|
+
PrcLib.error("cloud-init error detected:\n-----\n%s\n-----\n" \
|
306
|
+
'Please connect to the box to decide what you' \
|
307
|
+
' need to do.', s_reported)
|
308
|
+
m_cloud_init_error = m_critical
|
309
|
+
m_cloud_init_error
|
310
|
+
# end
|
311
|
+
end
|
312
|
+
|
313
|
+
def display_boot_moving_error(m_cloud_init_error)
|
314
|
+
if m_cloud_init_error != []
|
315
|
+
PrcLib.high_level_msg(
|
316
|
+
'Critical error cleared. Cloud-init seems moving...'
|
317
|
+
)
|
318
|
+
PrcLib.info('Critical error cleared. Cloud-init seems moving...')
|
319
|
+
m_cloud_init_error = []
|
320
|
+
end
|
321
|
+
m_cloud_init_error
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
# Functions for boot - build_forge
|
326
|
+
class ForjCoreProcess
|
327
|
+
def analyze_server_status(s_status, o_log, output_options)
|
328
|
+
if s_status == :cloud_init &&
|
329
|
+
/cloud-init-nonet gave up waiting for a network device/ =~ o_log
|
330
|
+
# Valid for ubuntu image 12.04
|
331
|
+
PrcLib.warning(
|
332
|
+
'Cloud-init has gave up to configure the network. waiting...'
|
333
|
+
)
|
334
|
+
output_options[:status] = :nonet
|
335
|
+
elsif s_status == :nonet &&
|
336
|
+
/Booting system without full network configuration/ =~ o_log
|
337
|
+
# Valid for ubuntu image 12.04
|
338
|
+
PrcLib.warning(
|
339
|
+
'forj has detected an issue to bring up your maestro server.' \
|
340
|
+
' Removing it and re-creating a new one. please be patient...'
|
341
|
+
)
|
342
|
+
output_options[:status] = :restart
|
343
|
+
elsif s_status == :restart
|
344
|
+
process_delete(:server)
|
345
|
+
process_create(:internet_server)
|
346
|
+
output_options[:status] = :starting
|
347
|
+
end
|
348
|
+
output_options
|
349
|
+
end
|
350
|
+
|
351
|
+
def read_blueprint_implemented(o_forge, o_address)
|
352
|
+
s_msg = format(
|
353
|
+
"Your Forge '%s' is ready and accessible from" \
|
354
|
+
" IP #{o_address[:public_ip]}.",
|
355
|
+
config[:instance_name]
|
356
|
+
)
|
357
|
+
# TODO: read the blueprint/layout to identify which services
|
358
|
+
# are implemented and can be accessible.
|
359
|
+
if config[:blueprint]
|
360
|
+
s_msg += format(
|
361
|
+
"\n" + 'Maestro has implemented the following server(s) for your' \
|
362
|
+
" blueprint '%s':",
|
363
|
+
config[:blueprint]
|
364
|
+
)
|
365
|
+
server_options = display_servers_with_ip(o_forge, s_msg)
|
366
|
+
s_msg += server_options[:message]
|
367
|
+
i_count = server_options[:count]
|
368
|
+
if i_count > 0
|
369
|
+
s_msg += format("\n%d server(s) identified.\n", i_count)
|
296
370
|
else
|
297
|
-
|
371
|
+
s_msg = 'No servers found except maestro'
|
372
|
+
PrcLib.warning(
|
373
|
+
format(
|
374
|
+
'Something went wrong, while creating nodes for blueprint' \
|
375
|
+
" '%s'. check maestro logs.",
|
376
|
+
config[:blueprint]
|
377
|
+
)
|
378
|
+
)
|
298
379
|
end
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
380
|
+
else
|
381
|
+
s_msg += "\nMaestro has NOT implemented any servers, because you did" \
|
382
|
+
' not provided a blueprint. Connect to Maestro, and ask Maestro to' \
|
383
|
+
' implement any kind of blueprint you need. (Feature currently' \
|
384
|
+
' under development)'
|
385
|
+
end
|
386
|
+
PrcLib.info(s_msg)
|
387
|
+
PrcLib.high_level_msg("\n%s\nEnjoy!\n", s_msg)
|
388
|
+
end
|
389
|
+
|
390
|
+
def display_servers_with_ip(o_forge, s_msg)
|
391
|
+
i_count = 0
|
392
|
+
o_forge[:servers].each do |server|
|
393
|
+
next if /^maestro\./ =~ server[:name]
|
394
|
+
register(server)
|
395
|
+
o_ip = process_query(:public_ip, :server_id => server[:id])
|
396
|
+
if o_ip.length == 0
|
397
|
+
s_msg += format("\n- %s (No public IP)", server[:name])
|
308
398
|
else
|
309
|
-
|
399
|
+
s_msg += format("\n- %s (%s)", server[:name], o_ip[0][:public_ip])
|
310
400
|
end
|
401
|
+
i_count += 1
|
402
|
+
end
|
403
|
+
server_options = { :message => s_msg, :count => i_count }
|
404
|
+
server_options
|
405
|
+
end
|
406
|
+
end
|
311
407
|
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
408
|
+
# Functions for boot - build_metadata
|
409
|
+
class ForjCoreProcess
|
410
|
+
def load_encoded_key
|
411
|
+
key_file = File.join(PrcLib.pdata_path, '.key')
|
412
|
+
if !File.exist?(key_file)
|
413
|
+
# Need to create a random key.
|
414
|
+
entr = {
|
415
|
+
:key => rand(36**10).to_s(36),
|
416
|
+
:salt => Time.now.to_i.to_s,
|
417
|
+
:iv => Base64.strict_encode64(
|
418
|
+
OpenSSL::Cipher::Cipher.new('aes-256-cbc').random_iv
|
419
|
+
)
|
420
|
+
}
|
421
|
+
|
422
|
+
PrcLib.debug("Writing '%s' key file", key_file)
|
423
|
+
File.open(key_file, 'w') do |out|
|
424
|
+
out.write(Base64.encode64(entr.to_yaml))
|
327
425
|
end
|
328
|
-
|
426
|
+
else
|
427
|
+
PrcLib.debug("Loading '%s' key file", key_file)
|
428
|
+
encoded_key = IO.read(key_file)
|
429
|
+
entr = YAML.load(Base64.decode64(encoded_key))
|
430
|
+
end
|
431
|
+
entr
|
432
|
+
end
|
329
433
|
|
330
|
-
|
434
|
+
def decrypt_key(os_enckey, entr)
|
435
|
+
begin
|
436
|
+
os_key = Encryptor.decrypt(
|
437
|
+
:value => Base64.strict_decode64(os_enckey),
|
438
|
+
:key => entr[:key],
|
439
|
+
:iv => Base64.strict_decode64(entr[:iv]),
|
440
|
+
:salt => entr[:salt]
|
441
|
+
)
|
442
|
+
rescue
|
443
|
+
raise 'Unable to decript your password. You need to re-execute setup.'
|
444
|
+
end
|
445
|
+
os_key
|
446
|
+
end
|
331
447
|
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
448
|
+
def load_hpcloud(hParams, os_key)
|
449
|
+
hpcloud_priv = nil
|
450
|
+
IO.popen('gzip -c', 'r+') do|pipe|
|
451
|
+
pipe.puts(format('HPCLOUD_OS_USER=%s', hParams[:os_user]))
|
452
|
+
pipe.puts(format('HPCLOUD_OS_KEY=%s', os_key))
|
453
|
+
pipe.puts(format('DNS_KEY=%s', hParams[:account_id]))
|
454
|
+
pipe.puts(format('DNS_SECRET=%s', hParams[:account_key]))
|
455
|
+
pipe.close_write
|
456
|
+
hpcloud_priv = pipe.read
|
457
|
+
end
|
458
|
+
hpcloud_priv
|
459
|
+
end
|
336
460
|
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
end
|
357
|
-
oMaestro = register(hResult, sObjectType)
|
358
|
-
oMaestro[:maestro_repo] = hResult[:maestro_repo]
|
359
|
-
oMaestro
|
360
|
-
end
|
461
|
+
def load_h_meta(hParams, hpcloud_priv)
|
462
|
+
h_meta = {
|
463
|
+
'cdksite' => config.get(:server_name),
|
464
|
+
'cdkdomain' => hParams[:domain_name],
|
465
|
+
'eroip' => '127.0.0.1',
|
466
|
+
'erosite' => config.get(:server_name),
|
467
|
+
'erodomain' => hParams[:domain_name],
|
468
|
+
'gitbranch' => hParams[:branch],
|
469
|
+
'security_groups' => hParams[:security_group],
|
470
|
+
'tenant_name' => hParams[:tenant_name],
|
471
|
+
'network_name' => hParams[:network_name],
|
472
|
+
'hpcloud_os_region' => hParams[:compute],
|
473
|
+
'PUPPET_DEBUG' => 'True',
|
474
|
+
'image_name' => hParams[:image_name],
|
475
|
+
'key_name' => hParams[:keypair_name],
|
476
|
+
'hpcloud_priv' => Base64.strict_encode64(
|
477
|
+
hpcloud_priv
|
478
|
+
).gsub('=', '') # Remove pad
|
479
|
+
}
|
361
480
|
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
481
|
+
if hParams[:dns_service]
|
482
|
+
h_meta['dns_zone'] = hParams[:dns_service]
|
483
|
+
h_meta['dns_tenantid'] = hParams[:dns_tenant_id]
|
484
|
+
end
|
485
|
+
# If requested by user, ask Maestro to instantiate a blueprint.
|
486
|
+
h_meta['blueprint'] = hParams[:blueprint] if hParams[:blueprint]
|
487
|
+
|
488
|
+
# Add init additionnal git clone steps.
|
489
|
+
h_meta['repos'] = hParams[:repos] if hParams[:repos]
|
490
|
+
# Add init bootstrap additionnal steps
|
491
|
+
h_meta['bootstrap'] = hParams[:bootstrap] if hParams[:bootstrap]
|
492
|
+
h_meta
|
493
|
+
end
|
369
494
|
|
370
|
-
|
495
|
+
def build_metadata(sObjectType, hParams)
|
496
|
+
entr = load_encoded_key
|
371
497
|
|
372
|
-
|
498
|
+
os_enckey = hParams[:os_enckey]
|
373
499
|
|
374
|
-
|
500
|
+
os_key = decrypt_key(os_enckey, entr)
|
375
501
|
|
376
|
-
|
377
|
-
PrcLib.state("Building your infra workspace in '%s'" % [infra])
|
502
|
+
hpcloud_priv = load_hpcloud(hParams, os_key)
|
378
503
|
|
379
|
-
|
380
|
-
|
504
|
+
config.set(
|
505
|
+
:server_name,
|
506
|
+
format('maestro.%s', hParams[:instance_name])
|
507
|
+
) # Used by :server object
|
381
508
|
|
382
|
-
|
383
|
-
File.write(file_ver, $INFRA_VERSION)
|
384
|
-
PrcLib.info("The infra workspace '%s' has been built from maestro predefined files." % [infra])
|
385
|
-
else
|
386
|
-
PrcLib.info("Re-using your infra... in '%s'" % [infra])
|
387
|
-
end
|
509
|
+
h_meta = load_h_meta(hParams, hpcloud_priv)
|
388
510
|
|
511
|
+
config.set(:meta_data, h_meta) # Used by :server object
|
389
512
|
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
end
|
394
|
-
|
395
|
-
# Function which compare directories from maestro templates to infra.
|
396
|
-
def infra_is_original?(infra_dir, maestro_dir)
|
397
|
-
dest_cloud_init = File.join(infra_dir, 'cloud-init')
|
398
|
-
template = File.join(maestro_dir, 'templates', 'infra')
|
399
|
-
sMD5List = File.join(infra_dir, '.maestro_original.yaml')
|
400
|
-
bResult = true
|
401
|
-
hResult = {}
|
402
|
-
if File.exists?(sMD5List)
|
403
|
-
begin
|
404
|
-
hResult = YAML.load_file(sMD5List)
|
405
|
-
rescue => e
|
406
|
-
PrcLib.error("Unable to load valid Original files list '%s'. Your infra workspace won't be migrated, until fixed." % sMD5List)
|
407
|
-
bResult = false
|
408
|
-
end
|
409
|
-
if not hResult
|
410
|
-
hResult = {}
|
411
|
-
bResult = false
|
412
|
-
end
|
413
|
-
end
|
414
|
-
# We are taking care on bootstrap files only.
|
415
|
-
Find.find(File.join(template, 'cloud-init')) { | path |
|
416
|
-
if not File.directory?(path)
|
417
|
-
sMaestroRelPath = path.clone
|
418
|
-
sMaestroRelPath[File.join(template, 'cloud-init/')] = ""
|
419
|
-
sInfra_path = File.join(dest_cloud_init, sMaestroRelPath)
|
420
|
-
if File.exists?(sInfra_path)
|
421
|
-
md5_file = Digest::MD5.file(sInfra_path).hexdigest
|
422
|
-
if hResult.key?(sMaestroRelPath) and hResult[sMaestroRelPath] != md5_file
|
423
|
-
bResult = false
|
424
|
-
PrcLib.info("'%s' infra file has changed from original template in maestro." % sInfra_path)
|
425
|
-
else
|
426
|
-
PrcLib.debug("'%s' infra file has not been updated." % sInfra_path)
|
427
|
-
end
|
428
|
-
end
|
429
|
-
md5_file = Digest::MD5.file(path).hexdigest
|
430
|
-
hResult[sMaestroRelPath] = md5_file
|
431
|
-
end
|
432
|
-
}
|
433
|
-
begin
|
434
|
-
File.open(sMD5List, 'w') do |out|
|
435
|
-
YAML.dump(hResult, out)
|
436
|
-
end
|
437
|
-
rescue => e
|
438
|
-
PrcLib.error("%s\n%s" % [e.message, e.backtrace.join("\n")])
|
439
|
-
end
|
440
|
-
if bResult
|
441
|
-
PrcLib.debug("No original files found has been updated. Infra workspace can be updated/created if needed.")
|
442
|
-
else
|
443
|
-
PrcLib.warning("At least, one file has been updated. Infra workspace won't be updated by forj cli.")
|
444
|
-
end
|
445
|
-
bResult
|
446
|
-
end
|
513
|
+
h_meta_printable = h_meta.clone
|
514
|
+
h_meta_printable['hpcloud_priv'] = 'XXX - data hidden - XXX'
|
515
|
+
PrcLib.info("Metadata set:\n%s", h_meta_printable)
|
447
516
|
|
448
|
-
|
449
|
-
|
517
|
+
o_meta_data = register(h_meta, sObjectType)
|
518
|
+
o_meta_data[:meta_data] = h_meta
|
450
519
|
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
def old_infra_data_update(oConfig, version, infra_dir)
|
464
|
-
PrcLib.info("Migrating your local infra repo (%s) to the latest version." % version)
|
465
|
-
bRebuild = false # Be default migration is successful. No need to rebuild it.
|
466
|
-
case version
|
467
|
-
when '0.0.36'
|
468
|
-
# Moving from 0.0.36 or less to 0.0.37 or higher.
|
469
|
-
# SET_COMPUTE="{SET_COMPUTE!}" => Setting for Compute. ignored. Coming from HPC
|
470
|
-
# SET_TENANT_NAME="{SET_TENANT_NAME!}" => Setting for Compute. ignored. Need to query HPC from current Tenant ID
|
471
|
-
|
472
|
-
# SET_DNS_TENANTID="{SET_DNS_TENANTID!}" => Setting for DNS. meta = dns_tenantid
|
473
|
-
# ==> :forj_accounts, sAccountName, :dns, :tenant_id
|
474
|
-
|
475
|
-
# SET_DNS_ZONE="{SET_DNS_ZONE!}" => Setting for DNS. meta = dns_zone
|
476
|
-
# ==> :forj_accounts, sAccountName, :dns, :service
|
477
|
-
|
478
|
-
# SET_DOMAIN="{SET_DOMAIN!}" => Setting for Maestro (required) and DNS if enabled.
|
479
|
-
# ==> :forj_accounts, sAccountName, :dns, :domain_name
|
480
|
-
sAccountName = oConfig.get(:account_name)
|
481
|
-
|
482
|
-
yDns = {}
|
483
|
-
yDns = oConfig.oConfig.ExtraGet(:forj_accounts, sAccountName, :dns) if oConfig.oConfig.ExtraExist?(:forj_accounts, sAccountName, :dns)
|
484
|
-
Dir.foreach(infra_dir) do | file |
|
485
|
-
next if not /^maestro\.box\..*\.env$/ =~ file
|
486
|
-
build_env = File.join(infra_dir, file)
|
487
|
-
PrcLib.debug("Reading data from '%s'" % build_env)
|
488
|
-
tags = {'SET_DNS_TENANTID' => :tenant_id,
|
489
|
-
'SET_DNS_ZONE' => :service,
|
490
|
-
'SET_DOMAIN' => :domain_name
|
491
|
-
}
|
492
|
-
begin
|
493
|
-
bUpdate = nil
|
494
|
-
|
495
|
-
File.open(build_env) do |f|
|
496
|
-
f.each_line do |line|
|
497
|
-
mObj = line.match(/^(SET_[A-Z_]+)=["'](.*)["'].*$/)
|
498
|
-
if mObj
|
499
|
-
PrcLib.debug("Reviewing detected '%s' tag" % [mObj[1]])
|
500
|
-
tag = (tags[mObj[1]]? tags[mObj[1]] : nil)
|
501
|
-
if tag and mObj[2]
|
502
|
-
if bUpdate == nil and rhGet(yDns, tag) and rhGet(yDns, tag) != mObj[2]
|
503
|
-
PrcLib.message("Your account setup is different than build env.")
|
504
|
-
PrcLib.message("We suggest you to update your account setup with data from your build env.")
|
505
|
-
bUpdate = agree("Do you want to update your setup with those build environment data?")
|
506
|
-
end
|
507
|
-
if bUpdate != nil and bUpdate
|
508
|
-
PrcLib.debug("Saved: '%s' = '%s'" % [mObj[1],mObj[2]])
|
509
|
-
rhSet(yDns, mObj[2], tag)
|
510
|
-
end
|
511
|
-
end
|
512
|
-
end
|
513
|
-
end
|
514
|
-
end
|
515
|
-
rescue => e
|
516
|
-
PrcLib.fatal(1, "Failed to open the build environment file '%s'" % build_env, e)
|
517
|
-
end
|
518
|
-
end
|
519
|
-
file_ver = File.join(infra_dir, 'forj-cli.ver')
|
520
|
-
File.write(file_ver, $INFRA_VERSION)
|
521
|
-
oConfig.oConfig.ExtraSet(:forj_accounts, sAccountName, :dns, yDns)
|
522
|
-
oConfig.oConfig.ExtraSave(File.join($FORJ_ACCOUNTS_PATH, sAccountName), :forj_accounts, sAccountName)
|
523
|
-
return bRebuild
|
520
|
+
o_meta_data
|
521
|
+
end
|
522
|
+
end
|
523
|
+
|
524
|
+
# Functions for boot - clone_or_use_maestro_repo
|
525
|
+
class ForjCoreProcess
|
526
|
+
def clone_maestro_repo(maestro_url, path_maestro, config)
|
527
|
+
PrcLib.state("Cloning maestro repo from '%s' to '%s'",
|
528
|
+
maestro_url, File.join(path_maestro, 'maestro'))
|
529
|
+
if File.directory?(path_maestro)
|
530
|
+
if File.directory?(File.join(path_maestro, 'maestro'))
|
531
|
+
FileUtils.rm_r File.join(path_maestro, 'maestro')
|
524
532
|
end
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
# generate boot_*.sh
|
545
|
-
mime_cmd = "#{build_tmpl_dir}/write-mime-multipart.py"
|
546
|
-
bootstrap = "#{build_tmpl_dir}/bootstrap_build.sh"
|
547
|
-
|
548
|
-
cmd = "%s '%s' '%s' '%s' '%s' '%s' '%s' '%s'" % [
|
549
|
-
bootstrap, # script
|
550
|
-
$FORJ_DATA_PATH, # $1 = Forj data base dir
|
551
|
-
hParams[:maestro_repository, :maestro_repo], # $2 = Maestro repository dir
|
552
|
-
config[:bootstrap_dirs], # $3 = Bootstrap directories
|
553
|
-
config[:bootstrap_extra_dir], # $4 = Bootstrap extra directory
|
554
|
-
meta_data, # $5 = meta_data (string)
|
555
|
-
mime_cmd, # $6: mime script file to execute.
|
556
|
-
mime # $7: mime file generated.
|
557
|
-
]
|
558
|
-
|
559
|
-
# TODO: Replace shell script call to ruby functions
|
560
|
-
if $LIB_FORJ_DEBUG >= 1
|
561
|
-
cmd += " >> #{$FORJ_DATA_PATH}/forj.log"
|
533
|
+
end
|
534
|
+
git = Git.clone(maestro_url, 'maestro', :path => path_maestro)
|
535
|
+
git.checkout(config[:branch]) if config[:branch] != 'master'
|
536
|
+
PrcLib.info("Maestro repo '%s' cloned on branch '%s'",
|
537
|
+
File.join(path_maestro, 'maestro'), config[:branch])
|
538
|
+
end
|
539
|
+
|
540
|
+
def clone_or_use_maestro_repo(sObjectType, hParams)
|
541
|
+
maestro_url = hParams[:maestro_url]
|
542
|
+
maestro_repo = File.expand_path(
|
543
|
+
hParams[:maestro_repo]
|
544
|
+
) unless hParams[:maestro_repo].nil?
|
545
|
+
path_maestro = File.expand_path('~/.forj/')
|
546
|
+
h_result = {}
|
547
|
+
|
548
|
+
begin
|
549
|
+
if maestro_repo && File.directory?(maestro_repo)
|
550
|
+
PrcLib.info("Using maestro repo '%s'", maestro_repo)
|
551
|
+
h_result[:maestro_repo] = maestro_repo
|
562
552
|
else
|
563
|
-
|
553
|
+
h_result[:maestro_repo] = File.join(path_maestro, 'maestro')
|
554
|
+
clone_maestro_repo(maestro_url, path_maestro, config)
|
564
555
|
end
|
565
|
-
|
566
|
-
|
567
|
-
|
556
|
+
rescue => e
|
557
|
+
PrcLib.error('Error while cloning the repo from %s\n%s\n%s',
|
558
|
+
maestro_url, e.message, e.backtrace.join("\n"))
|
559
|
+
PrcLib.info(
|
560
|
+
'If this error persist you could clone the repo manually in ~/.forj/'
|
561
|
+
)
|
562
|
+
end
|
563
|
+
o_maestro = register(h_result, sObjectType)
|
564
|
+
o_maestro[:maestro_repo] = h_result[:maestro_repo]
|
565
|
+
o_maestro
|
566
|
+
end
|
567
|
+
end
|
568
|
+
|
569
|
+
# Functions for boot - create_or_use_infra
|
570
|
+
class ForjCoreProcess
|
571
|
+
def create_or_use_infra(sObjectType, hParams)
|
572
|
+
infra = File.expand_path(hParams[:infra_repo])
|
573
|
+
maestro_repo = hParams[:maestro_repository, :maestro_repo]
|
574
|
+
# branch = hParams[:branch]
|
575
|
+
dest_cloud_init = File.join(infra, 'cloud-init')
|
576
|
+
template = File.join(maestro_repo, 'templates', 'infra')
|
577
|
+
cloud_init = File.join(template, 'cloud-init')
|
568
578
|
|
569
|
-
|
579
|
+
h_infra = { :infra_repo => dest_cloud_init }
|
570
580
|
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
PrcLib.fatal(1, e.message)
|
575
|
-
end
|
576
|
-
if $LIB_FORJ_DEBUG < 5
|
577
|
-
File.delete(mime)
|
578
|
-
else
|
579
|
-
ForjLib.debug(5, "user_data temp file '%s' kept" % mime)
|
580
|
-
end
|
581
|
+
PrcLib.ensure_dir_exists(dest_cloud_init)
|
582
|
+
|
583
|
+
b_rebuild_infra = infra_is_original?(infra, maestro_repo)
|
581
584
|
|
582
|
-
|
585
|
+
if b_rebuild_infra
|
586
|
+
PrcLib.state("Building your infra workspace in '%s'", infra)
|
583
587
|
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
588
|
+
PrcLib.debug("Copying recursively '%s' to '%s'", cloud_init, infra)
|
589
|
+
FileUtils.copy_entry(cloud_init, dest_cloud_init)
|
590
|
+
|
591
|
+
file_ver = File.join(infra, 'forj-cli.ver')
|
592
|
+
File.write(file_ver, INFRA_VERSION)
|
593
|
+
PrcLib.info("The infra workspace '%s' has been built from maestro" \
|
594
|
+
' predefined files.', infra)
|
595
|
+
else
|
596
|
+
PrcLib.info("Re-using your infra... in '%s'", infra)
|
597
|
+
end
|
598
|
+
|
599
|
+
o_infra = register(h_infra, sObjectType)
|
600
|
+
o_infra[:infra_repo] = h_infra[:infra_repo]
|
601
|
+
o_infra
|
602
|
+
end
|
603
|
+
|
604
|
+
def load_infra(template, dest_cloud_init, h_result, b_result)
|
605
|
+
# We are taking care on bootstrap files only.
|
606
|
+
Find.find(File.join(template, 'cloud-init')) do |path|
|
607
|
+
# unless File.directory?(path)
|
608
|
+
next if File.directory?(path)
|
609
|
+
s_maestro_rel_path = path.clone
|
610
|
+
s_maestro_rel_path[File.join(template, 'cloud-init/')] = ''
|
611
|
+
s_infra_path = File.join(dest_cloud_init, s_maestro_rel_path)
|
612
|
+
if File.exist?(s_infra_path)
|
613
|
+
md5_file = Digest::MD5.file(s_infra_path).hexdigest
|
614
|
+
if h_result.key?(s_maestro_rel_path) &&
|
615
|
+
h_result[s_maestro_rel_path] != md5_file
|
616
|
+
b_result = false
|
617
|
+
PrcLib.info("'%s' infra file has changed from original template" \
|
618
|
+
' in maestro.', s_infra_path)
|
619
|
+
else
|
620
|
+
PrcLib.debug("'%s' infra file has not been updated.", s_infra_path)
|
621
|
+
end
|
622
|
+
end
|
623
|
+
md5_file = Digest::MD5.file(path).hexdigest
|
624
|
+
h_result[s_maestro_rel_path] = md5_file
|
625
|
+
# end
|
626
|
+
end
|
627
|
+
b_result
|
590
628
|
end
|
591
629
|
|
630
|
+
def open_md5(s_md5_list, h_result)
|
631
|
+
# begin
|
632
|
+
File.open(s_md5_list, 'w') do |out|
|
633
|
+
YAML.dump(h_result, out)
|
634
|
+
end
|
635
|
+
rescue => e
|
636
|
+
PrcLib.error("%s\n%s", e.message, e.backtrace.join("\n"))
|
637
|
+
# end
|
638
|
+
end
|
592
639
|
end
|
593
640
|
|
594
|
-
# Functions for
|
641
|
+
# Functions for boot - create_or_use_infra
|
595
642
|
class ForjCoreProcess
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
end
|
612
|
-
else
|
613
|
-
return false
|
614
|
-
end
|
643
|
+
# Function which compare directories from maestro templates to infra.
|
644
|
+
def infra_is_original?(infra_dir, maestro_dir)
|
645
|
+
dest_cloud_init = File.join(infra_dir, 'cloud-init')
|
646
|
+
template = File.join(maestro_dir, 'templates', 'infra')
|
647
|
+
s_md5_list = File.join(infra_dir, '.maestro_original.yaml')
|
648
|
+
b_result = true
|
649
|
+
h_result = {}
|
650
|
+
if File.exist?(s_md5_list)
|
651
|
+
begin
|
652
|
+
h_result = YAML.load_file(s_md5_list)
|
653
|
+
rescue
|
654
|
+
PrcLib.error("Unable to load valid Original files list '%s'. " \
|
655
|
+
"Your infra workspace won't be migrated, until fixed.",
|
656
|
+
s_md5_list)
|
657
|
+
b_result = false
|
615
658
|
end
|
616
|
-
|
617
|
-
|
659
|
+
unless h_result
|
660
|
+
h_result = {}
|
661
|
+
b_result = false
|
662
|
+
end
|
663
|
+
end
|
664
|
+
b_result = load_infra(template, dest_cloud_init, h_result, b_result)
|
665
|
+
open_md5(s_md5_list, h_result)
|
666
|
+
if b_result
|
667
|
+
PrcLib.debug(
|
668
|
+
'No original files found has been updated. Infra workspace' \
|
669
|
+
' can be updated/created if needed.'
|
670
|
+
)
|
671
|
+
else
|
672
|
+
PrcLib.warning(
|
673
|
+
'At least, one file has been updated. Infra workspace' \
|
674
|
+
" won't be updated by forj cli."
|
675
|
+
)
|
676
|
+
end
|
677
|
+
b_result
|
678
|
+
end
|
618
679
|
|
619
|
-
|
620
|
-
|
621
|
-
# Getting Account keypair information
|
622
|
-
key_name = config[:keypair_name]
|
623
|
-
key_path = File.expand_path(config[:keypair_files])
|
680
|
+
def infra_rebuild(infra_dir)
|
681
|
+
return false unless File.exist?(infra_dir)
|
624
682
|
|
625
|
-
|
626
|
-
|
627
|
-
|
683
|
+
file_ver = File.join(infra_dir, 'forj-cli.ver')
|
684
|
+
forj_infra_version = nil
|
685
|
+
forj_infra_version = File.read(file_ver) if File.exist?(file_ver)
|
628
686
|
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
end
|
637
|
-
new_key_name = new_key_name.to_s
|
638
|
-
sMsg = "Incorrect. You have to choose a keypair name different than '#{key_name}'. If you want to interrupt, press Ctrl-C and retry later.\nSo, please, provide a different keypair name:" if key_name == new_key_name
|
639
|
-
end
|
640
|
-
key_name = new_key_name
|
641
|
-
config.set(:key_name, key_name)
|
642
|
-
keys = keypair_detect(key_name, key_path)
|
643
|
-
end
|
687
|
+
if forj_infra_version.nil? || forj_infra_version == ''
|
688
|
+
# Prior version 37
|
689
|
+
return(old_infra_data_update(oConfig, '0.0.36', infra_dir))
|
690
|
+
elsif Gem::Version.new(forj_infra_version) < Gem::Version.new(INFRA_VERSION)
|
691
|
+
return(old_infra_data_update(oConfig, forj_infra_version, infra_dir))
|
692
|
+
end
|
693
|
+
end
|
644
694
|
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
PrcLib.message("The private key file attached to keypair named '%s' is not found. Running ssh-keygen to create it." % keys[:keypair_name])
|
653
|
-
if not File.exists?(private_key_file)
|
654
|
-
AppInit.ensure_dir_exists(File.dirname(private_key_file))
|
655
|
-
command = 'ssh-keygen -t rsa -f %s' % private_key_file
|
656
|
-
PrcLib.debug("Executing '%s'" % command)
|
657
|
-
system(command)
|
658
|
-
end
|
659
|
-
if not File.exists?(private_key_file)
|
660
|
-
PrcLib.fatal(1, "'%s' not found. Unable to add your keypair to hpcloud. Create it yourself and provide it with -p option. Then retry." % [private_key_file])
|
661
|
-
else
|
662
|
-
PrcLib.fatal(1, "ssh-keygen did not created your key pairs. Aborting. Please review errors in ~/.forj/forj.log")
|
663
|
-
end
|
664
|
-
end
|
695
|
+
def update_build_env(b_update, tag, y_dns, m_obj)
|
696
|
+
if !b_update.nil? && b_update
|
697
|
+
PrcLib.debug("Saved: '%s' = '%s'", m_obj[1], m_obj[2])
|
698
|
+
y_dns.rh_set(m_obj[2], tag)
|
699
|
+
end
|
700
|
+
b_update
|
701
|
+
end
|
665
702
|
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
703
|
+
def update_build_env?(b_update, tag, y_dns, m_obj)
|
704
|
+
if tag && m_obj[2]
|
705
|
+
if b_update.nil? &&
|
706
|
+
y_dns.rh_get(tag) && y_dns.rh_get(tag) != m_obj[2]
|
707
|
+
PrcLib.message('Your account setup is different than'\
|
708
|
+
' build env.')
|
709
|
+
PrcLib.message('We suggest you to update your account'\
|
710
|
+
' setup with data from your build env.')
|
711
|
+
b_update = agree('Do you want to update your setup with'\
|
712
|
+
' those build environment data?')
|
671
713
|
end
|
714
|
+
b_update = update_build_env(b_update, tag, y_dns, m_obj)
|
715
|
+
end
|
716
|
+
b_update
|
717
|
+
end
|
672
718
|
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
if
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
PrcLib.info("Private key keypair up to date.")
|
694
|
-
end
|
695
|
-
if Digest::MD5.file(public_key_file).hexdigest != Digest::MD5.file(forj_public_key_file).hexdigest
|
696
|
-
PrcLib.info("Updating public key keypair piece to FORJ keypairs list.")
|
697
|
-
FileUtils.copy(public_key_file, forj_public_key_file)
|
698
|
-
else
|
699
|
-
PrcLib.info("Public key keypair up to date.")
|
700
|
-
end
|
701
|
-
end
|
702
|
-
end
|
703
|
-
# Saving internal copy of private key file for forj use.
|
704
|
-
config.set(:keypair_path, forj_private_key_file )
|
705
|
-
PrcLib.info("Configured forj keypair '%s' with '%s'" % [ keys[:keypair_name], File.join(keys[:keypair_path], keys[:key_basename]) ] )
|
706
|
-
true # forj_setup_keypairs_files successfull
|
707
|
-
end
|
708
|
-
|
709
|
-
def forj_DNS_settings()
|
710
|
-
sAsk = "Optionally, you can ask Maestro to use/manage a domain name on your cloud. It requires your DNS cloud service to be enabled.\nDo you want to configure it?"
|
711
|
-
config.set(:dns_settings, agree(sAsk))
|
712
|
-
true
|
713
|
-
end
|
714
|
-
|
715
|
-
def forj_DNS_settings?(sKey)
|
716
|
-
# Return true to ask the question. false otherwise
|
717
|
-
if not config.get(:dns_settings)
|
718
|
-
config.set(sKey, nil)
|
719
|
-
return false # Do not ask
|
720
|
-
end
|
721
|
-
true
|
722
|
-
end
|
719
|
+
def open_build_env(build_env, tags, y_dns)
|
720
|
+
b_update = nil
|
721
|
+
|
722
|
+
File.open(build_env) do |f|
|
723
|
+
line = f.readline
|
724
|
+
next if line.match(/^(SET_[A-Z_]+)=["'](.*)["'].*$/).nil?
|
725
|
+
# f.each_line do |line|
|
726
|
+
m_obj = line.match(/^(SET_[A-Z_]+)=["'](.*)["'].*$/)
|
727
|
+
# if m_obj
|
728
|
+
PrcLib.debug("Reviewing detected '%s' tag", m_obj[1])
|
729
|
+
tag = (tags[m_obj[1]] ? tags[m_obj[1]] : nil)
|
730
|
+
b_update = update_build_env(b_update, tag, y_dns, m_obj)
|
731
|
+
# end
|
732
|
+
# end
|
733
|
+
end
|
734
|
+
rescue => e
|
735
|
+
PrcLib.fatal(1, "Failed to open the build environment file '%s'",
|
736
|
+
build_env, e)
|
737
|
+
end
|
738
|
+
end
|
723
739
|
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
740
|
+
# Functions for boot - create_or_use_infra
|
741
|
+
class ForjCoreProcess
|
742
|
+
def old_infra_data_update(oConfig, version, infra_dir)
|
743
|
+
PrcLib.info('Migrating your local infra repo (%s) to the latest version.',
|
744
|
+
version)
|
745
|
+
# Be default migration is successful. No need to rebuild it.
|
746
|
+
b_rebuild = false
|
747
|
+
case version
|
748
|
+
when '0.0.36'
|
749
|
+
# Moving from 0.0.36 or less to 0.0.37 or higher.
|
750
|
+
# SET_COMPUTE="{SET_COMPUTE!}" => Setting for Compute.
|
751
|
+
# ignored. Coming from HPC
|
752
|
+
# SET_TENANT_NAME="{SET_TENANT_NAME!}" => Setting for Compute.
|
753
|
+
# ignored. Need to query HPC from current Tenant ID
|
754
|
+
|
755
|
+
# SET_DNS_TENANTID="{SET_DNS_TENANTID!}" => Setting for DNS.
|
756
|
+
# meta = dns_tenantid
|
757
|
+
# ==> :forj_accounts, s_account_name, :dns, :tenant_id
|
758
|
+
|
759
|
+
# SET_DNS_ZONE="{SET_DNS_ZONE!}" => Setting for DNS. meta = dns_zone
|
760
|
+
# ==> :forj_accounts, s_account_name, :dns, :service
|
761
|
+
|
762
|
+
# SET_DOMAIN="{SET_DOMAIN!}" => Setting for Maestro (required)
|
763
|
+
# and DNS if enabled.
|
764
|
+
# ==> :forj_accounts, s_account_name, :dns, :domain_name
|
765
|
+
y_dns = {}
|
766
|
+
y_dns = oConfig[:dns] if oConfig.exist?(:dns)
|
767
|
+
|
768
|
+
Dir.foreach(infra_dir) do |file|
|
769
|
+
next unless /^maestro\.box\..*\.env$/ =~ file
|
770
|
+
build_env = File.join(infra_dir, file)
|
771
|
+
PrcLib.debug("Reading data from '%s'", build_env)
|
772
|
+
tags = { 'SET_DNS_TENANTID' => :tenant_id,
|
773
|
+
'SET_DNS_ZONE' => :service,
|
774
|
+
'SET_DOMAIN' => :domain_name
|
775
|
+
}
|
776
|
+
|
777
|
+
open_build_env(build_env, tags, y_dns)
|
744
778
|
end
|
745
|
-
|
746
|
-
|
779
|
+
file_ver = File.join(infra_dir, 'forj-cli.ver')
|
780
|
+
File.write(file_ver, INFRA_VERSION)
|
781
|
+
oConfig[:dns] = y_dns
|
782
|
+
oConfig.ac_save
|
783
|
+
return b_rebuild
|
784
|
+
end
|
785
|
+
end
|
786
|
+
end
|
747
787
|
|
788
|
+
# Functions for boot - build_userdata
|
789
|
+
class ForjCoreProcess
|
790
|
+
def run_userdata_cmd(cmd, bootstrap, mime)
|
791
|
+
# TODO: Replace shell script call to ruby functions
|
792
|
+
if PrcLib.core_level >= 1
|
793
|
+
cmd += " >> #{PrcLib.log_file}"
|
794
|
+
else
|
795
|
+
cmd += " | tee -a #{PrcLib.log_file}"
|
796
|
+
end
|
797
|
+
fail ForjError.new, "#{bootstrap} script file is" \
|
798
|
+
' not found.' unless File.exist?(bootstrap)
|
799
|
+
PrcLib.debug("Running '%s'", cmd)
|
800
|
+
Kernel.system(cmd)
|
801
|
+
|
802
|
+
fail ForjError.new, format(
|
803
|
+
"mime file '%s' not found.",
|
804
|
+
mime
|
805
|
+
) unless File.exist?(mime)
|
806
|
+
end
|
807
|
+
|
808
|
+
def build_userdata(sObjectType, hParams)
|
809
|
+
# get the paths for maestro and infra repositories
|
810
|
+
# maestro_path = hParams[:maestro_repository].values
|
811
|
+
# infra_path = hParams[:infra_repository].values
|
812
|
+
|
813
|
+
# concatenate the paths for boothook and cloud_config files
|
814
|
+
# ~ build_dir = File.expand_path(File.join($FORJ_DATA_PATH, '.build'))
|
815
|
+
# ~ boothook = File.join(maestro_path, 'build', 'bin', 'build-tools')
|
816
|
+
# ~ cloud_config = File.join(maestro_path, 'build', 'maestro')
|
817
|
+
|
818
|
+
mime = File.join(
|
819
|
+
Forj.build_path,
|
820
|
+
format('userdata.mime.%s', rand(36**5).to_s(36))
|
821
|
+
)
|
822
|
+
|
823
|
+
meta_data = JSON.generate(hParams[:metadata, :meta_data])
|
824
|
+
|
825
|
+
build_tmpl_dir = File.expand_path(File.join(LIB_PATH, 'build_tmpl'))
|
826
|
+
|
827
|
+
PrcLib.state("Preparing user_data - file '%s'", mime)
|
828
|
+
# generate boot_*.sh
|
829
|
+
mime_cmd = "#{build_tmpl_dir}/write-mime-multipart.py"
|
830
|
+
bootstrap = "#{build_tmpl_dir}/bootstrap_build.sh"
|
831
|
+
|
832
|
+
cmd = format(
|
833
|
+
"%s '%s' '%s' '%s' '%s' '%s' '%s' '%s'",
|
834
|
+
bootstrap, # script
|
835
|
+
PrcLib.data_path, # $1 = Forj data base dir
|
836
|
+
# $2 = Maestro repository dir
|
837
|
+
hParams[:maestro_repository, :maestro_repo],
|
838
|
+
config[:bootstrap_dirs], # $3 = Bootstrap directories
|
839
|
+
config[:bootstrap_extra_dir], # $4 = Bootstrap extra directory
|
840
|
+
meta_data, # $5 = meta_data (string)
|
841
|
+
mime_cmd, # $6: mime script file to execute.
|
842
|
+
mime # $7: mime file generated.
|
843
|
+
)
|
844
|
+
|
845
|
+
run_userdata_cmd(cmd, bootstrap, mime)
|
846
|
+
|
847
|
+
begin
|
848
|
+
user_data = File.read(mime)
|
849
|
+
rescue => e
|
850
|
+
PrcLib.fatal(1, e.message)
|
851
|
+
end
|
852
|
+
if PrcLib.core_level < 5
|
853
|
+
File.delete(mime)
|
854
|
+
else
|
855
|
+
ForjLib.debug(5, "user_data temp file '%s' kept", mime)
|
856
|
+
end
|
857
|
+
|
858
|
+
config[:user_data] = user_data
|
859
|
+
|
860
|
+
o_user_data = register(hParams, sObjectType)
|
861
|
+
o_user_data[:user_data] = user_data
|
862
|
+
o_user_data[:user_data_encoded] = Base64.strict_encode64(user_data)
|
863
|
+
o_user_data[:mime] = mime
|
864
|
+
PrcLib.info("user_data prepared. File: '%s'", mime)
|
865
|
+
o_user_data
|
866
|
+
end
|
748
867
|
end
|
749
868
|
|
750
|
-
#
|
869
|
+
# Functions for setup
|
751
870
|
class ForjCoreProcess
|
752
|
-
def
|
753
|
-
|
754
|
-
|
755
|
-
|
871
|
+
def create_directory(base_dir)
|
872
|
+
# unless File.directory?(base_dir)
|
873
|
+
return true if FIle.directory?(base_dir)
|
874
|
+
if agree(
|
875
|
+
format("'%s' doesn't exist. Do you want to create it?", base_dir)
|
876
|
+
)
|
877
|
+
PrcLib.ensure_dir_exists(base_dir)
|
878
|
+
# true
|
879
|
+
else
|
880
|
+
return false
|
881
|
+
end
|
882
|
+
# end
|
883
|
+
end
|
884
|
+
# Check files existence
|
885
|
+
def forj_check_keypairs_files(keypath)
|
886
|
+
key_name = config.get(:keypair_name)
|
887
|
+
|
888
|
+
keys_entered = keypair_detect(key_name, keypath)
|
889
|
+
if !keys_entered[:private_key_exist?] && !keys_entered[:public_key_exist?]
|
890
|
+
if agree('The key you entered was not found. Do you want to create' \
|
891
|
+
' this one?')
|
892
|
+
base_dir = keys_entered[:keypair_path]
|
893
|
+
return create_directory(base_dir)
|
894
|
+
else
|
895
|
+
return false
|
896
|
+
end
|
897
|
+
end
|
898
|
+
true
|
899
|
+
end
|
756
900
|
|
757
|
-
|
901
|
+
def duplicate_keyname?(keys_imported, keys, key_name)
|
902
|
+
if keys_imported && keys_imported[:key_basename] != keys[:key_basename] &&
|
903
|
+
Forj.keypairs_path != keys[:keypair_path]
|
904
|
+
PrcLib.warning("The private key '%s' was assigned to a different private"\
|
905
|
+
" key file '%s'.\nTo not overwrite it, we recommend you"\
|
906
|
+
' to choose a different keypair name.',
|
907
|
+
keys, keys_imported[:key_basename])
|
908
|
+
new_key_name = key_name
|
909
|
+
s_msg = 'Please, provide a different keypair name:'
|
910
|
+
while key_name == new_key_name
|
911
|
+
new_key_name = ask(s_msg) do |q|
|
912
|
+
q.validate = /.+/
|
913
|
+
end
|
914
|
+
new_key_name = new_key_name.to_s
|
915
|
+
s_msg = 'Incorrect. You have to choose a keypair name different' \
|
916
|
+
" than '#{key_name}'. If you want to interrupt, press Ctrl-C and" \
|
917
|
+
' retry later.\nSo, please, provide a different keypair' \
|
918
|
+
' name:' if key_name == new_key_name
|
919
|
+
end
|
920
|
+
key_name = new_key_name
|
921
|
+
config.set(:key_name, key_name)
|
922
|
+
keys = keypair_detect(key_name, key_path)
|
923
|
+
end
|
924
|
+
keys
|
925
|
+
end
|
758
926
|
|
759
|
-
|
927
|
+
def create_keys_automatically(keys, private_key_file)
|
928
|
+
return if keys[:private_key_exist?]
|
929
|
+
# Need to create a key. ask if we need so.
|
930
|
+
PrcLib.message("The private key file attached to keypair named '%s' is not"\
|
931
|
+
' found. Running ssh-keygen to create it.',
|
932
|
+
keys[:keypair_name])
|
933
|
+
unless File.exist?(private_key_file)
|
934
|
+
PrcLib.ensure_dir_exists(File.dirname(private_key_file))
|
935
|
+
command = format('ssh-keygen -t rsa -f %s', private_key_file)
|
936
|
+
PrcLib.debug(format("Executing '%s'", command))
|
937
|
+
system(command)
|
938
|
+
end
|
939
|
+
if !File.exist?(private_key_file)
|
940
|
+
PrcLib.fatal(1, "'%s' not found. Unable to add your keypair to hpcloud."\
|
941
|
+
' Create it yourself and provide it with -p option. '\
|
942
|
+
'Then retry.', private_key_file)
|
943
|
+
else
|
944
|
+
PrcLib.fatal(1, 'ssh-keygen did not created your key pairs. Aborting.'\
|
945
|
+
' Please review errors in ~/.forj/forj.log')
|
946
|
+
end
|
947
|
+
end
|
948
|
+
end
|
760
949
|
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
950
|
+
# Functions for setup
|
951
|
+
class ForjCoreProcess
|
952
|
+
def load_key_with_passphrase(keys, public_key_file, private_key_file)
|
953
|
+
# unless keys[:public_key_exist?]
|
954
|
+
return if keys[:private_key_exist?]
|
955
|
+
PrcLib.message("Your public key '%s' was not found. Getting it from the" \
|
956
|
+
' private one. It may require your passphrase.',
|
957
|
+
public_key_file)
|
958
|
+
command = format(
|
959
|
+
'ssh-keygen -y -f %s > %s',
|
960
|
+
private_key_file,
|
961
|
+
public_key_file
|
962
|
+
)
|
963
|
+
PrcLib.debug("Executing '%s'", command)
|
964
|
+
system(command)
|
965
|
+
# end
|
966
|
+
end
|
967
|
+
|
968
|
+
def save_sequences(private_key_file, forj_private_key_file,
|
969
|
+
public_key_file, forj_public_key_file, key_name
|
970
|
+
)
|
971
|
+
PrcLib.info('Importing key pair to FORJ keypairs list.')
|
972
|
+
FileUtils.copy(private_key_file, forj_private_key_file)
|
973
|
+
FileUtils.copy(public_key_file, forj_public_key_file)
|
974
|
+
# Attaching this keypair to the account
|
975
|
+
@hAccountData.rh_set(key_name, :credentials, 'keypair_name')
|
976
|
+
@hAccountData.rh_set(forj_private_key_file, :credentials, 'keypair_path')
|
977
|
+
config.local_set(key_name.to_s, private_key_file, :imported_keys)
|
978
|
+
end
|
766
979
|
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
980
|
+
def save_md5(private_key_file, forj_private_key_file,
|
981
|
+
public_key_file, forj_public_key_file
|
982
|
+
)
|
983
|
+
# Checking source/dest files content
|
984
|
+
if Digest::MD5.file(private_key_file).hexdigest !=
|
985
|
+
Digest::MD5.file(forj_private_key_file).hexdigest
|
986
|
+
PrcLib.info(
|
987
|
+
'Updating private key keypair piece to FORJ keypairs list.'
|
988
|
+
)
|
989
|
+
FileUtils.copy(private_key_file, forj_private_key_file)
|
990
|
+
else
|
991
|
+
PrcLib.info('Private key keypair up to date.')
|
992
|
+
end
|
993
|
+
if Digest::MD5.file(public_key_file).hexdigest !=
|
994
|
+
Digest::MD5.file(forj_public_key_file).hexdigest
|
995
|
+
PrcLib.info(
|
996
|
+
'Updating public key keypair piece to FORJ keypairs list.'
|
997
|
+
)
|
998
|
+
FileUtils.copy(public_key_file, forj_public_key_file)
|
999
|
+
else
|
1000
|
+
PrcLib.info('Public key keypair up to date.')
|
1001
|
+
end
|
771
1002
|
end
|
772
1003
|
end
|
773
1004
|
|
774
|
-
#
|
1005
|
+
# Functions for setup
|
775
1006
|
class ForjCoreProcess
|
776
|
-
|
1007
|
+
def save_internal_key(forj_private_key_file, keys)
|
1008
|
+
# Saving internal copy of private key file for forj use.
|
1009
|
+
config.set(:keypair_path, forj_private_key_file)
|
1010
|
+
PrcLib.info("Configured forj keypair '%s' with '%s'",
|
1011
|
+
keys[:keypair_name],
|
1012
|
+
File.join(keys[:keypair_path], keys[:key_basename])
|
1013
|
+
)
|
1014
|
+
end
|
777
1015
|
|
778
|
-
|
1016
|
+
# keypair_files post setup
|
1017
|
+
def forj_setup_keypairs_files
|
1018
|
+
# Getting Account keypair information
|
1019
|
+
key_name = config[:keypair_name]
|
1020
|
+
key_path = File.expand_path(config[:keypair_files])
|
779
1021
|
|
780
|
-
|
1022
|
+
keys_imported = nil
|
1023
|
+
keys_imported = keypair_detect(
|
1024
|
+
key_name,
|
1025
|
+
config.local_get(key_name, :imported_keys)
|
1026
|
+
) if config.local_exist?(key_name, :imported_keys)
|
1027
|
+
keys = keypair_detect(key_name, key_path)
|
781
1028
|
|
782
|
-
|
1029
|
+
keys = duplicate_keyname?(keys_imported, keys, key_name)
|
783
1030
|
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
1031
|
+
private_key_file = File.join(keys[:keypair_path], keys[:private_key_name])
|
1032
|
+
public_key_file = File.join(keys[:keypair_path], keys[:public_key_name])
|
1033
|
+
|
1034
|
+
# Creation sequences
|
1035
|
+
create_keys_automatically(keys, private_key_file)
|
1036
|
+
|
1037
|
+
load_key_with_passphrase(keys, public_key_file, private_key_file)
|
1038
|
+
|
1039
|
+
forj_private_key_file = File.join(Forj.keypairs_path, key_name)
|
1040
|
+
# forj_public_key_file = File.join($FORJ_KEYPAIRS_PATH, key_name + '.pub')
|
1041
|
+
|
1042
|
+
# Saving sequences
|
1043
|
+
if keys[:keypair_path] != Forj.keypairs_path
|
1044
|
+
if !File.exist?(forj_private_key_file) ||
|
1045
|
+
!File.exist?(forj_public_key_file)
|
1046
|
+
save_sequences(private_key_file, forj_private_key_file,
|
1047
|
+
public_key_file, forj_public_key_file, key_name
|
1048
|
+
)
|
792
1049
|
else
|
793
|
-
|
1050
|
+
save_md5(private_key_file, forj_private_key_file,
|
1051
|
+
public_key_file, forj_public_key_file
|
1052
|
+
)
|
794
1053
|
end
|
1054
|
+
end
|
1055
|
+
|
1056
|
+
save_internal_key(forj_private_key_file, keys)
|
1057
|
+
true # forj_setup_keypairs_files successfull
|
1058
|
+
end
|
1059
|
+
|
1060
|
+
def forj_dns_settings
|
1061
|
+
s_ask = 'Optionally, you can ask Maestro to use/manage a domain name on' \
|
1062
|
+
" your cloud. It requires your DNS cloud service to be enabled.\nDo " \
|
1063
|
+
' you want to configure it?'
|
1064
|
+
config.set(:dns_settings, agree(s_ask))
|
1065
|
+
true
|
1066
|
+
end
|
1067
|
+
|
1068
|
+
def forj_dns_settings?(sKey)
|
1069
|
+
# Return true to ask the question. false otherwise
|
1070
|
+
unless config.get(:dns_settings)
|
1071
|
+
config.set(sKey, nil)
|
1072
|
+
return false # Do not ask
|
1073
|
+
end
|
1074
|
+
true
|
1075
|
+
end
|
1076
|
+
|
1077
|
+
def setup_tenant_name
|
1078
|
+
# TODO: To re-introduce with a Controller call instead.
|
1079
|
+
o_ssl_error = SSLErrorMgt.new # Retry object
|
1080
|
+
PrcLib.debug('Getting tenants from hpcloud cli libraries')
|
1081
|
+
begin
|
1082
|
+
tenants = Connection.instance.tenants(@sAccountName)
|
1083
|
+
rescue => e
|
1084
|
+
retry unless o_ssl_error.ErrorDetected(e.message, e.backtrace, e)
|
1085
|
+
PrcLib.fatal(1, 'Network: Unable to connect.')
|
1086
|
+
end
|
1087
|
+
tenant_id = @oConfig.ExtraGet(:hpc_accounts, @sAccountName,
|
1088
|
+
:credentials).rh_get(:tenant_id)
|
1089
|
+
tenant_name = nil
|
1090
|
+
tenants.each do |elem|
|
1091
|
+
tenant_name = elem['name'] if elem['id'] == tenant_id
|
1092
|
+
end
|
1093
|
+
if tenant_name
|
1094
|
+
PrcLib.debug("Tenant ID '%s': '%s' found.", tenant_id, tenant_name)
|
1095
|
+
@hAccountData.rh_set(tenant_name, :maestro, :tenant_name)
|
1096
|
+
else
|
1097
|
+
PrcLib.error("Unable to find the tenant Name for '%s' ID.", tenant_id)
|
1098
|
+
end
|
1099
|
+
@oConfig.set('tenants', tenants)
|
795
1100
|
end
|
796
1101
|
end
|
797
1102
|
|
798
|
-
#
|
1103
|
+
# Funtions for get
|
799
1104
|
class ForjCoreProcess
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
1105
|
+
def get_forge(sCloudObj, sForgeId, _hParams)
|
1106
|
+
s_query = {}
|
1107
|
+
h_servers = []
|
1108
|
+
s_query[:name] = sForgeId
|
1109
|
+
|
1110
|
+
o_servers = process_query(:server, s_query)
|
1111
|
+
|
1112
|
+
regex = Regexp.new(format('\.%s$', sForgeId))
|
1113
|
+
|
1114
|
+
o_servers.each do |o_server|
|
1115
|
+
o_name = o_server[:name]
|
1116
|
+
h_servers << o_server if regex =~ o_name
|
1117
|
+
end
|
1118
|
+
PrcLib.info('%s server(s) were found under instance name %s ',
|
1119
|
+
h_servers.count, s_query[:name])
|
1120
|
+
|
1121
|
+
o_forge = register(h_servers, sCloudObj)
|
1122
|
+
o_forge[:servers] = h_servers
|
1123
|
+
o_forge[:name] = sForgeId
|
1124
|
+
o_forge
|
1125
|
+
end
|
1126
|
+
end
|
809
1127
|
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
1128
|
+
# Funtions for destroy
|
1129
|
+
class ForjCoreProcess
|
1130
|
+
def delete_forge(_sCloudObj, hParams)
|
1131
|
+
PrcLib.state('Destroying server(s) of your forge')
|
1132
|
+
|
1133
|
+
forge_serverid = config.get(:forge_server)
|
1134
|
+
|
1135
|
+
o_forge = hParams[:forge]
|
1136
|
+
|
1137
|
+
o_forge[:servers].each do|server|
|
1138
|
+
next if forge_serverid && forge_serverid != server[:id]
|
1139
|
+
register(server)
|
1140
|
+
PrcLib.state("Destroying server '%s'", server[:name])
|
1141
|
+
process_delete(:server)
|
1142
|
+
end
|
1143
|
+
if forge_serverid.nil?
|
1144
|
+
PrcLib.high_level_msg("The forge '%s' has been destroyed. (all servers" \
|
1145
|
+
" linked to the forge)\n", o_forge[:name])
|
1146
|
+
else
|
1147
|
+
PrcLib.high_level_msg("Server(s) selected in the forge '%s' has been"\
|
1148
|
+
" removed.\n", o_forge[:name])
|
1149
|
+
end
|
1150
|
+
end
|
1151
|
+
end
|
814
1152
|
|
815
|
-
|
816
|
-
|
1153
|
+
# Functions for ssh
|
1154
|
+
class ForjCoreProcess
|
1155
|
+
def ssh_connection(sObjectType, hParams)
|
1156
|
+
o_forge = hParams[:forge]
|
1157
|
+
o_server = nil
|
817
1158
|
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
1159
|
+
o_forge[:servers].each do|server|
|
1160
|
+
next if hParams[:forge_server] != server[:id]
|
1161
|
+
o_server = server
|
1162
|
+
break
|
1163
|
+
end
|
823
1164
|
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
end
|
1165
|
+
# Get server information
|
1166
|
+
PrcLib.state('Getting server information')
|
1167
|
+
o_server = process_get(:server, o_server[:id])
|
1168
|
+
register(o_server)
|
829
1169
|
|
830
|
-
|
831
|
-
public_key_file = File.join(hKeys[:keypair_path], hKeys[:public_key_name])
|
1170
|
+
public_ip = ssh_server_public_ip(o_server)
|
832
1171
|
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
PrcLib.warning("Openssh public key file '%s' not found. Unable to verify keys coherence with remote server." % public_key_file)
|
838
|
-
end
|
1172
|
+
ssh_options = ssh_keypair(o_server)
|
1173
|
+
# Get ssh user
|
1174
|
+
image = process_get(:image, o_server[:image_id])
|
1175
|
+
user = hParams[:ssh_user]
|
839
1176
|
|
840
|
-
|
841
|
-
ssh_options = { :keys => private_key_file}
|
842
|
-
PrcLib.debug("Using private key '%s'." % private_key_file)
|
843
|
-
else
|
844
|
-
PrcLib.fatal 1, <<-END
|
845
|
-
The server '#{oServer[:name]}' has been configured with a keypair '#{oServer[:key_name]}' which is not found locally.
|
846
|
-
You won't be able to connect to that server without '#{oServer[:key_name]}' private key.
|
847
|
-
To connect to this box, you need to provide the appropriate private key file with option -i
|
848
|
-
END
|
849
|
-
end
|
1177
|
+
user = image[:ssh_user] if user.nil?
|
850
1178
|
|
851
|
-
|
852
|
-
image = Get(:image, oServer[:image_id])
|
853
|
-
user = hParams[:ssh_user]
|
1179
|
+
PrcLib.debug("Using account '%s'.", user)
|
854
1180
|
|
855
|
-
|
856
|
-
|
1181
|
+
begin
|
1182
|
+
PrcLib.state("creating ssh connection with '%s' box", o_server[:name])
|
1183
|
+
session = Net::SSH.start(public_ip, user, ssh_options) do |_ssh|
|
1184
|
+
ssh_login(ssh_options, user, public_ip)
|
857
1185
|
end
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
PrcLib.state("creating ssh connection with '%s' box" % oServer[:name])
|
863
|
-
session = Net::SSH.start(public_ip, user, ssh_options) do |ssh|
|
864
|
-
ssh_login(ssh_options, user, public_ip)
|
865
|
-
end
|
866
|
-
PrcLib.debug("Error closing ssh connection, box %s " % oServer[:name]) if not session
|
867
|
-
rescue => e
|
868
|
-
PrcLib.fatal 1, <<-END
|
1186
|
+
PrcLib.debug('Error closing ssh connection, box %s ',
|
1187
|
+
o_server[:name]) unless session
|
1188
|
+
rescue => e
|
1189
|
+
PrcLib.fatal 1, <<-END
|
869
1190
|
#{e.message}
|
870
|
-
You were not able to connect to this box. Please note that there is no
|
1191
|
+
You were not able to connect to this box. Please note that there is no
|
1192
|
+
garantuee that your local private key file '#{ssh_options[:keys]}' is the
|
1193
|
+
one that was used while building this box.
|
871
1194
|
You have to check with the user who created that box.
|
872
1195
|
END
|
1196
|
+
end
|
1197
|
+
register({ :success => true }, sObjectType)
|
1198
|
+
end
|
1199
|
+
|
1200
|
+
def ssh_keypair(o_server)
|
1201
|
+
if config[:identity].nil? || !config[:identity].is_a?(String)
|
1202
|
+
h_keys = keypair_detect(
|
1203
|
+
o_server[:key_name],
|
1204
|
+
File.join(Forj.keypairs_path, o_server[:key_name])
|
1205
|
+
)
|
1206
|
+
else
|
1207
|
+
h_keys = keypair_detect(
|
1208
|
+
o_server[:key_name],
|
1209
|
+
File.expand_path(config[:identity])
|
1210
|
+
)
|
1211
|
+
end
|
1212
|
+
|
1213
|
+
private_key_file = File.join(
|
1214
|
+
h_keys[:keypair_path],
|
1215
|
+
h_keys[:private_key_name]
|
1216
|
+
)
|
1217
|
+
public_key_file = File.join(h_keys[:keypair_path], h_keys[:public_key_name])
|
1218
|
+
|
1219
|
+
PrcLib.info("Found openssh private key file '%s'.",
|
1220
|
+
private_key_file) if h_keys[:private_key_exist?]
|
1221
|
+
|
1222
|
+
if h_keys[:public_key_exist?]
|
1223
|
+
PrcLib.info("Found openssh public key file '%s'.", public_key_file)
|
1224
|
+
else
|
1225
|
+
PrcLib.warning("Openssh public key file '%s' not found. Unable to verify"\
|
1226
|
+
' keys coherence with remote server.', public_key_file)
|
1227
|
+
end
|
1228
|
+
ssh_options = ssh_options(h_keys, private_key_file, o_server)
|
1229
|
+
ssh_options
|
1230
|
+
end
|
1231
|
+
|
1232
|
+
def ssh_options(h_keys, private_key_file, o_server)
|
1233
|
+
if h_keys[:private_key_exist?]
|
1234
|
+
ssh_options = { :keys => private_key_file }
|
1235
|
+
PrcLib.debug("Using private key '%s'.", private_key_file)
|
1236
|
+
else
|
1237
|
+
PrcLib.fatal 1, <<-END
|
1238
|
+
The server '#{o_server[:name]}' has been configured with a keypair
|
1239
|
+
'#{o_server[:key_name]}' which is not found locally.
|
1240
|
+
You won't be able to connect to that server without
|
1241
|
+
'#{o_server[:key_name]}' private key.
|
1242
|
+
To connect to this box, you need to provide the appropriate private
|
1243
|
+
key file with option -i
|
1244
|
+
END
|
1245
|
+
end
|
1246
|
+
ssh_options
|
1247
|
+
end
|
1248
|
+
|
1249
|
+
def ssh_server_public_ip(o_server)
|
1250
|
+
# Get Public IP of the server. Needs the server to be loaded.
|
1251
|
+
o_address = process_query(:public_ip, :server_id => o_server[:id])
|
1252
|
+
|
1253
|
+
if o_address.length == 0
|
1254
|
+
PrcLib.fatal(1, 'ip address for %s server was not found', o_server[:name])
|
1255
|
+
else
|
1256
|
+
public_ip = o_address[0][:public_ip]
|
1257
|
+
end
|
1258
|
+
public_ip
|
1259
|
+
end
|
1260
|
+
end
|
1261
|
+
|
1262
|
+
# Functions for ssh
|
1263
|
+
class ForjCoreProcess
|
1264
|
+
def setup_ssh_user(_sCloudObj, hParams)
|
1265
|
+
images = process_query(:image, :name => hParams[:image_name])
|
1266
|
+
case images.length
|
1267
|
+
when 0
|
1268
|
+
s_default = hParams[:default_value]
|
1269
|
+
else
|
1270
|
+
if images[0, :ssh_user].nil?
|
1271
|
+
s_default = hParams[:default_value]
|
1272
|
+
else
|
1273
|
+
s_default = images[0, :ssh_user]
|
873
1274
|
end
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
def setup_ssh_user(sCloudObj, hParams)
|
878
|
-
images = Query(:image, { name: hParams[:image_name]} )
|
879
|
-
case images.length
|
880
|
-
when 0
|
881
|
-
sDefault = hParams[:default_value]
|
882
|
-
else
|
883
|
-
if images[0, :ssh_user].nil?
|
884
|
-
sDefault = hParams[:default_value]
|
885
|
-
else
|
886
|
-
sDefault = images[0, :ssh_user]
|
887
|
-
end
|
888
|
-
end
|
889
|
-
{ default_value: sDefault, list: config[:users] }
|
890
|
-
end
|
891
|
-
|
892
|
-
def ssh_login(options, user, public_ip)
|
893
|
-
sOpts = "-o StrictHostKeyChecking=no -o ServerAliveInterval=180"
|
894
|
-
sOpts += " -i %s" % options[:keys] if options[:keys]
|
895
|
-
|
896
|
-
command = 'ssh %s %s@%s' % [sOpts, user, public_ip]
|
897
|
-
PrcLib.debug("Running '%s'" % command)
|
898
|
-
system(command)
|
899
|
-
end
|
1275
|
+
end
|
1276
|
+
{ :default_value => s_default, :list => config[:users] }
|
1277
|
+
end
|
900
1278
|
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
return "ubuntu"
|
905
|
-
end
|
1279
|
+
def ssh_login(options, user, public_ip)
|
1280
|
+
s_opts = '-o StrictHostKeyChecking=no -o ServerAliveInterval=180'
|
1281
|
+
s_opts += format(' -i %s', options[:keys]) if options[:keys]
|
906
1282
|
|
1283
|
+
command = format('ssh %s %s@%s', s_opts, user, public_ip)
|
1284
|
+
PrcLib.debug("Running '%s'", command)
|
1285
|
+
system(command)
|
1286
|
+
end
|
1287
|
+
|
1288
|
+
def ssh_user(image_name)
|
1289
|
+
return 'fedora' if image_name =~ /fedora/i
|
1290
|
+
return 'centos' if image_name =~ /centos/i
|
1291
|
+
'ubuntu'
|
1292
|
+
end
|
907
1293
|
end
|