forj 0.0.43 → 0.0.44

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/defaults.yaml CHANGED
@@ -41,6 +41,8 @@
41
41
  :build_config: box
42
42
  :branch: master
43
43
  :box_name: maestro
44
+
45
+ :provider_name: hpcloud
44
46
  :description:
45
47
  # Description of build.sh environment variable defined by forj cli for build.sh. (~/.forj/infra/build/<Account>.build.env)
46
48
  :FORJ_HPC: "HPCloud cli Account used to build your Maestro box"
@@ -57,64 +59,116 @@
57
59
  :FORJ_DNS_ZONE: "HPCloud Domain name service to use for each boxes DNS entries. (Ex: region-a.geo-1)"
58
60
  :FORJ_DNS_DOMAIN: "Domain used for DNS. Each server will be attached to a public IP. An 'A' record in the DNS service will need to be added to your HPCloud DOMAIN."
59
61
 
60
- # Use by Object ForjAccount, function get, to identify the section name in ~/.forj/accounts/YourAccount. We can set those variables with 'forj set key=value [-a account]'
61
- :account_section_mapping:
62
- :security_group:
63
- :section: :maestro
64
- :desc: "Security group name to configure and attach to each forge boxes."
65
-
66
- :image:
67
- :section: :maestro
68
- :desc: "Image used to create Maestro and all forge boxes. By default, it is 'Ubuntu Precise 12.04.4 LTS Server 64-bit 20140414 (Rescue Image)'"
69
-
70
- :flavor:
71
- :section: :maestro
72
- :desc: "Maestro Flavor name. This flavor is for Maestro only. Your blueprint layout defines each node flavors on needs."
73
-
74
- :bp_flavor:
75
- :section: :maestro
76
- :desc: "Blueprint nodes default flavor. Usually, blueprint node are smaller than Maestro."
77
-
78
- :build_config:
79
- :section: :maestro
80
- :desc: "forj cli use 'build.sh' to create Maestro. See build_config option on build.sh to get more information. By default 'box'"
81
-
82
- :box_name:
83
- :section: :maestro
84
- :desc: "forj cli use 'build.sh' to create Maestro. See box_name option on build.sh to get more information. By default 'maestro'"
85
-
86
- :keypair_name:
87
- :section: :credentials
88
- :desc: "keypair name defined in your cloud to access your server. By default we named it 'forj'. If it doesn't exist, it will be created."
89
-
90
- :keypair_path:
91
- :section: :credentials
92
- :desc: "public key file to send to the cloud under keypair name, and private key to keep on your local forj environment to access your boxes."
93
-
94
- :tenant_name:
95
- :section: :compute
96
- :desc: "Tenant name required by fog/openstack on gardener"
97
-
98
- :domain_name:
99
- :section: :dns
100
- :desc: "Domain name added to each hosts."
101
-
102
- :tenant_id:
103
- :section: :dns
104
- :desc: "DNS Tenant ID Maestro will use"
105
-
106
- :service:
107
- :section: :dns
108
- :desc: "DNS service region name Maestro will use."
109
-
110
- :network:
111
- :section: :network
112
- :desc: "Network name to attach to each forge boxes. By default we use 'private'. If it doesn't exist, it will be created."
113
-
114
- :infra_repo:
115
- :section: :maestro
116
- :desc: "Defines your Infra directory to use while booting."
117
-
118
- :maestro_repo:
119
- :section: :maestro
120
- :desc: "To use a different Maestro repository already cloned."
62
+ # Following declares generic FORJ data to handle Fog object (compute/network/dns/...)
63
+ # It defines the account file structure. section/key=value
64
+ # All data can be predefined by default value (config.yaml/defaults.yaml) except
65
+ # those identified by :account_exclusive: true
66
+ :sections:
67
+ # This section define updatable data available from config.yaml. But will never be added in an account file.
68
+ # Used by forj set/get functions
69
+ :default:
70
+ :account_name:
71
+ :desc: "Default account name used by forj cli"
72
+ :provider_name:
73
+ :desc: "Default provider name while running forj setup. By default, hpcloud is selected."
74
+ # Defines account credentials data
75
+ :account:
76
+ :name:
77
+ :desc: "Name of the Forj cli account. use forj account rename <oldName> <NewName> to update it."
78
+ :readonly: true
79
+ :account_exclusive: true
80
+ :provider:
81
+ :desc: "Provider name attached to the forj cli account. To update it, use forj setup."
82
+ :readonly: true
83
+ :account_exclusive: true
84
+ :default: :provider_name
85
+
86
+ # Defines services
87
+ :services:
88
+ :compute:
89
+ :desc: "Generic service identification for compute"
90
+ :account_exclusive: true
91
+ :account: true
92
+ :network:
93
+ :desc: "Generic service identification for network"
94
+ :account_exclusive: true
95
+ :account: true
96
+
97
+ # Defines ssh keys credentials
98
+ :credentials:
99
+ :keypair_path:
100
+ :desc: "public key file to send to the cloud under keypair name, and private key to keep on your local forj environment to access your boxes."
101
+ :keypair_name:
102
+ :desc: "keypair name defined in your cloud to access your server. By default we named it 'forj'. If it doesn't exist, it will be created."
103
+ :auth_uri:
104
+ :desc: "Generic service auth url"
105
+ :account_exclusive: true
106
+ :account: true
107
+ :required: true
108
+ :ask_sort: 0
109
+ :account_id:
110
+ :desc: "Generic Cloud Account name."
111
+ :account_exclusive: true
112
+ :account: true
113
+ :required: true
114
+ :account_key:
115
+ :desc: "Generic cloud account key"
116
+ :account_exclusive: true
117
+ :account: true
118
+ :required: true
119
+ :tenant:
120
+ :desc: "Generic Tenant identification"
121
+ :account_exclusive: true
122
+ :account: true
123
+ :required: true
124
+ :os_user:
125
+ :desc: "User name required by Maestro to access the cloud compute via openstack. Will be obsoleted soon."
126
+ :account_exclusive: true
127
+ :account: true
128
+ :required: true
129
+ :validate: !ruby/regexp /\w+/
130
+ :os_enckey:
131
+ :desc: "Encrypted password required by Maestro to access the cloud compute via openstack. Will be obsoleted soon."
132
+ :account_exclusive: true
133
+ :encrypted: true
134
+ :account: true
135
+ :required: true
136
+
137
+ # Defines DNS services for maestro
138
+ :dns:
139
+ :service:
140
+ :desc: "DNS service region name Maestro will use."
141
+ :account_exclusive: true
142
+ :tenant_id:
143
+ :desc: "DNS Tenant ID Maestro will use"
144
+ :account_exclusive: true
145
+ :domain_name:
146
+ :desc: "Domain name added to each hosts."
147
+ :account_exclusive: true
148
+
149
+ :maestro:
150
+ :tenant_name:
151
+ :desc: "Tenant name required by fog/openstack on gardener"
152
+ :network_name:
153
+ :desc: "Network name to attach to each forge boxes. By default we use 'forj'. If it doesn't exist, it will be created."
154
+ :default: network
155
+ :security_group:
156
+ :desc: "Security group name to configure and attach to each forge boxes."
157
+ :maestro_repo:
158
+ :desc: "To use a different Maestro repository already cloned."
159
+ :infra_repo:
160
+ :desc: "Defines your Infra directory to use while booting."
161
+ :box_name:
162
+ :desc: "forj cli use 'build.sh' to create Maestro. See box_name option on build.sh to get more information. By default 'maestro'"
163
+ :build_config:
164
+ :desc: "forj cli use 'build.sh' to create Maestro. See build_config option on build.sh to get more information. By default 'box'"
165
+ :bp_flavor:
166
+ :desc: "Blueprint nodes default flavor. Usually, blueprint node are smaller than Maestro."
167
+ :flavor:
168
+ :desc: "Maestro Flavor name. This flavor is for Maestro only. Your blueprint layout defines each node flavors on needs."
169
+ :image:
170
+ :desc: "Image used to create Maestro and all forge boxes. By default, it is 'Ubuntu Precise 12.04.4 LTS Server 64-bit 20140414 (Rescue Image)'"
171
+ :ports:
172
+ :desc: "List of security group rules (1 port or range of ports) to open to the external network."
173
+ :branch:
174
+ :desc: "Branch to use to build your forge"
data/lib/forj-account.rb CHANGED
@@ -45,10 +45,38 @@ class ForjAccounts
45
45
  end
