From a26d3522e61369efb03fbceaeed5a2855f8efcc9 Mon Sep 17 00:00:00 2001 From: DanNiESh Date: Tue, 16 Apr 2024 16:11:41 -0400 Subject: [PATCH] Extend the client to use ESI SDK --- esileapclient/common/http.py | 101 ------------------ esileapclient/osc/plugin.py | 33 +++--- esileapclient/osc/v1/event.py | 3 +- esileapclient/osc/v1/lease.py | 22 ++-- esileapclient/osc/v1/mdc/mdc_lease.py | 7 +- esileapclient/osc/v1/mdc/mdc_offer.py | 17 ++- esileapclient/osc/v1/node.py | 2 +- esileapclient/osc/v1/offer.py | 21 ++-- esileapclient/tests/unit/common/test_http.py | 93 ---------------- esileapclient/tests/unit/osc/test_plugin.py | 49 ++++----- .../tests/unit/osc/v1/mdc/test_mdc_lease.py | 25 ++--- .../tests/unit/osc/v1/mdc/test_mdc_offer.py | 67 ++++++------ esileapclient/tests/unit/osc/v1/test_event.py | 16 ++- esileapclient/tests/unit/osc/v1/test_lease.py | 24 ++--- esileapclient/tests/unit/osc/v1/test_node.py | 18 +++- esileapclient/tests/unit/osc/v1/test_offer.py | 18 ++-- esileapclient/tests/unit/v1/__init__.py | 0 esileapclient/tests/unit/v1/test_client.py | 44 -------- esileapclient/v1/client.py | 48 --------- esileapclient/v1/event.py | 21 ---- esileapclient/v1/lease.py | 61 ----------- esileapclient/v1/node.py | 20 ---- esileapclient/v1/offer.py | 66 ------------ requirements.txt | 1 + test-requirements.txt | 3 +- 25 files changed, 172 insertions(+), 608 deletions(-) delete mode 100644 esileapclient/common/http.py delete mode 100644 esileapclient/tests/unit/common/test_http.py delete mode 100644 esileapclient/tests/unit/v1/__init__.py delete mode 100644 esileapclient/tests/unit/v1/test_client.py delete mode 100644 esileapclient/v1/client.py diff --git a/esileapclient/common/http.py b/esileapclient/common/http.py deleted file mode 100644 index 9d3b1cf..0000000 --- a/esileapclient/common/http.py +++ /dev/null @@ -1,101 +0,0 @@ -# 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. - -import logging - -from keystoneauth1 import adapter -from oslo_serialization import jsonutils - -DEFAULT_VER = '1.0' -LOG = logging.getLogger(__name__) - -USER_AGENT = 'python-esileapclient' - - -class SessionClient(adapter.LegacyJsonAdapter): - """HTTP client based on Keystone client session.""" - - def __init__(self, - os_esileap_api_version, - **kwargs): - self.os_esileap_api_version = os_esileap_api_version - - super(SessionClient, self).__init__(**kwargs) - - endpoint = self.get_endpoint() - - if endpoint is None: - # placeholder for actual error handling - raise Exception('The Lease API endpoint cannot be detected and ' - 'was not provided explicitly') - - def _http_request(self, url, method, **kwargs): - - kwargs.setdefault('user_agent', USER_AGENT) - kwargs.setdefault('auth', self.auth) - - if getattr(self, 'os_esileap_api_version', None): - kwargs['headers'].setdefault('X-OpenStack-ESI-Leap-API-Version', - self.os_esileap_api_version) - - endpoint_filter = kwargs.setdefault('endpoint_filter', {}) - endpoint_filter.setdefault('interface', self.interface) - endpoint_filter.setdefault('service_type', self.service_type) - endpoint_filter.setdefault('region_name', self.region_name) - - resp = self.session.request(url, method, - raise_exc=False, **kwargs) - - return resp - - def json_request(self, method, url, **kwargs): - - kwargs.setdefault('headers', {}) - kwargs['headers'].setdefault('Content-Type', 'application/json') - kwargs['headers'].setdefault('Accept', 'application/json') - - if 'body' in kwargs: - kwargs['data'] = jsonutils.dump_as_bytes(kwargs.pop('body')) - - resp = self._http_request(url, method, **kwargs) - - body = resp.content - content_type = resp.headers.get('content-type', None) - - if content_type is None: - return resp, list() - - if 'application/json' in content_type: - - try: - body = resp.json() - except ValueError: - LOG.error('Could not decode response body as JSON') - else: - body = None - - return resp, body - - -def _construct_http_client(session, - os_esileap_api_version=DEFAULT_VER, - **kwargs): - - kwargs.setdefault('service_type', 'lease') - kwargs.setdefault('user_agent', 'python-esileapclient') - kwargs.setdefault('interface', kwargs.pop('endpoint_type', - 'publicURL')) - - return SessionClient(os_esileap_api_version=os_esileap_api_version, - session=session, - **kwargs - ) diff --git a/esileapclient/osc/plugin.py b/esileapclient/osc/plugin.py index 8393e1f..81221cf 100644 --- a/esileapclient/osc/plugin.py +++ b/esileapclient/osc/plugin.py @@ -10,18 +10,18 @@ # License for the specific language governing permissions and limitations # under the License. +from esi import connection import logging - -from osc_lib import utils +from openstackclient.i18n import _ DEFAULT_API_VERSION = '1' # Required by the OSC plugin interface API_NAME = 'lease' -API_VERSION_OPTION = 'os_lease_api_version' +API_VERSION_OPTION = 'os_esileap_api_version' API_VERSIONS = { - '1': 'esileapclient.v1.client.Client', + '1': 'esi.connection.ESIConnection', } OS_LEASE_API_LATEST = True @@ -40,22 +40,7 @@ def make_client(instance): :param ClientManager instance: The ClientManager that owns the new client """ - - requested_api_version = instance._api_version[API_NAME] - - plugin_client = utils.get_client_class( - API_NAME, - requested_api_version, - API_VERSIONS) - - client = plugin_client( - os_esileap_api_version=requested_api_version, - session=instance.session, - region_name=instance._region_name, - endpoint_override=None - ) - - return client + return connection.ESIConnection(config=instance._cli_options).lease def build_option_parser(parser): @@ -69,4 +54,12 @@ def build_option_parser(parser): :param argparse.ArgumentParser parser: The parser object that has been initialized by OpenStackShell. """ + parser.add_argument( + '--os-esileap-api-version', + metavar='', + default=DEFAULT_API_VERSION, + help=_('ESI-LEAP API version, default=%s') + % DEFAULT_API_VERSION, + ) + return parser diff --git a/esileapclient/osc/v1/event.py b/esileapclient/osc/v1/event.py index 36413da..a5de74c 100644 --- a/esileapclient/osc/v1/event.py +++ b/esileapclient/osc/v1/event.py @@ -73,9 +73,8 @@ def take_action(self, parsed_args): 'resource_uuid': parsed_args.resource_uuid, } - data = client.event.list(filters) + data = list(client.events(**filters)) columns = EVENT_RESOURCE.fields.keys() labels = EVENT_RESOURCE.fields.values() - return (labels, (oscutils.get_item_properties(s, columns) for s in data)) diff --git a/esileapclient/osc/v1/lease.py b/esileapclient/osc/v1/lease.py index a90ed5d..44b4ed3 100644 --- a/esileapclient/osc/v1/lease.py +++ b/esileapclient/osc/v1/lease.py @@ -80,7 +80,7 @@ def take_action(self, parsed_args): if 'properties' in fields: fields['properties'] = json.loads(fields['properties']) - lease = client.lease.create(**fields) + lease = client.create_lease(**fields) data = dict([(f, getattr(lease, f, '')) for f in LEASE_RESOURCE.fields]) @@ -114,11 +114,9 @@ def take_action(self, parsed_args): fields = dict((k, v) for (k, v) in vars(parsed_args).items() if k in field_list and v is not None) - lease = client.lease.update(parsed_args.uuid, **fields) - - data = dict([(f, getattr(lease, f, '')) for f in + lease = client.update_lease(parsed_args.uuid, **fields) + data = dict([(f, lease.get(f, '')) for f in LEASE_RESOURCE.fields]) - return self.dict2columns(data) @@ -213,7 +211,7 @@ def take_action(self, parsed_args): 'purpose': parsed_args.purpose } - data = client.lease.list(filters) + data = list(client.leases(**filters)) if parsed_args.long: columns = LEASE_RESOURCE.long_fields.keys() @@ -245,13 +243,15 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.lease + lease = client.get_lease(parsed_args.uuid) - lease = client.lease.get(parsed_args.uuid)._info - resource_properties = lease['resource_properties'] - lease['resource_properties'] = oscutils.format_dict( + lease_info = {k: getattr(lease, k, '') for k in + LEASE_RESOURCE.detailed_fields} + resource_properties = lease_info['resource_properties'] + lease_info['resource_properties'] = oscutils.format_dict( resource_properties) - return zip(*sorted(lease.items())) + return zip(*sorted(lease_info.items())) class DeleteLease(command.Command): @@ -271,5 +271,5 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.lease - client.lease.delete(parsed_args.uuid) + client.delete_lease(parsed_args.uuid) print('Deleted lease %s' % parsed_args.uuid) diff --git a/esileapclient/osc/v1/mdc/mdc_lease.py b/esileapclient/osc/v1/mdc/mdc_lease.py index 024751f..b37312c 100644 --- a/esileapclient/osc/v1/mdc/mdc_lease.py +++ b/esileapclient/osc/v1/mdc/mdc_lease.py @@ -15,8 +15,7 @@ import openstack from osc_lib.command import command from osc_lib import utils as oscutils - -from esileapclient.v1 import client as esileapclient +from esi import connection from esileapclient.v1.lease import Lease as LEASE_RESOURCE LOG = logging.getLogger(__name__) @@ -90,9 +89,9 @@ def take_action(self, parsed_args): } for c in cloud_regions: - client = esileapclient.Client(session=c.get_session()) + client = connection.ESIConnection(config=c).lease - leases = client.lease.list(filters) + leases = list(client.leases(**filters)) for lease in leases: lease.cloud = c.name lease.region = c.config['region_name'] diff --git a/esileapclient/osc/v1/mdc/mdc_offer.py b/esileapclient/osc/v1/mdc/mdc_offer.py index f389ff9..6c0b84b 100644 --- a/esileapclient/osc/v1/mdc/mdc_offer.py +++ b/esileapclient/osc/v1/mdc/mdc_offer.py @@ -17,8 +17,7 @@ from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils as oscutils - -from esileapclient.v1 import client as esileapclient +from esi import connection from esileapclient.v1.lease import Lease as LEASE_RESOURCE from esileapclient.v1.offer import Offer as OFFER_RESOURCE @@ -104,9 +103,8 @@ def take_action(self, parsed_args): } for c in cloud_regions: - client = esileapclient.Client(session=c.get_session()) - - offers = client.offer.list(filters) + client = connection.ESIConnection(config=c).lease + offers = list(client.offers(**filters)) for offer in offers: offer.cloud = c.name offer.region = c.config['region_name'] @@ -179,8 +177,8 @@ def take_action(self, parsed_args): available_offers = [] for c in cloud_regions: - client = esileapclient.Client(session=c.get_session()) - offers = client.offer.list(filters) + client = connection.ESIConnection(config=c).lease + offers = list(client.offers(**filters)) for offer in offers: offer.cloud_region = c offer.cloud = c.name @@ -194,10 +192,9 @@ def take_action(self, parsed_args): offers_to_claim = random.sample(available_offers, node_count) leases = [] for offer in offers_to_claim: - client = esileapclient.Client( - session=offer.cloud_region.get_session()) + client = connection.ESIConnection(config=offer.cloud_region).lease try: - lease = client.offer.claim( + lease = client.claim_offer( offer.uuid, **{'start_time': parsed_args.start_time, 'end_time': parsed_args.end_time}) diff --git a/esileapclient/osc/v1/node.py b/esileapclient/osc/v1/node.py index 25f04ee..614ada9 100644 --- a/esileapclient/osc/v1/node.py +++ b/esileapclient/osc/v1/node.py @@ -44,7 +44,7 @@ def take_action(self, parsed_args): filters = { } - data = client.node.list(filters) + data = list(client.nodes(**filters)) if parsed_args.long: columns = NODE_RESOURCE.detailed_fields.keys() diff --git a/esileapclient/osc/v1/offer.py b/esileapclient/osc/v1/offer.py index aa2f165..0312ac1 100644 --- a/esileapclient/osc/v1/offer.py +++ b/esileapclient/osc/v1/offer.py @@ -81,7 +81,7 @@ def take_action(self, parsed_args): if 'properties' in fields: fields['properties'] = json.loads(fields['properties']) - offer = client.offer.create(**fields) + offer = client.create_offer(**fields) data = dict([(f, getattr(offer, f, '')) for f in OFFER_RESOURCE.fields]) @@ -174,7 +174,7 @@ def take_action(self, parsed_args): 'resource_class': parsed_args.resource_class } - data = client.offer.list(filters) + data = list(client.offers(**filters)) if parsed_args.long: columns = OFFER_RESOURCE.long_fields.keys() @@ -207,12 +207,15 @@ def take_action(self, parsed_args): client = self.app.client_manager.lease - offer = client.offer.get(parsed_args.uuid)._info - resource_properties = offer['resource_properties'] - offer['resource_properties'] = oscutils.format_dict( + offer = client.get_offer(parsed_args.uuid) + + offer_info = {k: getattr(offer, k, '') for k in + OFFER_RESOURCE.detailed_fields} + resource_properties = offer_info['resource_properties'] + offer_info['resource_properties'] = oscutils.format_dict( resource_properties) - return zip(*sorted(offer.items())) + return zip(*sorted(offer_info.items())) class DeleteOffer(command.Command): @@ -232,7 +235,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): client = self.app.client_manager.lease - client.offer.delete(parsed_args.uuid) + client.delete_offer(parsed_args.uuid) print('Deleted offer %s' % parsed_args.uuid) @@ -280,9 +283,9 @@ def take_action(self, parsed_args): if 'properties' in fields: fields['properties'] = json.loads(fields['properties']) - lease = client.offer.claim(parsed_args.offer_uuid, **fields) + lease = client.claim_offer(parsed_args.offer_uuid, **fields) - data = dict([(f, getattr(lease, f, '')) for f in + data = dict([(f, lease.get(f, '')) for f in LEASE_RESOURCE.fields]) return self.dict2columns(data) diff --git a/esileapclient/tests/unit/common/test_http.py b/esileapclient/tests/unit/common/test_http.py deleted file mode 100644 index f40929c..0000000 --- a/esileapclient/tests/unit/common/test_http.py +++ /dev/null @@ -1,93 +0,0 @@ -# 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. - -import mock -import pytest -import testtools -import requests - - -from esileapclient.common import http - - -DEFAULT_TIMEOUT = 600 - -DEFAULT_HOST = 'localhost' -DEFAULT_PORT = '1234' - - -def mockSessionResponse(headers, content=None, status_code=None, - request_headers={}): - - request = mock.Mock() - request.headers = request_headers - response = mock.Mock(headers=headers, - content=content, - status_code=status_code, - request=request) - response.text = content - - return response - - -def mockSession(headers, content=None, status_code=None, version=None): - session = mock.Mock(spec=requests.Session, - verify=False, - cert=('test_cert', 'test_key')) - session.get_endpoint = mock.Mock(return_value='https://test') - response = mockSessionResponse(headers, content, status_code, version) - session.request = mock.Mock(return_value=response) - - return session - - -def _session_client(**kwargs): - return http.SessionClient(os_esileap_api_version='1.6', - interface='publicURL', - service_type='lease', - region_name='', - auth=None, - **kwargs) - - -class SessionClientTest(testtools.TestCase): - - def test_json_request(self): - session = mockSession({}) - - client = _session_client(session=session) - resp, body = client.json_request('GET', 'url') - - session.request.assert_called_once_with( - 'url', 'GET', - raise_exc=False, - headers={'Content-Type': 'application/json', - 'Accept': 'application/json', - 'X-OpenStack-ESI-Leap-API-Version': '1.6'}, - user_agent=http.USER_AGENT, - auth=None, - endpoint_filter={ - 'interface': 'publicURL', - 'service_type': 'lease', - 'region_name': '' - }, - ) - - self.assertEqual(resp, session.request.return_value) - self.assertEqual(body, []) - - @mock.patch.object(http.SessionClient, 'get_endpoint', autospec=True) - def test_endpoint_not_found(self, mock_get_endpoint): - mock_get_endpoint.return_value = None - - with pytest.raises(Exception): - _session_client(session=mockSession({})) diff --git a/esileapclient/tests/unit/osc/test_plugin.py b/esileapclient/tests/unit/osc/test_plugin.py index ef7ad9a..b6eda4f 100644 --- a/esileapclient/tests/unit/osc/test_plugin.py +++ b/esileapclient/tests/unit/osc/test_plugin.py @@ -14,7 +14,8 @@ import testtools from esileapclient.osc import plugin -from esileapclient.v1 import client + +from esi import connection API_VERSION = '1' @@ -27,38 +28,34 @@ def __init__(self): self._region_name = 'RegionOne' self.session = 'fake session' self._api_version = {'lease': API_VERSION} + self._cli_options = None class MakeClientTest(testtools.TestCase): - @mock.patch.object(client, 'Client') - def test_make_client_explicit_version(self, mock_client): + @mock.patch.object(connection, 'ESIConnection') + def test_make_client_explicit_version(self, mock_conn): instance = FakeClientManager() - plugin.make_client(instance) - mock_client.assert_called_once_with( - os_esileap_api_version=API_VERSION, - session=instance.session, - region_name=instance._region_name, - endpoint_override=None) - - @mock.patch.object(client, 'Client') - def test_make_client_latest(self, mock_client): + mock_conn.return_value.lease = mock.Mock() + instance._api_version = {'lease': API_VERSION} + lease = plugin.make_client(instance) + mock_conn.assert_called_once_with(config=None) + self.assertEqual(lease, mock_conn.return_value.lease) + + @mock.patch.object(connection, 'ESIConnection') + def test_make_client_latest(self, mock_conn): instance = FakeClientManager() + mock_conn.return_value.lease = mock.Mock() instance._api_version = {'lease': plugin.LATEST_VERSION} - plugin.make_client(instance) - mock_client.assert_called_once_with( - os_esileap_api_version=plugin.LATEST_VERSION, - session=instance.session, - region_name=instance._region_name, - endpoint_override=None) + lease = plugin.make_client(instance) + mock_conn.assert_called_once_with(config=None) + self.assertEqual(lease, mock_conn.return_value.lease) - @mock.patch.object(client, 'Client') - def test_make_client_v1(self, mock_client): + @mock.patch.object(connection, 'ESIConnection') + def test_make_client_v1(self, mock_conn): instance = FakeClientManager() + mock_conn.return_value.lease = mock.Mock() instance._api_version = {'lease': '1'} - plugin.make_client(instance) - mock_client.assert_called_once_with( - os_esileap_api_version=plugin.LATEST_VERSION, - session=instance.session, - region_name=instance._region_name, - endpoint_override=None) + lease = plugin.make_client(instance) + mock_conn.assert_called_once_with(config=None) + self.assertEqual(lease, mock_conn.return_value.lease) diff --git a/esileapclient/tests/unit/osc/v1/mdc/test_mdc_lease.py b/esileapclient/tests/unit/osc/v1/mdc/test_mdc_lease.py index 9665cc1..f964d0a 100644 --- a/esileapclient/tests/unit/osc/v1/mdc/test_mdc_lease.py +++ b/esileapclient/tests/unit/osc/v1/mdc/test_mdc_lease.py @@ -13,6 +13,8 @@ import copy import mock +from esi import connection + from esileapclient.osc.v1.mdc import mdc_lease from esileapclient.tests.unit.osc.v1 import base from esileapclient.tests.unit.osc.v1 import fakes @@ -45,13 +47,13 @@ def get_session(self): self.lease2 = base.FakeResource(copy.deepcopy(fakes.LEASE)) self.cmd = mdc_lease.MDCListLease(self.app, None) - @mock.patch('esileapclient.v1.client.Client') @mock.patch('openstack.config.loader.OpenStackConfig.get_all_clouds') - def test_mdc_lease_list(self, mock_clouds, mock_client): + @mock.patch.object(connection, 'ESIConnection') + def test_mdc_lease_list(self, mock_conn, mock_clouds): mock_clouds.return_value = [self.cloud1, self.cloud2] - mock_client.return_value = self.client_mock - self.client_mock.lease.list.side_effect = [[self.lease1], - [self.lease2]] + mock_conn.return_value.lease = self.client_mock + self.client_mock.leases.side_effect = [[self.lease1], + [self.lease2]] arglist = [] verifylist = [] @@ -69,8 +71,7 @@ def test_mdc_lease_list(self, mock_clouds, mock_client): 'resource_class': parsed_args.resource_class, 'purpose': parsed_args.purpose, } - - self.client_mock.lease.list.assert_called_with(filters) + self.client_mock.leases.assert_called_with(**filters) collist = [ "Cloud", @@ -112,12 +113,12 @@ def test_mdc_lease_list(self, mock_clouds, mock_client): )) self.assertEqual(datalist, tuple(data)) - @mock.patch('esileapclient.v1.client.Client') @mock.patch('openstack.config.loader.OpenStackConfig.get_all_clouds') - def test_mdc_lease_list_filter(self, mock_clouds, mock_client): + @mock.patch.object(connection, 'ESIConnection') + def test_mdc_lease_list_filter(self, mock_conn, mock_clouds): mock_clouds.return_value = [self.cloud1, self.cloud2] - mock_client.return_value = self.client_mock - self.client_mock.lease.list.return_value = [self.lease2] + mock_conn.return_value.lease = self.client_mock + self.client_mock.leases.return_value = [self.lease2] arglist = ['--clouds', 'cloud2'] verifylist = [] @@ -136,7 +137,7 @@ def test_mdc_lease_list_filter(self, mock_clouds, mock_client): 'purpose': parsed_args.purpose, } - self.client_mock.lease.list.assert_called_with(filters) + self.client_mock.leases.assert_called_with(**filters) collist = [ "Cloud", diff --git a/esileapclient/tests/unit/osc/v1/mdc/test_mdc_offer.py b/esileapclient/tests/unit/osc/v1/mdc/test_mdc_offer.py index f0734ba..0e2f343 100644 --- a/esileapclient/tests/unit/osc/v1/mdc/test_mdc_offer.py +++ b/esileapclient/tests/unit/osc/v1/mdc/test_mdc_offer.py @@ -14,6 +14,8 @@ import json import mock +from esi import connection + from osc_lib import exceptions from esileapclient.osc.v1.mdc import mdc_offer @@ -48,13 +50,13 @@ def get_session(self): self.offer2 = base.FakeResource(copy.deepcopy(fakes.OFFER)) self.cmd = mdc_offer.MDCListOffer(self.app, None) - @mock.patch('esileapclient.v1.client.Client') @mock.patch('openstack.config.loader.OpenStackConfig.get_all_clouds') - def test_mdc_offer_list(self, mock_clouds, mock_client): + @mock.patch.object(connection, 'ESIConnection') + def test_mdc_offer_list(self, mock_conn, mock_clouds): mock_clouds.return_value = [self.cloud1, self.cloud2] - mock_client.return_value = self.client_mock - self.client_mock.offer.list.side_effect = [[self.offer1], - [self.offer2]] + mock_conn.return_value.lease = self.client_mock + self.client_mock.offers.side_effect = [[self.offer1], + [self.offer2]] arglist = [] verifylist = [] @@ -76,7 +78,7 @@ def test_mdc_offer_list(self, mock_clouds, mock_client): 'resource_class': parsed_args.resource_class, } - self.client_mock.offer.list.assert_called_with(filters) + self.client_mock.offers.assert_called_with(**filters) collist = [ "Cloud", @@ -115,12 +117,12 @@ def test_mdc_offer_list(self, mock_clouds, mock_client): )) self.assertEqual(datalist, tuple(data)) - @mock.patch('esileapclient.v1.client.Client') @mock.patch('openstack.config.loader.OpenStackConfig.get_all_clouds') - def test_mdc_offer_list_filter(self, mock_clouds, mock_client): + @mock.patch.object(connection, 'ESIConnection') + def test_mdc_offer_list_filter(self, mock_conn, mock_clouds): mock_clouds.return_value = [self.cloud1, self.cloud2] - mock_client.return_value = self.client_mock - self.client_mock.offer.list.return_value = [self.offer2] + mock_conn.return_value.lease = self.client_mock + self.client_mock.offers.return_value = [self.offer2] arglist = ['--clouds', 'cloud2'] verifylist = [] @@ -142,7 +144,7 @@ def test_mdc_offer_list_filter(self, mock_clouds, mock_client): 'resource_class': parsed_args.resource_class, } - self.client_mock.offer.list.assert_called_with(filters) + self.client_mock.offers.assert_called_with(**filters) collist = [ "Cloud", @@ -194,14 +196,14 @@ def get_session(self): self.lease3 = base.FakeResource(copy.deepcopy(fakes.LEASE)) self.cmd = mdc_offer.MDCClaimOffer(self.app, None) - @mock.patch('esileapclient.v1.client.Client') @mock.patch('openstack.config.loader.OpenStackConfig.get_all_clouds') - def test_mdc_offer_claim(self, mock_clouds, mock_client): + @mock.patch.object(connection, 'ESIConnection') + def test_mdc_offer_claim(self, mock_conn, mock_clouds): mock_clouds.return_value = [self.cloud1, self.cloud2] - mock_client.return_value = self.client_mock - self.client_mock.offer.list.side_effect = [[self.offer1, self.offer2], - [self.offer3]] - self.client_mock.offer.claim.side_effect = [self.lease1, self.lease2, + mock_conn.return_value.lease = self.client_mock + self.client_mock.offers.side_effect = [[self.offer1, self.offer2], + [self.offer3]] + self.client_mock.claim_offer.side_effect = [self.lease1, self.lease2, self.lease3] arglist = ['3', fakes.lease_start_time, fakes.lease_end_time] @@ -218,8 +220,9 @@ def test_mdc_offer_claim(self, mock_clouds, mock_client): 'resource_class': parsed_args.resource_class, } - self.client_mock.offer.list.assert_called_with(list_filters) - self.client_mock.offer.claim.assert_called_with( + self.client_mock.offers.assert_called_with(**list_filters) + + self.client_mock.claim_offer.assert_called_with( fakes.offer_uuid, start_time=str(parsed_args.start_time), end_time=str(parsed_args.end_time)) @@ -268,13 +271,13 @@ def test_mdc_offer_claim(self, mock_clouds, mock_client): self.assertEqual(2, parsed_data.count(cloud1_lease)) self.assertEqual(1, parsed_data.count(cloud2_lease)) - @mock.patch('esileapclient.v1.client.Client') @mock.patch('openstack.config.loader.OpenStackConfig.get_all_clouds') - def test_mdc_offer_claim_filter(self, mock_clouds, mock_client): + @mock.patch.object(connection, 'ESIConnection') + def test_mdc_offer_claim_filter(self, mock_conn, mock_clouds): mock_clouds.return_value = [self.cloud1, self.cloud2] - mock_client.return_value = self.client_mock - self.client_mock.offer.list.return_value = [self.offer1, self.offer2] - self.client_mock.offer.claim.side_effect = [self.lease1, self.lease2] + mock_conn.return_value.lease = self.client_mock + self.client_mock.offers.return_value = [self.offer1, self.offer2] + self.client_mock.claim_offer.side_effect = [self.lease1, self.lease2] arglist = ['2', fakes.lease_start_time, fakes.lease_end_time, '--clouds', 'cloud1'] @@ -291,8 +294,8 @@ def test_mdc_offer_claim_filter(self, mock_clouds, mock_client): 'resource_class': parsed_args.resource_class, } - self.client_mock.offer.list.assert_called_with(list_filters) - self.client_mock.offer.claim.assert_called_with( + self.client_mock.offers.assert_called_with(**list_filters) + self.client_mock.claim_offer.assert_called_with( fakes.offer_uuid, start_time=str(parsed_args.start_time), end_time=str(parsed_args.end_time)) @@ -329,13 +332,13 @@ def test_mdc_offer_claim_filter(self, mock_clouds, mock_client): self.assertEqual(2, len(parsed_data)) self.assertEqual(2, parsed_data.count(cloud1_lease)) - @mock.patch('esileapclient.v1.client.Client') @mock.patch('openstack.config.loader.OpenStackConfig.get_all_clouds') - def test_mdc_offer_claim_not_enough_offers(self, mock_clouds, mock_client): + @mock.patch.object(connection, 'ESIConnection') + def test_mdc_offer_claim_not_enough_offers(self, mock_conn, mock_clouds): mock_clouds.return_value = [self.cloud1, self.cloud2] - mock_client.return_value = self.client_mock - self.client_mock.offer.list.side_effect = [[self.offer1, self.offer2], - [self.offer3]] + mock_conn.return_value.lease = self.client_mock + self.client_mock.offers.side_effect = [[self.offer1, self.offer2], + [self.offer3]] arglist = ['4', fakes.lease_start_time, fakes.lease_end_time] verifylist = [] @@ -352,4 +355,4 @@ def test_mdc_offer_claim_not_enough_offers(self, mock_clouds, mock_client): 'resource_class': parsed_args.resource_class, } - self.client_mock.offer.list.assert_called_with(list_filters) + self.client_mock.offers.assert_called_with(**list_filters) diff --git a/esileapclient/tests/unit/osc/v1/test_event.py b/esileapclient/tests/unit/osc/v1/test_event.py index 66cbd84..6325d21 100644 --- a/esileapclient/tests/unit/osc/v1/test_event.py +++ b/esileapclient/tests/unit/osc/v1/test_event.py @@ -1,3 +1,15 @@ +# 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. + import copy from esileapclient.osc.v1 import event @@ -19,7 +31,7 @@ class TestEventList(TestEvent): def setUp(self): super(TestEventList, self).setUp() - self.client_mock.event.list.return_value = [ + self.client_mock.events.return_value = [ base.FakeResource(copy.deepcopy(fakes.EVENT)) ] self.cmd = event.ListEvent(self.app, None) @@ -40,7 +52,7 @@ def test_event_list(self): 'resource_uuid': parsed_args.resource_uuid, } - self.client_mock.event.list.assert_called_with(filters) + self.client_mock.events.assert_called_with(**filters) collist = [ "ID", diff --git a/esileapclient/tests/unit/osc/v1/test_lease.py b/esileapclient/tests/unit/osc/v1/test_lease.py index 70d2055..bdfd038 100644 --- a/esileapclient/tests/unit/osc/v1/test_lease.py +++ b/esileapclient/tests/unit/osc/v1/test_lease.py @@ -33,7 +33,7 @@ class TestCreateLease(TestLease): def setUp(self): super(TestCreateLease, self).setUp() - self.client_mock.lease.create.return_value = ( + self.client_mock.create_lease.return_value = ( base.FakeResource(copy.deepcopy(fakes.LEASE)) ) @@ -79,16 +79,16 @@ def test_lease_create(self): 'purpose': fakes.lease_purpose, } - self.client_mock.lease.create.assert_called_once_with(**args) + self.client_mock.create_lease.assert_called_once_with(**args) class TestUpdateLease(TestLease): def setUp(self): super(TestUpdateLease, self).setUp() - - self.client_mock.lease.update.return_value = ( - base.FakeResource(copy.deepcopy(fakes.LEASE)) + lease_return = base.FakeResource(copy.deepcopy(fakes.LEASE)) + self.client_mock.update_lease.return_value = ( + dict(lease_return.__dict__) ) # Get the command object to test @@ -109,7 +109,7 @@ def test_lease_update(self): self.cmd.take_action(parsed_args) - self.client_mock.lease.update.assert_called_once_with( + self.client_mock.update_lease.assert_called_once_with( fakes.lease_uuid, end_time=fakes.lease_end_time) def test_update_show_no_id(self): @@ -125,7 +125,7 @@ class TestLeaseList(TestLease): def setUp(self): super(TestLeaseList, self).setUp() - self.client_mock.lease.list.return_value = [ + self.client_mock.leases.return_value = [ base.FakeResource(copy.deepcopy(fakes.LEASE)) ] self.cmd = lease.ListLease(self.app, None) @@ -153,7 +153,7 @@ def test_lease_list(self): 'purpose': parsed_args.purpose } - self.client_mock.lease.list.assert_called_with(filters) + self.client_mock.leases.assert_called_with(**filters) collist = [ "UUID", @@ -204,7 +204,7 @@ def test_lease_list_long(self): 'purpose': parsed_args.purpose } - self.client_mock.lease.list.assert_called_with(filters) + self.client_mock.leases.assert_called_with(**filters) long_collist = [ 'UUID', @@ -247,7 +247,7 @@ class TestLeaseShow(TestLease): def setUp(self): super(TestLeaseShow, self).setUp() - self.client_mock.lease.get.return_value = \ + self.client_mock.get_lease.return_value = \ base.FakeResource(copy.deepcopy(fakes.LEASE)) self.cmd = lease.ShowLease(self.app, None) @@ -259,7 +259,7 @@ def test_lease_show(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.client_mock.lease.get.assert_called_once_with( + self.client_mock.get_lease.assert_called_once_with( fakes.lease_uuid) collist = ( @@ -331,7 +331,7 @@ def test_lease_delete(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.client_mock.lease.delete.assert_called_once_with( + self.client_mock.delete_lease.assert_called_once_with( fakes.lease_uuid) def test_lease_delete_no_id(self): diff --git a/esileapclient/tests/unit/osc/v1/test_node.py b/esileapclient/tests/unit/osc/v1/test_node.py index aacf6ee..9db338f 100644 --- a/esileapclient/tests/unit/osc/v1/test_node.py +++ b/esileapclient/tests/unit/osc/v1/test_node.py @@ -1,3 +1,15 @@ +# 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. + import copy from esileapclient.osc.v1 import node @@ -19,7 +31,7 @@ class TestNodeList(TestNode): def setUp(self): super(TestNodeList, self).setUp() - self.client_mock.node.list.return_value = [ + self.client_mock.nodes.return_value = [ base.FakeResource(copy.deepcopy(fakes.NODE)) ] self.cmd = node.ListNode(self.app, None) @@ -34,7 +46,7 @@ def test_node_list(self): filters = { } - self.client_mock.node.list.assert_called_with(filters) + self.client_mock.nodes.assert_called_with(**filters) collist = [ "Name", @@ -66,7 +78,7 @@ def test_node_list_long(self): filters = { } - self.client_mock.node.list.assert_called_with(filters) + self.client_mock.nodes.assert_called_with(**filters) long_collist = [ "UUID", diff --git a/esileapclient/tests/unit/osc/v1/test_offer.py b/esileapclient/tests/unit/osc/v1/test_offer.py index 0e86f44..848e5d6 100644 --- a/esileapclient/tests/unit/osc/v1/test_offer.py +++ b/esileapclient/tests/unit/osc/v1/test_offer.py @@ -33,7 +33,7 @@ class TestOfferCreate(TestOffer): def setUp(self): super(TestOfferCreate, self).setUp() - self.client_mock.offer.create.return_value = ( + self.client_mock.create_offer.return_value = ( base.FakeResource(copy.deepcopy(fakes.OFFER)) ) @@ -76,14 +76,14 @@ def test_offer_create(self): 'start_time': fakes.lease_start_time, } - self.client_mock.offer.create.assert_called_once_with(**args) + self.client_mock.create_offer.assert_called_once_with(**args) class TestOfferList(TestOffer): def setUp(self): super(TestOfferList, self).setUp() - self.client_mock.offer.list.return_value = [ + self.client_mock.offers.return_value = [ base.FakeResource(copy.deepcopy(fakes.OFFER)) ] self.cmd = offer.ListOffer(self.app, None) @@ -111,7 +111,7 @@ def test_offer_list(self): 'resource_class': parsed_args.resource_class } - self.client_mock.offer.list.assert_called_with(filters) + self.client_mock.offers.assert_called_with(**filters) collist = [ "UUID", @@ -160,7 +160,7 @@ def test_offer_list_long(self): 'resource_class': parsed_args.resource_class } - self.client_mock.offer.list.assert_called_with(filters) + self.client_mock.offers.assert_called_with(**filters) long_collist = [ 'UUID', @@ -197,7 +197,7 @@ class TestOfferShow(TestOffer): def setUp(self): super(TestOfferShow, self).setUp() - self.client_mock.offer.get.return_value = \ + self.client_mock.get_offer.return_value = \ base.FakeResource(copy.deepcopy(fakes.OFFER)) self.cmd = offer.ShowOffer(self.app, None) @@ -209,7 +209,7 @@ def test_offer_show(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.client_mock.offer.get.assert_called_once_with( + self.client_mock.get_offer.assert_called_once_with( fakes.offer_uuid) collist = ( @@ -275,7 +275,7 @@ def test_offer_delete(self): parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) - self.client_mock.offer.delete.assert_called_once_with( + self.client_mock.delete_offer.assert_called_once_with( fakes.offer_uuid) def test_offer_delete_no_id(self): @@ -315,7 +315,7 @@ def test_offer_claim(self): 'start_time': fakes.lease_start_time, } - self.client_mock.offer.claim.assert_called_once_with( + self.client_mock.claim_offer.assert_called_once_with( fakes.offer_uuid, **lease_args) def test_offer_claim_no_id(self): diff --git a/esileapclient/tests/unit/v1/__init__.py b/esileapclient/tests/unit/v1/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/esileapclient/tests/unit/v1/test_client.py b/esileapclient/tests/unit/v1/test_client.py deleted file mode 100644 index acb4cc7..0000000 --- a/esileapclient/tests/unit/v1/test_client.py +++ /dev/null @@ -1,44 +0,0 @@ -import mock -import testtools - -from esileapclient.common import http -from esileapclient.v1 import client - - -@mock.patch.object(http, '_construct_http_client', autospec=True) -class ClientTest(testtools.TestCase): - - def test_client_user_api_version(self, http_client_mock): - os_esileap_api_version = '1.15' - session = mock.Mock() - - client.Client(session=session, - os_esileap_api_version=os_esileap_api_version) - - http_client_mock.assert_called_once_with( - session=session, - os_esileap_api_version=os_esileap_api_version) - - def test_client_initialized_managers(self, http_client_mock): - session = mock.Mock() - cl = client.Client(session=session, - os_esileap_api_version='1') - - self.assertIsInstance(cl.offer, client.offer.OfferManager) - - def test_client_no_session(self, http_client_mock): - self.assertRaises(TypeError, client.Client, os_esileap_api_version='1') - - def test_client_session_via_posargs(self, http_client_mock): - session = mock.Mock() - client.Client(session) - http_client_mock.assert_called_once_with( - session, - os_esileap_api_version=client.DEFAULT_VER) - - def test_client_session_via_kwargs(self, http_client_mock): - session = mock.Mock() - client.Client(session=session) - http_client_mock.assert_called_once_with( - session, - os_esileap_api_version=client.DEFAULT_VER) diff --git a/esileapclient/v1/client.py b/esileapclient/v1/client.py deleted file mode 100644 index 738cf03..0000000 --- a/esileapclient/v1/client.py +++ /dev/null @@ -1,48 +0,0 @@ -# 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. - - -import logging - - -from esileapclient.common import http -from esileapclient.common.http import DEFAULT_VER -from esileapclient.v1 import event -from esileapclient.v1 import lease -from esileapclient.v1 import node -from esileapclient.v1 import offer - -LOG = logging.getLogger(__name__) - - -class Client(object): - """Client for the ESI-Leap v1 API. - :param session: A keystoneauth Session object (must be provided as - a keyword argument). - """ - - def __init__(self, *args, **kwargs): - """Initialize a new client for the ESI-Leap v1 API.""" - - if 'os_esileap_api_version' not in kwargs: - kwargs['os_esileap_api_version'] = DEFAULT_VER - - if not args and not kwargs.get('session'): - raise TypeError("A session is required for creating a client, " - "use esileapclient.client.get_client to create " - "it automatically") - - self.http_client = http._construct_http_client(*args, **kwargs) - self.event = event.EventManager(self.http_client) - self.lease = lease.LeaseManager(self.http_client) - self.node = node.NodeManager(self.http_client) - self.offer = offer.OfferManager(self.http_client) diff --git a/esileapclient/v1/event.py b/esileapclient/v1/event.py index 8913e61..8a641d8 100644 --- a/esileapclient/v1/event.py +++ b/esileapclient/v1/event.py @@ -50,24 +50,3 @@ class Event(base.Resource): def __repr__(self): return "" % self._info - - -class EventManager(base.Manager): - resource_class = Event - _resource_name = 'events' - - def list(self, filters, os_esileap_api_version=None): - """Retrieve a list of events. - :returns: A list of events. - """ - - resource_id = '' - - url_variables = EventManager._url_variables(filters) - url = self._path(resource_id) + url_variables - - events = self._list(url, - os_esileap_api_version=os_esileap_api_version) - - if type(events) is list: - return events diff --git a/esileapclient/v1/lease.py b/esileapclient/v1/lease.py index bc57dbc..78470c1 100644 --- a/esileapclient/v1/lease.py +++ b/esileapclient/v1/lease.py @@ -79,64 +79,3 @@ class Lease(base.Resource): def __repr__(self): return "" % self._info - - -class LeaseManager(base.Manager): - resource_class = Lease - _resource_name = 'leases' - - def create(self, os_esileap_api_version=None, **kwargs): - """Create a lease based on a kwargs dictionary of attributes. - :returns: a :class: `Lease` object - """ - - lease = self._create(os_esileap_api_version=os_esileap_api_version, - **kwargs) - - return lease - - def update(self, lease_uuid, os_esileap_api_version=None, **kwargs): - """Update a lease based on a kwargs dictionary of attributes. - :returns: a :class: `Lease` object - """ - - lease = self._update( - lease_uuid, - os_esileap_api_version=os_esileap_api_version, - **kwargs) - - return lease - - def list(self, filters, os_esileap_api_version=None): - """Retrieve a list of leases. - :returns: A list of leases. - """ - - resource_id = "" - - url_variables = LeaseManager._url_variables(filters) - url = self._path(resource_id) + url_variables - - leases = self._list(url, - os_esileap_api_version=os_esileap_api_version) - - if type(leases) is list: - return leases - - def get(self, lease_uuid): - """Get a lease with the specified identifier. - :param lease_uuid: The uuid of a lease. - :returns: a :class:`Lease` object. - """ - - lease = self._get(lease_uuid) - - return lease - - def delete(self, lease_uuid): - """Delete a lease with the specified identifier. - :param lease_uuid: The uuid of a lease. - :returns: a :class:`Lease` object. - """ - - self._delete(resource_id=lease_uuid) diff --git a/esileapclient/v1/node.py b/esileapclient/v1/node.py index 7092ac4..b7841e7 100644 --- a/esileapclient/v1/node.py +++ b/esileapclient/v1/node.py @@ -53,23 +53,3 @@ class Node(base.Resource): def __repr__(self): return "" % self._info - - -class NodeManager(base.Manager): - resource_class = Node - _resource_name = 'nodes' - - def list(self, filters, os_esileap_api_version=None): - """Retrieve a list of nodes. - :returns: A list of nodes. - """ - - resource_id = "" - - url_variables = NodeManager._url_variables(filters) - url = self._path(resource_id) + url_variables - - nodes = self._list(url, os_esileap_api_version=os_esileap_api_version) - - if type(nodes) is list: - return nodes diff --git a/esileapclient/v1/offer.py b/esileapclient/v1/offer.py index f022ea6..e0ab7ca 100644 --- a/esileapclient/v1/offer.py +++ b/esileapclient/v1/offer.py @@ -10,11 +10,8 @@ # License for the specific language governing permissions and limitations # under the License. -import json import logging -from osc_lib import exceptions - from esileapclient.common import base LOG = logging.getLogger(__name__) @@ -74,66 +71,3 @@ class Offer(base.Resource): def __repr__(self): return "" % self._info - - -class OfferManager(base.Manager): - resource_class = Offer - _resource_name = 'offers' - - def create(self, os_esileap_api_version=None, **kwargs): - """Create an offer based on a kwargs dictionary of attributes. - :returns: a :class: `Offer` object - """ - - offer = self._create(os_esileap_api_version=os_esileap_api_version, - **kwargs) - - return offer - - def list(self, filters, os_esileap_api_version=None): - """Retrieve a list of offers. - :returns: A list of offers. - """ - - resource_id = '' - - url_variables = OfferManager._url_variables(filters) - url = self._path(resource_id) + url_variables - - offers = self._list(url, - os_esileap_api_version=os_esileap_api_version) - - if type(offers) is list: - return offers - - def get(self, offer_uuid): - """Get an offer with the specified identifier. - :param offer_uuid: The uuid of an offer. - :returns: a :class:`Offer` object. - """ - - offer = self._get(offer_uuid) - - return offer - - def delete(self, offer_uuid): - """Delete an offer with the specified identifier. - :param offer_uuid: The uuid of an offer. - :returns: a :class:`Offer` object. - """ - - self._delete(resource_id=offer_uuid) - - def claim(self, offer_uuid, **kwargs): - """Claim an offer with the specified identifier. - :param offer_uuid: The uuid of an offer. - :returns: a :class:`Offer` object. - """ - url = self._path(offer_uuid) + "/claim" - - resp, body = self.api.json_request('POST', url, body=kwargs) - - if resp.status_code == 201: - return self.resource_class(self, body) - else: - raise exceptions.CommandError(json.loads(resp.text)['faultstring']) diff --git a/requirements.txt b/requirements.txt index ab68f99..40ab858 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,4 @@ openstacksdk<1.3.0 pbr!=2.1.0,>=2.0.0 # Apache-2.0 python-openstackclient>=3.18.0 six>=1.12.0 +esisdk==0.2 diff --git a/test-requirements.txt b/test-requirements.txt index dffd33c..80694ff 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -6,10 +6,11 @@ mock>=2.0.0 osc-lib>=2.0.0 oslo.serialization>=3.1.0 pycodestyle>=2.5.0 -pytest>= 4.6.3 +pytest<=8.1.2 pytest-cov>=2.7.1 requests-mock>=1.2.0 tempest>=17.1.0 testtools>=2.2.0 tox>= 3.12.1 WebTest>=2.0.33 +esisdk==0.2