add ability to cli/api to add/clear ssh keys in password file
- add new password setkey cli command - add new api password_set_sshkey - add ability to param checker to not display bad param - add new change_password util for kolla_actions to call - update kolla_actions to handle ssh keys - add utest for setting/clearing ssh keys Change-Id: I1fedb85d21cd04c222f7250bdda66ad42a9ddca3 Jira-Issue: OPENSTACK-1071
This commit is contained in:
parent
8858b0fd16
commit
3f029a7bd4
|
@ -16,6 +16,7 @@ import kollacli.i18n as u
|
|||
from kollacli.common.passwords import clear_password
|
||||
from kollacli.common.passwords import get_password_names
|
||||
from kollacli.common.passwords import set_password
|
||||
from kollacli.common.passwords import set_password_sshkey
|
||||
from kollacli.common.utils import check_arg
|
||||
|
||||
|
||||
|
@ -31,8 +32,25 @@ class PasswordApi(object):
|
|||
:type value: string
|
||||
"""
|
||||
check_arg(name, u._('Password name'), str)
|
||||
check_arg(value, u._('Password value'), str, display_param=False)
|
||||
set_password(name, value)
|
||||
|
||||
def password_set_sshkey(self, name, private_key, public_key):
|
||||
# type: (str, str, str) -> None
|
||||
"""Set password to an ssh key
|
||||
|
||||
:param name: name of the password
|
||||
:type name: string
|
||||
:param private_key: ssh private key
|
||||
:type value: string
|
||||
:param public_key: ssh public key
|
||||
:type value: string
|
||||
"""
|
||||
check_arg(name, u._('Password name'), str)
|
||||
check_arg(private_key, u._('Private key'), str, display_param=False)
|
||||
check_arg(public_key, u._('Public key'), str, display_param=False)
|
||||
set_password_sshkey(name, private_key, public_key)
|
||||
|
||||
def password_clear(self, name):
|
||||
# type: (str) -> None
|
||||
"""Clear password
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
# under the License.
|
||||
import argparse
|
||||
import getpass
|
||||
import os
|
||||
import traceback
|
||||
|
||||
import kollacli.i18n as u
|
||||
|
@ -54,6 +55,45 @@ class PasswordSet(Command):
|
|||
raise Exception(traceback.format_exc())
|
||||
|
||||
|
||||
class PasswordSetKey(Command):
|
||||
"Password Set SSH Key"
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(PasswordSetKey, self).get_parser(prog_name)
|
||||
parser.add_argument('passwordname', metavar='<passwordname>',
|
||||
help=u._('Password name'))
|
||||
parser.add_argument('privatekeypath', metavar='<privatekeypath>',
|
||||
help=u._('Path to private key file'))
|
||||
parser.add_argument('publickeypath', metavar='<publickeypath>',
|
||||
help=u._('Path to public key file'))
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
try:
|
||||
password_name = parsed_args.passwordname.strip()
|
||||
private_keypath = parsed_args.privatekeypath.strip()
|
||||
private_keypath = os.path.abspath(private_keypath)
|
||||
public_keypath = parsed_args.publickeypath.strip()
|
||||
public_keypath = os.path.abspath(public_keypath)
|
||||
|
||||
if not os.path.isfile(private_keypath):
|
||||
raise(CommandError(u._('Private key file not found: {path}')
|
||||
.format(path=private_keypath)))
|
||||
if not os.path.isfile(public_keypath):
|
||||
raise(CommandError(u._('Public key file not found: {path}')
|
||||
.format(path=public_keypath)))
|
||||
|
||||
with open(private_keypath, 'r') as f:
|
||||
private_key = f.read()
|
||||
with open(public_keypath, 'r') as f:
|
||||
public_key = f.read()
|
||||
CLIENT.password_set_sshkey(password_name, private_key.strip(),
|
||||
public_key.strip())
|
||||
|
||||
except Exception:
|
||||
raise Exception(traceback.format_exc())
|
||||
|
||||
|
||||
class PasswordClear(Command):
|
||||
"Password Clear"
|
||||
|
||||
|
|
|
@ -35,6 +35,16 @@ def set_password(pwd_key, pwd_value):
|
|||
.format(error=err_msg, message=output))
|
||||
|
||||
|
||||
def set_password_sshkey(pwd_key, private_key, public_key):
|
||||
cmd = '%s -k %s -r "%s" -u "%s"' % (_get_cmd_prefix(), pwd_key,
|
||||
private_key, public_key)
|
||||
err_msg, output = utils.run_cmd(cmd, print_output=False)
|
||||
if err_msg:
|
||||
raise FailedOperation(
|
||||
u._('Password ssh key set failed. {error} {message}')
|
||||
.format(error=err_msg, message=output))
|
||||
|
||||
|
||||
def clear_password(pwd_key):
|
||||
"""clear a password
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import six
|
|||
import subprocess # nosec
|
||||
import sys
|
||||
import time
|
||||
import yaml
|
||||
|
||||
import kollacli.i18n as u
|
||||
|
||||
|
@ -196,6 +197,42 @@ def run_cmd(cmd, print_output=True):
|
|||
return err, output
|
||||
|
||||
|
||||
def change_password(file_path, pname, pvalue=None, public_key=None,
|
||||
private_key=None, clear=False):
|
||||
"""change password in passwords.yml file
|
||||
|
||||
file_path: path to passwords file
|
||||
pname: name of password
|
||||
pvalue: value of password when not ssh key
|
||||
public_key: public ssh key
|
||||
private_key: private ssh key
|
||||
clear: flag to remove password
|
||||
|
||||
If clear, and password exists, remove it from the password file.
|
||||
If clear, and password doesn't exists, nothing is done.
|
||||
If not clear, and key is not found, the new password will be added.
|
||||
If not clear, and key is found, edit password in place.
|
||||
|
||||
The passwords file contains both key-value pairs and key-dictionary
|
||||
pairs.
|
||||
"""
|
||||
read_data = sync_read_file(file_path)
|
||||
file_pwds = yaml.safe_load(read_data)
|
||||
if clear:
|
||||
# clear
|
||||
if pname in file_pwds:
|
||||
del file_pwds[pname]
|
||||
else:
|
||||
# edit
|
||||
if pvalue:
|
||||
file_pwds[pname] = pvalue
|
||||
elif private_key:
|
||||
file_pwds[pname] = {'private_key': private_key,
|
||||
'public_key': public_key}
|
||||
write_data = yaml.safe_dump(file_pwds, default_flow_style=False)
|
||||
sync_write_file(file_path, write_data)
|
||||
|
||||
|
||||
def change_property(file_path, property_dict, clear=False):
|
||||
"""change property with a file
|
||||
|
||||
|
@ -375,7 +412,8 @@ def convert_list_to_string(alist):
|
|||
return '[' + ','.join(alist) + ']'
|
||||
|
||||
|
||||
def check_arg(param, param_name, expected_type, none_ok=False, empty_ok=False):
|
||||
def check_arg(param, param_name, expected_type, none_ok=False, empty_ok=False,
|
||||
display_param=True):
|
||||
if param is None:
|
||||
if none_ok:
|
||||
return
|
||||
|
@ -395,9 +433,14 @@ def check_arg(param, param_name, expected_type, none_ok=False, empty_ok=False):
|
|||
|
||||
if not isinstance(param, expected_type):
|
||||
# wrong type
|
||||
raise InvalidArgument(u._('{name} ({param}) is not a {type}')
|
||||
.format(name=param_name, param=param,
|
||||
type=expected_type))
|
||||
if display_param:
|
||||
raise InvalidArgument(u._('{name} ({param}) is not a {type}')
|
||||
.format(name=param_name, param=param,
|
||||
type=expected_type))
|
||||
else:
|
||||
raise InvalidArgument(u._('{name} is not a {type}')
|
||||
.format(name=param_name,
|
||||
type=expected_type))
|
||||
|
||||
|
||||
class Lock(object):
|
||||
|
|
|
@ -48,6 +48,7 @@ kolla.cli =
|
|||
password_clear = kollacli.commands.password:PasswordClear
|
||||
password_list = kollacli.commands.password:PasswordList
|
||||
password_set = kollacli.commands.password:PasswordSet
|
||||
password_setkey = kollacli.commands.password:PasswordSetKey
|
||||
property_clear = kollacli.commands.property:PropertyClear
|
||||
property_list = kollacli.commands.property:PropertyList
|
||||
property_set = kollacli.commands.property:PropertySet
|
||||
|
|
|
@ -16,8 +16,40 @@ from common import KollaCliTest
|
|||
import os
|
||||
import unittest
|
||||
|
||||
from kollacli.api import client
|
||||
from kollacli.common.utils import get_kolla_etc
|
||||
|
||||
CLIENT = client.ClientApi()
|
||||
|
||||
PUBLIC_KEY = (
|
||||
'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCqusDp5jpkbng3sRue8gZV6/PCQp9'
|
||||
'ogUd5/OZ3sh9VgdaigHoYUfXZElTZLlkL71tD9WZJr69PDwmG/nE4quba8rLcDY2wC0'
|
||||
'qjq+r06ExhlRu4ivy7OxT29s8FSe8Uht9Pz8ahnXxddLF55yTbC81XrSXDBFc6Nnogz'
|
||||
'+g6GgXVKtwTkm5g3K+qix5zVECu8zzawBR/s+v0dkDxKwSY8XOG6JZlMUndaDaikZZi'
|
||||
'qp8KAOJpajM77aCfDkY3VZGFBCJiEGLVDhFrtXuBI9I0YzX4j9pZZWpSzkM/FwlPjDR'
|
||||
'SW1C9MAAFLoEQTN4j1Z5hkDNXDsr49wJBi+jjQ0FPMMvfJktrznRuO2fUa9W2iilOrv'
|
||||
'1PyrknssmW1iYXiWJ5Bq8A9sKE1r7Nbdjhcjskp77X57tNjtarRUcj3FqGjC8pv+k92'
|
||||
'9Y+FvkXbjpBsHpdMFh8BlM+EnwnsjkiQpmjLv8bpeeQooLyQQmZn94zY73bbGrsjzXe'
|
||||
'OhOTDnKAS14hxCnBlEbudHB4erp/5Nj+A8UVAT0KXPM+mkDrum/dsvV0wnvBicAVt/a'
|
||||
'tmkwDKJqXDmj4elNe8/jTXSYHpTDo29xtcGpka9AtWarmnt8QkRuieD1xSXsEUQswjq'
|
||||
'aQD2ikitKt/hEyCmT+7fy4yYKK35kukUj5qV85A8O/hOYf5vFjtRw==')
|
||||
|
||||
PRIVATE_KEY = (
|
||||
'-----BEGIN RSA PRIVATE KEY-----\n'
|
||||
'MIIJKAIBAAKCAgEAqrrA6eY6ZG54N7EbnvIGVevzwkKfaIFHefzmd7IfVYHWooB6\n'
|
||||
'GFH12RJU2S5ZC+9bQ/VmSa+vTw8Jhv5xOKrm2vKy3A2NsAtKo6vq9OhMYZUbuIr8\n'
|
||||
'uzsU9vbPBUnvFIbfT8/GoZ18XXSxeeck2wvNV60lwwRXOjZ6IPoOhoF1SrcE5JuY\n'
|
||||
'Nyvqosec1RArvM82sAUf7Pr9HZA8SsEmPFzhuiWZTFJ3Wg2opGWYqqfCgDiaWozO\n'
|
||||
'+2gnw5GN1WRhQQiYhBi1Q4Ra7V7gSPSNGM1+I/aWWVqUs5DPxcJT4w0UltQvTAAB\n'
|
||||
'S6BEEzeI9WeYZAzVw7K+PcCQYvo40NBTzDL3yZLa850bjtn1GvVtoopTq79T8q5J\n'
|
||||
'7LJltYmF4lieQavAPbChNa+zW3Y4XI7JKe+1+e7TY7Wq0VHI9xahowvKb/pPdvWP\n'
|
||||
'hb5F246QbB6XTBYfAZTPhJ8J7I5IkKZoy7/G6XnkKKC8kEJmZ/eM2O922xq7I813\n'
|
||||
'joTkw5ygEteIcQpwZRG7nRweHq6f+TY/gPFFQE9ClzzPppA67pv3bL1dMJ7wYnAF\n'
|
||||
'69VedCYMSoYIHpcN80w9it/6Cfm8niAy3v9e0icSVEsvkzcV6eFjLggY1DQ9WBPN\n'
|
||||
'MR4LKGNDuxEWeZAQi+A6Ejclx1KKBhL/E4SNj3ev4/5glaMjzSIUpA4415o=\n'
|
||||
'-----END RSA PRIVATE KEY-----'
|
||||
)
|
||||
|
||||
|
||||
class TestFunctional(KollaCliTest):
|
||||
|
||||
|
@ -66,6 +98,15 @@ class TestFunctional(KollaCliTest):
|
|||
'(%s/%s) not in output: %s' %
|
||||
(key, value, msg))
|
||||
|
||||
# test setting/clearing an ssh key
|
||||
key = 'TeStKeY'
|
||||
CLIENT.password_set_sshkey(key, PRIVATE_KEY, PUBLIC_KEY)
|
||||
keynames = CLIENT.password_get_names()
|
||||
self.assertIn(key, keynames, 'ssh key not in passwords')
|
||||
CLIENT.password_clear(key)
|
||||
keynames = CLIENT.password_get_names()
|
||||
self.assertNotIn(key, keynames, 'ssh key not cleared from passwords')
|
||||
|
||||
# check that passwords.yml file size didn't change
|
||||
size_end = os.path.getsize(pwds_path)
|
||||
self.assertEqual(size_start, size_end, 'passwords.yml size changed ' +
|
||||
|
|
|
@ -18,7 +18,7 @@ import signal
|
|||
import sys
|
||||
import yaml
|
||||
|
||||
from kollacli.common.utils import change_property
|
||||
from kollacli.common.utils import change_password
|
||||
|
||||
|
||||
def _get_empty_keys(path):
|
||||
|
@ -64,17 +64,21 @@ def _password_cmd(argv):
|
|||
"""password command
|
||||
|
||||
args for password command:
|
||||
-p path # path to passwords.yaml
|
||||
-k key # key of password
|
||||
-v value # value of password
|
||||
-c # flag to clear the password
|
||||
-l # print to stdout a csv string of the existing keys
|
||||
-e # get keys of passwords with empty values
|
||||
-p path # path to passwords.yaml
|
||||
-k key # key of password
|
||||
-v value # value of password (if not ssh keys)
|
||||
-r private key value # ssh private key
|
||||
-u public key value # ssh public key
|
||||
-c # flag to clear the password
|
||||
-l # print to stdout a csv string of the existing keys
|
||||
-e # get keys of passwords with empty values
|
||||
"""
|
||||
opts, _ = getopt.getopt(argv[2:], 'p:k:v:cle')
|
||||
opts, _ = getopt.getopt(argv[2:], 'p:k:v:r:u:cle')
|
||||
path = ''
|
||||
pwd_key = ''
|
||||
pwd_value = ''
|
||||
pwd_ssh_private = ''
|
||||
pwd_ssh_public = ''
|
||||
clear_flag = False
|
||||
list_flag = False
|
||||
empty_flag = False
|
||||
|
@ -85,6 +89,10 @@ def _password_cmd(argv):
|
|||
pwd_key = arg
|
||||
elif opt == '-v':
|
||||
pwd_value = arg
|
||||
elif opt == '-r':
|
||||
pwd_ssh_private = arg.replace('"', '')
|
||||
elif opt == '-u':
|
||||
pwd_ssh_public = arg.replace('"', '')
|
||||
elif opt == '-c':
|
||||
clear_flag = True
|
||||
elif opt == '-l':
|
||||
|
@ -98,10 +106,10 @@ def _password_cmd(argv):
|
|||
# get empty passwords
|
||||
_get_empty_keys(path)
|
||||
else:
|
||||
# edit a password
|
||||
property_dict = {}
|
||||
property_dict[pwd_key] = pwd_value
|
||||
change_property(path, property_dict, clear_flag)
|
||||
# edit/clear a password
|
||||
change_password(path, pwd_key, pvalue=pwd_value,
|
||||
private_key=pwd_ssh_private,
|
||||
public_key=pwd_ssh_public, clear=clear_flag)
|
||||
|
||||
|
||||
def _job_cmd(argv):
|
||||
|
|
Loading…
Reference in New Issue