46
46
  end
47
47
 
48
+ # ForjAccount manage a list of key/value grouped by section.
49
+ # The intent of ForjAccount is to attach some keys/values to
50
+ # an account to help end users to switch between each of them.
51
+ #
52
+ # ForjAccount based on ForjConfig (see forj-config.rb)
53
+ # ensure ForjConfig and ForjAccount defines following common functions
54
+ # - set (key, value)
55
+ # - get (key)
56
+ #
57
+ # This means that key HAVE to be unique across sections
58
+ # By default, keys maps with the same key name in ForjConfig.
59
+ # But we can redefine the ForjConfig mapping of any key on need.
60
+ #
61
+ # ForjConfig, loads Account meta structure from defaults.yaml, sections
62
+ #
63
+ # defaults.yaml structure is:
64
+ # sections:
65
+ # default: => defines key/values recognized by ForjAccount to be only managed by ForjConfig.
66
+ # <key> :
67
+ # :desc : <value> => defines the ForjConfig key description.
68
+ # <section>: Define a section name. For each keys on this section, the account file will kept those data under this section.
69
+ # <key>:
70
+ # :desc: defines the key description.
71
+ # :readonly: true if this key cannot be updated by ForjAccount.set
72
+ # :account_exclusive: true if this key cannot be predefined on ForjConfig keys list
73
+ # :default: <ForjConfig real key name> Used to map the ForjAccount key to a different ForjConfig key name.
74
+
48
75
  class ForjAccount
