|
36 | 36 | DEFAULT_DOMAIN = 'default' |
37 | 37 |
|
38 | 38 |
|
| 39 | +def prompt_for_password(prompt=None): |
| 40 | + """Prompt user for a password |
| 41 | +
|
| 42 | + Propmpt for a password if stdin is a tty. |
| 43 | + """ |
| 44 | + |
| 45 | + if not prompt: |
| 46 | + prompt = 'Password: ' |
| 47 | + pw = None |
| 48 | + # If stdin is a tty, try prompting for the password |
| 49 | + if hasattr(sys.stdin, 'isatty') and sys.stdin.isatty(): |
| 50 | + # Check for Ctl-D |
| 51 | + try: |
| 52 | + pw = getpass.getpass(prompt) |
| 53 | + except EOFError: |
| 54 | + pass |
| 55 | + # No password because we did't have a tty or nothing was entered |
| 56 | + if not pw: |
| 57 | + raise exc.CommandError( |
| 58 | + "No password entered, or found via --os-password or OS_PASSWORD", |
| 59 | + ) |
| 60 | + return pw |
| 61 | + |
| 62 | + |
39 | 63 | class OpenStackShell(app.App): |
40 | 64 |
|
41 | 65 | CONSOLE_MESSAGE_FORMAT = '%(levelname)s: %(name)s %(message)s' |
@@ -206,112 +230,6 @@ def build_option_parser(self, description, version): |
206 | 230 |
|
207 | 231 | return clientmanager.build_plugin_option_parser(parser) |
208 | 232 |
|
209 | | - def initialize_clientmanager(self): |
210 | | - """Validating authentication options and generate a clientmanager""" |
211 | | - |
212 | | - if self.client_manager: |
213 | | - self.log.debug('The clientmanager has been initialized already') |
214 | | - return |
215 | | - |
216 | | - self.log.debug("validating authentication options") |
217 | | - |
218 | | - # Assuming all auth plugins will be named in the same fashion, |
219 | | - # ie vXpluginName |
220 | | - if (not self.options.os_url and |
221 | | - self.options.os_auth_plugin.startswith('v') and |
222 | | - self.options.os_auth_plugin[1] != |
223 | | - self.options.os_identity_api_version[0]): |
224 | | - raise exc.CommandError( |
225 | | - "Auth plugin %s not compatible" |
226 | | - " with requested API version" % self.options.os_auth_plugin |
227 | | - ) |
228 | | - # TODO(mhu) All these checks should be exposed at the plugin level |
229 | | - # or just dropped altogether, as the client instantiation will fail |
230 | | - # anyway |
231 | | - if self.options.os_url and not self.options.os_token: |
232 | | - # service token needed |
233 | | - raise exc.CommandError( |
234 | | - "You must provide a service token via" |
235 | | - " either --os-token or env[OS_TOKEN]") |
236 | | - |
237 | | - if (self.options.os_auth_plugin.endswith('token') and |
238 | | - (self.options.os_token or self.options.os_auth_url)): |
239 | | - # Token flow auth takes priority |
240 | | - if not self.options.os_token: |
241 | | - raise exc.CommandError( |
242 | | - "You must provide a token via" |
243 | | - " either --os-token or env[OS_TOKEN]") |
244 | | - |
245 | | - if not self.options.os_auth_url: |
246 | | - raise exc.CommandError( |
247 | | - "You must provide a service URL via" |
248 | | - " either --os-auth-url or env[OS_AUTH_URL]") |
249 | | - |
250 | | - if (not self.options.os_url and |
251 | | - not self.options.os_auth_plugin.endswith('token')): |
252 | | - # Validate password flow auth |
253 | | - if not self.options.os_username: |
254 | | - raise exc.CommandError( |
255 | | - "You must provide a username via" |
256 | | - " either --os-username or env[OS_USERNAME]") |
257 | | - |
258 | | - if not self.options.os_password: |
259 | | - # No password, if we've got a tty, try prompting for it |
260 | | - if hasattr(sys.stdin, 'isatty') and sys.stdin.isatty(): |
261 | | - # Check for Ctl-D |
262 | | - try: |
263 | | - self.options.os_password = getpass.getpass() |
264 | | - except EOFError: |
265 | | - pass |
266 | | - # No password because we did't have a tty or the |
267 | | - # user Ctl-D when prompted? |
268 | | - if not self.options.os_password: |
269 | | - raise exc.CommandError( |
270 | | - "You must provide a password via" |
271 | | - " either --os-password, or env[OS_PASSWORD], " |
272 | | - " or prompted response") |
273 | | - |
274 | | - if not ((self.options.os_project_id |
275 | | - or self.options.os_project_name) or |
276 | | - (self.options.os_domain_id |
277 | | - or self.options.os_domain_name) or |
278 | | - self.options.os_trust_id): |
279 | | - if self.options.os_auth_plugin.endswith('password'): |
280 | | - raise exc.CommandError( |
281 | | - "You must provide authentication scope as a project " |
282 | | - "or a domain via --os-project-id " |
283 | | - "or env[OS_PROJECT_ID], " |
284 | | - "--os-project-name or env[OS_PROJECT_NAME], " |
285 | | - "--os-domain-id or env[OS_DOMAIN_ID], or" |
286 | | - "--os-domain-name or env[OS_DOMAIN_NAME], or " |
287 | | - "--os-trust-id or env[OS_TRUST_ID].") |
288 | | - |
289 | | - if not self.options.os_auth_url: |
290 | | - raise exc.CommandError( |
291 | | - "You must provide an auth url via" |
292 | | - " either --os-auth-url or via env[OS_AUTH_URL]") |
293 | | - |
294 | | - if (self.options.os_trust_id and |
295 | | - self.options.os_identity_api_version != '3'): |
296 | | - raise exc.CommandError( |
297 | | - "Trusts can only be used with Identity API v3") |
298 | | - |
299 | | - if (self.options.os_trust_id and |
300 | | - ((self.options.os_project_id |
301 | | - or self.options.os_project_name) or |
302 | | - (self.options.os_domain_id |
303 | | - or self.options.os_domain_name))): |
304 | | - raise exc.CommandError( |
305 | | - "Authentication cannot be scoped to multiple targets. " |
306 | | - "Pick one of project, domain or trust.") |
307 | | - |
308 | | - self.client_manager = clientmanager.ClientManager( |
309 | | - auth_options=self.options, |
310 | | - verify=self.verify, |
311 | | - api_version=self.api_version, |
312 | | - ) |
313 | | - return |
314 | | - |
315 | 233 | def initialize_app(self, argv): |
316 | 234 | """Global app init bits: |
317 | 235 |
|
@@ -368,19 +286,23 @@ def initialize_app(self, argv): |
368 | 286 | else: |
369 | 287 | self.verify = not self.options.insecure |
370 | 288 |
|
| 289 | + self.client_manager = clientmanager.ClientManager( |
| 290 | + auth_options=self.options, |
| 291 | + verify=self.verify, |
| 292 | + api_version=self.api_version, |
| 293 | + pw_func=prompt_for_password, |
| 294 | + ) |
| 295 | + |
371 | 296 | def prepare_to_run_command(self, cmd): |
372 | 297 | """Set up auth and API versions""" |
373 | 298 | self.log.debug('prepare_to_run_command %s', cmd.__class__.__name__) |
374 | 299 |
|
375 | | - if not cmd.auth_required: |
376 | | - return |
377 | | - if cmd.best_effort: |
| 300 | + if cmd.auth_required and cmd.best_effort: |
378 | 301 | try: |
379 | | - self.initialize_clientmanager() |
| 302 | + # Trigger the Identity client to initialize |
| 303 | + self.client_manager.auth_ref |
380 | 304 | except Exception: |
381 | 305 | pass |
382 | | - else: |
383 | | - self.initialize_clientmanager() |
384 | 306 | return |
385 | 307 |
|
386 | 308 | def clean_up(self, cmd, result, err): |
@@ -412,12 +334,6 @@ def clean_up(self, cmd, result, err): |
412 | 334 | targs = tparser.parse_args(['-f', format]) |
413 | 335 | tcmd.run(targs) |
414 | 336 |
|
415 | | - def interact(self): |
416 | | - # NOTE(dtroyer): Maintain the old behaviour for interactive use as |
417 | | - # this path does not call prepare_to_run_command() |
418 | | - self.initialize_clientmanager() |
419 | | - super(OpenStackShell, self).interact() |
420 | | - |
421 | 337 |
|
422 | 338 | def main(argv=sys.argv[1:]): |
423 | 339 | return OpenStackShell().run(argv) |
|
0 commit comments