Add API call to fetch tenant entities

This commit is contained in:
Frédéric Guillot 2017-05-15 15:57:03 -04:00
parent 75b72d4e34
commit d263c9ae17
8 changed files with 114 additions and 14 deletions

View File

@ -16,7 +16,7 @@ from cliff.command import Command
class EndpointCommand(Command): class EndpointCommand(Command):
"""Show the Almanach Endpoint URL""" """Show Almanach Endpoint URL"""
def take_action(self, parsed_args): def take_action(self, parsed_args):
self.app.stdout.write('{}\n'.format(self.app.get_client().get_url())) self.app.stdout.write('{}\n'.format(self.app.get_client().get_url()))

View File

@ -0,0 +1,46 @@
# Copyright 2017 INAP
#
# 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.
from cliff.lister import Lister
from dateutil import parser
class TenantEntityCommand(Lister):
"""Show all entities for a given tenant"""
def get_parser(self, prog_name):
parser = super().get_parser(prog_name)
parser.add_argument('tenant_id', help='Tenant ID')
parser.add_argument('start', help='Start Date')
parser.add_argument('end', help='End Date')
return parser
def take_action(self, parsed_args):
start = parser.parse(parsed_args.start)
end = parser.parse(parsed_args.end)
entities = self.app.get_client().get_tenant_entities(parsed_args.tenant_id, start, end)
rows = []
for entity in entities:
entity_type = entity.get('entity_type')
if entity_type == 'instance':
properties = dict(flavor=entity.get('flavor'), image=entity.get('image_meta'))
else:
properties = dict(volume_type=entity.get('volume_type'), attached_to=entity.get('attached_to'))
rows.append((entity.get('entity_id'), entity_type, entity.get('name'),
entity.get('start'), entity.get('end'), properties))
return ('Entity ID', 'Type', 'Name', 'Start', 'End', 'Properties'), rows

View File

@ -16,7 +16,7 @@ from cliff.command import Command
class VersionCommand(Command): class VersionCommand(Command):
"""Show the Almanach version number""" """Show Almanach version number"""
def take_action(self, parsed_args): def take_action(self, parsed_args):
info = self.app.get_client().get_info() info = self.app.get_client().get_info()

View File

@ -24,18 +24,25 @@ logger = logging.getLogger(__name__)
class HttpClient(metaclass=abc.ABCMeta): class HttpClient(metaclass=abc.ABCMeta):
def _get(self, url):
def __init__(self, url, token=None):
self.url = url
self.token = token
def _get(self, url, params=None):
logger.debug(url) logger.debug(url)
response = requests.get(url, headers=self._get_headers()) response = requests.get(url, headers=self._get_headers(), params=params)
body = response.json()
if response.status_code != 200: if response.status_code != 200:
raise exceptions.HTTPError('HTTP Error ({})'.format(response.status_code)) raise exceptions.HTTPError('{} ({})'.format(body.get('error') or 'HTTP Error', response.status_code))
return response.json() return body
def _get_headers(self): def _get_headers(self):
return { return {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'Accept': 'application/json', 'Accept': 'application/json',
'User-Agent': 'python-almanachclient/{}'.format(client_version.__version__), 'User-Agent': 'python-almanachclient/{}'.format(client_version.__version__),
'X-Auth-Token': self.token,
} }

View File

@ -19,6 +19,7 @@ from cliff import app
from cliff import commandmanager from cliff import commandmanager
from almanachclient.commands.endpoint import EndpointCommand from almanachclient.commands.endpoint import EndpointCommand
from almanachclient.commands.tenant_entities import TenantEntityCommand
from almanachclient.commands.version import VersionCommand from almanachclient.commands.version import VersionCommand
from almanachclient.keystone_client import KeystoneClient from almanachclient.keystone_client import KeystoneClient
from almanachclient.v1.client import Client from almanachclient.v1.client import Client
@ -29,6 +30,7 @@ class AlmanachCommandManager(commandmanager.CommandManager):
SHELL_COMMANDS = { SHELL_COMMANDS = {
'version': VersionCommand, 'version': VersionCommand,
'endpoint': EndpointCommand, 'endpoint': EndpointCommand,
'tenant entities': TenantEntityCommand,
} }
def load_commands(self, namespace): def load_commands(self, namespace):
@ -66,8 +68,12 @@ class AlmanachApp(app.App):
help='OpenStack username (Env: OS_USERNAME).') help='OpenStack username (Env: OS_USERNAME).')
parser.add_argument('--almanach-service', parser.add_argument('--almanach-service',
default=os.environ.get('ALMANACH_SERVICE'), default=os.environ.get('ALMANACH_SERVICE', 'almanach'),
help='Almanach keystone service name (Env: ALMANACH_SERVICE).') help='Almanach keystone service name (Env: ALMANACH_SERVICE).')
parser.add_argument('--almanach-token',
default=os.environ.get('ALMANACH_TOKEN'),
help='Almanach API token (Env: ALMANACH_TOKEN).')
return parser return parser
def get_client(self): def get_client(self):
@ -77,7 +83,7 @@ class AlmanachApp(app.App):
service=self.options.almanach_service, service=self.options.almanach_service,
region_name=self.options.os_region_name) region_name=self.options.os_region_name)
return Client(keystone.get_endpoint_url()) return Client(keystone.get_endpoint_url(), token=self.options.almanach_token)
def main(argv=sys.argv[1:]): def main(argv=sys.argv[1:]):