49
76
 
50
77
  attr_reader :sAccountName
51
78
  attr_reader :hAccountData
79
+ attr_reader :oConfig
52
80
 
53
81
  # This object manage data located in oConfig[:hpc_accounts/AccountName]
54
82
 
@@ -67,53 +95,148 @@ class ForjAccount
67
95
  sProvider = @oConfig.get(:provider) if @oConfig.get(:provider)
68
96
 
69
97
  @hAccountData = {}
70
- rhSet(@hAccountData, @sAccountName, [:account, :name]) if rhExist?(@hAccountData, [:account, :name]) != 2
71
- rhSet(@hAccountData, sProvider, [:account, :provider]) if rhExist?(@hAccountData, [:account, :provider]) != 2
98
+ _set(:account, :name, @sAccountName) if exist?(:name) != 'hash'
99
+ _set(:account, :provider, sProvider) if exist?(:provider) != 'hash'
100
+
72
101
  end
73
102
 
74
- # oForjAccount data get are retrieved from the account file under section described in defaults.yaml (:account_section_mapping), as soon as this mapping exists.
75
- # If not found, get the data from the local configuration file. Usually ~/.forj/config.yaml
76
- # If not found, get the data from defaults.yaml
103
+ # oForjAccount data get at several levels:
104
+ # - get the data from runtime (runtimeSet/runtimeGet)
105
+ # - otherwise, get data from account file under section described in defaults.yaml (:account_section_mapping), as soon as this mapping exists.
106
+ # - otherwise, get the data from the local configuration file. Usually ~/.forj/config.yaml
107
+ # - otherwise, get the data from defaults.yaml
77
108
  # otherwise, use the get default parameter as value. Default is nil.
78
109
  def get(key, default = nil)
79
110
  return nil if not key
80
111
 
81
112
  key = key.to_sym if key.class == String
82
- section = rhGet(@oConfig.getAppDefault(:account_section_mapping, key), :section)
83
- yInterm = nil
84
- if section
85
- yInterm = rhGet(@hAccountData, section)
86
- else
113
+
114
+ return @oConfig.runtimeGet(key) if @oConfig.runtimeExist?(key)
115
+
116
+ section = ForjDefault.get_meta_section(key)
117
+ default_key = key
118
+
119
+ if not section
87
120
  Logging.debug("ForjAccount.get: No section found for key '%s'." % [key])
