Add authentication in fuel-cli

* modified APIClient to get token
* modified base test class for fuel-cli to mock keystone authentication
* modified fuel-cli to use user and password

Change-Id: I4b0a2dc485c086949b103c11689ea892069f02e5
Implements: blueprint access-control-master-node
This commit is contained in:
Kamil Sambor
2014-06-30 14:52:02 +02:00
parent 416ba6dc5d
commit 86c2e5b2f0
6 changed files with 77 additions and 6 deletions

View File

@@ -46,6 +46,11 @@ class Action(object):
"""Entry point for all actions subclasses
"""
APIClient.debug_mode(debug=params.debug)
if getattr(params, 'user') and getattr(params, 'password'):
APIClient.user = params.user
APIClient.password = params.password
APIClient.initialize_keystone_client()
self.serializer = Serializer.from_params(params)
if self.flag_func_map is not None:
for flag, method in self.flag_func_map:

View File

@@ -13,6 +13,7 @@
# under the License.
from functools import wraps
from keystoneclient.exceptions import Unauthorized
import sys
import urllib2
@@ -77,6 +78,8 @@ def handle_exceptions(exc):
))
elif isinstance(exc, urllib2.URLError):
exit_with_error("Can't connect to Nailgun server!")
elif isinstance(exc, Unauthorized):
exit_with_error("Unauthorized: need authentication!")
elif isinstance(exc, FuelClientException):
exit_with_error(exc.message)
else:

View File

@@ -44,6 +44,7 @@ class Parser:
self.generate_actions()
self.add_version_args()
self.add_debug_arg()
self.add_keystone_credentials_args()
self.add_serializers_args()
def generate_actions(self):
@@ -101,6 +102,26 @@ class Parser:
default=False
)
def add_keystone_credentials_args(self):
self.universal_flags.append("--os-username")
self.parser.add_argument(
"--os-username",
dest="user",
action="store",
type=str,
help="credentials for keystone authentication user",
default=None
)
self.universal_flags.append("--os-password")
self.parser.add_argument(
"--os-password",
dest="password",
action="store",
type=str,
help="credentials for keystone authentication password",
default=None
)
def add_version_args(self):
for args in (get_version_arg(), get_fuel_version_arg()):
self.parser.add_argument(*args["args"], **args["params"])

View File

@@ -18,6 +18,8 @@ import urllib2
import yaml
from keystoneclient import client as auth_client
from fuelclient.cli.error import exceptions_decorator
@@ -27,10 +29,14 @@ class Client(object):
def __init__(self):
self.debug = False
path_to_config = "/etc/fuel-client.yaml"
self.test_mod = bool(os.environ.get('TEST_MODE', ''))
path_to_config = "/etc/fuel/client/config.yaml"
defaults = {
"LISTEN_ADDRESS": "127.0.0.1",
"LISTEN_PORT": "8000"
"SERVER_ADDRESS": "127.0.0.1",
"LISTEN_PORT": "8000",
"KEYSTONE_USER": "admin",
"KEYSTONE_PASS": "admin",
"KEYSTONE_PORT": "5000",
}
if os.path.exists(path_to_config):
with open(path_to_config, "r") as fh:
@@ -38,9 +44,34 @@ class Client(object):
defaults.update(config)
else:
defaults.update(os.environ)
self.root = "http://{LISTEN_ADDRESS}:{LISTEN_PORT}".format(**defaults)
self.root = "http://{SERVER_ADDRESS}:{LISTEN_PORT}".format(**defaults)
self.keystone_base = (
"http://{SERVER_ADDRESS}:{LISTEN_PORT}/keystone/v2.0"
.format(**defaults)
)
self.api_root = self.root + "/api/v1/"
self.ostf_root = self.root + "/ostf/"
self.auth_status()
self.user = defaults["KEYSTONE_USER"]
self.password = defaults["KEYSTONE_PASS"]
self.keystone_client = None
self.initialize_keystone_client()
def auth_status(self):
self.auth_required = False
if not self.test_mod:
request = urllib2.urlopen(''.join([self.api_root, 'version']))
self.auth_required = json.loads(
request.read()).get('auth_required', False)
def initialize_keystone_client(self):
if not self.test_mod and self.auth_required:
self.keystone_client = auth_client.Client(
username=self.user,
password=self.password,
auth_url=self.keystone_base,
tenant_name="admin")
self.keystone_client.authenticate()
def debug_mode(self, debug=False):
self.debug = debug
@@ -53,12 +84,14 @@ class Client(object):
def delete_request(self, api):
"""Make DELETE request to specific API with some data
"""
token = self.keystone_client.auth_token if self.keystone_client else ''
self.print_debug(
"DELETE {0}".format(self.api_root + api)
)
opener = urllib2.build_opener(urllib2.HTTPHandler)
request = urllib2.Request(self.api_root + api)
request.add_header('Content-Type', 'application/json')
request.add_header('X-Auth-Token', token)
request.get_method = lambda: 'DELETE'
opener.open(request)
return {}
@@ -66,6 +99,7 @@ class Client(object):
def put_request(self, api, data):
"""Make PUT request to specific API with some data
"""
token = self.keystone_client.auth_token if self.keystone_client else ''
data_json = json.dumps(data)
self.print_debug(
"PUT {0} data={1}"
@@ -74,6 +108,7 @@ class Client(object):
opener = urllib2.build_opener(urllib2.HTTPHandler)
request = urllib2.Request(self.api_root + api, data=data_json)
request.add_header('Content-Type', 'application/json')
request.add_header('X-Auth-Token', token)
request.get_method = lambda: 'PUT'
return json.loads(
opener.open(request).read()
@@ -82,19 +117,23 @@ class Client(object):
def get_request(self, api, ostf=False):
"""Make GET request to specific API
"""
token = self.keystone_client.auth_token if self.keystone_client else ''
url = (self.ostf_root if ostf else self.api_root) + api
self.print_debug(
"GET {0}"
.format(url)
)
request = urllib2.urlopen(url)
opener = urllib2.build_opener(urllib2.HTTPHandler)
request = urllib2.Request(url)
request.add_header('X-Auth-Token', token)
return json.loads(
request.read()
opener.open(request).read()
)
def post_request(self, api, data, ostf=False):
"""Make POST request to specific API with some data
"""
token = self.keystone_client.auth_token if self.keystone_client else ''
url = (self.ostf_root if ostf else self.api_root) + api
data_json = json.dumps(data)
self.print_debug(
@@ -108,6 +147,7 @@ class Client(object):
'Content-Type': 'application/json'
}
)
request.add_header('X-Auth-Token', token)
try:
response = json.loads(
urllib2.urlopen(request)

View File

@@ -23,3 +23,4 @@ simplejson==3.3.0
web.py==0.37
wsgilog==0.3
wsgiref==0.1.2
python-keystoneclient>=0.7

View File

@@ -102,6 +102,7 @@ class BaseTestCase(TestCase):
def run_cli_command(self, command_line, check_errors=False):
modified_env = os.environ.copy()
modified_env['TEST_MODE'] = 'True'
command_args = [" ".join((self.fuel_path, command_line))]
process_handle = subprocess.Popen(
command_args,