View File

@ -12,8 +12,10 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from datetime import datetime
from unittest import mock from unittest import mock
from almanachclient import exceptions
from almanachclient.tests import base from almanachclient.tests import base
from almanachclient.v1.client import Client from almanachclient.v1.client import Client
@ -21,8 +23,14 @@ from almanachclient.v1.client import Client
class TestClient(base.TestCase): class TestClient(base.TestCase):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self.almanach_url = 'http://almanach_url' self.url = 'http://almanach_url'
self.client = Client(self.almanach_url) self.token = 'token'
self.headers = {'Content-Type': 'application/json',
'User-Agent': 'python-almanachclient/0.0.1',
'X-Auth-Token': self.token,
'Accept': 'application/json'}
self.client = Client(self.url, self.token)
@mock.patch('requests.get') @mock.patch('requests.get')
def test_get_info(self, requests): def test_get_info(self, requests):
@ -37,3 +45,31 @@ class TestClient(base.TestCase):
response.status_code = 200 response.status_code = 200
self.assertEqual(expected, self.client.get_info()) self.assertEqual(expected, self.client.get_info())
requests.assert_called_once_with('{}{}'.format(self.url, '/v1/info'), headers=self.headers, params=None)
@mock.patch('requests.get')
def test_get_info_with_http_error(self, requests):
response = mock.Mock()
requests.return_value = response
response.status_code = 500
self.assertRaises(exceptions.HTTPError, self.client.get_info)
@mock.patch('requests.get')
def test_get_tenant_entities(self, requests):
response = mock.Mock()
expected = [mock.Mock()]
requests.return_value = response
response.json.return_value = expected
response.status_code = 200
start = datetime.now()
end = datetime.now()
params = dict(start=start.strftime(Client.DATE_FORMAT), end=end.strftime(Client.DATE_FORMAT))
self.assertEqual(expected, self.client.get_tenant_entities('my_tenant_id', start, end))
requests.assert_called_once_with('{}{}'.format(self.url, '/v1/project/my_tenant_id/entities'),
params=params,
headers=self.headers)

View File

@ -16,13 +16,17 @@ from almanachclient.http_client import HttpClient
class Client(HttpClient): class Client(HttpClient):
api_version = 'v1' DATE_FORMAT = '%Y-%m-%d %H:%M:%S.%f'
def __init__(self, url): api_version = 'v1'
self.url = url
def get_url(self): def get_url(self):
return self.url return self.url
def get_info(self): def get_info(self):
return self._get('{}/{}/info'.format(self.url, self.api_version)) return self._get('{}/{}/info'.format(self.url, self.api_version))
def get_tenant_entities(self, tenant_id, start, end):
url = '{}/{}/project/{}/entities'.format(self.url, self.api_version, tenant_id)
params = {'start': start.strftime(self.DATE_FORMAT), 'end': end.strftime(self.DATE_FORMAT)}
return self._get(url, params)

View File

@ -1,4 +1,5 @@
pbr>=2.0.0,!=2.1.0 # Apache-2.0 pbr>=2.0.0,!=2.1.0 # Apache-2.0
cliff>=2.6.0 # Apache-2.0 cliff>=2.6.0 # Apache-2.0
requests>=2.10.0,!=2.12.2,!=2.13.0 # Apache-2.0 requests>=2.10.0,!=2.12.2,!=2.13.0 # Apache-2.0
python-keystoneclient>=3.8.0 # Apache-2.0 python-keystoneclient>=3.8.0 # Apache-2.0
python-dateutil>=2.4.2 # BSD