121
+ else
122
+ return rhGet(@hAccountData, section, key) if rhExist?(@hAccountData, section, key) == 2
123
+
124
+ hMeta = @oConfig.getAppDefault(:sections)
125
+ if rhExist?(hMeta, section, key, :default) == 3
126
+ default_key = rhGet(hMeta, section, key, :default)
127
+ Logging.debug("ForjAccount.get: Reading default key '%s' instead of '%s'" % [default_key, key])
128
+ end
129
+ return default if rhExist?(hMeta, section, key, :account_exclusive) == 3
88
130
  end
89
- @oConfig.get(key, yInterm , default )
131
+
132
+ @oConfig.get(default_key , default )
90
133
  end
91
134
 
92
135
  def exist?(key)
93
136
  return nil if not key
94
137
 
95
138
  key = key.to_sym if key.class == String
96
- section = rhGet(@oConfig.getAppDefault(:account_section_mapping, key), :section)
97
- yInterm = nil
98
- yInterm = rhGet(@hAccountData, section) if section
99
- @oConfig.exist?(key, yInterm)
139
+ section = ForjDefault.get_meta_section(key)
140
+ if not section
141
+ Logging.debug("ForjAccount.exist?: No section found for key '%s'." % [key])
142
+ return nil
143
+ end
144
+ return @sAccountName if rhExist?(@hAccountData, section, key) == 2
145
+
146
+ hMeta = @oConfig.getAppDefault(:sections)
147
+ if rhExist?(hMeta, section, key, :default) == 3
148
+ default_key = rhGet(hMeta, section, key, :default)
149
+ Logging.debug("ForjAccount.exist?: Reading default key '%s' instead of '%s'" % [default_key, key])
150
+ else
151
+ default_key = key
152
+ end
153
+ return nil if rhExist?(hMeta, section, key, :account_exclusive) == 3
154
+
155
+ @oConfig.exist?(default_key)
156
+
157
+ end
158
+
159
+ # Return true if readonly. set won't be able to update this value.
160
+ # Only _set (private function) is able.
161
+ def readonly?(key)
162
+ return nil if not key
163
+
164
+ key = key.to_sym if key.class == String
165
+ section = ForjDefault.get_meta_section(key)
166
+
167
+ rhGet(@oConfig.getAppDefault(:sections, section), key, :readonly)
168
+
169
+ end
170
+
171
+ def meta_set(key, hMeta)
172
+ key = key.to_sym if key.class == String
173
+ section = ForjDefault.get_meta_section(key)
174
+ hCurMeta = rhGet(@oConfig.getAppDefault(:sections, section), key)
175
+ hMeta.each { | mykey, myvalue |
176
+ rhSet(hCurMeta, myvalue, mykey)
177
+ }
178
+ end
179
+
180
+ def meta_exist?(key)
181
+ return nil if not key
182
+
183
+ key = key.to_sym if key.class == String
184
+ section = ForjDefault.get_meta_section(key)
185
+ rhExist?(@oConfig.getAppDefault(:sections, section), key) == 1
186
+ end
187
+
188
+ def get_meta_section(key)
189
+ key = key.to_sym if key.class == String
190
+ rhGet(@account_section_mapping, key)
191
+ end
192
+
193
+ def meta_type?(key)
194
+ return nil if not key
195
+
196
+ section = ForjDefault.get_meta_section(key)
197
+
198
+ return section if section == :default
199
+ @sAccountName
200
+ end
201
+
202
+ # Loop on account metadata
203
+ def metadata_each
204
+ rhGet(ForjDefault.dump(), :sections).each { | section, hValue |
205
+ next if section == :default
206
+ hValue.each { | key, value |
207
+ yield section, key, value
208
+ }
209
+ }
210
+ end
211
+
212
+ # Return true if exclusive
213
+ def exclusive?(key)
214
+ return nil if not key
215
+
216
+ key = key.to_sym if key.class == String
217
+ section = ForjDefault.get_meta_section(key)
100
218
 
