Add API call to fetch tenant entities
This commit is contained in:
parent
75b72d4e34
commit
d263c9ae17
@ -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()))
|
||||||
|
46
almanachclient/commands/tenant_entities.py
Normal file
46
almanachclient/commands/tenant_entities.py
Normal 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
|
@ -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()
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
|
@ -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:]):
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
Loading…
x
Reference in New Issue
Block a user