diff --git a/novaclient/tests/idfake.pem b/novaclient/tests/idfake.pem new file mode 100644 index 000000000..f7b446641 --- /dev/null +++ b/novaclient/tests/idfake.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEA9QstF/7prDY7a9La7GS9TpMX+MWWXQgK6pHRLakDFp1WX1Q3 +Vly7rWitaZUGirUPMm181oJXBwkKlAxFD7hKjyHYaSswNszPYIAsVkc1+AO5epXz +g9kUBNtfg44Pg72UecwLrZ8JpmNZpJlKQOx6vF+yi7JmHrrIf6il/grIGUPzoT2L +yReimpyPoBrGtXhJYaCJ/XbKg1idRZiQdmwh1F/OmZWn9p0wunnsv08a0+qIywuw +WhG9/Zy9fjnEByfusS6gI0GIxDRL4RWzOqphd3PZzunwIBgEKFhgiki9+2DgcRVO +9I5wnDvfwQREJRZWh1uJa5ZTcfPa1EzZryVeOQIDAQABAoIBABxO3Te/cBk/7p9n +LXlPrfrszUEk+ljm+/PbQpIGy1+Kb5b1sKrebaP7ysS+vZG6lvXZZimVxx398mXm +APhu7tYYL9r+bUR3ZqGcTQLumRJ8w6mgtxANPN3Oxfr5p1stxIBJjTPSgpfhNFLq +joRvjUJDv+mZg2ibZVwyDHMLpdAdKp+3XMdyTLZcH9esqwii+natix7rHd1RuF85 +L1dfpxjkItwhgHsfdYS++5X3fRByFOhQ+Nhabh/kPQbQMcteRn1bN6zeCWBSglNb +Ka/ZrXb6ApRUc22Ji62mNO2ZPPekLJeCHk2h2E7ezYX+sGDNvvd/jHVDJJ20FjD1 +Z9KXuK0CgYEA/2vniy9yWd925QQtWbmrxgy6yj89feMH/LTv4qP298rGZ2nqxsyd +9pdBdb4NMsi4HmV5PG1hp3VRNBHl53DNh5eqzT8WEXnIF+sbrIU3KzrCVAx1kZTl ++OWKA6aVUsvvO3y85SOvInnsV+IsOGmU4/WBSjYoe39Bo7mq/YuZB9MCgYEA9ZlB +KBm6PjFdHQGNgedXahWzRcwC+ALCYqequPYqJolNzhrK4Uc2sWPSGdnldcHZ4XCQ +wbfCxUSwrMpA1oyuIQ0U4aowmOw5DjIueBWI8XBYEVRBlwvJwbXpBZ/DspGzTUDx +MBrrEwEaMadQvxhRnAzhp0rQAepatcz6Fgb1JkMCgYBMwDLiew5kfSav6JJsDMPW +DksurNQgeNEUmZYfx19V1EPMHWKj/CZXS9oqtEIpCXFyCNHmW4PlmvYcrGgmJJpN +7UAwzo0mES8UKNy2+Yy7W7u7H8dQSKrWILtZH3xtVcR8Xp4wSIm+1V40hkz9YpSP +71y7XQzLF1E1DnyYFZOVawKBgAFrmHfd5jjT2kD/sEzPBK9lXrsJmf7LLUqaw578 +NXQxmRSXDRNOcR+Hf0CNBQmwTE1EdGHaaTLw2cC2Drfu6lbgl31SmaNYwl+1pJUn +MrqKtseq4BI6jDkljypsKRqQQyQwOvTXQwLCH9+nowzn3Bj17hwkj51jOJESlWOp +OKO3AoGBALm+jjqyqX7gSnqK3FAumB8mlhv3yI1Wr1ctwe18mKfKbz17HxXRu9pF +K/6e7WMCA1p+jhoE8gj1h2WBcH0nV2qt8Ye8gJBbCi4dhI08o4AfrIV47oZx1RlO +qYcA1U9lyaODY5SL8+6PHOy5J/aYtuA+wvfEnWiCIdKQrhWetcn3 +-----END RSA PRIVATE KEY----- diff --git a/novaclient/tests/v1_1/fakes.py b/novaclient/tests/v1_1/fakes.py index 8a17c61cd..387ba1979 100644 --- a/novaclient/tests/v1_1/fakes.py +++ b/novaclient/tests/v1_1/fakes.py @@ -483,8 +483,27 @@ class FakeHTTPClient(base_client.HTTPClient): # Server password # + # Testing with the following password and key + # + # Clear password: FooBar123 + # + # RSA Private Key: novaclient/tests/idfake.pem + # + # Encrypted password + # OIuEuQttO8Rk93BcKlwHQsziDAnkAm/V6V8VPToA8ZeUaUBWwS0gwo2K6Y61Z96r + # qG447iRz0uTEEYq3RAYJk1mh3mMIRVl27t8MtIecR5ggVVbz1S9AwXJQypDKl0ho + # QFvhCBcMWPohyGewDJOhDbtuN1IoFI9G55ZvFwCm5y7m7B2aVcoLeIsJZE4PLsIw + # /y5a6Z3/AoJZYGG7IH5WN88UROU3B9JZGFB2qtPLQTOvDMZLUhoPRIJeHiVSlo1N + # tI2/++UsXVg3ow6ItqCJGgdNuGG5JB+bslDHWPxROpesEIHdczk46HCpHQN8f1sk + # Hi/fmZZNQQqj1Ijq0caOIw== def get_servers_1234_os_server_password(self, **kw): - return (200, {}, {'password': ''}) + return (200, {}, {'password': + 'OIuEuQttO8Rk93BcKlwHQsziDAnkAm/V6V8VPToA8ZeUaUBWwS0gwo2K6Y61Z96r' + 'qG447iRz0uTEEYq3RAYJk1mh3mMIRVl27t8MtIecR5ggVVbz1S9AwXJQypDKl0ho' + 'QFvhCBcMWPohyGewDJOhDbtuN1IoFI9G55ZvFwCm5y7m7B2aVcoLeIsJZE4PLsIw' + '/y5a6Z3/AoJZYGG7IH5WN88UROU3B9JZGFB2qtPLQTOvDMZLUhoPRIJeHiVSlo1N' + 'tI2/++UsXVg3ow6ItqCJGgdNuGG5JB+bslDHWPxROpesEIHdczk46HCpHQN8f1sk' + 'Hi/fmZZNQQqj1Ijq0caOIw=='}) def delete_servers_1234_os_server_password(self, **kw): return (202, {}, None) diff --git a/novaclient/tests/v1_1/test_servers.py b/novaclient/tests/v1_1/test_servers.py index a8b75d384..8c4c9ea70 100644 --- a/novaclient/tests/v1_1/test_servers.py +++ b/novaclient/tests/v1_1/test_servers.py @@ -420,9 +420,35 @@ class ServersTest(utils.TestCase): self.assertEqual(cs.servers.get_console_output(s, length=50), success) cs.assert_called('POST', '/servers/1234/action') + # Testing password methods with the following password and key + # + # Clear password: FooBar123 + # + # RSA Private Key: novaclient/tests/idfake.pem + # + # Encrypted password + # OIuEuQttO8Rk93BcKlwHQsziDAnkAm/V6V8VPToA8ZeUaUBWwS0gwo2K6Y61Z96r + # qG447iRz0uTEEYq3RAYJk1mh3mMIRVl27t8MtIecR5ggVVbz1S9AwXJQypDKl0ho + # QFvhCBcMWPohyGewDJOhDbtuN1IoFI9G55ZvFwCm5y7m7B2aVcoLeIsJZE4PLsIw + # /y5a6Z3/AoJZYGG7IH5WN88UROU3B9JZGFB2qtPLQTOvDMZLUhoPRIJeHiVSlo1N + # tI2/++UsXVg3ow6ItqCJGgdNuGG5JB+bslDHWPxROpesEIHdczk46HCpHQN8f1sk + # Hi/fmZZNQQqj1Ijq0caOIw== + def test_get_password(self): s = cs.servers.get(1234) - self.assertEqual(s.get_password('/foo/id_rsa'), '') + self.assertEqual(s.get_password('novaclient/tests/idfake.pem'), + b'FooBar123') + cs.assert_called('GET', '/servers/1234/os-server-password') + + def test_get_password_without_key(self): + s = cs.servers.get(1234) + self.assertEqual(s.get_password(), + 'OIuEuQttO8Rk93BcKlwHQsziDAnkAm/V6V8VPToA8ZeUaUBWwS0gwo2K6Y61Z96r' + 'qG447iRz0uTEEYq3RAYJk1mh3mMIRVl27t8MtIecR5ggVVbz1S9AwXJQypDKl0ho' + 'QFvhCBcMWPohyGewDJOhDbtuN1IoFI9G55ZvFwCm5y7m7B2aVcoLeIsJZE4PLsIw' + '/y5a6Z3/AoJZYGG7IH5WN88UROU3B9JZGFB2qtPLQTOvDMZLUhoPRIJeHiVSlo1N' + 'tI2/++UsXVg3ow6ItqCJGgdNuGG5JB+bslDHWPxROpesEIHdczk46HCpHQN8f1sk' + 'Hi/fmZZNQQqj1Ijq0caOIw==') cs.assert_called('GET', '/servers/1234/os-server-password') def test_clear_password(self): diff --git a/novaclient/tests/v1_1/test_shell.py b/novaclient/tests/v1_1/test_shell.py index bd2caf2a8..f1dad3e5c 100644 --- a/novaclient/tests/v1_1/test_shell.py +++ b/novaclient/tests/v1_1/test_shell.py @@ -1635,6 +1635,10 @@ class ShellTest(utils.TestCase): self.run_command('get-password sample-server /foo/id_rsa') self.assert_called('GET', '/servers/1234/os-server-password') + def test_get_password_without_key(self): + self.run_command('get-password sample-server') + self.assert_called('GET', '/servers/1234/os-server-password') + def test_clear_password(self): self.run_command('clear-password sample-server') self.assert_called('DELETE', '/servers/1234/os-server-password') diff --git a/novaclient/v1_1/servers.py b/novaclient/v1_1/servers.py index a64e955b9..ef73d8669 100644 --- a/novaclient/v1_1/servers.py +++ b/novaclient/v1_1/servers.py @@ -74,11 +74,15 @@ class Server(base.Resource): """ return self.manager.get_spice_console(self, console_type) - def get_password(self, private_key): + def get_password(self, private_key=None): """ Get password for a Server. + Returns the clear password of an instance if private_key is + provided, returns the ciphered password otherwise. + :param private_key: Path to private key file for decryption + (optional) """ return self.manager.get_password(self, private_key) @@ -497,24 +501,29 @@ class ServerManager(base.BootingManagerWithFind): return self._action('os-getSPICEConsole', server, {'type': console_type})[1] - def get_password(self, server, private_key): + def get_password(self, server, private_key=None): """ Get password for an instance + Returns the clear password of an instance if private_key is + provided, returns the ciphered password otherwise. + Requires that openssl is installed and in the path :param server: The :class:`Server` (or its ID) to add an IP to. :param private_key: The private key to decrypt password + (optional) """ _resp, body = self.api.client.get("/servers/%s/os-server-password" % base.getid(server)) - if body and body.get('password'): + ciphered_pw = body.get('password', '') if body else '' + if private_key and ciphered_pw: try: - return crypto.decrypt_password(private_key, body['password']) + return crypto.decrypt_password(private_key, ciphered_pw) except Exception as exc: - return '%sFailed to decrypt:\n%s' % (exc, body['password']) - return '' + return '%sFailed to decrypt:\n%s' % (exc, ciphered_pw) + return ciphered_pw def clear_password(self, server): """ diff --git a/novaclient/v1_1/shell.py b/novaclient/v1_1/shell.py index 6edbe381d..3b4449c4f 100644 --- a/novaclient/v1_1/shell.py +++ b/novaclient/v1_1/shell.py @@ -1867,7 +1867,12 @@ def do_get_spice_console(cs, args): @utils.arg('server', metavar='', help='Name or ID of server.') @utils.arg('private_key', metavar='', - help='Private key (used locally to decrypt password).') + help='Private key (used locally to decrypt password) (Optional). ' + 'When specified, the command displays the clear (decrypted) VM ' + 'password. When not specified, the ciphered VM password is ' + 'displayed.', + nargs='?', + default=None) def do_get_password(cs, args): """Get password for a server.""" server = _find_server(cs, args.server) diff --git a/novaclient/v3/shell.py b/novaclient/v3/shell.py index 3044e6d9f..66b425bbf 100644 --- a/novaclient/v3/shell.py +++ b/novaclient/v3/shell.py @@ -1701,7 +1701,12 @@ def do_get_spice_console(cs, args): @utils.arg('server', metavar='', help='Name or ID of server.') @utils.arg('private_key', metavar='', - help='Private key (used locally to decrypt password).') + help='Private key (used locally to decrypt password) (Optional). ' + 'When specified, the command displays the clear (decrypted) VM ' + 'password. When not specified, the ciphered VM password is ' + 'displayed.', + nargs='?', + default=None) def do_get_password(cs, args): """Get password for a server.""" server = _find_server(cs, args.server)