219
+ rhGet(@oConfig.getAppDefault(:sections, section), key, :account_exclusive)
101
220
  end
102
221
 
222
+ # This function update a section/key=value if the account structure is defined.
223
+ # If no section is defined, set it in runtime config.
103
224
  def set(key, value)
104
225
  return nil if not key
105
226
 
106
227
  key = key.to_sym if key.class == String
107
- section = rhGet(@oConfig.getAppDefault(:account_section_mapping, key), :section)
108
- return nil if not section
109
- rhSet(@hAccountData, value, section, key)
228
+ section = ForjDefault.get_meta_section(key)
229
+
230
+ return @oConfig.set(key, value) if not section
231
+ return nil if readonly?(key)
232
+ _set(section, key, value)
110
233
  end
111
234
 
112
235
  def del(key)
113
236
  return nil if not key
114
237
 
115
238
  key = key.to_sym if key.class == String
116
- section = rhGet(@oConfig.getAppDefault(:account_section_mapping, key), :section)
239
+ section = ForjDefault.get_meta_section(key)
117
240
  return nil if not section
118
241
  rhSet(@hAccountData, nil, section, key)
119
242
  end
@@ -123,23 +246,30 @@ class ForjAccount
123
246
  default
124
247
  end
125
248
 
126
- def ac_load(sAccountName = @sAccountName)
249
+ def ac_new(sAccountName)
250
+ return nil if sAccountName.nil?
251
+ @sAccountName = sAccountName
252
+ @sAccountFile = File.join($FORJ_ACCOUNTS_PATH, @sAccountName)
253
+
254
+ @hAccountData = {:account => {:name => sAccountName, :provider => @oConfig.get(:provider_name)}}
255
+ end
256
+
257
+ def ac_load(sAccountName = @sAccountName, bHPCloudLoad = true)
127
258
  # Load Account Information
128
259
 
129
260
  if sAccountName != @sAccountName
130
- @sAccountName = sAccountName
131
- @sAccountFile = File.join($FORJ_ACCOUNTS_PATH, @sAccountName)
261
+ ac_new(sAccountName)
132
262
  end
133
263
 
134
264
  if File.exists?(@sAccountFile)
135
265
  @hAccountData = @oConfig.ExtraLoad(@sAccountFile, :forj_accounts, @sAccountName)
136
266
  # Check if hAccountData are using symbol or needs to be updated.
137
- sProvider = @oConfig.get(:provider, nil, 'hpcloud')
267
+ sProvider = @oConfig.get(:provider, 'hpcloud')
138
268
  rhSet(@hAccountData, @sAccountName, :account, :name) if rhExist?(@hAccountData, :account, :name) != 2
139
269
  rhSet(@hAccountData, sProvider, :account, :provider) if rhExist?(@hAccountData, :account, :provider) != 2
140
- provider_load()
270
+ provider_load() if bHPCloudLoad
141
271
  if rhKeyToSymbol?(@hAccountData, 2)
142
- @hAccountData = rhKeyToSymbol(@hAccountData, 2)
272
+ @hAccountData = rhKeyToSymbol(@hAccountData, 2)
143
273
  self.ac_save()
144
274
  end
145
275
  return @hAccountData
@@ -178,7 +308,7 @@ class ForjAccount
178
308
 
179
309
  # Checking cloud connection
180
310
  Logging.message("Checking cloud connection")
181
- ForjConnection.new(@oConfig)
311
+ ForjConnection.new(self)
182
312
 
183
313
  Logging.message("Setup '%s' done. Thank you." % @sAccountName)
184
314
  end
@@ -236,7 +366,7 @@ class ForjAccount
236
366
  if tenant_name
237
367
  Logging.debug("Tenant ID '%s': '%s' found." % [tenant_id, tenant_name])
238
368
  hCompute = { :tenant_name => tenant_name }
239
- rhSet(@hAccountData, hCompute, :compute)
369
+ rhSet(@hAccountData, hCompute, :maestro)
240
370
  else
241
371
  Logging.error("Unable to find the tenant Name for '%s' ID." % tenant_id)
242
372
  end
