forj 0.0.43 → 0.0.44

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