client retrieval of freezer api endpoint
The api client queries keystone to obtain the freezer api endpoint, provided that the freezer api service and endpoint have been registered in keystone. An optional parameter to specify the api endpoint is supported Change-Id: I6626a60d1fd5d18a59376165e94c789832865ae0 Implements: blueprint freezer-apiclient-endpoint
This commit is contained in:
parent
72a6c7396e
commit
616e742792
|
@ -29,10 +29,13 @@ class BackupsManager(object):
|
||||||
|
|
||||||
def __init__(self, client):
|
def __init__(self, client):
|
||||||
self.client = client
|
self.client = client
|
||||||
self.endpoint = self.client.endpoint + 'backups/'
|
self.endpoint = self.client.api_endpoint + '/v1/backups/'
|
||||||
self.headers = {'X-Auth-Token': client.token}
|
|
||||||
|
|
||||||
def create(self, backup_metadata, username=None, tenant_name=None):
|
@property
|
||||||
|
def headers(self):
|
||||||
|
return {'X-Auth-Token': self.client.auth_token}
|
||||||
|
|
||||||
|
def create(self, backup_metadata):
|
||||||
r = requests.post(self.endpoint,
|
r = requests.post(self.endpoint,
|
||||||
data=json.dumps(backup_metadata),
|
data=json.dumps(backup_metadata),
|
||||||
headers=self.headers)
|
headers=self.headers)
|
||||||
|
@ -42,14 +45,14 @@ class BackupsManager(object):
|
||||||
backup_id = r.json()['backup_id']
|
backup_id = r.json()['backup_id']
|
||||||
return backup_id
|
return backup_id
|
||||||
|
|
||||||
def delete(self, backup_id, username=None, tenant_name=None):
|
def delete(self, backup_id):
|
||||||
endpoint = self.endpoint + backup_id
|
endpoint = self.endpoint + backup_id
|
||||||
r = requests.delete(endpoint, headers=self.headers)
|
r = requests.delete(endpoint, headers=self.headers)
|
||||||
if r.status_code != 204:
|
if r.status_code != 204:
|
||||||
raise exceptions.MetadataDeleteFailure(
|
raise exceptions.MetadataDeleteFailure(
|
||||||
"[*] Error {0}".format(r.status_code))
|
"[*] Error {0}".format(r.status_code))
|
||||||
|
|
||||||
def list(self, username=None, tenant_name=None):
|
def list(self):
|
||||||
r = requests.get(self.endpoint, headers=self.headers)
|
r = requests.get(self.endpoint, headers=self.headers)
|
||||||
if r.status_code != 200:
|
if r.status_code != 200:
|
||||||
raise exceptions.MetadataGetFailure(
|
raise exceptions.MetadataGetFailure(
|
||||||
|
@ -57,7 +60,7 @@ class BackupsManager(object):
|
||||||
|
|
||||||
return r.json()['backups']
|
return r.json()['backups']
|
||||||
|
|
||||||
def get(self, backup_id, username=None, tenant_name=None):
|
def get(self, backup_id):
|
||||||
endpoint = self.endpoint + backup_id
|
endpoint = self.endpoint + backup_id
|
||||||
r = requests.get(endpoint, headers=self.headers)
|
r = requests.get(endpoint, headers=self.headers)
|
||||||
if r.status_code == 200:
|
if r.status_code == 200:
|
||||||
|
|
|
@ -22,14 +22,15 @@ Hudson (tjh@cryptsoft.com).
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
from openstackclient.identity import client as os_client
|
||||||
|
|
||||||
possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
|
possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
|
||||||
os.pardir, os.pardir, os.pardir))
|
os.pardir, os.pardir, os.pardir))
|
||||||
if os.path.exists(os.path.join(possible_topdir, 'freezer', '__init__.py')):
|
if os.path.exists(os.path.join(possible_topdir, 'freezer', '__init__.py')):
|
||||||
sys.path.insert(0, possible_topdir)
|
sys.path.insert(0, possible_topdir)
|
||||||
|
|
||||||
import keystoneclient
|
|
||||||
|
|
||||||
from freezer.apiclient.backups import BackupsManager
|
from freezer.apiclient.backups import BackupsManager
|
||||||
|
import exceptions
|
||||||
|
|
||||||
|
|
||||||
class Client(object):
|
class Client(object):
|
||||||
|
@ -40,26 +41,66 @@ class Client(object):
|
||||||
password=None,
|
password=None,
|
||||||
tenant_name=None,
|
tenant_name=None,
|
||||||
auth_url=None,
|
auth_url=None,
|
||||||
endpoint=None,
|
session=None,
|
||||||
session=None):
|
api_endpoint=None):
|
||||||
if endpoint is None:
|
self.version = version
|
||||||
raise Exception('Missing endpoint information')
|
|
||||||
self.endpoint = endpoint
|
|
||||||
|
|
||||||
if token is not None:
|
|
||||||
# validate the token ?
|
|
||||||
self.token = token
|
self.token = token
|
||||||
elif session is not None:
|
|
||||||
pass
|
|
||||||
# TODO: handle session auth
|
|
||||||
# assert isinstance(session, keystoneclient.session.Session)
|
|
||||||
else:
|
|
||||||
self.username = username
|
self.username = username
|
||||||
self.tenant_name = tenant_name
|
self.tenant_name = tenant_name
|
||||||
kc = keystoneclient.v2_0.client.Client(
|
self.password = password
|
||||||
username=username,
|
self.auth_url = auth_url
|
||||||
password=password,
|
self._api_endpoint = api_endpoint
|
||||||
tenant_name=tenant_name,
|
self.session = session
|
||||||
auth_url=auth_url)
|
self._auth = None
|
||||||
self.token = kc.auth_token
|
|
||||||
self.backups = BackupsManager(self)
|
self.backups = BackupsManager(self)
|
||||||
|
|
||||||
|
def _update_api_endpoint(self):
|
||||||
|
services = self.auth.services.list()
|
||||||
|
try:
|
||||||
|
freezer_service = next(x for x in services if x.name == 'freezer')
|
||||||
|
except:
|
||||||
|
raise exceptions.AuthFailure(
|
||||||
|
'freezer service not found in services list')
|
||||||
|
endpoints = self.auth.endpoints.list()
|
||||||
|
try:
|
||||||
|
freezer_endpoint =\
|
||||||
|
next(x for x in endpoints
|
||||||
|
if x.service_id == freezer_service.id)
|
||||||
|
except:
|
||||||
|
raise exceptions.AuthFailure(
|
||||||
|
'freezer endpoint not found in endpoint list')
|
||||||
|
self._api_endpoint = freezer_endpoint.publicurl
|
||||||
|
|
||||||
|
@property
|
||||||
|
def auth(self):
|
||||||
|
if self._auth is None:
|
||||||
|
if self.username and self.password:
|
||||||
|
self._auth = os_client.IdentityClientv2(
|
||||||
|
auth_url=self.auth_url,
|
||||||
|
username=self.username,
|
||||||
|
password=self.password,
|
||||||
|
tenant_name=self.tenant_name)
|
||||||
|
elif self.token:
|
||||||
|
self._auth = os_client.IdentityClientv2(
|
||||||
|
endpoint=self.auth_url,
|
||||||
|
token=self.token)
|
||||||
|
else:
|
||||||
|
raise exceptions.AuthFailure("Missing auth credentials")
|
||||||
|
return self._auth
|
||||||
|
|
||||||
|
@property
|
||||||
|
def auth_token(self):
|
||||||
|
return self.auth.auth_token
|
||||||
|
|
||||||
|
@property
|
||||||
|
def api_endpoint(self):
|
||||||
|
if self._api_endpoint is None:
|
||||||
|
self._update_api_endpoint()
|
||||||
|
return self._api_endpoint
|
||||||
|
|
||||||
|
def api_exists(self):
|
||||||
|
try:
|
||||||
|
if self.api_endpoint is not None:
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
|
@ -20,27 +20,17 @@ Hudson (tjh@cryptsoft.com).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class FreezerClientException(Exception):
|
class MetadataCreationFailure(Exception):
|
||||||
"""
|
|
||||||
Base Freezer API Exception
|
|
||||||
"""
|
|
||||||
message = ("Unknown exception occurred")
|
|
||||||
|
|
||||||
def __init__(self, message=None, *args, **kwargs):
|
|
||||||
if not message:
|
|
||||||
message = self.message
|
|
||||||
message = message % kwargs
|
|
||||||
|
|
||||||
Exception.__init__(self, message)
|
|
||||||
|
|
||||||
|
|
||||||
class MetadataCreationFailure(FreezerClientException):
|
|
||||||
message = "Metadata creation failed: %reason"
|
message = "Metadata creation failed: %reason"
|
||||||
|
|
||||||
|
|
||||||
class MetadataGetFailure(FreezerClientException):
|
class MetadataGetFailure(Exception):
|
||||||
message = "Metadata read failed: %reason"
|
message = "Metadata read failed: %reason"
|
||||||
|
|
||||||
|
|
||||||
class MetadataDeleteFailure(FreezerClientException):
|
class MetadataDeleteFailure(Exception):
|
||||||
message = "Metadata deletion failed: %reason"
|
message = "Metadata deletion failed: %reason"
|
||||||
|
|
||||||
|
|
||||||
|
class AuthFailure(Exception):
|
||||||
|
message = "Authentication Error: %reason"
|
||||||
|
|
|
@ -61,6 +61,22 @@ utilizes the timestamp of the first (level 0) backup in the session
|
||||||
It is identified by (container, hostname, backupname, timestamp-of-level-0)
|
It is identified by (container, hostname, backupname, timestamp-of-level-0)
|
||||||
|
|
||||||
|
|
||||||
|
API registration
|
||||||
|
================
|
||||||
|
keystone user-create --name freezer --pass FREEZER_PWD
|
||||||
|
keystone user-role-add --user freezer --tenant service --role admin
|
||||||
|
|
||||||
|
keystone service-create --name freezer --type backup \
|
||||||
|
--description "Freezer Backup Service"
|
||||||
|
|
||||||
|
keystone endpoint-create \
|
||||||
|
--service-id $(keystone service-list | awk '/ backup / {print $2}') \
|
||||||
|
--publicurl http://freezer_api_publicurl:port \
|
||||||
|
--internalurl http://freezer_api_internalurl:port \
|
||||||
|
--adminurl http://freezer_api_adminurl:port \
|
||||||
|
--region regionOne
|
||||||
|
|
||||||
|
|
||||||
API routes
|
API routes
|
||||||
==========
|
==========
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,112 @@
|
||||||
|
"""Freezer swift.py related tests
|
||||||
|
|
||||||
|
Copyright 2014 Hewlett-Packard
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
This product includes cryptographic software written by Eric Young
|
||||||
|
(eay@cryptsoft.com). This product includes software written by Tim
|
||||||
|
Hudson (tjh@cryptsoft.com).
|
||||||
|
========================================================================
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
from mock import Mock, patch
|
||||||
|
|
||||||
|
from freezer.apiclient import exceptions
|
||||||
|
from freezer.apiclient import backups
|
||||||
|
|
||||||
|
|
||||||
|
class TestBackupManager(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.mock_client = Mock()
|
||||||
|
self.mock_client.api_endpoint = 'http://testendpoint:9999'
|
||||||
|
self.mock_client.auth_token = 'testtoken'
|
||||||
|
self.b = backups.BackupsManager(self.mock_client)
|
||||||
|
|
||||||
|
@patch('freezer.apiclient.backups.requests')
|
||||||
|
def test_create(self, mock_requests):
|
||||||
|
self.assertEqual(self.b.endpoint, 'http://testendpoint:9999/v1/backups/')
|
||||||
|
self.assertEqual(self.b.headers, {'X-Auth-Token': 'testtoken'})
|
||||||
|
|
||||||
|
@patch('freezer.apiclient.backups.requests')
|
||||||
|
def test_create_ok(self, mock_requests):
|
||||||
|
mock_response = Mock()
|
||||||
|
mock_response.status_code = 201
|
||||||
|
mock_response.json.return_value = {'backup_id': 'qwerqwer'}
|
||||||
|
mock_requests.post.return_value = mock_response
|
||||||
|
retval = self.b.create(backup_metadata={'backup': 'metadata'})
|
||||||
|
self.assertEqual(retval, 'qwerqwer')
|
||||||
|
|
||||||
|
@patch('freezer.apiclient.backups.requests')
|
||||||
|
def test_create_fail(self, mock_requests):
|
||||||
|
mock_response = Mock()
|
||||||
|
mock_response.status_code = 500
|
||||||
|
#mock_response.json.return_value = {'backup_id': 'qwerqwer'}
|
||||||
|
mock_requests.post.return_value = mock_response
|
||||||
|
self.assertRaises(exceptions.MetadataCreationFailure, self.b.create, {'backup': 'metadata'})
|
||||||
|
|
||||||
|
@patch('freezer.apiclient.backups.requests')
|
||||||
|
def test_delete_ok(self, mock_requests):
|
||||||
|
mock_response = Mock()
|
||||||
|
mock_response.status_code = 204
|
||||||
|
mock_requests.delete.return_value = mock_response
|
||||||
|
retval = self.b.delete('test_backup_id')
|
||||||
|
self.assertIsNone(retval)
|
||||||
|
|
||||||
|
@patch('freezer.apiclient.backups.requests')
|
||||||
|
def test_delete_fail(self, mock_requests):
|
||||||
|
mock_response = Mock()
|
||||||
|
mock_response.status_code = 500
|
||||||
|
mock_requests.delete.return_value = mock_response
|
||||||
|
self.assertRaises(exceptions.MetadataDeleteFailure, self.b.delete, 'test_backup_id')
|
||||||
|
|
||||||
|
@patch('freezer.apiclient.backups.requests')
|
||||||
|
def test_get_ok(self, mock_requests):
|
||||||
|
mock_response = Mock()
|
||||||
|
mock_response.status_code = 200
|
||||||
|
mock_response.json.return_value = {'backup_id': 'qwerqwer'}
|
||||||
|
mock_requests.get.return_value = mock_response
|
||||||
|
retval = self.b.get('test_backup_id')
|
||||||
|
self.assertEqual(retval, {'backup_id': 'qwerqwer'})
|
||||||
|
|
||||||
|
@patch('freezer.apiclient.backups.requests')
|
||||||
|
def test_get_none(self, mock_requests):
|
||||||
|
mock_response = Mock()
|
||||||
|
mock_response.status_code = 404
|
||||||
|
mock_requests.get.return_value = mock_response
|
||||||
|
retval = self.b.get('test_backup_id')
|
||||||
|
self.assertIsNone(retval)
|
||||||
|
|
||||||
|
# get_error
|
||||||
|
|
||||||
|
@patch('freezer.apiclient.backups.requests')
|
||||||
|
def test_list_ok(self, mock_requests):
|
||||||
|
mock_response = Mock()
|
||||||
|
mock_response.status_code = 200
|
||||||
|
backup_list = [{'backup_id_0': 'qwerqwer'}, {'backup_id_1': 'asdfasdf'}]
|
||||||
|
mock_response.json.return_value = {'backups': backup_list}
|
||||||
|
mock_requests.get.return_value = mock_response
|
||||||
|
retval = self.b.list()
|
||||||
|
self.assertEqual(retval, backup_list)
|
||||||
|
|
||||||
|
@patch('freezer.apiclient.backups.requests')
|
||||||
|
def test_list_error(self, mock_requests):
|
||||||
|
mock_response = Mock()
|
||||||
|
mock_response.status_code = 404
|
||||||
|
backup_list = [{'backup_id_0': 'qwerqwer'}, {'backup_id_1': 'asdfasdf'}]
|
||||||
|
mock_response.json.return_value = {'backups': backup_list}
|
||||||
|
mock_requests.get.return_value = mock_response
|
||||||
|
self.assertRaises(exceptions.MetadataGetFailure, self.b.list)
|
|
@ -0,0 +1,108 @@
|
||||||
|
"""Freezer swift.py related tests
|
||||||
|
|
||||||
|
Copyright 2014 Hewlett-Packard
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
This product includes cryptographic software written by Eric Young
|
||||||
|
(eay@cryptsoft.com). This product includes software written by Tim
|
||||||
|
Hudson (tjh@cryptsoft.com).
|
||||||
|
========================================================================
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
from mock import Mock, patch
|
||||||
|
|
||||||
|
from freezer.apiclient import client
|
||||||
|
from freezer.apiclient import exceptions
|
||||||
|
|
||||||
|
|
||||||
|
class TestClientMock(unittest.TestCase):
|
||||||
|
|
||||||
|
def create_mock_endpoint(self, service_id):
|
||||||
|
m = Mock()
|
||||||
|
m.service_id = service_id
|
||||||
|
m.publicurl = 'http://frezerapiurl:9090'
|
||||||
|
return m
|
||||||
|
|
||||||
|
def create_mock_service(self, name, id):
|
||||||
|
m = Mock()
|
||||||
|
m.name = name
|
||||||
|
m.id = id
|
||||||
|
return m
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
mock_enpointlist_ok = [self.create_mock_endpoint('idqwerty'),
|
||||||
|
self.create_mock_endpoint('idfreak'),
|
||||||
|
self.create_mock_endpoint('blabla')]
|
||||||
|
mock_servicelist_ok = [self.create_mock_service(name='glance', id='idqwerty'),
|
||||||
|
self.create_mock_service(name='freezer', id='idfreak')]
|
||||||
|
self.mock_IdentityClientv2 = Mock()
|
||||||
|
self.mock_IdentityClientv2.endpoints.list.return_value = mock_enpointlist_ok
|
||||||
|
self.mock_IdentityClientv2.services.list.return_value = mock_servicelist_ok
|
||||||
|
|
||||||
|
@patch('freezer.apiclient.client.os_client')
|
||||||
|
def test_client_create_username(self, mock_os_client):
|
||||||
|
mock_os_client.IdentityClientv2.return_value = self.mock_IdentityClientv2
|
||||||
|
c = client.Client(username='myname',
|
||||||
|
password='mypasswd',
|
||||||
|
tenant_name='mytenant',
|
||||||
|
auth_url='http://whatever:35357/v2.0/')
|
||||||
|
self.assertIsInstance(c, client.Client)
|
||||||
|
self.assertEqual(c.api_endpoint, 'http://frezerapiurl:9090')
|
||||||
|
|
||||||
|
@patch('freezer.apiclient.client.os_client')
|
||||||
|
def test_client_create_token(self, mock_os_client):
|
||||||
|
mock_os_client.IdentityClientv2.return_value = self.mock_IdentityClientv2
|
||||||
|
c = client.Client(token='mytoken',
|
||||||
|
auth_url='http://whatever:35357/v2.0/')
|
||||||
|
self.assertIsInstance(c, client.Client)
|
||||||
|
self.assertEqual(c.api_endpoint, 'http://frezerapiurl:9090')
|
||||||
|
|
||||||
|
@patch('freezer.apiclient.client.os_client')
|
||||||
|
def test_client_error_no_credentials(self, mock_os_client):
|
||||||
|
mock_os_client.IdentityClientv2.return_value = self.mock_IdentityClientv2
|
||||||
|
self.assertRaises(exceptions.AuthFailure, client.Client, auth_url='http://whatever:35357/v2.0/')
|
||||||
|
|
||||||
|
@patch('freezer.apiclient.client.os_client')
|
||||||
|
def test_client_service_not_found(self, mock_os_client):
|
||||||
|
mock_servicelist_bad = [self.create_mock_service(name='glance', id='idqwerty'),
|
||||||
|
self.create_mock_service(name='spanishinquisition', id='idfreak')]
|
||||||
|
self.mock_IdentityClientv2.services.list.return_value = mock_servicelist_bad
|
||||||
|
mock_os_client.IdentityClientv2.return_value = self.mock_IdentityClientv2
|
||||||
|
self.assertRaises(exceptions.AuthFailure, client.Client, token='mytoken', auth_url='http://whatever:35357/v2.0/')
|
||||||
|
|
||||||
|
@patch('freezer.apiclient.client.os_client')
|
||||||
|
def test_client_endpoint_not_found(self, mock_os_client):
|
||||||
|
mock_enpointlist_bad = [self.create_mock_endpoint('idqwerty'),
|
||||||
|
self.create_mock_endpoint('idfiasco'),
|
||||||
|
self.create_mock_endpoint('blabla')]
|
||||||
|
self.mock_IdentityClientv2.endpoints.list.return_value = mock_enpointlist_bad
|
||||||
|
mock_os_client.IdentityClientv2.return_value = self.mock_IdentityClientv2
|
||||||
|
self.assertRaises(exceptions.AuthFailure, client.Client, token='mytoken', auth_url='http://whatever:35357/v2.0/')
|
||||||
|
|
||||||
|
@patch('freezer.apiclient.client.os_client')
|
||||||
|
def test_client_api_exists(self, mock_os_client):
|
||||||
|
mock_os_client.IdentityClientv2.return_value = self.mock_IdentityClientv2
|
||||||
|
c = client.Client(token='mytoken',
|
||||||
|
auth_url='http://whatever:35357/v2.0/')
|
||||||
|
self.assertTrue(c.api_exists())
|
||||||
|
|
||||||
|
@patch('freezer.apiclient.client.os_client')
|
||||||
|
def test_client_auth_token(self, mock_os_client):
|
||||||
|
self.mock_IdentityClientv2.auth_token = 'stotoken'
|
||||||
|
mock_os_client.IdentityClientv2.return_value = self.mock_IdentityClientv2
|
||||||
|
c = client.Client(token='mytoken',
|
||||||
|
auth_url='http://whatever:35357/v2.0/')
|
||||||
|
self.assertEqual(c.auth_token, 'stotoken')
|
Loading…
Reference in New Issue