@@ -334,12 +464,12 @@ class ForjAccount
334
464
  q.validate = /.*+/
335
465
  end
336
466
  keys_entered = keypair_detect(key_name, key_path)
337
- if not keys_entered[:private_key_exist?] and not keys_entered[:public_key_exist?]
467
+ if not keys_entered[:private_key_exist? ] and not keys_entered[:public_key_exist? ]
338
468
  if agree("The key you entered was not found. Do you want to create this one?")
339
469
  base_dir = keys_entered[:keypair_path]
340
470
  if not File.directory?(base_dir)
341
471
  if agree("'%s' doesn't exist. Do you want to create it?" % base_dir)
342
- Helpers.ensure_dir_exists(base_dir)
472
+ AppInit.ensure_dir_exists(base_dir)
343
473
  end
344
474
  end
345
475
  else
@@ -366,7 +496,7 @@ class ForjAccount
366
496
 
367
497
 
368
498
  # Creation sequences
369
- if not keys[:private_key_exist?]
499
+ if not keys[:private_key_exist? ]
370
500
  # Need to create a key. ask if we need so.
371
501
  Logging.message("The private key file attached to keypair named '%s' is not found. forj will propose to create one for you. Please review the proposed private key file name and path.\nYou can press Enter to accept the default value." % keys[:keypair_name])
372
502
  real_key_path = File.expand_path(ask("Private key file path:") do |q|
@@ -374,7 +504,7 @@ class ForjAccount
374
504
  q.default = private_key_file
375
505
  end)
376
506
  if not File.exists?(real_key_path)
377
- Helpers.ensure_dir_exists(File.dirname(real_key_path))
507
+ AppInit.ensure_dir_exists(File.dirname(real_key_path))
378
508
  command = 'ssh-keygen -t rsa -f %s' % real_key_path
379
509
  Logging.debug("Executing '%s'" % command)
380
510
  system(command)
@@ -390,7 +520,7 @@ class ForjAccount
390
520
  end
391
521
  end
392
522
 
393
- if not keys[:public_key_exist?]
523
+ if not keys[:public_key_exist? ]
394
524
  Logging.message("Your public key '%s' was not found. Getting it from the private one. It may require your passphrase." % [public_key_file])
395
525
  command = 'ssh-keygen -y -f %s > %s' % [private_key_file,public_key_file ]
396
526
  Logging.debug("Executing '%s'" % command)
@@ -522,24 +652,12 @@ class ForjAccount
522
652
  }
523
653
  Logging.info("'%s' written." % cloud_fog)
524
654
  end
525
- end
655
+ # private functions
656
+ private
657
+ def _set(section, key, value)
658
+ return nil if not key or not section
526
659
 
527
- def ensure_forj_dirs_exists()
528
- # Function to create FORJ paths if missing.
529
-
530
- # Defining Global variables
531
- $FORJ_DATA_PATH = File.expand_path(File.join('~', '.forj'))
532
- $FORJ_ACCOUNTS_PATH = File.join($FORJ_DATA_PATH, 'accounts')
533
- $FORJ_KEYPAIRS_PATH = File.join($FORJ_DATA_PATH, 'keypairs')
534
- $FORJ_CREDS_PATH = File.expand_path(File.join('~', '.cache', 'forj'))
535
-
536
- # TODO: To move to an hpcloud object.
537
- $HPC_KEYPAIRS = File.expand_path(File.join('~', '.hpcloud', 'keypairs'))
538
- $HPC_ACCOUNTS = File.expand_path(File.join('~', '.hpcloud', 'accounts'))
660
+ rhSet(@hAccountData, value, section, key)
661
+ end
539
662
 
540
- Helpers.ensure_dir_exists($FORJ_DATA_PATH)
541
- Helpers.ensure_dir_exists($FORJ_ACCOUNTS_PATH)
542
- Helpers.ensure_dir_exists($FORJ_KEYPAIRS_PATH)
543
- FileUtils.chmod(0700, $FORJ_KEYPAIRS_PATH)
544
- Helpers.ensure_dir_exists($FORJ_CREDS_PATH)
545
663
  end